@seahax/elemental 0.1.0 → 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/README.md CHANGED
@@ -1,16 +1,17 @@
1
1
  # @seahax/elemental
2
2
 
3
- Small building blocks with a lot of potential.
3
+ Functional, reactive, web component base library.
4
4
 
5
- Extremely small reactive web components library, containing everything you really need to build anything up to a full reactive application, with only about 3KB overhead (depending on compression, minification, and tree-shaking).
5
+ Contains everything you need to build anything from a single component up to a full reactive application, with only about 3KB overhead (depending on compression, minification, and tree-shaking).
6
6
 
7
+ - Create fully portable web components
8
+ - Direct and safe access to the DOM (no virtual DOM)
9
+ - React-style list rendering using unique keys
10
+ - React-style code reuse using composable hooks
11
+ - Global & local state reactivity
12
+ - Client-side routing
7
13
  - No Build Tooling
8
14
  - No Dependencies
9
- - Portable Web Components
10
- - Global & Local Reactivity
11
- - Efficient DOM Rendering
12
- - Functional Hooks
13
- - Routing
14
15
 
15
16
  ## Define A Web Component
16
17
 
@@ -147,6 +148,51 @@ const element = new MyComponent();
147
148
  element.checked = true;
148
149
  ```
149
150
 
151
+ ## Render An Element
152
+
153
+ ```ts
154
+ // By tag name.
155
+ const element = h('div', {
156
+ // Set attributes.
157
+ class: 'my-class',
158
+ // Set properties.
159
+ ':id': 'my-id',
160
+ }, [
161
+ // Set children.
162
+ h('p', [text]),
163
+ ]);
164
+
165
+ // By custom element constructor.
166
+ const element = h(MyComponent, {
167
+ // Set attributes.
168
+ class: 'my-class',
169
+ // Set properties.
170
+ ':id': 'my-id',
171
+ }, [
172
+ // Set children.
173
+ h('p', [text]),
174
+ ]);
175
+ ```
176
+
177
+ ## Update An Element
178
+
179
+ ```ts
180
+ // Update an existing element.
181
+ h(element, {
182
+ // Set attributes.
183
+ class: 'my-class',
184
+ // Remove attributes.
185
+ class: null,
186
+ // Set properties.
187
+ ':id': 'my-id',
188
+ // Attributes and properties that are not provided are left alone.
189
+ }, [
190
+ // Replace all children. Children are left alone if no child array is
191
+ // provided (undefined or omitted).
192
+ h('p', [text]),
193
+ ]);
194
+ ```
195
+
150
196
  ## Render Lists With Keys
151
197
 
152
198
  ```ts
