UNPKG

dap-design-system

Version:

Official design system for the DÁP (dap.gov.hu)

532 lines (531 loc) 18.2 kB
import { clsx as u } from "../../node_modules/clsx/dist/clsx.js"; import "../../node_modules/@lit/reactive-element/reactive-element.js"; import { nothing as p, html as d } from "../../node_modules/lit-html/lit-html.js"; import "../../node_modules/lit-element/lit-element.js"; import { property as n } from "../../node_modules/@lit/reactive-element/decorators/property.js"; import { query as y } from "../../node_modules/@lit/reactive-element/decorators/query.js"; import { DdsElement as E } from "../../internal/dds-hu-element.js"; import { SizedMixin as B } from "../../internal/mixin/sizedMixin.js"; import "../../localization/localization.js"; import v from "./modal.scss.js"; import { unsafeCSS as C } from "../../node_modules/@lit/reactive-element/css-tag.js"; import { translate as f } from "../../node_modules/lit-i18n/src/lit-i18n.js"; var k = Object.defineProperty, s = (b, t, e, i) => { for (var a = void 0, l = b.length - 1, r; l >= 0; l--) (r = b[l]) && (a = r(t, e, a) || a); return a && k(t, e, a), a; }; const g = B(E), $ = 50, m = class m extends g { constructor() { super(...arguments), this.open = !1, this.fullScreen = !1, this.closeOnEsc = "true", this.closeOnOverlayClick = "true", this.closeButton = "true", this.okButtonVariant = "primary", this.cancelButtonVariant = "outline", this.okButtonDisabled = !1, this.cancelButtonDisabled = !1, this.cancelButtonDanger = !1, this.okButtonDanger = !1, this.hideOkButton = !1, this.hideCancelButton = !1, this.okButtonSize = "", this.okButtonSizeMap = "", this.cancelButtonSize = "", this.cancelButtonSizeMap = "", this.closeButtonSize = "md", this.closeButtonSizeMap = "", this.okButtonTestId = "modal-ok-button", this.cancelButtonTestId = "modal-cancel-button", this.closeButtonTestId = "modal-close-button", this.footer = "true", this.header = "true", this.wideFooter = !1, this.bottomFooter = !1, this.useNativeFocus = !1, this.fixedHeader = !1, this.fixedFooter = !1, this.noModal = !1, this.previouslyFocusedElement = null, this.focusableElements = [], this.canceled = !1, this.closeEventCanceled = !1, this.scrollLock = (t) => { t ? (document.body.style.overflow = "hidden", document.body.dataset.modalOpen = "true") : (document.body.style.overflow = "", delete document.body.dataset.modalOpen); }; } connectedCallback() { super.connectedCallback(), this.hasAttribute("aria-hidden") || (this.setAttribute("aria-hidden", "true"), this.setAttribute("tabindex", "-1")); } attributeChangedCallback(t, e, i) { t === "title" && (this.header = i ? "true" : "false"), super.attributeChangedCallback(t, e, i); } updated(t) { super.updated(t), t.has("open") && t.get("open") !== void 0 && (this.open ? this.handleOpen() : this.handleCloseAction()); } handleOpen() { this.emit("dds-before-open", void 0, { cancelable: !0 }).defaultPrevented || (this.canceled = !1, this.closeEventCanceled = !1, this.scrollLock(!0), this.previouslyFocusedElement = this.getActiveElement(), this.noModal ? this.dialog.show() : this.dialog.showModal(), this.afterTransition(() => { !this.open || (this.dropTransform(), this.emit("dds-opened", void 0, { cancelable: !0 }).defaultPrevented) || (this.focusableElements = this.getFocusableElements(), this.focusableElements.length > 0 && this.focusableElements[0].focus()); }), this.setAttribute("aria-hidden", "false"), this.setAttribute("tabindex", "0")); } handleCloseAction() { this.emit("dds-before-close", void 0, { cancelable: !0 }).defaultPrevented || (this.restoreTransform(), this.dialog.close(), this.canceled = !1, this.scrollLock(!1), this.afterTransition(() => { this.open || this.emit("dds-closed", void 0, { cancelable: !0 }).defaultPrevented || this.previouslyFocusedElement instanceof HTMLElement && this.previouslyFocusedElement.focus(); }), this.setAttribute("aria-hidden", "true"), this.setAttribute("tabindex", "-1")); } /** * Run `callback` once the dialog's transition settles -- or on a timer if no * transition runs at all. * * Focus management and the `dds-opened` / `dds-closed` events used to hang directly * off a `transitionend` listener registered `{ once: true }`. That event never fires * when the transition is skipped (`prefers-reduced-motion: reduce`, a consumer * zeroing `--dds-modal-transition-speed`, a backgrounded tab) or when it is * interrupted, so focus would never enter the dialog and would never be restored on * close. Accessibility behaviour must not depend on an animation completing. */ afterTransition(t) { const e = this.dialog; let i = !1; const a = () => { i || (i = !0, e.removeEventListener("transitionend", l), window.clearTimeout(r), t()); }, l = (c) => { c.target === e && a(); }; e.addEventListener("transitionend", l); const r = window.setTimeout( a, this.transitionDurationMs() + $ ); } /** * Apply a transform change to the dialog without animating it. * * The dialog animates its entrance with `transform` (`transition: transform ...`), * so any later transform change would try to animate too. Suppressing the * transition for one reflow makes the change instant -- and importantly avoids * the unreliable "transition to `none`" interpolation, which Chrome can leave * pinned at the start value. */ setTransformInstantly(t) { const e = this.dialog; if (!e) return; const i = e.style.transition; e.style.transition = "none", e.style.transform = t, e.offsetHeight, e.style.transition = i; } /** * Drop the dialog's transform once it is settled open. * * A settled-open dialog keeps an identity transform (`translate(0, 0)`) for the * entrance animation. Any transform -- even the identity -- makes the dialog a * containing block for `position: fixed` descendants, which clips * `floatingStrategy="fixed"` dropdowns (select / combobox / tooltip) to the * modal instead of letting them escape and overflow it. Removing it (visually a * no-op) frees those popups. Restored in `restoreTransform` before closing. */ dropTransform() { this.setTransformInstantly("none"); } /** Undo `dropTransform` so the close animation starts from the on-screen position. */ restoreTransform() { this.setTransformInstantly(""); } /** Longest transition duration + delay declared on the dialog, in milliseconds. */ transitionDurationMs() { const { transitionDuration: t, transitionDelay: e } = window.getComputedStyle( this.dialog ), i = (a) => a.split(",").reduce( (l, r) => Math.max(l, (Number.parseFloat(r) || 0) * 1e3), 0 ); return i(t) + i(e); } firstUpdated(t) { super.firstUpdated(t), this.open ? (this.setAttribute("aria-hidden", "false"), this.dialog.showModal()) : this.setAttribute("aria-hidden", "true"); } show() { this.open = !0; } hide() { this.open = !1; } handleClose(t) { if (this.emit( "dds-close", { source: t }, { cancelable: !0 } ).defaultPrevented) { this.closeEventCanceled = !0; return; } this.closeEventCanceled = !1, this.hide(); } handleCancel(t) { if (this.closeOnEsc !== "true") { t.preventDefault(), this.canceled = !0; return; } if (this.closeEventCanceled) { t.preventDefault(), this.closeEventCanceled = !1; return; } t.preventDefault(); } handleEscClose(t) { if (this.closeEventCanceled) { this.dialog.showModal(), this.closeEventCanceled = !1; return; } if (this.canceled || this.closeOnEsc !== "true") { t.preventDefault(); return; } t.preventDefault(); } handleOkButton() { this.emit( "dds-ok", { source: "ok-button" }, { cancelable: !0 } ).defaultPrevented || this.handleClose("ok-button"); } handleCancelButton() { this.emit( "dds-cancel", { source: "cancel-button" }, { cancelable: !0 } ).defaultPrevented || this.handleClose("cancel-button"); } handleCloseButton() { this.emit( "dds-cancel", { source: "close-button" }, { cancelable: !0 } ).defaultPrevented || this.handleClose("close-button"); } handleClick(t) { this.closeOnOverlayClick === "true" && !t.composedPath().includes(this.modalBox) && this.handleClose("overlay"); } handleKeyDown(t) { if (t.key === "Tab" && !this.useNativeFocus) { this.handleFocusTrap(t); return; } if (t.key === "Escape") if (this.closeOnEsc === "true") { if (this.closeEventCanceled = !1, this.emit( "dds-cancel", { source: "esc" }, { cancelable: !0 } ).defaultPrevented) { t.preventDefault(), t.stopImmediatePropagation(); return; } this.handleClose("esc"), this.closeEventCanceled && (t.preventDefault(), t.stopImmediatePropagation()); } else t.preventDefault(), t.stopImmediatePropagation(); } getFocusableElements() { const e = [ "button", "[href]", "input", "select", "textarea", '[tabindex]:not([tabindex="-1"])', '[role="button"]', '[role="link"]', '[role="menuitem"]', '[role="tab"]', '[role="option"]', '[role="radio"]', '[role="checkbox"]', '[role="switch"]', '[role="combobox"]', '[role="listbox"]', '[role="textbox"]', '[contenteditable="true"]', "dap-ds-input", "dap-ds-textarea", "dap-ds-select", "dap-ds-button", "dap-ds-icon-button", "dap-ds-search-input", "dap-ds-combobox", "dap-ds-checkbox", "dap-ds-radio", "dap-ds-switch", "dap-ds-slider", "dap-ds-file-input", "dap-ds-calendar", "dap-ds-date-picker", "slot" ].join(","), i = (a) => { const l = []; return a.querySelectorAll(e).forEach((c) => { c instanceof HTMLSlotElement ? c.assignedElements().forEach((h) => { h instanceof HTMLElement && (h.matches(e) && l.push(h), l.push(...i(h))); }) : c instanceof HTMLElement && l.push(c); }), l; }; return i(this.modalBox); } getActiveElement() { let t = document.activeElement; if (!t) return null; for (; t && t.shadowRoot; ) { const e = t.shadowRoot.activeElement; if (e) t = e; else break; } return t; } getElementFromElement(t) { if (t.shadowRoot) { const e = t; if (e.focusElement && e.focusElement instanceof HTMLElement) return e.focusElement; const i = [ "button", "[href]", "input", "select", "textarea", '[tabindex]:not([tabindex="-1"])', '[role="button"]', '[role="link"]' ], a = t.shadowRoot.querySelector( i.join(",") ); if (a instanceof HTMLElement) return a; } return t; } isElementContainedIn(t, e) { if (t === e || e.shadowRoot && e.shadowRoot.contains(t)) return !0; const i = e; return i.focusElement && i.focusElement === t ? !0 : e.contains(t); } handleFocusTrap(t) { if (t.key !== "Tab" || this.useNativeFocus) return; this.focusableElements.length === 0 && (this.focusableElements = this.getFocusableElements()); const e = this.getElementFromElement( this.focusableElements[0] ), i = this.focusableElements[this.focusableElements.length - 1], a = this.getElementFromElement( i ), l = this.getActiveElement(); l && (t.shiftKey ? (l === e || this.isElementContainedIn(l, e) || this.isElementContainedIn(l, this.focusableElements[0])) && (t.preventDefault(), a && a.focus()) : (l === a || this.isElementContainedIn(l, a) || this.isElementContainedIn(l, i)) && (t.preventDefault(), e && e.focus())); } renderModal() { var t, e, i, a; return d`<dialog part="base" class=${u("modal", { "modal--open": this.open, "modal--full": this.fullScreen, "modal--no-modal": this.noModal, [`modal--${this.size}`]: this.size })} aria-label=${`${(t = this.title) != null ? t : ""} ${(e = this.description) != null ? e : ""}`} role="dialog" aria-modal=${this.noModal ? "false" : "true"} @close=${this.handleEscClose} @cancel=${this.handleCancel} @click=${this.handleClick} @keydown=${this.handleKeyDown}> <div part="panel" class="modal__box"> <div class=${u("modal__header-container", { [`modal__header-container--${this.size}`]: this.size, [`modal__header-container--${this.size}-fixed`]: this.fixedHeader })} part="header-container"> ${this.header === "true" ? d`<header class="modal__header" part="header" id="header"> <slot name="title"> <dap-ds-typography variant="h3" part="title" >${this.title}</dap-ds-typography > </slot> <slot name="description"> <span class="modal__description" part="description" id="description"> ${this.description} </span> </slot> </header>` : d`<span></span>`} ${this.closeButton === "true" ? d`<dap-ds-icon-button part="closebutton" class="modal__close" data-testid=${this.closeButtonTestId} size=${this.closeButtonSize || this.effectiveSize} sizeMap=${this.closeButtonSizeMap || this.sizeMap} aria-label=${this.closeButtonLabel ? this.closeButtonLabel : f("close")} @click=${this.handleCloseButton}> <dap-ds-icon name="close-line"></dap-ds-icon> </dap-ds-icon-button>` : p} </div> <div class=${u("modal__content", { "modal__content--bottom": this.bottomFooter })} part="content"> <slot></slot> </div> ${this.footer === "true" ? d`<footer class=${u("modal__footer", { "modal__footer--wide": this.wideFooter, [`modal__footer--${this.size}`]: this.size, [`modal__footer--${this.size}-fixed`]: this.fixedFooter })} part="footer"> <slot name="footer"> ${this.hideOkButton ? p : d`<dap-ds-button data-testid=${this.okButtonTestId} size=${this.okButtonSize || this.effectiveSize} sizeMap=${this.okButtonSizeMap || this.sizeMap} @click=${this.handleOkButton} variant=${this.okButtonVariant} ?danger=${this.okButtonDanger} ?disabled=${this.okButtonDisabled} ?hidden=${this.hideOkButton} >${(i = this.okButtonLabel) != null ? i : f("ok")}</dap-ds-button >`} ${this.hideCancelButton ? p : d`<dap-ds-button data-testid=${this.cancelButtonTestId} size=${this.cancelButtonSize || this.effectiveSize} sizeMap=${this.cancelButtonSizeMap || this.sizeMap} variant=${this.cancelButtonVariant} ?danger=${this.cancelButtonDanger} ?disabled=${this.cancelButtonDisabled} ?hidden=${this.hideCancelButton} @click=${this.handleCancelButton} >${(a = this.cancelButtonLabel) != null ? a : f("cancel")}</dap-ds-button >`} </slot> </footer>` : p} </div> </dialog>`; } }; m.styles = C(v); let o = m; s([ y(".modal") ], o.prototype, "dialog"); s([ y(".modal__box") ], o.prototype, "modalBox"); s([ n({ type: Boolean, reflect: !0 }) ], o.prototype, "open"); s([ n({ type: Boolean, reflect: !0 }) ], o.prototype, "fullScreen"); s([ n({ type: String, reflect: !0 }) ], o.prototype, "closeOnEsc"); s([ n({ type: String, reflect: !0 }) ], o.prototype, "closeOnOverlayClick"); s([ n({ type: String, reflect: !0 }) ], o.prototype, "closeButton"); s([ n() ], o.prototype, "title"); s([ n() ], o.prototype, "description"); s([ n() ], o.prototype, "okButtonLabel"); s([ n() ], o.prototype, "cancelButtonLabel"); s([ n() ], o.prototype, "closeButtonLabel"); s([ n() ], o.prototype, "okButtonVariant"); s([ n() ], o.prototype, "cancelButtonVariant"); s([ n({ type: Boolean }) ], o.prototype, "okButtonDisabled"); s([ n({ type: Boolean }) ], o.prototype, "cancelButtonDisabled"); s([ n({ type: Boolean }) ], o.prototype, "cancelButtonDanger"); s([ n({ type: Boolean }) ], o.prototype, "okButtonDanger"); s([ n({ type: Boolean }) ], o.prototype, "hideOkButton"); s([ n({ type: Boolean }) ], o.prototype, "hideCancelButton"); s([ n() ], o.prototype, "okButtonSize"); s([ n() ], o.prototype, "okButtonSizeMap"); s([ n() ], o.prototype, "cancelButtonSize"); s([ n() ], o.prototype, "cancelButtonSizeMap"); s([ n() ], o.prototype, "closeButtonSize"); s([ n() ], o.prototype, "closeButtonSizeMap"); s([ n() ], o.prototype, "okButtonTestId"); s([ n() ], o.prototype, "cancelButtonTestId"); s([ n() ], o.prototype, "closeButtonTestId"); s([ n({ type: String }) ], o.prototype, "footer"); s([ n({ type: String }) ], o.prototype, "header"); s([ n({ type: Boolean }) ], o.prototype, "wideFooter"); s([ n({ type: Boolean }) ], o.prototype, "bottomFooter"); s([ n({ type: Boolean }) ], o.prototype, "useNativeFocus"); s([ n({ type: Boolean }) ], o.prototype, "fixedHeader"); s([ n({ type: Boolean }) ], o.prototype, "fixedFooter"); s([ n({ type: Boolean }) ], o.prototype, "noModal"); export { o as default }; //# sourceMappingURL=modal-base-element.js.map