@seahax/elemental 0.2.3 → 0.3.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.
Files changed (46) hide show
  1. package/README.md +12 -2
  2. package/dist/component.d.ts +2 -24
  3. package/dist/component.js +17 -49
  4. package/dist/component.js.map +1 -1
  5. package/dist/hooks/attributes.d.ts +5 -0
  6. package/dist/hooks/attributes.js +25 -0
  7. package/dist/hooks/attributes.js.map +1 -0
  8. package/dist/hooks/child-effect.d.ts +2 -0
  9. package/dist/hooks/child-effect.js +14 -0
  10. package/dist/hooks/child-effect.js.map +1 -0
  11. package/dist/hooks/context.d.ts +3 -0
  12. package/dist/hooks/context.js +11 -0
  13. package/dist/hooks/context.js.map +1 -0
  14. package/dist/hooks/effect.d.ts +3 -0
  15. package/dist/hooks/effect.js +17 -0
  16. package/dist/hooks/effect.js.map +1 -0
  17. package/dist/hooks/host.d.ts +2 -0
  18. package/dist/hooks/host.js +9 -0
  19. package/dist/hooks/host.js.map +1 -0
  20. package/dist/hooks/loading.d.ts +3 -3
  21. package/dist/hooks/loading.js +9 -8
  22. package/dist/hooks/loading.js.map +1 -1
  23. package/dist/hooks/ref.d.ts +15 -0
  24. package/dist/hooks/ref.js +19 -0
  25. package/dist/hooks/ref.js.map +1 -0
  26. package/dist/hooks/route.d.ts +2 -2
  27. package/dist/hooks/route.js +4 -3
  28. package/dist/hooks/route.js.map +1 -1
  29. package/dist/hooks/store.d.ts +2 -2
  30. package/dist/hooks/store.js +4 -3
  31. package/dist/hooks/store.js.map +1 -1
  32. package/dist/index.d.ts +5 -1
  33. package/dist/index.js +13 -9
  34. package/dist/internal/callbacks.d.ts +5 -6
  35. package/dist/internal/callbacks.js +16 -8
  36. package/dist/internal/callbacks.js.map +1 -1
  37. package/dist/internal/context.d.ts +13 -0
  38. package/dist/internal/context.js +32 -0
  39. package/dist/internal/context.js.map +1 -0
  40. package/package.json +2 -2
  41. package/dist/hooks/core.d.ts +0 -9
  42. package/dist/hooks/core.js +0 -44
  43. package/dist/hooks/core.js.map +0 -1
  44. package/dist/internal/constants.d.ts +0 -2
  45. package/dist/internal/constants.js +0 -6
  46. package/dist/internal/constants.js.map +0 -1
package/README.md CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  useLoading,
27
27
  useEffect,
28
28
  useChildEffect,
29
+ useHost,
29
30
  h,
30
31
  } from '@seahax/elemental';
31
32
 
@@ -45,18 +46,24 @@ export const MyComponent = defineComponent((shadow) => {
45
46
  ]),
46
47
  ]);
47
48
 
48
- // Use reference (reactive state) hooks.
49
+ // Use a reference (reactive state) value.
49
50
  const localStateRef = useRef('initial value', (newValue) => {
50
51
  // Handle
51
52
  });
53
+
54
+ // Use a reference (reactive state) bound to a (shared) store.
52
55
  const globalStateRef = useStore(myStore, select, mutate);
56
+
57
+ // Use references (reactive state) bound to attributes.
53
58
  const [dataValueRef, ...] = useAttributes('data-value', ...);
59
+
60
+ // Use a reference (reactive state) bound to route matching.
54
61
  const routeMatchRef = useRoute('/path/', {
55
62
  match: 'prefix', // 'exact' | 'prefix' | RegExp
56
63
  source: 'pathname', // 'pathname' | 'hash'
57
64
  });
58
65
 
59
- // Reactively load async data.
66
+ // Use a reference (reactive state) bound to an async loader function.
60
67
  const loadingStateRef = useLoading([
61
68
  // dependency references
62
69
  ], async (signal, ...dependencyValues) => {
@@ -96,6 +103,9 @@ export const MyComponent = defineComponent((shadow) => {
96
103
  // is disconnected from the document.
97
104
  };
98
105
  });
106
+
107
+ // Use the host element (generally only useful in reusable hooks).
108
+ const host = useHost();
99
109
  });