@@ -0,0 +1,66 @@
1
+ import { createCallbacks as e } from "./internal/callbacks.js";
2
+ import { $$ref as t, $$renderContextStack as n } from "./internal/constants.js";
3
+ //#region src/component.ts
4
+ Object.assign(window, { [n]: [] });
5
+ function r(r, i = {}) {
6
+ return class extends HTMLElement {
7
+ #e = e();
8
+ #t = e();
9
+ #n = i;
10
+ #r;
11
+ #i = !1;
12
+ constructor() {
13
+ super();
14
+ let e = this.#r = {};
15
+ if (this.#n.props) {
16
+ let t = this.#n.props;
17
+ for (let [n, r] of Object.entries(t)) {
18
+ if (n in this) continue;
19
+ let t = r(e[n] = this.#a(void 0), this);
20
+ Object.defineProperty(this, n, t);
21
+ }
22
+ }
23
+ }
24
+ connectedCallback() {
25
+ let e = this.attachShadow({
26
+ ...this.#n.shadow,
27
+ mode: this.#n.shadow?.mode ?? "open"
28
+ });
29
+ try {
30
+ window[n].push({
31
+ host: this,
32
+ onDisconnect: this.#e,
33
+ onSetRef: this.#t,
34
+ useRef: this.#a
35
+ }), r(e, this.#r);
36
+ } finally {
37
+ window[n].pop();
38
+ }
39
+ this.#t.run();
40
+ }
41
+ disconnectedCallback() {
42
+ this.#t.clear(), this.#e.run({ clear: !0 });
43
+ }
44
+ #a = (e, n) => {
45
+ let r = this.#o, i = e;
46
+ return {
47
+ [t]: !0,
48
+ get value() {
49
+ return i;
50
+ },
51
+ set value(e) {
52
+ e !== i && (i = e, n?.(i), r());
53
+ }
54
+ };
55
+ };
56
+ #o = () => {
57
+ this.#i || (this.#i = !0, queueMicrotask(() => {
58
+ this.#i = !1, this.#t.run();
59
+ }));
60
+ };
61
+ };
62
+ }
63
+ //#endregion
64
+ export { r as defineComponent };
65
+
66
+ //# sourceMappingURL=component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.js","names":["#onDisconnect","#onSetRef","#options","#refs","#useRef","#notify","#notifying"],"sources":["../src/component.ts"],"sourcesContent":["import { type Callbacks, createCallbacks } from './internal/callbacks.ts';\nimport { $$ref, $$renderContextStack } from './internal/constants.ts';\n\ntype SafeProps<TProps> = any extends any\n ? { [P in keyof TProps as P extends keyof HTMLElement ? never : P]: TProps[P] }\n : never;\n\nexport interface ComponentConstructor<TProps extends object> {\n new (): ComponentWithProps<TProps>;\n}\n\nexport interface ComponentOptions<TProps extends object> {\n readonly shadow?: Partial<ShadowRootInit>;\n readonly props?: ComponentPropDescriptors<TProps>;\n}\n\nexport type ComponentPropDescriptors<TProps extends object> = {\n readonly [P in keyof SafeProps<TProps>]: ComponentPropDescriptorFactory<TProps[P]>;\n};\n\nexport type ComponentPropDescriptorFactory<TType> = (\n ref: Ref<TType | undefined>,\n host: HTMLElement,\n) => ComponentPropDescriptor<TType>;\n\nexport interface ComponentPropDescriptor<T> extends Omit<PropertyDescriptor, 'value' | 'get' | 'set'> {\n get(): T;\n set?(value: T): void;\n}\n\nexport type ComponentWithProps<TProps extends object> = HTMLElement & {\n -readonly [P in keyof SafeProps<TProps>]: TProps[P];\n};\n\nexport type ComponentShadowRoot<TProps extends object> = Omit<ShadowRoot, 'host'> & {\n readonly host: ComponentWithProps<TProps>;\n};\n\nexport type ComponentPropRefs<TProps extends object> = {\n readonly [P in keyof SafeProps<TProps>]: Ref<TProps[P] | undefined>;\n};\n\nexport interface Ref<T> extends ReadonlyRef<T> {\n value: T;\n}\n\nexport interface ReadonlyRef<T> {\n /** @hidden */\n [$$ref]: unknown;\n readonly value: T;\n}\n\nexport type RefValues<T> = T extends readonly any[]\n ? { [K in keyof T]: T[K] extends ReadonlyRef<infer V> ? V : never }\n : never;\n\ndeclare global {\n interface Window {\n readonly [$$renderContextStack]: {\n readonly host: HTMLElement;\n readonly onDisconnect: Callbacks;\n readonly onSetRef: Callbacks;\n readonly useRef: <T>(initialValue: T, onChange?: (value: T) => void) => Ref<T>;\n }[];\n }\n}\n\nObject.assign(window, { [$$renderContextStack]: [] });\n\n/** Define a custom `HTMLElement` that is functional and reactive. */\nexport function defineComponent<TProps extends object = {}>(\n render: (shadow: ComponentShadowRoot<TProps>, props: ComponentPropRefs<TProps>) => void,\n options?: ComponentOptions<TProps>,\n): ComponentConstructor<TProps>;\nexport function defineComponent(\n render: (\n shadow: ComponentShadowRoot<Record<string, unknown>>,\n props: ComponentPropRefs<Record<string, unknown>>,\n ) => void,\n options: ComponentOptions<Record<string, unknown>> = {},\n): new () => HTMLElement {\n return class extends HTMLElement {\n readonly #onDisconnect = createCallbacks();\n readonly #onSetRef = createCallbacks();\n readonly #options = options;\n readonly #refs: ComponentPropRefs<Record<string, unknown>>;\n #notifying = false;\n\n constructor() {\n super();\n const refs: Record<string, Ref<unknown>> = (this.#refs = {});\n\n if (this.#options.props) {\n const descriptors = this.#options.props;\n\n for (const [key, getDescriptor] of Object.entries(descriptors)) {\n if (key in this) continue;\n const ref = (refs[key] = this.#useRef<any>(undefined));\n const descriptor = getDescriptor(ref, this);\n Object.defineProperty(this, key, descriptor);\n }\n }\n }\n\n protected connectedCallback(): void {\n const shadow = this.attachShadow({ ...this.#options.shadow, mode: this.#options.shadow?.mode ?? 'open' });\n\n try {\n window[$$renderContextStack].push({\n host: this,\n onDisconnect: this.#onDisconnect,\n onSetRef: this.#onSetRef,\n useRef: this.#useRef,\n });\n\n render(shadow as ComponentShadowRoot<Record<string, unknown>>, this.#refs);\n } finally {\n window[$$renderContextStack].pop();\n }\n\n this.#onSetRef.run();\n }\n\n protected disconnectedCallback(): void {\n this.#onSetRef.clear();\n this.#onDisconnect.run({ clear: true });\n }\n\n readonly #useRef = <T>(initialValue: T, onChange?: (value: T) => void): Ref<T> => {\n const notify = this.#notify;\n let value = initialValue;\n\n return {\n [$$ref]: true,\n get value() {\n return value;\n },\n set value(newValue) {\n if (newValue === value) return;\n value = newValue;\n onChange?.(value);\n notify();\n },\n };\n };\n\n readonly #notify = (): void => {\n if (this.#notifying) return;\n this.#notifying = true;\n\n queueMicrotask(() => {\n this.#notifying = false;\n this.#onSetRef.run();\n });\n };\n };\n}\n"],"mappings":";;;AAmEA,OAAO,OAAO,QAAQ,GAAG,IAAuB,EAAE,EAAE,CAAC;AAOrD,SAAgB,EACd,GAIA,IAAqD,EAAE,EAChC;AACvB,QAAO,cAAc,YAAY;EAC/B,KAAyB,GAAiB;EAC1C,KAAqB,GAAiB;EACtC,KAAoB;EACpB;EACA,KAAa;EAEb,cAAc;AACZ,UAAO;GACP,IAAM,IAAsC,MAAA,IAAa,EAAE;AAE3D,OAAI,MAAA,EAAc,OAAO;IACvB,IAAM,IAAc,MAAA,EAAc;AAElC,SAAK,IAAM,CAAC,GAAK,MAAkB,OAAO,QAAQ,EAAY,EAAE;AAC9D,SAAI,KAAO,KAAM;KAEjB,IAAM,IAAa,EAAc,EADf,KAAO,MAAA,EAAkB,KAAA,EAAU,EACf,KAAK;AAC3C,YAAO,eAAe,MAAM,GAAK,EAAW;;;;EAKlD,oBAAoC;GAClC,IAAM,IAAS,KAAK,aAAa;IAAE,GAAG,MAAA,EAAc;IAAQ,MAAM,MAAA,EAAc,QAAQ,QAAQ;IAAQ,CAAC;AAEzG,OAAI;AAQF,IAPA,OAAO,GAAsB,KAAK;KAChC,MAAM;KACN,cAAc,MAAA;KACd,UAAU,MAAA;KACV,QAAQ,MAAA;KACT,CAAC,EAEF,EAAO,GAAwD,MAAA,EAAW;aAClE;AACR,WAAO,GAAsB,KAAK;;AAGpC,SAAA,EAAe,KAAK;;EAGtB,uBAAuC;AAErC,GADA,MAAA,EAAe,OAAO,EACtB,MAAA,EAAmB,IAAI,EAAE,OAAO,IAAM,CAAC;;EAGzC,MAAuB,GAAiB,MAA0C;GAChF,IAAM,IAAS,MAAA,GACX,IAAQ;AAEZ,UAAO;KACJ,IAAQ;IACT,IAAI,QAAQ;AACV,YAAO;;IAET,IAAI,MAAM,GAAU;AACd,WAAa,MACjB,IAAQ,GACR,IAAW,EAAM,EACjB,GAAQ;;IAEX;;EAGH,WAA+B;AACzB,SAAA,MACJ,MAAA,IAAkB,IAElB,qBAAqB;AAEnB,IADA,MAAA,IAAkB,IAClB,MAAA,EAAe,KAAK;KACpB"}
@@ -0,0 +1,38 @@
1
+ import { createCallbacks as e } from "../internal/callbacks.js";
2
+ import { $$renderContextStack as t } from "../internal/constants.js";
3
+ import "../component.js";
4
+ //#region src/hooks/core.ts
5
+ function n(e, t) {
6
+ return a().useRef(e, t);
7
+ }
8
+ function r(t, n) {
9
+ let { onSetRef: r, onDisconnect: i } = a(), o = e(), s = () => o.run({ clear: !0 }), c;
10
+ r.push(() => {
11
+ let e = t.map((e) => e.value);
12
+ if (c?.length === e.length && c?.every((t, n) => t === e[n])) return;
13
+ c = e, s();
14
+ let r = n(...c);
15
+ r && o.push(() => r());
16
+ }), i.push(s);
17
+ }
18
+ function i(...e) {
19
+ if (e.length === 0) return {};
20
+ let { host: t } = a(), i = Object.fromEntries(e.map((e) => [e, n(t.getAttribute(e), (n) => {
21
+ n == null ? t.removeAttribute(e) : t.setAttribute(e, n);
22
+ })])), o = new MutationObserver((e) => {
23
+ for (let { attributeName: n } of e) n != null && Object.hasOwn(i, n) && (i[n].value = t.getAttribute(n));
24
+ });
25
+ return r([], () => (o.observe(t, {
26
+ attributeFilter: e,
27
+ attributes: !0
28
+ }), () => o.disconnect())), i;
29
+ }
30
+ function a() {
31
+ let e = window[t].at(-1);
32
+ if (!e) throw Error("hooks must be called inside a render function");
33
+ return e;
34
+ }
35
+ //#endregion
36
+ export { i as useAttributes, r as useEffect, n as useRef };
37
+
38
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","names":[],"sources":["../../src/hooks/core.ts"],"sourcesContent":["import { type ReadonlyRef, type Ref, type RefValues } from '../component.ts';\nimport { createCallbacks } from '../internal/callbacks.ts';\nimport { $$renderContextStack } from '../internal/constants.ts';\n\n/** Create an observable value. */\nexport function useRef<T>(initialValue: T, onChange?: (value: T) => void): Ref<T> {\n return getHookContext().useRef(initialValue, onChange);\n}\n\n/** React to observable (reference) changes. */\nexport function useEffect<const TDeps extends readonly ReadonlyRef<any>[]>(\n deps: TDeps,\n callback: (...values: RefValues<TDeps>) => (() => void) | void,\n): void {\n const { onSetRef, onDisconnect } = getHookContext();\n const cleanupCallback = createCallbacks();\n const cleanup = (): void => cleanupCallback.run({ clear: true });\n let values: any[] | undefined;\n\n onSetRef.push((): void => {\n const newValues = deps.map((dep) => dep.value);\n if (values?.length === newValues.length && values?.every((value, i) => value === newValues[i])) return;\n values = newValues;\n cleanup();\n const maybeCleanup = callback(...(values as any));\n if (maybeCleanup) cleanupCallback.push(() => maybeCleanup());\n });\n\n onDisconnect.push(cleanup);\n}\n\n/** Observe attribute changes. */\nexport function useAttributes<TName extends string>(...names: TName[]): Readonly<Record<TName, Ref<string | null>>> {\n if (names.length === 0) return {} as any;\n const { host } = getHookContext();\n\n const refs = Object.fromEntries(\n names.map((name) => [\n name,\n useRef(host.getAttribute(name), (value) => {\n if (value == null) host.removeAttribute(name);\n else host.setAttribute(name, value);\n }),\n ]),\n );\n\n const observer = new MutationObserver((mutation) => {\n for (const { attributeName } of mutation) {\n if (attributeName != null && Object.hasOwn(refs, attributeName)) {\n refs[attributeName]!.value = host.getAttribute(attributeName);\n }\n }\n });\n\n useEffect([], () => {\n observer.observe(host, { attributeFilter: names, attributes: true });\n return () => observer.disconnect();\n });\n\n return refs as Readonly<Record<TName, Ref<string | null>>>;\n}\n\nfunction getHookContext(): (typeof window)[typeof $$renderContextStack][0] {\n const context = window[$$renderContextStack].at(-1);\n if (!context) throw new Error('hooks must be called inside a render function');\n return context;\n}\n"],"mappings":";;;;AAKA,SAAgB,EAAU,GAAiB,GAAuC;AAChF,QAAO,GAAgB,CAAC,OAAO,GAAc,EAAS;;AAIxD,SAAgB,EACd,GACA,GACM;CACN,IAAM,EAAE,aAAU,oBAAiB,GAAgB,EAC7C,IAAkB,GAAiB,EACnC,UAAsB,EAAgB,IAAI,EAAE,OAAO,IAAM,CAAC,EAC5D;AAWJ,CATA,EAAS,WAAiB;EACxB,IAAM,IAAY,EAAK,KAAK,MAAQ,EAAI,MAAM;AAC9C,MAAI,GAAQ,WAAW,EAAU,UAAU,GAAQ,OAAO,GAAO,MAAM,MAAU,EAAU,GAAG,CAAE;AAEhG,EADA,IAAS,GACT,GAAS;EACT,IAAM,IAAe,EAAS,GAAI,EAAe;AACjD,EAAI,KAAc,EAAgB,WAAW,GAAc,CAAC;GAC5D,EAEF,EAAa,KAAK,EAAQ;;AAI5B,SAAgB,EAAoC,GAAG,GAA6D;AAClH,KAAI,EAAM,WAAW,EAAG,QAAO,EAAE;CACjC,IAAM,EAAE,YAAS,GAAgB,EAE3B,IAAO,OAAO,YAClB,EAAM,KAAK,MAAS,CAClB,GACA,EAAO,EAAK,aAAa,EAAK,GAAG,MAAU;AACzC,EAAI,KAAS,OAAM,EAAK,gBAAgB,EAAK,GACxC,EAAK,aAAa,GAAM,EAAM;GACnC,CACH,CAAC,CACH,EAEK,IAAW,IAAI,kBAAkB,MAAa;AAClD,OAAK,IAAM,EAAE,sBAAmB,EAC9B,CAAI,KAAiB,QAAQ,OAAO,OAAO,GAAM,EAAc,KAC7D,EAAK,GAAgB,QAAQ,EAAK,aAAa,EAAc;GAGjE;AAOF,QALA,EAAU,EAAE,SACV,EAAS,QAAQ,GAAM;EAAE,iBAAiB;EAAO,YAAY;EAAM,CAAC,QACvD,EAAS,YAAY,EAClC,EAEK;;AAGT,SAAS,IAAkE;CACzE,IAAM,IAAU,OAAO,GAAsB,GAAG,GAAG;AACnD,KAAI,CAAC,EAAS,OAAU,MAAM,gDAAgD;AAC9E,QAAO"}
@@ -0,0 +1,31 @@
1
+ import { useEffect as e, useRef as t } from "./core.js";
2
+ //#region src/hooks/loading.ts
3
+ function n(n, r, { debounceMs: i } = {}) {
4
+ let a = t({
5
+ loading: !0,
6
+ value: void 0,
7
+ error: void 0
8
+ }), o = !0;
9
+ return e(n, () => (...e) => {
10
+ let t = new AbortController();
11
+ return Promise.race(o ? [Promise.resolve()] : [new Promise((e) => setTimeout(e, i)), new Promise((e) => t.signal.addEventListener("abort", e, { once: !0 }))]).then(() => {
12
+ if (!t.signal.aborted) return r(t.signal, ...e);
13
+ }).then((e) => {
14
+ t.signal.aborted || (a.value = {
15
+ loading: !1,
16
+ value: e,
17
+ error: void 0
18
+ });
19
+ }).catch((e) => {
20
+ t.signal.aborted || (a.value = {
21
+ loading: !1,
22
+ value: void 0,
23
+ error: e
24
+ });
25
+ }), o = !1, () => t.abort();
26
+ }), e([], () => () => o = !0), a;
27
+ }
28
+ //#endregion
29
+ export { n as useLoading };
30
+
31
+ //# sourceMappingURL=loading.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loading.js","names":[],"sources":["../../src/hooks/loading.ts"],"sourcesContent":["import type { ReadonlyRef, Ref, RefValues } from '../component.ts';\nimport { useEffect, useRef } from './core.ts';\n\nexport interface LoadingValue<TValue> {\n readonly loading: boolean;\n readonly value: TValue | undefined;\n readonly error: unknown;\n}\n\nexport interface LoadingOptions {\n readonly debounceMs?: number;\n}\n\n/** Load data asynchronously. */\nexport function useLoading<const TDeps extends readonly ReadonlyRef<any>[], TValue>(\n deps: TDeps,\n callback: (signal: AbortSignal, ...values: RefValues<TDeps>) => Promise<TValue>,\n { debounceMs }: LoadingOptions = {},\n): Ref<LoadingValue<TValue>> {\n const ref = useRef<LoadingValue<TValue>>({ loading: true, value: undefined, error: undefined });\n let skipDebounce = true;\n\n useEffect(deps, () => (...values) => {\n const ac = new AbortController();\n\n Promise.race(\n skipDebounce\n ? [Promise.resolve()]\n : [\n new Promise((resolve) => setTimeout(resolve, debounceMs)),\n new Promise((resolve) => ac.signal.addEventListener('abort', resolve, { once: true })),\n ],\n )\n .then(() => {\n if (ac.signal.aborted) return;\n return callback(ac.signal, ...(values as any));\n })\n .then((value) => {\n if (ac.signal.aborted) return;\n ref.value = { loading: false, value, error: undefined };\n })\n .catch((error: unknown) => {\n if (ac.signal.aborted) return;\n ref.value = { loading: false, value: undefined, error };\n });\n\n skipDebounce = false;\n return () => ac.abort();\n });\n\n // Skip the debounce again if the component unmounts.\n useEffect([], () => () => (skipDebounce = true));\n\n return ref;\n}\n"],"mappings":";;AAcA,SAAgB,EACd,GACA,GACA,EAAE,kBAA+B,EAAE,EACR;CAC3B,IAAM,IAAM,EAA6B;EAAE,SAAS;EAAM,OAAO,KAAA;EAAW,OAAO,KAAA;EAAW,CAAC,EAC3F,IAAe;AAiCnB,QA/BA,EAAU,UAAa,GAAG,MAAW;EACnC,IAAM,IAAK,IAAI,iBAAiB;AAwBhC,SAtBA,QAAQ,KACN,IACI,CAAC,QAAQ,SAAS,CAAC,GACnB,CACE,IAAI,SAAS,MAAY,WAAW,GAAS,EAAW,CAAC,EACzD,IAAI,SAAS,MAAY,EAAG,OAAO,iBAAiB,SAAS,GAAS,EAAE,MAAM,IAAM,CAAC,CAAC,CACvF,CACN,CACE,WAAW;AACN,UAAG,OAAO,QACd,QAAO,EAAS,EAAG,QAAQ,GAAI,EAAe;IAC9C,CACD,MAAM,MAAU;AACX,KAAG,OAAO,YACd,EAAI,QAAQ;IAAE,SAAS;IAAO;IAAO,OAAO,KAAA;IAAW;IACvD,CACD,OAAO,MAAmB;AACrB,KAAG,OAAO,YACd,EAAI,QAAQ;IAAE,SAAS;IAAO,OAAO,KAAA;IAAW;IAAO;IACvD,EAEJ,IAAe,UACF,EAAG,OAAO;GACvB,EAGF,EAAU,EAAE,cAAe,IAAe,GAAM,EAEzC"}
@@ -7,4 +7,4 @@ export type RouteMatchArray = readonly [string, ...string[]] & {
7
7
  readonly groups: Record<string, string>;
8
8
  };
9
9
  /** Observe route (window.history) changes. */
