wj-elements 0.1.255 → 0.2.0-alpha.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"wje-dialog.js","sources":["../packages/wje-dialog/dialog.element.js","../packages/wje-dialog/dialog.js"],"sourcesContent":["import { default as WJElement, event } from '../wje-element/element.js';\nimport '../wje-button/button.element.js';\nimport '../wje-icon/icon.element.js';\nimport styles from './styles/styles.css?inline';\n\n/**\n * `Dialog` is a custom web component that represents a dialog.\n * @summary This element represents a dialog.\n * @documentation https://elements.webjet.sk/components/dialog\n * @status stable\n * @augments {WJElement}\n * @slot header - Slot for the header content.\n * @slot body - Slot for the body content.\n * @slot footer - Slot for the footer content.\n * @csspart dialog - The dialog wrapper.\n * @csspart header - The header of the dialog.\n * @csspart body - The body of the dialog.\n * @csspart footer - The footer of the dialog.\n * @csspart close - The close button of the dialog.\n * @cssproperty [--wje-dialog-background=var(--wje-background-color)] - Specifies the background color of the dialog.\n * @cssproperty [--wje-dialog-color=var(--wje-text-color)] - Defines the text color within the dialog.\n * @cssproperty [--wje-dialog-padding=1rem] - Controls the padding inside the dialog.\n * @cssproperty [--wje-dialog-border-radius=0.5rem] - Sets the border radius for the dialog's corners.\n * @cssproperty [--wje-dialog-box-shadow=0 2px 10px rgba(0, 0, 0, 0.1)] - Applies a shadow effect to the dialog.\n * @tag wje-dialog\n */\n\nexport default class Dialog extends WJElement {\n /**\n * @class\n */\n constructor() {\n super();\n }\n\n /**\n * Sets the value of the 'headline' attribute.\n * @param {string} value The new value for the 'headline' attribute.\n */\n set headline(value) {\n this.setAttribute('headline', value);\n }\n\n /**\n * Retrieves the value of the \"headline\" attribute from the element.\n * If the \"headline\" attribute is not present, returns an empty string.\n * @returns {string} The headline attribute value or an empty string if not set.\n */\n get headline() {\n return this.getAttribute('headline') || '';\n }\n\n /**\n * Sets the headline of the dialog.\n * @param value\n */\n set placement(value) {\n this.setAttribute('placement', value);\n }\n\n /**\n * Gets the headline of the dialog.\n * @returns {string|string}\n */\n get placement() {\n return this.getAttribute('placement') || 'slide-up';\n }\n\n /**\n * Sets the headline of the dialog.\n * @param value\n */\n set async(value) {\n this.setAttribute('async', '');\n }\n\n /**\n * Gets the headline of the dialog.\n * @returns {boolean}\n */\n get async() {\n return this.hasAttribute('async');\n }\n\n /**\n * Sets the headline of the dialog.\n * @param value\n */\n set closeHidden(value) {\n if (value) this.setAttribute('close-hidden', '');\n }\n\n /**\n * Gets the headline of the dialog.\n * @returns {boolean}\n */\n get closeHidden() {\n return !!this.hasAttribute('close-hidden');\n }\n\n set hiddenHeader(value) {\n if (value) this.setAttribute('hidden-header', '');\n }\n\n get hiddenHeader() {\n return !!this.hasAttribute('hidden-header');\n }\n\n set hiddenFooter(value) {\n if (value) this.setAttribute('hidden-footer', '');\n }\n\n get hiddenFooter() {\n return !!this.hasAttribute('hidden-footer');\n }\n\n /**\n * Sets the headline of the dialog.\n * @type {string}\n */\n className = 'Dialog';\n\n /**\n * Returns the CSS styles for the component.\n * @returns {*}\n */\n static get cssStyleSheet() {\n return styles;\n }\n\n /**\n * Returns the list of attributes to observe for changes.\n * @returns {*[]}\n */\n static get observedAttributes() {\n return [];\n }\n\n /**\n * Sets up the attributes for the component.\n */\n setupAttributes() {\n this.isShadowRoot = 'open';\n }\n\n /**\n * Draws the component.\n * @param {object} context The context for drawing.\n * @param {object} store The store for drawing.\n * @param {object} params The parameters for drawing.\n * @returns {DocumentFragment}\n */\n draw(context, store, params) {\n let fragment = document.createDocumentFragment();\n\n this.classList.add('fade', this.placement, params.size);\n\n let dialog = document.createElement('dialog');\n dialog.setAttribute('part', 'dialog');\n dialog.classList.add('modal-dialog');\n\n fragment.appendChild(dialog);\n\n this.dialog = dialog;\n\n return fragment;\n }\n\n /**\n * Draws the component after it has been drawn.\n * @param {object} context The context for drawing.\n * @param {object} store The store for drawing.\n * @param {object} params The parameters for drawing.\n */\n afterDraw(context, store, params) {\n if (params.trigger) {\n event.addListener(document, params.trigger, null, this.onOpen);\n }\n }\n\n /**\n * Creates the dialog body.\n * @param dialog\n */\n htmlDialogBody(dialog) {\n let icon = document.createElement('wje-icon');\n icon.setAttribute('name', 'x');\n icon.setAttribute('slot', 'icon-only');\n\n let close = document.createElement('wje-button');\n close.setAttribute('fill', 'link');\n close.setAttribute('size', 'small');\n close.setAttribute('part', 'close');\n close.addEventListener('click', (e) => {\n this.close(e);\n });\n\n let header = document.createElement('div');\n header.setAttribute('part', 'header');\n header.classList.add('dialog-header');\n if (this.hasAttribute('headline'))\n header.innerHTML = `<span part=\"headline\">${this.headline}</span>`;\n\n let slotHeader = document.createElement('slot');\n slotHeader.setAttribute('name', 'header');\n\n const headerActions = document.createElement('div');\n headerActions.classList.add('header-actions');\n headerActions.setAttribute('part', 'header-actions');\n headerActions.appendChild(slotHeader);\n\n let contentSlot = document.createElement('slot');\n\n let body = document.createElement('div');\n body.setAttribute('part', 'body');\n body.classList.add('dialog-content');\n\n let footer = document.createElement('div');\n footer.setAttribute('part', 'footer');\n footer.classList.add('dialog-footer');\n footer.innerHTML = '';\n\n let slotFooter = document.createElement('slot');\n slotFooter.setAttribute('name', 'footer');\n\n close.appendChild(icon);\n\n if (!this.closeHidden) header.appendChild(close);\n\n header.appendChild(headerActions);\n body.appendChild(contentSlot);\n footer.appendChild(slotFooter);\n\n if (!this.hiddenHeader) dialog.appendChild(header);\n dialog.appendChild(body);\n if (!this.hiddenFooter) dialog.appendChild(footer);\n }\n\n /**\n * Closes the dialog.\n * @param e\n */\n close(e) {\n this.onClose(e);\n }\n\n /**\n * Before the component is disconnected.\n */\n beforeDisconnect() {\n if (this.params?.trigger) {\n event.removeListener(document, this.params?.trigger, null, this.onOpen);\n }\n\n //this.dialog.removeEventListener('close', this.onClose);\n }\n\n /**\n * Before the dialog opens.\n */\n beforeOpen(dialog, trigger) {\n // Hook for extending behavior before the dialog opens\n }\n\n /**\n * After the dialog opens.\n */\n afterOpen(dialog, trigger) {\n // Hook for extending behavior after the dialog opens\n }\n\n /**\n * Before the dialog closes.\n */\n beforeClose(dialog, trigger) {\n // Hook for extending behavior before the dialog closes\n }\n\n /**\n * After the dialog closes.\n */\n afterClose(dialog, trigger) {\n // Hook for extending behavior after the dialog closes\n }\n\n /**\n * Opens the dialog.\n * @param e\n */\n onOpen = (e) => {\n if (this.dialog) {\n this.dialog.innerHTML = '';\n }\n\n setTimeout(() => {\n Promise.resolve(this.beforeOpen(this, e)).then((res) => {\n this.htmlDialogBody(this.dialog);\n\n this.dialog.showModal(); // Now open the dialog\n\n if (this.dialog.open) {\n Promise.resolve(this.afterOpen(this, e));\n }\n });\n }, 0);\n }\n\n /**\n * Closes the dialog.\n * @param {object} e\n */\n onClose = (e) => {\n Promise.resolve(this.beforeClose(this, e)).then((res) => {\n this.dialog.close(); // Now close the dialog\n\n if (!this.dialog.open) {\n Promise.resolve(this.afterClose(this, e));\n }\n });\n };\n\n /**\n * Registers an event listener on the provided button that triggers a blocking UI element\n * and executes a given promise when the button is clicked.\n * @param {HTMLElement} button The button element to attach the event listener to.\n * @param {Function} promise A function that returns a promise to be executed when the button is clicked.\n */\n registerBlockingEvent(button, promise) {\n button.addEventListener('wje-button:click', async (e) => {\n let blockingElement = document.createElement('div');\n blockingElement.classList.add('blocking-element');\n\n let icon = document.createElement('wje-icon');\n icon.setAttribute('name', 'loader-2');\n icon.setAttribute('size', '2x-large');\n\n blockingElement.appendChild(icon);\n\n let scrollOffset = this.dialog.scrollTop;\n blockingElement.style.top = `${scrollOffset}px`;\n blockingElement.style.bottom = `-${scrollOffset}px`;\n\n this.dialog.appendChild(blockingElement);\n\n await promise()\n .then((res) => {\n this.close();\n blockingElement.remove();\n })\n .catch((err) => {\n console.error(err);\n blockingElement.remove();\n });\n });\n }\n}\n","import Dialog from './dialog.element.js';\n\nexport default Dialog;\n\nDialog.define('wje-dialog', Dialog);\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wje-dialog.js","sources":["../packages/wje-dialog/dialog.element.js","../packages/wje-dialog/dialog.js"],"sourcesContent":["import { default as WJElement, event } from '../wje-element/element.js';\nimport '../wje-button/button.element.js';\nimport '../wje-icon/icon.element.js';\nimport styles from './styles/styles.css?inline';\n\n/**\n * `Dialog` is a custom web component that represents a dialog.\n * @summary This element represents a dialog.\n * @documentation https://elements.webjet.sk/components/dialog\n * @status stable\n * @augments {WJElement}\n * @slot header - Slot for the header content.\n * @slot body - Slot for the body content.\n * @slot footer - Slot for the footer content.\n * @csspart dialog - The dialog wrapper.\n * @csspart header - The header of the dialog.\n * @csspart body - The body of the dialog.\n * @csspart footer - The footer of the dialog.\n * @csspart close - The close button of the dialog.\n * @cssproperty [--wje-dialog-background=var(--wje-background-color)] - Specifies the background color of the dialog.\n * @cssproperty [--wje-dialog-color=var(--wje-text-color)] - Defines the text color within the dialog.\n * @cssproperty [--wje-dialog-padding=1rem] - Controls the padding inside the dialog.\n * @cssproperty [--wje-dialog-border-radius=0.5rem] - Sets the border radius for the dialog's corners.\n * @cssproperty [--wje-dialog-box-shadow=0 2px 10px rgba(0, 0, 0, 0.1)] - Applies a shadow effect to the dialog.\n * @tag wje-dialog\n */\n\nexport default class Dialog extends WJElement {\n /**\n * @class\n */\n constructor() {\n super();\n }\n\n /**\n * Sets the value of the 'headline' attribute.\n * @param {string} value The new value for the 'headline' attribute.\n */\n set headline(value) {\n this.setAttribute('headline', value);\n }\n\n /**\n * Retrieves the value of the \"headline\" attribute from the element.\n * If the \"headline\" attribute is not present, returns an empty string.\n * @returns {string} The headline attribute value or an empty string if not set.\n */\n get headline() {\n return this.getAttribute('headline') || '';\n }\n\n /**\n * Sets the headline of the dialog.\n * @param value\n */\n set placement(value) {\n this.setAttribute('placement', value);\n }\n\n /**\n * Gets the headline of the dialog.\n * @returns {string|string}\n */\n get placement() {\n return this.getAttribute('placement') || 'slide-up';\n }\n\n /**\n * Sets the headline of the dialog.\n * @param value\n */\n set async(value) {\n this.setAttribute('async', '');\n }\n\n /**\n * Gets the headline of the dialog.\n * @returns {boolean}\n */\n get async() {\n return this.hasAttribute('async');\n }\n\n /**\n * Sets the headline of the dialog.\n * @param value\n */\n set closeHidden(value) {\n if (value) this.setAttribute('close-hidden', '');\n }\n\n /**\n * Gets the headline of the dialog.\n * @returns {boolean}\n */\n get closeHidden() {\n return !!this.hasAttribute('close-hidden');\n }\n\n set hiddenHeader(value) {\n if (value) this.setAttribute('hidden-header', '');\n }\n\n get hiddenHeader() {\n return !!this.hasAttribute('hidden-header');\n }\n\n set hiddenFooter(value) {\n if (value) this.setAttribute('hidden-footer', '');\n }\n\n get hiddenFooter() {\n return !!this.hasAttribute('hidden-footer');\n }\n\n /**\n * Sets the headline of the dialog.\n * @type {string}\n */\n className = 'Dialog';\n\n /**\n * Returns the CSS styles for the component.\n * @returns {*}\n */\n static get cssStyleSheet() {\n return styles;\n }\n\n /**\n * Returns the list of attributes to observe for changes.\n * @returns {*[]}\n */\n static get observedAttributes() {\n return [];\n }\n\n /**\n * Sets up the attributes for the component.\n */\n setupAttributes() {\n this.isShadowRoot = 'open';\n }\n\n /**\n * Draws the component.\n * @param {object} context The context for drawing.\n * @param {object} store The store for drawing.\n * @param {object} params The parameters for drawing.\n * @returns {DocumentFragment}\n */\n draw(context, store, params) {\n let fragment = document.createDocumentFragment();\n\n this.classList.add('fade', this.placement, params.size);\n\n let dialog = document.createElement('dialog');\n dialog.setAttribute('part', 'dialog');\n dialog.classList.add('modal-dialog');\n\n fragment.appendChild(dialog);\n\n this.dialog = dialog;\n\n return fragment;\n }\n\n /**\n * Draws the component after it has been drawn.\n * @param {object} context The context for drawing.\n * @param {object} store The store for drawing.\n * @param {object} params The parameters for drawing.\n */\n afterDraw(context, store, params) {\n if (params.trigger) {\n event.addListener(document, params.trigger, null, this.onOpen);\n }\n }\n\n /**\n * Creates the dialog body.\n * @param dialog\n */\n htmlDialogBody(dialog) {\n let icon = document.createElement('wje-icon');\n icon.setAttribute('name', 'x');\n icon.setAttribute('slot', 'icon-only');\n\n let close = document.createElement('wje-button');\n close.setAttribute('fill', 'link');\n close.setAttribute('size', 'small');\n close.setAttribute('part', 'close');\n close.addEventListener('click', (e) => {\n this.close(e);\n });\n\n let header = document.createElement('div');\n header.setAttribute('part', 'header');\n header.classList.add('dialog-header');\n if (this.hasAttribute('headline'))\n header.innerHTML = `<span part=\"headline\">${this.headline}</span>`;\n\n let slotHeader = document.createElement('slot');\n slotHeader.setAttribute('name', 'header');\n\n const headerActions = document.createElement('div');\n headerActions.classList.add('header-actions');\n headerActions.setAttribute('part', 'header-actions');\n headerActions.appendChild(slotHeader);\n\n let contentSlot = document.createElement('slot');\n\n let body = document.createElement('div');\n body.setAttribute('part', 'body');\n body.classList.add('dialog-content');\n\n let footer = document.createElement('div');\n footer.setAttribute('part', 'footer');\n footer.classList.add('dialog-footer');\n footer.innerHTML = '';\n\n let slotFooter = document.createElement('slot');\n slotFooter.setAttribute('name', 'footer');\n slotFooter.id = 'footerSlot';\n slotFooter.addEventListener('slotchange', () => this.updateHasFooter());\n\n close.appendChild(icon);\n\n if (!this.closeHidden) header.appendChild(close);\n\n header.appendChild(headerActions);\n body.appendChild(contentSlot);\n footer.appendChild(slotFooter);\n\n if (!this.hiddenHeader) dialog.appendChild(header);\n dialog.appendChild(body);\n if (!this.hiddenFooter) dialog.appendChild(footer);\n\n // Slot assignment may not be reflected synchronously on first render.\n // Run in a microtask to ensure assignedNodes() sees slotted content.\n Promise.resolve().then(() => this.updateHasFooter());\n }\n\n /**\n * Closes the dialog.\n * @param e\n */\n close(e) {\n this.onClose(e);\n }\n\n /**\n * Before the component is disconnected.\n */\n beforeDisconnect() {\n if (this.params?.trigger) {\n event.removeListener(document, this.params?.trigger, null, this.onOpen);\n }\n\n //this.dialog.removeEventListener('close', this.onClose);\n }\n\n /**\n * Before the dialog opens.\n */\n beforeOpen(dialog, trigger) {\n // Hook for extending behavior before the dialog opens\n }\n\n /**\n * After the dialog opens.\n */\n afterOpen(dialog, trigger) {\n // Hook for extending behavior after the dialog opens\n }\n\n /**\n * Before the dialog closes.\n */\n beforeClose(dialog, trigger) {\n // Hook for extending behavior before the dialog closes\n }\n\n /**\n * After the dialog closes.\n */\n afterClose(dialog, trigger) {\n // Hook for extending behavior after the dialog closes\n }\n\n /**\n * Opens the dialog.\n * @param e\n */\n onOpen = (e) => {\n if (this.dialog) {\n this.dialog.innerHTML = '';\n }\n\n setTimeout(() => {\n Promise.resolve(this.beforeOpen(this, e)).then((res) => {\n this.htmlDialogBody(this.dialog);\n\n this.dialog.showModal(); // Now open the dialog\n\n if (this.dialog.open) {\n Promise.resolve(this.afterOpen(this, e));\n }\n });\n }, 0);\n }\n\n /**\n * Closes the dialog.\n * @param {object} e\n */\n onClose = (e) => {\n Promise.resolve(this.beforeClose(this, e)).then((res) => {\n this.dialog.close(); // Now close the dialog\n\n if (!this.dialog.open) {\n Promise.resolve(this.afterClose(this, e));\n }\n });\n };\n\n /**\n * Registers an event listener on the provided button that triggers a blocking UI element\n * and executes a given promise when the button is clicked.\n * @param {HTMLElement} button The button element to attach the event listener to.\n * @param {Function} promise A function that returns a promise to be executed when the button is clicked.\n */\n registerBlockingEvent(button, promise) {\n button.addEventListener('wje-button:click', async (e) => {\n let blockingElement = document.createElement('div');\n blockingElement.classList.add('blocking-element');\n\n let icon = document.createElement('wje-icon');\n icon.setAttribute('name', 'loader-2');\n icon.setAttribute('size', '2x-large');\n\n blockingElement.appendChild(icon);\n\n let scrollOffset = this.dialog.scrollTop;\n blockingElement.style.top = `${scrollOffset}px`;\n blockingElement.style.bottom = `-${scrollOffset}px`;\n\n this.dialog.appendChild(blockingElement);\n\n await promise()\n .then((res) => {\n this.close();\n blockingElement.remove();\n })\n .catch((err) => {\n console.error(err);\n blockingElement.remove();\n });\n });\n }\n\n updateHasFooter() {\n // If footer is intentionally hidden, ensure it doesn't reserve space\n if (this.hiddenFooter) {\n const footerEl = this.shadowRoot?.querySelector('.dialog-footer');\n if (footerEl) footerEl.setAttribute('hidden', '');\n this.removeAttribute('has-footer');\n return;\n }\n const slot = this.shadowRoot?.getElementById('footerSlot');\n if (!slot) {\n this.removeAttribute('has-footer');\n return;\n }\n\n const assigned = slot.assignedNodes({ flatten: true });\n const hasContent = assigned.some((n) => {\n if (n.nodeType === Node.ELEMENT_NODE) return true;\n if (n.nodeType === Node.TEXT_NODE) return n.textContent.trim().length > 0;\n return false;\n });\n\n // Prefer toggling the actual footer element so CSS/spacing is always correct\n const footerEl = this.shadowRoot?.querySelector('.dialog-footer');\n if (footerEl) footerEl.toggleAttribute('hidden', !hasContent);\n\n // Keep host attribute too (harmless, may be used elsewhere)\n this.toggleAttribute('has-footer', hasContent);\n }\n}\n","import Dialog from './dialog.element.js';\n\nexport default Dialog;\n\nDialog.define('wje-dialog', Dialog);\n"],"names":["footerEl"],"mappings":"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"}
@@ -15,8 +15,201 @@ import { Permissions } from "./permissions.js";
15
15
  import { WjElementUtils } from "./element-utils.js";
