@adukiorg/anza 0.2.0
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/CHANGELOG.md +137 -0
- package/README.md +215 -0
- package/bin/anza.js +63 -0
- package/bin/create.js +150 -0
- package/importmap.json +72 -0
- package/package.json +100 -0
- package/src/core/animations/index.js +55 -0
- package/src/core/animations/play.js +111 -0
- package/src/core/animations/registry.js +54 -0
- package/src/core/animations/scroll.js +50 -0
- package/src/core/animations/tokens.js +58 -0
- package/src/core/animations/usage.md +301 -0
- package/src/core/animations/waapi.js +86 -0
- package/src/core/api/cache.js +120 -0
- package/src/core/api/caches/glob.js +24 -0
- package/src/core/api/caches/index.js +118 -0
- package/src/core/api/events/index.js +75 -0
- package/src/core/api/fetch.js +99 -0
- package/src/core/api/index.js +158 -0
- package/src/core/api/pipeline.js +98 -0
- package/src/core/api/plan.md +209 -0
- package/src/core/api/prefixes/index.js +66 -0
- package/src/core/api/retry.js +69 -0
- package/src/core/api/stream.js +127 -0
- package/src/core/api/upload.js +180 -0
- package/src/core/api/usage.md +206 -0
- package/src/core/events/bus.js +38 -0
- package/src/core/events/delegate.js +79 -0
- package/src/core/events/index.js +26 -0
- package/src/core/events/listen.js +50 -0
- package/src/core/events/missing.md +103 -0
- package/src/core/events/once.js +49 -0
- package/src/core/events/plan.md +177 -0
- package/src/core/events/types/index.js +34 -0
- package/src/core/events/usage.md +107 -0
- package/src/core/offline/bridge.js +51 -0
- package/src/core/offline/clock.js +100 -0
- package/src/core/offline/connectivity.js +116 -0
- package/src/core/offline/index.js +41 -0
- package/src/core/offline/missing.md +89 -0
- package/src/core/offline/plan.md +143 -0
- package/src/core/offline/queue.js +168 -0
- package/src/core/offline/state.js +18 -0
- package/src/core/offline/sync.js +106 -0
- package/src/core/offline/usage.md +273 -0
- package/src/core/platform/guard.js +104 -0
- package/src/core/platform/index.js +42 -0
- package/src/core/platform/missing.md +119 -0
- package/src/core/platform/platform.d.ts +88 -0
- package/src/core/platform/polyfills/anchor.js +79 -0
- package/src/core/platform/polyfills/navigation.js +142 -0
- package/src/core/platform/polyfills/popover.js +142 -0
- package/src/core/platform/polyfills/scheduler.js +194 -0
- package/src/core/platform/polyfills/shadow.js +35 -0
- package/src/core/platform/polyfills/urlpattern.js +119 -0
- package/src/core/platform/supports.js +186 -0
- package/src/core/platform/usage.md +287 -0
- package/src/core/router/cache.js +95 -0
- package/src/core/router/container.js +146 -0
- package/src/core/router/handler.js +52 -0
- package/src/core/router/history.js +120 -0
- package/src/core/router/index.js +158 -0
- package/src/core/router/intercept.js +376 -0
- package/src/core/router/match.js +145 -0
- package/src/core/router/missing.md +716 -0
- package/src/core/router/outlet.js +139 -0
- package/src/core/router/plan.md +370 -0
- package/src/core/router/sync/index.js +16 -0
- package/src/core/router/sync/tab.js +115 -0
- package/src/core/router/sync/transport.js +139 -0
- package/src/core/router/transitions.js +59 -0
- package/src/core/router/usage.md +773 -0
- package/src/core/security/crypto.js +159 -0
- package/src/core/security/index.js +49 -0
- package/src/core/security/missing.md +97 -0
- package/src/core/security/permissions.js +64 -0
- package/src/core/security/sanitize.js +100 -0
- package/src/core/security/usage.md +283 -0
- package/src/core/state/derived.js +117 -0
- package/src/core/state/index.js +23 -0
- package/src/core/state/missing.md +165 -0
- package/src/core/state/persist.js +284 -0
- package/src/core/state/store.js +308 -0
- package/src/core/state/sync.js +46 -0
- package/src/core/state/usage.md +440 -0
- package/src/core/storage/cache.js +83 -0
- package/src/core/storage/idb.js +196 -0
- package/src/core/storage/index.js +373 -0
- package/src/core/storage/lru.js +107 -0
- package/src/core/storage/missing.md +165 -0
- package/src/core/storage/opfs.js +190 -0
- package/src/core/storage/plan.md +69 -0
- package/src/core/storage/quota.js +69 -0
- package/src/core/storage/usage.md +226 -0
- package/src/core/ui/base.js +50 -0
- package/src/core/ui/define/container.js +82 -0
- package/src/core/ui/define/define.js +12 -0
- package/src/core/ui/define/element.js +390 -0
- package/src/core/ui/define/index.js +9 -0
- package/src/core/ui/define/orchestrator.js +105 -0
- package/src/core/ui/define/proxy.js +644 -0
- package/src/core/ui/define/state.js +6 -0
- package/src/core/ui/define/utils.js +134 -0
- package/src/core/ui/implementation.md +170 -0
- package/src/core/ui/index.js +41 -0
- package/src/core/ui/observe.js +117 -0
- package/src/core/ui/plan.md +510 -0
- package/src/core/ui/schedule.js +60 -0
- package/src/core/ui/template.js +37 -0
- package/src/core/ui/transitions.js +37 -0
- package/src/core/ui/ui.types.md +890 -0
- package/src/core/ui/usage.md +1124 -0
- package/src/core/ui/watch.md +346 -0
- package/src/core/workers/broadcast.js +138 -0
- package/src/core/workers/dedicated.js +153 -0
- package/src/core/workers/index.js +169 -0
- package/src/core/workers/locks.js +160 -0
- package/src/core/workers/offscreen.js +166 -0
- package/src/core/workers/plan.md +381 -0
- package/src/core/workers/pool.js +267 -0
- package/src/core/workers/shared.js +137 -0
- package/src/core/workers/usage.md +622 -0
- package/src/elements/base.js +12 -0
- package/src/elements/data/card/index.html +9 -0
- package/src/elements/data/card/index.js +19 -0
- package/src/elements/data/card/index.tags.json +1 -0
- package/src/elements/data/card/style.css +46 -0
- package/src/elements/data/chart/index.html +1 -0
- package/src/elements/data/chart/index.js +143 -0
- package/src/elements/data/chart/index.tags.json +1 -0
- package/src/elements/data/chart/style.css +13 -0
- package/src/elements/data/list/index.html +3 -0
- package/src/elements/data/list/index.js +19 -0
- package/src/elements/data/list/index.tags.json +1 -0
- package/src/elements/data/list/style.css +39 -0
- package/src/elements/data/stat/index.html +9 -0
- package/src/elements/data/stat/index.js +19 -0
- package/src/elements/data/stat/index.tags.json +1 -0
- package/src/elements/data/stat/style.css +50 -0
- package/src/elements/data/table/index.html +1 -0
- package/src/elements/data/table/index.js +16 -0
- package/src/elements/data/table/index.tags.json +1 -0
- package/src/elements/data/table/style.css +50 -0
- package/src/elements/feedback/alert/index.html +11 -0
- package/src/elements/feedback/alert/index.js +28 -0
- package/src/elements/feedback/alert/index.tags.json +1 -0
- package/src/elements/feedback/alert/style.css +75 -0
- package/src/elements/feedback/empty/index.html +13 -0
- package/src/elements/feedback/empty/index.js +34 -0
- package/src/elements/feedback/empty/index.tags.json +1 -0
- package/src/elements/feedback/empty/style.css +45 -0
- package/src/elements/feedback/progress/index.html +7 -0
- package/src/elements/feedback/progress/index.js +46 -0
- package/src/elements/feedback/progress/index.tags.json +1 -0
- package/src/elements/feedback/progress/style.css +36 -0
- package/src/elements/feedback/skeleton/index.html +1 -0
- package/src/elements/feedback/skeleton/index.js +78 -0
- package/src/elements/feedback/skeleton/index.tags.json +1 -0
- package/src/elements/feedback/skeleton/style.css +28 -0
- package/src/elements/feedback/toast/index.html +3 -0
- package/src/elements/feedback/toast/index.js +65 -0
- package/src/elements/feedback/toast/index.tags.json +1 -0
- package/src/elements/feedback/toast/style.css +36 -0
- package/src/elements/forms/checkbox/index.html +7 -0
- package/src/elements/forms/checkbox/index.js +104 -0
- package/src/elements/forms/checkbox/index.tags.json +1 -0
- package/src/elements/forms/checkbox/style.css +86 -0
- package/src/elements/forms/field/index.html +13 -0
- package/src/elements/forms/field/index.js +42 -0
- package/src/elements/forms/field/index.tags.json +1 -0
- package/src/elements/forms/field/style.css +42 -0
- package/src/elements/forms/form/index.html +3 -0
- package/src/elements/forms/form/index.js +122 -0
- package/src/elements/forms/form/index.tags.json +1 -0
- package/src/elements/forms/form/style.css +11 -0
- package/src/elements/forms/input/index.html +4 -0
- package/src/elements/forms/input/index.js +103 -0
- package/src/elements/forms/input/index.tags.json +1 -0
- package/src/elements/forms/input/style.css +39 -0
- package/src/elements/forms/radio/index.html +4 -0
- package/src/elements/forms/radio/index.js +109 -0
- package/src/elements/forms/radio/index.tags.json +1 -0
- package/src/elements/forms/radio/style.css +65 -0
- package/src/elements/forms/select/index.html +9 -0
- package/src/elements/forms/select/index.js +114 -0
- package/src/elements/forms/select/index.tags.json +1 -0
- package/src/elements/forms/select/style.css +95 -0
- package/src/elements/forms/textarea/index.html +4 -0
- package/src/elements/forms/textarea/index.js +115 -0
- package/src/elements/forms/textarea/index.tags.json +1 -0
- package/src/elements/forms/textarea/style.css +46 -0
- package/src/elements/forms/toggle/index.html +4 -0
- package/src/elements/forms/toggle/index.js +89 -0
- package/src/elements/forms/toggle/index.tags.json +1 -0
- package/src/elements/forms/toggle/style.css +63 -0
- package/src/elements/forms/upload/index.html +13 -0
- package/src/elements/forms/upload/index.js +120 -0
- package/src/elements/forms/upload/index.tags.json +1 -0
- package/src/elements/forms/upload/style.css +61 -0
- package/src/elements/index.js +71 -0
- package/src/elements/layout/app/index.html +7 -0
- package/src/elements/layout/app/index.js +16 -0
- package/src/elements/layout/app/index.tags.json +1 -0
- package/src/elements/layout/app/style.css +41 -0
- package/src/elements/layout/grid/index.html +3 -0
- package/src/elements/layout/grid/index.js +41 -0
- package/src/elements/layout/grid/index.tags.json +1 -0
- package/src/elements/layout/grid/style.css +12 -0
- package/src/elements/layout/header/index.html +8 -0
- package/src/elements/layout/header/index.js +16 -0
- package/src/elements/layout/header/index.tags.json +1 -0
- package/src/elements/layout/header/style.css +28 -0
- package/src/elements/layout/scroll/index.html +3 -0
- package/src/elements/layout/scroll/index.js +19 -0
- package/src/elements/layout/scroll/index.tags.json +1 -0
- package/src/elements/layout/scroll/style.css +24 -0
- package/src/elements/layout/sidebar/index.html +3 -0
- package/src/elements/layout/sidebar/index.js +24 -0
- package/src/elements/layout/sidebar/index.tags.json +1 -0
- package/src/elements/layout/sidebar/style.css +30 -0
- package/src/elements/layout/split/index.html +3 -0
- package/src/elements/layout/split/index.js +18 -0
- package/src/elements/layout/split/index.tags.json +1 -0
- package/src/elements/layout/split/style.css +28 -0
- package/src/elements/layout/stack/index.html +3 -0
- package/src/elements/layout/stack/index.js +31 -0
- package/src/elements/layout/stack/index.tags.json +1 -0
- package/src/elements/layout/stack/style.css +15 -0
- package/src/elements/layout/surface/index.html +3 -0
- package/src/elements/layout/surface/index.js +19 -0
- package/src/elements/layout/surface/index.tags.json +1 -0
- package/src/elements/layout/surface/style.css +29 -0
- package/src/elements/navigation/breadcrumb/index.html +5 -0
- package/src/elements/navigation/breadcrumb/index.js +16 -0
- package/src/elements/navigation/breadcrumb/index.tags.json +1 -0
- package/src/elements/navigation/breadcrumb/style.css +36 -0
- package/src/elements/navigation/nav/index.html +3 -0
- package/src/elements/navigation/nav/index.js +24 -0
- package/src/elements/navigation/nav/index.tags.json +1 -0
- package/src/elements/navigation/nav/style.css +38 -0
- package/src/elements/navigation/pagination/index.html +3 -0
- package/src/elements/navigation/pagination/index.js +94 -0
- package/src/elements/navigation/pagination/index.tags.json +1 -0
- package/src/elements/navigation/pagination/style.css +39 -0
- package/src/elements/navigation/steps/index.html +6 -0
- package/src/elements/navigation/steps/index.js +64 -0
- package/src/elements/navigation/steps/index.tags.json +1 -0
- package/src/elements/navigation/steps/style.css +78 -0
- package/src/elements/navigation/tabs/index.html +6 -0
- package/src/elements/navigation/tabs/index.js +132 -0
- package/src/elements/navigation/tabs/index.tags.json +1 -0
- package/src/elements/navigation/tabs/style.css +52 -0
- package/src/elements/overlay/dialog/index.html +5 -0
- package/src/elements/overlay/dialog/index.js +57 -0
- package/src/elements/overlay/dialog/index.tags.json +1 -0
- package/src/elements/overlay/dialog/style.css +31 -0
- package/src/elements/overlay/drawer/index.html +3 -0
- package/src/elements/overlay/drawer/index.js +56 -0
- package/src/elements/overlay/drawer/index.tags.json +1 -0
- package/src/elements/overlay/drawer/style.css +48 -0
- package/src/elements/overlay/menu/index.html +3 -0
- package/src/elements/overlay/menu/index.js +107 -0
- package/src/elements/overlay/menu/index.tags.json +1 -0
- package/src/elements/overlay/menu/style.css +43 -0
- package/src/elements/overlay/popover/index.html +3 -0
- package/src/elements/overlay/popover/index.js +44 -0
- package/src/elements/overlay/popover/index.tags.json +1 -0
- package/src/elements/overlay/popover/style.css +21 -0
- package/src/elements/overlay/sheet/index.html +8 -0
- package/src/elements/overlay/sheet/index.js +105 -0
- package/src/elements/overlay/sheet/index.tags.json +1 -0
- package/src/elements/overlay/sheet/style.css +64 -0
- package/src/elements/overlay/tooltip/index.html +6 -0
- package/src/elements/overlay/tooltip/index.js +16 -0
- package/src/elements/overlay/tooltip/index.tags.json +1 -0
- package/src/elements/overlay/tooltip/style.css +41 -0
- package/src/elements/primitives/avatar/index.html +2 -0
- package/src/elements/primitives/avatar/index.js +79 -0
- package/src/elements/primitives/avatar/index.tags.json +1 -0
- package/src/elements/primitives/avatar/style.css +36 -0
- package/src/elements/primitives/badge/index.html +3 -0
- package/src/elements/primitives/badge/index.js +20 -0
- package/src/elements/primitives/badge/index.tags.json +1 -0
- package/src/elements/primitives/badge/style.css +67 -0
- package/src/elements/primitives/button/index.html +3 -0
- package/src/elements/primitives/button/index.js +61 -0
- package/src/elements/primitives/button/index.tags.json +1 -0
- package/src/elements/primitives/button/style.css +66 -0
- package/src/elements/primitives/divider/index.html +1 -0
- package/src/elements/primitives/divider/index.js +43 -0
- package/src/elements/primitives/divider/index.tags.json +1 -0
- package/src/elements/primitives/divider/style.css +39 -0
- package/src/elements/primitives/icon/index.html +3 -0
- package/src/elements/primitives/icon/index.js +66 -0
- package/src/elements/primitives/icon/index.tags.json +1 -0
- package/src/elements/primitives/icon/style.css +20 -0
- package/src/elements/primitives/link/index.html +3 -0
- package/src/elements/primitives/link/index.js +129 -0
- package/src/elements/primitives/link/index.tags.json +1 -0
- package/src/elements/primitives/link/style.css +40 -0
- package/src/elements/primitives/spinner/index.html +1 -0
- package/src/elements/primitives/spinner/index.js +62 -0
- package/src/elements/primitives/spinner/index.tags.json +1 -0
- package/src/elements/primitives/spinner/style.css +20 -0
- package/src/elements/primitives/text/index.html +1 -0
- package/src/elements/primitives/text/index.js +79 -0
- package/src/elements/primitives/text/index.tags.json +1 -0
- package/src/elements/primitives/text/style.css +25 -0
- package/src/index.js +23 -0
- package/src/styles/base.css +66 -0
- package/src/styles/index.css +10 -0
- package/src/styles/layers.css +9 -0
- package/src/styles/reset.css +66 -0
- package/src/sw/activate.js +51 -0
- package/src/sw/expire.js +47 -0
- package/src/sw/index.js +28 -0
- package/src/sw/install.js +35 -0
- package/src/sw/push.js +58 -0
- package/src/sw/queue.js +60 -0
- package/src/sw/routes.js +71 -0
- package/src/sw/strategies.js +247 -0
- package/src/sw/sync.js +80 -0
- package/src/tokens/index.css +26 -0
- package/src/tokens/primitives/colors.css +54 -0
- package/src/tokens/primitives/motion.css +34 -0
- package/src/tokens/primitives/radius.css +16 -0
- package/src/tokens/primitives/shadow.css +34 -0
- package/src/tokens/primitives/spacing.css +27 -0
- package/src/tokens/primitives/typography.css +46 -0
- package/src/tokens/primitives/zindex.css +18 -0
- package/src/tokens/registered/colors.css +133 -0
- package/src/tokens/registered/dimensions.css +31 -0
- package/src/tokens/semantic/components.css +125 -0
- package/src/tokens/semantic/contrast.css +33 -0
- package/src/tokens/semantic/dark.css +61 -0
- package/src/tokens/semantic/light.css +64 -0
- package/types/core/animations/index.d.ts +52 -0
- package/types/core/api/index.d.ts +68 -0
- package/types/core/events/index.d.ts +50 -0
- package/types/core/offline/index.d.ts +68 -0
- package/types/core/platform/index.d.ts +60 -0
- package/types/core/router/index.d.ts +203 -0
- package/types/core/security/index.d.ts +33 -0
- package/types/core/state/index.d.ts +68 -0
- package/types/core/storage/index.d.ts +40 -0
- package/types/core/ui/index.d.ts +446 -0
- package/types/core/workers/index.d.ts +221 -0
- package/types/elements/index.d.ts +150 -0
- package/types/index.d.ts +18 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { BaseElement } from '../base.js';
|
|
2
|
+
import { scheduleFrame } from '../schedule.js';
|
|
3
|
+
import { router } from '../../router/index.js';
|
|
4
|
+
import { specRegistry, internalsMap, initializedMap, pendingUpdatesMap, updateScheduledMap } from './state.js';
|
|
5
|
+
import { preloadResources } from './utils.js';
|
|
6
|
+
import { createComponentContext } from './proxy.js';
|
|
7
|
+
|
|
8
|
+
// Platform lifecycle method names that must never be overridden by spec.methods.
|
|
9
|
+
const RESERVED = new Set([
|
|
10
|
+
'connectedCallback', 'disconnectedCallback', 'attributeChangedCallback',
|
|
11
|
+
'adoptedCallback', 'formAssociatedCallback', 'formDisabledCallback',
|
|
12
|
+
'formResetCallback', 'formStateRestoreCallback',
|
|
13
|
+
'mount', 'unmount', 'constructor'
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* High-performance declarative element factory.
|
|
18
|
+
*/
|
|
19
|
+
export function element(tag, spec, base) {
|
|
20
|
+
if (typeof customElements === 'undefined') return;
|
|
21
|
+
if (customElements.get(tag)) {
|
|
22
|
+
console.warn(`Declarative Element "${tag}" is already defined. Skipping.`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Normalize prop shorthand (e.g. `count: 0`) into full configs before use.
|
|
27
|
+
if (spec.props) {
|
|
28
|
+
spec.props = normalizeProps(spec.props);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Cache element spec for automated layout orchestration and diffing
|
|
32
|
+
specRegistry.set(tag.toLowerCase(), spec);
|
|
33
|
+
|
|
34
|
+
// Automatically register elements with the route matcher if a url pattern is specified
|
|
35
|
+
if (spec.url) {
|
|
36
|
+
const meta = { ...spec.meta, container: spec.container };
|
|
37
|
+
router.register(spec.url, tag, meta);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Define properties to watch and their Symbol backing store keys (R-01)
|
|
41
|
+
const propKeys = spec.props ? Object.keys(spec.props) : [];
|
|
42
|
+
const observedAttrs = propKeys.map(k => k.toLowerCase());
|
|
43
|
+
const store = {};
|
|
44
|
+
for (const key of propKeys) {
|
|
45
|
+
store[key] = Symbol(key);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Warn loudly when a relative asset is given without a base — otherwise the
|
|
49
|
+
// resource would silently fail to load (U2).
|
|
50
|
+
warnMissingBase(tag, 'style', spec.style, base);
|
|
51
|
+
warnMissingBase(tag, 'template', spec.template, base);
|
|
52
|
+
|
|
53
|
+
// Resolve absolute URLs relative to import.meta.url (base)
|
|
54
|
+
const styleUrl = spec.style && base && (spec.style.endsWith('.css') || spec.style.startsWith('./') || spec.style.startsWith('/'))
|
|
55
|
+
? new URL(spec.style, base).href
|
|
56
|
+
: null;
|
|
57
|
+
const templateUrl = spec.template && base && (spec.template.endsWith('.html') || spec.template.startsWith('./') || spec.template.startsWith('/'))
|
|
58
|
+
? new URL(spec.template, base).href
|
|
59
|
+
: null;
|
|
60
|
+
|
|
61
|
+
// Initiate resource fetching exactly once per component registration (R-06)
|
|
62
|
+
let resolved = null;
|
|
63
|
+
const resourcesPromise = preloadResources(tag, styleUrl, templateUrl, spec.template, spec.style).then(res => {
|
|
64
|
+
resolved = res;
|
|
65
|
+
return res;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Handle hot reloading of constructable stylesheets (one global listener per unique styleUrl - R-05)
|
|
69
|
+
if (styleUrl && typeof window !== 'undefined') {
|
|
70
|
+
if (!window.__native_hmr_listeners__) {
|
|
71
|
+
window.__native_hmr_listeners__ = new Set();
|
|
72
|
+
}
|
|
73
|
+
if (!window.__native_hmr_listeners__.has(styleUrl)) {
|
|
74
|
+
window.__native_hmr_listeners__.add(styleUrl);
|
|
75
|
+
const hmrHandler = async (e) => {
|
|
76
|
+
const { path: changedPath, css } = e.detail;
|
|
77
|
+
const absoluteChangedUrl = new URL(changedPath, window.location.origin).href;
|
|
78
|
+
|
|
79
|
+
if (styleUrl === absoluteChangedUrl || styleUrl.endsWith(changedPath)) {
|
|
80
|
+
const res = await resourcesPromise;
|
|
81
|
+
if (res.stylesheet) {
|
|
82
|
+
res.stylesheet.replaceSync(css);
|
|
83
|
+
console.log(`[HMR] Shared AdoptedStyleSheet hot-swapped for <${tag}>`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
window.addEventListener('native:hmr:css', hmrHandler);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class DeclarativeElement extends BaseElement {
|
|
92
|
+
static observedAttributes = observedAttrs;
|
|
93
|
+
|
|
94
|
+
constructor() {
|
|
95
|
+
super();
|
|
96
|
+
this.attachShadow({ mode: spec.mode || 'open' });
|
|
97
|
+
|
|
98
|
+
initializedMap.set(this, false);
|
|
99
|
+
pendingUpdatesMap.set(this, new Map());
|
|
100
|
+
updateScheduledMap.set(this, false);
|
|
101
|
+
|
|
102
|
+
if (spec.form) {
|
|
103
|
+
const internals = this.attachInternals();
|
|
104
|
+
internalsMap.set(this, internals);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Initialize default properties backing store dynamically (R-01)
|
|
108
|
+
if (spec.props) {
|
|
109
|
+
for (const [key, config] of Object.entries(spec.props)) {
|
|
110
|
+
const sym = store[key];
|
|
111
|
+
const attrName = key.toLowerCase();
|
|
112
|
+
let initial = config.default;
|
|
113
|
+
if (config.type === Boolean) {
|
|
114
|
+
initial = this.hasAttribute(attrName);
|
|
115
|
+
} else {
|
|
116
|
+
const attrVal = this.getAttribute(attrName);
|
|
117
|
+
if (attrVal !== null) {
|
|
118
|
+
initial = config.type === Number ? Number(attrVal) : attrVal;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
this[sym] = initial ?? (config.type === Boolean ? false : null);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async connectedCallback() {
|
|
127
|
+
// AbortController bootstrap inside BaseElement
|
|
128
|
+
super.connectedCallback();
|
|
129
|
+
|
|
130
|
+
// Wait for resolved resources to compile (synchronously if already cached)
|
|
131
|
+
let res = resolved;
|
|
132
|
+
if (!res) {
|
|
133
|
+
res = await resourcesPromise;
|
|
134
|
+
}
|
|
135
|
+
const { templateNode, stylesheet, cssText, tagsDescriptor } = res;
|
|
136
|
+
|
|
137
|
+
if (!this.ctrl || this.ctrl.signal.aborted || !this.isConnected) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (templateNode && this.shadowRoot.childNodes.length === 0) {
|
|
142
|
+
this.shadowRoot.appendChild(templateNode.cloneNode(true));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (stylesheet) {
|
|
146
|
+
// Constructable stylesheets path
|
|
147
|
+
this.shadowRoot.adoptedStyleSheets = [stylesheet];
|
|
148
|
+
} else if (cssText) {
|
|
149
|
+
// Fallback: inject <style> for browsers without adoptedStyleSheets
|
|
150
|
+
const style = document.createElement('style');
|
|
151
|
+
style.textContent = cssText;
|
|
152
|
+
this.shadowRoot.prepend(style);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const context = createComponentContext({
|
|
156
|
+
el: this,
|
|
157
|
+
shadowRoot: this.shadowRoot,
|
|
158
|
+
ctrl: this.ctrl,
|
|
159
|
+
descriptor: tagsDescriptor,
|
|
160
|
+
internals: internalsMap.get(this)
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
this._ctx = context;
|
|
164
|
+
this._tags = context.tags;
|
|
165
|
+
this._on = context.on;
|
|
166
|
+
this._refs = context.refs;
|
|
167
|
+
this._watch = context.watch;
|
|
168
|
+
|
|
169
|
+
initializedMap.set(this, true);
|
|
170
|
+
|
|
171
|
+
// Mount hook trigger passed with unified AbortController signal
|
|
172
|
+
if (spec.mount) {
|
|
173
|
+
try {
|
|
174
|
+
const res = spec.mount(context);
|
|
175
|
+
if (res instanceof Promise) {
|
|
176
|
+
res.catch((err) => {
|
|
177
|
+
console.error('[Native UI] mount failed:', err);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
} catch (err) {
|
|
181
|
+
console.error('[Native UI] mount failed:', err);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
disconnectedCallback() {
|
|
187
|
+
if (spec.unmount) {
|
|
188
|
+
spec.unmount({
|
|
189
|
+
el: this,
|
|
190
|
+
tags: this._tags,
|
|
191
|
+
refs: this._refs,
|
|
192
|
+
watch: this._watch,
|
|
193
|
+
internals: internalsMap.get(this)
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
super.disconnectedCallback();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
200
|
+
if (oldVal === newVal) return;
|
|
201
|
+
|
|
202
|
+
// Find the camelCase property matching lowercase attribute name
|
|
203
|
+
const key = propKeys.find(k => k.toLowerCase() === name);
|
|
204
|
+
if (!key) return;
|
|
205
|
+
|
|
206
|
+
const config = spec.props[key];
|
|
207
|
+
let castedVal = newVal;
|
|
208
|
+
|
|
209
|
+
if (config.type === Boolean) {
|
|
210
|
+
castedVal = newVal !== null;
|
|
211
|
+
} else if (config.type === Number) {
|
|
212
|
+
castedVal = newVal !== null ? Number(newVal) : (config.default ?? 0);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Sync property to trigger update callback and schedule updates
|
|
216
|
+
if (this[key] !== castedVal) {
|
|
217
|
+
this[key] = castedVal;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Form-associated lifecycle callbacks — mapped from short spec hook names
|
|
222
|
+
formAssociatedCallback(form) {
|
|
223
|
+
spec.associated?.call(this, form);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
formDisabledCallback(value) {
|
|
227
|
+
spec.disabled?.call(this, value);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
formResetCallback() {
|
|
231
|
+
spec.reset?.call(this);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
formStateRestoreCallback(state, mode) {
|
|
235
|
+
spec.restore?.call(this, state, mode);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 1. Install spec.methods onto the prototype before define (§1.1)
|
|
240
|
+
if (spec.methods) {
|
|
241
|
+
for (const [name, fn] of Object.entries(spec.methods)) {
|
|
242
|
+
if (RESERVED.has(name) || name in DeclarativeElement.prototype) {
|
|
243
|
+
console.warn(`[Native UI] Method "${name}" conflicts with an existing or reserved name on <${tag}>. Skipping.`);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
Object.defineProperty(DeclarativeElement.prototype, name, {
|
|
248
|
+
value: fn,
|
|
249
|
+
writable: false,
|
|
250
|
+
configurable: true,
|
|
251
|
+
enumerable: false
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// 2. Generate type-safe getter/setters dynamically on class prototype (R-01)
|
|
257
|
+
for (const key of propKeys) {
|
|
258
|
+
const config = spec.props[key];
|
|
259
|
+
const attrName = key.toLowerCase();
|
|
260
|
+
const sym = store[key];
|
|
261
|
+
// reflect defaults to true for back-compatibility (§1.2)
|
|
262
|
+
const reflect = config.reflect !== false;
|
|
263
|
+
|
|
264
|
+
Object.defineProperty(DeclarativeElement.prototype, key, {
|
|
265
|
+
get() {
|
|
266
|
+
return this[sym];
|
|
267
|
+
},
|
|
268
|
+
set(val) {
|
|
269
|
+
const oldVal = this[sym];
|
|
270
|
+
if (oldVal === val) return;
|
|
271
|
+
|
|
272
|
+
this[sym] = val;
|
|
273
|
+
|
|
274
|
+
// Attribute reflection — only when reflect !== false (§1.2)
|
|
275
|
+
if (reflect) {
|
|
276
|
+
if (config.type === Boolean) {
|
|
277
|
+
if (val) {
|
|
278
|
+
if (!this.hasAttribute(attrName)) this.setAttribute(attrName, '');
|
|
279
|
+
} else {
|
|
280
|
+
if (this.hasAttribute(attrName)) this.removeAttribute(attrName);
|
|
281
|
+
}
|
|
282
|
+
} else if (val === null || val === undefined) {
|
|
283
|
+
if (this.hasAttribute(attrName)) this.removeAttribute(attrName);
|
|
284
|
+
} else {
|
|
285
|
+
const strVal = String(val);
|
|
286
|
+
if (this.getAttribute(attrName) !== strVal) {
|
|
287
|
+
this.setAttribute(attrName, strVal);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Custom state synchronization (:state(name))
|
|
293
|
+
const internals = internalsMap.get(this);
|
|
294
|
+
if (config.state && internals?.states) {
|
|
295
|
+
if (val) {
|
|
296
|
+
internals.states.add(key);
|
|
297
|
+
} else {
|
|
298
|
+
internals.states.delete(key);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Schedule batched updates via cooperative microtask scheduling
|
|
303
|
+
const initialized = initializedMap.get(this);
|
|
304
|
+
if (initialized && spec.update) {
|
|
305
|
+
const pendingUpdates = pendingUpdatesMap.get(this);
|
|
306
|
+
pendingUpdates.set(key, { val, old: oldVal });
|
|
307
|
+
if (!updateScheduledMap.get(this)) {
|
|
308
|
+
updateScheduledMap.set(this, true);
|
|
309
|
+
|
|
310
|
+
// Choose queueMicrotask or scheduleFrame depending on update.visual hint (R-03)
|
|
311
|
+
const flush = (spec.update.visual === true)
|
|
312
|
+
? (fn) => scheduleFrame(fn)
|
|
313
|
+
: (fn) => queueMicrotask(fn);
|
|
314
|
+
|
|
315
|
+
flush(() => {
|
|
316
|
+
if (!this.ctrl || this.ctrl.signal.aborted || !this.isConnected) {
|
|
317
|
+
pendingUpdates.clear();
|
|
318
|
+
updateScheduledMap.set(this, false);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const changes = Array.from(pendingUpdates.entries());
|
|
323
|
+
pendingUpdates.clear();
|
|
324
|
+
updateScheduledMap.set(this, false);
|
|
325
|
+
|
|
326
|
+
for (const [name, { val: v, old: o }] of changes) {
|
|
327
|
+
spec.update({
|
|
328
|
+
el: this,
|
|
329
|
+
ctrl: this.ctrl,
|
|
330
|
+
tags: this._tags,
|
|
331
|
+
on: this._on,
|
|
332
|
+
refs: this._refs,
|
|
333
|
+
watch: this._watch,
|
|
334
|
+
name,
|
|
335
|
+
val: v,
|
|
336
|
+
old: o,
|
|
337
|
+
prev: o
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// 3. Form association
|
|
348
|
+
if (spec.form) {
|
|
349
|
+
Object.defineProperty(DeclarativeElement, 'formAssociated', {
|
|
350
|
+
value: true,
|
|
351
|
+
writable: false
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Define element globally
|
|
356
|
+
customElements.define(tag, DeclarativeElement);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Normalizes prop definitions, expanding shorthand literals into full configs.
|
|
361
|
+
* `count: 0` -> `count: { type: Number, default: 0 }`. Object configs that
|
|
362
|
+
* already declare `type` are passed through unchanged.
|
|
363
|
+
*/
|
|
364
|
+
function normalizeProps(props) {
|
|
365
|
+
const out = {};
|
|
366
|
+
for (const [key, value] of Object.entries(props)) {
|
|
367
|
+
if (value && typeof value === 'object' && 'type' in value) {
|
|
368
|
+
out[key] = value;
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
let type = String;
|
|
372
|
+
if (typeof value === 'boolean') type = Boolean;
|
|
373
|
+
else if (typeof value === 'number') type = Number;
|
|
374
|
+
out[key] = { type, default: value };
|
|
375
|
+
}
|
|
376
|
+
return out;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Warns when a relative `template`/`style` is supplied without a base URL,
|
|
381
|
+
* which would otherwise silently fail to resolve.
|
|
382
|
+
*/
|
|
383
|
+
function warnMissingBase(tag, field, value, base) {
|
|
384
|
+
if (!base && typeof value === 'string' && (value.startsWith('./') || value.startsWith('../'))) {
|
|
385
|
+
console.error(
|
|
386
|
+
`[Native UI] <${tag}> ${field} "${value}" is relative but no base was given. ` +
|
|
387
|
+
`Pass import.meta.url as the third argument to ui.element(...).`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { define } from './define.js';
|
|
2
|
+
import { element } from './element.js';
|
|
3
|
+
import { container } from './container.js';
|
|
4
|
+
import { initOrchestrator } from './orchestrator.js';
|
|
5
|
+
|
|
6
|
+
// Initialize the global routing orchestrator
|
|
7
|
+
initOrchestrator();
|
|
8
|
+
|
|
9
|
+
export { define, element, container };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { router } from '../../router/index.js';
|
|
2
|
+
import { specRegistry } from './state.js';
|
|
3
|
+
|
|
4
|
+
let dispose = null; // One-word module-level disposer variable (RT-11)
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Initializes the global routing orchestrator.
|
|
8
|
+
* Listens for navigation found events and dynamically updates layout containers.
|
|
9
|
+
*/
|
|
10
|
+
export function initOrchestrator() {
|
|
11
|
+
if (typeof window !== 'undefined') {
|
|
12
|
+
dispose?.();
|
|
13
|
+
dispose = router.on('found', async ({ tag, params, query, hash, chain, direction }) => {
|
|
14
|
+
// Resolve the top-level layout element in the chain
|
|
15
|
+
const topTag = chain && chain.length > 0 ? chain[0].tag : tag;
|
|
16
|
+
const topParams = chain && chain.length > 0 ? chain[0].params : params;
|
|
17
|
+
|
|
18
|
+
// RT-13: Async hydration gate — await custom element definition before mounting
|
|
19
|
+
if (typeof customElements !== 'undefined' && topTag.includes('-') && !customElements.get(topTag)) {
|
|
20
|
+
await customElements.whenDefined(topTag);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const spec = specRegistry.get(topTag.toLowerCase());
|
|
24
|
+
if (!spec || !spec.container) return;
|
|
25
|
+
|
|
26
|
+
// Use Advanced Container Registry lookup instead of blind DOM query
|
|
27
|
+
const containerEl = router.getContainer(spec.container);
|
|
28
|
+
if (!containerEl) {
|
|
29
|
+
console.warn(`Target container "${spec.container}" not found in DOM for element <${topTag}>`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const props = {};
|
|
34
|
+
|
|
35
|
+
// 1. Cast params
|
|
36
|
+
for (const [key, val] of Object.entries(topParams)) {
|
|
37
|
+
let casted = val;
|
|
38
|
+
if (spec.props && spec.props[key]) {
|
|
39
|
+
const type = spec.props[key].type;
|
|
40
|
+
if (type === Boolean) {
|
|
41
|
+
casted = val === 'true' || val === '1' || val === '';
|
|
42
|
+
} else if (type === Number) {
|
|
43
|
+
const num = Number(val);
|
|
44
|
+
casted = isNaN(num) ? 0 : num;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
props[key] = casted;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2. Map query params
|
|
51
|
+
if (spec.query && Array.isArray(spec.query) && query) {
|
|
52
|
+
for (const key of spec.query) {
|
|
53
|
+
const val = query[key];
|
|
54
|
+
if (val !== undefined) {
|
|
55
|
+
let casted = val;
|
|
56
|
+
if (spec.props && spec.props[key]) {
|
|
57
|
+
const type = spec.props[key].type;
|
|
58
|
+
if (type === Boolean) {
|
|
59
|
+
casted = val === 'true' || val === '1' || val === '';
|
|
60
|
+
} else if (type === Number) {
|
|
61
|
+
const num = Number(val);
|
|
62
|
+
casted = isNaN(num) ? 0 : num;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
props[key] = casted;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 3. Map hash property
|
|
71
|
+
if (spec.props && spec.props.hash && hash !== undefined) {
|
|
72
|
+
props.hash = hash;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Layout-preserving diffing: Sync parameters reactively if the element is already mounted
|
|
76
|
+
const currentChild = containerEl.querySelector('.page-content');
|
|
77
|
+
if (currentChild && currentChild.tagName.toLowerCase() === topTag.toLowerCase()) {
|
|
78
|
+
for (const [key, value] of Object.entries(props)) {
|
|
79
|
+
currentChild[key] = value;
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Instantiate the new declarative page element
|
|
85
|
+
const pageEl = document.createElement(topTag);
|
|
86
|
+
pageEl.classList.add('page-content');
|
|
87
|
+
for (const [key, value] of Object.entries(props)) {
|
|
88
|
+
pageEl[key] = value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Delegated UI Swap: If the container implements swapView, let it handle the DOM transitions
|
|
92
|
+
if (typeof containerEl.swapView === 'function') {
|
|
93
|
+
await containerEl.swapView(pageEl, { params: props, direction });
|
|
94
|
+
} else {
|
|
95
|
+
// Fallback to standard atomic replace
|
|
96
|
+
containerEl.replaceChildren(pageEl);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function destroyOrchestrator() {
|
|
103
|
+
dispose?.();
|
|
104
|
+
dispose = null;
|
|
105
|
+
}
|