10
- export declare function useRoute(path: string | readonly string[], { match, source }?: RouteOptions): [match: Ref<RouteMatchArray | null>, state: Ref<unknown>];
10
+ export declare function useRoute(path: string | readonly string[], { match, source }?: RouteOptions): Ref<RouteMatchArray | null>;
@@ -0,0 +1,18 @@
1
+ import { useEffect as e, useRef as t } from "./core.js";
2
+ import { getRouter as n } from "../router.js";
3
+ import { useStore as r } from "./store.js";
4
+ //#region src/hooks/route.ts
5
+ function i(i, { match: a = "prefix", source: o = "pathname" } = {}) {
6
+ let s = a === "exact" ? /^$/u : a === "prefix" ? /^.*$/u : a, c = Array.isArray(i) ? i : [i], l = r(n(), (e) => e[o]), u = t(d(l.value));
7
+ return e([l], (e) => {
8
+ u.value = d(e);
9
+ }), u;
10
+ function d(e) {
11
+ let t = c.find((t) => e.endsWith(t)) ?? null;
12
+ return t == null ? null : (e = e.slice(t.length), e.match(s));
13
+ }
14
+ }
15
+ //#endregion
16
+ export { i as useRoute };
17
+
18
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","names":[],"sources":["../../src/hooks/route.ts"],"sourcesContent":["import type { Ref } from '../component.ts';\nimport { getRouter } from '../router.ts';\nimport { useEffect, useRef } from './core.ts';\nimport { useStore } from './store.ts';\n\nexport interface RouteOptions {\n readonly match?: 'prefix' | 'exact' | RegExp;\n readonly source?: 'pathname' | 'hash';\n}\n\nexport type RouteMatchArray = readonly [string, ...string[]] & { readonly groups: Record<string, string> };\n\n/** Observe route (window.history) changes. */\nexport function useRoute(\n path: string | readonly string[],\n { match = 'prefix', source = 'pathname' }: RouteOptions = {},\n): Ref<RouteMatchArray | null> {\n const matchRx = match === 'exact' ? /^$/u : match === 'prefix' ? /^.*$/u : match;\n const paths = Array.isArray(path) ? (path as readonly string[]) : [path as string];\n const refRoute = useStore(getRouter(), (state) => state[source]);\n const refMatch = useRef<RouteMatchArray | null>(getMatch(refRoute.value));\n\n useEffect([refRoute], (route) => {\n refMatch.value = getMatch(route);\n });\n\n return refMatch;\n\n function getMatch(route: string): RouteMatchArray | null {\n const prefix = paths.find((path) => route.endsWith(path)) ?? null;\n if (prefix == null) return null;\n route = route.slice(prefix.length);\n return route.match(matchRx) as RouteMatchArray | null;\n }\n}\n"],"mappings":";;;;AAaA,SAAgB,EACd,GACA,EAAE,WAAQ,UAAU,YAAS,eAA6B,EAAE,EAC/B;CAC7B,IAAM,IAAU,MAAU,UAAU,QAAQ,MAAU,WAAW,UAAU,GACrE,IAAQ,MAAM,QAAQ,EAAK,GAAI,IAA6B,CAAC,EAAe,EAC5E,IAAW,EAAS,GAAW,GAAG,MAAU,EAAM,GAAQ,EAC1D,IAAW,EAA+B,EAAS,EAAS,MAAM,CAAC;AAMzE,QAJA,EAAU,CAAC,EAAS,GAAG,MAAU;AAC/B,IAAS,QAAQ,EAAS,EAAM;GAChC,EAEK;CAEP,SAAS,EAAS,GAAuC;EACvD,IAAM,IAAS,EAAM,MAAM,MAAS,EAAM,SAAS,EAAK,CAAC,IAAI;AAG7D,SAFI,KAAU,OAAa,QAC3B,IAAQ,EAAM,MAAM,EAAO,OAAO,EAC3B,EAAM,MAAM,EAAQ"}
@@ -0,0 +1,10 @@
1
+ import { useEffect as e, useRef as t } from "./core.js";
2
+ //#region src/hooks/store.ts
3
+ function n(n, r = (e) => e, i) {
4
+ let a = t(r(n.state), i && ((e) => i(n, e)));
5
+ return e([], () => n.subscribe((e) => a.value = r(e))), a;
6
+ }
7
+ //#endregion
8
+ export { n as useStore };
9
+
10
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","names":[],"sources":["../../src/hooks/store.ts"],"sourcesContent":["import type { ReadonlyRef, Ref } from '../component.ts';\nimport type { Store } from '../store.ts';\nimport { useEffect, useRef } from './core.ts';\n\n/** Observe store (external state) changes. */\nexport function useStore<TState, TValue = TState>(\n store: Store<TState>,\n select: (state: TState) => TValue,\n mutate: (store: Store<TState>, value: TValue) => void,\n): Ref<TValue>;\nexport function useStore<TState, TValue = TState>(\n store: Store<TState>,\n select?: (state: TState) => TValue,\n): ReadonlyRef<TValue>;\nexport function useStore<TState, TValue = TState>(\n store: Store<TState>,\n select: (state: TState) => TValue = (state) => state as unknown as TValue,\n mutate?: (store: Store<TState>, value: TValue) => void,\n): Ref<TValue> {\n const ref = useRef(select(store.state), mutate && ((value) => mutate(store, value)));\n useEffect([], () => store.subscribe((state) => (ref.value = select(state))));\n return ref;\n}\n"],"mappings":";;AAcA,SAAgB,EACd,GACA,KAAqC,MAAU,GAC/C,GACa;CACb,IAAM,IAAM,EAAO,EAAO,EAAM,MAAM,EAAE,OAAY,MAAU,EAAO,GAAO,EAAM,EAAE;AAEpF,QADA,EAAU,EAAE,QAAQ,EAAM,WAAW,MAAW,EAAI,QAAQ,EAAO,EAAM,CAAE,CAAC,EACrE"}
package/dist/html.d.ts CHANGED
@@ -1,33 +1,37 @@
1
- type TagMap = HTMLElementTagNameMap & HTMLElementDeprecatedTagNameMap;
2
- type TagType<TTag extends string> = TTag extends keyof TagMap ? TagMap[TTag] : HTMLElement;
3
- type ChildValue = Node | HtmlDeferred | string | number | bigint | false | null | undefined;
1
+ type Alpha = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
2
+ type ChildValue = Node | HtmlDeferredElement | string | number | bigint | false | null | undefined;
4
3
  type AttrValue = string | number | bigint | boolean | null | undefined;
