@postnord/web-components
Version:
PostNord Web Components
248 lines (242 loc) • 15.5 kB
JavaScript
/*!
* Built with Stencil
* By PostNord.
*/
import { proxyCustomElement, HTMLElement, createEvent, forceUpdate, h, Host } from '@stencil/core/internal/client';
import { r as reduceMotion, k as awaitTopbar, e as en, i as isSmallScreen } from './helpers.js';
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 translations = {
CLOSE_MODAL: {
en: 'Close dialog',
sv: 'Stäng dialogrutan',
da: 'Luk dialogboks',
fi: 'Sulje valintaikkuna',
no: 'Lukk dialogboksen',
},
};
const pnModalCss = "/* Global utility variables */\n/* Input styles */\n/* Transition variables */\npn-modal .pn-modal {\n --pn-modal-max-width: 45em;\n z-index: 10000;\n position: fixed;\n top: 0;\n inset-block-start: 0;\n inset-block-end: 0;\n margin: auto;\n padding: 0;\n max-width: var(--pn-modal-max-width);\n overflow: hidden;\n border: 0;\n border-radius: 1.5em;\n box-shadow: 0 0.25em 0.875em rgba(0, 0, 0, 0.18), 0 1.625em 3.5em rgba(0, 0, 0, 0.22);\n background-color: #ffffff;\n display: none;\n opacity: 0;\n transform: translate(0%, 20%);\n outline: 0.2rem solid transparent;\n outline-offset: 0.2rem;\n}\npn-modal .pn-modal:focus-visible {\n outline-color: #ffffff;\n}\npn-modal .pn-modal {\n transition-property: opacity, overlay, display, transform, outline-color, border-radius;\n transition-duration: 0.4s;\n transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);\n}\n@media (prefers-reduced-motion: reduce) {\n pn-modal .pn-modal {\n transition-duration: 0s;\n transition-delay: 0s;\n }\n}\npn-modal .pn-modal {\n transition-behavior: allow-discrete;\n}\npn-modal .pn-modal[data-allow-overflow] {\n overflow: unset;\n}\npn-modal .pn-modal[data-allow-overflow] .pn-modal-container {\n overflow: unset;\n}\npn-modal .pn-modal[data-allow-overflow] .pn-modal-picture {\n border-top-right-radius: 1.5em;\n}\npn-modal .pn-modal[data-image] {\n padding-top: 0;\n}\npn-modal .pn-modal[data-sheet] {\n margin: 0 0 0 auto;\n height: 100%;\n max-height: unset;\n border-radius: 1.5em 0 0 1.5em;\n transform: translate(20%, 0%);\n}\npn-modal .pn-modal[data-sheet] .pn-modal-container {\n max-height: unset;\n}\npn-modal .pn-modal[open] {\n display: flex;\n opacity: 1;\n transform: translate(0%, 0%);\n}\n@starting-style {\n pn-modal .pn-modal[open] {\n display: flex;\n opacity: 0;\n transform: translate(0%, 20%);\n }\n}\npn-modal .pn-modal[open][data-sheet] {\n transform: translate(0%, 0%);\n}\n@starting-style {\n pn-modal .pn-modal[open][data-sheet] {\n transform: translate(20%, 0%);\n }\n}\npn-modal .pn-modal::backdrop {\n background-color: rgba(0, 0, 0, 0);\n transition-property: opacity, background-color, overlay, display;\n transition-duration: 0.4s;\n transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);\n}\n@media (prefers-reduced-motion: reduce) {\n pn-modal .pn-modal::backdrop {\n transition-duration: 0s;\n transition-delay: 0s;\n }\n}\npn-modal .pn-modal::backdrop {\n transition-behavior: allow-discrete;\n}\npn-modal .pn-modal[open]::backdrop {\n background-color: rgba(0, 0, 0, 0.55);\n}\n@starting-style {\n pn-modal .pn-modal[open]::backdrop {\n background-color: rgba(0, 0, 0, 0);\n }\n}\npn-modal .pn-modal-container {\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n height: 100%;\n overflow: hidden auto;\n}\npn-modal .pn-modal-container::-webkit-scrollbar {\n background-color: #ffffff;\n width: 0.875em;\n border-radius: 0.5em;\n}\npn-modal .pn-modal-container::-webkit-scrollbar-track {\n background-color: #ffffff;\n border-radius: 0.5em;\n}\npn-modal .pn-modal-container::-webkit-scrollbar-thumb {\n cursor: pointer;\n background-color: #969087;\n border-radius: 1em;\n border: 0.25em solid #ffffff;\n}\npn-modal .pn-modal-container::-webkit-scrollbar-thumb:hover {\n background-color: #5e554a;\n}\npn-modal .pn-modal-container::-webkit-scrollbar-corner, pn-modal .pn-modal-container::-webkit-scrollbar-button {\n display: none;\n}\npn-modal .pn-modal-close-button {\n position: absolute;\n z-index: 10;\n right: 0.75em;\n top: 0.75em;\n}\npn-modal .pn-modal-header {\n display: flex;\n flex-direction: column;\n gap: 0.5em;\n padding: clamp(1em, 5vw, 1.5em);\n}\npn-modal .pn-modal-header[hidden] {\n display: none;\n}\npn-modal .pn-modal-header:not([hidden]) + .pn-modal-content:not([hidden]) {\n padding-top: 0;\n}\npn-modal .pn-modal-label {\n line-height: 1.5;\n padding-right: 1.5em;\n margin: 0;\n}\npn-modal .pn-modal-text {\n margin: 0;\n}\npn-modal .pn-modal-content {\n padding: clamp(1em, 5vw, 1.5em);\n}\npn-modal .pn-modal-image {\n position: relative;\n display: flex;\n flex-direction: column;\n}\npn-modal .pn-modal-image[hidden] {\n display: none;\n}\npn-modal .pn-modal-picture {\n margin: 0;\n padding: 0;\n display: block;\n overflow: hidden;\n border-radius: 1.5em 0 0 0;\n}\npn-modal .pn-modal-picture > *[slot=image] {\n display: block;\n height: 100%;\n width: 100%;\n object-fit: cover;\n}\npn-modal .pn-modal-buttons {\n background-color: #ffffff;\n border-top: 0.0625em solid #d3cecb;\n padding: clamp(1em, 5vw, 1.5em);\n display: flex;\n justify-content: flex-end;\n gap: 0.5em;\n border-radius: 0 0 1.5em 1.5em;\n}\npn-modal .pn-modal-buttons > [slot=buttons]:not(pn-button) {\n display: flex;\n justify-content: flex-end;\n gap: 0.5em;\n}\npn-modal .pn-modal-buttons > [slot=buttons]:not(pn-button) > [data-left] {\n margin-right: auto;\n}\npn-modal .pn-modal-buttons > [data-left] {\n margin-right: auto;\n}\npn-modal .pn-modal-buttons:empty {\n display: none;\n}\n\n@media (max-width: 55em) {\n pn-modal .pn-modal {\n max-height: 95vh;\n margin-bottom: 0;\n bottom: 0;\n transform: translate(0%, 20%);\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n }\n}";
const PnModal$1 = /*@__PURE__*/ proxyCustomElement(class PnModal extends HTMLElement {
constructor(registerHost) {
super();
if (registerHost !== false) {
this.__registerHost();
}
this.modalToggle = createEvent(this, "modalToggle", 7);
this.modalVisiblity = createEvent(this, "modalVisiblity", 7);
this.close = createEvent(this, "close", 7);
}
mo;
standardAnimationDuration = 400;
animationDuration = this.standardAnimationDuration;
modalTimeout;
modalElement;
modalContainer;
modalPicture;
get hostElement() { return this; }
isClosing = false;
isOpening = false;
/** If true, the modal content will remove overflow CSS property. */
removeOverflow = false;
/** Set a label for the modal. @since v7.14.0 */
label;
/** Set a descriptive text for the modal. @since v7.14.0 */
helpertext;
/** Set the language. @since v7.14.0 */
language = null;
/** Bind to this property if you want to control the visibility of the modal from your own data. @category Features */
open = false;
/**
* Prevent users from closing the modal by clicking on the backdrop or the `ESC` key.
* @since v7.14.0
* @category Features
*/
persistent = false;
/**
* Hide the close button. If you disable this, make sure you build your own cancel button.
* @since v7.14.0
* @category Features
*/
hideClose = false;
/**
* Allow overflow if possible. When the modal opens, it will check scrollHeight > offsetHeight.
* Do not use if you have a lot of conditional rendering inside the modal.
* @since v7.14.0
*/
allowOverflow = false;
/**
* Use the `sheet` visual. Aligns the modal to the right.
* @since v7.14.0
* @category Visual
*/
sheet = false;
/**
* Define your own max width of the modal. Default is `45em`.
* @category Visual
* @since v7.14.0
*/
maxWidth = null;
handleOpen() {
if (this.open)
this.openModal();
else {
this.closeModal();
/** In the next update, we can remove this one so you can finally stack modals. */
this.close.emit(true);
}
this.handleOverflow();
clearTimeout(this.modalTimeout);
this.isClosing = !this.open && true;
if (reduceMotion())
this.animationDuration = 0;
else
this.animationDuration = this.standardAnimationDuration;
this.modalTimeout = setTimeout(() => {
this.isClosing = false;
this.modalVisiblity.emit({ visible: this.open });
}, this.animationDuration);
}
handleMaxWidth() {
const width = this.maxWidth || '45em';
this.modalElement.style.setProperty('--pn-modal-max-width', width);
}
handleOverflow() {
if (this.allowOverflow)
this.setOverflow();
}
/** This event is fired when the modal is toggled. {@since v7.14.0} */
modalToggle;
/** This event is dispatched after the opening/closing animation has finished playing. {@since v7.14.0} */
modalVisiblity;
/**
* Event fired when the modal is closed, either by clicking on the dark background or by clicking on the close button.
* @deprecated Use the new `modalToggle` event instead.
*/
close;
connectedCallback() {
this.mo = new MutationObserver(() => forceUpdate(this.hostElement));
this.mo.observe(this.hostElement, { childList: true, subtree: true });
}
disconnectedCallback() {
if (this.mo)
this.mo.disconnect();
}
async componentWillLoad() {
if (this.language === null)
await awaitTopbar(this.hostElement);
}
componentDidLoad() {
this.handleMaxWidth();
if (this.open)
this.openModal();
}
translate(prop) {
return translations?.[prop]?.[this.language || en] || prop;
}
showImage() {
return !!this.modalPicture?.innerHTML;
}
showHeader() {
return !!(this.label || this.helpertext || this.hostElement.querySelector('[slot="header"]')?.textContent);
}
setOverflow() {
this.removeOverflow = !this.hasOverflow();
}
hasOverflow() {
const multiplyWith = isSmallScreen() ? 0.95 : 0.85;
return this.modalContainer?.scrollHeight > innerHeight * multiplyWith;
}
isSameModal(target) {
return this.modalElement.isSameNode(target);
}
handleKeyboard(event) {
if (event.key !== 'Escape')
return;
event.preventDefault();
event.stopImmediatePropagation();
if (!this.persistent)
this.setModalClose();
}
clickModalBackground(event) {
const { clientY, clientX } = event;
const notInsideAnyModal = event.target.localName !== this.modalElement.localName;
const notThisModal = !this.isSameModal(event.target);
if (notInsideAnyModal || notThisModal)
return;
const { top, left, height, width } = this.modalElement.getBoundingClientRect();
const isInModal = top <= clientY && clientY <= top + height && left <= clientX && clientX <= left + width;
if (!isInModal && !this.persistent)
this.toggleOpen(false);
}
setModalClose() {
this.toggleOpen(false);
}
closeModal() {
this.modalElement.close();
}
openModal() {
this.modalElement.showModal();
}
toggleOpen(state) {
this.open = state ?? !this.open;
}
render() {
return (h(Host, { key: 'd965a05833b311fed1f1f7ba245c9401ab77b765' }, h("dialog", { key: 'd60d253a75dd3f82b684fce659a9a917d6181c62', class: "pn-modal", "data-closing": this.isClosing, "data-sheet": this.sheet, "data-image": this.showImage(), "data-allow-overflow": this.removeOverflow, onClose: () => this.setModalClose(), onCancel: () => this.setModalClose(), onToggle: () => this.modalToggle.emit({ open: this.open }), onKeyDown: event => this.handleKeyboard(event), onClick: event => this.clickModalBackground(event), ref: el => (this.modalElement = el) }, !this.hideClose && (h("pn-button", { key: '4d25a2e559a8aca6286a9fc7a94f82285bd8ad4e', small: true, class: "pn-modal-close-button", icon: close, iconOnly: true, arialabel: this.translate('CLOSE_MODAL'), appearance: "light", variant: "borderless", type: "button", onPnClick: () => this.setModalClose() })), h("div", { key: '3953eef2505ccda4786177c01ae9af92874d65a1', class: "pn-modal-container", ref: el => (this.modalContainer = el) }, h("div", { key: 'de0730ef36d305f2ff2652510838df85bdbdee5e', class: "pn-modal-image", hidden: !this.showImage() }, h("picture", { key: 'c26ae160c243b630d7a1ba71a8d4cac523096cfd', class: "pn-modal-picture", ref: el => (this.modalPicture = el) }, h("slot", { key: '4d2425b17367b4ccd2215e54cfedbee6731a9daf', name: "image" }))), h("header", { key: '815bef72c5b9a2982b7dd62059314ef120221ee0', class: "pn-modal-header", hidden: !this.showHeader() }, this.label && h("h2", { key: '41ae0e9a7d8adca27764bf6be026d0bc59df9b90', class: "pn-modal-label" }, this.label), this.helpertext && h("p", { key: '4fac7c6daf49eaff4a56b7c217f49e7f0507299f', class: "pn-modal-text" }, this.helpertext), h("slot", { key: '8ce017defaa66a4bfec5ef38398b3104ed0ff7f9', name: "header" })), h("section", { key: '3ad1dc180db0ad42a4fa99dc6c9a17fc153af362', class: "pn-modal-content" }, h("slot", { key: 'bddef9aa3cd8b78be3facd5ebd3e88d72b9eb219' })), h("nav", { key: 'edc771b8ca813ed6c9b20f688847c9c22072f0a1', class: "pn-modal-buttons" }, h("slot", { key: '75dcf50e07c985f95836251881b8c214bc1b58c6', name: "buttons" }))))));
}
static get watchers() { return {
"open": ["handleOpen"],
"maxWidth": ["handleMaxWidth"]
}; }
static get style() { return pnModalCss; }
}, [260, "pn-modal", {
"label": [1],
"helpertext": [1],
"language": [1],
"open": [1540],
"persistent": [4],
"hideClose": [4, "hide-close"],
"allowOverflow": [4, "allow-overflow"],
"sheet": [4],
"maxWidth": [1, "max-width"],
"isClosing": [32],
"isOpening": [32],
"removeOverflow": [32]
}, [[9, "resize", "handleOverflow"]], {
"open": ["handleOpen"],
"maxWidth": ["handleMaxWidth"]
}]);
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
//# sourceMappingURL=pn-modal.js.map