100
110
  ```
101
111
 
@@ -1,5 +1,4 @@
1
- import { type Callbacks } from './internal/callbacks.ts';
2
- import { $$ref, $$renderContextStack } from './internal/constants.ts';
1
+ import { type Ref } from './hooks/ref.ts';
3
2
  type SafeProps<TProps> = any extends any ? {
4
3
  [P in keyof TProps as P extends keyof HTMLElement ? never : P]: TProps[P];
5
4
  } : never;
@@ -27,27 +26,6 @@ export type ComponentShadowRoot<TProps extends object> = Omit<ShadowRoot, 'host'
27
26
  export type ComponentPropRefs<TProps extends object> = {
28
27
  readonly [P in keyof SafeProps<TProps>]: Ref<TProps[P] | undefined>;
29
28
  };
30
- export interface Ref<T> extends ReadonlyRef<T> {
31
- value: T;
32
- }
33
- export interface ReadonlyRef<T> {
34
- /** @hidden */
35
- [$$ref]: unknown;
36
- readonly value: T;
37
- }
38
- export type RefValues<T> = T extends readonly any[] ? {
39
- [K in keyof T]: T[K] extends ReadonlyRef<infer V> ? V : never;
40
- } : never;
41
- declare global {
42
- interface Window {
43
- readonly [$$renderContextStack]: {
44
- readonly host: HTMLElement;
45
- readonly onDisconnect: Callbacks;
46
- readonly onSetRef: Callbacks;
47
- readonly useRef: <T>(initialValue: T, onChange?: (value: T) => void) => Ref<T>;
48
- }[];
49
- }
50
- }
51
29
  /** Define a custom `HTMLElement` that is functional and reactive. */
52
- export declare function defineComponent<TProps extends object = {}>(render: (shadow: ComponentShadowRoot<TProps>, props: ComponentPropRefs<TProps>) => void, options?: ComponentOptions<TProps>): ComponentConstructor<TProps>;
30
+ export declare function defineComponent<TProps extends object = {}>(render: (shadowRoot: ComponentShadowRoot<TProps>, props: ComponentPropRefs<TProps>) => void, options?: ComponentOptions<TProps>): ComponentConstructor<TProps>;
53
31
  export {};
package/dist/component.js CHANGED
@@ -1,66 +1,34 @@
1
- import { createCallbacks as e } from "./internal/callbacks.js";
2
- import { $$ref as t, $$renderContextStack as n } from "./internal/constants.js";
1
+ import { createContextController as e } from "./internal/context.js";
2
+ import { useRef as t } from "./hooks/ref.js";
3
3
  //#region src/component.ts
4
- Object.assign(window, { [n]: [] });
5
- function r(r, i = {}) {
4
+ function n(n, { props: r, shadow: i } = {}) {
6
5
  return class extends HTMLElement {
7
- #e = e();
8
- #t = e();
9
- #n = i;
10
- #r;
11
- #i = !1;
6
+ #e = {};
7
+ #t = e(this);
12
8
  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)) {
9
+ if (super(), r) {
10
+ let e = this.#e;
11
+ for (let [n, i] of Object.entries(r)) {
18
12
  if (n in this) continue;
19
- let t = r(e[n] = this.#a(void 0), this);
20
- Object.defineProperty(this, n, t);
13
+ let r = i(e[n] = t(void 0), this);
14
+ Object.defineProperty(this, n, r);
21
15
  }
22
16
  }
23
17
  }
24
18
  connectedCallback() {
25
- let e = this.attachShadow({
26
- ...this.#n.shadow,
27
- mode: this.#n.shadow?.mode ?? "open"
19
+ this.#t.connect(() => {
20
+ n(this.attachShadow({
21
+ ...i,
22
+ mode: i?.mode ?? "open"
23
+ }), this.#e);
28
24
  });
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
25
  }
41
26
  disconnectedCallback() {
42
- this.#t.clear(), this.#e.run({ clear: !0 });
27
+ this.#t.disconnect();
43
28
  }
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
29
  };
62
30
  }
63
31
  //#endregion
64
- export { r as defineComponent };
32
+ export { n as defineComponent };
65
33
 
66
34
  //# sourceMappingURL=component.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"component.js","names":["#propRefs","#contextController"],"sources":["../src/component.ts"],"sourcesContent":["import { type Ref, useRef } from './hooks/ref.ts';\nimport { createContextController } from './internal/context.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\n/** Define a custom `HTMLElement` that is functional and reactive. */\nexport function defineComponent<TProps extends object = {}>(\n render: (shadowRoot: ComponentShadowRoot<TProps>, props: ComponentPropRefs<TProps>) => void,\n options?: ComponentOptions<TProps>,\n): ComponentConstructor<TProps>;\nexport function defineComponent(\n render: (\n shadowRoot: ComponentShadowRoot<Record<string, unknown>>,\n props: ComponentPropRefs<Record<string, unknown>>,\n ) => void,\n { props, shadow }: ComponentOptions<Record<string, unknown>> = {},\n): ComponentConstructor<{}> {\n return class extends HTMLElement {\n readonly #propRefs: ComponentPropRefs<Record<string, unknown>> = {};\n readonly #contextController = createContextController(this);\n\n constructor() {\n super();\n\n if (props) {\n const propRefs: Record<string, Ref<unknown>> = this.#propRefs;\n\n for (const [key, getDescriptor] of Object.entries(props)) {\n if (key in this) continue;\n const ref = (propRefs[key] = useRef<any>(undefined));\n const descriptor = getDescriptor(ref, this);\n Object.defineProperty(this, key, descriptor);\n }\n }\n }\n\n protected connectedCallback(): void {\n this.#contextController.connect(() => {\n const shadowRoot = this.attachShadow({ ...shadow, mode: shadow?.mode ?? 'open' });\n render(shadowRoot as ComponentShadowRoot<Record<string, unknown>>, this.#propRefs);\n });\n }\n\n protected disconnectedCallback(): void {\n this.#contextController.disconnect();\n }\n };\n}\n"],"mappings":";;;AA+CA,SAAgB,EACd,GAIA,EAAE,UAAO,cAAsD,EAAE,EACvC;AAC1B,QAAO,cAAc,YAAY;EAC/B,KAAiE,EAAE;EACnE,KAA8B,EAAwB,KAAK;EAE3D,cAAc;AAGZ,OAFA,OAAO,EAEH,GAAO;IACT,IAAM,IAAyC,MAAA;AAE/C,SAAK,IAAM,CAAC,GAAK,MAAkB,OAAO,QAAQ,EAAM,EAAE;AACxD,SAAI,KAAO,KAAM;KAEjB,IAAM,IAAa,EAAc,EADX,KAAO,EAAY,KAAA,EAAU,EACb,KAAK;AAC3C,YAAO,eAAe,MAAM,GAAK,EAAW;;;;EAKlD,oBAAoC;AAClC,SAAA,EAAwB,cAAc;AAEpC,MADmB,KAAK,aAAa;KAAE,GAAG;KAAQ,MAAM,GAAQ,QAAQ;KAAQ,CACzE,EAA4D,MAAA,EAAe;KAClF;;EAGJ,uBAAuC;AACrC,SAAA,EAAwB,YAAY"}
@@ -0,0 +1,5 @@
1
+ import { type Ref } from './ref.ts';
2
+ /** Use references (reactive state) bound to attributes. */
3
+ export declare function useAttributes<const TNames extends string[]>(...names: TNames): {
4
+ -readonly [P in keyof TNames]: Ref<string | null>;
5
+ };
@@ -0,0 +1,25 @@
1
+ import { useRef as e } from "./ref.js";
2
+ import { useEffect as t } from "./effect.js";
3
+ import { useHost as n } from "./host.js";
4
+ //#region src/hooks/attributes.ts
5
+ function r(...r) {
6
+ if (r.length === 0) return {};
7
+ let i = n(), a = [], o = /* @__PURE__ */ new Map();
8
+ for (let t of r) {
9
+ let n = e(i.getAttribute(t), (e) => {
10
+ e == null ? i.removeAttribute(t) : i.setAttribute(t, e);
11
+ });
12
+ a.push(n), o.set(t, n);
13
+ }
14
+ let s = new MutationObserver((e) => {
15
+ for (let { attributeName: t } of e) t != null && Object.hasOwn(a, t) && (o.get(t).value = i.getAttribute(t));
16
+ });
17
+ return t([], () => (s.observe(i, {
18
+ attributeFilter: r,
19
+ attributes: !0
20
+ }), () => s.disconnect())), a;
21
+ }
22
+ //#endregion
23
+ export { r as useAttributes };
24
+
25
+ //# sourceMappingURL=attributes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attributes.js","names":[],"sources":["../../src/hooks/attributes.ts"],"sourcesContent":["import { useEffect } from './effect.ts';\nimport { useHost } from './host.ts';\nimport { type Ref, useRef } from './ref.ts';\n\n/** Use references (reactive state) bound to attributes. */\nexport function useAttributes<const TNames extends string[]>(\n ...names: TNames\n): { -readonly [P in keyof TNames]: Ref<string | null> } {\n if (names.length === 0) return {} as any;\n const host = useHost();\n const refs: Ref<string | null>[] = [];\n const refMap = new Map<string, Ref<string | null>>();\n\n for (const name of names) {\n const ref = useRef(host.getAttribute(name), (value) => {\n if (value == null) host.removeAttribute(name);\n else host.setAttribute(name, value);\n });\n\n refs.push(ref);\n refMap.set(name, ref);\n }\n\n const observer = new MutationObserver((mutation) => {\n for (const { attributeName } of mutation) {\n if (attributeName != null && Object.hasOwn(refs, attributeName)) {\n refMap.get(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 any;\n}\n"],"mappings":";;;;AAKA,SAAgB,EACd,GAAG,GACoD;AACvD,KAAI,EAAM,WAAW,EAAG,QAAO,EAAE;CACjC,IAAM,IAAO,GAAS,EAChB,IAA6B,EAAE,EAC/B,oBAAS,IAAI,KAAiC;AAEpD,MAAK,IAAM,KAAQ,GAAO;EACxB,IAAM,IAAM,EAAO,EAAK,aAAa,EAAK,GAAG,MAAU;AACrD,GAAI,KAAS,OAAM,EAAK,gBAAgB,EAAK,GACxC,EAAK,aAAa,GAAM,EAAM;IACnC;AAGF,EADA,EAAK,KAAK,EAAI,EACd,EAAO,IAAI,GAAM,EAAI;;CAGvB,IAAM,IAAW,IAAI,kBAAkB,MAAa;AAClD,OAAK,IAAM,EAAE,sBAAmB,EAC9B,CAAI,KAAiB,QAAQ,OAAO,OAAO,GAAM,EAAc,KAC7D,EAAO,IAAI,EAAc,CAAE,QAAQ,EAAK,aAAa,EAAc;GAGvE;AAOF,QALA,EAAU,EAAE,SACV,EAAS,QAAQ,GAAM;EAAE,iBAAiB;EAAO,YAAY;EAAM,CAAC,QACvD,EAAS,YAAY,EAClC,EAEK"}
@@ -0,0 +1,2 @@
1
+ /** React to child list changes (non-recursive). */
2
+ export declare function useChildEffect(callback: () => (() => void) | void): void;
@@ -0,0 +1,14 @@
1
+ import { useRef as e } from "./ref.js";
2
+ import { useEffect as t } from "./effect.js";
3
+ import { useHost as n } from "./host.js";
4
+ //#region src/hooks/child-effect.ts
5
+ function r(r) {
6
+ let i = n(), a = e(0), o = new MutationObserver((e) => {
7
+ e.some((e) => e.type === "childList") && (a.value = (a.value + 1) % (2 ** 53 - 1));
8
+ });
9
+ t([], () => (o.observe(i, { childList: !0 }), () => o.disconnect())), t([a], () => r());
10
+ }
11
+ //#endregion
12
+ export { r as useChildEffect };
13
+
14
+ //# sourceMappingURL=child-effect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-effect.js","names":[],"sources":["../../src/hooks/child-effect.ts"],"sourcesContent":["import { useEffect } from './effect.ts';\nimport { useHost } from './host.ts';\nimport { useRef } from './ref.ts';\n\n/** React to child list changes (non-recursive). */\nexport function useChildEffect(callback: () => (() => void) | void): void {\n const host = useHost();\n const ref = useRef(0);\n\n const observer = new MutationObserver((mutation) => {\n if (mutation.some((m) => m.type === 'childList')) {\n ref.value = (ref.value + 1) % Number.MAX_SAFE_INTEGER;\n }\n });\n\n useEffect([], () => {\n observer.observe(host, { childList: true });\n return () => observer.disconnect();\n });\n\n useEffect([ref], () => {\n return callback();\n });\n}\n"],"mappings":";;;;AAKA,SAAgB,EAAe,GAA2C;CACxE,IAAM,IAAO,GAAS,EAChB,IAAM,EAAO,EAAE,EAEf,IAAW,IAAI,kBAAkB,MAAa;AAClD,EAAI,EAAS,MAAM,MAAM,EAAE,SAAS,YAAY,KAC9C,EAAI,SAAS,EAAI,QAAQ;GAE3B;AAOF,CALA,EAAU,EAAE,SACV,EAAS,QAAQ,GAAM,EAAE,WAAW,IAAM,CAAC,QAC9B,EAAS,YAAY,EAClC,EAEF,EAAU,CAAC,EAAI,QACN,GAAU,CACjB"}
@@ -0,0 +1,3 @@
1
+ import { type Context } from '../internal/context.ts';
2
+ /** @internal Get the context of the currently rendering component. */
3
+ export declare function useContext(): Context;
@@ -0,0 +1,11 @@
1
+ import { contextStack as e } from "../internal/context.js";
2
+ //#region src/hooks/context.ts
3
+ function t() {
4
+ let t = e.at(-1);
5
+ if (!t) throw Error("hooks must be called inside a render function");
6
+ return t;
7
+ }
8
+ //#endregion
9
+ export { t as useContext };
10
+
11
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","names":[],"sources":["../../src/hooks/context.ts"],"sourcesContent":["import { type Context, contextStack } from '../internal/context.ts';\n\n/** @internal Get the context of the currently rendering component. */\nexport function useContext(): Context {\n const context = contextStack.at(-1);\n if (!context) throw new Error('hooks must be called inside a render function');\n return context;\n}\n"],"mappings":";;AAGA,SAAgB,IAAsB;CACpC,IAAM,IAAU,EAAa,GAAG,GAAG;AACnC,KAAI,CAAC,EAAS,OAAU,MAAM,gDAAgD;AAC9E,QAAO"}
@@ -0,0 +1,3 @@
1
+ import type { ReadonlyRef, RefValues } from './ref.ts';
2
+ /** React to observable (reference) changes. */
3
+ export declare function useEffect<const TDeps extends readonly ReadonlyRef<any>[]>(deps: TDeps, callback: (...values: RefValues<TDeps>) => (() => void) | void): void;
@@ -0,0 +1,17 @@
1
+ import { createCallbacks as e } from "../internal/callbacks.js";
2
+ import { useContext as t } from "./context.js";
3
+ //#region src/hooks/effect.ts
4
+ function n(n, r) {
5
+ let { onNotify: i, onDisconnect: a } = t(), o = e(), s = () => o.runAndClear(), c;
6
+ i.push(() => {
7
+ let e = n.map((e) => e.value);
8
+ if (c?.length === e.length && c?.every((t, n) => t === e[n])) return;
9
+ c = e, s();
10
+ let t = r(...c);
11
+ t && o.push(() => t());
12
+ }), a.push(s);
13
+ }
14
+ //#endregion
15
+ export { n as useEffect };
16
+
17
+ //# sourceMappingURL=effect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effect.js","names":[],"sources":["../../src/hooks/effect.ts"],"sourcesContent":["import { createCallbacks } from '../internal/callbacks.ts';\nimport { useContext } from './context.ts';\nimport type { ReadonlyRef, RefValues } from './ref.ts';\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 { onNotify, onDisconnect } = useContext();\n const cleanupCallback = createCallbacks();\n const cleanup = (): void => cleanupCallback.runAndClear();\n let values: any[] | undefined;\n\n onNotify.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"],"mappings":";;;AAKA,SAAgB,EACd,GACA,GACM;CACN,IAAM,EAAE,aAAU,oBAAiB,GAAY,EACzC,IAAkB,GAAiB,EACnC,UAAsB,EAAgB,aAAa,EACrD;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"}
@@ -0,0 +1,2 @@
1
+ /** Get the component host element. */
2
+ export declare function useHost(): HTMLElement;
@@ -0,0 +1,9 @@
1
+ import { useContext as e } from "./context.js";
2
+ //#region src/hooks/host.ts
3
+ function t() {
4
+ return e().host;
5
+ }
6
+ //#endregion
7
+ export { t as useHost };
8
+
9
+ //# sourceMappingURL=host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","names":[],"sources":["../../src/hooks/host.ts"],"sourcesContent":["import { useContext } from './context.ts';\n\n/** Get the component host element. */\nexport function useHost(): HTMLElement {\n return useContext().host;\n}\n"],"mappings":";;AAGA,SAAgB,IAAuB;AACrC,QAAO,GAAY,CAAC"}
@@ -1,11 +1,11 @@
1
- import type { ReadonlyRef, Ref, RefValues } from '../component.ts';
1
+ import { type ReadonlyRef, type Ref, type RefValues } from './ref.ts';
2
2
  export interface LoadingValue<TValue> {
3
- readonly loading: boolean;
4
3
  readonly value: TValue | undefined;
5
4
  readonly error: unknown;
5
+ readonly isLoading: boolean;
6
6
  }
7
7
  export interface LoadingOptions {
8
8
  readonly debounceMs?: number;
9
9
  }
10
- /** Load data asynchronously. */
10
+ /** Use a reference (reactive state) bound to an async loader function. */
11
11
  export declare function useLoading<const TDeps extends readonly ReadonlyRef<any>[], TValue>(deps: TDeps, callback: (signal: AbortSignal, ...values: RefValues<TDeps>) => Promise<TValue>, { debounceMs }?: LoadingOptions): Ref<LoadingValue<TValue>>;
@@ -1,29 +1,30 @@
1
- import { useEffect as e, useRef as t } from "./core.js";
1
+ import { useRef as e } from "./ref.js";
2
+ import { useEffect as t } from "./effect.js";
2
3
  //#region src/hooks/loading.ts
3
4
  function n(n, r, { debounceMs: i } = {}) {
4
- let a = t({
5
- loading: !0,
5
+ let a = e({
6
6
  value: void 0,
7
- error: void 0
7
+ error: void 0,
8
+ isLoading: !0
8
9
  }), o = !0;
9
- return e(n, () => (...e) => {
10
+ return t(n, () => (...e) => {
10
11
  let t = new AbortController();
11
12
  return Promise.race(o ? [Promise.resolve()] : [new Promise((e) => setTimeout(e, i)), new Promise((e) => t.signal.addEventListener("abort", e, { once: !0 }))]).then(() => {
12
13
  if (!t.signal.aborted) return r(t.signal, ...e);
13
14
  }).then((e) => {
14
15
  t.signal.aborted || (a.value = {
15
- loading: !1,
16
+ isLoading: !1,
16
17
  value: e,
17
18
  error: void 0
18
19
  });
19
20
  }).catch((e) => {
20
21
  t.signal.aborted || (a.value = {
21
- loading: !1,
22
+ isLoading: !1,
22
23
  value: void 0,
23
24
  error: e
24
25
  });
25
26
  }), o = !1, () => t.abort();
26
- }), e([], () => () => o = !0), a;
27
+ }), t([], () => () => o = !0), a;
27
28
  }
28
29
  //#endregion
29
30
  export { n as useLoading };
@@ -1 +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"}
1
+ {"version":3,"file":"loading.js","names":[],"sources":["../../src/hooks/loading.ts"],"sourcesContent":["import { useEffect } from './effect.ts';\nimport { type ReadonlyRef, type Ref, type RefValues, useRef } from './ref.ts';\n\nexport interface LoadingValue<TValue> {\n readonly value: TValue | undefined;\n readonly error: unknown;\n readonly isLoading: boolean;\n}\n\nexport interface LoadingOptions {\n readonly debounceMs?: number;\n}\n\n/** Use a reference (reactive state) bound to an async loader function. */\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>>({ value: undefined, error: undefined, isLoading: true });\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 = { isLoading: false, value, error: undefined };\n })\n .catch((error: unknown) => {\n if (ac.signal.aborted) return;\n ref.value = { isLoading: 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,OAAO,KAAA;EAAW,OAAO,KAAA;EAAW,WAAW;EAAM,CAAC,EAC7F,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,WAAW;IAAO;IAAO,OAAO,KAAA;IAAW;IACzD,CACD,OAAO,MAAmB;AACrB,KAAG,OAAO,YACd,EAAI,QAAQ;IAAE,WAAW;IAAO,OAAO,KAAA;IAAW;IAAO;IACzD,EAEJ,IAAe,UACF,EAAG,OAAO;GACvB,EAGF,EAAU,EAAE,cAAe,IAAe,GAAM,EAEzC"}
@@ -0,0 +1,15 @@
1
+ export interface Ref<T> extends ReadonlyRef<T> {
2
+ value: T;
3
+ }
4
+ export interface ReadonlyRef<T> {
5
+ /** @hidden */
6
+ [$$ref]: unknown;
7
+ readonly value: T;
8
+ }
9
+ export type RefValues<T> = T extends readonly any[] ? {
10
+ [K in keyof T]: T[K] extends ReadonlyRef<infer V> ? V : never;
11
+ } : never;
12
+ declare const $$ref: unique symbol;
13
+ /** Use a reference (reactive state) value. */
14
+ export declare function useRef<T>(initialValue: T, onChange?: (value: T) => void): Ref<T>;
15
+ export {};
@@ -0,0 +1,19 @@
1
+ import { useContext as e } from "./context.js";
2
+ //#region src/hooks/ref.ts
3
+ var t = Symbol();
4
+ function n(n, r) {
5
+ let { notify: i } = e(), a = n;
6
+ return {
7
+ [t]: !0,
8
+ get value() {
9
+ return a;
10
+ },
11
+ set value(e) {
12
+ e !== a && (a = e, r?.(a), i());
13
+ }
14
+ };
15
+ }
16
+ //#endregion
17
+ export { n as useRef };
18
+
19
+ //# sourceMappingURL=ref.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ref.js","names":[],"sources":["../../src/hooks/ref.ts"],"sourcesContent":["import { useContext } from './context.ts';\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\nconst $$ref = Symbol();\n\n/** Use a reference (reactive state) value. */\nexport function useRef<T>(initialValue: T, onChange?: (value: T) => void): Ref<T> {\n const { notify } = useContext();\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"],"mappings":";;AAgBA,IAAM,IAAQ,QAAQ;AAGtB,SAAgB,EAAU,GAAiB,GAAuC;CAChF,IAAM,EAAE,cAAW,GAAY,EAC3B,IAAQ;AAEZ,QAAO;GACJ,IAAQ;EACT,IAAI,QAAQ;AACV,UAAO;;EAET,IAAI,MAAM,GAAU;AACd,SAAa,MACjB,IAAQ,GACR,IAAW,EAAM,EACjB,GAAQ;;EAEX"}
@@ -1,4 +1,4 @@
1
- import type { Ref } from '../component.ts';
1
+ import { type Ref } from './ref.ts';
2
2
  export interface RouteOptions {
3
3
  readonly match?: 'prefix' | 'exact' | RegExp;
4
4
  readonly source?: 'pathname' | 'hash';
@@ -6,5 +6,5 @@ export interface RouteOptions {
6
6
  export type RouteMatchArray = readonly [string, ...string[]] & {
7
7
  readonly groups: Record<string, string>;
8
8
  };
9
- /** Observe route (window.history) changes. */
9
+ /** Use a reference (reactive state) bound to route matching. */
10
10
  export declare function useRoute(path: string | readonly string[], { match, source }?: RouteOptions): Ref<RouteMatchArray | null>;
@@ -1,10 +1,11 @@
1
- import { useEffect as e, useRef as t } from "./core.js";
1
+ import { useRef as e } from "./ref.js";
2
+ import { useEffect as t } from "./effect.js";
2
3
  import { getRouter as n } from "../router.js";
3
4
  import { useStore as r } from "./store.js";
4
5
  //#region src/hooks/route.ts
5
6
  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) => {
7
+ let s = a === "exact" ? /^$/u : a === "prefix" ? /^.*$/u : a, c = Array.isArray(i) ? i : [i], l = r(n(), (e) => e[o]), u = e(d(l.value));
8
+ return t([l], (e) => {
8
9
  u.value = d(e);
9
10
  }), u;
10
11
  function d(e) {
@@ -1 +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"}
1
+ {"version":3,"file":"route.js","names":[],"sources":["../../src/hooks/route.ts"],"sourcesContent":["import { getRouter } from '../router.ts';\nimport { useEffect } from './effect.ts';\nimport { type Ref, useRef } from './ref.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/** Use a reference (reactive state) bound to route matching. */\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"}
@@ -1,5 +1,5 @@
1
- import type { ReadonlyRef, Ref } from '../component.ts';
2
1
  import type { Store } from '../store.ts';
3
- /** Observe store (external state) changes. */
2
+ import { type ReadonlyRef, type Ref } from './ref.ts';
3
+ /** Use a reference (reactive state) bound to a (shared) store. */
4
4
  export declare function useStore<TState, TValue = TState>(store: Store<TState>, select: (state: TState) => TValue, mutate: (store: Store<TState>, value: TValue) => void): Ref<TValue>;
5
5
  export declare function useStore<TState, TValue = TState>(store: Store<TState>, select?: (state: TState) => TValue): ReadonlyRef<TValue>;
@@ -1,8 +1,9 @@
1
- import { useEffect as e, useRef as t } from "./core.js";
1
+ import { useRef as e } from "./ref.js";
2
+ import { useEffect as t } from "./effect.js";
2
3
  //#region src/hooks/store.ts
3
4
  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;
5
+ let a = e(r(n.state), i && ((e) => i(n, e)));
6
+ return t([], () => n.subscribe((e) => a.value = r(e))), a;
6
7
  }
7
8
  //#endregion
8
9
  export { n as useStore };
@@ -1 +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"}
1
+ {"version":3,"file":"store.js","names":[],"sources":["../../src/hooks/store.ts"],"sourcesContent":["import type { Store } from '../store.ts';\nimport { useEffect } from './effect.ts';\nimport { type ReadonlyRef, type Ref, useRef } from './ref.ts';\n\n/** Use a reference (reactive state) bound to a (shared) store. */\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/index.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  export * from './component.ts';
2
- export * from './hooks/core.ts';
2
+ export * from './hooks/attributes.ts';
3
+ export * from './hooks/child-effect.ts';
4
+ export * from './hooks/effect.ts';
5
+ export * from './hooks/host.ts';
3
6
  export * from './hooks/loading.ts';
7
+ export * from './hooks/ref.ts';
4
8
  export * from './hooks/route.ts';
5
9
  export * from './hooks/store.ts';
6
10
  export * from './html.ts';
package/dist/index.js CHANGED
@@ -1,9 +1,13 @@
1
- import { defineComponent as e } from "./component.js";
2
- import { useAttributes as t, useChildEffect as n, useEffect as r, useRef as i } from "./hooks/core.js";
3
- import { useLoading as a } from "./hooks/loading.js";
4
- import { createStore as o } from "./store.js";
5
- import { getRouter as s } from "./router.js";
6
- import { useStore as c } from "./hooks/store.js";
7
- import { useRoute as l } from "./hooks/route.js";
8
- import { html as u } from "./html.js";
9
- export { o as createStore, e as defineComponent, s as getRouter, u as h, u as html, t as useAttributes, n as useChildEffect, r as useEffect, a as useLoading, i as useRef, l as useRoute, c as useStore };
1
+ import { useRef as e } from "./hooks/ref.js";
2
+ import { defineComponent as t } from "./component.js";
3
+ import { useEffect as n } from "./hooks/effect.js";
4
+ import { useHost as r } from "./hooks/host.js";
5
+ import { useAttributes as i } from "./hooks/attributes.js";
6
+ import { useChildEffect as a } from "./hooks/child-effect.js";
7
+ import { useLoading as o } from "./hooks/loading.js";
8
+ import { createStore as s } from "./store.js";
9
+ import { getRouter as c } from "./router.js";
10
+ import { useStore as l } from "./hooks/store.js";
11
+ import { useRoute as u } from "./hooks/route.js";
12
+ import { html as d } from "./html.js";
13
+ export { s as createStore, t as defineComponent, c as getRouter, d as h, d as html, i as useAttributes, a as useChildEffect, n as useEffect, r as useHost, o as useLoading, e as useRef, u as useRoute, l as useStore };
@@ -1,8 +1,7 @@
1
- export interface Callbacks {
2
- readonly push: (callback: () => void) => () => void;
3
- readonly run: (options?: {
4
- readonly clear?: boolean;
5
- }) => void;
1
+ export interface Callbacks<TArgs extends unknown[] = []> {
2
+ readonly push: (callback: (...args: TArgs) => void) => () => void;
3
+ readonly run: (...args: TArgs) => void;
4
+ readonly runAndClear: (...args: TArgs) => void;
6
5
  readonly clear: () => void;
7
6
  }
8
- export declare function createCallbacks(): Callbacks;
7
+ export declare function createCallbacks<TArgs extends unknown[] = []>(): Callbacks<TArgs>;
@@ -2,17 +2,25 @@
2
2
  function e() {
3
3
  let e = /* @__PURE__ */ new Set(), t = {
4
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();
5
+ run: (...t) => {
6
+ let n = [], r = [...e];
7
+ for (let e of r) try {
8
+ e(...t);
9
9
  } catch (e) {
10
- r.push(e);
10
+ n.push(e);
11
11
  }
12
- if (n && e.clear(), r.length > 0) throw AggregateError(r);
13
- return t;
12
+ if (n.length > 0) throw AggregateError(n);
14
13
  },
15
- clear: () => e.clear()
14
+ runAndClear: (...e) => {
15
+ try {
16
+ t.run(...e);
17
+ } finally {
18
+ t.clear();
19
+ }
20
+ },
21
+ clear: () => {
22
+ e.clear();
23
+ }
16
24
  };
17
25
  return t;
18
26
  }
@@ -1 +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"}
1
+ {"version":3,"file":"callbacks.js","names":[],"sources":["../../src/internal/callbacks.ts"],"sourcesContent":["export interface Callbacks<TArgs extends unknown[] = []> {\n readonly push: (callback: (...args: TArgs) => void) => () => void;\n readonly run: (...args: TArgs) => void;\n readonly runAndClear: (...args: TArgs) => void;\n readonly clear: () => void;\n}\n\nexport function createCallbacks<TArgs extends unknown[] = []>(): Callbacks<TArgs> {\n const callbacks = new Set<(...args: TArgs) => void>();\n\n const self: Callbacks<TArgs> = {\n push: (callback) => {\n callbacks.add(callback);\n return () => callbacks.delete(callback);\n },\n run: (...args) => {\n const errors: unknown[] = [];\n const callbacksCopy = [...callbacks];\n\n for (const callback of callbacksCopy) {\n try {\n callback(...args);\n } catch (error: unknown) {\n errors.push(error);\n }\n }\n\n if (errors.length > 0) throw new AggregateError(errors);\n },\n runAndClear: (...args: TArgs) => {\n try {\n self.run(...args);\n } finally {\n self.clear();\n }\n },\n clear: () => {\n callbacks.clear();\n },\n };\n\n return self;\n}\n"],"mappings":";AAOA,SAAgB,IAAkE;CAChF,IAAM,oBAAY,IAAI,KAA+B,EAE/C,IAAyB;EAC7B,OAAO,OACL,EAAU,IAAI,EAAS,QACV,EAAU,OAAO,EAAS;EAEzC,MAAM,GAAG,MAAS;GAChB,IAAM,IAAoB,EAAE,EACtB,IAAgB,CAAC,GAAG,EAAU;AAEpC,QAAK,IAAM,KAAY,EACrB,KAAI;AACF,MAAS,GAAG,EAAK;YACV,GAAgB;AACvB,MAAO,KAAK,EAAM;;AAItB,OAAI,EAAO,SAAS,EAAG,OAAU,eAAe,EAAO;;EAEzD,cAAc,GAAG,MAAgB;AAC/B,OAAI;AACF,MAAK,IAAI,GAAG,EAAK;aACT;AACR,MAAK,OAAO;;;EAGhB,aAAa;AACX,KAAU,OAAO;;EAEpB;AAED,QAAO"}
@@ -0,0 +1,13 @@
1
+ import { type Callbacks } from './callbacks.ts';
2
+ export interface Context {
3
+ readonly host: HTMLElement;
4
+ readonly onNotify: Callbacks;
5
+ readonly onDisconnect: Callbacks;
6
+ readonly notify: () => void;
7
+ }
8
+ export interface ContextController {
9
+ readonly connect: (callback: () => void) => void;
10
+ readonly disconnect: () => void;
11
+ }
12
+ export declare const contextStack: Context[];
13
+ export declare function createContextController(host: HTMLElement): ContextController;
@@ -0,0 +1,32 @@
1
+ import { createCallbacks as e } from "./callbacks.js";
2
+ //#region src/internal/context.ts
3
+ var t = [];
4
+ function n(n) {
5
+ let r = !1, i = {
6
+ host: n,
7
+ onNotify: e(),
8
+ onDisconnect: e(),
9
+ notify: () => {
10
+ r || (r = !0, queueMicrotask(() => {
11
+ r = !1, i.onNotify.run();
12
+ }));
13
+ }
14
+ };
15
+ return {
16
+ connect: (e) => {
17
+ try {
18
+ t.push(i), e();
19
+ } finally {
20
+ t.pop();
21
+ }
22
+ i.onNotify.run();
23
+ },
24
+ disconnect: () => {
25
+ i.onNotify.clear(), i.onDisconnect.runAndClear();
26
+ }
27
+ };
28
+ }
29
+ //#endregion
30
+ export { t as contextStack, n as createContextController };
31
+
32
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","names":[],"sources":["../../src/internal/context.ts"],"sourcesContent":["import { type Callbacks, createCallbacks } from './callbacks.ts';\n\nexport interface Context {\n readonly host: HTMLElement;\n readonly onNotify: Callbacks;\n readonly onDisconnect: Callbacks;\n readonly notify: () => void;\n}\n\nexport interface ContextController {\n readonly connect: (callback: () => void) => void;\n readonly disconnect: () => void;\n}\n\nexport const contextStack: Context[] = [];\n\nexport function createContextController(host: HTMLElement): ContextController {\n let notifying = false;\n\n const context: Context = {\n host,\n onNotify: createCallbacks(),\n onDisconnect: createCallbacks(),\n notify: () => {\n if (notifying) return;\n notifying = true;\n\n queueMicrotask(() => {\n notifying = false;\n context.onNotify.run();\n });\n },\n };\n\n return {\n connect: (callback) => {\n try {\n contextStack.push(context);\n callback();\n } finally {\n contextStack.pop();\n }\n\n context.onNotify.run();\n },\n disconnect: () => {\n context.onNotify.clear();\n context.onDisconnect.runAndClear();\n },\n };\n}\n"],"mappings":";;AAcA,IAAa,IAA0B,EAAE;AAEzC,SAAgB,EAAwB,GAAsC;CAC5E,IAAI,IAAY,IAEV,IAAmB;EACvB;EACA,UAAU,GAAiB;EAC3B,cAAc,GAAiB;EAC/B,cAAc;AACR,SACJ,IAAY,IAEZ,qBAAqB;AAEnB,IADA,IAAY,IACZ,EAAQ,SAAS,KAAK;KACtB;;EAEL;AAED,QAAO;EACL,UAAU,MAAa;AACrB,OAAI;AAEF,IADA,EAAa,KAAK,EAAQ,EAC1B,GAAU;aACF;AACR,MAAa,KAAK;;AAGpB,KAAQ,SAAS,KAAK;;EAExB,kBAAkB;AAEhB,GADA,EAAQ,SAAS,OAAO,EACxB,EAAQ,aAAa,aAAa;;EAErC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seahax/elemental",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "Functional, reactive, web component base library.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,7 +20,7 @@
20
20
  ],
21
21
  "scripts": {
22
22
  "build": "vite build && tsc -b -f tsconfig.lib.json",
23
- "prepare": "npm run build && npm test",
23
+ "prepublishOnly": "npm run build && npm test",
24
24
  "test": "eslint src"
25
25
  },
26
26
  "devDependencies": {
@@ -1,9 +0,0 @@
1
- import { type ReadonlyRef, type Ref, type RefValues } from '../component.ts';
2
- /** Create an observable value. */
3
- export declare function useRef<T>(initialValue: T, onChange?: (value: T) => void): Ref<T>;
4
- /** React to observable (reference) changes. */
5
- export declare function useEffect<const TDeps extends readonly ReadonlyRef<any>[]>(deps: TDeps, callback: (...values: RefValues<TDeps>) => (() => void) | void): void;
6
- /** React to child list changes (non-recursive). */
7
- export declare function useChildEffect(callback: () => (() => void) | void): void;
8
- /** Observe attribute changes. */
9
- export declare function useAttributes<TName extends string>(...names: TName[]): Readonly<Record<TName, Ref<string | null>>>;
@@ -1,44 +0,0 @@
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 o().useRef(e, t);
7
- }
8
- function r(t, n) {
9
- let { onSetRef: r, onDisconnect: i } = o(), a = e(), s = () => a.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 && a.push(() => r());
16
- }), i.push(s);
17
- }
18
- function i(e) {
19
- let { host: t } = o(), i = n(0), a = new MutationObserver((e) => {
20
- e.some((e) => e.type === "childList") && (i.value = (i.value + 1) % (2 ** 53 - 1));
21
- });
22
- r([], () => (a.observe(t, { childList: !0 }), () => a.disconnect())), r([i], () => e());
23
- }
24
- function a(...e) {
25
- if (e.length === 0) return {};
26
- let { host: t } = o(), i = Object.fromEntries(e.map((e) => [e, n(t.getAttribute(e), (n) => {
27
- n == null ? t.removeAttribute(e) : t.setAttribute(e, n);
28
- })])), a = new MutationObserver((e) => {
29
- for (let { attributeName: n } of e) n != null && Object.hasOwn(i, n) && (i[n].value = t.getAttribute(n));
30
- });
31
- return r([], () => (a.observe(t, {
32
- attributeFilter: e,
33
- attributes: !0
34
- }), () => a.disconnect())), i;
35
- }
36
- function o() {
37
- let e = window[t].at(-1);
38
- if (!e) throw Error("hooks must be called inside a render function");
39
- return e;
40
- }
41
- //#endregion
42
- export { a as useAttributes, i as useChildEffect, r as useEffect, n as useRef };
43
-
44
- //# sourceMappingURL=core.js.map
@@ -1 +0,0 @@
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/** React to child list changes (non-recursive). */\nexport function useChildEffect(callback: () => (() => void) | void): void {\n const { host } = getHookContext();\n const ref = useRef(0);\n\n const observer = new MutationObserver((mutation) => {\n if (mutation.some((m) => m.type === 'childList')) {\n ref.value = (ref.value + 1) % Number.MAX_SAFE_INTEGER;\n }\n });\n\n useEffect([], () => {\n observer.observe(host, { childList: true });\n return () => observer.disconnect();\n });\n\n useEffect([ref], () => {\n return callback();\n });\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,EAAe,GAA2C;CACxE,IAAM,EAAE,YAAS,GAAgB,EAC3B,IAAM,EAAO,EAAE,EAEf,IAAW,IAAI,kBAAkB,MAAa;AAClD,EAAI,EAAS,MAAM,MAAM,EAAE,SAAS,YAAY,KAC9C,EAAI,SAAS,EAAI,QAAQ;GAE3B;AAOF,CALA,EAAU,EAAE,SACV,EAAS,QAAQ,GAAM,EAAE,WAAW,IAAM,CAAC,QAC9B,EAAS,YAAY,EAClC,EAEF,EAAU,CAAC,EAAI,QACN,GAAU,CACjB;;AAIJ,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"}
@@ -1,2 +0,0 @@
1
- export declare const $$ref: unique symbol;
2
- export declare const $$renderContextStack: unique symbol;
@@ -1,6 +0,0 @@
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
@@ -1 +0,0 @@
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"}