5
- export interface HtmlDeferred<TTag extends keyof TagMap | (string & {}) = string> {
6
- readonly tag: TTag;
7
- readonly key: string;
8
- readonly attrs: Readonly<Record<string, AttrValue>>;
4
+ type TagMap = HTMLElementTagNameMap & HTMLElementDeprecatedTagNameMap;
5
+ type IfEquals<T, U, Y = unknown, N = never> = (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2 ? Y : N;
6
+ type ElementType<TElement> = TElement extends string ? TElement extends keyof TagMap ? TagMap[TElement] : HTMLElement : TElement extends CustomElementConstructor ? InstanceType<TElement> : TElement extends Element | Document | ShadowRoot ? TElement : never;
7
+ type ElementProps<TElement> = {
8
+ readonly [P in keyof TElement & string as TElement[P] extends ((...args: any[]) => any) | null | undefined ? never : IfEquals<Record<P, TElement[P]>, Pick<TElement, P>, `:${P}`, never>]?: TElement[P];
9
+ };
10
+ type ElementAttrs<TElement> = TElement extends string | CustomElementConstructor | Element ? {} : Readonly<Record<`${Alpha}${string}`, AttrValue>>;
11
+ export type HtmlConfig<TElement> = ElementAttrs<TElement> & ElementProps<ElementType<TElement>>;
12
+ export type HtmlConfigWithKey<TElement> = {
13
+ readonly [DATA_KEY]: string;
14
+ } & HtmlConfig<TElement>;
15
+ export interface HtmlDeferredElement<TElement extends HTMLElement = HTMLElement> {
16
+ readonly tag: string;
17
+ readonly config: {
18
+ readonly [DATA_KEY]: string;
19
+ } & HtmlConfig<TElement>;
9
20
  readonly children: readonly ChildValue[] | undefined;
10
- resolve: () => TagType<TTag>;
21
+ toElement: () => TElement;
11
22
  }
23
+ declare const DATA_KEY = "data-key";
24
+ /**
25
+ * Create a deferred HTML element with a key.
26
+ *
27
+ * Final element creation is deferred until the element is used as a child,
28
+ * when it can be determined if a new element should be created, or an existing
29
+ * element with the same key and tag-name should be updated.
30
+ */
31
+ export declare function html<const TElement extends keyof TagMap | (string & {}) | CustomElementConstructor>(el: TElement, config: HtmlConfigWithKey<TElement>, children?: readonly ChildValue[]): HtmlDeferredElement<ElementType<TElement>>;
12
32
  /**
13
- * Create a deferred HTML element with a key. Final element creation is
14
- * deferred until the element is used as a child, when it can be determined if
15
- * a new element should be created, or an existing element with the same key
16
- * should be updated.
33
+ * Create or update an HTML element.
17
34
  */
18
- export declare function html<const TTag extends keyof TagMap | (string & {})>(tag: TTag | CustomElementConstructor, attrs: {
19
- readonly $key: string;
20
- } & Readonly<Record<string, AttrValue>>, children?: readonly ChildValue[]): HtmlDeferred<TTag>;
21
- /** Create an HTML element with attributes and children. */
22
- export declare function html<const TTag extends keyof TagMap | (string & {})>(tag: TTag | CustomElementConstructor, attrs?: Readonly<Record<string, AttrValue>>, children?: readonly ChildValue[]): TagType<TTag>;
23
- /** Create an HTML element with children. */
24
- export declare function html<const TTag extends keyof TagMap | (string & {})>(tag: TTag | CustomElementConstructor, children?: readonly ChildValue[]): TagType<TTag>;
25
- /** Update element attributes and replace children. */
26
- export declare function html<TElement extends Element | Document | ShadowRoot>(element: TElement, attrs?: TElement extends {
27
- setAttribute: (...args: any[]) => any;
28
- } ? Readonly<Record<string, AttrValue>> : undefined, children?: readonly ChildValue[]): TElement;
29
- /** Replace element children. */
30
- export declare function html<TElement extends Element | Document | ShadowRoot>(element: TElement, children?: readonly ChildValue[]): TElement;
31
- /** Parse a raw HTML string into a `DocumentFragment`. */
32
- export declare function parseHTML(htmlString: string): DocumentFragment;
35
+ export declare function html<TElement extends keyof TagMap | (string & {}) | CustomElementConstructor | Element | Document | ShadowRoot>(el: TElement, attrs?: HtmlConfig<TElement>, children?: readonly ChildValue[]): ElementType<NoInfer<TElement>>;
36
+ export declare function html<TElement extends keyof TagMap | (string & {}) | CustomElementConstructor | Element | Document | ShadowRoot>(el: TElement, children?: readonly ChildValue[]): ElementType<TElement>;
33
37
  export {};
package/dist/html.js ADDED
@@ -0,0 +1,42 @@
1
+ //#region src/html.ts
2
+ var e = "data-key";
3
+ function t(n, r = {}, i = []) {
4
+ if (typeof n == "function") {
5
+ let e = customElements.getName(n);
6
+ e ?? (e = `ce-${crypto.randomUUID()}`, customElements.define(e, n)), n = e;
7
+ }
8
+ let a;
9
+ if ([a, i] = Array.isArray(r) ? [{}, r] : [r, i], typeof n == "string") return e in a ? {
10
+ tag: n,
11
+ config: a,
12
+ children: i,
13
+ toElement: () => t(document.createElement(n), a, i)
14
+ } : t(document.createElement(n), a, i);
15
+ if ("setAttribute" in n) {
16
+ for (let [e, t] of Object.entries(a)) if (!(t === void 0 || e.startsWith(":"))) if (t === null || t === !1) n.hasAttribute(e) && n.removeAttribute(e);
17
+ else {
18
+ let r = t == 1 ? "" : String(t);
19
+ n.getAttribute(e) !== r && n.setAttribute(e, r);
20
+ }
21
+ }
22
+ for (let [e, t] of Object.entries(a)) {
23
+ if (t === void 0 || !e.startsWith(":")) continue;
24
+ let r = e.slice(1);
25
+ Reflect.get(n, r) !== t && Reflect.set(n, r, t);
26
+ }
27
+ let o;
28
+ return n.replaceChildren(...i.filter((e) => e != null && e !== !1).map((r) => {
29
+ if (typeof r != "object") return document.createTextNode(String(r));
30
+ if (r instanceof Node) return r;
31
+ o ??= new Map([...n.children].flatMap((t) => {
32
+ let n = t.getAttribute(e);
33
+ return n == null ? [] : [[n, t]];
34
+ }));
35
+ let i = r.config[e], a = o.get(i);
36
+ return a && a.tagName.toLowerCase() === r.tag ? (o.delete(i), t(a, r.config, r.children)) : r.toElement();
37
+ })), n;
38
+ }
39
+ //#endregion
40
+ export { t as html };
41
+
42
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","names":[],"sources":["../src/html.ts"],"sourcesContent":["// prettier-ignore\ntype Alpha = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';\ntype ChildValue = Node | HtmlDeferredElement | string | number | bigint | false | null | undefined;\ntype AttrValue = string | number | bigint | boolean | null | undefined;\ntype TagMap = HTMLElementTagNameMap & HTMLElementDeprecatedTagNameMap;\n\ntype IfEquals<T, U, Y = unknown, N = never> =\n (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2 ? Y : N;\n\ntype ElementType<TElement> = TElement extends string\n ? TElement extends keyof TagMap\n ? TagMap[TElement]\n : HTMLElement\n : TElement extends CustomElementConstructor\n ? InstanceType<TElement>\n : TElement extends Element | Document | ShadowRoot\n ? TElement\n : never;\n\ntype ElementProps<TElement> = {\n readonly [P in keyof TElement & string as TElement[P] extends ((...args: any[]) => any) | null | undefined\n ? never\n : IfEquals<Record<P, TElement[P]>, Pick<TElement, P>, `:${P}`, never>]?: TElement[P];\n};\n\ntype ElementAttrs<TElement> = TElement extends string | CustomElementConstructor | Element\n ? {}\n : Readonly<Record<`${Alpha}${string}`, AttrValue>>;\n\nexport type HtmlConfig<TElement> = ElementAttrs<TElement> & ElementProps<ElementType<TElement>>;\nexport type HtmlConfigWithKey<TElement> = { readonly [DATA_KEY]: string } & HtmlConfig<TElement>;\n\nexport interface HtmlDeferredElement<TElement extends HTMLElement = HTMLElement> {\n readonly tag: string;\n readonly config: { readonly [DATA_KEY]: string } & HtmlConfig<TElement>;\n readonly children: readonly ChildValue[] | undefined;\n toElement: () => TElement;\n}\n\nconst DATA_KEY = 'data-key';\n\n/**\n * Create a deferred HTML element with a key.\n *\n * Final element creation is deferred until the element is used as a child,\n * when it can be determined if a new element should be created, or an existing\n * element with the same key and tag-name should be updated.\n */\nexport function html<const TElement extends keyof TagMap | (string & {}) | CustomElementConstructor>(\n el: TElement,\n config: HtmlConfigWithKey<TElement>,\n children?: readonly ChildValue[],\n): HtmlDeferredElement<ElementType<TElement>>;\n\n/**\n * Create or update an HTML element.\n */\nexport function html<\n TElement extends keyof TagMap | (string & {}) | CustomElementConstructor | Element | Document | ShadowRoot,\n>(el: TElement, attrs?: HtmlConfig<TElement>, children?: readonly ChildValue[]): ElementType<NoInfer<TElement>>;\nexport function html<\n TElement extends keyof TagMap | (string & {}) | CustomElementConstructor | Element | Document | ShadowRoot,\n>(el: TElement, children?: readonly ChildValue[]): ElementType<TElement>;\n\nexport function html(\n el: string | CustomElementConstructor | Element | Document | ShadowRoot,\n attrsOrChildren: readonly ChildValue[] | Readonly<Record<string, any>> = {},\n children: readonly ChildValue[] = [],\n): Node | HtmlDeferredElement {\n if (typeof el === 'function') {\n let name = customElements.getName(el);\n\n if (name == null) {\n name = `ce-${crypto.randomUUID()}`;\n customElements.define(name, el);\n }\n\n el = name;\n }\n\n let attrs: Readonly<Record<string, any>>;\n [attrs, children] = Array.isArray(attrsOrChildren)\n ? [{}, attrsOrChildren as readonly ChildValue[]]\n : [attrsOrChildren as Readonly<Record<string, any>>, children];\n\n if (typeof el === 'string') {\n return DATA_KEY in attrs\n ? {\n tag: el,\n config: attrs as HtmlDeferredElement['config'],\n children,\n toElement: () => html(document.createElement(el), attrs, children),\n }\n : html(document.createElement(el), attrs, children);\n }\n\n if ('setAttribute' in el) {\n for (const [name, rawValue] of Object.entries(attrs)) {\n if (rawValue === undefined || name.startsWith(':')) continue;\n\n if (rawValue === null || rawValue === false) {\n if (el.hasAttribute(name)) el.removeAttribute(name);\n } else {\n const value = rawValue == true ? '' : String(rawValue);\n if (el.getAttribute(name) !== value) el.setAttribute(name, value);\n }\n }\n }\n\n for (const [rawName, value] of Object.entries(attrs)) {\n if (value === undefined || !rawName.startsWith(':')) continue;\n const name = rawName.slice(1);\n if (Reflect.get(el, name) !== value) Reflect.set(el, name, value);\n }\n\n let keyElements: Map<string, Element> | undefined;\n\n el.replaceChildren(\n ...children\n .filter((child) => child != null && child !== false)\n .map((child) => {\n if (typeof child !== 'object') return document.createTextNode(String(child));\n if (child instanceof Node) return child;\n\n keyElements ??= new Map(\n [...el.children].flatMap((child) => {\n const key = child.getAttribute(DATA_KEY);\n return key == null ? [] : [[key, child] as const];\n }),\n );\n\n const key = child.config[DATA_KEY];\n const reused = keyElements.get(key);\n\n if (reused && reused.tagName.toLowerCase() === child.tag) {\n keyElements.delete(key);\n return html(reused, child.config, child.children);\n }\n\n return child.toElement();\n }),\n );\n\n return el;\n}\n"],"mappings":";AAuCA,IAAM,IAAW;AAyBjB,SAAgB,EACd,GACA,IAAyE,EAAE,EAC3E,IAAkC,EAAE,EACR;AAC5B,KAAI,OAAO,KAAO,YAAY;EAC5B,IAAI,IAAO,eAAe,QAAQ,EAAG;AAOrC,EALI,MACF,IAAO,MAAM,OAAO,YAAY,IAChC,eAAe,OAAO,GAAM,EAAG,GAGjC,IAAK;;CAGP,IAAI;AAKJ,KAJA,CAAC,GAAO,KAAY,MAAM,QAAQ,EAAgB,GAC9C,CAAC,EAAE,EAAE,EAAyC,GAC9C,CAAC,GAAkD,EAAS,EAE5D,OAAO,KAAO,SAChB,QAAO,KAAY,IACf;EACE,KAAK;EACL,QAAQ;EACR;EACA,iBAAiB,EAAK,SAAS,cAAc,EAAG,EAAE,GAAO,EAAS;EACnE,GACD,EAAK,SAAS,cAAc,EAAG,EAAE,GAAO,EAAS;AAGvD,KAAI,kBAAkB,GACpB;OAAK,IAAM,CAAC,GAAM,MAAa,OAAO,QAAQ,EAAM,CAC9C,aAAa,KAAA,KAAa,EAAK,WAAW,IAAI,EAElD,KAAI,MAAa,QAAQ,MAAa,IAChC,EAAG,aAAa,EAAK,IAAE,EAAG,gBAAgB,EAAK;OAC9C;GACL,IAAM,IAAQ,KAAY,IAAO,KAAK,OAAO,EAAS;AACtD,GAAI,EAAG,aAAa,EAAK,KAAK,KAAO,EAAG,aAAa,GAAM,EAAM;;;AAKvE,MAAK,IAAM,CAAC,GAAS,MAAU,OAAO,QAAQ,EAAM,EAAE;AACpD,MAAI,MAAU,KAAA,KAAa,CAAC,EAAQ,WAAW,IAAI,CAAE;EACrD,IAAM,IAAO,EAAQ,MAAM,EAAE;AAC7B,EAAI,QAAQ,IAAI,GAAI,EAAK,KAAK,KAAO,QAAQ,IAAI,GAAI,GAAM,EAAM;;CAGnE,IAAI;AA4BJ,QA1BA,EAAG,gBACD,GAAG,EACA,QAAQ,MAAU,KAAS,QAAQ,MAAU,GAAM,CACnD,KAAK,MAAU;AACd,MAAI,OAAO,KAAU,SAAU,QAAO,SAAS,eAAe,OAAO,EAAM,CAAC;AAC5E,MAAI,aAAiB,KAAM,QAAO;AAElC,QAAgB,IAAI,IAClB,CAAC,GAAG,EAAG,SAAS,CAAC,SAAS,MAAU;GAClC,IAAM,IAAM,EAAM,aAAa,EAAS;AACxC,UAAO,KAAO,OAAO,EAAE,GAAG,CAAC,CAAC,GAAK,EAAM,CAAU;IACjD,CACH;EAED,IAAM,IAAM,EAAM,OAAO,IACnB,IAAS,EAAY,IAAI,EAAI;AAOnC,SALI,KAAU,EAAO,QAAQ,aAAa,KAAK,EAAM,OACnD,EAAY,OAAO,EAAI,EAChB,EAAK,GAAQ,EAAM,QAAQ,EAAM,SAAS,IAG5C,EAAM,WAAW;GACxB,CACL,EAEM"}
package/dist/index.js CHANGED
@@ -1,257 +1,9 @@
1
- //#region src/internal/callbacks.ts
2
- function e() {
3
- let e = /* @__PURE__ */ new Set(), t = {
4
- push: (t) => (e.add(t), () => e.delete(t)),
5
- run: ({ clear: n = !1 } = {}) => {
6
- let r = [], i = [...e];
7
- for (let e of i) try {
8
- e();
9
- } catch (e) {
10
- r.push(e);
11
- }
12
- if (n && e.clear(), r.length > 0) throw AggregateError(r);
13
- return t;
14
- },
15
- clear: () => e.clear()
16
- };
17
- return t;
18
- }
19
- //#endregion
20
- //#region src/internal/constants.ts
21
- var t = Symbol(), n = Symbol();
22
- //#endregion
23
- //#region src/component.ts
24
- Object.assign(window, { [n]: [] });
25
- function r(r, i = {}) {
26
- return class extends HTMLElement {
27
- #e = e();
28
- #t = e();
29
- #n = i;
30
- #r;
31
- #i = !1;
32
- constructor() {
33
- super();
34
- let e = this.#r = {};
35
- if (this.#n.props) {
36
- let t = this.#n.props;
37
- for (let [n, r] of Object.entries(t)) {
38
- if (n in this) continue;
39
- let t = r(e[n] = this.#a(void 0), this);
40
- Object.defineProperty(this, n, t);
41
- }
42
- }
43
- }
44
- connectedCallback() {
45
- let e = this.attachShadow({
46
- ...this.#n.shadow,
47
- mode: this.#n.shadow?.mode ?? "open"
48
- });
49
- try {
50
- window[n].push({
51
- host: this,
52
- onDisconnect: this.#e,
53
- onSetRef: this.#t,
54
- useRef: this.#a
55
- }), r(e, this.#r);
56
- } finally {
57
- window[n].pop();
58
- }
59
- this.#t.run();
60
- }
61
- disconnectedCallback() {
62
- this.#t.clear(), this.#e.run({ clear: !0 });
63
- }
64
- #a = (e, n) => {
65
- let r = this.#o, i = e;
66
- return {
67
- [t]: !0,
68
- get value() {
69
- return i;
70
- },
71
- set value(e) {
72
- e !== i && (i = e, n?.(i), r());
73
- }
74
- };
75
- };
76
- #o = () => {
77
- this.#i || (this.#i = !0, queueMicrotask(() => {
78
- this.#i = !1, this.#t.run();
79
- }));
80
- };
81
- };
82
- }
83
- //#endregion
84
- //#region src/hooks/core.ts
85
- function i(e, t) {
86
- return s().useRef(e, t);
87
- }
88
- function a(t, n) {
89
- let { onSetRef: r, onDisconnect: i } = s(), a = e(), o = () => a.run({ clear: !0 }), c;
90
- r.push(() => {
91
- let e = t.map((e) => e.value);
92
- if (c?.length === e.length && c?.every((t, n) => t === e[n])) return;
93
- c = e, o();
94
- let r = n(...c);
95
- r && a.push(() => r());
96
- }), i.push(o);
97
- }
98
- function o(...e) {
99
- if (e.length === 0) return {};
100
- let { host: t } = s(), n = Object.fromEntries(e.map((e) => [e, i(t.getAttribute(e), (n) => {
101
- n == null ? t.removeAttribute(e) : t.setAttribute(e, n);
102
- })])), r = new MutationObserver((e) => {
103
- for (let { attributeName: r } of e) r != null && Object.hasOwn(n, r) && (n[r].value = t.getAttribute(r));
104
- });
105
- return a([], () => (r.observe(t, {
106
- attributeFilter: e,
107
- attributes: !0
108
- }), () => r.disconnect())), n;
109
- }
110
- function s() {
111
- let e = window[n].at(-1);
112
- if (!e) throw Error("hooks must be called inside a render function");
113
- return e;
114
- }
115
- //#endregion
116
- //#region src/hooks/loading.ts
117
- function c(e, t, { debounceMs: n } = {}) {
118
- let r = i({
119
- loading: !0,
120
- value: void 0,
121
- error: void 0
122
- }), o = !0;
123
- return a(e, () => (...e) => {
124
- let i = new AbortController();
125
- return Promise.race(o ? [Promise.resolve()] : [new Promise((e) => setTimeout(e, n)), new Promise((e) => i.signal.addEventListener("abort", e, { once: !0 }))]).then(() => {
126
- if (!i.signal.aborted) return t(i.signal, ...e);
127
- }).then((e) => {
128
- i.signal.aborted || (r.value = {
129
- loading: !1,
130
- value: e,
131
- error: void 0
132
- });
133
- }).catch((e) => {
134
- i.signal.aborted || (r.value = {
135
- loading: !1,
136
- value: void 0,
137
- error: e
138
- });
139
- }), o = !1, () => i.abort();
140
- }), a([], () => () => o = !0), r;
141
- }
142
- //#endregion
143
- //#region src/store.ts
144
- function l(t) {
145
- let n = e(), r = t;
146
- return {
147
- get state() {
148
- return r;
149
- },
150
- set state(e) {
151
- e !== r && (r = e, n.run());
152
- },
153
- subscribe: (e) => n.push(() => e(r))
154
- };
155
- }
156
- //#endregion
157
- //#region src/router.ts
158
- function u() {
159
- return d ??= f();
160
- }
161
- var d;
162
- function f() {
163
- let e = l({
164
- url: window.location.href,
165
- pathname: window.location.pathname,
166
- hash: window.location.hash,
167
- state: window.history.state
168
- }), t = () => {
169
- let t = e.state;
170
- t.url !== window.location.href && (t = {
171
- ...t,
172
- url: window.location.href,
173
- pathname: window.location.pathname,
174
- hash: window.location.hash
175
- }), t.state !== window.history.state && (t = {
176
- ...t,
177
- state: window.history.state
178
- }), e.state = t;
179
- };
180
- return Object.defineProperties(history, Object.fromEntries(["pushState", "replaceState"].map((e) => {
181
- let n = history[e].bind(history);
182
- return [e, {
183
- value: (...e) => {
184
- n(...e), t();
185
- },
186
- enumerable: !0,
187
- configurable: !0
188
- }];
189
- }))), window.addEventListener("popstate", t), e;
190
- }
191
- //#endregion
192
- //#region src/hooks/route.ts
193
- function p(e, { match: t = "prefix", source: n = "pathname" } = {}) {
194
- let r = t === "exact" ? /^$/u : t === "prefix" ? /^.*$/u : t, o = Array.isArray(e) ? e : [e], s = (e) => {
195
- let t = new URL(e)[n], i = o.find((e) => t.endsWith(e)) ?? null;
196
- return i == null ? null : (t = t.slice(i.length), t.match(r));
197
- }, c = i(window.location.href), l = i(s(window.location.href)), d = i(window.history.state);
198
- return a([], () => u().subscribe(({ url: e, state: t }) => {
199
- c.value = e, d.value = t;
200
- })), a([c], (e) => {
201
- l.value = s(e);
202
- }), [l, d];
203
- }
204
- //#endregion
205
- //#region src/hooks/store.ts
206
- function m(e, t = (e) => e, n) {
207
- let r = i(t(e.state), n && ((t) => n(e, t)));
208
- return a([], () => e.subscribe((e) => r.value = t(e))), r;
209
- }
210
- //#endregion
211
- //#region src/html.ts
212
- var h = "data-key";
213
- function g(e, t = {}, n = []) {
214
- if (typeof e == "function") {
215
- let t = customElements.getName(e);
216
- t ?? (t = `ce-${crypto.randomUUID()}`, customElements.define(t, e)), e = t;
217
- }
218
- let r;
219
- if ([r, n] = Array.isArray(t) ? [{}, t] : [t, n], typeof e == "string") {
220
- let { [h]: t, ...i } = r;
221
- return typeof t == "string" ? {
222
- tag: e,
223
- key: t,
224
- attrs: i,
225
- children: n,
226
- resolve: () => g(document.createElement(e), i, n)
227
- } : g(document.createElement(e), r, n);
228
- }
229
- if ("setAttribute" in e) {
230
- let t = new Set(e.getAttributeNames());
231
- for (let [n, i] of Object.entries(r)) {
232
- if (t?.delete(n), i == null || i == 0) continue;
233
- let r = i == 1 ? "" : String(i);
234
- e.getAttribute(n) !== r && e.setAttribute(n, r);
235
- }
236
- for (let n of t) e.removeAttribute(n);
237
- }
238
- let i;
239
- return e.replaceChildren(...n.filter((e) => e != null && e !== !1).map((t) => {
240
- if (typeof t != "object") return document.createTextNode(String(t));
241
- if (t instanceof Node) return t;
242
- i ??= new Map([...e.children].flatMap((e) => {
243
- let t = e.getAttribute(h);
244
- return t == null ? [] : [[t, e]];
245
- }));
246
- let n = i.get(t.key);
247
- return n && n.tagName.toLowerCase() === t.tag ? (i.delete(t.key), g(n, t.attrs, t.children)) : t.resolve();
248
- })), e;
249
- }
250
- function _(e) {
251
- let t = g("template");
252
- return t.innerHTML = e, t.content;
253
- }
254
- //#endregion
255
- export { l as createStore, r as defineComponent, u as getRouter, g as h, g as html, _ as parseHTML, o as useAttributes, a as useEffect, c as useLoading, i as useRef, p as useRoute, m as useStore };
256
-
257
- //# sourceMappingURL=index.js.map
1
+ import { defineComponent as e } from "./component.js";
2
+ import { useAttributes as t, useEffect as n, useRef as r } from "./hooks/core.js";
3
+ import { useLoading as i } from "./hooks/loading.js";
4
+ import { createStore as a } from "./store.js";
5
+ import { getRouter as o } from "./router.js";
6
+ import { useStore as s } from "./hooks/store.js";
7
+ import { useRoute as c } from "./hooks/route.js";
8
+ import { html as l } from "./html.js";
9
+ export { a as createStore, e as defineComponent, o as getRouter, l as h, l as html, t as useAttributes, n as useEffect, i as useLoading, r as useRef, c as useRoute, s as useStore };
@@ -0,0 +1,22 @@
1
+ //#region src/internal/callbacks.ts
2
+ function e() {
3
+ let e = /* @__PURE__ */ new Set(), t = {
4
+ push: (t) => (e.add(t), () => e.delete(t)),
5
+ run: ({ clear: n = !1 } = {}) => {
6
+ let r = [], i = [...e];
7
+ for (let e of i) try {
8
+ e();
9
+ } catch (e) {
10
+ r.push(e);
11
+ }
12
+ if (n && e.clear(), r.length > 0) throw AggregateError(r);
13
+ return t;
14
+ },
15
+ clear: () => e.clear()
16
+ };
17
+ return t;
18
+ }
19
+ //#endregion
20
+ export { e as createCallbacks };
21
+
22
+ //# sourceMappingURL=callbacks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callbacks.js","names":[],"sources":["../../src/internal/callbacks.ts"],"sourcesContent":["export interface Callbacks {\n readonly push: (callback: () => void) => () => void;\n readonly run: (options?: { readonly clear?: boolean }) => void;\n readonly clear: () => void;\n}\n\nexport function createCallbacks(): Callbacks {\n const callbacks = new Set<() => void>();\n\n const self: Callbacks = {\n push: (callback) => {\n callbacks.add(callback);\n return () => callbacks.delete(callback);\n },\n run: ({ clear = false } = {}) => {\n const errors: unknown[] = [];\n const callbacksCopy = [...callbacks];\n\n for (const callback of callbacksCopy) {\n try {\n callback();\n } catch (error: unknown) {\n errors.push(error);\n }\n }\n\n if (clear) callbacks.clear();\n if (errors.length > 0) throw new AggregateError(errors);\n return self;\n },\n clear: () => callbacks.clear(),\n };\n\n return self;\n}\n"],"mappings":";AAMA,SAAgB,IAA6B;CAC3C,IAAM,oBAAY,IAAI,KAAiB,EAEjC,IAAkB;EACtB,OAAO,OACL,EAAU,IAAI,EAAS,QACV,EAAU,OAAO,EAAS;EAEzC,MAAM,EAAE,WAAQ,OAAU,EAAE,KAAK;GAC/B,IAAM,IAAoB,EAAE,EACtB,IAAgB,CAAC,GAAG,EAAU;AAEpC,QAAK,IAAM,KAAY,EACrB,KAAI;AACF,OAAU;YACH,GAAgB;AACvB,MAAO,KAAK,EAAM;;AAKtB,OADI,KAAO,EAAU,OAAO,EACxB,EAAO,SAAS,EAAG,OAAU,eAAe,EAAO;AACvD,UAAO;;EAET,aAAa,EAAU,OAAO;EAC/B;AAED,QAAO"}
@@ -0,0 +1,6 @@
1
+ //#region src/internal/constants.ts
2
+ var e = Symbol(), t = Symbol();
3
+ //#endregion
4
+ export { e as $$ref, t as $$renderContextStack };
5
+
6
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","names":[],"sources":["../../src/internal/constants.ts"],"sourcesContent":["export const $$ref = Symbol();\nexport const $$renderContextStack = Symbol();\n"],"mappings":";AAAA,IAAa,IAAQ,QAAQ,EAChB,IAAuB,QAAQ"}
package/dist/router.d.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import { type Store } from './store.ts';
2
2
  export interface RouterState {
3
- readonly url: string;
4
3
  readonly pathname: string;
5
4
  readonly hash: string;
6
- readonly state: unknown;
7
5
  }
