diff --git a/assets/js/theme/blocks/draggableBlocks.js b/assets/js/theme/blocks/draggableBlocks.js index fcde80907af054d6068d5a757e9358fadcd7e148..b653531f74784e774c107bf25032805d665a821a 100644 --- a/assets/js/theme/blocks/draggableBlocks.js +++ b/assets/js/theme/blocks/draggableBlocks.js @@ -47,7 +47,7 @@ class DraggableBlock { this.update(); } - onClickItem(i) { + onClickItem (i) { if (!this.isManipulated) { this.goTo(i); } @@ -57,7 +57,7 @@ class DraggableBlock { this.previous.addEventListener('click', () => { this.goTo(this.index-1); }); - + this.next.addEventListener('click', () => { this.goTo(this.index+1); }); @@ -80,7 +80,7 @@ class DraggableBlock { this.isPointerDown = true; this.content.classList.add('is-grabbing'); startX = event.clientX; - } + } }); this.block.addEventListener('pointermove', (event) => { @@ -90,7 +90,7 @@ class DraggableBlock { event.preventDefault(); this.items.forEach((item) => { // on enlève le pointerevents pour que les liens ne soient pas cliquable au drag - item.style.pointerEvents = "none"; + item.style.pointerEvents = 'none'; }); } }); @@ -105,12 +105,12 @@ class DraggableBlock { } }); } - - handleScroll() { + + handleScroll () { // On écoute le scroll sur le contenu du bloc this.content.addEventListener('wheel', (event) => { - const deltaX = event.deltaX; - const deltaY = event.deltaY; + const deltaX = event.deltaX, + deltaY = event.deltaY; // navigation entre les items (comme onManipulationEnd) if (Math.abs(deltaX) > Math.abs(deltaY)) { if (deltaX !== 0) { @@ -130,11 +130,11 @@ class DraggableBlock { } else if (start < end - threshold) { this.goTo(this.index-1); } - + this.content.classList.remove('is-grabbing'); this.items.forEach((item) => { // On rend le pointervents pour pouvoir cliquer sur le lien si on drag pas - item.style.pointerEvents = "all"; + item.style.pointerEvents = 'all'; }); setTimeout(() => { diff --git a/assets/js/theme/blocks/keyFigures.js b/assets/js/theme/blocks/keyFigures.js index 0387418d8002c7a0aac595db3b3d3e73c61af783..3175739595b4ca400f04b7db3489007af545bba6 100644 --- a/assets/js/theme/blocks/keyFigures.js +++ b/assets/js/theme/blocks/keyFigures.js @@ -4,10 +4,11 @@ import '../utils/number'; // Compatibilities window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || - window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; + window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; const OPTIONS = { - DURATION: 2000 // in ms + // in ms + DURATION: 2000 }; class KeyFigures { @@ -85,7 +86,7 @@ class KeyFigures { return value.toFixed(decimalsLength); } - formatValue (value, separator = " ") { + formatValue (value, separator = ' ') { return value.toLocaleString('en').replace(',', separator); } diff --git a/assets/js/theme/blocks/organizations.js b/assets/js/theme/blocks/organizations.js index ec5e4703ed517652e4a29f6d9d24ed4f42a9d171..5203930e917b1eecbfdcd6aafcb16f20e37cae65 100644 --- a/assets/js/theme/blocks/organizations.js +++ b/assets/js/theme/blocks/organizations.js @@ -1,3 +1,4 @@ +/* global L */ const organizationsMaps = document.querySelectorAll('.block-organizations--map'); class BlockOrganizations { @@ -6,7 +7,8 @@ class BlockOrganizations { this.init(); } - init() { + + init () { this.markers = []; this.setMap = false; this.content = this.dom.querySelector('.map'); @@ -20,7 +22,7 @@ class BlockOrganizations { this.themeMarker = L.icon({ iconUrl: this.content.getAttribute('data-marker-icon') || '/assets/images/map-marker.svg', - iconSize: [17, 26], + iconSize: [17, 26] }); this.setOrganizations(map); @@ -29,21 +31,22 @@ class BlockOrganizations { this.getMapBounds(map); } else { this.dom.classList.add(this.classHidden); - this.dom.setAttribute("aria-hidden", "true") - return; + this.dom.setAttribute('aria-hidden', 'true'); } } + setOrganizations (map) { this.organizationsList.forEach((organization) => { let latitude = parseFloat(organization.getAttribute('data-latitude')), longitude = parseFloat(organization.getAttribute('data-longitude')), mapLocation = [latitude, longitude]; - if (!!latitude && !!longitude) { + if (Boolean(latitude) && Boolean(longitude)) { this.newMarker(map, mapLocation, organization); this.setMap = true; } }); } + listen (map) { L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, @@ -51,7 +54,7 @@ class BlockOrganizations { }).addTo(map); } - newMarker(map, mapLocation, organization) { + newMarker (map, mapLocation, organization) { let marker = new L.marker(mapLocation, { draggable: false, icon: this.themeMarker @@ -61,13 +64,12 @@ class BlockOrganizations { this.markers.push(marker); } - getMapBounds(map) { + getMapBounds (map) { this.group = L.featureGroup(this.markers).addTo(map); map.fitBounds(this.group.getBounds()); } - } organizationsMaps.forEach((dom) => { new BlockOrganizations(dom); -}); \ No newline at end of file +}); diff --git a/assets/js/theme/blocks/videos.js b/assets/js/theme/blocks/videos.js index fcb28a4d16d11f92f11c7e8fe0e338d050036526..5aed445a8140486bbf6c5da36b904bc2bc3f9c0a 100644 --- a/assets/js/theme/blocks/videos.js +++ b/assets/js/theme/blocks/videos.js @@ -1,32 +1,34 @@ const videos = document.querySelectorAll('.block-video'); -class videoPlayer { +class VideoPlayer { constructor (dom) { this.dom = dom; - + this.player = this.dom.querySelector('.lazy-video-player'); if (!this.player) { return; } - + this.playBtn = this.player.querySelector('button'); this.videoIframe = this.dom.querySelector('.video-container iframe'); this.videoSrc = this.videoIframe.getAttribute('data-unloaded-src'); - + this.listen(); } + listen () { this.playBtn.addEventListener('click', () => { this.playVideo(); }); } - playVideo() { + + playVideo () { this.player.style.display = 'none'; this.videoIframe.src = this.videoSrc; } } videos.forEach((dom) => { - new videoPlayer(dom); -}); \ No newline at end of file + new VideoPlayer(dom); +}); diff --git a/assets/js/theme/design-system/clickToCopy.js b/assets/js/theme/design-system/clickToCopy.js index e98a0f0287516a999632f0026da941532bbfb881..16dc67b4fb63ed3b4869114a6f917846ec571879 100644 --- a/assets/js/theme/design-system/clickToCopy.js +++ b/assets/js/theme/design-system/clickToCopy.js @@ -32,5 +32,5 @@ class ClickToCopy { const clickToCopy = document.querySelectorAll("[data-click-to-copy]"); clickToCopy.forEach(button => { new ClickToCopy(button); - }) -})(); \ No newline at end of file + }); +}()); diff --git a/assets/js/theme/design-system/font.js b/assets/js/theme/design-system/font.js index 9480ebd6704d8047adce439b2121a6ed08db9b8c..639a50e3ffd25e1a162d694b0a6de541265be3b0 100644 --- a/assets/js/theme/design-system/font.js +++ b/assets/js/theme/design-system/font.js @@ -1,4 +1,4 @@ // Fix async font load document.fonts.ready.then(() => { - window.dispatchEvent(new Event('resize')); -}); \ No newline at end of file + window.dispatchEvent(new Event('resize')); +}); diff --git a/assets/js/theme/design-system/mainMenu.js b/assets/js/theme/design-system/mainMenu.js index feae6792b95bbd66c0ac12430956417c061efa16..1466410ac19cb3357e437539fefcd0deb924d1f2 100644 --- a/assets/js/theme/design-system/mainMenu.js +++ b/assets/js/theme/design-system/mainMenu.js @@ -1,4 +1,4 @@ -import { breakpoints, isMobile } from '../utils/breakpoints'; +import { isMobile } from '../utils/breakpoints'; import { a11yClick } from '../utils/a11y'; const CLASSES = { @@ -60,7 +60,7 @@ class MainMenu { resize () { document.documentElement.style.setProperty('--header-height', this.element.offsetHeight + 'px'); - document.documentElement.style.setProperty('--header-menu-max-height', (window.innerHeight - this.element.offsetHeight) + 'px'); + document.documentElement.style.setProperty('--header-menu-max-height', window.innerHeight - this.element.offsetHeight + 'px'); // is state changed ? if (this.state.isMobile === isMobile()) { @@ -79,7 +79,7 @@ class MainMenu { this.mainButton.setAttribute('aria-expanded', this.state.isOpened); this.menu.classList[classAction](CLASSES.mainMenuOpened); - // Close dropdown to avoid keeping overlay when mobile and menu closed + // Close dropdown to avoid keeping overlay when mobile and menu closed if (this.state.isMobile && !this.state.isOpened) { this.state.hasDropdownOpened = false; } diff --git a/assets/js/theme/design-system/modal.js b/assets/js/theme/design-system/modal.js index 53d6f67cc38553e0351a6064d4bb40701f927270..0c10d6b6ee092c77ee3bf4c4800830c045f9b869 100644 --- a/assets/js/theme/design-system/modal.js +++ b/assets/js/theme/design-system/modal.js @@ -4,7 +4,7 @@ const CLASSES = { }; class Modal { - constructor(button) { + constructor (button) { this.button = button; this.id = this.button.getAttribute('data-open-modal'); this.element = document.getElementById(this.id); @@ -21,7 +21,7 @@ class Modal { this.listen(); } - listen() { + listen () { this.button.addEventListener('click', () => { this.toggle(true); }); @@ -29,14 +29,14 @@ class Modal { this.closeButtons.forEach(button => { button.addEventListener('click', () => { this.toggle(false); - this.button.focus() + this.button.focus(); }); }); window.addEventListener('keydown', (event) => { if (event.keyCode === 27 || event.key === 'Escape') { this.toggle(false); - } else if (event.key === "Tab" && this.state.isOpened) { + } else if (event.key === 'Tab' && this.state.isOpened) { this.innerFocus(event); event.preventDefault(); } @@ -49,13 +49,12 @@ class Modal { }); } - innerFocus(event) { - const focusables = 'a, button, input, textarea, select, details, [tabindex], [contenteditable="true"]'; - const elements = this.element.querySelectorAll(focusables); - - const focusableInDialog = Array.from(elements).filter(element => element.tabIndex >= 0); - const firstFocusable = focusableInDialog[0]; - const lastFocusable = focusableInDialog.at(-1); + innerFocus (event) { + const focusables = 'a, button, input, textarea, select, details, [tabindex], [contenteditable="true"]', + elements = this.element.querySelectorAll(focusables), + focusableInDialog = Array.from(elements).filter(element => element.tabIndex >= 0), + firstFocusable = focusableInDialog[0], + lastFocusable = focusableInDialog.at(-1); if (!this.state.isOpened) { return; @@ -63,27 +62,25 @@ class Modal { if (!this.element.contains(event.target) && event.shiftKey) { lastFocusable.focus(); - } - else if (!this.element.contains(event.target)) { + } else if (!this.element.contains(event.target)) { firstFocusable.focus(); } firstFocusable.focus(); } - toggle(open = !this.state.isOpened) { + toggle (open = !this.state.isOpened) { this.state.isOpened = open; const classAction = this.state.isOpened ? 'add' : 'remove'; this.element.setAttribute('aria-hidden', !this.state.isOpened); document.documentElement.classList[classAction](CLASSES.modalOpened); } - } // Selectors (function () { - const modalButtons = document.querySelectorAll("[data-open-modal]"); + const modalButtons = document.querySelectorAll('[data-open-modal]'); modalButtons.forEach(button => { new Modal(button); - }) -})(); + }); +}()); diff --git a/assets/js/theme/design-system/notes.js b/assets/js/theme/design-system/notes.js index 4fdc3f4b9a65b8c26b3ace7d8fd075826cdb8957..1c80ea2f4db9c0466308bf3ec94dbb1bec22d286 100644 --- a/assets/js/theme/design-system/notes.js +++ b/assets/js/theme/design-system/notes.js @@ -3,32 +3,72 @@ class Note { this.note = note; this.active = false; this.call = this.note.querySelector('.note__call'); + this.content = this.note.querySelector('.note__content'); this.call.addEventListener('click', this.toggle.bind(this)); + + this.note.addEventListener('keydown', (event) => { + if (event.keyCode === 13 || event.key === 'Enter' || event.keyCode === 32 || event.key === 'Space') { + event.preventDefault(); + this.toggle(); + } + }); } + toggle () { if (this.active) { this.deactivate(); - } else { + } else { this.activate(); } } + activate () { this.deactivateAllNotes(); this.active = true; this.note.classList.add('note--active'); + this.content.removeAttribute('aria-hidden'); this.definePosition(); + this.a11yDisabling(); } + deactivate () { this.active = false; + this.content.setAttribute('aria-hidden', 'true'); this.note.classList.remove('note--active'); + this.removeA11yDisabling(); } + deactivateAllNotes () { window.notes.forEach(note => { note.deactivate(); - }) + }); } + + a11yDisabling () { + this.closeWithKeyboard = (event) => { + if (event.keyCode === 27 || event.key === 'Escape' || event.key === 'Tab' || event.keyCode === 9) { + this.deactivateAllNotes(); + this.call.focus(); + } + }; + + this.closeWithClick = (event) => { + if (!event.target.closest('.note--active')) { + this.deactivateAllNotes(); + } + }; + + window.addEventListener('keydown', this.closeWithKeyboard); + document.addEventListener('click', this.closeWithClick); + } + + removeA11yDisabling () { + window.removeEventListener('keydown', this.closeWithKeyboard); + document.removeEventListener('click', this.closeWithClick); + } + definePosition () { - let isOnTheLeftSide = this.note.offsetLeft < (window.innerWidth / 2) + let isOnTheLeftSide = this.note.offsetLeft < window.innerWidth / 2; if (isOnTheLeftSide) { this.note.classList.add('note--left'); this.note.classList.remove('note--right'); @@ -41,8 +81,8 @@ class Note { (function () { window.notes = []; - const notes = document.querySelectorAll(".note"); + const notes = document.querySelectorAll('.note'); notes.forEach(note => { window.notes.push(new Note(note)); - }) -})(); \ No newline at end of file + }); +}()); diff --git a/assets/js/theme/design-system/toc.js b/assets/js/theme/design-system/toc.js index 54f0f3da489840c07735385ec68a35b86641dd5b..e6db37102963c741595401a3cbf502568fa22d9d 100644 --- a/assets/js/theme/design-system/toc.js +++ b/assets/js/theme/design-system/toc.js @@ -1,143 +1,151 @@ import { isMobile } from '../utils/breakpoints'; const CLASSES = { - offcanvasOpened: 'has-offcanvas-opened', - linkActive: 'active', - isOpened: 'is-opened', - fullWidth: 'full-width', - offcanvas: 'offcanvas-toc' + offcanvasOpened: 'has-offcanvas-opened', + linkActive: 'active', + isOpened: 'is-opened', + fullWidth: 'full-width', + offcanvas: 'offcanvas-toc' }; class TableOfContents { - constructor(element) { - this.element = element; - this.content = this.element.querySelector('.toc-content'); - this.nav = this.element.querySelector('.toc'); - this.links = this.element.querySelectorAll('a'); - this.sections = document.querySelectorAll('.heading[id]'); - // TODO : handle sublinks update in toc - this.ctaTitle = document.querySelector('.toc-cta-title span'); - this.togglers = document.querySelectorAll('.toc-cta button, .toc-container button'); - this.state = { - opened: false, - currentId: null, - currentLink: 0, - isOffcanvas : this.isOffcanvas() + constructor (element) { + this.element = element; + this.content = this.element.querySelector('.toc-content'); + this.nav = this.element.querySelector('.toc'); + this.links = this.element.querySelectorAll('a'); + this.sections = document.querySelectorAll('.heading[id]'); + // TODO : handle sublinks update in toc + this.ctaTitle = document.querySelector('.toc-cta-title span'); + this.togglers = document.querySelectorAll('.toc-cta button, .toc-container button'); + this.state = { + opened: false, + currentId: null, + currentLink: 0, + isOffcanvas: this.isOffcanvas() + }; + this.listen(); + + if (this.state.isOffcanvas) { + this.element.setAttribute('aria-hidden', true); + } } - this.listen(); - if (this.state.isOffcanvas) { - this.element.setAttribute("aria-hidden", true); + isOffcanvas () { + return isMobile() || document.body.classList.contains(CLASSES.fullWidth) || document.body.classList.contains(CLASSES.offcanvas); } - } - isOffcanvas() { - return isMobile() || document.body.classList.contains(CLASSES.fullWidth) || document.body.classList.contains(CLASSES.offcanvas); - } - listen() { - window.addEventListener('scroll', this.update.bind(this), false); - - this.togglers.forEach(toggler => { - toggler.addEventListener('click', () => { - this.toggle(); - }); - }) - - this.links.forEach(links => { - links.addEventListener('click', () => { - this.toggle(false); - }); - }) - - window.addEventListener('click', (event) => { - if (event.target === document.body) { - this.toggle(false); - } - }); - - window.addEventListener('keydown', (event) => { - if (event.keyCode === 27 || event.key === 'Escape') { - this.toggle(false); - } - }); - } - toggle(open) { - this.state.opened = typeof open !== 'undefined' ? open : !this.state.opened; - const classAction = this.state.opened ? 'add' : 'remove'; - const transitionDuration = this.state.opened ? 0 : this.getTransitionDuration(); - - // TODO: refacto timeout and css transition - setTimeout(() => { - this.element.setAttribute("aria-hidden", !this.state.opened); - }, transitionDuration * 1000); - - setTimeout(() => { - this.element.classList[classAction](CLASSES.isOpened); - }, 50) - - document.documentElement.classList[classAction](CLASSES.offcanvasOpened); - } - getTransitionDuration() { - let transitionDuration = getComputedStyle(this.element).getPropertyValue('--toc-transition-duration'); - transitionDuration = parseFloat(transitionDuration.replace('s', '')) - return transitionDuration; - } - update() { - const scroll = document.documentElement.scrollTop || document.body.scrollTop; - let id = null; - this.sections.forEach(section => { - if (this.getAbsoluteOffsetTop(section) <= scroll + window.innerHeight/2) { - id = section.id; - } - }); - - if (id && id !== this.state.currentId) { - this.activateLink(id); + + listen () { + window.addEventListener('scroll', this.update.bind(this), false); + + this.togglers.forEach(toggler => { + toggler.addEventListener('click', () => { + this.toggle(); + }); + }); + + this.links.forEach(links => { + links.addEventListener('click', () => { + this.toggle(false); + }); + }); + + window.addEventListener('click', (event) => { + if (event.target === document.body) { + this.toggle(false); + } + }); + + window.addEventListener('keydown', (event) => { + if (event.keyCode === 27 || event.key === 'Escape') { + this.toggle(false); + } + }); } - this.updateScrollspy(scroll); - } - getAbsoluteOffsetTop(element) { - let top = 0; - do { - top += element.offsetTop || 0; - element = element.offsetParent; - } while(element); - return top - } - activateLink(id) { - const currentLink = this.element.querySelector(`[href*="${ id }"]`); - - this.links.forEach((link, index) => { - if (link == currentLink) { - link.classList.add(CLASSES.linkActive); - this.updateCtaTitle(link); - this.state.id = id; - this.state.currentLink = link; - } else { - link.classList.remove(CLASSES.linkActive) - } - }); - } - updateCtaTitle(link) { - if (isMobile()) { - this.ctaTitle.innerText = link.innerText; - } else { - this.ctaTitle.innerText = this.ctaTitle.getAttribute('data-default'); + + toggle (open) { + this.state.opened = typeof open !== 'undefined' ? open : !this.state.opened; + const classAction = this.state.opened ? 'add' : 'remove', + transitionDuration = this.state.opened ? 0 : this.getTransitionDuration(); + + // TODO: refacto timeout and css transition + setTimeout(() => { + this.element.setAttribute('aria-hidden', !this.state.opened); + }, transitionDuration * 1000); + + setTimeout(() => { + this.element.classList[classAction](CLASSES.isOpened); + }, 50); + + document.documentElement.classList[classAction](CLASSES.offcanvasOpened); } - } - updateScrollspy(scroll) { - const container = this.state.isOffcanvas ? this.nav : this.content; - if (this.state.currentLink && scroll > window.innerHeight) { - let progress = (this.getAbsoluteOffsetTop(this.state.currentLink) - container.offsetHeight/2); - progress = this.state.isOffcanvas ? progress : progress - scroll; - container.scrollTo({ - top: progress - }) + + getTransitionDuration () { + let transitionDuration = getComputedStyle(this.element).getPropertyValue('--toc-transition-duration'); + transitionDuration = parseFloat(transitionDuration.replace('s', '')) + return transitionDuration; + } + + update () { + const scroll = document.documentElement.scrollTop || document.body.scrollTop; + let id = null; + this.sections.forEach(section => { + if (this.getAbsoluteOffsetTop(section) <= scroll + window.innerHeight / 2) { + id = section.id; + } + }); + + if (id && id !== this.state.currentId) { + this.activateLink(id); + } + this.updateScrollspy(scroll); + } + + getAbsoluteOffsetTop (element) { + let top = 0; + do { + top += element.offsetTop || 0; + element = element.offsetParent; + } while (element); + return top; + } + + activateLink (id) { + const currentLink = this.element.querySelector(`[href*='${id}']`); + + this.links.forEach((link) => { + if (link === currentLink) { + link.classList.add(CLASSES.linkActive); + this.updateCtaTitle(link); + this.state.id = id; + this.state.currentLink = link; + } else { + link.classList.remove(CLASSES.linkActive) + } + }); + } + + updateCtaTitle (link) { + if (isMobile()) { + this.ctaTitle.innerText = link.innerText; + } else { + this.ctaTitle.innerText = this.ctaTitle.getAttribute('data-default'); + } + } + + updateScrollspy (scroll) { + const container = this.state.isOffcanvas ? this.nav : this.content; + if (this.state.currentLink && scroll > window.innerHeight) { + let progress = this.getAbsoluteOffsetTop(this.state.currentLink) - container.offsetHeight / 2; + progress = this.state.isOffcanvas ? progress : progress - scroll; + container.scrollTo({ + top: progress + }); + } } - } } const toc = document.querySelector('.toc-container'); - if (toc) { - new TableOfContents(toc); -} \ No newline at end of file + new TableOfContents(toc); +} diff --git a/assets/sass/_theme/design-system/notes.sass b/assets/sass/_theme/design-system/notes.sass index 5152aa947ad542b4408bffa1239d855af5fe1097..41be21a40ba936a369c2b672567506514972d55a 100644 --- a/assets/sass/_theme/design-system/notes.sass +++ b/assets/sass/_theme/design-system/notes.sass @@ -1,21 +1,23 @@ .note - margin-left: pxToRem($space-unit) + display: inline-block &__call - background: $color-background-alt + @include meta + background: var(--color-background-alt) border-radius: 4px cursor: pointer display: inline - line-height: 100% + line-height: inherit min-width: $minimum-accessible-size - min-height: $minimum-accessible-size + line-height: inherit + display: block padding-left: $spacing-2 padding-right: $spacing-2 padding-bottom: 0 text-align: center - @include meta + user-select: none &__content - background: $color-text - color: $color-background + background: var(--color-text) + color: var(--color-background) display: none max-width: 100vw padding: $spacing-3 @@ -25,8 +27,8 @@ @include small &--active .note__call - background: $color-text - color: $color-background + background: var(--color-text) + color: var(--color-background) border-bottom-left-radius: 0 border-bottom-right-radius: 0 .note__content