diff --git a/assets/js/theme/components/events.js b/assets/js/theme/components/events.js index 0bea85088..1daf469df 100644 --- a/assets/js/theme/components/events.js +++ b/assets/js/theme/components/events.js @@ -4,11 +4,11 @@ window.osuny.components = window.osuny.components || {}; window.osuny.components.events = { components: { - lightbox: null, carousel: null }, handleKeyDownEvent: function (e) { var target = this._targetDirector(); + if (target) { if (e.key === 'ArrowLeft') { target.previous(); @@ -19,18 +19,10 @@ window.osuny.components.events = { }, // who receives the event depending on lightbox state and carousels _targetDirector () { - if (this._isLightBoxOpened()) { - return this.components.lightbox; - } - if (this._hasFocusedCarousel()) { + if (this._hasFocusedCarousel() && !window.osuny.lightbox.state.opened) { return this.components.carousel.focusedCarousel; } }, - _isLightBoxOpened () { - if (this.components.lightbox) { - return this.components.lightbox.container.opened; - } - }, _hasFocusedCarousel () { if (this.components.carousel) { return this.components.carousel.focusedCarousel; diff --git a/assets/js/theme/components/lightbox.js b/assets/js/theme/components/lightbox.js deleted file mode 100644 index 316801aa9..000000000 --- a/assets/js/theme/components/lightbox.js +++ /dev/null @@ -1,7 +0,0 @@ -import './lightbox/classes'; -import './lightbox/container'; -import './lightbox/controls'; -import './lightbox/events'; -import './lightbox/lightbox'; -import './lightbox/manager'; -import './lightbox/popupDetail'; diff --git a/assets/js/theme/components/lightbox/classes.js b/assets/js/theme/components/lightbox/classes.js deleted file mode 100644 index f584f43b5..000000000 --- a/assets/js/theme/components/lightbox/classes.js +++ /dev/null @@ -1,23 +0,0 @@ -window.osuny = window.osuny || {}; -window.osuny.lightbox = window.osuny.lightbox || {}; - -window.osuny.lightbox.classes = { - closeButton: 'close', - container: 'lightbox-container', - content: 'lightbox-content', - controls: 'lightbox-controls', - contentImage: 'lightbox-content-image', - creditButton: 'credit', - figure: 'lightbox-figure', - infoButton: 'info', - launcher: 'lightbox-launcher', - nextButton: 'next', - prevButton: 'prev', - detailWindow: 'details-window', - detailWindowContent: 'details-window-content', - detailWindowTitleInformation: 'details-window-title-information', - detailWindowTitleCredit: 'details-window-title-credit', - detailWindowClose: 'details-window-close' -}; - -window.osuny.lightbox.dataAttribute = 'data-lightbox'; diff --git a/assets/js/theme/components/lightbox/container.js b/assets/js/theme/components/lightbox/container.js deleted file mode 100644 index 9c763de63..000000000 --- a/assets/js/theme/components/lightbox/container.js +++ /dev/null @@ -1,120 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -window.osuny = window.osuny || {}; -window.osuny.lightbox = window.osuny.lightbox || {}; - -window.osuny.lightbox.LightboxContainer = function (element) { - this.element = element; - this.pageFocusableElements = ['main', 'nav', 'header', 'footer']; - this.bodyElement = document.querySelector('body'); - this.opened = false; - this.controlRack = null; - this.popupDetails = null; - this.eventsCallback = {}; - this.environment = window.osuny.lightbox; - this._findElement = window.osuny.components.utils.findElement.bind(this); - this._dispatchEvent = window.osuny.components.utils.dispatchEvent.bind(this); - this.content = this._findElement('content'); - this._initialize(); -}; - -window.osuny.lightbox.LightboxContainer.prototype = { - open () { - this._setPageElementsEnabled(false); - this.bodyElement.style.overflow = 'hidden'; - this.element.style.display = 'block'; - this.element.addEventListener('click', this.eventsCallback.click); - document.addEventListener('keydown', this.eventsCallback.keyDown); - this.opened = true; - }, - close () { - this._removeImageContent(); - this._setPageElementsEnabled(true); - this.bodyElement.style.overflow = 'visible'; - this.element.style.display = 'none'; - this._closePopup(); - this.element.removeEventListener('click', this.eventsCallback.click); - document.removeEventListener('keydown', this.eventsCallback.keyDown); - this.opened = false; - }, - show (lightbox) { - this.popupDetails.close(); - this._removeImageContent(); - this._setImageContent(lightbox); - this.controlRack.load(lightbox); - this.popupDetails.load(lightbox); - }, - _initialize () { - var controlRackElement = this._findElement('controls'), - popupDetailsElement = this._findElement('detailWindow'); - this.controlRack = new window.osuny.lightbox.ControlRack(controlRackElement); - this.popupDetails = new window.osuny.lightbox.Popup(popupDetailsElement); - this._initializeListeners(); - }, - _initializeListeners () { - var buttonEvents = Object.keys(this.controlRack.buttons); - // Dispaching to manager - this.eventsCallback.close = this._dispatchEvent.bind(this, 'close'); - this.eventsCallback.next = this._dispatchEvent.bind(this, 'next'); - this.eventsCallback.previous = this._dispatchEvent.bind(this, 'previous'); - - // handling local event - this.eventsCallback.keyDown = this._onKeydown.bind(this); - this.eventsCallback.click = this._onClick.bind(this); - this.eventsCallback.description = this._showPopup.bind(this, 'description'); - this.eventsCallback.credit = this._showPopup.bind(this, 'credit'); - this.eventsCallback.closePopup = this._closePopup.bind(this); - - buttonEvents.forEach(function (eventname) { - this.controlRack.element.addEventListener(eventname, this.eventsCallback[eventname]); - }.bind(this)); - this.popupDetails.element.addEventListener('closePopup', this.eventsCallback.closePopup); - }, - _onKeydown (event) { - if (event.key === 'Escape') { - this.eventsCallback.close(); - } - }, - _onClick (event) { - if (event.target === this.content) { - this.close(); - } - }, - _setImageContent (lightbox) { - var image = document.createElement('img'), - imageDescription = lightbox.descriptionPlain || lightbox.creditPlain || ''; - this._closePopup(); - image.setAttribute('src', lightbox.url); - image.setAttribute('alt', imageDescription); - this.content.setAttribute('aria-label', imageDescription); - this.content.append(image); - this.content.focus(); - }, - _removeImageContent () { - this.content.innerHTML = ''; - }, - _setPageElementsEnabled (enabled) { - this.pageFocusableElements.forEach(function (elem) { - this._setPageElementEnabled(elem, enabled); - }.bind(this)); - }, - _setPageElementEnabled (elem, enabled) { - var element = document.querySelector(elem); - element.querySelectorAll('a, button, iframe').forEach(function (e) { - e.setAttribute('tabindex', enabled ? '0' : '-1'); - }); - element.setAttribute('tabindex', enabled ? '0' : '-1'); - element.setAttribute('aria-hidden', String(!enabled)); - }, - _showPopup (popupContent) { - if (this.popupDetails.opened && this.popupDetails.current === popupContent) { - this._closePopup(); - } else { - this.popupDetails.show(popupContent); - this.controlRack.show(popupContent); - } - }, - _closePopup () { - this.popupDetails.close(); - this.controlRack.reset(); - } -}; diff --git a/assets/js/theme/components/lightbox/controls.js b/assets/js/theme/components/lightbox/controls.js deleted file mode 100644 index b5edca609..000000000 --- a/assets/js/theme/components/lightbox/controls.js +++ /dev/null @@ -1,76 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -window.osuny = window.osuny || {}; -window.osuny.lightbox = window.osuny.lightbox || {}; - -window.osuny.lightbox.ControlRack = function (element) { - this.element = element; - this.environment = window.osuny.lightbox; - this._findElement = window.osuny.components.utils.findElement.bind(this); - this.buttons = { - close: this._findElement('closeButton'), - previous: this._findElement('prevButton'), - next: this._findElement('nextButton'), - description: this._findElement('infoButton'), - credit: this._findElement('creditButton') - }; - this._initializeEvents(); -}; - -window.osuny.lightbox.ControlRack.prototype = { - _initializeEvents () { - var i = 0, - key = 0, - action = null; - for (i=0; i< Object.keys(this.buttons).length; i+=1) { - key = Object.keys(this.buttons)[i]; - action = this._dispachEvent.bind(this, key); - this.buttons[key].addEventListener('click', action); - } - }, - _displayArrows (display) { - if (display) { - this._showButton(this.buttons.previous); - this._showButton(this.buttons.next); - } else { - this._hideButton(this.buttons.previous); - this._hideButton(this.buttons.next); - } - }, - _dispachEvent (eventname) { - var event = new Event(eventname); - this.element.dispatchEvent(event); - }, - _showButton (button) { - button.style.display = 'block'; - }, - _hideButton (button) { - button.style.display = 'none'; - }, - load (lightbox) { - this._displayArrows(lightbox.isGallery); - this.buttons.next.disabled = lightbox.next === null; - this.buttons.previous.disabled = lightbox.previous === null; - - this._loadButton(lightbox, 'credit'); - this._loadButton(lightbox, 'description'); - }, - _loadButton (lightbox, content) { - if (lightbox[content]) { - this._showButton(this.buttons[content]); - } else { - this._hideButton(this.buttons[content]); - } - }, - show (popupContent = null) { - this.buttons.description.setAttribute('aria-expanded', false); - this.buttons.credit.setAttribute('aria-expanded', false); - if (popupContent === 'description') { - this.buttons.description.setAttribute('aria-expanded', true); - } else if (popupContent === 'credit') { - this.buttons.credit.setAttribute('aria-expanded', true); - } - }, - reset () { - this.show(null); - } -}; diff --git a/assets/js/theme/components/lightbox/events.js b/assets/js/theme/components/lightbox/events.js deleted file mode 100644 index 552940152..000000000 --- a/assets/js/theme/components/lightbox/events.js +++ /dev/null @@ -1,9 +0,0 @@ -window.osuny = window.osuny || {}; -window.osuny.lightbox = window.osuny.lightbox || {}; - -window.osuny.lightbox.events = { - close: 'osuny.lightbox.close', - next: 'osuny.lightbox.next', - previous: 'osuny.lightbox.previous', - instanciated: 'osuny.lightbox.instanciated' -}; diff --git a/assets/js/theme/components/lightbox/lightbox.js b/assets/js/theme/components/lightbox/lightbox.js deleted file mode 100644 index 8ef69adeb..000000000 --- a/assets/js/theme/components/lightbox/lightbox.js +++ /dev/null @@ -1,46 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -window.osuny = window.osuny || {}; -window.osuny.lightbox = window.osuny.lightbox || {}; - -window.osuny.lightbox.Lightbox = function (element, index) { - this.element = element; - this.environment = window.osuny.lightbox; - this._findElement = window.osuny.components.utils.findElement.bind(this); - this.index = index; - this.url = null; - this.credit = null; - this.description = null; - this.launcher = null; - this.previous = null; - this.next = null; - this.isGallery = false; - this._initialize(); -}; - -window.osuny.lightbox.Lightbox.prototype = { - _initialize () { - var options; - this.launcher = this._findElement('launcher'); - if (this.launcher) { - this.launcher.setAttribute('value', this.index); - this.url = this.launcher.href; - options = JSON.parse(this.launcher.getAttribute(window.osuny.lightbox.dataAttribute)); - this.descriptionPlain = options.description; - this.description = options.descriptionhtml; - this.creditPlain = options.credit; - this.credit = options.credithtml; - if (options.gallery_values) { - this.isGallery = true; - this._initializeGroupOptions(options.gallery_values); - } - } - }, - _initializeGroupOptions (options) { - if (options.index > 0) { - this.previous = this.index - 1; - } - if (options.index < options.total - 1) { - this.next = this.index + 1; - } - } -}; diff --git a/assets/js/theme/components/lightbox/manager.js b/assets/js/theme/components/lightbox/manager.js deleted file mode 100644 index 32be1eccc..000000000 --- a/assets/js/theme/components/lightbox/manager.js +++ /dev/null @@ -1,87 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -window.osuny = window.osuny || {}; -window.osuny.lightbox = window.osuny.lightbox || {}; - -window.osuny.lightbox.manager = { - initialized: false, - lightboxes: [], - container: null, - currentLightbox: null, - initialize () { - var i = 0; - if (!this.initialized) { - this._createLightboxes(); - this.bodyElement = document.querySelector('body'); - this.initialized = true; - } - // Il y a au moins une lightbox dans la page alors on crée le conteneur - if (this.lightboxes.length > 0) { - this.awake = window.osuny.components.utils.dispatchAwakeEvent.bind(this); - this._initializeContainer(); - for (i = 0; i < this.lightboxes.length; i += 1) { - this.lightboxes[i].launcher.addEventListener('click', function (event) { - event.preventDefault(); - this._onLauncherClick(event); - }.bind(this)); - } - this.awake('lightbox'); - } - }, - _createLightboxes () { - var lightboxElements = document.getElementsByClassName(window.osuny.lightbox.classes.figure), - lightbox = null, - i = 0; - for (i = 0; i < lightboxElements.length; i += 1) { - lightbox = new window.osuny.lightbox.Lightbox(lightboxElements[i], i); - this.lightboxes.push(lightbox); - } - }, - // one container handles all lightboxes contents - _initializeContainer () { - var containerElement = document.getElementsByClassName(window.osuny.lightbox.classes.container).item(0); - this.container = new window.osuny.lightbox.LightboxContainer(containerElement); - this._addContainerListener('close', this.close.bind(this)); - this._addContainerListener('next', this.next.bind(this)); - this._addContainerListener('previous', this.previous.bind(this)); - }, - _onLauncherClick (event) { - var index = event.currentTarget.getAttribute('value'); - this._setLightboxContent(index); - this._open(); - }, - _setLightboxContent (index) { - this.currentLightbox = this.lightboxes[index]; - this.container.show(this.currentLightbox); - }, - _open () { - if (!this.container.opened) { - this.container.open(); - } - }, - close () { - this.container.close(); - this.currentLightbox.launcher.focus(); - }, - next () { - if (this.currentLightbox.next) { - this._setLightboxContent(this.currentLightbox.next); - } - }, - previous () { - if (this.currentLightbox.previous) { - this._setLightboxContent(this.currentLightbox.previous); - } - }, - _addContainerListener (event, callback) { - this.container.element.addEventListener(window.osuny.lightbox.events[event], callback); - }, - invoke: function () { - return { - initialize: this.initialize.bind(this) - }; - } -}.invoke(); - -window.addEventListener('load', function () { - window.osuny.lightbox.manager.initialize(); -}); diff --git a/assets/js/theme/components/lightbox/popupDetail.js b/assets/js/theme/components/lightbox/popupDetail.js deleted file mode 100644 index 8fa48b421..000000000 --- a/assets/js/theme/components/lightbox/popupDetail.js +++ /dev/null @@ -1,64 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -window.osuny = window.osuny || {}; -window.osuny.lightbox = window.osuny.lightbox || {}; - -window.osuny.lightbox.Popup = function (element) { - this.element = element; - this.environment = window.osuny.lightbox; - this._findElement = window.osuny.components.utils.findElement.bind(this); - this.titles = {}; - this.title = null; - this.currentContent = {}; - this.currentContent.description = ''; - this.currentContent.credit = ''; - this.current = null; - this.opened = false; - - this.content = this._findElement('detailWindowContent'); - this.titles.description = this._findElement('detailWindowTitleInformation'); - this.titles.credit = this._findElement('detailWindowTitleCredit'); - this.closeButton = this._findElement('detailWindowClose'); - this.closeButton.addEventListener('click', function (e) { - var event = new Event('closePopup'); - this.element.dispatchEvent(event); - e.preventDefault(); - }.bind(this)); - this.close(); -}; - -window.osuny.lightbox.Popup.prototype = { - close () { - this._resetTitle(); - this.current = null; - this.content.innerHTML = ''; - this.element.style.display = 'none'; - this.opened = false; - }, - open () { - this.element.style.display = 'block'; - this.opened = true; - }, - load (lightbox) { - this.currentContent.description = lightbox.description; - this.currentContent.credit = lightbox.credit; - }, - show (content) { - if (Object.keys(this.currentContent).includes(content)) { - this._resetTitle(); - this.current = content; - this.title = this.titles[content]; - this.content.innerHTML = this.currentContent[content]; - } - this.title.style.display = 'block'; - if (!this.opened) { - this.open(); - } - this.element.focus(); - }, - _resetTitle () { - if (this.title) { - this.title.style.display = 'none'; - this.title = null; - } - } -}; diff --git a/assets/js/theme/design-system/Popup.js b/assets/js/theme/design-system/Popup.js new file mode 100644 index 000000000..061aff5d1 --- /dev/null +++ b/assets/js/theme/design-system/Popup.js @@ -0,0 +1,84 @@ +import { focusTrap } from '../utils/focus-trap'; + +var CLASSES = { + popupOpened: 'has-popup-opened', + popupIsOpened: 'is-opened' +}; + +/* eslint-disable no-underscore-dangle */ +window.osuny = window.osuny || {}; +window.osuny.Popup = window.osuny.Popup || {}; + +window.osuny.Popup = function (element) { + this.element = element; + this.closeElements = this.element.querySelectorAll('.popup-close'); + this.extendables = this.element.querySelectorAll('.extendable'); + this.state = { + opened: false, + lastTriggerElement: null + }; +}; + +window.osuny.Popup.prototype = { + _listen: function () { + this.closeElements.forEach(function (button) { + button.addEventListener('click', this.toggle.bind(this, false)); + }.bind(this)); + + window.addEventListener('keydown', function (event) { + if (event.keyCode === 27 || event.key === 'Escape') { + this.toggle(false); + } else if (event.key === 'Tab' && this.state.opened) { + focusTrap(event, this.element, this.state.opened); + } + }.bind(this)); + + window.addEventListener('click', function (event) { + if (event.target === this.element) { + this.toggle(false); + } + }.bind(this)); + }, + + toggle (open, triggerElement) { + var classAction, + closeEvent = new Event('extendable-close'); + this.state.opened = typeof open !== 'undefined' ? open : !this.state.opened; + classAction = this.state.opened ? 'add' : 'remove'; + + document.documentElement.classList[classAction](CLASSES.popupOpened); + + this.element.setAttribute('aria-hidden', !this.state.opened); + this.element.classList[classAction](CLASSES.popupIsOpened); + + if (!this.state.opened) { + // Close extendables boxes + this.extendables.forEach(function (extendable) { + extendable.dispatchEvent(closeEvent); + }); + } + + this.updateDocumentAccessibility(); + + // If open action, save the element that's trigger it + if (this.state.opened && triggerElement) { + this.lastTriggerElement = triggerElement; + } + + // If close action, focus the last element which open the popup + if (!this.state.opened && this.lastTriggerElement) { + this.lastTriggerElement.focus(); + } + }, + + updateDocumentAccessibility: function () { + var bodyChildren = document.body.children, + action = this.state.opened ? 'setAttribute' : 'removeAttribute'; + + Array.prototype.forEach.call(bodyChildren, function (element) { + if (this.element !== element) { + element[action]('inert', ''); + } + }.bind(this)); + } +}; diff --git a/assets/js/theme/design-system/collapse.js b/assets/js/theme/design-system/collapse.js deleted file mode 100644 index 4ffdbea8e..000000000 --- a/assets/js/theme/design-system/collapse.js +++ /dev/null @@ -1,28 +0,0 @@ -import { a11yClick } from '../utils/a11y'; - -window.osuny = window.osuny || {}; - -window.osuny.Collapse = function (element) { - this.element = element; - this.button = document.querySelector('[aria-controls=' + this.element.id + ']'); - this.state = { - isOpened: false - }; - - a11yClick(this.button, function (event) { - event.preventDefault(); - this.toggle(); - }.bind(this)); -}; - -window.osuny.Collapse.prototype.toggle = function () { - this.state.isOpened = !this.state.isOpened; - this.button.setAttribute('aria-expanded', this.state.isOpened); -}; - -(function () { - var collapses = document.querySelectorAll('.collapse'); - Array.prototype.forEach.call(collapses, function (element) { - new window.osuny.Collapse(element); - }); -}()); diff --git a/assets/js/theme/design-system/extendables.js b/assets/js/theme/design-system/extendables.js new file mode 100644 index 000000000..4a995fb80 --- /dev/null +++ b/assets/js/theme/design-system/extendables.js @@ -0,0 +1,54 @@ +import { a11yClick } from '../utils/a11y'; + +window.osuny = window.osuny || {}; + +window.osuny.Extendable = function (element) { + this.element = element; + this.buttons = document.querySelectorAll('[aria-controls=' + this.element.id + ']'); + this.state = { + opened: false + }; + this.options = { + // This attribute determine if extendable should close others when opened + closeSiblings: this.element.getAttribute('data-extendable-close-siblings') + }; + this.listen(); +}; + +window.osuny.Extendable.prototype.listen = function () { + this.buttons.forEach(function (button) { + a11yClick(button, function (event) { + event.preventDefault(); + this.toggle(); + }.bind(this)); + }.bind(this)); + + this.element.addEventListener('extendable-close', this.toggle.bind(this, true)); +}; + +window.osuny.Extendable.prototype.toggle = function (forceClose) { + this.state.opened = forceClose ? false : !this.state.opened; + + if (this.state.opened && this.options.closeSiblings) { + this.closeSiblings(); + } + + this.buttons.forEach(function (button) { + button.setAttribute('aria-expanded', this.state.opened); + }.bind(this)); +}; + +window.osuny.Extendable.prototype.closeSiblings = function () { + var siblings = this.element.parentNode.querySelectorAll('button[aria-expanded="true"]'); + + siblings.forEach(function (button) { + button.click(); + }.bind(this)); +}; + +(function () { + var extendables = document.querySelectorAll('.extendable, .collapse'); + Array.prototype.forEach.call(extendables, function (element) { + new window.osuny.Extendable(element); + }); +}()); diff --git a/assets/js/theme/design-system/lightbox.js b/assets/js/theme/design-system/lightbox.js new file mode 100644 index 000000000..6ff922d40 --- /dev/null +++ b/assets/js/theme/design-system/lightbox.js @@ -0,0 +1,139 @@ +import { setButtonEnability } from '../utils/a11y'; + +/* eslint-disable no-underscore-dangle */ +window.osuny = window.osuny || {}; +window.osuny.Lightbox = window.osuny.Lightbox || {}; + +window.osuny.Lightbox = function () { + var element = document.getElementById('lightbox'); + window.osuny.Popup.call(this, element); + + this.state.currentData = {}; + this.state.previousData = {}; + this.state.nextData = {}; + + this._setup(); + this._listen(); +}; + +window.osuny.Lightbox.prototype = Object.create(window.osuny.Popup.prototype); + +window.osuny.Lightbox.prototype._setup = function () { + this.buttons = document.querySelectorAll('[data-lightbox]'); + this.contentElements = { + media: this.element.querySelector('#lightbox-media'), + information: this.element.querySelector('#lightbox-information'), + informationButton: this.element.querySelector('.lightbox-button-information'), + credit: this.element.querySelector('#lightbox-credit'), + creditButton: this.element.querySelector('.lightbox-button-credit'), + previousButton: this.element.querySelector('.lightbox-button-previous'), + nextButton: this.element.querySelector('.lightbox-button-next') + }; +}; + +window.osuny.Lightbox.prototype._listen = function () { + window.osuny.Popup.prototype._listen.call(this); + + this.buttons.forEach(function (button) { + button.addEventListener('click', this.open.bind(this, button)); + }.bind(this)); + + this.contentElements.previousButton.addEventListener('click', this.navigateTo.bind(this, 'previousData')); + this.contentElements.nextButton.addEventListener('click', this.navigateTo.bind(this, 'nextData')); + + window.addEventListener('keydown', function (event) { + if (this.state.opened && event.key === 'ArrowLeft') { + this.navigateTo('previousData'); + } else if (this.state.opened && event.key === 'ArrowRight') { + this.navigateTo('nextData'); + } + }.bind(this)); +}; + +window.osuny.Lightbox.prototype.open = function (button) { + var data = this._getData(button); + this._update(data); + this.toggle(true, button); + this.element.focus(); +}; + +window.osuny.Lightbox.prototype._getData = function (button) { + var data = JSON.parse(button.getAttribute('data-lightbox')); + data.buttonElement = button; + return data; +}; + +window.osuny.Lightbox.prototype._setSiblings = function () { + var figure = this.state.currentData.buttonElement.parentElement, + galleryElement = figure.closest('.gallery, .carousel__container'), + figureIndex = 0; + + if (!galleryElement || galleryElement.children.length === 1) { + this.contentElements.previousButton.style.display = 'none'; + this.contentElements.nextButton.style.display = 'none'; + return; + } + + figureIndex = Array.prototype.indexOf.call(galleryElement.children, figure); + + setButtonEnability(this.contentElements.previousButton, figureIndex > 0); + setButtonEnability(this.contentElements.nextButton, figureIndex < galleryElement.children.length - 1); + + this.state.previousData = this._getSiblingsData(figure.previousElementSibling); + this.state.nextData = this._getSiblingsData(figure.nextElementSibling); +}; + +window.osuny.Lightbox.prototype._getSiblingsData = function (element) { + var button; + if (!element) { + return null; + } + button = element.querySelector('.lightbox-button'); + if (button) { + return this._getData(button); + } +}; + +window.osuny.Lightbox.prototype._clear = function () { + this.contentElements.previousButton.style.display = 'block'; + this.contentElements.nextButton.style.display = 'block'; + this.contentElements.media.innerHTML = ''; + this.contentElements.information.innerHTML = ''; + this.contentElements.credit.innerHTML = ''; + + setButtonEnability(this.contentElements.previousButton, false); + setButtonEnability(this.contentElements.nextButton, false); +}; + +window.osuny.Lightbox.prototype._update = function (data) { + this._clear(); + this.state.currentData = data; + + this._setSiblings(); + + if (data.imageSrc) { + this._createImage(data); + } + + this.contentElements.information.innerHTML = data.information || ''; + setButtonEnability(this.contentElements.informationButton, Boolean(data.information)); + this.contentElements.credit.innerHTML = data.credit || ''; + setButtonEnability(this.contentElements.creditButton, Boolean(data.credit)); +}; + +window.osuny.Lightbox.prototype._createImage = function (data) { + var image = document.createElement('img'); + image.src = data.imageSrc; + image.alt = data.alt || ''; + image.tabIndex = 0; + this.contentElements.media.append(image); + image.focus(); +}; + +window.osuny.Lightbox.prototype.navigateTo = function (key) { + if (this.state[key]) { + this._update(this.state[key]); + } +}; + +window.osuny.lightbox = new window.osuny.Lightbox(); diff --git a/assets/js/theme/index.js b/assets/js/theme/index.js index d0cc58abc..9b420da51 100644 --- a/assets/js/theme/index.js +++ b/assets/js/theme/index.js @@ -1,7 +1,7 @@ import './body.js'; import './design-system/accordion.js'; import './design-system/clickToCopy'; -import './design-system/collapse'; +import './design-system/extendables'; import './design-system/dropdowns'; import './design-system/font'; import './design-system/mainMenu'; @@ -9,6 +9,7 @@ import './design-system/modal'; import './design-system/notes'; import './design-system/search'; import './design-system/toc'; +import './design-system/Popup'; import './blocks/keyFigures'; import './blocks/organizations'; import './blocks/draggableBlocks.js'; @@ -19,4 +20,5 @@ import './utils/utils.js'; import './components/events.js'; import './components/utils.js'; import './components/carousel.js'; -import './components/lightbox.js'; +import './design-system/lightbox.js'; + diff --git a/assets/js/theme/utils/a11y.js b/assets/js/theme/utils/a11y.js index 8358fb92e..b40f28928 100644 --- a/assets/js/theme/utils/a11y.js +++ b/assets/js/theme/utils/a11y.js @@ -14,6 +14,12 @@ const a11yClick = function(element, action) { }); } +const setButtonEnability = function(button, enable) { + button.disabled = !enable; + button.ariaHidden = !enable; +} + export { - a11yClick + a11yClick, + setButtonEnability } \ No newline at end of file diff --git a/assets/sass/_theme/components/lightbox.sass b/assets/sass/_theme/components/lightbox.sass index 788afb9e1..0422487de 100644 --- a/assets/sass/_theme/components/lightbox.sass +++ b/assets/sass/_theme/components/lightbox.sass @@ -1,18 +1,18 @@ .lightbox - &-figure + backdrop-filter: $lightbox-backdrop + background: $lightbox-overlay-color + bottom: 0 + display: none + left: 0 + padding: $spacing-2 + position: fixed + right: 0 + top: 0 + z-index: 100 + &.is-opened + display: block + &-media position: relative - &-container - backdrop-filter: $lightbox-backdrop - background: $lightbox-overlay-color - bottom: 0 - display: none - left: 0 - padding: $spacing-2 - position: fixed - right: 0 - top: 0 - z-index: 100 - &-content display: flex height: 100% width: 100% @@ -26,56 +26,32 @@ position: absolute right: $spacing-2 z-index: 101 - .details-window - @include meta - background-color: $color-background - bottom: calc(var(--spacing-4) + var(--spacing-2) + 1px) - color: $color-text - padding: $spacing-2 - position: fixed - right: $spacing-2 - width: columns(6) - z-index: 102 - &-title - &-information, - &-credit - display: none - margin-top: 0 - > div - display: flex - justify-content: space-between - width: 100% - a - color: $color-text - text-decoration: underline - &-content - @include body-text - margin-top: $spacing-3 - @include media-breakpoint-down(sm) - left: $spacing-2 - width: initial + @include media-breakpoint-down(sm) + left: $spacing-2 + width: initial button @include button-reset @include meta background-color: $color-background color: $color-text height: $spacing-4 + outline-color: $color-background margin: 0 padding: 0 width: $spacing-4 &:not(:last-child) border-right: 1px solid $color-border - &.next + &.lightbox-button-next @include icon(arrow-right-s-line, before) - &.prev + &.lightbox-button-previous @include icon(arrow-left-s-line, before) - &.credit + &.lightbox-button-credit &::before - content: '©' - &.info + content: '©' / '' + &.lightbox-button-information &::before - content: 'i' - &.close + content: 'i' / '' + &.lightbox-button-close @include icon(close-line, before) &[aria-expanded='true'] background-color: $color-text @@ -84,3 +60,39 @@ background: $color-background-alt &::before color: $color-border + &-detail + background-color: $color-background + bottom: calc(var(--spacing-4) + var(--spacing-2) + 1px) + // color: $color-text + display: none + padding: $spacing-2 + position: fixed + right: $spacing-2 + width: columns(6) + z-index: 102 + &__title, &__close + @include meta + color: var(--color-text-alt) + &__close + cursor: pointer + position: absolute + right: $spacing-2 + top: $spacing-2 + &-content + margin-top: $spacing-3 + > div + margin-top: $spacing-3 + [aria-expanded="true"] + & + display: block + +.lightbox-figure + position: relative + +.lightbox-button + @include button-reset + cursor: pointer + position: absolute + bottom: 0 + left: 0 + top: 0 + right: 0 \ No newline at end of file diff --git a/i18n/en.yml b/i18n/en.yml index 86360b385..3581163df 100644 --- a/i18n/en.yml +++ b/i18n/en.yml @@ -127,16 +127,17 @@ commons: in: In language: Language lightbox: - info: "Display image information" - credit: "Display image credit" - prev: "Go to previous image" - next: "Go to next image" - close: "Close lightbox" - closedetailwindow: "Close" - infoTitle: "Information" - creditTitle: "Credit" - link: - title: Enlarge image + close: Close + controls: + credits: Display image credits + informations: Display image information + previous: Go to previous image + next: Go to the next image + close: Close the viewer + credits: Credits + informations: Information + button: View larger image + title: Image viewer link: blank: external link blank_aria: “{{ .Title }}” - external link diff --git a/i18n/fr.yml b/i18n/fr.yml index 1e7b03e89..a7cd1a7a7 100644 --- a/i18n/fr.yml +++ b/i18n/fr.yml @@ -136,16 +136,17 @@ commons: index: Accueil - {{ .Title }} language: Langue lightbox: - info: "Afficher les informations de l'image" - credit: "Afficher les crédits de l'image" - prev: "Aller à l'image précédente" - next: "Aller à l'image suivante" - close: "Fermer la lightbox" - closedetailwindow: "Fermer" - infoTitle: "Information" - creditTitle: "Crédit" - link: - title: Agrandir l'image + close: Fermer + controls: + credits: Afficher les crédits de l'image + informations: Afficher les informations de l'image + previous: Aller à l'image précédente + next: Aller à l'image suivante + close: Fermer la visionneuse + credits: Crédits + informations: Informations + button: Agrandir l'image + title: Visionneuse link: blank: lien externe blank_aria: “{{ .Title }}” - lien externe diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 05f8369a8..54765f214 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -27,12 +27,12 @@ {{- partial "hooks/before-main-end" . -}} {{- partial "footer/footer.html" . -}} + {{- partial "commons/lightbox/lightbox.html" . -}} {{- partial "footer/plausible.html" . -}} {{- partial "footer/js.html" . -}} {{- partial "footer/script.html" . -}} {{- if or (not hugo.IsProduction) site.Params.debug.active -}} {{- partial "footer/debug.html" . -}} {{- end -}} - {{- partial "commons/lightbox/lightbox-container.html" . -}} diff --git a/layouts/partials/blocks/templates/gallery/carousel-image.html b/layouts/partials/blocks/templates/gallery/carousel-image.html index 552b6fb38..d4eb9e189 100644 --- a/layouts/partials/blocks/templates/gallery/carousel-image.html +++ b/layouts/partials/blocks/templates/gallery/carousel-image.html @@ -2,8 +2,6 @@ "image" .params "sizes" site.Params.image_sizes.blocks.gallery.carousel "is_gallery" true - "index" .index - "total" .total "role" "tabpanel" ) }} \ No newline at end of file diff --git a/layouts/partials/commons/image-figure.html b/layouts/partials/commons/image-figure.html index 8d0602d82..23be1438a 100644 --- a/layouts/partials/commons/image-figure.html +++ b/layouts/partials/commons/image-figure.html @@ -1,15 +1,9 @@ {{ if .image.id }} {{ $sizes := .sizes }} {{ $lazy := default true .lazy }} - {{ $is_lightbox := false }} - {{ $is_gallery := .is_gallery }} - {{ $index := .index }} - {{ $total := .total }} - {{ if or (not site.Params.image_sizes.design_system.lightbox.disabled) (and $is_gallery (not site.Params.image_sizes.design_system.gallerylightbox.disabled)) }} - {{ $is_lightbox = true }} - {{ end }} {{ $image := partial "GetMedia" .image.id }} {{ $image_class := printf "image-%s" (partial "GetImageDirection" .image) }} + {{ $is_lightbox := not site.Params.image_sizes.design_system.lightbox.disabled }} {{ if $image }} {{ with .image }} @@ -20,25 +14,15 @@ aria-label="{{ . | plainify }}" {{ end }} > + {{ partial "commons/image.html" (dict + "image" .id + "alt" .alt + "sizes" $sizes + "lazy" $lazy + ) }} + {{ if $is_lightbox }} - {{ partial "commons/lightbox/lightbox-launcher.html" (dict - "description" .text - "image" .id - "alt" .alt - "sizes" $sizes - "lazy" $lazy - "credit" .credit - "is_gallery" $is_gallery - "index" $index - "total" $total - ) }} - {{ else }} - {{ partial "commons/image.html" (dict - "image" .id - "alt" .alt - "sizes" $sizes - "lazy" $lazy - ) }} + {{ partial "commons/lightbox/button.html" . }} {{ end }} {{ if or .text .credit }} diff --git a/layouts/partials/commons/lightbox/button.html b/layouts/partials/commons/lightbox/button.html new file mode 100644 index 000000000..0aca1bd33 --- /dev/null +++ b/layouts/partials/commons/lightbox/button.html @@ -0,0 +1,21 @@ +{{ with . }} + {{ $title := i18n "commons.lightbox.button" }} + {{ $options := dict "imageSrc" ( partial "GetLightboxUrl" .id ) }} + + {{ with .text }} + {{ $options = merge $options (dict "information" (partial "PrepareHTML" . )) }} + {{ $title = printf "%s %s" $title ( . | plainify ) }} + {{ end }} + + {{ with .credit }} + {{ $options = merge $options (dict "credit" (partial "PrepareHTML" . )) }} + {{ end }} + + {{ with .alt }} + {{ $options = merge $options (dict "alt" (. | plainify)) }} + {{ end }} + + +{{ end }} \ No newline at end of file diff --git a/layouts/partials/commons/lightbox/lightbox-container.html b/layouts/partials/commons/lightbox/lightbox-container.html deleted file mode 100644 index d84e0626a..000000000 --- a/layouts/partials/commons/lightbox/lightbox-container.html +++ /dev/null @@ -1,29 +0,0 @@ - \ No newline at end of file diff --git a/layouts/partials/commons/lightbox/lightbox-launcher.html b/layouts/partials/commons/lightbox/lightbox-launcher.html deleted file mode 100644 index 918417b9e..000000000 --- a/layouts/partials/commons/lightbox/lightbox-launcher.html +++ /dev/null @@ -1,33 +0,0 @@ -{{ $linkTitle := i18n "commons.lightbox.link.title" }} -{{ $options := (dict ) }} -{{ with .description }} - {{ $options = merge $options (dict "description" ( . | plainify )) }} - {{ $options = merge $options (dict "descriptionhtml" (partial "PrepareHTML" . )) }} - {{ $linkTitle = (printf "%s %s" $linkTitle . ) }} -{{ end }} - -{{ with .credit }} - {{ $options = merge $options (dict "credit" (. | plainify)) }} - {{ $options = merge $options (dict "credithtml" (partial "PrepareHTML" . )) }} -{{ end }} - -{{ $index := partial "PrepareHTML" .index }} -{{ $total := partial "PrepareHTML" .total }} - -{{ if and $index $total }} - {{ $options = merge $options (dict "gallery_values" (dict "index" $index "total" $total)) }} -{{ end }} - - - {{ $linkTitle | plainify }} - {{ partial "commons/image.html" (dict - "image" .image - "alt" .alt - "sizes" .sizes - "lazy" .lazy - ) }} - \ No newline at end of file diff --git a/layouts/partials/commons/lightbox/lightbox.html b/layouts/partials/commons/lightbox/lightbox.html new file mode 100644 index 000000000..00041c7a4 --- /dev/null +++ b/layouts/partials/commons/lightbox/lightbox.html @@ -0,0 +1,33 @@ + \ No newline at end of file diff --git a/layouts/partials/commons/menu.html b/layouts/partials/commons/menu.html index b49654547..ff06ee86e 100644 --- a/layouts/partials/commons/menu.html +++ b/layouts/partials/commons/menu.html @@ -82,13 +82,15 @@ {{- end -}} {{ end -}} - {{ if and (site.Params.search.active) (or (eq $kind "primary") (eq $kind "upper-menu")) (eq site.Params.search.position $kind) }} - + {{ if site.Params.search.active }} + {{ if or (and (eq $kind "primary") (eq site.Params.search.position "menu")) (eq $kind site.Params.search.position "upper-menu") }} + + {{ end }} {{ end }} {{ if and (eq $kind "primary") (site.Params.menu.i18n.display) }} {{ partial "header/i18n.html" .context }}