8
6
  /**
9
7
  * Get a router {@link Store} that is updated when the client side route (aka:
package/dist/router.js ADDED
@@ -0,0 +1,35 @@
1
+ import { createStore as e } from "./store.js";
2
+ //#region src/router.ts
3
+ function t() {
4
+ return n ??= r();
5
+ }
6
+ var n;
7
+ function r() {
8
+ let t = e({
9
+ pathname: window.location.pathname,
10
+ hash: window.location.hash
11
+ }), n = () => {
12
+ let e = t.state;
13
+ t.state.pathname !== window.location.pathname && (e = {
14
+ ...e,
15
+ pathname: window.location.pathname
16
+ }), t.state.hash !== window.location.hash && (e = {
17
+ ...e,
18
+ hash: window.location.hash
19
+ }), t.state = e;
20
+ };
21
+ return Object.defineProperties(history, Object.fromEntries(["pushState", "replaceState"].map((e) => {
22
+ let t = history[e].bind(history);
23
+ return [e, {
24
+ value: (...e) => {
25
+ t(...e), n();
26
+ },
27
+ enumerable: !0,
28
+ configurable: !0
29
+ }];
30
+ }))), window.addEventListener("popstate", n), t;
31
+ }
32
+ //#endregion
33
+ export { t as getRouter };
34
+
35
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","names":[],"sources":["../src/router.ts"],"sourcesContent":["import { createStore, type Store } from './store.ts';\n\nexport interface RouterState {\n readonly pathname: string;\n readonly hash: string;\n}\n\n/**\n * Get a router {@link Store} that is updated when the client side route (aka:\n * `History`) changes. This method returns the same store every time it is\n * called (ie. a singleton).\n */\nexport function getRouter(): Store<RouterState> {\n return (singleton ??= createRouter());\n}\n\nlet singleton: Store<RouterState> | undefined;\n\nfunction createRouter(): Store<RouterState> {\n const store = createStore<RouterState>({\n pathname: window.location.pathname,\n hash: window.location.hash,\n });\n\n const onUpdate = (): void => {\n let state = store.state;\n\n if (store.state.pathname !== window.location.pathname) {\n state = { ...state, pathname: window.location.pathname };\n }\n\n if (store.state.hash !== window.location.hash) {\n state = { ...state, hash: window.location.hash };\n }\n\n store.state = state;\n };\n\n Object.defineProperties(\n history,\n Object.fromEntries(\n (['pushState', 'replaceState'] as const).map((method: 'pushState' | 'replaceState') => {\n const original = history[method].bind(history) as History['pushState'] & History['replaceState'];\n const descriptor: PropertyDescriptor = {\n value: (...args: Parameters<typeof original>) => {\n original(...args);\n onUpdate();\n },\n enumerable: true,\n configurable: true,\n };\n\n return [method, descriptor];\n }),\n ),\n );\n\n window.addEventListener('popstate', onUpdate);\n\n return store;\n}\n"],"mappings":";;AAYA,SAAgB,IAAgC;AAC9C,QAAQ,MAAc,GAAc;;AAGtC,IAAI;AAEJ,SAAS,IAAmC;CAC1C,IAAM,IAAQ,EAAyB;EACrC,UAAU,OAAO,SAAS;EAC1B,MAAM,OAAO,SAAS;EACvB,CAAC,EAEI,UAAuB;EAC3B,IAAI,IAAQ,EAAM;AAUlB,EARI,EAAM,MAAM,aAAa,OAAO,SAAS,aAC3C,IAAQ;GAAE,GAAG;GAAO,UAAU,OAAO,SAAS;GAAU,GAGtD,EAAM,MAAM,SAAS,OAAO,SAAS,SACvC,IAAQ;GAAE,GAAG;GAAO,MAAM,OAAO,SAAS;GAAM,GAGlD,EAAM,QAAQ;;AAwBhB,QArBA,OAAO,iBACL,SACA,OAAO,YACJ,CAAC,aAAa,eAAe,CAAW,KAAK,MAAyC;EACrF,IAAM,IAAW,QAAQ,GAAQ,KAAK,QAAQ;AAU9C,SAAO,CAAC,GAAQ;GARd,QAAQ,GAAG,MAAsC;AAE/C,IADA,EAAS,GAAG,EAAK,EACjB,GAAU;;GAEZ,YAAY;GACZ,cAAc;GAGA,CAAW;GAC3B,CACH,CACF,EAED,OAAO,iBAAiB,YAAY,EAAS,EAEtC"}
package/dist/store.js ADDED
@@ -0,0 +1,18 @@
1
+ import { createCallbacks as e } from "./internal/callbacks.js";
2
+ //#region src/store.ts
3
+ function t(t) {
4
+ let n = e(), r = t;
5
+ return {
6
+ get state() {
7
+ return r;
8
+ },
9
+ set state(e) {
10
+ e !== r && (r = e, n.run());
11
+ },
12
+ subscribe: (e) => n.push(() => e(r))
13
+ };
14
+ }
15
+ //#endregion
16
+ export { t as createStore };
17
+
18
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","names":[],"sources":["../src/store.ts"],"sourcesContent":["import { createCallbacks } from './internal/callbacks.ts';\n\nexport interface Store<TState> {\n state: TState;\n subscribe: (callback: (state: TState) => void) => () => void;\n}\n\n/** Create a new store containing an observable state. */\nexport function createStore<TState>(initialState: TState): Store<TState> {\n const callbacks = createCallbacks();\n let state = initialState;\n\n return {\n get state() {\n return state;\n },\n set state(newState) {\n if (newState === state) return;\n state = newState;\n callbacks.run();\n },\n subscribe: (callback) => callbacks.push(() => callback(state)),\n };\n}\n"],"mappings":";;AAQA,SAAgB,EAAoB,GAAqC;CACvE,IAAM,IAAY,GAAiB,EAC/B,IAAQ;AAEZ,QAAO;EACL,IAAI,QAAQ;AACV,UAAO;;EAET,IAAI,MAAM,GAAU;AACd,SAAa,MACjB,IAAQ,GACR,EAAU,KAAK;;EAEjB,YAAY,MAAa,EAAU,WAAW,EAAS,EAAM,CAAC;EAC/D"}
package/package.json CHANGED
@@ -1,12 +1,10 @@
1
1
  {
2
2
  "name": "@seahax/elemental",
3
- "version": "0.1.0",
4
- "private": false,
5
- "description": "Extremely small reactive web components library.",
3
+ "version": "0.2.0",
4
+ "description": "Functional, reactive, web component base library.",
6
5
  "repository": {
7
6
  "type": "git",
8
- "url": "git+https://github.com/seahax/workshop.git",
9
- "directory": "packages/elemental"
7
+ "url": "git+https://github.com/seahax/elemental.git"
10
8
  },
11
9
  "license": "Unlicense",
12
10
  "type": "module",
@@ -22,19 +20,22 @@
22
20
  ],