16
16
  import { event } from "./event.js";
17
17
  import { store, defaultStoreActions } from "./wje-store.js";
18
+ const skeletonCss = `/*
19
+ [ Skeleton ]
20
+ */
21
+
22
+ @keyframes wje-skeleton-shimmer {
23
+ from {
24
+ transform: translateX(-100%);
25
+ }
26
+ to {
27
+ transform: translateX(100%);
28
+ }
29
+ }
30
+
31
+ .wje-skeleton {
32
+ cursor: progress;
33
+ user-select: none;
34
+ }
35
+
36
+ .wje-skeleton-overlay {
37
+ display: grid;
38
+ }
39
+
40
+ .wje-skeleton-overlay-content,
41
+ .wje-skeleton-overlay-skeleton {
42
+ grid-area: 1 / 1;
43
+ }
44
+
45
+ .wje-skeleton-overlay-skeleton {
46
+ display: none; /* shown in loading state */
47
+ pointer-events: none; /* skeleton should not be interactive */
48
+ align-self: stretch;
49
+ justify-self: stretch;
50
+ }
51
+
52
+ .wje-skeleton-overlay-skeleton > .wje-skeleton {
53
+ width: 100%;
54
+ height: 100%;
55
+ box-sizing: border-box;
56
+ }
57
+
58
+ /* Base "shape" primitives */
59
+ .wje-skeleton-line,
60
+ .wje-skeleton-head,
61
+ .wje-skeleton-circle,
62
+ .wje-skeleton-square {
63
+ position: relative;
64
+ overflow: hidden;
65
+ background-color: var(--wje-skeleton-bg);
66
+ }
67
+
68
+ .wje-skeleton-line {
69
+ display: block;
70
+ height: var(--wje-skeleton-height);
71
+ border-radius: var(--wje-skeleton-radius);
72
+ width: 100%;
73
+ flex: 0 0 auto;
74
+ }
75
+
76
+ .wje-skeleton-flex-row {
77
+ display: flex;
78
+ flex-direction: row;
79
+ align-items: center;
80
+ gap: var(--wje-skeleton-gap);
81
+ }
82
+
83
+ .wje-skeleton-flex-column {
84
+ display: flex;
85
+ flex-direction: column;
86
+ gap: var(--wje-skeleton-gap);
87
+ }
88
+
89
+ .wje-skeleton-line + .wje-skeleton-line,
90
+ .wje-skeleton-line + .wje-skeleton-head,
91
+ .wje-skeleton-head + .wje-skeleton-line,
92
+ .wje-skeleton-head + .wje-skeleton-head,
93
+ .wje-skeleton-circle + .wje-skeleton-line,
94
+ .wje-skeleton-line + .wje-skeleton-circle {
95
+ /*margin-top: var(--wje-skeleton-gap);*/
96
+ }
97
+
98
+ .wje-skeleton-head {
99
+ display: block;
100
+ height: var(--wje-skeleton-head-height);
101
+ border-radius: var(--wje-skeleton-radius);
102
+ width: 100%;
103
+ }
104
+
105
+ .wje-skeleton-circle {
106
+ display: block;
107
+ /* Take remaining width in flex row (e.g. 40% if sibling is w-60) */
108
+ flex: 1 1 0;
109
+ max-width: 100%;
110
+ aspect-ratio: 1 / 1;
111
+ border-radius: 50%;
112
+ }
113
+
114
+ .wje-skeleton-square {
115
+ display: block;
116
+ width: 100%;
117
+ height: 150px;
118
+ border-radius: 8px;
119
+ }
120
+
121
+ /* Shimmer layer */
122
+ .wje-skeleton-line::after,
123
+ .wje-skeleton-head::after,
124
+ .wje-skeleton-circle::after,
125
+ .wje-skeleton-square::after {
126
+ content: '';
127
+ position: absolute;
128
+ inset: 0;
129
+ transform: translateX(-100%);
130
+ animation: wje-skeleton-shimmer var(--wje-skeleton-duration) infinite;
131
+ background: linear-gradient(90deg, transparent, var(--wje-skeleton-highlight), transparent);
132
+ }
133
+
134
+ /* "Stronger" variant */
135
+ .wje-skeleton-strong .wje-skeleton-line,
136
+ .wje-skeleton-strong .wje-skeleton-head,
137
+ .wje-skeleton-strong .wje-skeleton-circle,
138
+ .wje-skeleton-strong .wje-skeleton-square {
139
+ background-color: var(--wje-skeleton-bg-strong);
140
+ }
141
+
142
+ /* Simple layout helpers (opt-in) */
143
+ .wje-skeleton .flex { display: flex; }
144
+ .wje-skeleton .inline-flex { display: inline-flex; }
145
+ .wje-skeleton .align-center { align-items: center; }
146
+ .wje-skeleton .align-start { align-items: flex-start; }
147
+ .wje-skeleton .align-end { align-items: flex-end; }
148
+ .wje-skeleton .justify-start { justify-content: flex-start; }
149
+ .wje-skeleton .justify-end { justify-content: flex-end; }
150
+ .wje-skeleton .justify-between { justify-content: space-between; }
151
+ .wje-skeleton .justify-center { justify-content: center; }
152
+ .wje-skeleton .justify-around { justify-content: space-around; }
153
+
154
+ .wje-skeleton .gap { gap: var(--wje-skeleton-gap); }
155
+
156
+ .wje-skeleton .w-5 { width: 5%; }
157
+ .wje-skeleton .w-10 { width: 10%; }
158
+ .wje-skeleton .w-15 { width: 15%; }
159
+ .wje-skeleton .w-20 { width: 20%; }
160
+ .wje-skeleton .w-25 { width: 25%; }
161
+ .wje-skeleton .w-30 { width: 30%; }
162
+ .wje-skeleton .w-35 { width: 35%; }
163
+ .wje-skeleton .w-40 { width: 40%; }
164
+ .wje-skeleton .w-45 { width: 45%; }
165
+ .wje-skeleton .w-50 { width: 50%; }
166
+ .wje-skeleton .w-55 { width: 55%; }
167
+ .wje-skeleton .w-60 { width: 60%; }
168
+ .wje-skeleton .w-65 { width: 65%; }
169
+ .wje-skeleton .w-70 { width: 70%; }
170
+ .wje-skeleton .w-75 { width: 75%; }
171
+ .wje-skeleton .w-80 { width: 80%; }
172
+ .wje-skeleton .w-85 { width: 85%; }
173
+ .wje-skeleton .w-90 { width: 90%; }
174
+ .wje-skeleton .w-95 { width: 95%; }
175
+ .wje-skeleton .w-100 { width: 100%; }
176
+
177
+ .wje-skeleton .mb { margin-bottom: 16px; }
178
+ .wje-skeleton .mt { margin-top: 16px; }
179
+ .wje-skeleton .mr { margin-right: 16px; }
180
+ .wje-skeleton .ml { margin-left: 16px; }
181
+ .wje-skeleton .mbs { margin-bottom: 8px; }
182
+ .wje-skeleton .mts { margin-top: 8px; }
183
+ .wje-skeleton .mrs { margin-right: 8px; }
184
+ .wje-skeleton .mls { margin-left: 8px; }
185
+
186
+ /* Optional state helpers */
187
+ .wje-skeleton-loading .wje-skeleton-overlay-content {
188
+ visibility: hidden;
189
+ pointer-events: none;
190
+ }
191
+
192
+ .wje-skeleton-loading .wje-skeleton-overlay-skeleton {
193
+ display: block;
194
+ }
195
+
196
+ @media (prefers-reduced-motion: reduce) {
197
+ .wje-skeleton-line::after,
198
+ .wje-skeleton-head::after,
199
+ .wje-skeleton-circle::after,
200
+ .wje-skeleton-square::after {
201
+ animation: none;
202
+ transform: none;
203
+ opacity: 0.35;
204
+ }
205
+ }`;
18
206
  const template = document.createElement("template");
