From 85d4b0d7077f3e78caef857bcd3bff569b56cbfa Mon Sep 17 00:00:00 2001 From: claraRigo <clararigaud0@gmail.com> Date: Thu, 26 Sep 2024 14:31:52 +0200 Subject: [PATCH] A11y composant carousel (#609) --- assets/js/theme/components/carousel.js | 2 +- assets/js/theme/components/carousel/arrows.js | 26 +++-- .../theme/components/carousel/autoplayer.js | 41 ++++---- .../js/theme/components/carousel/carousel.js | 99 ++++++++----------- .../js/theme/components/carousel/classes.js | 40 ++++---- assets/js/theme/components/carousel/config.js | 28 +++--- assets/js/theme/components/carousel/events.js | 12 +-- .../js/theme/components/carousel/manager.js | 76 ++++++++------ .../theme/components/carousel/pagination.js | 19 ++-- .../components/carousel/paginationButton.js | 32 +++--- assets/js/theme/components/carousel/slide.js | 32 ++++-- assets/js/theme/components/carousel/slider.js | 71 +++++++++---- assets/js/theme/components/carousel/utils.js | 8 +- assets/js/vendors/carousel.js | 69 ------------- .../partials/blocks/templates/carousel.html | 26 ++--- .../templates/gallery/carousel-image.html | 1 - .../blocks/templates/testimonials/single.html | 2 +- .../blocks/templates/timeline/horizontal.html | 2 +- layouts/partials/commons/image-figure.html | 8 +- layouts/partials/posts/post.html | 2 +- 20 files changed, 282 insertions(+), 314 deletions(-) delete mode 100644 assets/js/vendors/carousel.js diff --git a/assets/js/theme/components/carousel.js b/assets/js/theme/components/carousel.js index 974eb2c9..9d81b85d 100644 --- a/assets/js/theme/components/carousel.js +++ b/assets/js/theme/components/carousel.js @@ -12,4 +12,4 @@ import './carousel/manager'; import './carousel/paginationButton'; import './carousel/pagination'; import './carousel/slide'; -import './carousel/slider'; \ No newline at end of file +import './carousel/slider'; diff --git a/assets/js/theme/components/carousel/arrows.js b/assets/js/theme/components/carousel/arrows.js index 9c5b32c9..a40b3499 100644 --- a/assets/js/theme/components/carousel/arrows.js +++ b/assets/js/theme/components/carousel/arrows.js @@ -1,29 +1,27 @@ +/* eslint-disable no-underscore-dangle */ window.osuny = window.osuny || {}; window.osuny.carousel = window.osuny.carousel || {}; window.osuny.carousel.Arrows = function (element) { this.element = element; - if (!this.element) { return }; + if (!this.element) { + return; + } this._findElement = window.osuny.carousel.utils.findElement.bind(this); this._dispatchEvent = window.osuny.carousel.utils.dispatchEvent.bind(this); this.counter = this._findElement('arrowsCounter'); this.next = this._findElement('arrowsNext'); this.previous = this._findElement('arrowsPrevious'); - this.next.addEventListener( - "click", - this._onNext.bind(this) - ); - this.previous.addEventListener( - "click", - this._onPrevious.bind(this) - ); -} + this.next.addEventListener('click', this._onNext.bind(this)); + this.previous.addEventListener('click', this._onPrevious.bind(this)); +}; + window.osuny.carousel.Arrows.prototype = { update: function (index, total) { if (this.element) { - this.counter.innerHTML = (index + 1) + '/' + total; - this.next.disabled = index + 1 == total; - this.previous.disabled = index == 0; + this.counter.innerHTML = index + 1 + '/' + total; + this.next.disabled = index + 1 === total; + this.previous.disabled = index === 0; } }, _onNext: function () { @@ -32,4 +30,4 @@ window.osuny.carousel.Arrows.prototype = { _onPrevious: function () { this._dispatchEvent('arrowsPrevious'); } -} \ No newline at end of file +}; diff --git a/assets/js/theme/components/carousel/autoplayer.js b/assets/js/theme/components/carousel/autoplayer.js index a1302cfa..eb9ace5b 100644 --- a/assets/js/theme/components/carousel/autoplayer.js +++ b/assets/js/theme/components/carousel/autoplayer.js @@ -1,13 +1,16 @@ +/* eslint-disable no-underscore-dangle */ window.osuny = window.osuny || {}; window.osuny.carousel = window.osuny.carousel || {}; window.osuny.carousel.Autoplayer = function (element) { this.element = element; - if (!this.element) { return }; + if (!this.element) { + return; + } this.icons = { play: this.element.getElementsByClassName(window.osuny.carousel.classes.autoplayerToggleIconPlay).item(0), pause: this.element.getElementsByClassName(window.osuny.carousel.classes.autoplayerToggleIconPause).item(0) - } + }; this.ariaLiveElement = null; // Etat de l'autoplay this.enabled = false; @@ -21,17 +24,14 @@ window.osuny.carousel.Autoplayer = function (element) { // Bouton toggle this.classList = this.element.classList; this.classList.add(window.osuny.carousel.classes.autoplayerPaused); - this.element.addEventListener( - "click", - this._onClick.bind(this) - ); + this.element.addEventListener('click', this._onClick.bind(this)); this._dispatchEvent = window.osuny.carousel.utils.dispatchEvent.bind(this); -} +}; window.osuny.carousel.Autoplayer.prototype = { setInterval: function (interval) { this.interval = interval; }, - // enable() et disable() activent la boucle + // enable() et disable() activent la boucle enable: function () { this.enabled = true; this._loop(); @@ -46,17 +46,15 @@ window.osuny.carousel.Autoplayer.prototype = { pause: function () { this.paused = true; this._updateToggle(); - this._updateAriaLiveElement(); }, unpause: function () { this.paused = false; this.softPaused = false; this._updateToggle(); - this._updateAriaLiveElement(); }, // Les méthodes soft se produisent quand on passe sur le carousel avec la souris. // L'idée est de permettre aux personnes de lire une citation. - // Elles ne changent pas réellement l'état de l'autoplayer, + // Elles ne changent pas réellement l'état de l'autoplayer, // mais elles l'arrêtent temporairement. softPause: function () { this.softPaused = true; @@ -65,8 +63,10 @@ window.osuny.carousel.Autoplayer.prototype = { this.softPaused = false; }, _loop: function () { - if (!this.enabled) { return } var now = Date.now(); + if (!this.enabled) { + return; + } if (!this.paused && !this.softPaused) { this.elapsedSinceLastTrigger += now - this.lastLoopAt; this.progression = this.elapsedSinceLastTrigger / this.interval; @@ -90,13 +90,15 @@ window.osuny.carousel.Autoplayer.prototype = { this.lastLoopAt = Date.now(); }, _dispatchTrigger: function () { - this._dispatchEvent("autoplayerTrigger"); + this._dispatchEvent('autoplayerTrigger'); }, - _dispatchProgression(){ - this._dispatchEvent("autoplayerProgression", this.progression); + _dispatchProgression () { + this._dispatchEvent('autoplayerProgression', this.progression); }, _updateToggle: function () { - if (!this.element) { return } + if (!this.element) { + return; + } this.classList.remove(window.osuny.carousel.classes.autoplayerPlaying); this.classList.remove(window.osuny.carousel.classes.autoplayerPaused); if (this.paused) { @@ -107,11 +109,6 @@ window.osuny.carousel.Autoplayer.prototype = { this.icons.play.setAttribute('aria-hidden', !this.paused); this.icons.pause.setAttribute('aria-hidden', this.paused); }, - _updateAriaLiveElement: function() { - if (!this.ariaLiveElement) { return } - var state = this.paused ? "off" : "polite"; - this.ariaLiveElement.setAttribute("aria-live", state); - }, _onClick: function () { if (this.paused) { this.unpause(); @@ -120,4 +117,4 @@ window.osuny.carousel.Autoplayer.prototype = { this.pause(); } } -} \ No newline at end of file +}; diff --git a/assets/js/theme/components/carousel/carousel.js b/assets/js/theme/components/carousel/carousel.js index 9b931f74..17e38ff2 100644 --- a/assets/js/theme/components/carousel/carousel.js +++ b/assets/js/theme/components/carousel/carousel.js @@ -1,3 +1,4 @@ +/* eslint-disable no-underscore-dangle */ window.osuny = window.osuny || {}; window.osuny.carousel = window.osuny.carousel || {}; @@ -6,14 +7,15 @@ window.osuny.carousel.Carousel = function (element) { this.slides = { current: 0, total: 0 - } + }; this.state = { initialized: false, visible: false, - hasMouseOver: false + hasMouseOver: false, + hasFocus: false }; - this.windowResizeTimeout; - this.lastScrollXTimeout; + this.windowResizeTimeout = null; + this.lastScrollXTimeout = null; this._findElement = window.osuny.carousel.utils.findElement.bind(this); this._initializeConfig(); this._initializeSlider(); @@ -23,7 +25,7 @@ window.osuny.carousel.Carousel = function (element) { this._initializeMouseEvents(); this.showSlide(0); this.state.initialized = true; -} +}; window.osuny.carousel.Carousel.prototype = { next: function () { var index = this.slides.current + 1; @@ -31,16 +33,14 @@ window.osuny.carousel.Carousel.prototype = { index = 0; } this.showSlide(index); - this.arrows.next.focus(); }, previous: function () { var index = this.slides.current - 1; if (index < 0) { - // -1 parce que 0-indexed + // -1 parce que 0-indexed index = this.slides.total - 1; } this.showSlide(index); - this.arrows.previous.focus(); }, showSlide: function (index) { this.slides.current = index; @@ -61,7 +61,7 @@ window.osuny.carousel.Carousel.prototype = { this.slider.recompute(); }.bind(this), 200); }, - isInViewPort: function(){ + isInViewPort: function () { var boundingRect = this.element.getBoundingClientRect(), screenHeight = window.innerHeight || document.documentElement.clientHeight, elementBottomInViewport = boundingRect.bottom >= 0, @@ -78,80 +78,56 @@ window.osuny.carousel.Carousel.prototype = { this.config.loadOptions(this.element.dataset.carousel); }, _initializePagination: function () { - var paginationElement = this._findElement("pagination"); + var paginationElement = this._findElement('pagination'); this.pagination = new window.osuny.carousel.Pagination(paginationElement); if (paginationElement) { - paginationElement.addEventListener( - window.osuny.carousel.events.paginationButtonClicked, - this._onPaginationButtonClicked.bind(this) - ); + paginationElement.addEventListener(window.osuny.carousel.events.paginationButtonClicked, this._onPaginationButtonClicked.bind(this)); } }, - _initializeArrows: function () { - var arrowsElement = this._findElement("arrows"); + _initializeArrows: function () { + var arrowsElement = this._findElement('arrows'); this.arrows = new window.osuny.carousel.Arrows(arrowsElement); if (arrowsElement) { - arrowsElement.addEventListener( - window.osuny.carousel.events.arrowsNext, - this.next.bind(this) - ); - arrowsElement.addEventListener( - window.osuny.carousel.events.arrowsPrevious, - this.previous.bind(this) - ); + arrowsElement.addEventListener(window.osuny.carousel.events.arrowsNext, this.next.bind(this)); + arrowsElement.addEventListener(window.osuny.carousel.events.arrowsPrevious, this.previous.bind(this)); } }, _initializeSlider: function () { - var sliderElement = this._findElement("slider"); + var sliderElement = this._findElement('slider'); this.slider = new window.osuny.carousel.Slider(sliderElement); this.slides.total = this.slider.length(); - sliderElement.addEventListener( - "scroll", - this._onSliderScroll.bind(this) - ); + sliderElement.addEventListener('scroll', this._onSliderScroll.bind(this)); }, - _initializeAutoplayer(){ - this.autoplayerElement = this._findElement("autoplayerToggle"); + _initializeAutoplayer () { + this.autoplayerElement = this._findElement('autoplayerToggle'); this.autoplayer = new window.osuny.carousel.Autoplayer(this.autoplayerElement); this.autoplayer.setInterval(this.config.autoplayinterval); - this.autoplayer.ariaLiveElement = this._findElement("container"); + this.autoplayer.ariaLiveElement = this._findElement('container'); if (this.autoplayerElement) { - this.autoplayerElement.addEventListener( - window.osuny.carousel.events.autoplayerTrigger, - this.next.bind(this) - ); - this.autoplayerElement.addEventListener( - window.osuny.carousel.events.autoplayerProgression, - this._onAutoplayerProgression.bind(this) - ); + this.autoplayerElement.addEventListener(window.osuny.carousel.events.autoplayerTrigger, this.next.bind(this)); + this.autoplayerElement.addEventListener(window.osuny.carousel.events.autoplayerProgression, this._onAutoplayerProgression.bind(this)); if (this.config.autoplay) { this.autoplayer.enable(); } } }, - _pointerStart: function() { + _pointerStart: function () { this.autoplayer.softPause(); this.state.hasMouseOver = true; }, - _pointerEnd: function() { + _pointerEnd: function () { this.autoplayer.softUnpause(); this.state.hasMouseOver = false; }, - _initializeMouseEvents: function(){ - this.element.addEventListener( - "mouseenter", this._pointerStart.bind(this) - ); - this.element.addEventListener( - "touchstart", this._pointerStart.bind(this) - ); - this.element.addEventListener( - "mouseleave", this._pointerEnd.bind(this) - ); - this.element.addEventListener( - "touchend", this._pointerEnd.bind(this) - ); - }, - + _initializeMouseEvents: function () { + this.element.addEventListener('mouseenter', this._pointerStart.bind(this)); + this.element.addEventListener('touchstart', this._pointerStart.bind(this)); + this.element.addEventListener('mouseleave', this._pointerEnd.bind(this)); + this.element.addEventListener('touchend', this._pointerEnd.bind(this)); + this.element.addEventListener('focusin', function () { + this.autoplayer.pause(); + }.bind(this)); + }, _onAutoplayerProgression: function (event) { this.pagination.setProgression(event.value); }, @@ -163,7 +139,7 @@ window.osuny.carousel.Carousel.prototype = { this.autoplayer.softPause(); clearTimeout(this.lastScrollXTimeout); this.lastScrollXTimeout = setTimeout(function () { - if(!this.state.hasMouseOver){ + if (!this.state.hasMouseOver) { this.autoplayer.softUnpause(); } this._onSliderScrollend(); @@ -171,8 +147,11 @@ window.osuny.carousel.Carousel.prototype = { }, _onSliderScrollend: function () { var index = this.slider.currentSlideIndex(); - if (this.slides.current != index) { + if (this.state.hasFocus) { + this.slider.focusOnSlide(index); + } + if (this.slides.current !== index) { this.showSlide(index); } } -} \ No newline at end of file +}; diff --git a/assets/js/theme/components/carousel/classes.js b/assets/js/theme/components/carousel/classes.js index 351e7aad..45c4792d 100644 --- a/assets/js/theme/components/carousel/classes.js +++ b/assets/js/theme/components/carousel/classes.js @@ -2,23 +2,23 @@ window.osuny = window.osuny || {}; window.osuny.carousel = window.osuny.carousel || {}; window.osuny.carousel.classes = { - arrows: "carousel__arrows", - arrowsCounter: "counter", - arrowsNext: "arrow-next", - arrowsPrevious: "arrow-prev", - autoplayerPaused: "toggle__paused", - autoplayerPlaying: "toggle__playing", - autoplayerToggle: "toggle", - autoplayerToggleIconPause: "pause", - autoplayerToggleIconPlay: "play", - carousel: "js-carousel", - container: "carousel__container", - pagination: "carousel__pagination__tabcontainer", - paginationPage: "carousel__pagination__page", - slider: "carousel__slider", - slideIsBefore: "is-before", - slideIsPrevious: "is-previous", - slideIsCurrent: "is-current", - slideIsNext: "is-next", - slideIsAfter: "is-after", -} \ No newline at end of file + arrows: 'carousel__arrows', + arrowsCounter: 'counter', + arrowsNext: 'arrow-next', + arrowsPrevious: 'arrow-prev', + autoplayerPaused: 'toggle__paused', + autoplayerPlaying: 'toggle__playing', + autoplayerToggle: 'toggle', + autoplayerToggleIconPause: 'pause', + autoplayerToggleIconPlay: 'play', + carousel: 'js-carousel', + container: 'carousel__container', + pagination: 'carousel__pagination__tabcontainer', + paginationPage: 'carousel__pagination__page', + slider: 'carousel__slider', + slideIsBefore: 'is-before', + slideIsPrevious: 'is-previous', + slideIsCurrent: 'is-current', + slideIsNext: 'is-next', + slideIsAfter: 'is-after' +}; diff --git a/assets/js/theme/components/carousel/config.js b/assets/js/theme/components/carousel/config.js index c411931a..334bb3fb 100644 --- a/assets/js/theme/components/carousel/config.js +++ b/assets/js/theme/components/carousel/config.js @@ -8,30 +8,32 @@ window.osuny.carousel.Config = function (instance) { // L'autoplay est-il activé ou pas ? this.autoplay = false; - // Pagination sous forme de tabulation + // Pagination sous forme de tabulation this.pagination = true; - // Controle du defilement avec les fleches + // Controle du defilement avec les fleches this.arrows = false; // Durée d'affichage d'un slide en cas d'autoplay this.autoplayinterval = 3000; -} +}; window.osuny.carousel.Config.prototype = { valuesInOptions: [ - "autoplay", - "arrows", - "pagination", - "autoplayinterval" + 'autoplay', + 'arrows', + 'pagination', + 'autoplayinterval' ], loadOptions: function (data) { - var options = JSON.parse(data); - for (var i = 0; i <= this.valuesInOptions.length; i += 1) { - var value = this.valuesInOptions[i]; - if (options[value] !== undefined) { + var options = JSON.parse(data), + i = 0, + value = null; + for (i = 0; i <= this.valuesInOptions.length; i += 1) { + value = this.valuesInOptions[i]; + if (options[value]) { this[value] = options[value]; } - }; + } } -} \ No newline at end of file +}; diff --git a/assets/js/theme/components/carousel/events.js b/assets/js/theme/components/carousel/events.js index c73b1c3e..bdeabb92 100644 --- a/assets/js/theme/components/carousel/events.js +++ b/assets/js/theme/components/carousel/events.js @@ -2,9 +2,9 @@ window.osuny = window.osuny || {}; window.osuny.carousel = window.osuny.carousel || {}; window.osuny.carousel.events = { - autoplayerProgression: "osuny.carousel.autoplayer.progression", - autoplayerTrigger: "osuny.carousel.autoplayer.trigger", - arrowsNext: "osuny.carousel.arrows.next", - arrowsPrevious: "osuny.carousel.arrows.previous", - paginationButtonClicked: "osuny.carousel.pagination.buttonClicked" -} \ No newline at end of file + autoplayerProgression: 'osuny.carousel.autoplayer.progression', + autoplayerTrigger: 'osuny.carousel.autoplayer.trigger', + arrowsNext: 'osuny.carousel.arrows.next', + arrowsPrevious: 'osuny.carousel.arrows.previous', + paginationButtonClicked: 'osuny.carousel.pagination.buttonClicked' +}; diff --git a/assets/js/theme/components/carousel/manager.js b/assets/js/theme/components/carousel/manager.js index 9390834a..74cfef32 100644 --- a/assets/js/theme/components/carousel/manager.js +++ b/assets/js/theme/components/carousel/manager.js @@ -1,3 +1,4 @@ +/* eslint-disable no-underscore-dangle */ window.osuny = window.osuny || {}; window.osuny.carousel = window.osuny.carousel || {}; @@ -18,26 +19,30 @@ window.osuny.carousel.manager = { } }, _createCarousels: function () { + var element, + carousel, + i, + carouselId = ''; this.elements = document.getElementsByClassName(window.osuny.carousel.classes.carousel); - for (var i = 0; i < this.elements.length; i += 1) { - var element = this.elements[i], - carousel = new window.osuny.carousel.Carousel(element); + for (i = 0; i < this.elements.length; i += 1) { + element = this.elements[i]; + carouselId = window.osuny.carousel.classes.carousel + '-' + i; + element.setAttribute('id', carouselId); + carousel = new window.osuny.carousel.Carousel(element); + this._setCarouselAriaDescribedBy(element); this.carousels.push(carousel); } }, + _setCarouselAriaDescribedBy (carousel) { + var id = carousel.getAttribute('id'); + carousel.querySelectorAll('button').forEach(function (child) { + child.setAttribute('aria-describedby', String(id)); + }.bind(this)); + }, _initializeListeners: function () { - window.addEventListener( - "resize", - this._resize.bind(this) - ); - window.addEventListener( - "scroll", - this._findCarouselsInViewport.bind(this) - ); - window.addEventListener( - "keydown", - this._onKeyPress.bind(this) - ); + window.addEventListener('resize', this._resize.bind(this)); + window.addEventListener('scroll', this._findCarouselsInViewport.bind(this)); + window.addEventListener('keydown', this._onKeyPress.bind(this)); }, _resize: function () { this._computeWindowCenterY(); @@ -45,51 +50,62 @@ window.osuny.carousel.manager = { carousel.resize(); }); }, - _computeWindowCenterY: function(){ + _computeWindowCenterY: function () { this.windowCenterY = (window.innerHeight || document.documentElement.clientHeight) / 2; }, _findCarouselsInViewport: function () { + var i = 0, + carousel = null; this.carouselsInViewport = []; - for (var i = 0; i < this.carousels.length; i += 1) { - var carousel = this.carousels[i]; + for (i = 0; i < this.carousels.length; i += 1) { + carousel = this.carousels[i]; if (carousel.isInViewPort()) { carousel.unpause(); this.carouselsInViewport.push(carousel); } else { carousel.pause(); } - }; + } this.focusedCarousel = this._findBestCarouselFocusCandidate(); }, _findBestCarouselFocusCandidate: function () { // On démarre avec la plus grande distance possible var distance = window.innerHeight, - bestCandidate = null; - for (var i = 0; i < this.carousels.length; i += 1) { - var carousel = this.carousels[i]; - var currentDistanceToCenter = Math.abs(carousel.getCenterPositionY() - this.windowCenterY); + bestCandidate = null, + i = 0, + carousel, + currentDistanceToCenter; + for (i = 0; i < this.carousels.length; i += 1) { + carousel = this.carousels[i]; + carousel.state.hasFocus = false; + currentDistanceToCenter = Math.abs(carousel.getCenterPositionY() - this.windowCenterY); if (currentDistanceToCenter < distance) { distance = currentDistanceToCenter; bestCandidate = carousel; } - }; + } + if (bestCandidate) { + bestCandidate.state.hasFocus = true; + } return bestCandidate; }, _onKeyPress: function (e) { if (this.focusedCarousel) { - if (e.key == 'ArrowLeft') { this.focusedCarousel.previous() } - else if (e.key == 'ArrowRight') { this.focusedCarousel.next() } + if (e.key === 'ArrowLeft') { + this.focusedCarousel.previous(); + } else if (e.key === 'ArrowRight') { + this.focusedCarousel.next(); + } } }, invoke: function () { - "use strict"; return { initialize: this.initialize.bind(this), - carousels: this.carousels, + carousels: this.carousels }; } }.invoke(); -window.addEventListener("load", function () { +window.addEventListener('load', function () { window.osuny.carousel.manager.initialize(); -}); \ No newline at end of file +}); diff --git a/assets/js/theme/components/carousel/pagination.js b/assets/js/theme/components/carousel/pagination.js index 99386d8a..c28ac3c7 100644 --- a/assets/js/theme/components/carousel/pagination.js +++ b/assets/js/theme/components/carousel/pagination.js @@ -2,22 +2,28 @@ window.osuny = window.osuny || {}; window.osuny.carousel = window.osuny.carousel || {}; window.osuny.carousel.Pagination = function (element) { + var index = 0, + button, + buttonElement; this.element = element; this.buttons = []; - if (!this.element) { return }; + if (!this.element) { + return; + } this.buttonElements = this.element.getElementsByClassName(window.osuny.carousel.classes.paginationPage); - for (var index = 0; index < this.buttonElements.length; index += 1) { - var buttonElement = this.buttonElements[index], - button = new window.osuny.carousel.PaginationButton(buttonElement, index, this.element); + for (index = 0; index < this.buttonElements.length; index += 1) { + buttonElement = this.buttonElements[index]; + button = new window.osuny.carousel.PaginationButton(buttonElement, index, this.element); this.buttons.push(button); } this.currentButton = this.buttons[0]; -} +}; window.osuny.carousel.Pagination.prototype = { selectButton: function (index) { if (this.element) { this.currentButton = this.buttons[index]; this.currentButton.select(); + this.currentButton.setAriaCurrent(true); } }, setProgression: function (progression) { @@ -27,7 +33,8 @@ window.osuny.carousel.Pagination.prototype = { }, unselectAllButtons: function () { this.buttons.forEach(function (button) { + button.setAriaCurrent(false); button.unselect(); }); } -} +}; diff --git a/assets/js/theme/components/carousel/paginationButton.js b/assets/js/theme/components/carousel/paginationButton.js index a0b60b3d..012e3d21 100644 --- a/assets/js/theme/components/carousel/paginationButton.js +++ b/assets/js/theme/components/carousel/paginationButton.js @@ -1,41 +1,37 @@ +/* eslint-disable no-underscore-dangle */ window.osuny = window.osuny || {}; window.osuny.carousel = window.osuny.carousel || {}; -window.osuny.carousel.PaginationButton = function PaginationButton(element, index, pagination) { +window.osuny.carousel.PaginationButton = function PaginationButton (element, index, pagination) { this.element = element; this.index = index; this.pagination = pagination; - this.progressBar = this.element.querySelector("i"); - this._setAria(); + this.progressBar = this.element.querySelector('i'); this.setProgression(0); - this.element.addEventListener( - "click", - this._onClick.bind(this) - ); -} + this.element.addEventListener('click', this._onClick.bind(this)); +}; window.osuny.carousel.PaginationButton.prototype = { setProgression: function (progression) { + var percent; this.progression = progression; - var percent = String(this.progression * 100) + "%"; - this.progressBar.style.setProperty("width", percent); + percent = String(this.progression * 100) + '%'; + this.progressBar.style.setProperty('width', percent); }, select: function () { this.setProgression(1); - this.element.setAttribute("aria-selected", "true"); + this.element.setAttribute('aria-selected', 'true'); }, unselect: function () { this.setProgression(0); - this.element.setAttribute("aria-selected", "false"); - }, - _setAria: function () { - var ariaLabel = this.element.getAttribute("aria-label"), - ariaNewLabel = ariaLabel.replace("%s", this.index); - this.element.setAttribute("aria-label", ariaNewLabel); + this.element.setAttribute('aria-selected', 'false'); }, _onClick: function () { var event = new Event(window.osuny.carousel.events.paginationButtonClicked); event.index = this.index; this.pagination.dispatchEvent(event); + }, + setAriaCurrent (current) { + this.element.setAttribute('aria-current', String(current)); } -} \ No newline at end of file +}; diff --git a/assets/js/theme/components/carousel/slide.js b/assets/js/theme/components/carousel/slide.js index ce6a2940..26b4683a 100644 --- a/assets/js/theme/components/carousel/slide.js +++ b/assets/js/theme/components/carousel/slide.js @@ -1,3 +1,4 @@ +/* eslint-disable no-underscore-dangle */ window.osuny = window.osuny || {}; window.osuny.carousel = window.osuny.carousel || {}; @@ -9,35 +10,48 @@ window.osuny.carousel.Slide = function (slider, container, index) { this.computedStyle = null; this.width = 0; this.computeWidth(); -} +}; window.osuny.carousel.Slide.prototype = { computeWidth: function () { this.computedStyle = getComputedStyle(this.container); - this.width = this.container.offsetWidth + - parseFloat(this.computedStyle.marginLeft) + + this.width = this.container.offsetWidth + + parseFloat(this.computedStyle.marginLeft) + parseFloat(this.computedStyle.marginRight); }, - setClasses() { + setClasses () { this._setState(this._isBefore(), window.osuny.carousel.classes.slideIsBefore); this._setState(this._isPrevious(), window.osuny.carousel.classes.slideIsPrevious); this._setState(this._isCurrent(), window.osuny.carousel.classes.slideIsCurrent); this._setState(this._isNext(), window.osuny.carousel.classes.slideIsNext); this._setState(this._isAfter(), window.osuny.carousel.classes.slideIsAfter); }, + setAriaHidden (hidden) { + this.container.setAttribute('aria-hidden', String(hidden)); + }, + setFocusBehavior (visible) { + if (visible) { + this.setTabIndex(0); + } else { + this.setTabIndex(-1); + } + }, + setTabIndex (n) { + this.container.setAttribute('tabindex', String(n)); + }, _isBefore: function () { return this.index < this.slider.index; }, _isPrevious: function () { - return this.index == this.slider.index - 1; + return this.index === this.slider.index - 1; }, _isCurrent: function () { - return this.index == this.slider.index; + return this.index === this.slider.index; }, _isNext: function () { - return this.index == this.slider.index + 1; + return this.index === this.slider.index + 1; }, - _isAfter: function () { + _isAfter: function () { return this.index > this.slider.index; }, _setState: function (active, className) { @@ -47,4 +61,4 @@ window.osuny.carousel.Slide.prototype = { this.classList.remove(className); } } -} +}; diff --git a/assets/js/theme/components/carousel/slider.js b/assets/js/theme/components/carousel/slider.js index 7ecca912..6052a1a8 100644 --- a/assets/js/theme/components/carousel/slider.js +++ b/assets/js/theme/components/carousel/slider.js @@ -1,34 +1,44 @@ +/* eslint-disable no-underscore-dangle */ window.osuny = window.osuny || {}; window.osuny.carousel = window.osuny.carousel || {}; -window.osuny.carousel.Slider = function Slider(element) { +window.osuny.carousel.Slider = function Slider (element) { + var slidesContainers = null, + slideContainer = null, + i; this.element = element; - this._findElement = window.osuny.carousel.utils.findElement.bind(this), - this.container = this._findElement("container"); + this.containerWidth = this.element.getBoundingClientRect().width; + this._findElement = window.osuny.carousel.utils.findElement.bind(this); + this.container = this._findElement('container'); this.index = 0; this.slides = []; this.deltaPosition = 0; - this.drag = null; - var slidesContainers = this.container.children; - for (var i = 0; i < slidesContainers.length; i += 1) { - var slideContainer = slidesContainers.item(i); + slidesContainers = this.container.children; + for (i = 0; i < slidesContainers.length; i += 1) { + slideContainer = slidesContainers.item(i); this.slides.push(new window.osuny.carousel.Slide(this, slideContainer, i)); } this.showSlide(this.index); -} + this.element.addEventListener('scroll', function () { + this._updateSlidesVisibilities(); + }.bind(this)); +}; window.osuny.carousel.Slider.prototype = { showSlide: function (index) { + var behavior = 'smooth'; this.index = index; - var behavior = "smooth"; this.element.scrollTo({ top: 0, left: this._slidePosition(index), behavior: behavior }); + // this.slides[index].container.focus(); // forcer le focus comme ca fonctionnne mais annule l'effet smooth du scroll this._updateSlidesClasses(); + this._updateSlidesVisibilities(); }, - recompute: function(){ - this.slides.forEach(function(slide) { + recompute: function () { + this.containerWidth = this.element.getBoundingClientRect().width; + this.slides.forEach(function (slide) { slide.computeWidth(); }); this.showSlide(this.index); @@ -36,12 +46,17 @@ window.osuny.carousel.Slider.prototype = { length: function () { return this.slides.length; }, + focusOnSlide: function (index) { + this.slides[index].container.focus(); + }, currentSlideIndex: function () { - var currentWidth = 0; // Le seuil permet d'éviter des erreurs d'arrondis qui causent un retour en slide 1, par étapes - var threshold = 20; - for (var index = 0; index < this.slides.length; index += 1) { - var slide = this.slides[index]; + var currentWidth = 0, + threshold = 20, + index, + slide; + for (index = 0; index < this.slides.length; index += 1) { + slide = this.slides[index]; currentWidth += slide.width; if (currentWidth > this.element.scrollLeft + threshold) { return index; @@ -50,15 +65,33 @@ window.osuny.carousel.Slider.prototype = { return 0; }, _slidePosition: function (index) { - var position = 0; - for (var i = 0; i < index; i += 1) { + var position = 0, + i; + for (i = 0; i < index; i += 1) { position += this.slides[i].width; } return position; }, _updateSlidesClasses: function () { - this.slides.forEach(function(slide) { + this.slides.forEach(function (slide) { slide.setClasses(); }); + }, + _updateSlidesVisibilities: function () { + var slideVisible = false; + this.slides.forEach(function (slide, i) { + slideVisible = this._slideIsVisible(i); + slide.setAriaHidden(!slideVisible); + slide.setFocusBehavior(slideVisible); + }.bind(this)); + }, + _slideIsVisible: function (index) { + var slidePos = { + min: null, + max: null + }; + slidePos.min = this._slidePosition(index) - this.element.scrollLeft; + slidePos.max = slidePos.min + this.slides[index].width; + return slidePos.min >= -2 && slidePos.max <= this.containerWidth + 2; } -} \ No newline at end of file +}; diff --git a/assets/js/theme/components/carousel/utils.js b/assets/js/theme/components/carousel/utils.js index 73ea31ed..c6768fd4 100644 --- a/assets/js/theme/components/carousel/utils.js +++ b/assets/js/theme/components/carousel/utils.js @@ -3,14 +3,14 @@ window.osuny.carousel = window.osuny.carousel || {}; window.osuny.carousel.utils = { // Méhodes ajoutées comme des traits (décorateur) aux objets qui en ont besoin - findElement: function(classKey) { + findElement: function (classKey) { var className = window.osuny.carousel.classes[classKey]; return this.element.getElementsByClassName(className).item(0); }, dispatchEvent: function (eventKey, value = null) { - var eventName = window.osuny.carousel.events[eventKey]; - var event = new Event(eventName); + var eventName = window.osuny.carousel.events[eventKey], + event = new Event(eventName); event.value = value; this.element.dispatchEvent(event); } -} \ No newline at end of file +}; diff --git a/assets/js/vendors/carousel.js b/assets/js/vendors/carousel.js deleted file mode 100644 index a983e5fc..00000000 --- a/assets/js/vendors/carousel.js +++ /dev/null @@ -1,69 +0,0 @@ -import Splide from '@splidejs/splide'; - -let siteLang = document.querySelector('html').getAttribute('lang'); - -if (siteLang == "fr") { - Splide.defaults = { - i18n: { - first: 'Aller au premier slide', - last: 'Aller au dernier slide', - next: 'Slide suivant', - pageX: 'Aller à la page %s', - pause: 'Mettre en pause le carousel', - play: 'Démarrer le carousel', - prev: 'Slide précedent', - slideX: 'Aller au slide %s' - } - }; -} - -class Carousel { - constructor (element) { - this.element = element; - this.init(); - } - - init () { - this.splide = new Splide(this.element).mount(); - const toggleButton = this.splide.root.querySelector('.splide__toggle'), - stepButtons = this.splide.root.querySelectorAll('.splide__pagination button'); - - this.listen(); - - if (toggleButton) { - stepButtons.forEach((stepButton) => { - stepButton.innerHTML = '<i></i>'; - }); - - this.splide.on('autoplay:playing', (rate) => { - const activeStepButton = this.splide.root.querySelector('.splide__pagination .is-active i'); - activeStepButton.style.width = rate * 100 + '%'; - }); - - stepButtons.forEach((stepButton) => { - const progressBar = stepButton.querySelector('i'); - stepButton.addEventListener('click', () => { - progressBar.style.removeProperty("width"); - }) - }); - } - } - - listen() { - this.splide.on('move', () => { - this.splide.root.classList.add('is-moving') - }); - - this.splide.on('moved', () => { - this.splide.root.classList.remove('is-moving') - }); - } -} - -(function () { - var splides = document.getElementsByClassName('splide'), - i = 0; - for (i = 0; i < splides.length; i+=1) { - new Carousel(splides[i]); - } -}()); diff --git a/layouts/partials/blocks/templates/carousel.html b/layouts/partials/blocks/templates/carousel.html index def12a89..0df5b0cb 100644 --- a/layouts/partials/blocks/templates/carousel.html +++ b/layouts/partials/blocks/templates/carousel.html @@ -2,13 +2,9 @@ {{ $heading_tag := .heading_tag | default 2 }} {{ $block_options := .block_options | default "" }} -<div class="carousel js-carousel" aria-roledescription="carousel" data-carousel="{{ .options | encoding.Jsonify }}"> +<div class="carousel js-carousel" data-carousel="{{ .options | encoding.Jsonify }}"> <div class="carousel__slider"> - <div id="carousel-items" class="carousel__container" aria-live="off" aria-atomic="false"> - {{ $slideRole := "group" }} - {{ if .options.pagination }} - {{ $slideRole = "tabpanel" }} - {{ end }} + <div id="carousel-items" class="carousel__container"> {{ $totalSlides := len .content }} {{ range $slideindex, $content := .content }} {{ with $content }} @@ -16,7 +12,6 @@ "is_carousel" true "index" (add $slideindex 1) "params" . - "role" $slideRole "heading_tag" $heading_tag "options" $block_options "total" $totalSlides @@ -27,10 +22,11 @@ </div> <div class="carousel__pagination"> {{ if .options.pagination }} - <ul class="carousel__pagination__tabcontainer {{ if .options.autoplay }} has_toggle {{ end }}" role="tablist"> + <ul class="carousel__pagination__tabcontainer {{ if .options.autoplay }} has_toggle {{ end }}"> {{ range $slideindex, $content := .content }} <li> - <button id="carousel-tab-{{$slideindex}}" role="tab" aria-controls="carousel-item-{{$slideindex}}" aria-selected="false" class="carousel__pagination__page" type="button" aria-label='{{ safeHTML (i18n "commons.carousel.slideX") }}'> + <button id="carousel-tab-{{$slideindex}}" aria-controls="carousel-item-{{$slideindex}}" aria-selected="false" class="carousel__pagination__page" type="button"> + <span class="sr-only">{{ safeHTML (replace (i18n "commons.carousel.slideX") "%s" $slideindex)}}</span> <i></i> </button> </li> @@ -40,20 +36,24 @@ {{ if .options.autoplay }} <button class="toggle"> - <span class="play" aria-label='{{ safeHTML (i18n "commons.carousel.play") }}'></span> - <span class="pause" aria-label='{{ safeHTML (i18n "commons.carousel.pause") }}'></span> + <span class="play"> + <span class="sr-only">{{ safeHTML (i18n "commons.carousel.play") }}</span> + </span> + <span class="pause"> + <span class="sr-only">{{ safeHTML (i18n "commons.carousel.pause") }}</span> + </span> </button> {{ end }} </div> {{ if .options.arrows }} <div class="carousel__arrows"> - <button class="arrow-prev" aria-controls="carousel-items" type="button" aria-label="{{ safeHTML (i18n "commons.carousel.prev")}}"> + <button class="arrow-prev" aria-controls="carousel-items" type="button"> <span class="sr-only">{{ safeHTML (i18n "commons.carousel.prev") }}</span> </button> <p class="counter"></p> - <button class="arrow-next" aria-controls="carousel-items" type="button" aria-label="{{ safeHTML (i18n "commons.carousel.next") }}"> + <button class="arrow-next" aria-controls="carousel-items" type="button"> <span class="sr-only">{{ safeHTML (i18n "commons.carousel.next") }}</span> </button> </div> diff --git a/layouts/partials/blocks/templates/gallery/carousel-image.html b/layouts/partials/blocks/templates/gallery/carousel-image.html index efcb61f3..9d4bb8b3 100644 --- a/layouts/partials/blocks/templates/gallery/carousel-image.html +++ b/layouts/partials/blocks/templates/gallery/carousel-image.html @@ -3,6 +3,5 @@ "sizes" site.Params.image_sizes.blocks.gallery.carousel "is_carousel" true "index" .index - "role" "tabpanel" ) }} \ No newline at end of file diff --git a/layouts/partials/blocks/templates/testimonials/single.html b/layouts/partials/blocks/templates/testimonials/single.html index 620628dd..8b7197b7 100644 --- a/layouts/partials/blocks/templates/testimonials/single.html +++ b/layouts/partials/blocks/templates/testimonials/single.html @@ -1,4 +1,4 @@ -<figure role="figure" {{- if .is_carousel }} id="carousel-item-{{.index}}" role="{{.role}}" aria-roledescription="slide" class="carousel__slide" aria-label="{{.index}} / {{ .total }}" {{ end }} class="{{ if .is_carousel }}carousel__slide{{ end }} {{ if .params.photo }}with-picture{{ end }}"> +<figure {{- if .is_carousel }} id="carousel-item-{{.index}}" class="carousel__slide" {{ end }} class="{{ if .is_carousel }}carousel__slide{{ end }} {{ if .params.photo }}with-picture{{ end }}"> {{ with .params }} {{ $is_long := gt (len .text) 150 }} <blockquote {{- if $is_long }} class="is-long" {{- end }}> diff --git a/layouts/partials/blocks/templates/timeline/horizontal.html b/layouts/partials/blocks/templates/timeline/horizontal.html index 6dd09cb6..9233405c 100644 --- a/layouts/partials/blocks/templates/timeline/horizontal.html +++ b/layouts/partials/blocks/templates/timeline/horizontal.html @@ -3,7 +3,7 @@ {{ $role := .role }} {{ with .params }} - <article class="timeline-event carousel__slide" id="carousel-item-{{$index}}" role="{{$role}}" aria-roledescription="slide" class="carousel__slide" aria-label="item-{{$index}}" itemscope itemtype="https://schema.org/Article"> + <article class="timeline-event carousel__slide" id="carousel-item-{{$index}}" class="carousel__slide" itemscope itemtype="https://schema.org/Article"> {{ $heading_tag.open -}} {{ .title | safeHTML }} {{ $heading_tag.close -}} diff --git a/layouts/partials/commons/image-figure.html b/layouts/partials/commons/image-figure.html index ba4ce74a..e6fab6f6 100644 --- a/layouts/partials/commons/image-figure.html +++ b/layouts/partials/commons/image-figure.html @@ -1,9 +1,7 @@ {{ if .image.id }} - {{ $index := .index }} {{ $sizes := .sizes }} {{ $lazy := default true .lazy }} - {{ $role := (or .role "figure")}} {{ $is_carousel := .is_carousel }} {{ $is_lightbox := false }} {{ $image := partial "GetMedia" .image.id }} @@ -15,12 +13,10 @@ {{ if $image }} {{ with .image}} - <figure role="{{$role}}" + <figure class="{{ $image_class }}{{ if $is_carousel }} carousel__slide {{ end }}{{ if $is_lightbox }} lightbox-figure {{ end }}" {{ if $is_carousel }} - id="carousel-item-{{$index}}" - aria-roledescription="slide" - aria-label="item-{{$index}} {{- with or .text .alt .credit }} | {{ . | plainify }} {{- end }}" + id="carousel-item-{{$index}}" {{ else }} {{- with or .text .alt .credit }} aria-label="{{ . | plainify }}" diff --git a/layouts/partials/posts/post.html b/layouts/partials/posts/post.html index 014afbe2..57658e56 100644 --- a/layouts/partials/posts/post.html +++ b/layouts/partials/posts/post.html @@ -27,7 +27,7 @@ {{ end }} {{ if $is_carousel }} - <li class="carousel__slide" id="carousel-item-{{$index}}" role="{{$role}}" aria-roledescription="slide" aria-label="item-{{$index}}"> + <li class="carousel__slide" id="carousel-item-{{$index}}"> {{ end }} <article class="{{ $post_class }}" itemprop="blogPosts" itemscope itemtype="http://schema.org/BlogPosting"> <div class="post-content"> -- GitLab