@umbraco-ui/uui-modal
Version:
Umbraco UI modal component
442 lines (436 loc) • 15.7 kB
JavaScript
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 };