19
207
  template.innerHTML = ``;
208
+ const wjSkeletonSheet = new CSSStyleSheet();
209
+ try {
210
+ wjSkeletonSheet.replaceSync(skeletonCss);
211
+ } catch (e) {
212
+ }
20
213
  const _WJElement = class _WJElement extends HTMLElement {
21
214
  /**
22
215
  * Initializes a new instance of the WJElement class.
@@ -44,10 +237,18 @@ const _WJElement = class _WJElement extends HTMLElement {
44
237
  }
45
238
  this.setUpAccessors();
46
239
  __privateSet(this, _drawingStatus, this.drawingStatuses.START);
47
- await this.display(force);
48
240
  const sheet = new CSSStyleSheet();
49
241
  sheet.replaceSync(this.constructor.cssStyleSheet);
50
- this.context.adoptedStyleSheets = [sheet];
242
+ if (this.shadowRoot) {
243
+ const existing = this.shadowRoot.adoptedStyleSheets || [];
244
+ const next = [...existing];
245
+ if (!next.includes(wjSkeletonSheet) && wjSkeletonSheet.cssRules !== void 0) {
246
+ next.push(wjSkeletonSheet);
247
+ }
248
+ if (!next.includes(sheet)) next.push(sheet);
249
+ this.shadowRoot.adoptedStyleSheets = next;
250
+ }
251
+ await this.display(force);
51
252
  resolve();
52
253
  });
53
254
  });
@@ -223,8 +424,9 @@ const _WJElement = class _WJElement extends HTMLElement {
223
424
  * Defines component dependencies by registering custom elements.
224
425
  */
225
426
  defineDependencies() {
226
- if (this.dependencies)
227
- Object.entries(this.dependencies).forEach((name, component) => _WJElement.define(name, component));
427
+ if (this.dependencies) {
428
+ Object.entries(this.dependencies).forEach(([name, component]) => _WJElement.define(name, component));
429
+ }
228
430
  }
229
431
  /**
230
432
  * Hook for extending behavior before drawing the component.
@@ -263,6 +465,81 @@ const _WJElement = class _WJElement extends HTMLElement {
263
465
  */
264
466
  afterDraw(context, appStoreObj, params) {
265
467
  }
468
+ /**
469
+ * Optional hook: return skeleton markup used while async draw is in progress.
470
+ * Prefer declarative skeleton via `<div slot="skeleton">...</div>`.
471
+ * Return: string | Node | DocumentFragment | null | Promise of those.
472
+ */
473
+ renderSkeleton(params) {
474
+ return null;
475
+ }
476
+ /**
477
+ * Retrieves the delay duration for the skeleton display, determining the value based on a hierarchy of overrides and defaults.
478
+ * The method prioritizes in the following order:
479
+ * 1. A finite number set as the `_wjSkeletonSlotClone` property.
480
+ * 2. A valid numeric value from the `skeleton-delay` attribute.
481
+ * 3. The `skeletonDelayMs` property, if defined with a finite number.
482
+ * 4. A default value of 150 if none of the above are set.
483
+ * @returns {number} The delay in milliseconds before the skeleton is displayed.
484
+ */
485
+ get skeletonDelay() {
486
+ if (Number.isFinite(this._wjSkeletonSlotClone)) return this._wjSkeletonSlotClone;
487
+ const v = this.getAttribute("skeleton-delay");
488
+ const n = v == null ? NaN : Number(v);
489
+ if (Number.isFinite(n)) return n;
490
+ if (Number.isFinite(this.skeletonDelayMs)) return this.skeletonDelayMs;
491
+ return 150;
492
+ }
493
+ /**
494
+ * Retrieves the minimum duration for the skeleton animation.
495
+ * The method checks for an internally stored finite value. If unavailable,
496
+ * it retrieves the value from the 'skeleton-min-duration' attribute,
497
+ * converts it to a number if possible, and uses it. If neither is valid,
498
+ * a default duration of 300 is returned.
499
+ * @returns {number} The minimum duration for the skeleton animation in milliseconds.
500
+ */
501
+ get skeletonMinDuration() {
502
+ if (Number.isFinite(this._wjSkeletonSlotClone)) return this._wjSkeletonSlotClone;
503
+ const v = this.getAttribute("skeleton-min-duration");
504
+ const n = v == null ? NaN : Number(v);
505
+ if (Number.isFinite(n)) return n;
506
+ return 300;
507
+ }
508
+ set skeletonMinDuration(value) {
509
+ if (value === null || value === void 0 || value === "") {
510
+ delete this._wjSkeletonSlotClone;
511
+ this.removeAttribute("skeleton-min-duration");
512
+ return;
513
+ }
514
+ const n = Number(value);
515
+ if (Number.isFinite(n)) {
516
+ this._wjSkeletonSlotClone = n;
517
+ this.setAttribute("skeleton-min-duration", String(n));
518
+ }
519
+ }
520
+ // Public API: property-based control
521
+ set skeleton(value) {
522
+ if (value) this.setAttribute("skeleton", "");
523
+ else this.removeAttribute("skeleton");
524
+ }
525
+ get skeleton() {
526
+ return this.hasAttribute("skeleton");
527
+ }
528
+ set skeletonDelay(value) {
529
+ if (value === null || value === void 0 || value === "") {
530
+ delete this._wjSkeletonSlotClone;
531
+ this.removeAttribute("skeleton-delay");
532
+ return;
533
+ }
534
+ const n = Number(value);
535
+ if (Number.isFinite(n)) {
536
+ this._wjSkeletonSlotClone = n;
537
+ this.setAttribute("skeleton-delay", String(n));
538
+ }
539
+ }
540
+ get skeletonDelayValue() {
541
+ return this.skeletonDelay;
542
+ }
266
543
  /**
267
544
  * Lifecycle method invoked when the component is connected to the DOM.
268
545
  */
@@ -386,22 +663,157 @@ const _WJElement = class _WJElement extends HTMLElement {
386
663
  */
387
664
  display(force = false) {
388
665
  this.template = this.constructor.customTemplate || document.createElement("template");
389
- if (force) {
390
- [...this.context.childNodes].forEach(this.context.removeChild.bind(this.context));
391
- }
392
- this.context.append(this.template.content.cloneNode(true));
666
+ const nextContext = document.createDocumentFragment();
667
+ nextContext.append(this.template.content.cloneNode(true));
393
668
  if (this.noShow || this.isPermissionCheck && !Permissions.isPermissionFulfilled(this.permission)) {
394
669
  this.remove();
395
670
  return Promise.resolve();
396
671
  }
397
- return __privateMethod(this, _WJElement_instances, resolveRender_fn).call(this);
672
+ let skeletonTimer = null;
673
+ let renderFinished = false;
674
+ let skeletonShownAt = null;
675
+ const clearSkeletonTimer = () => {
676
+ if (skeletonTimer) {
677
+ clearTimeout(skeletonTimer);
678
+ skeletonTimer = null;
679
+ }
680
+ };
681
+ const buildSkeletonFragment = async () => {
682
+ var _a, _b;
683
+ const slotted = this.querySelector('[slot="skeleton"]');
684
+ if (slotted) {
685
+ this._wjSkeletonSlotClone = slotted.cloneNode(true);
686
+ const frag2 = document.createDocumentFragment();
687
+ frag2.append(this._wjSkeletonSlotClone.cloneNode(true));
688
+ return frag2;
689
+ }
690
+ if (this._wjSkeletonSlotClone) {
691
+ const frag2 = document.createDocumentFragment();
692
+ frag2.append(this._wjSkeletonSlotClone.cloneNode(true));
693
+ return frag2;
694
+ }
695
+ const frag = document.createDocumentFragment();
696
+ let skel = (_a = this.renderSkeleton) == null ? void 0 : _a.call(this, WjElementUtils.getAttributes(this));
697
+ if (skel instanceof Promise || ((_b = skel == null ? void 0 : skel.constructor) == null ? void 0 : _b.name) === "Promise") {
698
+ skel = await skel;
699
+ }
700
+ if (skel == null) return null;
701
+ let node;
702
+ if (skel instanceof HTMLElement || skel instanceof DocumentFragment) {
703
+ node = skel;
704
+ } else {
705
+ const t = document.createElement("template");
706
+ t.innerHTML = skel;
707
+ node = t.content.cloneNode(true);
708
+ }
709
+ frag.append(node);
710
+ return frag;
711
+ };
712
+ const showSkeleton = async () => {
713
+ if (renderFinished) return;
714
+ if (!this.hasAttribute("skeleton")) return;
715
+ const frag = await buildSkeletonFragment();
716
+ if (!frag) return;
717
+ try {
718
+ const cs = getComputedStyle(this);
719
+ if (cs.display === "inline") this.style.display = "block";
720
+ if (cs.width === "0px") this.style.width = "100%";
721
+ } catch (e) {
722
+ }
723
+ if (this.hasShadowRoot) {
724
+ if (this.shadowRoot) {
725
+ const existing = this.shadowRoot.adoptedStyleSheets || [];
726
+ if (!existing.includes(wjSkeletonSheet) && wjSkeletonSheet.cssRules !== void 0) {
727
+ this.shadowRoot.adoptedStyleSheets = [...existing, wjSkeletonSheet];
728
+ }
729
+ }
730
+ this.shadowRoot.replaceChildren(frag);
731
+ } else {
732
+ this.replaceChildren(frag);
733
+ }
734
+ skeletonShownAt = performance.now();
735
+ this.dispatchEvent(new CustomEvent("wj:skeleton:show", {
736
+ detail: { delay: this.skeletonDelay },
737
+ bubbles: true,
738
+ composed: true
739
+ }));
740
+ if (this.hasAttribute("debug-skeleton")) {
741
+ debugger;
742
+ }
743
+ };
744
+ if (this.hasAttribute("skeleton") && this.style.visibility === "hidden") {
745
+ this.style.visibility = __privateGet(this, _originalVisibility) ?? "visible";
746
+ }
747
+ let skeletonPlannedAt;
748
+ if (this.hasAttribute("skeleton")) {
749
+ skeletonPlannedAt = performance.now();
750
+ skeletonTimer = setTimeout(() => {
751
+ showSkeleton();
752
+ }, this.skeletonDelay);
753
+ }
754
+ return __privateMethod(this, _WJElement_instances, resolveRender_fn).call(this, nextContext, { skipAfterDraw: true }).then(async () => {
755
+ var _a;
756
+ renderFinished = true;
757
+ clearSkeletonTimer();
758
+ if (skeletonShownAt === null) {
759
+ const elapsed = skeletonPlannedAt ? performance.now() - skeletonPlannedAt : 0;
760
+ this.dispatchEvent(new CustomEvent("wj:skeleton:skip", {
761
+ detail: { reason: "render-finished-fast", elapsedMs: elapsed, delay: this.skeletonDelay },
762
+ bubbles: true,
763
+ composed: true
764
+ }));
765
+ } else {
766
+ const now = performance.now();
767
+ const visibleFor = now - skeletonShownAt;
768
+ const remaining = this.skeletonMinDuration - visibleFor;
769
+ if (remaining > 0) {
770
+ await new Promise((r) => setTimeout(r, remaining));
771
+ }
772
+ }
773
+ if (this.hasShadowRoot) {
774
+ this.shadowRoot.replaceChildren(nextContext);
775
+ } else {
776
+ this.replaceChildren(nextContext);
777
+ }
778
+ if (skeletonShownAt !== null) {
779
+ this.dispatchEvent(new CustomEvent("wj:skeleton:hide", {
780
+ detail: {
781
+ reason: "render-finished",
782
+ visibleMs: Math.max(this.skeletonMinDuration, performance.now() - skeletonShownAt),
783
+ delay: this.skeletonDelay,
784
+ minDuration: this.skeletonMinDuration
785
+ },
786
+ bubbles: true,
787
+ composed: true
788
+ }));
789
+ }
790
+ const liveContext = this.hasShadowRoot ? this.shadowRoot : this;
791
+ const _afterDraw = (_a = this.afterDraw) == null ? void 0 : _a.call(this, liveContext, this.store, WjElementUtils.getAttributes(this));
792
+ if (_afterDraw instanceof Promise || (_afterDraw == null ? void 0 : _afterDraw.constructor.name) === "Promise") {
793
+ await _afterDraw;
794
+ }
795
+ }).finally(() => {
796
+ renderFinished = true;
797
+ clearSkeletonTimer();
798
+ if (!__privateGet(this, _isRendering)) {
799
+ this.dispatchEvent(new CustomEvent("wj:skeleton:hide", {
800
+ detail: { reason: "finally" },
801
+ bubbles: true,
802
+ composed: true
803
+ }));
804
+ }
805
+ });
398
806
  }
399
807
  /**
400
- * Renders the component's content.
808
+ * Renders the content into the provided target context.
809
+ * This method handles asynchronous rendering, processes the output from the `draw` method,
810
+ * and appends the resulting content to the specified target context.
811
+ * @returns {Promise<void>} A promise that resolves once the render operation is complete and the content is appended to the target context.
812
+ * @param targetContext
401
813
  */
402
- async render() {
814
+ async render(targetContext = this.context) {
403
815
  __privateSet(this, _drawingStatus, this.drawingStatuses.DRAWING);
404
- let _draw = this.draw(this.context, this.store, WjElementUtils.getAttributes(this));
816
+ let _draw = this.draw(targetContext, this.store, WjElementUtils.getAttributes(this));
405
817
  if (_draw instanceof Promise || (_draw == null ? void 0 : _draw.constructor.name) === "Promise") {
406
818
  _draw = await _draw;
407
819
  }
@@ -415,7 +827,7 @@ const _WJElement = class _WJElement extends HTMLElement {
415
827
  element = inputTemplate.content.cloneNode(true);
416
828
  }
417
829
  let rendered = element;
418
- this.context.appendChild(rendered);
830
+ targetContext.appendChild(rendered);
419
831
  }
420
832
  /**
421
833
  * Sanitizes a given name by converting it from kebab-case to camelCase.
@@ -524,22 +936,25 @@ refresh_fn = async function() {
524
936
  }
525
937
  };
526
938
  /**
527
- * Resolves the rendering process of the component.
939
+ * Resolves the rendering process of the component, using the given target context.
940
+ * @param {HTMLElement|ShadowRoot|DocumentFragment} targetContext Target for rendering (defaults to this.context)
528
941
  * @returns A promise that resolves when rendering is complete.
529
942
  * @private
530
943
  */
531
- resolveRender_fn = function() {
944
+ resolveRender_fn = function(targetContext = this.context, { skipAfterDraw = false } = {}) {
532
945
  this.params = WjElementUtils.getAttributes(this);
533
946
  return new Promise(async (resolve, reject) => {
534
947
  var _a;
535
- const __beforeDraw = this.beforeDraw(this.context, this.store, WjElementUtils.getAttributes(this));
536
- if (__beforeDraw instanceof Promise || (__beforeDraw == null ? void 0 : __beforeDraw.constructor.name) === "Promise") {
537
- await __beforeDraw;
948
+ const _beforeDraw = this.beforeDraw(targetContext, this.store, WjElementUtils.getAttributes(this));
949
+ if (_beforeDraw instanceof Promise || (_beforeDraw == null ? void 0 : _beforeDraw.constructor.name) === "Promise") {
950
+ await _beforeDraw;
538
951
  }
539
- await this.render();
540
- const __afterDraw = (_a = this.afterDraw) == null ? void 0 : _a.call(this, this.context, this.store, WjElementUtils.getAttributes(this));
541
- if (__afterDraw instanceof Promise || (__afterDraw == null ? void 0 : __afterDraw.constructor.name) === "Promise") {
542
- await __afterDraw;
952
+ await this.render(targetContext);
953
+ if (!skipAfterDraw) {
954
+ const _afterDraw = (_a = this.afterDraw) == null ? void 0 : _a.call(this, targetContext, this.store, WjElementUtils.getAttributes(this));
955
+ if (_afterDraw instanceof Promise || (_afterDraw == null ? void 0 : _afterDraw.constructor.name) === "Promise") {
956
+ await _afterDraw;
957
+ }
543
958
  }
544
959
  __privateSet(this, _isRendering, false);
545
960
  __privateSet(this, _isAttached, true);