UNPKG

@postnord/web-components

Version:

PostNord Web Components

159 lines (154 loc) 8.65 kB
/*! * Built with Stencil * By PostNord. */ import { proxyCustomElement, HTMLElement, createEvent, forceUpdate, h, Host } from '@stencil/core/internal/client'; import { c as close } from './close.js'; import { d as defineCustomElement$4 } from './pn-button2.js'; import { d as defineCustomElement$3 } from './pn-icon2.js'; import { d as defineCustomElement$2 } from './pn-spinner2.js'; const pnModalCss = "pn-modal .pn-modal{position:fixed;bottom:round(50%, 1px);left:round(50%, 1px);width:round(95%, 1px);max-width:45em;max-height:round(85vh, 1px);border:0.0625em solid #f3f2f2;border-radius:0.5em;transform:translate(-50%, 60%);box-shadow:0 0.25em 0.875em rgba(0, 0, 0, 0.18), 0 1.625em 3.5em rgba(0, 0, 0, 0.22);background-color:#ffffff;z-index:999;pointer-events:none;opacity:0;visibility:hidden;overflow-y:auto;transition:visibility 0s linear 0.55s, opacity 0.25s 0.15s, transform 0.55s cubic-bezier(0.7, 0, 0.3, 1)}pn-modal[data-open] .pn-modal{visibility:visible;opacity:1;pointer-events:auto;transition:visibility 0s linear 0s, opacity 0.55s, transform 0.55s cubic-bezier(0.7, 0, 0.3, 1);transform:translate(round(-50%, 1px), round(50%, 1px))}.pn-modal>.pn-modal-content{width:100%;height:round(100%, 1px);padding:1.5em}.pn-modal>.pn-modal-close-button{position:absolute;right:1em;top:1em}pn-modal .pn-modal>[slot=buttons]{position:static;width:100%;background-color:#ffffff;border-top:0.0625em solid #d3cecb;padding:0.5em 1em;display:flex;justify-content:flex-end;flex-wrap:wrap-reverse}pn-modal .pn-modal>[slot=buttons]>*{margin:0.5em}pn-modal>.pn-modal-backdrop{position:fixed;width:round(120vw, 1px);height:round(120vh, 1px);top:round(-10vh, 1px);left:round(-10vw, 1px);background-color:#000000;opacity:0;z-index:9;visibility:hidden;transition:visibility 0s linear 0.3s, opacity 0.3s}pn-modal[data-open] .pn-modal-backdrop{opacity:0.55;visibility:visible;transition:visibility 0s linear 0s, opacity 0.3s}@media screen and (max-width: 30em){pn-modal .pn-modal{width:round(100%, 1px);max-height:round(95vh, 1px);bottom:0;transform:translate(round(-50%, 1px), round(10%, 1px));border-radius:0.5em 0.5em 0 0}pn-modal[data-open] .pn-modal{transform:translate(round(-50%, 1px), 0)}pn-modal .pn-modal>[slot=buttons]{justify-content:center}pn-modal .pn-modal>[slot=buttons]>*{flex:1 0 auto}}@supports (position: sticky){pn-modal .pn-modal>[slot=buttons]{position:sticky;bottom:0;left:0;z-index:2}pn-modal .pn-modal>.pn-modal-close-button{position:sticky;left:calc(100% - 3em);margin:1em 1em 0 0;z-index:10}pn-modal .pn-modal>.pn-modal-content{margin-top:-3em}}"; const PnModalStyle0 = pnModalCss; const PnModal$1 = /*@__PURE__*/ proxyCustomElement(class PnModal extends HTMLElement { constructor() { super(); this.__registerHost(); this.close = createEvent(this, "close", 7); this.focusableEls = undefined; this.open = false; } mo; untabbable; elToFocus; handleFocus = this.focusHandler.bind(this); handleBlur = this.blurHandler.bind(this); handleEsc = this.escHandler.bind(this); handleGlobalClick = this.globalClickHandler.bind(this); get hostElement() { return this; } handleOpen() { if (this.open) { this.addEventListeners(); } else { this.removeEventListeners(); this.elToFocus = null; this.close.emit(this.open); } } /** Event fired when the modal is closed, either by clicking on the dark background or by clicking on the close button. */ close; componentDidLoad() { if (this.mo) this.mo.disconnect(); this.mo = new MutationObserver(() => { forceUpdate(this.hostElement); this.setFocusableElements(); }); this.mo.observe(this.hostElement.querySelector('.pn-modal'), { childList: true, subtree: true }); // If the modal is opened when the page loads we still want to register the events. if (this.open) this.handleOpen(); this.setFocusableElements(); } toggleOpen(state) { this.open = state ?? !this.open; } setFocusableElements() { // This place is where I see the most coming changes/bugs taking place. requestAnimationFrame(() => { this.focusableEls = Array.from(this.hostElement.querySelectorAll('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"]):not(.pn-modal-backdrop)')); this.untabbable = Array.from(this.hostElement.querySelectorAll('[tabindex="-1"]')); }); } addEventListeners() { const root = this.hostElement.getRootNode(); root.addEventListener('focusin', this.handleFocus); root.addEventListener('focusout', this.handleBlur); root.addEventListener('keydown', this.handleEsc); // Adding RAF to ensure clicks aren't registered before the modal has opened. requestAnimationFrame(() => document.addEventListener('pointerdown', this.handleGlobalClick)); } removeEventListeners() { const root = this.hostElement.getRootNode(); root.removeEventListener('focusin', this.handleFocus); root.removeEventListener('focusout', this.handleBlur); root.removeEventListener('keydown', this.handleEsc); document.removeEventListener('pointerdown', this.handleGlobalClick); } focusHandler(e) { const target = e.composedPath()[0]; if ((!this.focusableEls.includes(target) && !this.untabbable.includes(target)) || target.classList.contains('pn-modal-backdrop')) { if (this.elToFocus) { this.elToFocus.focus(); return; } this.focusableEls[0].focus(); } } blurHandler(e) { const target = e.composedPath?.()[0]; const index = this.focusableEls.indexOf(target); const numberOfEls = this.focusableEls.length - 1; if (index === 0) this.elToFocus = this.focusableEls[numberOfEls]; if (index === numberOfEls) this.elToFocus = this.focusableEls[0]; } escHandler({ code }) { if (code === 'Escape') this.toggleOpen(false); } globalClickHandler(e) { const target = e.composedPath?.()[0]; if (!this.hostElement.contains(target) || target.classList.contains('pn-modal-backdrop')) { // This is to prevent the focus and blur events from being triggered when closing the modal. e.preventDefault(); this.toggleOpen(false); } } render() { return (h(Host, { key: 'e147efddf99cb12726295253e89ec8f83c16d6a1', "data-open": this.open }, h("div", { key: '9ad10f6e7fdb2445d6b1a99801bd416cc77df976', class: "pn-modal-backdrop", tabindex: "0" }), h("div", { key: '479ee53eac130748bc33d2b49da57fe183a26c34', class: "pn-modal" }, h("pn-button", { key: 'fe7d40eed5b260949e889ed172d77311748873e9', small: true, class: "pn-modal-close-button", icon: close, iconOnly: true, arialabel: "close", appearance: "light", type: "button", onPnClick: () => this.toggleOpen() }), h("div", { key: '6f0a21860e2eaa16f01b87ac11cce3095768e0d9', class: "pn-modal-content" }, h("slot", { key: 'd15335e6f44b2a4b4d394bd3779e65ab2f3d7d4f' })), h("slot", { key: '24b457f92a61552a0cdd375c97d2a3cc1106013d', name: "buttons" })))); } static get watchers() { return { "open": ["handleOpen"] }; } static get style() { return PnModalStyle0; } }, [4, "pn-modal", { "open": [1028], "focusableEls": [32] }, undefined, { "open": ["handleOpen"] }]); function defineCustomElement$1() { if (typeof customElements === "undefined") { return; } const components = ["pn-modal", "pn-button", "pn-icon", "pn-spinner"]; components.forEach(tagName => { switch (tagName) { case "pn-modal": if (!customElements.get(tagName)) { customElements.define(tagName, PnModal$1); } break; case "pn-button": if (!customElements.get(tagName)) { defineCustomElement$4(); } break; case "pn-icon": if (!customElements.get(tagName)) { defineCustomElement$3(); } break; case "pn-spinner": if (!customElements.get(tagName)) { defineCustomElement$2(); } break; } }); } const PnModal = PnModal$1; const defineCustomElement = defineCustomElement$1; export { PnModal, defineCustomElement }; //# sourceMappingURL=pn-modal.js.map