snice 1.13.2 → 1.13.4
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/bin/templates/base/README.md +1 -1
- package/dist/components/accordion/snice-accordion-item.d.ts +25 -0
- package/dist/components/accordion/snice-accordion-item.js +260 -0
- package/dist/components/accordion/snice-accordion-item.js.map +1 -0
- package/dist/components/accordion/snice-accordion.d.ts +28 -0
- package/dist/components/accordion/snice-accordion.js +221 -0
- package/dist/components/accordion/snice-accordion.js.map +1 -0
- package/dist/components/accordion/snice-accordion.types.d.ts +29 -0
- package/dist/components/accordion/snice-accordion.types.js +2 -0
- package/dist/components/accordion/snice-accordion.types.js.map +1 -0
- package/dist/components/alert/snice-alert.d.ts +26 -0
- package/dist/components/alert/snice-alert.js +191 -0
- package/dist/components/alert/snice-alert.js.map +1 -0
- package/dist/components/alert/snice-alert.types.d.ts +11 -0
- package/dist/components/alert/snice-alert.types.js +2 -0
- package/dist/components/alert/snice-alert.types.js.map +1 -0
- package/dist/components/avatar/snice-avatar.d.ts +24 -0
- package/dist/components/avatar/snice-avatar.js +177 -0
- package/dist/components/avatar/snice-avatar.js.map +1 -0
- package/dist/components/avatar/snice-avatar.types.d.ts +12 -0
- package/dist/components/avatar/snice-avatar.types.js +2 -0
- package/dist/components/avatar/snice-avatar.types.js.map +1 -0
- package/dist/components/badge/snice-badge.d.ts +25 -0
- package/dist/components/badge/snice-badge.js +157 -0
- package/dist/components/badge/snice-badge.js.map +1 -0
- package/dist/components/badge/snice-badge.types.d.ts +15 -0
- package/dist/components/badge/snice-badge.types.js +2 -0
- package/dist/components/badge/snice-badge.types.js.map +1 -0
- package/dist/components/breadcrumbs/snice-breadcrumbs.d.ts +27 -0
- package/dist/components/breadcrumbs/snice-breadcrumbs.js +212 -0
- package/dist/components/breadcrumbs/snice-breadcrumbs.js.map +1 -0
- package/dist/components/breadcrumbs/snice-breadcrumbs.types.d.ts +23 -0
- package/dist/components/breadcrumbs/snice-breadcrumbs.types.js +2 -0
- package/dist/components/breadcrumbs/snice-breadcrumbs.types.js.map +1 -0
- package/dist/components/breadcrumbs/snice-crumb.d.ts +9 -0
- package/dist/components/breadcrumbs/snice-crumb.js +50 -0
- package/dist/components/breadcrumbs/snice-crumb.js.map +1 -0
- package/dist/components/button/snice-button.d.ts +32 -0
- package/dist/components/button/snice-button.js +212 -0
- package/dist/components/button/snice-button.js.map +1 -0
- package/dist/components/button/snice-button.types.d.ts +23 -0
- package/dist/components/button/snice-button.types.js +2 -0
- package/dist/components/button/snice-button.types.js.map +1 -0
- package/dist/components/card/snice-card.d.ts +19 -0
- package/dist/components/card/snice-card.js +132 -0
- package/dist/components/card/snice-card.js.map +1 -0
- package/dist/components/card/snice-card.types.d.ts +9 -0
- package/dist/components/card/snice-card.types.js +2 -0
- package/dist/components/card/snice-card.types.js.map +1 -0
- package/dist/components/checkbox/snice-checkbox.d.ts +34 -0
- package/dist/components/checkbox/snice-checkbox.js +289 -0
- package/dist/components/checkbox/snice-checkbox.js.map +1 -0
- package/dist/components/checkbox/snice-checkbox.types.d.ts +20 -0
- package/dist/components/checkbox/snice-checkbox.types.js +2 -0
- package/dist/components/checkbox/snice-checkbox.types.js.map +1 -0
- package/dist/components/chip/snice-chip.d.ts +28 -0
- package/dist/components/chip/snice-chip.js +203 -0
- package/dist/components/chip/snice-chip.js.map +1 -0
- package/dist/components/chip/snice-chip.types.d.ts +14 -0
- package/dist/components/chip/snice-chip.types.js +2 -0
- package/dist/components/chip/snice-chip.types.js.map +1 -0
- package/dist/components/date-picker/snice-date-picker.d.ts +82 -0
- package/dist/components/date-picker/snice-date-picker.js +880 -0
- package/dist/components/date-picker/snice-date-picker.js.map +1 -0
- package/dist/components/date-picker/snice-date-picker.types.d.ts +71 -0
- package/dist/components/date-picker/snice-date-picker.types.js +2 -0
- package/dist/components/date-picker/snice-date-picker.types.js.map +1 -0
- package/dist/components/divider/snice-divider.d.ts +17 -0
- package/dist/components/divider/snice-divider.js +111 -0
- package/dist/components/divider/snice-divider.js.map +1 -0
- package/dist/components/divider/snice-divider.types.d.ts +14 -0
- package/dist/components/divider/snice-divider.types.js +2 -0
- package/dist/components/divider/snice-divider.types.js.map +1 -0
- package/dist/components/drawer/snice-drawer.d.ts +37 -0
- package/dist/components/drawer/snice-drawer.js +335 -0
- package/dist/components/drawer/snice-drawer.js.map +1 -0
- package/dist/components/drawer/snice-drawer.types.d.ts +16 -0
- package/dist/components/drawer/snice-drawer.types.js +2 -0
- package/dist/components/drawer/snice-drawer.types.js.map +1 -0
- package/dist/components/input/snice-input.d.ts +65 -0
- package/dist/components/input/snice-input.js +603 -0
- package/dist/components/input/snice-input.js.map +1 -0
- package/dist/components/input/snice-input.types.d.ts +53 -0
- package/dist/components/input/snice-input.types.js +2 -0
- package/dist/components/input/snice-input.types.js.map +1 -0
- package/dist/components/layout/snice-layout-blog.d.ts +4 -0
- package/dist/components/layout/snice-layout-blog.js +56 -0
- package/dist/components/layout/snice-layout-blog.js.map +1 -0
- package/dist/components/layout/snice-layout-card.d.ts +6 -0
- package/dist/components/layout/snice-layout-card.js +53 -0
- package/dist/components/layout/snice-layout-card.js.map +1 -0
- package/dist/components/layout/snice-layout-centered.d.ts +5 -0
- package/dist/components/layout/snice-layout-centered.js +38 -0
- package/dist/components/layout/snice-layout-centered.js.map +1 -0
- package/dist/components/layout/snice-layout-dashboard.d.ts +4 -0
- package/dist/components/layout/snice-layout-dashboard.js +53 -0
- package/dist/components/layout/snice-layout-dashboard.js.map +1 -0
- package/dist/components/layout/snice-layout-fullscreen.d.ts +5 -0
- package/dist/components/layout/snice-layout-fullscreen.js +50 -0
- package/dist/components/layout/snice-layout-fullscreen.js.map +1 -0
- package/dist/components/layout/snice-layout-landing.d.ts +4 -0
- package/dist/components/layout/snice-layout-landing.js +55 -0
- package/dist/components/layout/snice-layout-landing.js.map +1 -0
- package/dist/components/layout/snice-layout-minimal.d.ts +4 -0
- package/dist/components/layout/snice-layout-minimal.js +27 -0
- package/dist/components/layout/snice-layout-minimal.js.map +1 -0
- package/dist/components/layout/snice-layout-sidebar.d.ts +5 -0
- package/dist/components/layout/snice-layout-sidebar.js +64 -0
- package/dist/components/layout/snice-layout-sidebar.js.map +1 -0
- package/dist/components/layout/snice-layout-split.d.ts +6 -0
- package/dist/components/layout/snice-layout-split.js +47 -0
- package/dist/components/layout/snice-layout-split.js.map +1 -0
- package/dist/components/layout/snice-layout.d.ts +4 -0
- package/dist/components/layout/snice-layout.js +43 -0
- package/dist/components/layout/snice-layout.js.map +1 -0
- package/dist/components/layout/snice-layout.types.d.ts +3 -0
- package/dist/components/layout/snice-layout.types.js +2 -0
- package/dist/components/layout/snice-layout.types.js.map +1 -0
- package/dist/components/login/snice-login.d.ts +45 -0
- package/dist/components/login/snice-login.js +385 -0
- package/dist/components/login/snice-login.js.map +1 -0
- package/dist/components/login/snice-login.types.d.ts +31 -0
- package/dist/components/login/snice-login.types.js +2 -0
- package/dist/components/login/snice-login.types.js.map +1 -0
- package/dist/components/modal/snice-modal.d.ts +32 -0
- package/dist/components/modal/snice-modal.js +288 -0
- package/dist/components/modal/snice-modal.js.map +1 -0
- package/dist/components/modal/snice-modal.types.d.ts +18 -0
- package/dist/components/modal/snice-modal.types.js +2 -0
- package/dist/components/modal/snice-modal.types.js.map +1 -0
- package/dist/components/pagination/snice-pagination.d.ts +26 -0
- package/dist/components/pagination/snice-pagination.js +373 -0
- package/dist/components/pagination/snice-pagination.js.map +1 -0
- package/dist/components/pagination/snice-pagination.types.d.ts +18 -0
- package/dist/components/pagination/snice-pagination.types.js +2 -0
- package/dist/components/pagination/snice-pagination.types.js.map +1 -0
- package/dist/components/progress/snice-progress.d.ts +35 -0
- package/dist/components/progress/snice-progress.js +295 -0
- package/dist/components/progress/snice-progress.js.map +1 -0
- package/dist/components/progress/snice-progress.types.d.ts +18 -0
- package/dist/components/progress/snice-progress.types.js +2 -0
- package/dist/components/progress/snice-progress.types.js.map +1 -0
- package/dist/components/radio/snice-radio.d.ts +33 -0
- package/dist/components/radio/snice-radio.js +286 -0
- package/dist/components/radio/snice-radio.js.map +1 -0
- package/dist/components/radio/snice-radio.types.d.ts +19 -0
- package/dist/components/radio/snice-radio.types.js +2 -0
- package/dist/components/radio/snice-radio.types.js.map +1 -0
- package/dist/components/select/snice-option.d.ts +17 -0
- package/dist/components/select/snice-option.js +77 -0
- package/dist/components/select/snice-option.js.map +1 -0
- package/dist/components/select/snice-option.types.d.ts +14 -0
- package/dist/components/select/snice-option.types.js +2 -0
- package/dist/components/select/snice-option.types.js.map +1 -0
- package/dist/components/select/snice-select.d.ts +89 -0
- package/dist/components/select/snice-select.js +900 -0
- package/dist/components/select/snice-select.js.map +1 -0
- package/dist/components/select/snice-select.types.d.ts +49 -0
- package/dist/components/select/snice-select.types.js +2 -0
- package/dist/components/select/snice-select.types.js.map +1 -0
- package/dist/components/skeleton/snice-skeleton.d.ts +16 -0
- package/dist/components/skeleton/snice-skeleton.js +159 -0
- package/dist/components/skeleton/snice-skeleton.js.map +1 -0
- package/dist/components/skeleton/snice-skeleton.types.d.ts +10 -0
- package/dist/components/skeleton/snice-skeleton.types.js +2 -0
- package/dist/components/skeleton/snice-skeleton.types.js.map +1 -0
- package/dist/components/switch/snice-switch.d.ts +38 -0
- package/dist/components/switch/snice-switch.js +309 -0
- package/dist/components/switch/snice-switch.js.map +1 -0
- package/dist/components/switch/snice-switch.types.d.ts +21 -0
- package/dist/components/switch/snice-switch.types.js +2 -0
- package/dist/components/switch/snice-switch.types.js.map +1 -0
- package/dist/components/symbols.d.ts +1 -0
- package/dist/components/symbols.js +20 -0
- package/dist/components/symbols.js.map +1 -0
- package/dist/components/table/snice-cell-boolean.d.ts +21 -0
- package/dist/components/table/snice-cell-boolean.js +152 -0
- package/dist/components/table/snice-cell-boolean.js.map +1 -0
- package/dist/components/table/snice-cell-date.d.ts +24 -0
- package/dist/components/table/snice-cell-date.js +240 -0
- package/dist/components/table/snice-cell-date.js.map +1 -0
- package/dist/components/table/snice-cell-duration.d.ts +16 -0
- package/dist/components/table/snice-cell-duration.js +123 -0
- package/dist/components/table/snice-cell-duration.js.map +1 -0
- package/dist/components/table/snice-cell-filesize.d.ts +16 -0
- package/dist/components/table/snice-cell-filesize.js +119 -0
- package/dist/components/table/snice-cell-filesize.js.map +1 -0
- package/dist/components/table/snice-cell-number.d.ts +23 -0
- package/dist/components/table/snice-cell-number.js +202 -0
- package/dist/components/table/snice-cell-number.js.map +1 -0
- package/dist/components/table/snice-cell-progress.d.ts +17 -0
- package/dist/components/table/snice-cell-progress.js +114 -0
- package/dist/components/table/snice-cell-progress.js.map +1 -0
- package/dist/components/table/snice-cell-rating.d.ts +17 -0
- package/dist/components/table/snice-cell-rating.js +113 -0
- package/dist/components/table/snice-cell-rating.js.map +1 -0
- package/dist/components/table/snice-cell-sparkline.d.ts +29 -0
- package/dist/components/table/snice-cell-sparkline.js +290 -0
- package/dist/components/table/snice-cell-sparkline.js.map +1 -0
- package/dist/components/table/snice-cell-text.d.ts +19 -0
- package/dist/components/table/snice-cell-text.js +153 -0
- package/dist/components/table/snice-cell-text.js.map +1 -0
- package/dist/components/table/snice-cell.d.ts +32 -0
- package/dist/components/table/snice-cell.js +451 -0
- package/dist/components/table/snice-cell.js.map +1 -0
- package/dist/components/table/snice-column.d.ts +62 -0
- package/dist/components/table/snice-column.js +440 -0
- package/dist/components/table/snice-column.js.map +1 -0
- package/dist/components/table/snice-header.d.ts +33 -0
- package/dist/components/table/snice-header.js +303 -0
- package/dist/components/table/snice-header.js.map +1 -0
- package/dist/components/table/snice-progress.d.ts +10 -0
- package/dist/components/table/snice-progress.js +91 -0
- package/dist/components/table/snice-progress.js.map +1 -0
- package/dist/components/table/snice-rating.d.ts +9 -0
- package/dist/components/table/snice-rating.js +68 -0
- package/dist/components/table/snice-rating.js.map +1 -0
- package/dist/components/table/snice-row.d.ts +43 -0
- package/dist/components/table/snice-row.js +365 -0
- package/dist/components/table/snice-row.js.map +1 -0
- package/dist/components/table/snice-table.d.ts +69 -0
- package/dist/components/table/snice-table.js +814 -0
- package/dist/components/table/snice-table.js.map +1 -0
- package/dist/components/table/snice-table.types.d.ts +137 -0
- package/dist/components/table/snice-table.types.js +2 -0
- package/dist/components/table/snice-table.types.js.map +1 -0
- package/dist/components/tabs/snice-tab-panel.d.ts +12 -0
- package/dist/components/tabs/snice-tab-panel.js +78 -0
- package/dist/components/tabs/snice-tab-panel.js.map +1 -0
- package/dist/components/tabs/snice-tab.d.ts +13 -0
- package/dist/components/tabs/snice-tab.js +90 -0
- package/dist/components/tabs/snice-tab.js.map +1 -0
- package/dist/components/tabs/snice-tabs.d.ts +34 -0
- package/dist/components/tabs/snice-tabs.js +367 -0
- package/dist/components/tabs/snice-tabs.js.map +1 -0
- package/dist/components/tabs/snice-tabs.types.d.ts +23 -0
- package/dist/components/tabs/snice-tabs.types.js +2 -0
- package/dist/components/tabs/snice-tabs.types.js.map +1 -0
- package/dist/components/toast/snice-toast-container.d.ts +25 -0
- package/dist/components/toast/snice-toast-container.js +251 -0
- package/dist/components/toast/snice-toast-container.js.map +1 -0
- package/dist/components/toast/snice-toast.d.ts +23 -0
- package/dist/components/toast/snice-toast.js +316 -0
- package/dist/components/toast/snice-toast.js.map +1 -0
- package/dist/components/toast/snice-toast.types.d.ts +30 -0
- package/dist/components/toast/snice-toast.types.js +2 -0
- package/dist/components/toast/snice-toast.types.js.map +1 -0
- package/dist/components/tooltip/snice-tooltip.d.ts +50 -0
- package/dist/components/tooltip/snice-tooltip.js +656 -0
- package/dist/components/tooltip/snice-tooltip.js.map +1 -0
- package/dist/components/tooltip/snice-tooltip.types.d.ts +18 -0
- package/dist/components/tooltip/snice-tooltip.types.js +2 -0
- package/dist/components/tooltip/snice-tooltip.types.js.map +1 -0
- package/dist/components/transitions.d.ts +11 -0
- package/dist/components/transitions.js +69 -0
- package/dist/components/transitions.js.map +1 -0
- package/dist/src/controller.d.ts +61 -0
- package/dist/src/controller.js +297 -0
- package/dist/src/controller.js.map +1 -0
- package/dist/src/element.d.ts +77 -0
- package/dist/src/element.js +805 -0
- package/dist/src/element.js.map +1 -0
- package/dist/src/events.d.ts +37 -0
- package/dist/src/events.js +289 -0
- package/dist/src/events.js.map +1 -0
- package/dist/src/global.d.ts +7 -0
- package/dist/src/global.js +23 -0
- package/dist/src/global.js.map +1 -0
- package/{src/index.ts → dist/src/index.d.ts} +1 -1
- package/dist/src/index.js +8 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/observe.d.ts +26 -0
- package/dist/src/observe.js +329 -0
- package/dist/src/observe.js.map +1 -0
- package/dist/src/request-response.d.ts +46 -0
- package/dist/src/request-response.js +267 -0
- package/dist/src/request-response.js.map +1 -0
- package/dist/src/router.d.ts +87 -0
- package/dist/src/router.js +375 -0
- package/dist/src/router.js.map +1 -0
- package/dist/src/symbols.d.ts +29 -0
- package/{src/symbols.ts → dist/src/symbols.js} +2 -12
- package/dist/src/symbols.js.map +1 -0
- package/dist/src/transitions.d.ts +50 -0
- package/dist/src/transitions.js +199 -0
- package/dist/src/transitions.js.map +1 -0
- package/package.json +6 -8
- package/src/controller.ts +0 -347
- package/src/element.ts +0 -897
- package/src/events.ts +0 -349
- package/src/global.ts +0 -31
- package/src/observe.ts +0 -414
- package/src/request-response.ts +0 -336
- package/src/router.ts +0 -552
- package/src/transitions.ts +0 -264
package/src/element.ts
DELETED
|
@@ -1,897 +0,0 @@
|
|
|
1
|
-
import { attachController, detachController } from './controller';
|
|
2
|
-
import { setupEventHandlers, cleanupEventHandlers } from './events';
|
|
3
|
-
import { setupObservers, cleanupObservers } from './observe';
|
|
4
|
-
import { setupResponseHandlers, cleanupResponseHandlers } from './request-response';
|
|
5
|
-
import { IS_ELEMENT_CLASS, IS_CONTROLLER_INSTANCE, READY_PROMISE, READY_RESOLVE, CONTROLLER, PROPERTIES, PROPERTY_VALUES, PROPERTIES_INITIALIZED, PROPERTY_WATCHERS, EXPLICITLY_SET_PROPERTIES, ROUTER_CONTEXT, READY_HANDLERS, DISPOSE_HANDLERS, PARTS, PART_TIMERS } from './symbols';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Applies core element functionality to a constructor
|
|
9
|
-
* This is shared between @element and @page decorators
|
|
10
|
-
*/
|
|
11
|
-
export function applyElementFunctionality(constructor: any) {
|
|
12
|
-
// Mark as element class for channel decorator detection
|
|
13
|
-
(constructor.prototype as any)[IS_ELEMENT_CLASS] = true;
|
|
14
|
-
|
|
15
|
-
// Add controller property to all elements
|
|
16
|
-
const originalConnectedCallback = constructor.prototype.connectedCallback;
|
|
17
|
-
const originalDisconnectedCallback = constructor.prototype.disconnectedCallback;
|
|
18
|
-
const originalAttributeChangedCallback = constructor.prototype.attributeChangedCallback;
|
|
19
|
-
|
|
20
|
-
// Add 'controller' and all reflected properties to observed attributes
|
|
21
|
-
const observedAttributes = constructor.observedAttributes || [];
|
|
22
|
-
if (!observedAttributes.includes('controller')) {
|
|
23
|
-
observedAttributes.push('controller');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Add all properties to observed attributes (not just reflected ones)
|
|
27
|
-
const properties = constructor[PROPERTIES];
|
|
28
|
-
if (properties) {
|
|
29
|
-
for (const [propName, propOptions] of properties) {
|
|
30
|
-
const attributeName = typeof propOptions.attribute === 'string' ? propOptions.attribute : propName.toLowerCase();
|
|
31
|
-
if (!observedAttributes.includes(attributeName)) {
|
|
32
|
-
observedAttributes.push(attributeName);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
Object.defineProperty(constructor, 'observedAttributes', {
|
|
38
|
-
get() { return observedAttributes; },
|
|
39
|
-
configurable: true
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Add ready property - always returns a promise
|
|
43
|
-
Object.defineProperty(constructor.prototype, 'ready', {
|
|
44
|
-
get() {
|
|
45
|
-
if (!this[READY_PROMISE]) {
|
|
46
|
-
// Create a pending promise if not yet initialized
|
|
47
|
-
this[READY_PROMISE] = new Promise<void>((resolve) => {
|
|
48
|
-
this[READY_RESOLVE] = resolve;
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
return this[READY_PROMISE];
|
|
52
|
-
},
|
|
53
|
-
enumerable: true,
|
|
54
|
-
configurable: true
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// Add controller property
|
|
58
|
-
Object.defineProperty(constructor.prototype, 'controller', {
|
|
59
|
-
get() {
|
|
60
|
-
return this[CONTROLLER];
|
|
61
|
-
},
|
|
62
|
-
set(value: string) {
|
|
63
|
-
const oldValue = this[CONTROLLER];
|
|
64
|
-
this[CONTROLLER] = value;
|
|
65
|
-
if (value !== oldValue && value) {
|
|
66
|
-
// Attach controller asynchronously
|
|
67
|
-
attachController(this, value).catch(error => {
|
|
68
|
-
console.error(`Failed to attach controller "${value}":`, error);
|
|
69
|
-
});
|
|
70
|
-
} else if (!value && oldValue) {
|
|
71
|
-
// Detach controller asynchronously
|
|
72
|
-
detachController(this).catch(error => {
|
|
73
|
-
console.error(`Failed to detach controller:`, error);
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
enumerable: true,
|
|
78
|
-
configurable: true
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
constructor.prototype.connectedCallback = async function() {
|
|
83
|
-
// If ready promise was already created (controller attached before connected), use existing resolve
|
|
84
|
-
// Otherwise create the ready promise now
|
|
85
|
-
if (!this[READY_PROMISE]) {
|
|
86
|
-
this[READY_PROMISE] = new Promise<void>((resolve) => {
|
|
87
|
-
this[READY_RESOLVE] = resolve;
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
// Initialize properties from attributes before rendering
|
|
93
|
-
const properties = constructor[PROPERTIES];
|
|
94
|
-
if (properties) {
|
|
95
|
-
for (const [propName, propOptions] of properties) {
|
|
96
|
-
// If attribute exists, it always wins
|
|
97
|
-
if (this.hasAttribute(propName)) {
|
|
98
|
-
// Attribute exists, parse and set the property value
|
|
99
|
-
const attrValue = this.getAttribute(propName);
|
|
100
|
-
|
|
101
|
-
// Mark as explicitly set since it came from an attribute
|
|
102
|
-
if (!this[EXPLICITLY_SET_PROPERTIES]) {
|
|
103
|
-
this[EXPLICITLY_SET_PROPERTIES] = new Set();
|
|
104
|
-
}
|
|
105
|
-
this[EXPLICITLY_SET_PROPERTIES].add(propName);
|
|
106
|
-
|
|
107
|
-
switch (propOptions.type) {
|
|
108
|
-
case Boolean:
|
|
109
|
-
this[propName] = attrValue !== null && attrValue !== 'false';
|
|
110
|
-
break;
|
|
111
|
-
case Number:
|
|
112
|
-
this[propName] = Number(attrValue);
|
|
113
|
-
break;
|
|
114
|
-
case String:
|
|
115
|
-
this[propName] = attrValue;
|
|
116
|
-
break;
|
|
117
|
-
case Date:
|
|
118
|
-
this[propName] = attrValue ? new Date(attrValue) : null;
|
|
119
|
-
break;
|
|
120
|
-
case BigInt:
|
|
121
|
-
if (attrValue && attrValue.endsWith('n')) {
|
|
122
|
-
this[propName] = BigInt(attrValue.slice(0, -1));
|
|
123
|
-
} else {
|
|
124
|
-
this[propName] = attrValue ? BigInt(attrValue) : null;
|
|
125
|
-
}
|
|
126
|
-
break;
|
|
127
|
-
case SimpleArray:
|
|
128
|
-
this[propName] = SimpleArray.parse(attrValue);
|
|
129
|
-
break;
|
|
130
|
-
default:
|
|
131
|
-
this[propName] = attrValue;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Mark that properties have been initialized
|
|
138
|
-
this[PROPERTIES_INITIALIZED] = true;
|
|
139
|
-
|
|
140
|
-
// Reflect properties that were explicitly set before connection
|
|
141
|
-
// but skip default values that were never explicitly set
|
|
142
|
-
if (properties && this[EXPLICITLY_SET_PROPERTIES]) {
|
|
143
|
-
for (const [propName, propOptions] of properties) {
|
|
144
|
-
if (propOptions.reflect && this[EXPLICITLY_SET_PROPERTIES].has(propName) && propName in this[PROPERTY_VALUES]) {
|
|
145
|
-
const value = this[PROPERTY_VALUES][propName];
|
|
146
|
-
const attributeName = typeof propOptions.attribute === 'string' ? propOptions.attribute : propName.toLowerCase();
|
|
147
|
-
|
|
148
|
-
if (value !== null && value !== undefined && value !== false &&
|
|
149
|
-
!(propOptions.type === SimpleArray && Array.isArray(value) && value.length === 0)) {
|
|
150
|
-
// Handle special types for reflection
|
|
151
|
-
let attributeValue: string;
|
|
152
|
-
if (value instanceof Date) {
|
|
153
|
-
attributeValue = value.toISOString();
|
|
154
|
-
} else if (typeof value === 'bigint') {
|
|
155
|
-
attributeValue = value.toString() + 'n';
|
|
156
|
-
} else if (propOptions.type === SimpleArray && Array.isArray(value)) {
|
|
157
|
-
attributeValue = SimpleArray.serialize(value);
|
|
158
|
-
} else {
|
|
159
|
-
attributeValue = String(value);
|
|
160
|
-
}
|
|
161
|
-
this.setAttribute(attributeName, attributeValue);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Clean up any existing event handlers first (for reconnection)
|
|
168
|
-
cleanupEventHandlers(this);
|
|
169
|
-
|
|
170
|
-
// Create shadow root if it doesn't exist
|
|
171
|
-
if (!this.shadowRoot) {
|
|
172
|
-
this.attachShadow({ mode: 'open' });
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Build the shadow DOM content
|
|
176
|
-
let shadowContent = '';
|
|
177
|
-
|
|
178
|
-
// Add HTML first (maintaining original order)
|
|
179
|
-
if (this.html) {
|
|
180
|
-
try {
|
|
181
|
-
const htmlResult = this.html();
|
|
182
|
-
// Handle both async and sync html
|
|
183
|
-
const htmlContent = htmlResult instanceof Promise ? await htmlResult : htmlResult;
|
|
184
|
-
if (htmlContent !== undefined) {
|
|
185
|
-
shadowContent += htmlContent;
|
|
186
|
-
}
|
|
187
|
-
} catch (error) {
|
|
188
|
-
console.error(`Error in html() method for ${this.tagName}:`, error);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Add CSS after HTML (maintaining original order)
|
|
193
|
-
if (this.css) {
|
|
194
|
-
try {
|
|
195
|
-
const cssResult = this.css();
|
|
196
|
-
// Handle both async and sync css
|
|
197
|
-
const cssResolved = cssResult instanceof Promise ? await cssResult : cssResult;
|
|
198
|
-
if (cssResolved) {
|
|
199
|
-
// Handle both string and array of strings
|
|
200
|
-
const cssContent = Array.isArray(cssResolved) ? cssResolved.join('\n') : cssResolved;
|
|
201
|
-
// No need for scoping with Shadow DOM, but add data attribute for compatibility
|
|
202
|
-
shadowContent += `<style data-component-css>${cssContent}</style>`;
|
|
203
|
-
}
|
|
204
|
-
} catch (error) {
|
|
205
|
-
console.error(`Error in css() method for ${this.tagName}:`, error);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Set shadow DOM content
|
|
210
|
-
if (shadowContent) {
|
|
211
|
-
this.shadowRoot.innerHTML = shadowContent;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Render all @part methods into their corresponding elements
|
|
215
|
-
const parts = constructor[PARTS];
|
|
216
|
-
if (parts && this.shadowRoot) {
|
|
217
|
-
for (const [partName, partHandler] of parts) {
|
|
218
|
-
try {
|
|
219
|
-
const partElement = this.shadowRoot.querySelector(`[part="${partName}"]`);
|
|
220
|
-
if (partElement) {
|
|
221
|
-
const partResult = partHandler.method.call(this);
|
|
222
|
-
const partContent = partResult instanceof Promise ? await partResult : partResult;
|
|
223
|
-
if (partContent !== undefined) {
|
|
224
|
-
partElement.innerHTML = partContent;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
} catch (error) {
|
|
228
|
-
console.error(`Error rendering @part('${partName}') in ${this.tagName}:`, error);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// NOW call the original user-defined connectedCallback after shadow DOM is set up
|
|
234
|
-
if (originalConnectedCallback) {
|
|
235
|
-
originalConnectedCallback.call(this);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const controllerName = this.getAttribute('controller');
|
|
239
|
-
if (controllerName) {
|
|
240
|
-
this.controller = controllerName;
|
|
241
|
-
}
|
|
242
|
-
// Setup @on event handlers - use element for host events, shadow root for delegated events
|
|
243
|
-
setupEventHandlers(this, this);
|
|
244
|
-
|
|
245
|
-
// Setup @respond handlers for elements
|
|
246
|
-
setupResponseHandlers(this, this);
|
|
247
|
-
|
|
248
|
-
// Setup @observe observers
|
|
249
|
-
try {
|
|
250
|
-
setupObservers(this, this);
|
|
251
|
-
} catch (error) {
|
|
252
|
-
console.error(`Error setting up observers for ${this.tagName}:`, error);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Call @ready handlers after everything is set up
|
|
256
|
-
const readyHandlers = constructor[READY_HANDLERS];
|
|
257
|
-
if (readyHandlers) {
|
|
258
|
-
for (const handler of readyHandlers) {
|
|
259
|
-
try {
|
|
260
|
-
await handler.method.call(this);
|
|
261
|
-
} catch (error) {
|
|
262
|
-
console.error(`Error in @ready handler ${handler.methodName}:`, error);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
} finally {
|
|
267
|
-
// Always mark element as ready, even if there were errors
|
|
268
|
-
if (this[READY_RESOLVE]) {
|
|
269
|
-
this[READY_RESOLVE]();
|
|
270
|
-
this[READY_RESOLVE] = null; // Clear the resolver
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
constructor.prototype.disconnectedCallback = async function() {
|
|
276
|
-
// Call @dispose handlers
|
|
277
|
-
const disposeHandlers = constructor[DISPOSE_HANDLERS];
|
|
278
|
-
if (disposeHandlers) {
|
|
279
|
-
for (const handler of disposeHandlers) {
|
|
280
|
-
try {
|
|
281
|
-
await handler.method.call(this);
|
|
282
|
-
} catch (error) {
|
|
283
|
-
console.error(`Error in @dispose handler ${handler.methodName}:`, error);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Call original user-defined disconnectedCallback
|
|
289
|
-
if (originalDisconnectedCallback) {
|
|
290
|
-
originalDisconnectedCallback.call(this);
|
|
291
|
-
}
|
|
292
|
-
if (this[CONTROLLER]) {
|
|
293
|
-
detachController(this).catch(error => {
|
|
294
|
-
console.error(`Failed to detach controller:`, error);
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
// Cleanup @on event handlers
|
|
298
|
-
cleanupEventHandlers(this);
|
|
299
|
-
// Cleanup @respond handlers
|
|
300
|
-
cleanupResponseHandlers(this);
|
|
301
|
-
// Cleanup @observe observers
|
|
302
|
-
cleanupObservers(this);
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
constructor.prototype.attributeChangedCallback = function(name: string, oldValue: string, newValue: string) {
|
|
306
|
-
originalAttributeChangedCallback?.call(this, name, oldValue, newValue);
|
|
307
|
-
if (name === 'controller') {
|
|
308
|
-
this.controller = newValue;
|
|
309
|
-
} else {
|
|
310
|
-
// Handle all properties (not just reflected ones)
|
|
311
|
-
const properties = constructor[PROPERTIES];
|
|
312
|
-
if (properties) {
|
|
313
|
-
for (const [propName, propOptions] of properties) {
|
|
314
|
-
const attributeName = typeof propOptions.attribute === 'string' ? propOptions.attribute : propName.toLowerCase();
|
|
315
|
-
if (attributeName === name) {
|
|
316
|
-
// Check if the current property value already matches to avoid feedback loops
|
|
317
|
-
const currentValue = this[PROPERTY_VALUES]?.[propName];
|
|
318
|
-
|
|
319
|
-
// Parse the new value based on type
|
|
320
|
-
let parsedValue: any;
|
|
321
|
-
if (propOptions.type === Boolean) {
|
|
322
|
-
parsedValue = newValue !== null && newValue !== 'false';
|
|
323
|
-
} else if (propOptions.type === Number) {
|
|
324
|
-
parsedValue = Number(newValue);
|
|
325
|
-
} else if (propOptions.type === Date) {
|
|
326
|
-
parsedValue = newValue ? new Date(newValue) : null;
|
|
327
|
-
} else if (propOptions.type === BigInt) {
|
|
328
|
-
if (newValue && newValue.endsWith('n')) {
|
|
329
|
-
parsedValue = BigInt(newValue.slice(0, -1));
|
|
330
|
-
} else {
|
|
331
|
-
parsedValue = newValue ? BigInt(newValue) : null;
|
|
332
|
-
}
|
|
333
|
-
} else if (propOptions.type === SimpleArray) {
|
|
334
|
-
parsedValue = SimpleArray.parse(newValue);
|
|
335
|
-
} else {
|
|
336
|
-
// If no type specified, try to infer from current value type
|
|
337
|
-
if (typeof currentValue === 'number' && newValue !== null) {
|
|
338
|
-
parsedValue = Number(newValue);
|
|
339
|
-
} else {
|
|
340
|
-
parsedValue = newValue;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Only update if the value actually changed and avoid infinite loops
|
|
345
|
-
if (currentValue !== parsedValue) {
|
|
346
|
-
// Mark as explicitly set since it came from an attribute change
|
|
347
|
-
if (!this[EXPLICITLY_SET_PROPERTIES]) {
|
|
348
|
-
this[EXPLICITLY_SET_PROPERTIES] = new Set();
|
|
349
|
-
}
|
|
350
|
-
this[EXPLICITLY_SET_PROPERTIES].add(propName);
|
|
351
|
-
|
|
352
|
-
// Set the property value directly in the storage to avoid triggering setter
|
|
353
|
-
if (!this[PROPERTY_VALUES]) {
|
|
354
|
-
this[PROPERTY_VALUES] = {};
|
|
355
|
-
}
|
|
356
|
-
this[PROPERTY_VALUES][propName] = parsedValue;
|
|
357
|
-
|
|
358
|
-
// Call watchers manually since we bypassed the setter
|
|
359
|
-
const watchers = constructor[PROPERTY_WATCHERS];
|
|
360
|
-
if (watchers) {
|
|
361
|
-
// Call specific property watchers
|
|
362
|
-
if (watchers.has(propName)) {
|
|
363
|
-
const propertyWatchers = watchers.get(propName);
|
|
364
|
-
for (const watcher of propertyWatchers) {
|
|
365
|
-
try {
|
|
366
|
-
watcher.method.call(this, currentValue, parsedValue, propName);
|
|
367
|
-
} catch (error) {
|
|
368
|
-
console.error(`Error in @watch('${propName}') method ${watcher.methodName}:`, error);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Call wildcard watchers (watching "*")
|
|
374
|
-
if (watchers.has('*')) {
|
|
375
|
-
const wildcardWatchers = watchers.get('*');
|
|
376
|
-
for (const watcher of wildcardWatchers) {
|
|
377
|
-
try {
|
|
378
|
-
watcher.method.call(this, currentValue, parsedValue, propName);
|
|
379
|
-
} catch (error) {
|
|
380
|
-
console.error(`Error in @watch('*') method ${watcher.methodName}:`, error);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
break;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
export function element(tagName: string) {
|
|
395
|
-
return function (constructor: any) {
|
|
396
|
-
applyElementFunctionality(constructor);
|
|
397
|
-
customElements.define(tagName, constructor);
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
export function layout(tagName: string) {
|
|
402
|
-
return function (constructor: any) {
|
|
403
|
-
applyElementFunctionality(constructor);
|
|
404
|
-
customElements.define(tagName, constructor);
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
export function property(options?: PropertyOptions) {
|
|
409
|
-
return function (target: any, propertyKey: string) {
|
|
410
|
-
const constructor = target.constructor;
|
|
411
|
-
|
|
412
|
-
// Warn about problematic reflection usage
|
|
413
|
-
if (options?.reflect && options?.type === Array) {
|
|
414
|
-
console.warn(`⚠️ Property '${propertyKey}' uses reflect:true with Array type.`);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
if (options?.reflect && options?.type === Object) {
|
|
418
|
-
console.warn(`⚠️ Property '${propertyKey}' uses reflect:true with Object type.`);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (!constructor[PROPERTIES]) {
|
|
422
|
-
constructor[PROPERTIES] = new Map();
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
constructor[PROPERTIES].set(propertyKey, options || {});
|
|
426
|
-
|
|
427
|
-
const descriptor: PropertyDescriptor = {
|
|
428
|
-
get(this: any) {
|
|
429
|
-
if (!this[PROPERTY_VALUES]) {
|
|
430
|
-
this[PROPERTY_VALUES] = {};
|
|
431
|
-
}
|
|
432
|
-
return this[PROPERTY_VALUES][propertyKey];
|
|
433
|
-
},
|
|
434
|
-
set(this: any, value: any) {
|
|
435
|
-
if (!this[PROPERTY_VALUES]) {
|
|
436
|
-
this[PROPERTY_VALUES] = {};
|
|
437
|
-
}
|
|
438
|
-
if (!this[EXPLICITLY_SET_PROPERTIES]) {
|
|
439
|
-
this[EXPLICITLY_SET_PROPERTIES] = new Set();
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const oldValue = this[PROPERTY_VALUES][propertyKey];
|
|
443
|
-
|
|
444
|
-
// Don't update if value hasn't changed
|
|
445
|
-
if (oldValue === value) return;
|
|
446
|
-
|
|
447
|
-
// Mark as explicitly set in these cases:
|
|
448
|
-
// 1. There was a previous value (normal property update)
|
|
449
|
-
// 2. This is during element construction and we have a non-null/non-undefined value
|
|
450
|
-
// (this handles default values declared in class properties)
|
|
451
|
-
const isInitialDefaultValue = oldValue === undefined && !this[PROPERTIES_INITIALIZED];
|
|
452
|
-
if (oldValue !== undefined || (isInitialDefaultValue && value !== null && value !== undefined)) {
|
|
453
|
-
this[EXPLICITLY_SET_PROPERTIES].add(propertyKey);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
this[PROPERTY_VALUES][propertyKey] = value;
|
|
457
|
-
|
|
458
|
-
// Only reflect to attributes if:
|
|
459
|
-
// 1. Properties have been initialized from attributes
|
|
460
|
-
// 2. The property was explicitly set (not just default value)
|
|
461
|
-
// This prevents default values from creating attributes
|
|
462
|
-
if (options?.reflect && this.setAttribute && this[PROPERTIES_INITIALIZED] && this[EXPLICITLY_SET_PROPERTIES].has(propertyKey)) {
|
|
463
|
-
const attributeName = typeof options.attribute === 'string' ? options.attribute : propertyKey.toLowerCase();
|
|
464
|
-
|
|
465
|
-
if (value === null || value === undefined || value === false ||
|
|
466
|
-
(options?.type === SimpleArray && Array.isArray(value) && value.length === 0)) {
|
|
467
|
-
this.removeAttribute(attributeName);
|
|
468
|
-
} else {
|
|
469
|
-
// Handle special types for reflection
|
|
470
|
-
let attributeValue: string;
|
|
471
|
-
if (value instanceof Date) {
|
|
472
|
-
attributeValue = value.toISOString();
|
|
473
|
-
} else if (typeof value === 'bigint') {
|
|
474
|
-
attributeValue = value.toString() + 'n';
|
|
475
|
-
} else if (options?.type === SimpleArray && Array.isArray(value)) {
|
|
476
|
-
attributeValue = SimpleArray.serialize(value);
|
|
477
|
-
} else {
|
|
478
|
-
attributeValue = String(value);
|
|
479
|
-
}
|
|
480
|
-
this.setAttribute(attributeName, attributeValue);
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// Call watchers for this property
|
|
485
|
-
const watchers = constructor[PROPERTY_WATCHERS];
|
|
486
|
-
if (watchers) {
|
|
487
|
-
// Call specific property watchers
|
|
488
|
-
if (watchers.has(propertyKey)) {
|
|
489
|
-
const propertyWatchers = watchers.get(propertyKey);
|
|
490
|
-
for (const watcher of propertyWatchers) {
|
|
491
|
-
try {
|
|
492
|
-
// Always pass oldValue, newValue, and propertyName
|
|
493
|
-
watcher.method.call(this, oldValue, value, propertyKey);
|
|
494
|
-
} catch (error) {
|
|
495
|
-
console.error(`Error in @watch('${propertyKey}') method ${watcher.methodName}:`, error);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// Call wildcard watchers (watching "*")
|
|
501
|
-
if (watchers.has('*')) {
|
|
502
|
-
const wildcardWatchers = watchers.get('*');
|
|
503
|
-
for (const watcher of wildcardWatchers) {
|
|
504
|
-
try {
|
|
505
|
-
// Same signature for consistency
|
|
506
|
-
watcher.method.call(this, oldValue, value, propertyKey);
|
|
507
|
-
} catch (error) {
|
|
508
|
-
console.error(`Error in @watch('*') method ${watcher.methodName}:`, error);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// Call requestUpdate if available and value changed
|
|
515
|
-
if (this.requestUpdate) {
|
|
516
|
-
this.requestUpdate(propertyKey, oldValue);
|
|
517
|
-
}
|
|
518
|
-
},
|
|
519
|
-
enumerable: true,
|
|
520
|
-
configurable: true,
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
Object.defineProperty(target, propertyKey, descriptor);
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
export interface QueryOptions {
|
|
528
|
-
light?: boolean;
|
|
529
|
-
shadow?: boolean;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
export function query(selector: string, options: QueryOptions = {}) {
|
|
533
|
-
return function (target: any, propertyKey: string) {
|
|
534
|
-
// Default to shadow DOM only
|
|
535
|
-
const { light = false, shadow = true } = options;
|
|
536
|
-
|
|
537
|
-
Object.defineProperty(target, propertyKey, {
|
|
538
|
-
get() {
|
|
539
|
-
// Check if this is a controller using the symbol
|
|
540
|
-
const isController = this[IS_CONTROLLER_INSTANCE] === true;
|
|
541
|
-
const root = isController && this.element ? this.element : this;
|
|
542
|
-
|
|
543
|
-
// Query in specified contexts
|
|
544
|
-
let result = null;
|
|
545
|
-
|
|
546
|
-
if (shadow && root.shadowRoot) {
|
|
547
|
-
result = root.shadowRoot.querySelector(selector);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
if (!result && light) {
|
|
551
|
-
result = root.querySelector(selector);
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
return result || null;
|
|
555
|
-
},
|
|
556
|
-
enumerable: true,
|
|
557
|
-
configurable: true,
|
|
558
|
-
});
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
export function queryAll(selector: string, options: QueryOptions = {}) {
|
|
563
|
-
return function (target: any, propertyKey: string) {
|
|
564
|
-
// Default to shadow DOM only
|
|
565
|
-
const { light = false, shadow = true } = options;
|
|
566
|
-
|
|
567
|
-
Object.defineProperty(target, propertyKey, {
|
|
568
|
-
get() {
|
|
569
|
-
// Check if this is a controller using the symbol
|
|
570
|
-
const isController = this[IS_CONTROLLER_INSTANCE] === true;
|
|
571
|
-
const root = isController && this.element ? this.element : this;
|
|
572
|
-
|
|
573
|
-
// Query in specified contexts and combine results
|
|
574
|
-
const results: Element[] = [];
|
|
575
|
-
|
|
576
|
-
if (shadow && root.shadowRoot) {
|
|
577
|
-
const shadowResults = root.shadowRoot.querySelectorAll(selector);
|
|
578
|
-
results.push(...shadowResults);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
if (light) {
|
|
582
|
-
const lightResults = root.querySelectorAll(selector);
|
|
583
|
-
results.push(...lightResults);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// Return a static NodeList-like object
|
|
587
|
-
return results as any as NodeListOf<Element>;
|
|
588
|
-
},
|
|
589
|
-
enumerable: true,
|
|
590
|
-
configurable: true,
|
|
591
|
-
});
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/**
|
|
596
|
-
* SimpleArray type for arrays that can be safely reflected to attributes
|
|
597
|
-
* Supports arrays of: string, number, boolean
|
|
598
|
-
* Uses full-width comma (,) as separator to avoid conflicts
|
|
599
|
-
* Strings cannot contain the full-width comma character
|
|
600
|
-
*/
|
|
601
|
-
export class SimpleArray {
|
|
602
|
-
static readonly SEPARATOR = ','; // U+FF0C Full-width comma
|
|
603
|
-
|
|
604
|
-
/**
|
|
605
|
-
* Serialize array to string for attribute storage
|
|
606
|
-
*/
|
|
607
|
-
static serialize(arr: (string | number | boolean)[]): string {
|
|
608
|
-
if (!Array.isArray(arr)) return '';
|
|
609
|
-
|
|
610
|
-
return arr.map(item => {
|
|
611
|
-
if (typeof item === 'string') {
|
|
612
|
-
// Validate string doesn't contain our separator
|
|
613
|
-
if (item.includes(SimpleArray.SEPARATOR)) {
|
|
614
|
-
throw new Error(`SimpleArray strings cannot contain the character "${SimpleArray.SEPARATOR}" (U+FF0C)`);
|
|
615
|
-
}
|
|
616
|
-
return item;
|
|
617
|
-
} else if (typeof item === 'number' || typeof item === 'boolean') {
|
|
618
|
-
return String(item);
|
|
619
|
-
} else {
|
|
620
|
-
throw new Error(`SimpleArray only supports string, number, and boolean types. Got: ${typeof item}`);
|
|
621
|
-
}
|
|
622
|
-
}).join(SimpleArray.SEPARATOR);
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
/**
|
|
626
|
-
* Parse string from attribute back to array
|
|
627
|
-
*/
|
|
628
|
-
static parse(str: string | null): (string | number | boolean)[] {
|
|
629
|
-
if (str === null || str === undefined) return [];
|
|
630
|
-
// Empty string should not be parsed as containing an empty string
|
|
631
|
-
// since empty arrays don't get reflected (handled by the reflection logic)
|
|
632
|
-
if (str === '') return [];
|
|
633
|
-
|
|
634
|
-
return str.split(SimpleArray.SEPARATOR).map(item => {
|
|
635
|
-
// Try to parse as number
|
|
636
|
-
if (/^-?\d+\.?\d*$/.test(item)) {
|
|
637
|
-
const num = Number(item);
|
|
638
|
-
if (!isNaN(num)) return num;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
// Parse as boolean
|
|
642
|
-
if (item === 'true') return true;
|
|
643
|
-
if (item === 'false') return false;
|
|
644
|
-
|
|
645
|
-
// Default to string
|
|
646
|
-
return item;
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
export interface PropertyOptions {
|
|
652
|
-
type?: StringConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor | DateConstructor | BigIntConstructor | typeof SimpleArray;
|
|
653
|
-
reflect?: boolean;
|
|
654
|
-
attribute?: string | boolean;
|
|
655
|
-
converter?: PropertyConverter;
|
|
656
|
-
hasChanged?: (value: any, oldValue: any) => boolean;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
export interface PropertyConverter {
|
|
660
|
-
fromAttribute?(value: string | null, type?: any): any;
|
|
661
|
-
toAttribute?(value: any, type?: any): string | null;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
/**
|
|
665
|
-
* Interface for Snice elements with all the framework-provided properties and methods
|
|
666
|
-
*/
|
|
667
|
-
export interface SniceElement extends HTMLElement {
|
|
668
|
-
ready: Promise<void>;
|
|
669
|
-
html?(): string | Promise<string>;
|
|
670
|
-
css?(): string | string[] | Promise<string | string[]>;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
export interface PartOptions {
|
|
674
|
-
throttle?: number; // Throttle in milliseconds - limits calls to once per interval
|
|
675
|
-
debounce?: number; // Debounce in milliseconds - delays execution until after calls stop
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
export function watch(...propertyNames: string[]) {
|
|
679
|
-
return function (target: any, methodName: string, descriptor: PropertyDescriptor) {
|
|
680
|
-
const constructor = target.constructor;
|
|
681
|
-
|
|
682
|
-
if (!constructor[PROPERTY_WATCHERS]) {
|
|
683
|
-
constructor[PROPERTY_WATCHERS] = new Map();
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
// Store the watcher method for each property
|
|
687
|
-
for (const propertyName of propertyNames) {
|
|
688
|
-
if (!constructor[PROPERTY_WATCHERS].has(propertyName)) {
|
|
689
|
-
constructor[PROPERTY_WATCHERS].set(propertyName, []);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
constructor[PROPERTY_WATCHERS].get(propertyName).push({
|
|
693
|
-
methodName,
|
|
694
|
-
method: descriptor.value
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
return descriptor;
|
|
699
|
-
};
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
/**
|
|
703
|
-
* Decorator that injects router context into a property
|
|
704
|
-
* The context is automatically provided to page components by the router
|
|
705
|
-
*/
|
|
706
|
-
export function context() {
|
|
707
|
-
return function(target: any, propertyKey: string) {
|
|
708
|
-
// Define property getter that returns the context
|
|
709
|
-
Object.defineProperty(target, propertyKey, {
|
|
710
|
-
get() {
|
|
711
|
-
// First check if context is stored directly on this element
|
|
712
|
-
if (this[ROUTER_CONTEXT] !== undefined) {
|
|
713
|
-
return this[ROUTER_CONTEXT];
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
// Otherwise, request context from parent page via event
|
|
717
|
-
const detail: any = { target: this };
|
|
718
|
-
const event = new CustomEvent('@context/request', {
|
|
719
|
-
bubbles: true,
|
|
720
|
-
cancelable: true,
|
|
721
|
-
detail
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
// Dispatch event and wait for response
|
|
725
|
-
// Check if this is a controller using the symbol
|
|
726
|
-
const isController = this[IS_CONTROLLER_INSTANCE] === true;
|
|
727
|
-
let targetElement = isController && this.element ? this.element : this;
|
|
728
|
-
|
|
729
|
-
// If element is null (e.g., controller was detached), can't get context
|
|
730
|
-
if (!targetElement || !targetElement.dispatchEvent) {
|
|
731
|
-
return undefined;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
// If we're in shadow DOM, dispatch on the host element to ensure proper bubbling
|
|
735
|
-
if (targetElement.getRootNode && targetElement.getRootNode() instanceof ShadowRoot) {
|
|
736
|
-
const shadowRoot = targetElement.getRootNode() as ShadowRoot;
|
|
737
|
-
targetElement = shadowRoot.host as HTMLElement;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
targetElement.dispatchEvent(event);
|
|
741
|
-
|
|
742
|
-
// Check if context was provided via the event
|
|
743
|
-
if (detail.context !== undefined) {
|
|
744
|
-
// Cache it for future use
|
|
745
|
-
this[ROUTER_CONTEXT] = detail.context;
|
|
746
|
-
return detail.context;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
return undefined;
|
|
750
|
-
},
|
|
751
|
-
enumerable: true,
|
|
752
|
-
configurable: true
|
|
753
|
-
});
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* Decorator for methods that should run when element is ready
|
|
759
|
-
* Runs after shadow DOM, controller attachment, and event setup
|
|
760
|
-
* Supports async methods
|
|
761
|
-
*/
|
|
762
|
-
export function ready() {
|
|
763
|
-
return function (target: any, methodName: string, descriptor: PropertyDescriptor) {
|
|
764
|
-
const constructor = target.constructor;
|
|
765
|
-
|
|
766
|
-
if (!constructor[READY_HANDLERS]) {
|
|
767
|
-
constructor[READY_HANDLERS] = [];
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
constructor[READY_HANDLERS].push({
|
|
771
|
-
methodName,
|
|
772
|
-
method: descriptor.value
|
|
773
|
-
});
|
|
774
|
-
|
|
775
|
-
return descriptor;
|
|
776
|
-
};
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
/**
|
|
780
|
-
* Decorator for methods that should run when element is being disposed
|
|
781
|
-
* Used for cleanup tasks when element is removed from DOM
|
|
782
|
-
*/
|
|
783
|
-
export function dispose() {
|
|
784
|
-
return function (target: any, methodName: string, descriptor: PropertyDescriptor) {
|
|
785
|
-
const constructor = target.constructor;
|
|
786
|
-
|
|
787
|
-
if (!constructor[DISPOSE_HANDLERS]) {
|
|
788
|
-
constructor[DISPOSE_HANDLERS] = [];
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
constructor[DISPOSE_HANDLERS].push({
|
|
792
|
-
methodName,
|
|
793
|
-
method: descriptor.value
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
return descriptor;
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
/**
|
|
801
|
-
* Decorator for methods that render specific parts of the template
|
|
802
|
-
* Parts are identified by the 'part' attribute in the HTML template
|
|
803
|
-
* When the decorated method is called, it automatically re-renders its part
|
|
804
|
-
*/
|
|
805
|
-
export function part(partName: string, options: PartOptions = {}) {
|
|
806
|
-
return function (target: any, methodName: string, descriptor: PropertyDescriptor) {
|
|
807
|
-
const constructor = target.constructor;
|
|
808
|
-
const originalMethod = descriptor.value;
|
|
809
|
-
|
|
810
|
-
if (!constructor[PARTS]) {
|
|
811
|
-
constructor[PARTS] = new Map();
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
constructor[PARTS].set(partName, {
|
|
815
|
-
methodName,
|
|
816
|
-
method: originalMethod
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
// Wrap the original method to automatically re-render the part when called
|
|
820
|
-
descriptor.value = async function (this: HTMLElement, ...args: any[]) {
|
|
821
|
-
// Initialize timers storage if not present
|
|
822
|
-
if (!(this as any)[PART_TIMERS]) {
|
|
823
|
-
(this as any)[PART_TIMERS] = new Map();
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
// Get or create timers for this specific part
|
|
827
|
-
if (!(this as any)[PART_TIMERS].has(partName)) {
|
|
828
|
-
(this as any)[PART_TIMERS].set(partName, {
|
|
829
|
-
throttleTimer: null,
|
|
830
|
-
debounceTimer: null,
|
|
831
|
-
lastThrottleCall: 0
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
const timers = (this as any)[PART_TIMERS].get(partName);
|
|
836
|
-
|
|
837
|
-
// Create the render function
|
|
838
|
-
const renderPart = async () => {
|
|
839
|
-
// Call the original method to get the content
|
|
840
|
-
const result = originalMethod.apply(this, args);
|
|
841
|
-
const content = result instanceof Promise ? await result : result;
|
|
842
|
-
|
|
843
|
-
// Re-render the part if shadow DOM exists and content is defined
|
|
844
|
-
if (this.shadowRoot && content !== undefined) {
|
|
845
|
-
const partElement = this.shadowRoot.querySelector(`[part="${partName}"]`);
|
|
846
|
-
if (partElement) {
|
|
847
|
-
partElement.innerHTML = content;
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
return content;
|
|
852
|
-
};
|
|
853
|
-
|
|
854
|
-
// Handle debounce (only if positive value)
|
|
855
|
-
if (options.debounce !== undefined && options.debounce > 0) {
|
|
856
|
-
if (timers.debounceTimer) {
|
|
857
|
-
clearTimeout(timers.debounceTimer);
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
return new Promise((resolve) => {
|
|
861
|
-
timers.debounceTimer = setTimeout(async () => {
|
|
862
|
-
const result = await renderPart();
|
|
863
|
-
resolve(result);
|
|
864
|
-
}, options.debounce);
|
|
865
|
-
});
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
// Handle throttle (only if positive value)
|
|
869
|
-
if (options.throttle !== undefined && options.throttle > 0) {
|
|
870
|
-
const now = Date.now();
|
|
871
|
-
|
|
872
|
-
if (timers.lastThrottleCall === 0 || now - timers.lastThrottleCall >= options.throttle) {
|
|
873
|
-
timers.lastThrottleCall = now;
|
|
874
|
-
return await renderPart();
|
|
875
|
-
} else {
|
|
876
|
-
// If throttled, schedule the next call if not already scheduled
|
|
877
|
-
if (!timers.throttleTimer) {
|
|
878
|
-
const remainingTime = options.throttle - (now - timers.lastThrottleCall);
|
|
879
|
-
timers.throttleTimer = setTimeout(async () => {
|
|
880
|
-
timers.throttleTimer = null;
|
|
881
|
-
timers.lastThrottleCall = Date.now();
|
|
882
|
-
await renderPart();
|
|
883
|
-
}, remainingTime);
|
|
884
|
-
}
|
|
885
|
-
// For throttled calls, don't execute the original method, just return undefined
|
|
886
|
-
// The actual render will happen in the scheduled timeout
|
|
887
|
-
return undefined;
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
// No throttle/debounce - render immediately
|
|
892
|
-
return await renderPart();
|
|
893
|
-
};
|
|
894
|
-
|
|
895
|
-
return descriptor;
|
|
896
|
-
};
|
|
897
|
-
}
|