UNPKG

@umbraco-ui/uui-modal

Version:

Umbraco UI modal component

442 lines (436 loc) 15.7 kB
import { LitElement, css, html } from 'lit'; import { query, property, state } from 'lit/decorators.js'; import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; var __defProp$2 = Object.defineProperty; var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor; var __decorateClass$3 = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp$2(target, key, result); return result; }; const UUIModalOpenEvent = "uui:modal-open"; const UUIModalCloseEvent = "uui:modal-close"; const UUIModalCloseEndEvent = "uui:modal-close-end"; class UUIModalElement extends LitElement { constructor() { super(...arguments); this.isOpen = false; this.isClosing = false; this.index = 0; this.uniqueIndex = 0; this._transitionDuration = 250; this.open = (event) => { event?.preventDefault(); event?.stopImmediatePropagation(); const openEvent = new CustomEvent(UUIModalOpenEvent, { cancelable: true }); const legacyOpenEvent = new CustomEvent("open", { cancelable: true }); this.dispatchEvent(openEvent); this.dispatchEvent(legacyOpenEvent); if (openEvent.defaultPrevented || legacyOpenEvent.defaultPrevented) return; this._openModal(); }; this.close = (event) => { event?.preventDefault(); event?.stopImmediatePropagation(); const closeEvent = new CustomEvent(UUIModalCloseEvent, { cancelable: true }); this.dispatchEvent(closeEvent); if (closeEvent.defaultPrevented) return; this.forceClose(); }; } get transitionDuration() { return this._transitionDuration; } set transitionDuration(value) { this._transitionDuration = value; this.style.setProperty( "--uui-modal-transition-duration", this._transitionDuration + "ms" ); } firstUpdated(_changedProperties) { super.firstUpdated(_changedProperties); if (!this.isClosing) { this.open(); } } _openModal() { this.isOpen = true; this._dialogElement?.showModal(); this._dialogElement?.addEventListener("cancel", this.close); } forceClose() { this.isClosing = true; this.isOpen = false; this._dialogElement?.close(); this.dispatchEvent(new CustomEvent("close-end")); this.dispatchEvent(new CustomEvent(UUIModalCloseEndEvent)); this.remove(); } static { this.styles = [ css` dialog { display: block; margin: 0; padding: 0; max-width: unset; max-height: unset; border: none; background: none; color: var(--uui-color-text,#060606); } dialog::backdrop { background: none; opacity: 0; } dialog::after { content: ''; position: absolute; inset: 0; background-color: var(--uui-modal-color-backdrop, rgba(0, 0, 0, 0.5)); pointer-events: none; opacity: 1; transition: opacity var(--uui-modal-transition-duration, 250ms); z-index: 1; } :host([index='0']) dialog::after { opacity: 0; } ` ]; } } __decorateClass$3([ query("dialog") ], UUIModalElement.prototype, "_dialogElement", 2); __decorateClass$3([ property({ type: Boolean, reflect: true, attribute: "is-open" }) ], UUIModalElement.prototype, "isOpen", 2); __decorateClass$3([ property({ type: Boolean, reflect: true, attribute: "is-closing" }) ], UUIModalElement.prototype, "isClosing", 2); __decorateClass$3([ property({ type: Number, reflect: true }) ], UUIModalElement.prototype, "index", 2); __decorateClass$3([ property({ type: Number, reflect: true, attribute: "unique-index" }) ], UUIModalElement.prototype, "uniqueIndex", 2); __decorateClass$3([ property({ type: Number, attribute: "transition-duration" }) ], UUIModalElement.prototype, "transitionDuration", 1); var __defProp$1 = Object.defineProperty; var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor; var __typeError$1 = (msg) => { throw TypeError(msg); }; var __decorateClass$2 = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp$1(target, key, result); return result; }; var __accessCheck$1 = (obj, member, msg) => member.has(obj) || __typeError$1("Cannot " + msg); var __privateGet$1 = (obj, member, getter) => (__accessCheck$1(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd$1 = (obj, member, value) => member.has(obj) ? __typeError$1("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var _UUIModalSidebarElement_instances, getWidth_get; let UUIModalSidebarElement = class extends UUIModalElement { constructor() { super(...arguments); __privateAdd$1(this, _UUIModalSidebarElement_instances); this.size = "full"; } firstUpdated(_changedProperties) { super.firstUpdated(_changedProperties); this.style.setProperty("--uui-modal-offset", -__privateGet$1(this, _UUIModalSidebarElement_instances, getWidth_get) + "px"); } updated(_changedProperties) { super.updated(_changedProperties); if (this.uniqueIndex > 10) { this.setAttribute("hide", ""); } else { this.removeAttribute("hide"); } } forceClose() { if (this.isClosing) return; this.isClosing = true; this.style.setProperty("--uui-modal-offset", -__privateGet$1(this, _UUIModalSidebarElement_instances, getWidth_get) + "px"); setTimeout(() => { super.forceClose(); }, this.transitionDuration); } render() { return html`<dialog> <slot></slot> </dialog>`; } }; _UUIModalSidebarElement_instances = new WeakSet(); getWidth_get = function() { return this._dialogElement?.getBoundingClientRect().width ?? 0; }; UUIModalSidebarElement.styles = [ ...UUIModalElement.styles, css` :host { outline: none; --uui-modal-sidebar-left-gap: 24px; --uui-modal-sidebar-background: var(--uui-color-surface,#fff); } @media (min-width: 600px) { :host { --uui-modal-sidebar-left-gap: 64px; } } dialog { height: 100%; width: 100%; box-sizing: border-box; max-width: calc(100% - var(--uui-modal-sidebar-left-gap)); margin-left: auto; right: var(--uui-modal-offset); transition: right var(--uui-modal-transition-duration, 250ms); background: var( --uui-modal-sidebar-background, var(--uui-color-surface,#fff) ); } :host([index='0']) dialog { box-shadow: var(--uui-shadow-depth-5,0 19px 38px rgba(0,0,0,0.30) , 0 15px 12px rgba(0,0,0,0.22)); } :host(:not([index='0'])) dialog { outline: 1px solid rgba(0, 0, 0, 0.1); } :host([hide]) dialog { display: none; } :host([size='large']) dialog { max-width: min(1200px, calc(100% - var(--uui-modal-sidebar-left-gap))); } :host([size='medium']) dialog { max-width: min(800px, calc(100% - var(--uui-modal-sidebar-left-gap))); } :host([size='small']) dialog { max-width: min(500px, calc(100% - var(--uui-modal-sidebar-left-gap))); } ` ]; __decorateClass$2([ property({ reflect: true }) ], UUIModalSidebarElement.prototype, "size", 2); UUIModalSidebarElement = __decorateClass$2([ defineElement("uui-modal-sidebar") ], UUIModalSidebarElement); var __defProp = Object.defineProperty; var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor; var __typeError = (msg) => { throw TypeError(msg); }; var __decorateClass$1 = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp(target, key, result); return result; }; var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); var _onSlotChange, _onCloseModalClose, _UUIModalContainerElement_instances, updateModals_fn, updateSidebars_fn; let UUIModalContainerElement = class extends LitElement { constructor() { super(); __privateAdd(this, _UUIModalContainerElement_instances); this.sidebarGap = 64; this.transitionDurationMS = 250; __privateAdd(this, _onSlotChange, () => { const existingModals = this._modals ?? []; this._modals = this.modalSlot?.assignedElements({ flatten: true }).filter( (el) => el instanceof UUIModalElement ) ?? []; const oldModals = existingModals.filter( (modal) => this._modals.indexOf(modal) === -1 ); oldModals.forEach( (modal) => modal.removeEventListener(UUIModalCloseEvent, __privateGet(this, _onCloseModalClose)) ); const newModals = this._modals.filter( (modal) => existingModals.indexOf(modal) === -1 ); newModals.forEach( (modal) => modal.addEventListener(UUIModalCloseEvent, __privateGet(this, _onCloseModalClose)) ); this._sidebars = this._modals.filter( (el) => el instanceof UUIModalSidebarElement ); if (this._modals.length === 0) { this.removeAttribute("backdrop"); return; } __privateMethod(this, _UUIModalContainerElement_instances, updateModals_fn).call(this); __privateMethod(this, _UUIModalContainerElement_instances, updateSidebars_fn).call(this); }); __privateAdd(this, _onCloseModalClose, (event) => { event.stopImmediatePropagation(); event.target?.removeEventListener( UUIModalCloseEvent, __privateGet(this, _onCloseModalClose) ); if (!this._modals || this._modals.length <= 1) { this.removeAttribute("backdrop"); return; } __privateMethod(this, _UUIModalContainerElement_instances, updateModals_fn).call(this); __privateMethod(this, _UUIModalContainerElement_instances, updateSidebars_fn).call(this); }); this.addEventListener(UUIModalCloseEvent, __privateGet(this, _onCloseModalClose)); } firstUpdated(_changedProperties) { super.firstUpdated(_changedProperties); this.style.setProperty( "--uui-modal-transition-duration", this.transitionDurationMS + "ms" ); } render() { return html`<slot @slotchange=${__privateGet(this, _onSlotChange)}></slot>`; } }; _onSlotChange = new WeakMap(); _onCloseModalClose = new WeakMap(); _UUIModalContainerElement_instances = new WeakSet(); updateModals_fn = function() { this.setAttribute("backdrop", ""); const reverse = this._modals?.filter((modal) => !modal.isClosing).reverse() ?? []; reverse?.forEach((modal, index) => { modal.index = index; modal.transitionDuration = this.transitionDurationMS; }); reverse?.forEach((modal) => { const sameType = reverse?.filter( (m) => m.constructor.name === modal.constructor.name ); modal.uniqueIndex = sameType?.indexOf(modal) ?? 0; }); }; updateSidebars_fn = function() { requestAnimationFrame(() => { let sidebarOffset = 0; const reversed = this._sidebars?.filter((modal) => !modal.isClosing).reverse() ?? []; for (let i = 0; i < reversed.length; i++) { const sidebar = reversed[i]; const nextSidebar = reversed[i + 1]; const tempSidebarOffset = sidebarOffset; sidebar.updateComplete.then(() => { sidebar.style.setProperty( "--uui-modal-offset", tempSidebarOffset + "px" ); }); if (nextSidebar?.hasAttribute("hide")) break; const currentWidth = sidebar.shadowRoot?.querySelector("dialog")?.getBoundingClientRect().width ?? 0; const nextWidth = nextSidebar?.shadowRoot?.querySelector("dialog")?.getBoundingClientRect().width ?? 0; const distance = currentWidth + sidebarOffset + this.sidebarGap - nextWidth; sidebarOffset = distance > 0 ? distance : 0; } }); }; UUIModalContainerElement.styles = css` :host { position: fixed; --uui-modal-color-backdrop: rgba(0, 0, 0, 0.5); } :host::after { content: ''; position: fixed; inset: 0; background-color: var(--uui-modal-color-backdrop, rgba(0, 0, 0, 0.5)); opacity: 0; pointer-events: none; transition: opacity var(--uui-modal-transition-duration, 250ms); } :host([backdrop])::after { opacity: 1; } `; __decorateClass$1([ query("slot") ], UUIModalContainerElement.prototype, "modalSlot", 2); __decorateClass$1([ state() ], UUIModalContainerElement.prototype, "_modals", 2); __decorateClass$1([ state() ], UUIModalContainerElement.prototype, "_sidebars", 2); __decorateClass$1([ property({ type: Number }) ], UUIModalContainerElement.prototype, "sidebarGap", 2); __decorateClass$1([ property({ type: Number }) ], UUIModalContainerElement.prototype, "transitionDurationMS", 2); UUIModalContainerElement = __decorateClass$1([ defineElement("uui-modal-container") ], UUIModalContainerElement); var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (decorator(result)) || result; return result; }; let UUIModalDialogElement = class extends UUIModalElement { render() { return html` <dialog> <slot></slot> </dialog> `; } }; UUIModalDialogElement.styles = [ ...UUIModalElement.styles, css` :host { outline: none; --uui-modal-dialog-background: var(--uui-color-surface,#fff); } dialog { margin: auto; max-width: 100%; max-height: 100%; border-radius: var( --uui-modal-dialog-border-radius, calc(var(--uui-border-radius,3px) * 4) ); background: var( --uui-modal-dialog-background, var(--uui-color-surface,#fff) ); } :host([index='0']) dialog { box-shadow: var(--uui-shadow-depth-5,0 19px 38px rgba(0,0,0,0.30) , 0 15px 12px rgba(0,0,0,0.22)); } :host(:not([index='0'])) dialog { outline: 1px solid rgba(0, 0, 0, 0.1); } ` ]; UUIModalDialogElement = __decorateClass([ defineElement("uui-modal-dialog") ], UUIModalDialogElement); export { UUIModalCloseEndEvent, UUIModalCloseEvent, UUIModalContainerElement, UUIModalDialogElement, UUIModalElement, UUIModalOpenEvent, UUIModalSidebarElement };