@codegouvfr/react-dsfr
Version:
French State Design System React integration library
425 lines (332 loc) • 13.6 kB
JavaScript
/*! DSFR v1.8.5 | SPDX-License-Identifier: MIT | License-Filename: LICENSE.md | restricted use (see terms and conditions) */
(function () {
'use strict';
var config = {
prefix: 'fr',
namespace: 'dsfr',
organisation: '@gouvfr',
version: '1.8.5'
};
var api = window[config.namespace];
var ModalSelector = {
MODAL: api.internals.ns.selector('modal'),
SCROLL_SHADOW: api.internals.ns.selector('scroll-shadow'),
BODY: api.internals.ns.selector('modal__body')
};
var ModalButton = /*@__PURE__*/(function (superclass) {
function ModalButton () {
superclass.call(this, api.core.DisclosureType.OPENED);
}
if ( superclass ) ModalButton.__proto__ = superclass;
ModalButton.prototype = Object.create( superclass && superclass.prototype );
ModalButton.prototype.constructor = ModalButton;
var staticAccessors = { instanceClassName: { configurable: true } };
staticAccessors.instanceClassName.get = function () {
return 'ModalButton';
};
Object.defineProperties( ModalButton, staticAccessors );
return ModalButton;
}(api.core.DisclosureButton));
var ModalAttribute = {
CONCEALING_BACKDROP: api.internals.ns.attr('concealing-backdrop')
};
var Modal = /*@__PURE__*/(function (superclass) {
function Modal () {
superclass.call(this, api.core.DisclosureType.OPENED, ModalSelector.MODAL, ModalButton, 'ModalsGroup');
this.scrolling = this.resize.bind(this, false);
this.resizing = this.resize.bind(this, true);
}
if ( superclass ) Modal.__proto__ = superclass;
Modal.prototype = Object.create( superclass && superclass.prototype );
Modal.prototype.constructor = Modal;
var prototypeAccessors = { body: { configurable: true } };
var staticAccessors = { instanceClassName: { configurable: true } };
staticAccessors.instanceClassName.get = function () {
return 'Modal';
};
Modal.prototype.init = function init () {
superclass.prototype.init.call(this);
this.listen('click', this.click.bind(this));
this.listenKey(api.core.KeyCodes.ESCAPE, this.conceal.bind(this, false, false), true, true);
};
prototypeAccessors.body.get = function () {
return this.element.getDescendantInstances('ModalBody', 'Modal')[0];
};
Modal.prototype.click = function click (e) {
if (e.target === this.node && this.getAttribute(ModalAttribute.CONCEALING_BACKDROP) !== 'false') { this.conceal(); }
};
Modal.prototype.disclose = function disclose (withhold) {
if (!superclass.prototype.disclose.call(this, withhold)) { return false; }
if (this.body) { this.body.activate(); }
this.isScrollLocked = true;
this.setAttribute('aria-modal', 'true');
this.setAttribute('open', 'true');
return true;
};
Modal.prototype.conceal = function conceal (withhold, preventFocus) {
if (!superclass.prototype.conceal.call(this, withhold, preventFocus)) { return false; }
this.isScrollLocked = false;
this.removeAttribute('aria-modal');
this.removeAttribute('open');
if (this.body) { this.body.deactivate(); }
return true;
};
Object.defineProperties( Modal.prototype, prototypeAccessors );
Object.defineProperties( Modal, staticAccessors );
return Modal;
}(api.core.Disclosure));
var unordereds = [
'[tabindex="0"]',
'a[href]',
'button:not([disabled])',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',
'audio[controls]',
'video[controls]',
'[contenteditable]:not([contenteditable="false"])',
'details>summary:first-of-type',
'details',
'iframe'
];
var UNORDEREDS = unordereds.join();
var ordereds = [
'[tabindex]:not([tabindex="-1"]):not([tabindex="0"])'
];
var ORDEREDS = ordereds.join();
var isFocusable = function (element, container) {
if (!(element instanceof Element)) { return false; }
var style = window.getComputedStyle(element);
if (!style) { return false; }
if (style.visibility === 'hidden') { return false; }
if (container === undefined) { container = element; }
while (container.contains(element)) {
if (style.display === 'none') { return false; }
element = element.parentElement;
}
return true;
};
var FocusTrap = function FocusTrap (onTrap, onUntrap) {
this.element = null;
this.activeElement = null;
this.onTrap = onTrap;
this.onUntrap = onUntrap;
this.waiting = this.wait.bind(this);
this.handling = this.handle.bind(this);
this.focusing = this.maintainFocus.bind(this);
this.current = null;
};
var prototypeAccessors = { trapped: { configurable: true },focusables: { configurable: true } };
prototypeAccessors.trapped.get = function () { return this.element !== null; };
FocusTrap.prototype.trap = function trap (element) {
if (this.trapped) { this.untrap(); }
this.element = element;
this.isTrapping = true;
this.wait();
if (this.onTrap) { this.onTrap(); }
};
FocusTrap.prototype.wait = function wait () {
if (!isFocusable(this.element)) {
window.requestAnimationFrame(this.waiting);
return;
}
this.trapping();
};
FocusTrap.prototype.trapping = function trapping () {
if (!this.isTrapping) { return; }
this.isTrapping = false;
var focusables = this.focusables;
if (focusables.length) { focusables[0].focus(); }
this.element.setAttribute('aria-modal', true);
window.addEventListener('keydown', this.handling);
document.body.addEventListener('focus', this.focusing, true);
};
FocusTrap.prototype.stun = function stun (node) {
for (var i = 0, list = node.children; i < list.length; i += 1) {
var child = list[i];
if (child === this.element) { continue; }
if (child.contains(this.element)) {
this.stun(child);
continue;
}
this.stunneds.push(new Stunned(child));
}
};
FocusTrap.prototype.maintainFocus = function maintainFocus (event) {
if (!this.element.contains(event.target)) {
var focusables = this.focusables;
if (focusables.length === 0) { return; }
var first = focusables[0];
event.preventDefault();
first.focus();
}
};
FocusTrap.prototype.handle = function handle (e) {
if (e.keyCode !== 9) { return; }
var focusables = this.focusables;
if (focusables.length === 0) { return; }
var first = focusables[0];
var last = focusables[focusables.length - 1];
var index = focusables.indexOf(document.activeElement);
if (e.shiftKey) {
if (!this.element.contains(document.activeElement) || index < 1) {
e.preventDefault();
last.focus();
} else if (document.activeElement.tabIndex > 0 || focusables[index - 1].tabIndex > 0) {
e.preventDefault();
focusables[index - 1].focus();
}
} else {
if (!this.element.contains(document.activeElement) || index === focusables.length - 1 || index === -1) {
e.preventDefault();
first.focus();
} else if (document.activeElement.tabIndex > 0) {
e.preventDefault();
focusables[index + 1].focus();
}
}
};
prototypeAccessors.focusables.get = function () {
var this$1$1 = this;
var unordereds = api.internals.dom.querySelectorAllArray(this.element, UNORDEREDS);
/**
*filtrage des radiobutttons de même name (la navigations d'un groupe de radio se fait à la flèche et non pas au tab
**/
var radios = api.internals.dom.querySelectorAllArray(document.documentElement, 'input[type="radio"]');
if (radios.length) {
var groups = {};
for (var i = 0, list = radios; i < list.length; i += 1) {
var radio = list[i];
var name = radio.getAttribute('name');
if (groups[name] === undefined) { groups[name] = new RadioButtonGroup(name); }
groups[name].push(radio);
}
unordereds = unordereds.filter(function (unordered) {
if (unordered.tagName.toLowerCase() !== 'input' || unordered.getAttribute('type').toLowerCase() !== 'radio') { return true; }
var name = unordered.getAttribute('name');
return groups[name].keep(unordered);
});
}
var ordereds = api.internals.dom.querySelectorAllArray(this.element, ORDEREDS);
ordereds.sort(function (a, b) { return a.tabIndex - b.tabIndex; });
var noDuplicates = unordereds.filter(function (element) { return ordereds.indexOf(element) === -1; });
var concateneds = ordereds.concat(noDuplicates);
return concateneds.filter(function (element) { return element.tabIndex !== '-1' && isFocusable(element, this$1$1.element); });
};
FocusTrap.prototype.untrap = function untrap () {
if (!this.trapped) { return; }
this.isTrapping = false;
this.element.removeAttribute('aria-modal');
window.removeEventListener('keydown', this.handling);
document.body.removeEventListener('focus', this.focusing, true);
this.element = null;
if (this.onUntrap) { this.onUntrap(); }
};
FocusTrap.prototype.dispose = function dispose () {
this.untrap();
};
Object.defineProperties( FocusTrap.prototype, prototypeAccessors );
var Stunned = function Stunned (element) {
this.element = element;
// this.hidden = element.getAttribute('aria-hidden');
this.inert = element.getAttribute('inert');
// this.element.setAttribute('aria-hidden', true);
this.element.setAttribute('inert', '');
};
Stunned.prototype.unstun = function unstun () {
/*
if (this.hidden === null) this.element.removeAttribute('aria-hidden');
else this.element.setAttribute('aria-hidden', this.hidden);
*/
if (this.inert === null) { this.element.removeAttribute('inert'); }
else { this.element.setAttribute('inert', this.inert); }
};
var RadioButtonGroup = function RadioButtonGroup (name) {
this.name = name;
this.buttons = [];
};
RadioButtonGroup.prototype.push = function push (button) {
this.buttons.push(button);
if (button === document.activeElement || button.checked || this.selected === undefined) { this.selected = button; }
};
RadioButtonGroup.prototype.keep = function keep (button) {
return this.selected === button;
};
var ModalsGroup = /*@__PURE__*/(function (superclass) {
function ModalsGroup () {
superclass.call(this, 'Modal', false);
this.focusTrap = new FocusTrap();
}
if ( superclass ) ModalsGroup.__proto__ = superclass;
ModalsGroup.prototype = Object.create( superclass && superclass.prototype );
ModalsGroup.prototype.constructor = ModalsGroup;
var staticAccessors = { instanceClassName: { configurable: true } };
staticAccessors.instanceClassName.get = function () {
return 'ModalsGroup';
};
ModalsGroup.prototype.apply = function apply (value, initial) {
superclass.prototype.apply.call(this, value, initial);
if (this.current === null) { this.focusTrap.untrap(); }
else { this.focusTrap.trap(this.current.node); }
};
Object.defineProperties( ModalsGroup, staticAccessors );
return ModalsGroup;
}(api.core.DisclosuresGroup));
var OFFSET = 32; // 32px => 8v => 2rem
var ModalBody = /*@__PURE__*/(function (superclass) {
function ModalBody () {
superclass.apply(this, arguments);
}
if ( superclass ) ModalBody.__proto__ = superclass;
ModalBody.prototype = Object.create( superclass && superclass.prototype );
ModalBody.prototype.constructor = ModalBody;
var staticAccessors = { instanceClassName: { configurable: true } };
staticAccessors.instanceClassName.get = function () {
return 'ModalBody';
};
ModalBody.prototype.init = function init () {
this.listen('scroll', this.shade.bind(this));
};
ModalBody.prototype.activate = function activate () {
this.isResizing = true;
this.resize();
};
ModalBody.prototype.deactivate = function deactivate () {
this.isResizing = false;
};
ModalBody.prototype.shade = function shade () {
if (this.node.scrollHeight > this.node.clientHeight) {
if (this.node.offsetHeight + this.node.scrollTop >= this.node.scrollHeight) {
this.removeClass(ModalSelector.SCROLL_SHADOW);
} else {
this.addClass(ModalSelector.SCROLL_SHADOW);
}
} else {
this.removeClass(ModalSelector.SCROLL_SHADOW);
}
};
ModalBody.prototype.resize = function resize () {
this.adjust();
this.request(this.adjust.bind(this));
};
ModalBody.prototype.adjust = function adjust () {
var offset = OFFSET * (this.isBreakpoint(api.core.Breakpoints.MD) ? 2 : 1);
if (this.isLegacy) { this.style.maxHeight = (window.innerHeight - offset) + "px"; }
else { this.style.setProperty('--modal-max-height', ((window.innerHeight - offset) + "px")); }
this.shade();
};
Object.defineProperties( ModalBody, staticAccessors );
return ModalBody;
}(api.core.Instance));
api.modal = {
Modal: Modal,
ModalButton: ModalButton,
ModalBody: ModalBody,
ModalsGroup: ModalsGroup,
ModalSelector: ModalSelector
};
api.internals.register(api.modal.ModalSelector.MODAL, api.modal.Modal);
api.internals.register(api.modal.ModalSelector.BODY, api.modal.ModalBody);
api.internals.register(api.core.RootSelector.ROOT, api.modal.ModalsGroup);
})();
//# sourceMappingURL=modal.nomodule.js.map