23
21
  "scripts": {
24
22
  "build": "vite build && tsc -b -f tsconfig.lib.json",
25
- "clean": "rm -rf dist",
26
- "diff": "npm run build >/dev/null && npm diff 'dist/**.mjs' 'dist/**.cjs' 'dist/**.js' 'dist/**.d.ts'",
27
- "start": "vite dev",
28
- "test": "eslint src && vitest run --coverage"
23
+ "prepare": "npm run build && npm test",
24
+ "test": "eslint src"
29
25
  },
30
26
  "devDependencies": {
31
- "@seahax/eslint": "^0.2.27",
32
- "@seahax/vitest": "^0.1.8",
33
- "@vitest/coverage-v8": "^4.0.16",
34
- "eslint": "^9.39.4",
35
- "vite": "^8.0.8",
36
- "vite-bundle-analyzer": "^1.3.7",
37
- "vitest": "^4.1.4"
27
+ "@eslint/js": "^10.0.1",
28
+ "@seahax/eslint-progress": "^0.1.17",
29
+ "@types/node": "^25.6.0",
30
+ "eslint": "^10.3.0",
31
+ "eslint-config-prettier": "^10.1.8",
32
+ "eslint-plugin-prettier": "^5.5.5",
33
+ "eslint-plugin-regexp": "^3.1.0",
34
+ "eslint-plugin-simple-import-sort": "^13.0.0",
35
+ "jiti": "^2.6.1",
36
+ "typescript-eslint": "^8.59.1",
37
+ "vite": "^8.0.10",
38
+ "vite-bundle-analyzer": "^1.3.8"
38
39
  },
