use Elementor\Controls_Manager;
use Elementor\Utils;
+use function Roots\asset;
class ImageMap extends _Base {
// Which versions should be handled?
protected $versions = [
'desktop',
- //'mobile', // TODO: mobile version!
+ 'mobile',
];
// Widget name / ID
* @return array Widget scripts dependencies.
*/
public function get_script_depends() {
- return [];
+
+ wp_register_script(
+ 'cube-image-map',
+ asset('scripts/image-map.js'),
+ ['jquery'], // Dependencies
+ null, // Version
+ true // In footer?
+ );
+
+ return ['cube-image-map'];
}
/**
'reminder',
[
'type' => Controls_Manager::RAW_HTML,
- 'raw' => 'Pour générer le HTML, <a href="https://www.zaneray.com/responsive-image-map/" target="_blank">utilisez cet outil</a>',
- // TODO: consider modifying Weebly Image Map generator so it generates code like Zaneray. Or just use Weebly version with jQuery plugin?
+ 'raw' => 'Pour générer le HTML, <a href="https://image-map.weebly.com/" target="_blank">utilisez cet outil</a>',
]
);
$this->add_control(
"html_$version",
[
- 'label' => "HTML ($version)",
+ 'label' => "HTML ($version)<br>Enter <pre style='display:inline;'><area></pre> tags only",
+ 'label_block' => true,
'type' => Controls_Manager::CODE,
'default' => '',
+ 'divider' => true,
]
);
$res = '';
foreach($this->versions as $version) {
+ $map_ID = "map_{$version}_" . $this->get_id();
$image = $this->get_settings("image_{$version}");
$image_meta = wp_get_attachment_metadata($image['id']);
$max_width = isset($image_meta['width']) ? $image_meta['width'] . 'px' : 'none'; // Container must not be wider than image
- $html = $this->get_settings("html_{$version}");
+ $map_html = $this->get_settings("html_{$version}");
$res .= '<div class="relative mx-auto cube-image-map-'. $version .'" style="max-width: '. $max_width .'">';
- $res .= '<img src="'. $image['url'] .'" class="cube-image-map-image">';
- $res .= $html;
+ $res .= '<img src="'. $image['url'] .'" class="cube-image-map-image" usemap="#'. $map_ID .'">';
+ $res .= '<map name="'. $map_ID .'">';
+ $res .= $map_html;
+ $res .= '</map>';
$res .= '</div>';
}
]
);
+ $repeater->add_control(
+ 'email',
+ [
+ 'label' => __('Adresse e-mail', 'cube'),
+ 'title_block' => false,
+ 'type' => Controls_Manager::TEXT,
+ 'default' => ''
+ ]
+ );
+
$repeater->add_control(
'details',
[
"cross-env": "^7.0.2",
"eslint": "^7.7.0",
"eslint-plugin-import": "^2.22.0",
+ "image-map": "^2.0.1",
"laravel-mix": "^5.0.4",
"laravel-mix-copy-watched": "^2.2.4",
"laravel-mix-purgecss": "^5.0.0",
--- /dev/null
+import ImageMap from "image-map";
+
+// ELEMENTOR Trigger
+(function($) {
+ $(window).on('elementor/frontend/init', function () {
+ elementorFrontend.hooks.addAction('frontend/element_ready/cube-image-map.default', function ($scope) {
+ // Call Image Map Resizer (https://github.com/clarketm/image-map)
+ ImageMap('img[usemap]');
+ });
+ });
+})(jQuery);
html
font-size: $font-size-base // Base used for REM calculations
+ +below(1000px)
+ font-size: 15px
+ +below(800px)
+ font-size: 14px
+
body
min-width: 320px
-input, textarea
- &::-webkit-input-placeholder /* Chrome/Opera/Safari */
- @apply text-dark
- &::-moz-placeholder /* Firefox 19+ */
- @apply text-dark
- &:-ms-input-placeholder /* IE 10+ */
- @apply text-dark
- &:-moz-placeholder /* Firefox 18- */
- @apply text-dark
-
-input, textarea, select
- @apply bg-white w-full px-6 py-2 rounded-md
- border: none
- outline: none
-
-input[type="text"], input[type="email"], input[type="number"], select
- appearance: none
-
-input[type="submit"]
- width: auto
-
-select
- background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' x='0' y='0' viewBox='0 0 15 9' xml:space='preserve'%3E%3Cg fill='none' stroke='%233b3b3b' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M13.9 1.3L7.5 7.7'/%3E%3Cpath d='M1.1 1.3l6.4 6.4'/%3E%3C/g%3E%3C/svg%3E") 98% center no-repeat
- background-size: 0.8em auto
-
-textarea
- min-height: 12rem
- padding: 1em
-
-//================================
-
.form-wrapper
> * + *
margin-top: 1.5rem
-.form-row
- display: grid
- constrain(grid-column-gap, 2vw)
- // Automatic columns based on number of child elements
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))
- grid-row-gap: 1.5rem // When wrapping columns (to match vertical gap between other rows)
+ .form-row
+ display: grid
+ constrain(grid-column-gap, 2vw)
+ // Automatic columns based on number of child elements
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))
+ grid-row-gap: 1.5rem // When wrapping columns (to match vertical gap between other rows)
+
+ input, textarea
+ &::-webkit-input-placeholder /* Chrome/Opera/Safari */
+ @apply text-dark
+ &::-moz-placeholder /* Firefox 19+ */
+ @apply text-dark
+ &:-ms-input-placeholder /* IE 10+ */
+ @apply text-dark
+ &:-moz-placeholder /* Firefox 18- */
+ @apply text-dark
+
+ input, textarea, select
+ @apply bg-white w-full px-6 py-2 rounded-md
+ border: none
+ outline: none
+
+ input[type="text"], input[type="email"], input[type="number"], select
+ appearance: none
+
+ input[type="submit"]
+ width: auto
+
+ select
+ background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' x='0' y='0' viewBox='0 0 15 9' xml:space='preserve'%3E%3Cg fill='none' stroke='%233b3b3b' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M13.9 1.3L7.5 7.7'/%3E%3Cpath d='M1.1 1.3l6.4 6.4'/%3E%3C/g%3E%3C/svg%3E") 98% center no-repeat
+ background-size: 0.8em auto
+
+ textarea
+ min-height: 12rem
+ padding: 1em
--- /dev/null
+$breakpoint-image-map = 900px
+
+.cube-image-map
+ &-mobile
+ display: none // Hidden by default
+
+ +below($breakpoint-image-map)
+ &-desktop
+ display: none
+ &-mobile
+ display: block
--- /dev/null
+// Override default Elementor styles that interfere with image sizing when links are applied
+.elementor-widget-image
+ .elementor-image
+ & >, figure >
+ a
+ display: block
+
+ img[src$=".svg"]
+ width: auto
--- /dev/null
+$breakpoint-team-grid = 800px
+
+.team-grid
+ display: grid
+ grid-template-columns: repeat(3, 1fr)
+ grid-column-gap: 2rem
+ vertical-spacing(2.5vw) // Offset the negative margins inside the grid
+
+ // Due to the absolutely positioned text blocks under the images, the text
+ // in the last row of the grid is in danger of overlapping with the content
+ // below it, so for these awkward sizes, we add some extra margin to cover it.
+ +between($breakpoint-team-grid, 1600px)
+ margin-bottom: 5rem // ~5 lines of extra space
+
+ +below($breakpoint-team-grid)
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)) // Will auto-wrap to 1 column when there's not enough space
+ grid-gap: 2.5vw
+ padding: 0
+
+ &-person
+ grid-column-start: unset !important
+ margin: 0 !important
+
+ // Centre content (relevant when in 1 column mode but column width is larger than max-width of contents)
+ > *
+ @apply mx-auto
+
+ &-details
+ @apply mb-6
+ position: initial !important
+
+
+ // The 3 column grid has a repeating pattern of one row with a single centred item (in column 2)
+ // then a row with two items, separated in columns 1 and 3:
+ // _ X _
+ // X _ X
+ &-person
+ // Middle column (items 1, 4, 7, 10 etc)
+ &:nth-of-type(3n + 1)
+ grid-column-start: 2
+ vertical-spacing(-2.5vw, margin) // Use negative vertical margins in the centre column to create overlaps
+
+ // When there's extra space, central column content should be centred
+ > *
+ @apply mx-auto
+
+ // Left column (items 2, 5, 8 etc)
+ &:nth-of-type(3n + 2)
+ grid-column-start: 1
+
+ // Right column (items 3, 6, 9 etc)
+ &:nth-of-type(3n + 3)
+ grid-column-start: 3
+
+ // When there's extra space, right column content should right-aligned
+ > *
+ @apply ml-auto
</div>
<h3 class="font-medium uppercase text-base mt-8 mb-1">Des promos & des news !</h3>
- <p>Inscrivez-vous à notre newsletter</p>
+ <p class="mb-6">Inscrivez-vous à notre newsletter</p>
<div class="footer-newsletter-form flex mx-auto" style="max-width: 300px">
{{-- TODO: add HTMLForms shortcode here? --}}
<input type="email" name="email" placeholder="Votre email" class="flex-1 text-dark leading-8 rounded-full focus:outline-none focus:ring-2 focus:ring-opacity-50 focus:ring-dark px-4 mr-3">
{{-- TEAM GRID --}}
<div class="team-grid">
- <div class="team-grid-items">
- @foreach ($team as $person)
- <div class="team-grid-person">
- <div class="team-grid-photos relative rounded-lg" style="max-width: 385px">
+ @foreach ($team as $person)
+ <div class="team-grid-person">
+
+ {{-- Content Wrapper --}}
+ <div class="relative" style="max-width: 385px">
+
+ {{-- Photo wrapper --}}
+ <div class="group relative rounded-xl overflow-hidden">
<div style="width: 100%; padding-bottom: 100%;">{{-- Proportional sizer to make a responsive square --}}</div>
- @php($photo_classes = 'absolute top-0 left-0 w-full h-full')
+ @php
+ $photo_classes = 'absolute inset-0 bg-light bg-cover'
+ @endphp
{{-- Main photo --}}
- <div class="z-20 group-hover:opacity-0 {{ $photo_classes }}" style="background-image:url({{ $person['photo']['url'] }})"></div>
- {{-- Hover photo --}}
+ <div class="z-30 group-hover:hidden {{ $photo_classes }}" style="background-image:url({{ $person['photo']['url'] }})"></div>
+
+ {{-- Hover photo + details --}}
+ @if ($person['email'])
+
+ @php
+ // Encode e-mail address to give it some protection from bots
+ // NOTE: this isn't working because the final output always seems
+ // to be converted by some intermediate process (WP / Blade?)
+ /*
+ $mail_link = "mailto:{$person['email']}";
+ $encoded_link = '';
+
+ for ($i = 0; $i < strlen($mail_link); $i++) {
+ $encoded_link .= '&#' . ord($mail_link[$i]) . ';';
+ }
+ */
+ @endphp
+
+ <div class="z-20 absolute bottom-0 w-full text-center">
+ <a href="mailto:{{ $person['email'] }}" class="btn mb-1v px-6">
+ @svg('images/icons/email', 'fill-current inline-block align-top w-5 mr-2')
+ Envoyer un email
+ </a>
+ </div>
+ @endif
+
<div class="z-10 {{ $photo_classes }}" style="background-image:url({{ $person['photo_hover']['url'] }})"></div>
+
</div>
- <div class="text-center">
+
+ {{-- Text needs to be absolutely positioned so it doesn't interfere with the grid layout --}}
+ <div class="team-grid-details text-center absolute w-full top-100% pt-6">
<strong class="text-lg">{{ $person['first_name'] }} {{ strtoupper($person['last_name']) }}</strong>
<br>
<em>{{ $person['role'] }}</em>
<br>
- {{ $person['details'] }}
+ {!! nl2br($person['details']) !!}
</div>
</div>
- @endforeach
- </div>
+ </div>
+ @endforeach
</div>
margin: {
'0!': '0 !important',
},
+ top: {
+ '100%': '100%',
+ },
},
},
- variants: {},
+ variants: {
+ extend: {
+ display: ['group-hover'],
+ },
+ },
plugins: [
wordpressUtilities,
].concat(
require('purgecss-with-wordpress').whitelist,
require('purgecss-with-wordpress').whitelistPatterns
- )
+ ),
},
},
};
mix.setPublicPath('./dist');
+// Public path helper
+const publicPath = path => `${mix.config.publicPath}/${path}`;
+
mix.browserSync({
proxy: proxyURL,
host: proxyURL, // Allows browsersync to work on main URL instead of localhost
mix
.js('resources/assets/scripts/app.js', 'scripts')
.js('resources/assets/scripts/intro-carousel.js', 'scripts')
+ .js('resources/assets/scripts/image-map.js', 'scripts')
.js('resources/assets/scripts/customizer.js', 'scripts')
.blocks('resources/assets/scripts/editor.js', 'scripts')
.extract();
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
+image-map@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/image-map/-/image-map-2.0.1.tgz#d9e87184cd944b263135d9f2e8dec5a75a8a5c7a"
+ integrity sha512-6SVn3CK7n4wtaWqW3vRSFvKKyQpChr7ADECmLTS9WsD4mTivkRgZ8OqfUBsg7bo5hm81oiO02aAyZ1dNu20xXQ==
+
imagemin@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/imagemin/-/imagemin-6.1.0.tgz#62508b465728fea36c03cdc07d915fe2d8cf9e13"