@sky.ui/core 0.0.55 → 0.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/custom-elements.json +3507 -3507
- package/dist/sky-dialog/sky-dialog.js +11 -4
- package/dist/sky-dialog/sky-dialog.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import{css as e,LitElement as t,html as o}from"lit";import{property as i,state as s,query as n,customElement as a}from"lit/decorators.js";import{when as r}from"lit/directives/when.js";import{createRef as d,ref as l}from"lit/directives/ref.js";import{classMap as c}from"lit/directives/class-map.js";import{animate as
|
|
1
|
+
import{css as e,LitElement as t,html as o}from"lit";import{property as i,state as s,query as n,customElement as a}from"lit/decorators.js";import{when as r}from"lit/directives/when.js";import{createRef as d,ref as l}from"lit/directives/ref.js";import{classMap as c}from"lit/directives/class-map.js";import{animate as p}from"@lit-labs/motion";import{SkyIcon as h}from"../sky-icon/sky-icon.js";import{PresetController as g}from"../helper/controllers/preset-controller.js";var y=Object.defineProperty,m=Object.getOwnPropertyDescriptor,u=(e,t,o,i)=>{for(var s,n=i>1?void 0:i?m(t,o):t,a=e.length-1;a>=0;a--)(s=e[a])&&(n=(i?s(t,o,n):s(n))||n);return i&&n&&y(t,o,n),n};let f=0,b=0,k=class extends t{constructor(){super(),this.open=!1,this.closable=!1,this.message="",this.overlay=!1,this.width="auto",this.separator=!1,this.persistent=!1,this.closeOnEsc=!1,this.headerPadding="sm",this.bodyPadding="sm",this.footerPadding="sm",this.highlightOnOutsideClick=!0,this._hasHeader=!1,this._hasBody=!1,this._hasFooter=!1,this._isClosing=!1,this._openAnimation=null,this._closeAnimation=null,this._highlightAnimation=null,this.dialogRef=d(),this._onBackdropPointerDown=e=>{const t=this.dialogElement;t&&t.open&&e.target===t&&(this.persistent?this._highlightDialog():this._requestClose("backdrop"))},this.preset="",this._presets=new g(this),this._onNativeClose=()=>{this.open&&requestAnimationFrame(()=>{this.dialogElement?.open||this.dialogElement?.showModal()})},this._onHeaderSlotChange=e=>{const t=e.target;this._hasHeader=this._slotHasContent(t)},this._onBodySlotChange=e=>{const t=e.target;this._hasBody=this._slotHasContent(t)},this._onFooterSlotChange=e=>{const t=e.target;this._hasFooter=this._slotHasContent(t)},this._onKeyDown=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),!this.persistent&&this.closeOnEsc?this._requestClose("escape"):this._highlightDialog())},this._handleCancel=this._handleCancel.bind(this)}firstUpdated(){const e=this.dialogElement;e&&(e.addEventListener("keydown",this._onKeyDown,{capture:!0}),e.addEventListener("pointerdown",this._onBackdropPointerDown),e.addEventListener("cancel",this._handleCancel),e.addEventListener("close",this._onNativeClose))}disconnectedCallback(){const e=this.dialogElement;e&&(e.removeEventListener("keydown",this._onKeyDown,{capture:!0}),e.removeEventListener("pointerdown",this._onBackdropPointerDown),e.removeEventListener("cancel",this._handleCancel),e.removeEventListener("close",this._onNativeClose)),super.disconnectedCallback(),this._unlockScroll(),e?.open&&e.close()}show(){this.open=!0}hide(){this.open=!1}focusDialog(){this.dialogElement&&this.dialogElement.focus()}getDialogElement(){return this.dialogElement}updated(e){e.has("open")&&requestAnimationFrame(()=>{this._handleOpenChange(e.get("open"))})}_handleOpenChange(e){this.dialogElement&&(this.open&&!this.dialogElement.open?(this._lockScroll(),this.dialogElement.showModal(),requestAnimationFrame(()=>{this._animateOpen()})):!this.open&&e&&(this._isClosing=!0,this._animateClose()))}_lockScroll(){"undefined"!=typeof window&&"undefined"!=typeof document&&(0===f&&(b=window.scrollY,window.scrollTo(0,0)),f++,document.documentElement.setAttribute("data-sky-dialog-open","true"))}_unlockScroll(){"undefined"!=typeof window&&"undefined"!=typeof document&&(f=Math.max(0,f-1),0===f&&(document.documentElement.removeAttribute("data-sky-dialog-open"),window.scrollTo(0,b)))}_prefersReducedMotion(){return!("undefined"==typeof window||!window.matchMedia)&&window.matchMedia("(prefers-reduced-motion: reduce)").matches}_animateOpen(){const e=this.dialogElement;if(!e)return;this._openAnimation?.cancel(),this._closeAnimation?.cancel();const t=this._prefersReducedMotion()?0:300,o=e.animate([{opacity:0,transform:"scale(0.95)",transformOrigin:"center center"},{opacity:1,transform:"scale(1)",transformOrigin:"center center"}],{duration:t,easing:"ease-out",fill:"forwards"});o.onfinish=()=>{this.dispatchEvent(new CustomEvent("dialog-opened",{bubbles:!0,composed:!0}))},this._openAnimation=o}_animateClose(){const e=this.dialogElement;if(!e)return;this._openAnimation?.cancel(),this._closeAnimation?.cancel();const t=this._prefersReducedMotion()?0:150,o=e.animate([{opacity:1,transform:"scale(1)",transformOrigin:"center center"},{opacity:0,transform:"scale(0.95)",transformOrigin:"center center"}],{duration:t,easing:"ease-out",fill:"forwards"});o.onfinish=()=>{e.close(),this._unlockScroll(),this._isClosing=!1,this.dispatchEvent(new CustomEvent("dialog-closed",{bubbles:!0,composed:!0}))},this._closeAnimation=o}_handleCancel(e){e.preventDefault(),!this.persistent&&this.closeOnEsc?this._requestClose("escape"):this._highlightDialog()}_highlightDialog(){if(!this.highlightOnOutsideClick)return;if(this._prefersReducedMotion())return;const e=this.dialogElement;if(!e)return;this._highlightAnimation?.cancel();const t=e.animate([{transform:"scale(1)"},{transform:"scale(1.05)"},{transform:"scale(1)"}],{duration:300,easing:"ease-in-out"});this._highlightAnimation=t}_requestClose(e){this._isClosing||this.dispatchEvent(new CustomEvent("request-close",{detail:{reason:e},bubbles:!0,composed:!0}))}_slotHasContent(e){return e.assignedNodes({flatten:!0}).some(e=>e.nodeType!==Node.TEXT_NODE||!!e.textContent?.trim())}render(){const e={none:!0,xs:!0,sm:!0,md:!0,lg:!0,xl:!0},t=e[this.headerPadding]?this.headerPadding:"md",i=e[this.bodyPadding]?this.bodyPadding:"md",s=e[this.footerPadding]?this.footerPadding:"md",n={};this._hasHeader&&(n[`header-padding-${t}`]=!0,this.separator&&(n["header-separator"]=!0));const a={};return this._hasFooter?(a.actions=!0,a[`footer-padding-${s}`]=!0,this.separator&&(a["footer-separator"]=!0)):a.actions=!0,o`
|
|
2
2
|
<dialog
|
|
3
3
|
part="dialog"
|
|
4
4
|
aria-modal="true"
|
|
5
5
|
style="max-width: ${this.width};"
|
|
6
6
|
@cancel="${this._handleCancel}"
|
|
7
7
|
${l(this.dialogRef)}
|
|
8
|
-
${
|
|
8
|
+
${p({keyframeOptions:{duration:300,easing:"ease-out"}})}
|
|
9
9
|
>
|
|
10
10
|
<slot></slot>
|
|
11
11
|
|
|
@@ -39,13 +39,15 @@ import{css as e,LitElement as t,html as o}from"lit";import{property as i,state a
|
|
|
39
39
|
</button>
|
|
40
40
|
`)}
|
|
41
41
|
</dialog>
|
|
42
|
-
`}};k.dependencies={"sky-icon":
|
|
42
|
+
`}};k.dependencies={"sky-icon":h},k.styles=e`
|
|
43
43
|
:host {
|
|
44
44
|
display: contents;
|
|
45
45
|
font-family: var(--sky-font);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/* Floating modal template (§20.3) — single canonical surface
|
|
48
|
+
/* Floating modal template (§20.3) — single canonical surface.
|
|
49
|
+
Do not set display here: author display overrides UA
|
|
50
|
+
dialog:not([open]) { display: none } and the closed dialog stays visible. */
|
|
49
51
|
dialog {
|
|
50
52
|
padding: 0;
|
|
51
53
|
margin: auto;
|
|
@@ -69,6 +71,11 @@ import{css as e,LitElement as t,html as o}from"lit";import{property as i,state a
|
|
|
69
71
|
var(--sky-box-shadow-tertiary);
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
dialog[open] {
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
}
|
|
78
|
+
|
|
72
79
|
dialog::backdrop {
|
|
73
80
|
backdrop-filter: none;
|
|
74
81
|
-webkit-backdrop-filter: none;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sky-dialog.js","sources":["../../src/sky-dialog/sky-dialog.ts"],"sourcesContent":["import { LitElement, html, css, nothing } from \"lit\";\r\nimport { customElement, property, query, state } from \"lit/decorators.js\";\r\nimport { when } from \"lit/directives/when.js\";\r\nimport { ref, createRef } from \"lit/directives/ref.js\";\r\nimport { classMap } from \"lit/directives/class-map.js\";\r\nimport { animate } from \"@lit-labs/motion\";\r\nimport { SkyIcon } from \"../sky-icon/sky-icon.js\";\r\nimport { PresetController } from \"../helper/controllers/preset-controller\";\r\n\r\nexport type SkyDialogPadding = \"none\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\r\nexport type SkyDialogRequestCloseDetail = { reason: \"backdrop\" | \"escape\" | \"close-button\" };\r\n/** Shared scroll lock state so multiple dialogs don't double-restore scroll. */\r\nlet _dialogOpenCount = 0;\r\nlet _savedScrollY = 0;\r\n\r\n/**\r\n * @element sky-dialog\r\n *\r\n * @summary Accessible modal dialog built on native `<dialog>` with controlled open state, slots, and keyboard/backdrop handling.\r\n *\r\n * @status stable\r\n * @since 1.0.0\r\n *\r\n * @documentation https://sky-ui.com/components/dialog\r\n * @dependency sky-icon\r\n *\r\n * @uiVModel open request-close sync=constant:false\r\n *\r\n * @slot header - Header content area at the top of the dialog.\r\n * @slot body - Main content area of the dialog.\r\n * @slot footer - Footer actions area at the bottom of the dialog.\r\n *\r\n * @csspart dialog - The dialog surface wrapper element.\r\n * @csspart close-button - The top-right close button.\r\n * @csspart message - Optional message text shown above the body.\r\n * @csspart header - Container for the header slot.\r\n * @csspart content - Container for the body slot.\r\n * @csspart actions - Container for the footer slot.\r\n * @csspart footer - Alias part on the footer container.\r\n *\r\n * @fires {CustomEvent<SkyDialogRequestCloseDetail>} request-close - Fired when close intent is detected and parent should update `open`.\r\n * @fires {CustomEvent<void>} dialog-opened - Fired after open animation completes.\r\n * @fires {CustomEvent<void>} dialog-closed - Fired after close animation completes.\r\n *\r\n * @property {boolean} open - Whether the dialog is visible.\r\n * @property {boolean} closable - Shows a top-right close button.\r\n * @property {string} message - Optional message displayed above body content.\r\n * @property {boolean} overlay - Applies dim/blur backdrop effect.\r\n * @property {string} width - Max width of the dialog surface.\r\n * @property {boolean} separator - Shows separators around header/footer regions.\r\n * @property {boolean} persistent - Prevents outside/Escape close and highlights surface instead.\r\n * @property {boolean} closeOnEsc - Enables close intent on Escape key.\r\n * @property {SkyDialogPadding} headerPadding - Header padding token.\r\n * @property {SkyDialogPadding} bodyPadding - Body padding token.\r\n * @property {SkyDialogPadding} footerPadding - Footer padding token.\r\n * @property {boolean} highlightOnOutsideClick - Enables highlight animation on rejected outside click.\r\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\r\n * @method show Opens the dialog by setting `open` to true.\r\n * @method hide Closes the dialog by setting `open` to false.\r\n * @method focusDialog Focuses the native dialog surface.\r\n * @method getDialogElement Returns the native dialog element reference.\r\n *\r\n * @Behavior\r\n * - Uses native `<dialog>` element with enhanced functionality\r\n * - Controlled exclusively by the `open` attribute/property\r\n * - Uses native dialog focus behavior\r\n * - Supports smooth animations with configurable timing\r\n * - Uses the floating modal surface (frosted tertiary glass)\r\n * - **Standard**: Click outside closes dialog\r\n * - **Persistent**: Click outside highlights dialog instead of closing\r\n * - **Closable**: Shows close button in top-right corner\r\n * - **Escape handling**: Configurable Escape key behavior\r\n * - Uses `aria-modal=\"true\"` for screen reader isolation\r\n * - Native dialog focus behavior\r\n * - Escape key handling with user preferences\r\n * - Close button with appropriate ARIA label\r\n * - Respects reduced motion preferences\r\n * - Smooth open/close animations with scale effects\r\n * - Highlight animation for persistent dialogs\r\n * - Configurable animation durations\r\n * - Uses Web Animations API for performance\r\n * - Works with any content in header/body/footer slots\r\n * - Compatible with form controls and interactive content\r\n * - Can be nested or stacked\r\n * - Responsive width and padding\r\n *\r\n * @example\r\n * ```html\r\n * <sky-dialog open closable width=\"420px\" separator>\r\n * <h3 slot=\"header\">Confirm Action</h3>\r\n * <p slot=\"body\">Are you sure you want to proceed?</p>\r\n * <div slot=\"footer\">\r\n * <button>Cancel</button>\r\n * <button>Confirm</button>\r\n * </div>\r\n * </sky-dialog>\r\n * ```\r\n * ```vue\r\n * <template>\r\n * <sky-dialog open closable width=\"420px\" separator>\r\n * <h3 slot=\"header\">Confirm Action</h3>\r\n * <p slot=\"body\">Are you sure you want to proceed?</p>\r\n * <div slot=\"footer\">\r\n * <button>Cancel</button>\r\n * <button>Confirm</button>\r\n * </div>\r\n * </sky-dialog>\r\n * </template>\r\n * ```\r\n * ```jsx\r\n * export default function Demo() {\r\n * return (\r\n * <sky-dialog open closable width=\"420px\" separator>\r\n * <h3 slot=\"header\">Confirm Action</h3>\r\n * <p slot=\"body\">Are you sure you want to proceed?</p>\r\n * <div slot=\"footer\">\r\n * <button>Cancel</button>\r\n * <button>Confirm</button>\r\n * </div>\r\n * </sky-dialog>\r\n * );\r\n * }\r\n * ```\r\n */\r\n@customElement(\"sky-dialog\")\r\nexport class SkyDialog extends LitElement {\r\n\r\n static dependencies: Record<string, CustomElementConstructor> = {\r\n \"sky-icon\": SkyIcon,\r\n };\r\n\r\n /** @public Whether the dialog is visible. */\r\n @property({ type: Boolean, reflect: true })\r\n open = false;\r\n\r\n /** @public Shows a top-right close button. */\r\n @property({ type: Boolean, reflect: true })\r\n closable = false;\r\n\r\n /** @public Optional message displayed above the body. */\r\n @property({ type: String })\r\n message = \"\";\r\n\r\n /** @public Applies dim/blur overlay on host. */\r\n @property({ type: Boolean, reflect: true })\r\n overlay = false;\r\n\r\n /** @public Max width of the dialog surface (e.g., \"480px\" or \"auto\"). */\r\n @property({ type: String })\r\n width = \"auto\";\r\n\r\n /** @public Shows header/footer separators. */\r\n @property({ type: Boolean, reflect: true })\r\n separator = false;\r\n\r\n /** @public Prevents close on outside click, highlights instead. */\r\n @property({ type: Boolean, reflect: true })\r\n persistent = false;\r\n\r\n /** @public Enables closing on Escape key. */\r\n @property({ type: Boolean, reflect: true })\r\n closeOnEsc = false;\r\n\r\n /** @public Padding token for header container. */\r\n @property({ type: String, reflect: true })\r\n headerPadding: SkyDialogPadding = \"sm\";\r\n\r\n /** @public Padding token for body container. */\r\n @property({ type: String, reflect: true })\r\n bodyPadding: SkyDialogPadding = \"sm\";\r\n\r\n /** @public Padding token for footer container. */\r\n @property({ type: String, reflect: true })\r\n footerPadding: SkyDialogPadding = \"sm\";\r\n\r\n\r\n /** @public Highlights dialog on outside click instead of closing. */\r\n @property({\r\n type: Boolean,\r\n reflect: true,\r\n })\r\n highlightOnOutsideClick = true;\r\n\r\n /** @private Whether header slot has content. */\r\n @state()\r\n private _hasHeader = false;\r\n\r\n /** @private Whether body slot has content. */\r\n @state()\r\n private _hasBody = false;\r\n\r\n /** @private Whether footer slot has content. */\r\n @state()\r\n private _hasFooter = false;\r\n\r\n /** @private Whether dialog is in closing animation. */\r\n @state()\r\n private _isClosing = false;\r\n\r\n /** @private Open animation instance. */\r\n private _openAnimation: Animation | null = null;\r\n\r\n /** @private Close animation instance. */\r\n private _closeAnimation: Animation | null = null;\r\n\r\n /** @private Highlight animation instance. */\r\n private _highlightAnimation: Animation | null = null;\r\n\r\n /** @private Reference to the native dialog element. */\r\n @query(\"dialog\")\r\n private dialogElement?: HTMLDialogElement;\r\n\r\n /** @public Ref for the dialog element (e.g. for focus trap or positioning). */\r\n dialogRef = createRef<HTMLDialogElement>();\r\n\r\n /** @private Handle clicks on the dialog backdrop (outside surface). */\r\n private _onBackdropPointerDown = (event: PointerEvent): void => {\r\n const dialog = this.dialogElement;\r\n if (!dialog || !dialog.open) return;\r\n\r\n // In the <dialog> element, backdrop clicks are retargeted to the dialog itself.\r\n // Clicks inside the surface target inner elements.\r\n if (event.target !== dialog) return;\r\n\r\n // \"Persistent\" dialogs don't close via outside click; they highlight instead (optional).\r\n if (this.persistent) {\r\n this._highlightDialog();\r\n return;\r\n }\r\n\r\n // Standard behavior: outside click requests close.\r\n this._requestClose(\"backdrop\");\r\n };\r\n\r\n\r\n /** Named prop preset from nearest `sky-config-provider`. */\r\n @property({ type: String, reflect: true }) preset = \"\";\r\n\r\n private _presets = new PresetController(this);\r\n\r\n static override styles = css`\r\n :host {\r\n display: contents;\r\n font-family: var(--sky-font);\r\n }\r\n\r\n /* Floating modal template (§20.3) — single canonical surface */\r\n dialog {\r\n padding: 0;\r\n margin: auto;\r\n color: var(--sky-text-primary);\r\n position: fixed;\r\n box-sizing: border-box;\r\n width: 100%;\r\n text-align: center;\r\n overflow: hidden;\r\n z-index: var(--sky-z-modal, 1300);\r\n background: var(--sky-glass-tertiary);\r\n background-image: var(--sky-glass-surface);\r\n backdrop-filter: var(--sky-blur-tertiary) var(--sky-saturation-tertiary)\r\n var(--sky-brightness-tertiary);\r\n -webkit-backdrop-filter: var(--sky-blur-tertiary)\r\n var(--sky-saturation-tertiary) var(--sky-brightness-tertiary);\r\n border: var(--sky-border-tertiary);\r\n border-radius: var(--sky-radius-tertiary);\r\n box-shadow:\r\n var(--sky-box-shadow-inset),\r\n var(--sky-box-shadow-tertiary);\r\n }\r\n\r\n dialog::backdrop {\r\n backdrop-filter: none;\r\n -webkit-backdrop-filter: none;\r\n transition: backdrop-filter 0.3s ease-in-out;\r\n }\r\n\r\n :host([overlay]) dialog::backdrop {\r\n backdrop-filter: var(--sky-blur-tertiary);\r\n -webkit-backdrop-filter: var(--sky-blur-tertiary);\r\n }\r\n\r\n .header-padding-none,\r\n .body-padding-none,\r\n .footer-padding-none {\r\n padding: var(--sky-space-0, 0);\r\n }\r\n\r\n .header-padding-xs,\r\n .body-padding-xs,\r\n .footer-padding-xs {\r\n padding: var(--sky-space-1, 4px);\r\n }\r\n\r\n .header-padding-sm,\r\n .body-padding-sm,\r\n .footer-padding-sm {\r\n padding: var(--sky-space-2, 8px);\r\n }\r\n\r\n .header-padding-md,\r\n .body-padding-md,\r\n .footer-padding-md {\r\n padding: var(--sky-space-4, 16px);\r\n }\r\n\r\n .header-padding-lg,\r\n .body-padding-lg,\r\n .footer-padding-lg {\r\n padding: var(--sky-space-6, 24px);\r\n }\r\n\r\n .header-padding-xl,\r\n .body-padding-xl,\r\n .footer-padding-xl {\r\n padding: var(--sky-space-8, 32px);\r\n }\r\n\r\n /* Close button — §20.6 inner dismiss affordance */\r\n .close-button {\r\n position: absolute;\r\n top: var(--sky-space-1\\\\.5, 6px);\r\n right: var(--sky-space-1\\\\.5, 6px);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: var(--sky-space-6, 24px);\r\n height: var(--sky-space-6, 24px);\r\n box-sizing: border-box;\r\n border-radius: var(--sky-radius-full);\r\n font-size: var(--sky-font-size-sm, 14px);\r\n cursor: pointer;\r\n color: var(--sky-text-primary);\r\n border: none;\r\n background-color: var(--sky-hover-primary);\r\n backdrop-filter: none;\r\n -webkit-backdrop-filter: none;\r\n transition: color 0.2s ease, background-color 0.2s ease;\r\n z-index: 1;\r\n }\r\n\r\n .close-button sky-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n line-height: 1;\r\n }\r\n\r\n .close-button:hover {\r\n color: var(--sky-danger-primary);\r\n }\r\n\r\n .close-button:focus-visible {\r\n outline: none;\r\n box-shadow: var(--sky-focus-ring);\r\n }\r\n\r\n /* Message */\r\n .message {\r\n padding: var(--sky-space-4, 16px) 0;\r\n margin: 0;\r\n }\r\n\r\n ::slotted([slot=\"header\"]) {\r\n font-size: var(--sky-font-size-md, 16px);\r\n font-weight: var(--sky-font-weight-bold, 700);\r\n color: var(--sky-text-primary);\r\n box-sizing: border-box;\r\n }\r\n\r\n dialog:focus,\r\n dialog:focus-visible {\r\n outline: none;\r\n }\r\n\r\n .header-separator {\r\n border-bottom: var(--sky-border-primary);\r\n }\r\n\r\n .footer-separator {\r\n border-top: var(--sky-border-primary);\r\n }\r\n\r\n sky-icon {\r\n display: inline-block;\r\n width: 1em;\r\n height: 1em;\r\n }\r\n\r\n /* RTL support */\r\n :host([dir=\"rtl\"]) .close-button {\r\n right: auto;\r\n left: var(--sky-space-1\\\\.5, 6px);\r\n }\r\n\r\n /* Animation classes */\r\n dialog.opening {\r\n opacity: 0;\r\n }\r\n\r\n dialog.closing {\r\n opacity: 1;\r\n }\r\n\r\n @media (prefers-reduced-motion: reduce) {\r\n dialog::backdrop,\r\n .close-button {\r\n transition: none;\r\n }\r\n }\r\n `;\r\n\r\n constructor() {\r\n super();\r\n this._handleCancel = this._handleCancel.bind(this);\r\n }\r\n\r\n /** @protected */\r\n override firstUpdated() {\r\n const d = this.dialogElement;\r\n if (!d) return;\r\n\r\n // Capture phase is important so we beat the native <dialog> cancel/close behavior\r\n d.addEventListener(\"keydown\", this._onKeyDown, { capture: true });\r\n d.addEventListener(\"pointerdown\", this._onBackdropPointerDown);\r\n d.addEventListener(\"cancel\", this._handleCancel as any); // optional extra safety\r\n d.addEventListener(\"close\", this._onNativeClose); // keep state in sync (below)\r\n }\r\n\r\n /** @protected */\r\n override disconnectedCallback() {\r\n const d = this.dialogElement;\r\n if (d) {\r\n d.removeEventListener(\"keydown\", this._onKeyDown, {\r\n capture: true,\r\n } as any);\r\n d.removeEventListener(\"pointerdown\", this._onBackdropPointerDown);\r\n d.removeEventListener(\"cancel\", this._handleCancel as any);\r\n d.removeEventListener(\"close\", this._onNativeClose);\r\n }\r\n super.disconnectedCallback();\r\n this._unlockScroll();\r\n if (d?.open) d.close();\r\n }\r\n\r\n /**\r\n * Opens the dialog programmatically.\r\n * @public\r\n */\r\n public show(): void {\r\n this.open = true;\r\n }\r\n\r\n /**\r\n * Closes the dialog programmatically.\r\n * @public\r\n */\r\n public hide(): void {\r\n this.open = false;\r\n }\r\n\r\n /**\r\n * Manually focuses the dialog element.\r\n * @public\r\n */\r\n public focusDialog(): void {\r\n if (this.dialogElement) {\r\n this.dialogElement.focus();\r\n }\r\n }\r\n\r\n /**\r\n * Gets the native dialog element.\r\n * @returns The native HTMLDialogElement or undefined\r\n * @public\r\n */\r\n public getDialogElement(): HTMLDialogElement | undefined {\r\n return this.dialogElement;\r\n }\r\n\r\n /** @private */\r\n private _onNativeClose = (): void => {\r\n // If parent still says open=true, reopen immediately (persistent behavior)\r\n if (this.open) {\r\n // restore modal state\r\n requestAnimationFrame(() => {\r\n if (!this.dialogElement?.open) {\r\n this.dialogElement?.showModal();\r\n }\r\n });\r\n }\r\n };\r\n\r\n /** @protected */\r\n override updated(changedProps: Map<string, any>) {\r\n if (changedProps.has(\"open\")) {\r\n // Use requestAnimationFrame to ensure DOM updates are complete\r\n requestAnimationFrame(() => {\r\n this._handleOpenChange(changedProps.get(\"open\"));\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Handles open state changes.\r\n * @param previousOpen - Previous open state\r\n * @private\r\n */\r\n private _handleOpenChange(previousOpen: boolean): void {\r\n if (!this.dialogElement) return;\r\n\r\n if (this.open && !this.dialogElement.open) {\r\n this._lockScroll();\r\n this.dialogElement.showModal();\r\n\r\n requestAnimationFrame(() => {\r\n this._animateOpen();\r\n });\r\n } else if (!this.open && previousOpen) {\r\n this._isClosing = true;\r\n this._animateClose();\r\n }\r\n }\r\n\r\n /** @private */\r\n private _lockScroll(): void {\r\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\r\n if (_dialogOpenCount === 0) {\r\n _savedScrollY = window.scrollY;\r\n window.scrollTo(0, 0);\r\n }\r\n _dialogOpenCount++;\r\n document.documentElement.setAttribute(\"data-sky-dialog-open\", \"true\");\r\n }\r\n\r\n /** @private */\r\n private _unlockScroll(): void {\r\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\r\n _dialogOpenCount = Math.max(0, _dialogOpenCount - 1);\r\n if (_dialogOpenCount === 0) {\r\n document.documentElement.removeAttribute(\"data-sky-dialog-open\");\r\n window.scrollTo(0, _savedScrollY);\r\n }\r\n }\r\n\r\n /** @private */\r\n private _prefersReducedMotion(): boolean {\r\n if (typeof window === \"undefined\" || !window.matchMedia) return false;\r\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\r\n }\r\n\r\n /** @private */\r\n private _animateOpen(): void {\r\n const dialog = this.dialogElement;\r\n if (!dialog) return;\r\n\r\n this._openAnimation?.cancel();\r\n this._closeAnimation?.cancel();\r\n\r\n const duration = this._prefersReducedMotion() ? 0 : 300;\r\n const animation = dialog.animate(\r\n [\r\n {\r\n opacity: 0,\r\n transform: \"scale(0.95)\",\r\n transformOrigin: \"center center\",\r\n },\r\n { opacity: 1, transform: \"scale(1)\", transformOrigin: \"center center\" },\r\n ],\r\n {\r\n duration,\r\n easing: \"ease-out\",\r\n fill: \"forwards\",\r\n },\r\n );\r\n\r\n animation.onfinish = () => {\r\n this.dispatchEvent(\r\n new CustomEvent(\"dialog-opened\", {\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n };\r\n\r\n this._openAnimation = animation;\r\n }\r\n\r\n /** @private */\r\n private _animateClose(): void {\r\n const dialog = this.dialogElement;\r\n if (!dialog) return;\r\n this._openAnimation?.cancel();\r\n this._closeAnimation?.cancel();\r\n\r\n const duration = this._prefersReducedMotion() ? 0 : 150;\r\n const animation = dialog.animate(\r\n [\r\n { opacity: 1, transform: \"scale(1)\", transformOrigin: \"center center\" },\r\n {\r\n opacity: 0,\r\n transform: \"scale(0.95)\",\r\n transformOrigin: \"center center\",\r\n },\r\n ],\r\n {\r\n duration,\r\n easing: \"ease-out\",\r\n fill: \"forwards\",\r\n },\r\n );\r\n\r\n animation.onfinish = () => {\r\n dialog.close();\r\n this._unlockScroll();\r\n this._isClosing = false;\r\n this.dispatchEvent(\r\n new CustomEvent(\"dialog-closed\", {\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n };\r\n\r\n this._closeAnimation = animation;\r\n }\r\n\r\n /** @private */\r\n private _handleCancel(event: Event): void {\r\n event.preventDefault();\r\n\r\n if (this.persistent || !this.closeOnEsc) {\r\n this._highlightDialog();\r\n return;\r\n }\r\n\r\n this._requestClose(\"escape\");\r\n }\r\n\r\n /** @private */\r\n private _highlightDialog(): void {\r\n if (!this.highlightOnOutsideClick) return;\r\n if (this._prefersReducedMotion()) return;\r\n\r\n const dialog = this.dialogElement;\r\n if (!dialog) return;\r\n\r\n this._highlightAnimation?.cancel();\r\n\r\n const animation = dialog.animate(\r\n [\r\n { transform: \"scale(1)\" },\r\n { transform: \"scale(1.05)\" },\r\n { transform: \"scale(1)\" },\r\n ],\r\n {\r\n duration: 300,\r\n easing: \"ease-in-out\",\r\n },\r\n );\r\n\r\n this._highlightAnimation = animation;\r\n }\r\n\r\n\r\n\r\n /** @private */\r\n private _requestClose(reason: SkyDialogRequestCloseDetail[\"reason\"]): void {\r\n if (this._isClosing) return;\r\n this.dispatchEvent(\r\n new CustomEvent<SkyDialogRequestCloseDetail>(\"request-close\", {\r\n detail: { reason },\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n }\r\n\r\n /**\r\n * Checks if a slot has content.\r\n * @param slot - Slot element to check\r\n * @returns True if slot has content\r\n * @private\r\n */\r\n private _slotHasContent(slot: HTMLSlotElement): boolean {\r\n const nodes = slot.assignedNodes({ flatten: true });\r\n return nodes.some((n) => {\r\n if (n.nodeType === Node.TEXT_NODE) {\r\n return !!n.textContent?.trim();\r\n }\r\n return true;\r\n });\r\n }\r\n\r\n /** @private */\r\n private _onHeaderSlotChange = (e: Event): void => {\r\n const slot = e.target as HTMLSlotElement;\r\n this._hasHeader = this._slotHasContent(slot);\r\n };\r\n\r\n /** @private */\r\n private _onBodySlotChange = (e: Event): void => {\r\n const slot = e.target as HTMLSlotElement;\r\n this._hasBody = this._slotHasContent(slot);\r\n };\r\n\r\n /** @private */\r\n private _onFooterSlotChange = (e: Event): void => {\r\n const slot = e.target as HTMLSlotElement;\r\n this._hasFooter = this._slotHasContent(slot);\r\n };\r\n\r\n /** @private */\r\n private _onKeyDown = (e: KeyboardEvent): void => {\r\n if (e.key !== \"Escape\") return;\r\n\r\n // Always stop native <dialog> ESC behavior\r\n e.preventDefault();\r\n e.stopPropagation();\r\n // If persistent OR closeOnEsc is false → do NOT close, just highlight\r\n if (this.persistent || !this.closeOnEsc) {\r\n this._highlightDialog();\r\n return;\r\n }\r\n\r\n // Allowed to close via ESC\r\n this._requestClose(\"escape\");\r\n };\r\n\r\n /** @protected */\r\n override render() {\r\n const valid: Record<SkyDialogPadding, true> = {\r\n none: true,\r\n xs: true,\r\n sm: true,\r\n md: true,\r\n lg: true,\r\n xl: true,\r\n };\r\n\r\n const hp = valid[this.headerPadding] ? this.headerPadding : \"md\";\r\n const bp = valid[this.bodyPadding] ? this.bodyPadding : \"md\";\r\n const fp = valid[this.footerPadding] ? this.footerPadding : \"md\";\r\n\r\n const headerClasses: Record<string, boolean> = {};\r\n if (this._hasHeader) {\r\n headerClasses[`header-padding-${hp}`] = true;\r\n if (this.separator) {\r\n headerClasses[\"header-separator\"] = true;\r\n }\r\n }\r\n\r\n const footerClasses: Record<string, boolean> = {};\r\n if (this._hasFooter) {\r\n footerClasses[\"actions\"] = true;\r\n footerClasses[`footer-padding-${fp}`] = true;\r\n if (this.separator) {\r\n footerClasses[\"footer-separator\"] = true;\r\n }\r\n } else {\r\n footerClasses[\"actions\"] = true;\r\n }\r\n\r\n return html`\r\n <dialog\r\n part=\"dialog\"\r\n aria-modal=\"true\"\r\n style=\"max-width: ${this.width};\"\r\n @cancel=\"${this._handleCancel}\"\r\n ${ref(this.dialogRef)}\r\n ${animate({\r\n keyframeOptions: {\r\n duration: 300,\r\n easing: \"ease-out\",\r\n },\r\n })}\r\n >\r\n <slot></slot>\r\n\r\n ${when(this.message, () => html`\r\n <p class=\"message\" part=\"message\">${this.message}</p>\r\n `)}\r\n\r\n <div class=${classMap(headerClasses)} part=\"header\">\r\n <slot name=\"header\" @slotchange=${this._onHeaderSlotChange}></slot>\r\n </div>\r\n\r\n <div\r\n class=${classMap({\r\n [`body-padding-${bp}`]: this._hasBody,\r\n })}\r\n part=\"content\"\r\n >\r\n <slot name=\"body\" @slotchange=${this._onBodySlotChange}></slot>\r\n </div>\r\n\r\n <div class=${classMap(footerClasses)} part=\"actions footer\">\r\n <slot name=\"footer\" @slotchange=${this._onFooterSlotChange}></slot>\r\n </div>\r\n ${when(this.closable, () => html`\r\n <button\r\n class=\"close-button\"\r\n part=\"close-button\"\r\n type=\"button\"\r\n aria-label=\"Close dialog\"\r\n @click=${() => this._requestClose(\"close-button\")}\r\n >\r\n <sky-icon icon=\"ion:close\"></sky-icon>\r\n </button>\r\n `)}\r\n </dialog>\r\n `;\r\n }\r\n}\r\n"],"names":["_dialogOpenCount","_savedScrollY","SkyDialog","LitElement","constructor","super","this","open","closable","message","overlay","width","separator","persistent","closeOnEsc","headerPadding","bodyPadding","footerPadding","highlightOnOutsideClick","_hasHeader","_hasBody","_hasFooter","_isClosing","_openAnimation","_closeAnimation","_highlightAnimation","dialogRef","createRef","_onBackdropPointerDown","event","dialog","dialogElement","target","_highlightDialog","_requestClose","preset","_presets","PresetController","_onNativeClose","requestAnimationFrame","showModal","_onHeaderSlotChange","e","slot","_slotHasContent","_onBodySlotChange","_onFooterSlotChange","_onKeyDown","key","preventDefault","stopPropagation","_handleCancel","bind","firstUpdated","d","addEventListener","capture","disconnectedCallback","removeEventListener","_unlockScroll","close","show","hide","focusDialog","focus","getDialogElement","updated","changedProps","has","_handleOpenChange","get","previousOpen","_lockScroll","_animateOpen","_animateClose","window","document","scrollY","scrollTo","documentElement","setAttribute","Math","max","removeAttribute","_prefersReducedMotion","matchMedia","matches","cancel","duration","animation","animate","opacity","transform","transformOrigin","easing","fill","onfinish","dispatchEvent","CustomEvent","bubbles","composed","reason","detail","assignedNodes","flatten","some","n","nodeType","Node","TEXT_NODE","textContent","trim","render","valid","none","xs","sm","md","lg","xl","hp","bp","fp","headerClasses","footerClasses","html","ref","keyframeOptions","when","classMap","dependencies","SkyIcon","styles","css","__decorateClass","property","type","Boolean","reflect","prototype","String","state","query","customElement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sky-dialog.js","sources":["../../src/sky-dialog/sky-dialog.ts"],"sourcesContent":["import { LitElement, html, css, nothing } from \"lit\";\r\nimport { customElement, property, query, state } from \"lit/decorators.js\";\r\nimport { when } from \"lit/directives/when.js\";\r\nimport { ref, createRef } from \"lit/directives/ref.js\";\r\nimport { classMap } from \"lit/directives/class-map.js\";\r\nimport { animate } from \"@lit-labs/motion\";\r\nimport { SkyIcon } from \"../sky-icon/sky-icon.js\";\r\nimport { PresetController } from \"../helper/controllers/preset-controller\";\r\n\r\nexport type SkyDialogPadding = \"none\" | \"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\";\r\nexport type SkyDialogRequestCloseDetail = { reason: \"backdrop\" | \"escape\" | \"close-button\" };\r\n/** Shared scroll lock state so multiple dialogs don't double-restore scroll. */\r\nlet _dialogOpenCount = 0;\r\nlet _savedScrollY = 0;\r\n\r\n/**\r\n * @element sky-dialog\r\n *\r\n * @summary Accessible modal dialog built on native `<dialog>` with controlled open state, slots, and keyboard/backdrop handling.\r\n *\r\n * @status stable\r\n * @since 1.0.0\r\n *\r\n * @documentation https://sky-ui.com/components/dialog\r\n * @dependency sky-icon\r\n *\r\n * @uiVModel open request-close sync=constant:false\r\n *\r\n * @slot header - Header content area at the top of the dialog.\r\n * @slot body - Main content area of the dialog.\r\n * @slot footer - Footer actions area at the bottom of the dialog.\r\n *\r\n * @csspart dialog - The dialog surface wrapper element.\r\n * @csspart close-button - The top-right close button.\r\n * @csspart message - Optional message text shown above the body.\r\n * @csspart header - Container for the header slot.\r\n * @csspart content - Container for the body slot.\r\n * @csspart actions - Container for the footer slot.\r\n * @csspart footer - Alias part on the footer container.\r\n *\r\n * @fires {CustomEvent<SkyDialogRequestCloseDetail>} request-close - Fired when close intent is detected and parent should update `open`.\r\n * @fires {CustomEvent<void>} dialog-opened - Fired after open animation completes.\r\n * @fires {CustomEvent<void>} dialog-closed - Fired after close animation completes.\r\n *\r\n * @property {boolean} open - Whether the dialog is visible.\r\n * @property {boolean} closable - Shows a top-right close button.\r\n * @property {string} message - Optional message displayed above body content.\r\n * @property {boolean} overlay - Applies dim/blur backdrop effect.\r\n * @property {string} width - Max width of the dialog surface.\r\n * @property {boolean} separator - Shows separators around header/footer regions.\r\n * @property {boolean} persistent - Prevents outside/Escape close and highlights surface instead.\r\n * @property {boolean} closeOnEsc - Enables close intent on Escape key.\r\n * @property {SkyDialogPadding} headerPadding - Header padding token.\r\n * @property {SkyDialogPadding} bodyPadding - Body padding token.\r\n * @property {SkyDialogPadding} footerPadding - Footer padding token.\r\n * @property {boolean} highlightOnOutsideClick - Enables highlight animation on rejected outside click.\r\n * @property {string} preset - Named prop preset from nearest `sky-config-provider`. Default: `\"\"`.\r\n * @method show Opens the dialog by setting `open` to true.\r\n * @method hide Closes the dialog by setting `open` to false.\r\n * @method focusDialog Focuses the native dialog surface.\r\n * @method getDialogElement Returns the native dialog element reference.\r\n *\r\n * @Behavior\r\n * - Uses native `<dialog>` element with enhanced functionality\r\n * - Controlled exclusively by the `open` attribute/property\r\n * - Uses native dialog focus behavior\r\n * - Supports smooth animations with configurable timing\r\n * - Uses the floating modal surface (frosted tertiary glass)\r\n * - **Standard**: Click outside closes dialog\r\n * - **Persistent**: Click outside highlights dialog instead of closing\r\n * - **Closable**: Shows close button in top-right corner\r\n * - **Escape handling**: Configurable Escape key behavior\r\n * - Uses `aria-modal=\"true\"` for screen reader isolation\r\n * - Native dialog focus behavior\r\n * - Escape key handling with user preferences\r\n * - Close button with appropriate ARIA label\r\n * - Respects reduced motion preferences\r\n * - Smooth open/close animations with scale effects\r\n * - Highlight animation for persistent dialogs\r\n * - Configurable animation durations\r\n * - Uses Web Animations API for performance\r\n * - Works with any content in header/body/footer slots\r\n * - Compatible with form controls and interactive content\r\n * - Can be nested or stacked\r\n * - Responsive width and padding\r\n *\r\n * @example\r\n * ```html\r\n * <sky-dialog open closable width=\"420px\" separator>\r\n * <h3 slot=\"header\">Confirm Action</h3>\r\n * <p slot=\"body\">Are you sure you want to proceed?</p>\r\n * <div slot=\"footer\">\r\n * <button>Cancel</button>\r\n * <button>Confirm</button>\r\n * </div>\r\n * </sky-dialog>\r\n * ```\r\n * ```vue\r\n * <template>\r\n * <sky-dialog open closable width=\"420px\" separator>\r\n * <h3 slot=\"header\">Confirm Action</h3>\r\n * <p slot=\"body\">Are you sure you want to proceed?</p>\r\n * <div slot=\"footer\">\r\n * <button>Cancel</button>\r\n * <button>Confirm</button>\r\n * </div>\r\n * </sky-dialog>\r\n * </template>\r\n * ```\r\n * ```jsx\r\n * export default function Demo() {\r\n * return (\r\n * <sky-dialog open closable width=\"420px\" separator>\r\n * <h3 slot=\"header\">Confirm Action</h3>\r\n * <p slot=\"body\">Are you sure you want to proceed?</p>\r\n * <div slot=\"footer\">\r\n * <button>Cancel</button>\r\n * <button>Confirm</button>\r\n * </div>\r\n * </sky-dialog>\r\n * );\r\n * }\r\n * ```\r\n */\r\n@customElement(\"sky-dialog\")\r\nexport class SkyDialog extends LitElement {\r\n\r\n static dependencies: Record<string, CustomElementConstructor> = {\r\n \"sky-icon\": SkyIcon,\r\n };\r\n\r\n /** @public Whether the dialog is visible. */\r\n @property({ type: Boolean, reflect: true })\r\n open = false;\r\n\r\n /** @public Shows a top-right close button. */\r\n @property({ type: Boolean, reflect: true })\r\n closable = false;\r\n\r\n /** @public Optional message displayed above the body. */\r\n @property({ type: String })\r\n message = \"\";\r\n\r\n /** @public Applies dim/blur overlay on host. */\r\n @property({ type: Boolean, reflect: true })\r\n overlay = false;\r\n\r\n /** @public Max width of the dialog surface (e.g., \"480px\" or \"auto\"). */\r\n @property({ type: String })\r\n width = \"auto\";\r\n\r\n /** @public Shows header/footer separators. */\r\n @property({ type: Boolean, reflect: true })\r\n separator = false;\r\n\r\n /** @public Prevents close on outside click, highlights instead. */\r\n @property({ type: Boolean, reflect: true })\r\n persistent = false;\r\n\r\n /** @public Enables closing on Escape key. */\r\n @property({ type: Boolean, reflect: true })\r\n closeOnEsc = false;\r\n\r\n /** @public Padding token for header container. */\r\n @property({ type: String, reflect: true })\r\n headerPadding: SkyDialogPadding = \"sm\";\r\n\r\n /** @public Padding token for body container. */\r\n @property({ type: String, reflect: true })\r\n bodyPadding: SkyDialogPadding = \"sm\";\r\n\r\n /** @public Padding token for footer container. */\r\n @property({ type: String, reflect: true })\r\n footerPadding: SkyDialogPadding = \"sm\";\r\n\r\n\r\n /** @public Highlights dialog on outside click instead of closing. */\r\n @property({\r\n type: Boolean,\r\n reflect: true,\r\n })\r\n highlightOnOutsideClick = true;\r\n\r\n /** @private Whether header slot has content. */\r\n @state()\r\n private _hasHeader = false;\r\n\r\n /** @private Whether body slot has content. */\r\n @state()\r\n private _hasBody = false;\r\n\r\n /** @private Whether footer slot has content. */\r\n @state()\r\n private _hasFooter = false;\r\n\r\n /** @private Whether dialog is in closing animation. */\r\n @state()\r\n private _isClosing = false;\r\n\r\n /** @private Open animation instance. */\r\n private _openAnimation: Animation | null = null;\r\n\r\n /** @private Close animation instance. */\r\n private _closeAnimation: Animation | null = null;\r\n\r\n /** @private Highlight animation instance. */\r\n private _highlightAnimation: Animation | null = null;\r\n\r\n /** @private Reference to the native dialog element. */\r\n @query(\"dialog\")\r\n private dialogElement?: HTMLDialogElement;\r\n\r\n /** @public Ref for the dialog element (e.g. for focus trap or positioning). */\r\n dialogRef = createRef<HTMLDialogElement>();\r\n\r\n /** @private Handle clicks on the dialog backdrop (outside surface). */\r\n private _onBackdropPointerDown = (event: PointerEvent): void => {\r\n const dialog = this.dialogElement;\r\n if (!dialog || !dialog.open) return;\r\n\r\n // In the <dialog> element, backdrop clicks are retargeted to the dialog itself.\r\n // Clicks inside the surface target inner elements.\r\n if (event.target !== dialog) return;\r\n\r\n // \"Persistent\" dialogs don't close via outside click; they highlight instead (optional).\r\n if (this.persistent) {\r\n this._highlightDialog();\r\n return;\r\n }\r\n\r\n // Standard behavior: outside click requests close.\r\n this._requestClose(\"backdrop\");\r\n };\r\n\r\n\r\n /** Named prop preset from nearest `sky-config-provider`. */\r\n @property({ type: String, reflect: true }) preset = \"\";\r\n\r\n private _presets = new PresetController(this);\r\n\r\n static override styles = css`\r\n :host {\r\n display: contents;\r\n font-family: var(--sky-font);\r\n }\r\n\r\n /* Floating modal template (§20.3) — single canonical surface.\r\n Do not set display here: author display overrides UA\r\n dialog:not([open]) { display: none } and the closed dialog stays visible. */\r\n dialog {\r\n padding: 0;\r\n margin: auto;\r\n color: var(--sky-text-primary);\r\n position: fixed;\r\n box-sizing: border-box;\r\n width: 100%;\r\n text-align: center;\r\n overflow: hidden;\r\n z-index: var(--sky-z-modal, 1300);\r\n background: var(--sky-glass-tertiary);\r\n background-image: var(--sky-glass-surface);\r\n backdrop-filter: var(--sky-blur-tertiary) var(--sky-saturation-tertiary)\r\n var(--sky-brightness-tertiary);\r\n -webkit-backdrop-filter: var(--sky-blur-tertiary)\r\n var(--sky-saturation-tertiary) var(--sky-brightness-tertiary);\r\n border: var(--sky-border-tertiary);\r\n border-radius: var(--sky-radius-tertiary);\r\n box-shadow:\r\n var(--sky-box-shadow-inset),\r\n var(--sky-box-shadow-tertiary);\r\n }\r\n\r\n dialog[open] {\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n\r\n dialog::backdrop {\r\n backdrop-filter: none;\r\n -webkit-backdrop-filter: none;\r\n transition: backdrop-filter 0.3s ease-in-out;\r\n }\r\n\r\n :host([overlay]) dialog::backdrop {\r\n backdrop-filter: var(--sky-blur-tertiary);\r\n -webkit-backdrop-filter: var(--sky-blur-tertiary);\r\n }\r\n\r\n .header-padding-none,\r\n .body-padding-none,\r\n .footer-padding-none {\r\n padding: var(--sky-space-0, 0);\r\n }\r\n\r\n .header-padding-xs,\r\n .body-padding-xs,\r\n .footer-padding-xs {\r\n padding: var(--sky-space-1, 4px);\r\n }\r\n\r\n .header-padding-sm,\r\n .body-padding-sm,\r\n .footer-padding-sm {\r\n padding: var(--sky-space-2, 8px);\r\n }\r\n\r\n .header-padding-md,\r\n .body-padding-md,\r\n .footer-padding-md {\r\n padding: var(--sky-space-4, 16px);\r\n }\r\n\r\n .header-padding-lg,\r\n .body-padding-lg,\r\n .footer-padding-lg {\r\n padding: var(--sky-space-6, 24px);\r\n }\r\n\r\n .header-padding-xl,\r\n .body-padding-xl,\r\n .footer-padding-xl {\r\n padding: var(--sky-space-8, 32px);\r\n }\r\n\r\n /* Close button — §20.6 inner dismiss affordance */\r\n .close-button {\r\n position: absolute;\r\n top: var(--sky-space-1\\\\.5, 6px);\r\n right: var(--sky-space-1\\\\.5, 6px);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: var(--sky-space-6, 24px);\r\n height: var(--sky-space-6, 24px);\r\n box-sizing: border-box;\r\n border-radius: var(--sky-radius-full);\r\n font-size: var(--sky-font-size-sm, 14px);\r\n cursor: pointer;\r\n color: var(--sky-text-primary);\r\n border: none;\r\n background-color: var(--sky-hover-primary);\r\n backdrop-filter: none;\r\n -webkit-backdrop-filter: none;\r\n transition: color 0.2s ease, background-color 0.2s ease;\r\n z-index: 1;\r\n }\r\n\r\n .close-button sky-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n line-height: 1;\r\n }\r\n\r\n .close-button:hover {\r\n color: var(--sky-danger-primary);\r\n }\r\n\r\n .close-button:focus-visible {\r\n outline: none;\r\n box-shadow: var(--sky-focus-ring);\r\n }\r\n\r\n /* Message */\r\n .message {\r\n padding: var(--sky-space-4, 16px) 0;\r\n margin: 0;\r\n }\r\n\r\n ::slotted([slot=\"header\"]) {\r\n font-size: var(--sky-font-size-md, 16px);\r\n font-weight: var(--sky-font-weight-bold, 700);\r\n color: var(--sky-text-primary);\r\n box-sizing: border-box;\r\n }\r\n\r\n dialog:focus,\r\n dialog:focus-visible {\r\n outline: none;\r\n }\r\n\r\n .header-separator {\r\n border-bottom: var(--sky-border-primary);\r\n }\r\n\r\n .footer-separator {\r\n border-top: var(--sky-border-primary);\r\n }\r\n\r\n sky-icon {\r\n display: inline-block;\r\n width: 1em;\r\n height: 1em;\r\n }\r\n\r\n /* RTL support */\r\n :host([dir=\"rtl\"]) .close-button {\r\n right: auto;\r\n left: var(--sky-space-1\\\\.5, 6px);\r\n }\r\n\r\n /* Animation classes */\r\n dialog.opening {\r\n opacity: 0;\r\n }\r\n\r\n dialog.closing {\r\n opacity: 1;\r\n }\r\n\r\n @media (prefers-reduced-motion: reduce) {\r\n dialog::backdrop,\r\n .close-button {\r\n transition: none;\r\n }\r\n }\r\n `;\r\n\r\n constructor() {\r\n super();\r\n this._handleCancel = this._handleCancel.bind(this);\r\n }\r\n\r\n /** @protected */\r\n override firstUpdated() {\r\n const d = this.dialogElement;\r\n if (!d) return;\r\n\r\n // Capture phase is important so we beat the native <dialog> cancel/close behavior\r\n d.addEventListener(\"keydown\", this._onKeyDown, { capture: true });\r\n d.addEventListener(\"pointerdown\", this._onBackdropPointerDown);\r\n d.addEventListener(\"cancel\", this._handleCancel as any); // optional extra safety\r\n d.addEventListener(\"close\", this._onNativeClose); // keep state in sync (below)\r\n }\r\n\r\n /** @protected */\r\n override disconnectedCallback() {\r\n const d = this.dialogElement;\r\n if (d) {\r\n d.removeEventListener(\"keydown\", this._onKeyDown, {\r\n capture: true,\r\n } as any);\r\n d.removeEventListener(\"pointerdown\", this._onBackdropPointerDown);\r\n d.removeEventListener(\"cancel\", this._handleCancel as any);\r\n d.removeEventListener(\"close\", this._onNativeClose);\r\n }\r\n super.disconnectedCallback();\r\n this._unlockScroll();\r\n if (d?.open) d.close();\r\n }\r\n\r\n /**\r\n * Opens the dialog programmatically.\r\n * @public\r\n */\r\n public show(): void {\r\n this.open = true;\r\n }\r\n\r\n /**\r\n * Closes the dialog programmatically.\r\n * @public\r\n */\r\n public hide(): void {\r\n this.open = false;\r\n }\r\n\r\n /**\r\n * Manually focuses the dialog element.\r\n * @public\r\n */\r\n public focusDialog(): void {\r\n if (this.dialogElement) {\r\n this.dialogElement.focus();\r\n }\r\n }\r\n\r\n /**\r\n * Gets the native dialog element.\r\n * @returns The native HTMLDialogElement or undefined\r\n * @public\r\n */\r\n public getDialogElement(): HTMLDialogElement | undefined {\r\n return this.dialogElement;\r\n }\r\n\r\n /** @private */\r\n private _onNativeClose = (): void => {\r\n // If parent still says open=true, reopen immediately (persistent behavior)\r\n if (this.open) {\r\n // restore modal state\r\n requestAnimationFrame(() => {\r\n if (!this.dialogElement?.open) {\r\n this.dialogElement?.showModal();\r\n }\r\n });\r\n }\r\n };\r\n\r\n /** @protected */\r\n override updated(changedProps: Map<string, any>) {\r\n if (changedProps.has(\"open\")) {\r\n // Use requestAnimationFrame to ensure DOM updates are complete\r\n requestAnimationFrame(() => {\r\n this._handleOpenChange(changedProps.get(\"open\"));\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Handles open state changes.\r\n * @param previousOpen - Previous open state\r\n * @private\r\n */\r\n private _handleOpenChange(previousOpen: boolean): void {\r\n if (!this.dialogElement) return;\r\n\r\n if (this.open && !this.dialogElement.open) {\r\n this._lockScroll();\r\n this.dialogElement.showModal();\r\n\r\n requestAnimationFrame(() => {\r\n this._animateOpen();\r\n });\r\n } else if (!this.open && previousOpen) {\r\n this._isClosing = true;\r\n this._animateClose();\r\n }\r\n }\r\n\r\n /** @private */\r\n private _lockScroll(): void {\r\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\r\n if (_dialogOpenCount === 0) {\r\n _savedScrollY = window.scrollY;\r\n window.scrollTo(0, 0);\r\n }\r\n _dialogOpenCount++;\r\n document.documentElement.setAttribute(\"data-sky-dialog-open\", \"true\");\r\n }\r\n\r\n /** @private */\r\n private _unlockScroll(): void {\r\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\r\n _dialogOpenCount = Math.max(0, _dialogOpenCount - 1);\r\n if (_dialogOpenCount === 0) {\r\n document.documentElement.removeAttribute(\"data-sky-dialog-open\");\r\n window.scrollTo(0, _savedScrollY);\r\n }\r\n }\r\n\r\n /** @private */\r\n private _prefersReducedMotion(): boolean {\r\n if (typeof window === \"undefined\" || !window.matchMedia) return false;\r\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\r\n }\r\n\r\n /** @private */\r\n private _animateOpen(): void {\r\n const dialog = this.dialogElement;\r\n if (!dialog) return;\r\n\r\n this._openAnimation?.cancel();\r\n this._closeAnimation?.cancel();\r\n\r\n const duration = this._prefersReducedMotion() ? 0 : 300;\r\n const animation = dialog.animate(\r\n [\r\n {\r\n opacity: 0,\r\n transform: \"scale(0.95)\",\r\n transformOrigin: \"center center\",\r\n },\r\n { opacity: 1, transform: \"scale(1)\", transformOrigin: \"center center\" },\r\n ],\r\n {\r\n duration,\r\n easing: \"ease-out\",\r\n fill: \"forwards\",\r\n },\r\n );\r\n\r\n animation.onfinish = () => {\r\n this.dispatchEvent(\r\n new CustomEvent(\"dialog-opened\", {\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n };\r\n\r\n this._openAnimation = animation;\r\n }\r\n\r\n /** @private */\r\n private _animateClose(): void {\r\n const dialog = this.dialogElement;\r\n if (!dialog) return;\r\n this._openAnimation?.cancel();\r\n this._closeAnimation?.cancel();\r\n\r\n const duration = this._prefersReducedMotion() ? 0 : 150;\r\n const animation = dialog.animate(\r\n [\r\n { opacity: 1, transform: \"scale(1)\", transformOrigin: \"center center\" },\r\n {\r\n opacity: 0,\r\n transform: \"scale(0.95)\",\r\n transformOrigin: \"center center\",\r\n },\r\n ],\r\n {\r\n duration,\r\n easing: \"ease-out\",\r\n fill: \"forwards\",\r\n },\r\n );\r\n\r\n animation.onfinish = () => {\r\n dialog.close();\r\n this._unlockScroll();\r\n this._isClosing = false;\r\n this.dispatchEvent(\r\n new CustomEvent(\"dialog-closed\", {\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n };\r\n\r\n this._closeAnimation = animation;\r\n }\r\n\r\n /** @private */\r\n private _handleCancel(event: Event): void {\r\n event.preventDefault();\r\n\r\n if (this.persistent || !this.closeOnEsc) {\r\n this._highlightDialog();\r\n return;\r\n }\r\n\r\n this._requestClose(\"escape\");\r\n }\r\n\r\n /** @private */\r\n private _highlightDialog(): void {\r\n if (!this.highlightOnOutsideClick) return;\r\n if (this._prefersReducedMotion()) return;\r\n\r\n const dialog = this.dialogElement;\r\n if (!dialog) return;\r\n\r\n this._highlightAnimation?.cancel();\r\n\r\n const animation = dialog.animate(\r\n [\r\n { transform: \"scale(1)\" },\r\n { transform: \"scale(1.05)\" },\r\n { transform: \"scale(1)\" },\r\n ],\r\n {\r\n duration: 300,\r\n easing: \"ease-in-out\",\r\n },\r\n );\r\n\r\n this._highlightAnimation = animation;\r\n }\r\n\r\n\r\n\r\n /** @private */\r\n private _requestClose(reason: SkyDialogRequestCloseDetail[\"reason\"]): void {\r\n if (this._isClosing) return;\r\n this.dispatchEvent(\r\n new CustomEvent<SkyDialogRequestCloseDetail>(\"request-close\", {\r\n detail: { reason },\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n }\r\n\r\n /**\r\n * Checks if a slot has content.\r\n * @param slot - Slot element to check\r\n * @returns True if slot has content\r\n * @private\r\n */\r\n private _slotHasContent(slot: HTMLSlotElement): boolean {\r\n const nodes = slot.assignedNodes({ flatten: true });\r\n return nodes.some((n) => {\r\n if (n.nodeType === Node.TEXT_NODE) {\r\n return !!n.textContent?.trim();\r\n }\r\n return true;\r\n });\r\n }\r\n\r\n /** @private */\r\n private _onHeaderSlotChange = (e: Event): void => {\r\n const slot = e.target as HTMLSlotElement;\r\n this._hasHeader = this._slotHasContent(slot);\r\n };\r\n\r\n /** @private */\r\n private _onBodySlotChange = (e: Event): void => {\r\n const slot = e.target as HTMLSlotElement;\r\n this._hasBody = this._slotHasContent(slot);\r\n };\r\n\r\n /** @private */\r\n private _onFooterSlotChange = (e: Event): void => {\r\n const slot = e.target as HTMLSlotElement;\r\n this._hasFooter = this._slotHasContent(slot);\r\n };\r\n\r\n /** @private */\r\n private _onKeyDown = (e: KeyboardEvent): void => {\r\n if (e.key !== \"Escape\") return;\r\n\r\n // Always stop native <dialog> ESC behavior\r\n e.preventDefault();\r\n e.stopPropagation();\r\n // If persistent OR closeOnEsc is false → do NOT close, just highlight\r\n if (this.persistent || !this.closeOnEsc) {\r\n this._highlightDialog();\r\n return;\r\n }\r\n\r\n // Allowed to close via ESC\r\n this._requestClose(\"escape\");\r\n };\r\n\r\n /** @protected */\r\n override render() {\r\n const valid: Record<SkyDialogPadding, true> = {\r\n none: true,\r\n xs: true,\r\n sm: true,\r\n md: true,\r\n lg: true,\r\n xl: true,\r\n };\r\n\r\n const hp = valid[this.headerPadding] ? this.headerPadding : \"md\";\r\n const bp = valid[this.bodyPadding] ? this.bodyPadding : \"md\";\r\n const fp = valid[this.footerPadding] ? this.footerPadding : \"md\";\r\n\r\n const headerClasses: Record<string, boolean> = {};\r\n if (this._hasHeader) {\r\n headerClasses[`header-padding-${hp}`] = true;\r\n if (this.separator) {\r\n headerClasses[\"header-separator\"] = true;\r\n }\r\n }\r\n\r\n const footerClasses: Record<string, boolean> = {};\r\n if (this._hasFooter) {\r\n footerClasses[\"actions\"] = true;\r\n footerClasses[`footer-padding-${fp}`] = true;\r\n if (this.separator) {\r\n footerClasses[\"footer-separator\"] = true;\r\n }\r\n } else {\r\n footerClasses[\"actions\"] = true;\r\n }\r\n\r\n return html`\r\n <dialog\r\n part=\"dialog\"\r\n aria-modal=\"true\"\r\n style=\"max-width: ${this.width};\"\r\n @cancel=\"${this._handleCancel}\"\r\n ${ref(this.dialogRef)}\r\n ${animate({\r\n keyframeOptions: {\r\n duration: 300,\r\n easing: \"ease-out\",\r\n },\r\n })}\r\n >\r\n <slot></slot>\r\n\r\n ${when(this.message, () => html`\r\n <p class=\"message\" part=\"message\">${this.message}</p>\r\n `)}\r\n\r\n <div class=${classMap(headerClasses)} part=\"header\">\r\n <slot name=\"header\" @slotchange=${this._onHeaderSlotChange}></slot>\r\n </div>\r\n\r\n <div\r\n class=${classMap({\r\n [`body-padding-${bp}`]: this._hasBody,\r\n })}\r\n part=\"content\"\r\n >\r\n <slot name=\"body\" @slotchange=${this._onBodySlotChange}></slot>\r\n </div>\r\n\r\n <div class=${classMap(footerClasses)} part=\"actions footer\">\r\n <slot name=\"footer\" @slotchange=${this._onFooterSlotChange}></slot>\r\n </div>\r\n ${when(this.closable, () => html`\r\n <button\r\n class=\"close-button\"\r\n part=\"close-button\"\r\n type=\"button\"\r\n aria-label=\"Close dialog\"\r\n @click=${() => this._requestClose(\"close-button\")}\r\n >\r\n <sky-icon icon=\"ion:close\"></sky-icon>\r\n </button>\r\n `)}\r\n </dialog>\r\n `;\r\n }\r\n}\r\n"],"names":["_dialogOpenCount","_savedScrollY","SkyDialog","LitElement","constructor","super","this","open","closable","message","overlay","width","separator","persistent","closeOnEsc","headerPadding","bodyPadding","footerPadding","highlightOnOutsideClick","_hasHeader","_hasBody","_hasFooter","_isClosing","_openAnimation","_closeAnimation","_highlightAnimation","dialogRef","createRef","_onBackdropPointerDown","event","dialog","dialogElement","target","_highlightDialog","_requestClose","preset","_presets","PresetController","_onNativeClose","requestAnimationFrame","showModal","_onHeaderSlotChange","e","slot","_slotHasContent","_onBodySlotChange","_onFooterSlotChange","_onKeyDown","key","preventDefault","stopPropagation","_handleCancel","bind","firstUpdated","d","addEventListener","capture","disconnectedCallback","removeEventListener","_unlockScroll","close","show","hide","focusDialog","focus","getDialogElement","updated","changedProps","has","_handleOpenChange","get","previousOpen","_lockScroll","_animateOpen","_animateClose","window","document","scrollY","scrollTo","documentElement","setAttribute","Math","max","removeAttribute","_prefersReducedMotion","matchMedia","matches","cancel","duration","animation","animate","opacity","transform","transformOrigin","easing","fill","onfinish","dispatchEvent","CustomEvent","bubbles","composed","reason","detail","assignedNodes","flatten","some","n","nodeType","Node","TEXT_NODE","textContent","trim","render","valid","none","xs","sm","md","lg","xl","hp","bp","fp","headerClasses","footerClasses","html","ref","keyframeOptions","when","classMap","dependencies","SkyIcon","styles","css","__decorateClass","property","type","Boolean","reflect","prototype","String","state","query","customElement"],"mappings":"qpBAYA,IAAIA,EAAmB,EACnBC,EAAgB,EAgHPC,EAAN,cAAwBC,EAqS7B,WAAAC,GACEC,QA9RFC,KAAAC,MAAO,EAIPD,KAAAE,UAAW,EAIXF,KAAAG,QAAU,GAIVH,KAAAI,SAAU,EAIVJ,KAAAK,MAAQ,OAIRL,KAAAM,WAAY,EAIZN,KAAAO,YAAa,EAIbP,KAAAQ,YAAa,EAIbR,KAAAS,cAAkC,KAIlCT,KAAAU,YAAgC,KAIhCV,KAAAW,cAAkC,KAQlCX,KAAAY,yBAA0B,EAI1BZ,KAAQa,YAAa,EAIrBb,KAAQc,UAAW,EAInBd,KAAQe,YAAa,EAIrBf,KAAQgB,YAAa,EAGrBhB,KAAQiB,eAAmC,KAG3CjB,KAAQkB,gBAAoC,KAG5ClB,KAAQmB,oBAAwC,KAOhDnB,KAAAoB,UAAYC,IAGZrB,KAAQsB,uBAA0BC,IAChC,MAAMC,EAASxB,KAAKyB,cACfD,GAAWA,EAAOvB,MAInBsB,EAAMG,SAAWF,IAGjBxB,KAAKO,WACPP,KAAK2B,mBAKP3B,KAAK4B,cAAc,cAKsB5B,KAAA6B,OAAS,GAEpD7B,KAAQ8B,SAAW,IAAIC,EAAiB/B,MAyPxCA,KAAQgC,eAAiB,KAEnBhC,KAAKC,MAEPgC,sBAAsB,KACfjC,KAAKyB,eAAexB,MACvBD,KAAKyB,eAAeS,eAgN5BlC,KAAQmC,oBAAuBC,IAC7B,MAAMC,EAAOD,EAAEV,OACf1B,KAAKa,WAAab,KAAKsC,gBAAgBD,IAIzCrC,KAAQuC,kBAAqBH,IAC3B,MAAMC,EAAOD,EAAEV,OACf1B,KAAKc,SAAWd,KAAKsC,gBAAgBD,IAIvCrC,KAAQwC,oBAAuBJ,IAC7B,MAAMC,EAAOD,EAAEV,OACf1B,KAAKe,WAAaf,KAAKsC,gBAAgBD,IAIzCrC,KAAQyC,WAAcL,IACN,WAAVA,EAAEM,MAGNN,EAAEO,iBACFP,EAAEQ,mBAEE5C,KAAKO,YAAeP,KAAKQ,WAM7BR,KAAK4B,cAAc,UALjB5B,KAAK2B,qBAnTP3B,KAAK6C,cAAgB7C,KAAK6C,cAAcC,KAAK9C,KAC/C,CAGS,YAAA+C,GACP,MAAMC,EAAIhD,KAAKyB,cACVuB,IAGLA,EAAEC,iBAAiB,UAAWjD,KAAKyC,WAAY,CAAES,SAAS,IAC1DF,EAAEC,iBAAiB,cAAejD,KAAKsB,wBACvC0B,EAAEC,iBAAiB,SAAUjD,KAAK6C,eAClCG,EAAEC,iBAAiB,QAASjD,KAAKgC,gBACnC,CAGS,oBAAAmB,GACP,MAAMH,EAAIhD,KAAKyB,cACXuB,IACFA,EAAEI,oBAAoB,UAAWpD,KAAKyC,WAAY,CAChDS,SAAS,IAEXF,EAAEI,oBAAoB,cAAepD,KAAKsB,wBAC1C0B,EAAEI,oBAAoB,SAAUpD,KAAK6C,eACrCG,EAAEI,oBAAoB,QAASpD,KAAKgC,iBAEtCjC,MAAMoD,uBACNnD,KAAKqD,gBACDL,GAAG/C,MAAM+C,EAAEM,OACjB,CAMO,IAAAC,GACLvD,KAAKC,MAAO,CACd,CAMO,IAAAuD,GACLxD,KAAKC,MAAO,CACd,CAMO,WAAAwD,GACDzD,KAAKyB,eACPzB,KAAKyB,cAAciC,OAEvB,CAOO,gBAAAC,GACL,OAAO3D,KAAKyB,aACd,CAgBS,OAAAmC,CAAQC,GACXA,EAAaC,IAAI,SAEnB7B,sBAAsB,KACpBjC,KAAK+D,kBAAkBF,EAAaG,IAAI,UAG9C,CAOQ,iBAAAD,CAAkBE,GACnBjE,KAAKyB,gBAENzB,KAAKC,OAASD,KAAKyB,cAAcxB,MACnCD,KAAKkE,cACLlE,KAAKyB,cAAcS,YAEnBD,sBAAsB,KACpBjC,KAAKmE,mBAEGnE,KAAKC,MAAQgE,IACvBjE,KAAKgB,YAAa,EAClBhB,KAAKoE,iBAET,CAGQ,WAAAF,GACgB,oBAAXG,QAA8C,oBAAbC,WACnB,IAArB5E,IACFC,EAAgB0E,OAAOE,QACvBF,OAAOG,SAAS,EAAG,IAErB9E,IACA4E,SAASG,gBAAgBC,aAAa,uBAAwB,QAChE,CAGQ,aAAArB,GACgB,oBAAXgB,QAA8C,oBAAbC,WAC5C5E,EAAmBiF,KAAKC,IAAI,EAAGlF,EAAmB,GACzB,IAArBA,IACF4E,SAASG,gBAAgBI,gBAAgB,wBACzCR,OAAOG,SAAS,EAAG7E,IAEvB,CAGQ,qBAAAmF,GACN,QAAsB,oBAAXT,SAA2BA,OAAOU,aACtCV,OAAOU,WAAW,oCAAoCC,OAC/D,CAGQ,YAAAb,GACN,MAAM3C,EAASxB,KAAKyB,cACpB,IAAKD,EAAQ,OAEbxB,KAAKiB,gBAAgBgE,SACrBjF,KAAKkB,iBAAiB+D,SAEtB,MAAMC,EAAWlF,KAAK8E,wBAA0B,EAAI,IAC9CK,EAAY3D,EAAO4D,QACvB,CACE,CACEC,QAAS,EACTC,UAAW,cACXC,gBAAiB,iBAEnB,CAAEF,QAAS,EAAGC,UAAW,WAAYC,gBAAiB,kBAExD,CACEL,WACAM,OAAQ,WACRC,KAAM,aAIVN,EAAUO,SAAW,KACnB1F,KAAK2F,cACH,IAAIC,YAAY,gBAAiB,CAC/BC,SAAS,EACTC,UAAU,MAKhB9F,KAAKiB,eAAiBkE,CACxB,CAGQ,aAAAf,GACN,MAAM5C,EAASxB,KAAKyB,cACpB,IAAKD,EAAQ,OACbxB,KAAKiB,gBAAgBgE,SACrBjF,KAAKkB,iBAAiB+D,SAEtB,MAAMC,EAAWlF,KAAK8E,wBAA0B,EAAI,IAC9CK,EAAY3D,EAAO4D,QACvB,CACE,CAAEC,QAAS,EAAGC,UAAW,WAAYC,gBAAiB,iBACtD,CACEF,QAAS,EACTC,UAAW,cACXC,gBAAiB,kBAGrB,CACEL,WACAM,OAAQ,WACRC,KAAM,aAIVN,EAAUO,SAAW,KACnBlE,EAAO8B,QACPtD,KAAKqD,gBACLrD,KAAKgB,YAAa,EAClBhB,KAAK2F,cACH,IAAIC,YAAY,gBAAiB,CAC/BC,SAAS,EACTC,UAAU,MAKhB9F,KAAKkB,gBAAkBiE,CACzB,CAGQ,aAAAtC,CAActB,GACpBA,EAAMoB,kBAEF3C,KAAKO,YAAeP,KAAKQ,WAK7BR,KAAK4B,cAAc,UAJjB5B,KAAK2B,kBAKT,CAGQ,gBAAAA,GACN,IAAK3B,KAAKY,wBAAyB,OACnC,GAAIZ,KAAK8E,wBAAyB,OAElC,MAAMtD,EAASxB,KAAKyB,cACpB,IAAKD,EAAQ,OAEbxB,KAAKmB,qBAAqB8D,SAE1B,MAAME,EAAY3D,EAAO4D,QACvB,CACE,CAAEE,UAAW,YACb,CAAEA,UAAW,eACb,CAAEA,UAAW,aAEf,CACEJ,SAAU,IACVM,OAAQ,gBAIZxF,KAAKmB,oBAAsBgE,CAC7B,CAKQ,aAAAvD,CAAcmE,GAChB/F,KAAKgB,YACThB,KAAK2F,cACH,IAAIC,YAAyC,gBAAiB,CAC5DI,OAAQ,CAAED,UACVF,SAAS,EACTC,UAAU,IAGhB,CAQQ,eAAAxD,CAAgBD,GAEtB,OADcA,EAAK4D,cAAc,CAAEC,SAAS,IAC/BC,KAAMC,GACbA,EAAEC,WAAaC,KAAKC,aACbH,EAAEI,aAAaC,OAI9B,CAsCS,MAAAC,GACP,MAAMC,EAAwC,CAC5CC,MAAM,EACNC,IAAI,EACJC,IAAI,EACJC,IAAI,EACJC,IAAI,EACJC,IAAI,GAGAC,EAAKP,EAAM3G,KAAKS,eAAiBT,KAAKS,cAAgB,KACtD0G,EAAKR,EAAM3G,KAAKU,aAAeV,KAAKU,YAAc,KAClD0G,EAAKT,EAAM3G,KAAKW,eAAiBX,KAAKW,cAAgB,KAEtD0G,EAAyC,CAAA,EAC3CrH,KAAKa,aACPwG,EAAc,kBAAkBH,MAAQ,EACpClH,KAAKM,YACP+G,EAAc,qBAAsB,IAIxC,MAAMC,EAAyC,CAAA,EAW/C,OAVItH,KAAKe,YACPuG,EAAuB,SAAI,EAC3BA,EAAc,kBAAkBF,MAAQ,EACpCpH,KAAKM,YACPgH,EAAc,qBAAsB,IAGtCA,EAAuB,SAAI,EAGtBC,CAAA;;;;4BAIiBvH,KAAKK;mBACdL,KAAK6C;UACd2E,EAAIxH,KAAKoB;UACTgE,EAAQ,CACRqC,gBAAiB,CACfvC,SAAU,IACVM,OAAQ;;;;UAMVkC,EAAK1H,KAAKG,QAAS,IAAMoH,CAAA;8CACWvH,KAAKG;;;qBAG9BwH,EAASN;4CACcrH,KAAKmC;;;;kBAI/BwF,EAAS,CACf,CAAC,gBAAgBR,KAAOnH,KAAKc;;;0CAICd,KAAKuC;;;qBAG1BoF,EAASL;4CACctH,KAAKwC;;UAEvCkF,EAAK1H,KAAKE,SAAU,IAAMqH,CAAA;;;;;;qBAMf,IAAMvH,KAAK4B,cAAc;;;;;;KAO5C,GArrBWhC,EAEJgI,aAAyD,CAC9D,WAAYC,GAHHjI,EAmHKkI,OAASC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA3GzBC,EAAA,CADCC,EAAS,CAAEC,KAAMC,QAASC,SAAS,KAPzBxI,EAQXyI,UAAA,OAAA,GAIAL,EAAA,CADCC,EAAS,CAAEC,KAAMC,QAASC,SAAS,KAXzBxI,EAYXyI,UAAA,WAAA,GAIAL,EAAA,CADCC,EAAS,CAAEC,KAAMI,UAfP1I,EAgBXyI,UAAA,UAAA,GAIAL,EAAA,CADCC,EAAS,CAAEC,KAAMC,QAASC,SAAS,KAnBzBxI,EAoBXyI,UAAA,UAAA,GAIAL,EAAA,CADCC,EAAS,CAAEC,KAAMI,UAvBP1I,EAwBXyI,UAAA,QAAA,GAIAL,EAAA,CADCC,EAAS,CAAEC,KAAMC,QAASC,SAAS,KA3BzBxI,EA4BXyI,UAAA,YAAA,GAIAL,EAAA,CADCC,EAAS,CAAEC,KAAMC,QAASC,SAAS,KA/BzBxI,EAgCXyI,UAAA,aAAA,GAIAL,EAAA,CADCC,EAAS,CAAEC,KAAMC,QAASC,SAAS,KAnCzBxI,EAoCXyI,UAAA,aAAA,GAIAL,EAAA,CADCC,EAAS,CAAEC,KAAMI,OAAQF,SAAS,KAvCxBxI,EAwCXyI,UAAA,gBAAA,GAIAL,EAAA,CADCC,EAAS,CAAEC,KAAMI,OAAQF,SAAS,KA3CxBxI,EA4CXyI,UAAA,cAAA,GAIAL,EAAA,CADCC,EAAS,CAAEC,KAAMI,OAAQF,SAAS,KA/CxBxI,EAgDXyI,UAAA,gBAAA,GAQAL,EAAA,CAJCC,EAAS,CACRC,KAAMC,QACNC,SAAS,KAtDAxI,EAwDXyI,UAAA,0BAAA,GAIQL,EAAA,CADPO,KA3DU3I,EA4DHyI,UAAA,aAAA,GAIAL,EAAA,CADPO,KA/DU3I,EAgEHyI,UAAA,WAAA,GAIAL,EAAA,CADPO,KAnEU3I,EAoEHyI,UAAA,aAAA,GAIAL,EAAA,CADPO,KAvEU3I,EAwEHyI,UAAA,aAAA,GAaAL,EAAA,CADPQ,EAAM,WApFI5I,EAqFHyI,UAAA,gBAAA,GA0BmCL,EAAA,CAA1CC,EAAS,CAAEC,KAAMI,OAAQF,SAAS,KA/GxBxI,EA+GgCyI,UAAA,SAAA,GA/GhCzI,EAANoI,EAAA,CADNS,EAAc,eACF7I"}
|