39
40
  "publishConfig": {
40
41
  "access": "public"
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":["#onDisconnect","#onSetRef","#options","#refs","#useRef","#notify","#notifying"],"sources":["../src/internal/callbacks.ts","../src/internal/constants.ts","../src/component.ts","../src/hooks/core.ts","../src/hooks/loading.ts","../src/store.ts","../src/router.ts","../src/hooks/route.ts","../src/hooks/store.ts","../src/html.ts"],"sourcesContent":["export interface Callbacks {\n readonly push: (callback: () => void) => () => void;\n readonly run: (options?: { readonly clear?: boolean }) => void;\n readonly clear: () => void;\n}\n\nexport function createCallbacks(): Callbacks {\n const callbacks = new Set<() => void>();\n\n const self: Callbacks = {\n push: (callback) => {\n callbacks.add(callback);\n return () => callbacks.delete(callback);\n },\n run: ({ clear = false } = {}) => {\n const errors: unknown[] = [];\n const callbacksCopy = [...callbacks];\n\n for (const callback of callbacksCopy) {\n try {\n callback();\n } catch (error: unknown) {\n errors.push(error);\n }\n }\n\n if (clear) callbacks.clear();\n if (errors.length > 0) throw new AggregateError(errors);\n return self;\n },\n clear: () => callbacks.clear(),\n };\n\n return self;\n}\n","export const $$ref = Symbol();\nexport const $$renderContextStack = Symbol();\n","import { type Callbacks, createCallbacks } from './internal/callbacks.ts';\nimport { $$ref, $$renderContextStack } from './internal/constants.ts';\n\ntype SafeProps<TProps> = any extends any\n ? { [P in keyof TProps as P extends keyof HTMLElement ? never : P]: TProps[P] }\n : never;\n\nexport interface ComponentConstructor<TProps extends object> {\n new (): ComponentWithProps<TProps>;\n}\n\nexport interface ComponentOptions<TProps extends object> {\n readonly shadow?: Partial<ShadowRootInit>;\n readonly props?: ComponentPropDescriptors<TProps>;\n}\n\nexport type ComponentPropDescriptors<TProps extends object> = {\n readonly [P in keyof SafeProps<TProps>]: ComponentPropDescriptorFactory<TProps[P]>;\n};\n\nexport type ComponentPropDescriptorFactory<TType> = (\n ref: Ref<TType | undefined>,\n host: HTMLElement,\n) => ComponentPropDescriptor<TType>;\n\nexport interface ComponentPropDescriptor<T> extends Omit<PropertyDescriptor, 'value' | 'get' | 'set'> {\n get(): T;\n set?(value: T): void;\n}\n\nexport type ComponentWithProps<TProps extends object> = HTMLElement & {\n -readonly [P in keyof SafeProps<TProps>]: TProps[P];\n};\n\nexport type ComponentShadowRoot<TProps extends object> = Omit<ShadowRoot, 'host'> & {\n readonly host: ComponentWithProps<TProps>;\n};\n\nexport type ComponentPropRefs<TProps extends object> = {\n readonly [P in keyof SafeProps<TProps>]: Ref<TProps[P] | undefined>;\n};\n\nexport interface Ref<T> extends ReadonlyRef<T> {\n value: T;\n}\n\nexport interface ReadonlyRef<T> {\n /** @hidden */\n [$$ref]: unknown;\n readonly value: T;\n}\n\nexport type RefValues<T> = T extends readonly any[]\n ? { [K in keyof T]: T[K] extends ReadonlyRef<infer V> ? V : never }\n : never;\n\ndeclare global {\n interface Window {\n readonly [$$renderContextStack]: {\n readonly host: HTMLElement;\n readonly onDisconnect: Callbacks;\n readonly onSetRef: Callbacks;\n readonly useRef: <T>(initialValue: T, onChange?: (value: T) => void) => Ref<T>;\n }[];\n }\n}\n\nObject.assign(window, { [$$renderContextStack]: [] });\n\n/** Define a custom `HTMLElement` that is functional and reactive. */\nexport function defineComponent<TProps extends object = {}>(\n render: (shadow: ComponentShadowRoot<TProps>, props: ComponentPropRefs<TProps>) => void,\n options?: ComponentOptions<TProps>,\n): ComponentConstructor<TProps>;\nexport function defineComponent(\n render: (\n shadow: ComponentShadowRoot<Record<string, unknown>>,\n props: ComponentPropRefs<Record<string, unknown>>,\n ) => void,\n options: ComponentOptions<Record<string, unknown>> = {},\n): new () => HTMLElement {\n return class extends HTMLElement {\n readonly #onDisconnect = createCallbacks();\n readonly #onSetRef = createCallbacks();\n readonly #options = options;\n readonly #refs: ComponentPropRefs<Record<string, unknown>>;\n #notifying = false;\n\n constructor() {\n super();\n const refs: Record<string, Ref<unknown>> = (this.#refs = {});\n\n if (this.#options.props) {\n const descriptors = this.#options.props;\n\n for (const [key, getDescriptor] of Object.entries(descriptors)) {\n if (key in this) continue;\n const ref = (refs[key] = this.#useRef<any>(undefined));\n const descriptor = getDescriptor(ref, this);\n Object.defineProperty(this, key, descriptor);\n }\n }\n }\n\n protected connectedCallback(): void {\n const shadow = this.attachShadow({ ...this.#options.shadow, mode: this.#options.shadow?.mode ?? 'open' });\n\n try {\n window[$$renderContextStack].push({\n host: this,\n onDisconnect: this.#onDisconnect,\n onSetRef: this.#onSetRef,\n useRef: this.#useRef,\n });\n\n render(shadow as ComponentShadowRoot<Record<string, unknown>>, this.#refs);\n } finally {\n window[$$renderContextStack].pop();\n }\n\n this.#onSetRef.run();\n }\n\n protected disconnectedCallback(): void {\n this.#onSetRef.clear();\n this.#onDisconnect.run({ clear: true });\n }\n\n readonly #useRef = <T>(initialValue: T, onChange?: (value: T) => void): Ref<T> => {\n const notify = this.#notify;\n let value = initialValue;\n\n return {\n [$$ref]: true,\n get value() {\n return value;\n },\n set value(newValue) {\n if (newValue === value) return;\n value = newValue;\n onChange?.(value);\n notify();\n },\n };\n };\n\n readonly #notify = (): void => {\n if (this.#notifying) return;\n this.#notifying = true;\n\n queueMicrotask(() => {\n this.#notifying = false;\n this.#onSetRef.run();\n });\n };\n };\n}\n","import { type ReadonlyRef, type Ref, type RefValues } from '../component.ts';\nimport { createCallbacks } from '../internal/callbacks.ts';\nimport { $$renderContextStack } from '../internal/constants.ts';\n\n/** Create an observable value. */\nexport function useRef<T>(initialValue: T, onChange?: (value: T) => void): Ref<T> {\n return getHookContext().useRef(initialValue, onChange);\n}\n\n/** React to observable (reference) changes. */\nexport function useEffect<const TDeps extends readonly ReadonlyRef<any>[]>(\n deps: TDeps,\n callback: (...values: RefValues<TDeps>) => (() => void) | void,\n): void {\n const { onSetRef, onDisconnect } = getHookContext();\n const cleanupCallback = createCallbacks();\n const cleanup = (): void => cleanupCallback.run({ clear: true });\n let values: any[] | undefined;\n\n onSetRef.push((): void => {\n const newValues = deps.map((dep) => dep.value);\n if (values?.length === newValues.length && values?.every((value, i) => value === newValues[i])) return;\n values = newValues;\n cleanup();\n const maybeCleanup = callback(...(values as any));\n if (maybeCleanup) cleanupCallback.push(() => maybeCleanup());\n });\n\n onDisconnect.push(cleanup);\n}\n\n/** Observe attribute changes. */\nexport function useAttributes<TName extends string>(...names: TName[]): Readonly<Record<TName, Ref<string | null>>> {\n if (names.length === 0) return {} as any;\n const { host } = getHookContext();\n\n const refs = Object.fromEntries(\n names.map((name) => [\n name,\n useRef(host.getAttribute(name), (value) => {\n if (value == null) host.removeAttribute(name);\n else host.setAttribute(name, value);\n }),\n ]),\n );\n\n const observer = new MutationObserver((mutation) => {\n for (const { attributeName } of mutation) {\n if (attributeName != null && Object.hasOwn(refs, attributeName)) {\n refs[attributeName]!.value = host.getAttribute(attributeName);\n }\n }\n });\n\n useEffect([], () => {\n observer.observe(host, { attributeFilter: names, attributes: true });\n return () => observer.disconnect();\n });\n\n return refs as Readonly<Record<TName, Ref<string | null>>>;\n}\n\nfunction getHookContext(): (typeof window)[typeof $$renderContextStack][0] {\n const context = window[$$renderContextStack].at(-1);\n if (!context) throw new Error('hooks must be called inside a render function');\n return context;\n}\n","import type { ReadonlyRef, Ref, RefValues } from '../component.ts';\nimport { useEffect, useRef } from './core.ts';\n\nexport interface LoadingValue<TValue> {\n readonly loading: boolean;\n readonly value: TValue | undefined;\n readonly error: unknown;\n}\n\nexport interface LoadingOptions {\n readonly debounceMs?: number;\n}\n\n/** Load data asynchronously. */\nexport function useLoading<const TDeps extends readonly ReadonlyRef<any>[], TValue>(\n deps: TDeps,\n callback: (signal: AbortSignal, ...values: RefValues<TDeps>) => Promise<TValue>,\n { debounceMs }: LoadingOptions = {},\n): Ref<LoadingValue<TValue>> {\n const ref = useRef<LoadingValue<TValue>>({ loading: true, value: undefined, error: undefined });\n let skipDebounce = true;\n\n useEffect(deps, () => (...values) => {\n const ac = new AbortController();\n\n Promise.race(\n skipDebounce\n ? [Promise.resolve()]\n : [\n new Promise((resolve) => setTimeout(resolve, debounceMs)),\n new Promise((resolve) => ac.signal.addEventListener('abort', resolve, { once: true })),\n ],\n )\n .then(() => {\n if (ac.signal.aborted) return;\n return callback(ac.signal, ...(values as any));\n })\n .then((value) => {\n if (ac.signal.aborted) return;\n ref.value = { loading: false, value, error: undefined };\n })\n .catch((error: unknown) => {\n if (ac.signal.aborted) return;\n ref.value = { loading: false, value: undefined, error };\n });\n\n skipDebounce = false;\n return () => ac.abort();\n });\n\n // Skip the debounce again if the component unmounts.\n useEffect([], () => () => (skipDebounce = true));\n\n return ref;\n}\n","import { createCallbacks } from './internal/callbacks.ts';\n\nexport interface Store<TState> {\n state: TState;\n subscribe: (callback: (state: TState) => void) => () => void;\n}\n\n/** Create a new store containing an observable state. */\nexport function createStore<TState>(initialState: TState): Store<TState> {\n const callbacks = createCallbacks();\n let state = initialState;\n\n return {\n get state() {\n return state;\n },\n set state(newState) {\n if (newState === state) return;\n state = newState;\n callbacks.run();\n },\n subscribe: (callback) => callbacks.push(() => callback(state)),\n };\n}\n","import { createStore, type Store } from './store.ts';\n\nexport interface RouterState {\n readonly url: string;\n readonly pathname: string;\n readonly hash: string;\n readonly state: unknown;\n}\n\n/**\n * Get a router {@link Store} that is updated when the client side route (aka:\n * `History`) changes. This method returns the same store every time it is\n * called (ie. a singleton).\n */\nexport function getRouter(): Store<RouterState> {\n return (singleton ??= createRouter());\n}\n\nlet singleton: Store<RouterState> | undefined;\n\nfunction createRouter(): Store<RouterState> {\n const store = createStore<RouterState>({\n url: window.location.href,\n pathname: window.location.pathname,\n hash: window.location.hash,\n state: window.history.state,\n });\n\n const onUpdate = (): void => {\n let state = store.state;\n\n if (state.url !== window.location.href) {\n state = {\n ...state,\n url: window.location.href,\n pathname: window.location.pathname,\n hash: window.location.hash,\n };\n }\n\n if (state.state !== window.history.state) {\n state = { ...state, state: window.history.state };\n }\n\n store.state = state;\n };\n\n Object.defineProperties(\n history,\n Object.fromEntries(\n (['pushState', 'replaceState'] as const).map((method: 'pushState' | 'replaceState') => {\n const original = history[method].bind(history) as History['pushState'] & History['replaceState'];\n const descriptor: PropertyDescriptor = {\n value: (...args: Parameters<typeof original>) => {\n original(...args);\n onUpdate();\n },\n enumerable: true,\n configurable: true,\n };\n\n return [method, descriptor];\n }),\n ),\n );\n\n window.addEventListener('popstate', onUpdate);\n\n return store;\n}\n","import type { Ref } from '../component.ts';\nimport { getRouter } from '../router.ts';\nimport { useEffect, useRef } from './core.ts';\n\nexport interface RouteOptions {\n readonly match?: 'prefix' | 'exact' | RegExp;\n readonly source?: 'pathname' | 'hash';\n}\n\nexport type RouteMatchArray = readonly [string, ...string[]] & { readonly groups: Record<string, string> };\n\n/** Observe route (window.history) changes. */\nexport function useRoute(\n path: string | readonly string[],\n { match = 'prefix', source = 'pathname' }: RouteOptions = {},\n): [match: Ref<RouteMatchArray | null>, state: Ref<unknown>] {\n const matchRx = match === 'exact' ? /^$/u : match === 'prefix' ? /^.*$/u : match;\n const paths = Array.isArray(path) ? (path as readonly string[]) : [path as string];\n\n const getMatch = (url: string): RouteMatchArray | null => {\n let value = new URL(url)[source];\n const prefix = paths.find((path) => value.endsWith(path)) ?? null;\n if (prefix == null) return null;\n value = value.slice(prefix.length);\n return value.match(matchRx) as RouteMatchArray | null;\n };\n\n const refUrl = useRef<string>(window.location.href);\n const refMatch = useRef<RouteMatchArray | null>(getMatch(window.location.href));\n const refState = useRef<unknown>(window.history.state);\n\n useEffect([], () => {\n return getRouter().subscribe(({ url, state }) => {\n refUrl.value = url;\n refState.value = state;\n });\n });\n\n useEffect([refUrl], (url) => {\n refMatch.value = getMatch(url);\n });\n\n return [refMatch, refState];\n}\n","import type { ReadonlyRef, Ref } from '../component.ts';\nimport type { Store } from '../store.ts';\nimport { useEffect, useRef } from './core.ts';\n\n/** Observe store (external state) changes. */\nexport function useStore<TState, TValue = TState>(\n store: Store<TState>,\n select: (state: TState) => TValue,\n mutate: (store: Store<TState>, value: TValue) => void,\n): Ref<TValue>;\nexport function useStore<TState, TValue = TState>(\n store: Store<TState>,\n select?: (state: TState) => TValue,\n): ReadonlyRef<TValue>;\nexport function useStore<TState, TValue = TState>(\n store: Store<TState>,\n select: (state: TState) => TValue = (state) => state as unknown as TValue,\n mutate?: (store: Store<TState>, value: TValue) => void,\n): Ref<TValue> {\n const ref = useRef(select(store.state), mutate && ((value) => mutate(store, value)));\n useEffect([], () => store.subscribe((state) => (ref.value = select(state))));\n return ref;\n}\n","type TagMap = HTMLElementTagNameMap & HTMLElementDeprecatedTagNameMap;\ntype TagType<TTag extends string> = TTag extends keyof TagMap ? TagMap[TTag] : HTMLElement;\ntype ChildValue = Node | HtmlDeferred | string | number | bigint | false | null | undefined;\ntype AttrValue = string | number | bigint | boolean | null | undefined;\n\nexport interface HtmlDeferred<TTag extends keyof TagMap | (string & {}) = string> {\n readonly tag: TTag;\n readonly key: string;\n readonly attrs: Readonly<Record<string, AttrValue>>;\n readonly children: readonly ChildValue[] | undefined;\n resolve: () => TagType<TTag>;\n}\n\nconst DATA_KEY = 'data-key';\n\n/**\n * Create a deferred HTML element with a key. Final element creation is\n * deferred until the element is used as a child, when it can be determined if\n * a new element should be created, or an existing element with the same key\n * should be updated.\n */\nexport function html<const TTag extends keyof TagMap | (string & {})>(\n tag: TTag | CustomElementConstructor,\n attrs: { readonly $key: string } & Readonly<Record<string, AttrValue>>,\n children?: readonly ChildValue[],\n): HtmlDeferred<TTag>;\n\n/** Create an HTML element with attributes and children. */\nexport function html<const TTag extends keyof TagMap | (string & {})>(\n tag: TTag | CustomElementConstructor,\n attrs?: Readonly<Record<string, AttrValue>>,\n children?: readonly ChildValue[],\n): TagType<TTag>;\n\n/** Create an HTML element with children. */\nexport function html<const TTag extends keyof TagMap | (string & {})>(\n tag: TTag | CustomElementConstructor,\n children?: readonly ChildValue[],\n): TagType<TTag>;\n\n/** Update element attributes and replace children. */\nexport function html<TElement extends Element | Document | ShadowRoot>(\n element: TElement,\n attrs?: TElement extends { setAttribute: (...args: any[]) => any } ? Readonly<Record<string, AttrValue>> : undefined,\n children?: readonly ChildValue[],\n): TElement;\n\n/** Replace element children. */\nexport function html<TElement extends Element | Document | ShadowRoot>(\n element: TElement,\n children?: readonly ChildValue[],\n): TElement;\n\nexport function html(\n el: string | CustomElementConstructor | Element | Document | ShadowRoot,\n attrsOrChildren: Readonly<Record<string, AttrValue>> | readonly ChildValue[] = {},\n children: readonly ChildValue[] = [],\n): Node | HtmlDeferred {\n if (typeof el === 'function') {\n let name = customElements.getName(el);\n\n if (name == null) {\n name = `ce-${crypto.randomUUID()}`;\n customElements.define(name, el);\n }\n\n el = name;\n }\n\n let attrs: Readonly<Record<string, AttrValue>>;\n [attrs, children] = Array.isArray(attrsOrChildren)\n ? [{}, attrsOrChildren as readonly ChildValue[]]\n : [attrsOrChildren as Readonly<Record<string, AttrValue>>, children];\n\n if (typeof el === 'string') {\n const { [DATA_KEY]: key, ...otherAttrs } = attrs;\n\n return typeof key === 'string'\n ? {\n tag: el,\n key,\n attrs: otherAttrs,\n children,\n resolve: () => html(document.createElement(el), otherAttrs, children),\n }\n : html(document.createElement(el), attrs, children);\n }\n\n if ('setAttribute' in el) {\n const oldAttrs = new Set(el.getAttributeNames());\n\n for (const [name, rawValue] of Object.entries(attrs)) {\n oldAttrs?.delete(name);\n if (rawValue == null || rawValue == false) continue;\n const value = rawValue == true ? '' : String(rawValue);\n if (el.getAttribute(name) === value) continue;\n el.setAttribute(name, value);\n }\n\n for (const name of oldAttrs) {\n el.removeAttribute(name);\n }\n }\n\n let keyElements: Map<string, Element> | undefined;\n\n el.replaceChildren(\n ...children\n .filter((child) => child != null && child !== false)\n .map((child) => {\n if (typeof child !== 'object') return document.createTextNode(String(child));\n if (child instanceof Node) return child;\n\n keyElements ??= new Map(\n [...el.children].flatMap((child) => {\n const key = child.getAttribute(DATA_KEY);\n return key == null ? [] : [[key, child] as const];\n }),\n );\n\n const reused = keyElements.get(child.key);\n\n if (reused && reused.tagName.toLowerCase() === child.tag) {\n keyElements.delete(child.key);\n return html(reused, child.attrs, child.children);\n }\n\n return child.resolve();\n }),\n );\n\n return el;\n}\n\n/** Parse a raw HTML string into a `DocumentFragment`. */\nexport function parseHTML(htmlString: string): DocumentFragment {\n const template = html('template');\n template.innerHTML = htmlString;\n return template.content;\n}\n"],"mappings":";AAMA,SAAgB,IAA6B;CAC3C,IAAM,oBAAY,IAAI,KAAiB,EAEjC,IAAkB;EACtB,OAAO,OACL,EAAU,IAAI,EAAS,QACV,EAAU,OAAO,EAAS;EAEzC,MAAM,EAAE,WAAQ,OAAU,EAAE,KAAK;GAC/B,IAAM,IAAoB,EAAE,EACtB,IAAgB,CAAC,GAAG,EAAU;AAEpC,QAAK,IAAM,KAAY,EACrB,KAAI;AACF,OAAU;YACH,GAAgB;AACvB,MAAO,KAAK,EAAM;;AAKtB,OADI,KAAO,EAAU,OAAO,EACxB,EAAO,SAAS,EAAG,OAAU,eAAe,EAAO;AACvD,UAAO;;EAET,aAAa,EAAU,OAAO;EAC/B;AAED,QAAO;;;;ACjCT,IAAa,IAAQ,QAAQ,EAChB,IAAuB,QAAQ;;;ACkE5C,OAAO,OAAO,QAAQ,GAAG,IAAuB,EAAE,EAAE,CAAC;AAOrD,SAAgB,EACd,GAIA,IAAqD,EAAE,EAChC;AACvB,QAAO,cAAc,YAAY;EAC/B,KAAyB,GAAiB;EAC1C,KAAqB,GAAiB;EACtC,KAAoB;EACpB;EACA,KAAa;EAEb,cAAc;AACZ,UAAO;GACP,IAAM,IAAsC,MAAA,IAAa,EAAE;AAE3D,OAAI,MAAA,EAAc,OAAO;IACvB,IAAM,IAAc,MAAA,EAAc;AAElC,SAAK,IAAM,CAAC,GAAK,MAAkB,OAAO,QAAQ,EAAY,EAAE;AAC9D,SAAI,KAAO,KAAM;KAEjB,IAAM,IAAa,EADN,EAAK,KAAO,MAAA,EAAkB,KAAA,EAAU,EACf,KAAK;AAC3C,YAAO,eAAe,MAAM,GAAK,EAAW;;;;EAKlD,oBAAoC;GAClC,IAAM,IAAS,KAAK,aAAa;IAAE,GAAG,MAAA,EAAc;IAAQ,MAAM,MAAA,EAAc,QAAQ,QAAQ;IAAQ,CAAC;AAEzG,OAAI;AAQF,IAPA,OAAO,GAAsB,KAAK;KAChC,MAAM;KACN,cAAc,MAAA;KACd,UAAU,MAAA;KACV,QAAQ,MAAA;KACT,CAAC,EAEF,EAAO,GAAwD,MAAA,EAAW;aAClE;AACR,WAAO,GAAsB,KAAK;;AAGpC,SAAA,EAAe,KAAK;;EAGtB,uBAAuC;AAErC,GADA,MAAA,EAAe,OAAO,EACtB,MAAA,EAAmB,IAAI,EAAE,OAAO,IAAM,CAAC;;EAGzC,MAAuB,GAAiB,MAA0C;GAChF,IAAM,IAAS,MAAA,GACX,IAAQ;AAEZ,UAAO;KACJ,IAAQ;IACT,IAAI,QAAQ;AACV,YAAO;;IAET,IAAI,MAAM,GAAU;AACd,WAAa,MACjB,IAAQ,GACR,IAAW,EAAM,EACjB,GAAQ;;IAEX;;EAGH,WAA+B;AACzB,SAAA,MACJ,MAAA,IAAkB,IAElB,qBAAqB;AAEnB,IADA,MAAA,IAAkB,IAClB,MAAA,EAAe,KAAK;KACpB;;;;;;ACpJR,SAAgB,EAAU,GAAiB,GAAuC;AAChF,QAAO,GAAgB,CAAC,OAAO,GAAc,EAAS;;AAIxD,SAAgB,EACd,GACA,GACM;CACN,IAAM,EAAE,aAAU,oBAAiB,GAAgB,EAC7C,IAAkB,GAAiB,EACnC,UAAsB,EAAgB,IAAI,EAAE,OAAO,IAAM,CAAC,EAC5D;AAWJ,CATA,EAAS,WAAiB;EACxB,IAAM,IAAY,EAAK,KAAK,MAAQ,EAAI,MAAM;AAC9C,MAAI,GAAQ,WAAW,EAAU,UAAU,GAAQ,OAAO,GAAO,MAAM,MAAU,EAAU,GAAG,CAAE;AAEhG,EADA,IAAS,GACT,GAAS;EACT,IAAM,IAAe,EAAS,GAAI,EAAe;AACjD,EAAI,KAAc,EAAgB,WAAW,GAAc,CAAC;GAC5D,EAEF,EAAa,KAAK,EAAQ;;AAI5B,SAAgB,EAAoC,GAAG,GAA6D;AAClH,KAAI,EAAM,WAAW,EAAG,QAAO,EAAE;CACjC,IAAM,EAAE,YAAS,GAAgB,EAE3B,IAAO,OAAO,YAClB,EAAM,KAAK,MAAS,CAClB,GACA,EAAO,EAAK,aAAa,EAAK,GAAG,MAAU;AACzC,EAAI,KAAS,OAAM,EAAK,gBAAgB,EAAK,GACxC,EAAK,aAAa,GAAM,EAAM;GACnC,CACH,CAAC,CACH,EAEK,IAAW,IAAI,kBAAkB,MAAa;AAClD,OAAK,IAAM,EAAE,sBAAmB,EAC9B,CAAI,KAAiB,QAAQ,OAAO,OAAO,GAAM,EAAc,KAC7D,EAAK,GAAgB,QAAQ,EAAK,aAAa,EAAc;GAGjE;AAOF,QALA,EAAU,EAAE,SACV,EAAS,QAAQ,GAAM;EAAE,iBAAiB;EAAO,YAAY;EAAM,CAAC,QACvD,EAAS,YAAY,EAClC,EAEK;;AAGT,SAAS,IAAkE;CACzE,IAAM,IAAU,OAAO,GAAsB,GAAG,GAAG;AACnD,KAAI,CAAC,EAAS,OAAU,MAAM,gDAAgD;AAC9E,QAAO;;;;ACnDT,SAAgB,EACd,GACA,GACA,EAAE,kBAA+B,EAAE,EACR;CAC3B,IAAM,IAAM,EAA6B;EAAE,SAAS;EAAM,OAAO,KAAA;EAAW,OAAO,KAAA;EAAW,CAAC,EAC3F,IAAe;AAiCnB,QA/BA,EAAU,UAAa,GAAG,MAAW;EACnC,IAAM,IAAK,IAAI,iBAAiB;AAwBhC,SAtBA,QAAQ,KACN,IACI,CAAC,QAAQ,SAAS,CAAC,GACnB,CACE,IAAI,SAAS,MAAY,WAAW,GAAS,EAAW,CAAC,EACzD,IAAI,SAAS,MAAY,EAAG,OAAO,iBAAiB,SAAS,GAAS,EAAE,MAAM,IAAM,CAAC,CAAC,CACvF,CACN,CACE,WAAW;AACN,UAAG,OAAO,QACd,QAAO,EAAS,EAAG,QAAQ,GAAI,EAAe;IAC9C,CACD,MAAM,MAAU;AACX,KAAG,OAAO,YACd,EAAI,QAAQ;IAAE,SAAS;IAAO;IAAO,OAAO,KAAA;IAAW;IACvD,CACD,OAAO,MAAmB;AACrB,KAAG,OAAO,YACd,EAAI,QAAQ;IAAE,SAAS;IAAO,OAAO,KAAA;IAAW;IAAO;IACvD,EAEJ,IAAe,UACF,EAAG,OAAO;GACvB,EAGF,EAAU,EAAE,cAAe,IAAe,GAAM,EAEzC;;;;AC7CT,SAAgB,EAAoB,GAAqC;CACvE,IAAM,IAAY,GAAiB,EAC/B,IAAQ;AAEZ,QAAO;EACL,IAAI,QAAQ;AACV,UAAO;;EAET,IAAI,MAAM,GAAU;AACd,SAAa,MACjB,IAAQ,GACR,EAAU,KAAK;;EAEjB,YAAY,MAAa,EAAU,WAAW,EAAS,EAAM,CAAC;EAC/D;;;;ACRH,SAAgB,IAAgC;AAC9C,QAAQ,MAAc,GAAc;;AAGtC,IAAI;AAEJ,SAAS,IAAmC;CAC1C,IAAM,IAAQ,EAAyB;EACrC,KAAK,OAAO,SAAS;EACrB,UAAU,OAAO,SAAS;EAC1B,MAAM,OAAO,SAAS;EACtB,OAAO,OAAO,QAAQ;EACvB,CAAC,EAEI,UAAuB;EAC3B,IAAI,IAAQ,EAAM;AAelB,EAbI,EAAM,QAAQ,OAAO,SAAS,SAChC,IAAQ;GACN,GAAG;GACH,KAAK,OAAO,SAAS;GACrB,UAAU,OAAO,SAAS;GAC1B,MAAM,OAAO,SAAS;GACvB,GAGC,EAAM,UAAU,OAAO,QAAQ,UACjC,IAAQ;GAAE,GAAG;GAAO,OAAO,OAAO,QAAQ;GAAO,GAGnD,EAAM,QAAQ;;AAwBhB,QArBA,OAAO,iBACL,SACA,OAAO,YACJ,CAAC,aAAa,eAAe,CAAW,KAAK,MAAyC;EACrF,IAAM,IAAW,QAAQ,GAAQ,KAAK,QAAQ;AAU9C,SAAO,CAAC,GAT+B;GACrC,QAAQ,GAAG,MAAsC;AAE/C,IADA,EAAS,GAAG,EAAK,EACjB,GAAU;;GAEZ,YAAY;GACZ,cAAc;GACf,CAE0B;GAC3B,CACH,CACF,EAED,OAAO,iBAAiB,YAAY,EAAS,EAEtC;;;;ACxDT,SAAgB,EACd,GACA,EAAE,WAAQ,UAAU,YAAS,eAA6B,EAAE,EACD;CAC3D,IAAM,IAAU,MAAU,UAAU,QAAQ,MAAU,WAAW,UAAU,GACrE,IAAQ,MAAM,QAAQ,EAAK,GAAI,IAA6B,CAAC,EAAe,EAE5E,KAAY,MAAwC;EACxD,IAAI,IAAQ,IAAI,IAAI,EAAI,CAAC,IACnB,IAAS,EAAM,MAAM,MAAS,EAAM,SAAS,EAAK,CAAC,IAAI;AAG7D,SAFI,KAAU,OAAa,QAC3B,IAAQ,EAAM,MAAM,EAAO,OAAO,EAC3B,EAAM,MAAM,EAAQ;IAGvB,IAAS,EAAe,OAAO,SAAS,KAAK,EAC7C,IAAW,EAA+B,EAAS,OAAO,SAAS,KAAK,CAAC,EACzE,IAAW,EAAgB,OAAO,QAAQ,MAAM;AAatD,QAXA,EAAU,EAAE,QACH,GAAW,CAAC,WAAW,EAAE,QAAK,eAAY;AAE/C,EADA,EAAO,QAAQ,GACf,EAAS,QAAQ;GACjB,CACF,EAEF,EAAU,CAAC,EAAO,GAAG,MAAQ;AAC3B,IAAS,QAAQ,EAAS,EAAI;GAC9B,EAEK,CAAC,GAAU,EAAS;;;;AC5B7B,SAAgB,EACd,GACA,KAAqC,MAAU,GAC/C,GACa;CACb,IAAM,IAAM,EAAO,EAAO,EAAM,MAAM,EAAE,OAAY,MAAU,EAAO,GAAO,EAAM,EAAE;AAEpF,QADA,EAAU,EAAE,QAAQ,EAAM,WAAW,MAAW,EAAI,QAAQ,EAAO,EAAM,CAAE,CAAC,EACrE;;;;ACRT,IAAM,IAAW;AAwCjB,SAAgB,EACd,GACA,IAA+E,EAAE,EACjF,IAAkC,EAAE,EACf;AACrB,KAAI,OAAO,KAAO,YAAY;EAC5B,IAAI,IAAO,eAAe,QAAQ,EAAG;AAOrC,EALI,MACF,IAAO,MAAM,OAAO,YAAY,IAChC,eAAe,OAAO,GAAM,EAAG,GAGjC,IAAK;;CAGP,IAAI;AAKJ,KAJA,CAAC,GAAO,KAAY,MAAM,QAAQ,EAAgB,GAC9C,CAAC,EAAE,EAAE,EAAyC,GAC9C,CAAC,GAAwD,EAAS,EAElE,OAAO,KAAO,UAAU;EAC1B,IAAM,GAAG,IAAW,GAAK,GAAG,MAAe;AAE3C,SAAO,OAAO,KAAQ,WAClB;GACE,KAAK;GACL;GACA,OAAO;GACP;GACA,eAAe,EAAK,SAAS,cAAc,EAAG,EAAE,GAAY,EAAS;GACtE,GACD,EAAK,SAAS,cAAc,EAAG,EAAE,GAAO,EAAS;;AAGvD,KAAI,kBAAkB,GAAI;EACxB,IAAM,IAAW,IAAI,IAAI,EAAG,mBAAmB,CAAC;AAEhD,OAAK,IAAM,CAAC,GAAM,MAAa,OAAO,QAAQ,EAAM,EAAE;AAEpD,OADA,GAAU,OAAO,EAAK,EAClB,KAAY,QAAQ,KAAY,EAAO;GAC3C,IAAM,IAAQ,KAAY,IAAO,KAAK,OAAO,EAAS;AAClD,KAAG,aAAa,EAAK,KAAK,KAC9B,EAAG,aAAa,GAAM,EAAM;;AAG9B,OAAK,IAAM,KAAQ,EACjB,GAAG,gBAAgB,EAAK;;CAI5B,IAAI;AA2BJ,QAzBA,EAAG,gBACD,GAAG,EACA,QAAQ,MAAU,KAAS,QAAQ,MAAU,GAAM,CACnD,KAAK,MAAU;AACd,MAAI,OAAO,KAAU,SAAU,QAAO,SAAS,eAAe,OAAO,EAAM,CAAC;AAC5E,MAAI,aAAiB,KAAM,QAAO;AAElC,QAAgB,IAAI,IAClB,CAAC,GAAG,EAAG,SAAS,CAAC,SAAS,MAAU;GAClC,IAAM,IAAM,EAAM,aAAa,EAAS;AACxC,UAAO,KAAO,OAAO,EAAE,GAAG,CAAC,CAAC,GAAK,EAAM,CAAU;IACjD,CACH;EAED,IAAM,IAAS,EAAY,IAAI,EAAM,IAAI;AAOzC,SALI,KAAU,EAAO,QAAQ,aAAa,KAAK,EAAM,OACnD,EAAY,OAAO,EAAM,IAAI,EACtB,EAAK,GAAQ,EAAM,OAAO,EAAM,SAAS,IAG3C,EAAM,SAAS;GACtB,CACL,EAEM;;AAIT,SAAgB,EAAU,GAAsC;CAC9D,IAAM,IAAW,EAAK,WAAW;AAEjC,QADA,EAAS,YAAY,GACd,EAAS"}