dap-design-system
Version:
Official design system for the DÁP (dap.gov.hu)
532 lines (531 loc) • 18.2 kB
JavaScript
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"}
=${this.handleEscClose}
=${this.handleCancel}
=${this.handleClick}
=${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")}
=${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}
=${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}
=${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