@seahax/elemental 0.4.2 → 0.5.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 (54) hide show
  1. package/README.md +67 -14
  2. package/dist/component.d.ts +5 -0
  3. package/dist/component.js +42 -19
  4. package/dist/component.js.map +1 -1
  5. package/dist/hooks/async.d.ts +11 -0
  6. package/dist/hooks/{loading.js → async.js} +3 -3
  7. package/dist/hooks/async.js.map +1 -0
  8. package/dist/hooks/{child-effect.js → child.js} +2 -2
  9. package/dist/hooks/child.js.map +1 -0
  10. package/dist/hooks/controller.d.ts +3 -0
  11. package/dist/hooks/controller.js +11 -0
  12. package/dist/hooks/controller.js.map +1 -0
  13. package/dist/hooks/disconnect.d.ts +2 -0
  14. package/dist/hooks/disconnect.js +9 -0
  15. package/dist/hooks/disconnect.js.map +1 -0
  16. package/dist/hooks/document.d.ts +3 -0
  17. package/dist/hooks/document.js +9 -0
  18. package/dist/hooks/document.js.map +1 -0
  19. package/dist/hooks/effect.d.ts +0 -6
  20. package/dist/hooks/effect.js +10 -13
  21. package/dist/hooks/effect.js.map +1 -1
  22. package/dist/hooks/form.d.ts +9 -0
  23. package/dist/hooks/form.js +23 -0
  24. package/dist/hooks/form.js.map +1 -0
  25. package/dist/hooks/host.d.ts +1 -1
  26. package/dist/hooks/host.js +1 -1
  27. package/dist/hooks/host.js.map +1 -1
  28. package/dist/hooks/internals.d.ts +2 -0
  29. package/dist/hooks/internals.js +9 -0
  30. package/dist/hooks/internals.js.map +1 -0
  31. package/dist/hooks/parent.d.ts +3 -0
  32. package/dist/hooks/parent.js +9 -0
  33. package/dist/hooks/parent.js.map +1 -0
  34. package/dist/hooks/ref.d.ts +1 -2
  35. package/dist/hooks/ref.js +4 -14
  36. package/dist/hooks/ref.js.map +1 -1
  37. package/dist/hooks/route.d.ts +2 -2
  38. package/dist/hooks/route.js.map +1 -1
  39. package/dist/index.d.ts +7 -2
  40. package/dist/index.js +16 -11
  41. package/dist/internal/controller.d.ts +33 -0
  42. package/dist/internal/controller.js +72 -0
  43. package/dist/internal/controller.js.map +1 -0
  44. package/package.json +1 -1
  45. package/dist/hooks/child-effect.js.map +0 -1
  46. package/dist/hooks/context.d.ts +0 -3
  47. package/dist/hooks/context.js +0 -11
  48. package/dist/hooks/context.js.map +0 -1
  49. package/dist/hooks/loading.d.ts +0 -11
  50. package/dist/hooks/loading.js.map +0 -1
  51. package/dist/internal/context.d.ts +0 -13
  52. package/dist/internal/context.js +0 -32
  53. package/dist/internal/context.js.map +0 -1
  54. /package/dist/hooks/{child-effect.d.ts → child.d.ts} +0 -0
package/README.md CHANGED
@@ -23,16 +23,19 @@ Contains everything you need to build anything from a single component up to a f
23
23
  ```ts
24
24
  import {
25
25
  defineComponent,
26
+ h,
26
27
  useRef,
27
28
  useStore,
28
29
  useAttributes,
30
+ useParent,
31
+ useDocument,
29
32
  useRoute,
30
- useLoading,
33
+ useAsync,
31
34
  useEffect,
32
35
  useChildEffect,
33
- useDisconnectEffect,
36
+ useDisconnectCallback,
37
+ useElementInternals,
34
38
  useHost,
35
- h,
36
39
  } from '@seahax/elemental';
37
40
 
38
41
  export const MyComponent = defineComponent((shadow) => {
@@ -65,9 +68,15 @@ export const MyComponent = defineComponent((shadow) => {
65
68
  // Use a reference (reactive state) bound to a (shared) store.
66
69
  const globalStateRef = useStore(myStore, select, mutate);
67
70
 
68
- // Use references (reactive state) bound to attributes.
71
+ // Use references (reactive state) bound to the component's attributes.
69
72
  const [dataValueRef, ...] = useAttributes('data-value', ...);
70
73
 
74
+ // Use a reference (reactive state) bound to the component's parent node.
75
+ const parentNode = useParent();
76
+
77
+ // Use a reference (reactive state) bound to the component's owner document.
78
+ const ownerDocument = useDocument();
79
+
71
80
  // Use a reference (reactive state) bound to route matching.
72
81
  const routeMatchRef = useRoute('/path/', {
73
82
  match: 'prefix', // 'exact' | 'prefix' | RegExp
@@ -75,7 +84,7 @@ export const MyComponent = defineComponent((shadow) => {
75
84
  });
76
85
 
77
86
  // Use a reference (reactive state) bound to an async loader function.
78
- const loadingStateRef = useLoading([
87
+ const asyncRef = useAsync([
79
88
  // dependency references
80
89
  ], async (signal, ...dependencyValues) => {
81
90
  // Reactive async code runs when the component is connected to the
@@ -91,8 +100,7 @@ export const MyComponent = defineComponent((shadow) => {
91
100
  globalStateRef,
92
101
  dataValueRef,
93
102
  routeMatchRef,
94
- routeStateRef,
95
- loadingStateRef,
103
+ asyncRef,
96
104
  ], (...dependencyValues) => {
97
105
  // Reactive code runs when the component is connected to the document,
98
106
  // and when any of the dependencies change.
@@ -116,29 +124,74 @@ export const MyComponent = defineComponent((shadow) => {
116
124
  };
117
125
  });
118
126
 
119
- useDisconnectEffect(() => {
120
- // Reactive code runs when the component is disconnected from the
121
- // document.
127
+ // Register a document disconnection callback.
128
+ useDisconnectCallback(() => {
129
+ // Called when the component is disconnected from the document.
122
130
  });
123
131
 
124
- // Use the host element (generally only useful in reusable hooks).
132
+ // Use the element's internals.
133
+ const elementInternals = useElementInternals();
134
+
135
+ // Use the component host element.
125
136
  const host = useHost();
126
137
  });
127
138
  ```
128
139
 
140
+ ## Enable Form Association
141
+
142
+ ```ts
143
+ import {
144
+ useElementInternals,
145
+ useForm,
146
+ useFormDisabled,
147
+ useFormResetCallback,
148
+ useFormRestoreCallback,
149
+ } from '@seahax/elemental';
150
+
151
+ const MyComponent = defineComponent(
152
+ (shadow) => {
153
+ // Use the element's internals.
154
+ const elementInternals = useElementInternals();
155
+
156
+ // Use a reference (reactive state) bound to the associated form.
157
+ const formRef = useForm();
158
+
159
+ // Use a reference (reactive state) bound to the form disabled state.
160
+ const formDisabledRef = useFormDisabled();
161
+
162
+ // Register a form reset callback.
163
+ useFormResetCallback(() => {
164
+ // Called when the associated form is reset. Only called on connect
165
+ // if the form was reset while the component was disconnected.
166
+ });
167
+
168
+ // Register a form restore callback.
169
+ useFormRestoreCallback((state, reason) => {
170
+ // Called when the associated form is restored. Only called on connect
171
+ // if the form was restored while the component was disconnected.
172
+ });
173
+ },
174
+ {
175
+ // Enable form association.
176
+ formAssociated: true,
177
+ }
178
+ );
179
+ ```
180
+
129
181
  ## Customize The Shadow Root
130
182
 
131
183
  ```ts
132
184
  const MyComponent = defineComponent(
133
185
  (shadow) => {
134
- // Renderer...
186
+ ...
135
187
  },
136
188
  {
137
- // Shadow root options.
189
+ // Use custom shadow root initialization options.
190
+ // (default: { mode: 'open' }).
138
191
  shadow: {
139
192
  mode: 'closed',
140
193
  ...
141
- }
194
+ },
142
195
  }
143
196
  );
144
197
  ```
@@ -3,11 +3,16 @@ type SafeProps<TProps> = any extends any ? {
3
3
  [P in keyof TProps as P extends keyof HTMLElement ? never : P]: TProps[P];
4
4
  } : never;
5
5
  export interface ComponentConstructor<TProps extends object> {
6
+ readonly formAssociated: boolean;
6
7
  new (): ComponentWithProps<TProps>;
7
8
  }
8
9
  export interface ComponentOptions<TProps extends object> {
10
+ /** Shadow root attachment options. */
9
11
  readonly shadow?: Partial<ShadowRootInit>;
12
+ /** Component custom property descriptors. */
10
13
  readonly props?: ComponentPropDescriptors<TProps>;
14
+ /** True to mark the component as form-associated. */
15
+ readonly formAssociated?: boolean;
11
16
  }
12
17
  export type ComponentPropDescriptors<TProps extends object> = {
13
18
  readonly [P in keyof SafeProps<TProps>]: ComponentPropDescriptorFactory<TProps[P]>;
package/dist/component.js CHANGED
@@ -1,34 +1,57 @@
1
- import { createContextController as e } from "./internal/context.js";
2
- import { useRef as t } from "./hooks/ref.js";
1
+ import { createController as e } from "./internal/controller.js";
3
2
  //#region src/component.ts
4
- function n(n, { props: r, shadow: i } = {}) {
3
+ function t(t, { props: n, shadow: r, formAssociated: i = !1 } = {}) {
5
4
  return class extends HTMLElement {
6
- #e = {};
7
- #t = e(this);
5
+ static formAssociated = i;
6
+ #e;
7
+ #t;
8
+ #n = {};
8
9
  constructor() {
9
- if (super(), r) {
10
- let e = this.#e;
11
- for (let [n, i] of Object.entries(r)) {
12
- if (n in this) continue;
13
- let r = i(e[n] = t(void 0), this);
14
- Object.defineProperty(this, n, r);
10
+ if (super(), this.#e = this.attachShadow({
11
+ ...r,
12
+ mode: r?.mode ?? "open"
13
+ }), this.#t = e({
14
+ host: this,
15
+ formAssociated: i,
16
+ render: () => t(this.#e, this.#n),
17
+ attachInternals: () => super.attachInternals()
18
+ }), n) {
19
+ let e = this.#n;
20
+ for (let [t, r] of Object.entries(n)) {
21
+ if (t in this) continue;
22
+ let n = r(e[t] = this.#t.createRef(void 0), this);
23
+ Object.defineProperty(this, t, n);
15
24
  }
16
25
  }
26
+ i && this.attachInternals();
27
+ }
28
+ attachInternals() {
29
+ return this.#t.attachInternals();
17
30
  }
18
31
  connectedCallback() {
19
- this.#t.connect(() => {
20
- n(this.attachShadow({
21
- ...i,
22
- mode: i?.mode ?? "open"
23
- }), this.#e);
24
- });
32
+ this.#t.connectedCallback();
33
+ }
34
+ connectedMoveCallback() {
35
+ this.#t.connectedMoveCallback();
25
36
  }
26
37
  disconnectedCallback() {
27
- this.#t.disconnect();
38
+ this.#t.disconnectedCallback();
39
+ }
40
+ adoptedCallback() {
41
+ this.#t.adoptedCallback();
42
+ }
43
+ formDisabledCallback(e) {
44
+ this.#t.formDisabledCallback(e);
45
+ }
46
+ formResetCallback() {
47
+ this.#t.formResetCallback();
48
+ }
49
+ formStateRestoreCallback(e, t) {
50
+ this.#t.formStateRestoreCallback(e, t);
28
51
  }
29
52
  };
30
53
  }
31
54
  //#endregion
32
- export { n as defineComponent };
55
+ export { t as defineComponent };
33
56
 
34
57
  //# sourceMappingURL=component.js.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"component.js","names":["#shadow","#controller","#props"],"sources":["../src/component.ts"],"sourcesContent":["import { type Ref } from './hooks/ref.ts';\nimport { type Controller, createController } from './internal/controller.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 readonly formAssociated: boolean;\n new (): ComponentWithProps<TProps>;\n}\n\nexport interface ComponentOptions<TProps extends object> {\n /** Shadow root attachment options. */\n readonly shadow?: Partial<ShadowRootInit>;\n /** Component custom property descriptors. */\n readonly props?: ComponentPropDescriptors<TProps>;\n /** True to mark the component as form-associated. */\n readonly formAssociated?: boolean;\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, formAssociated = false }: ComponentOptions<Record<string, unknown>> = {},\n): ComponentConstructor<{}> {\n return class extends HTMLElement {\n static readonly formAssociated = formAssociated;\n\n readonly #shadow: ComponentShadowRoot<Record<string, unknown>>;\n readonly #controller: Controller;\n readonly #props: ComponentPropRefs<Record<string, unknown>> = {};\n\n constructor() {\n super();\n\n this.#shadow = this.attachShadow({\n ...shadow,\n mode: shadow?.mode ?? 'open',\n }) as ComponentShadowRoot<Record<string, unknown>>;\n\n this.#controller = createController({\n host: this,\n formAssociated,\n render: () => render(this.#shadow, this.#props),\n attachInternals: () => super.attachInternals(),\n });\n\n if (props) {\n const propRefs: Record<string, Ref<unknown>> = this.#props;\n\n for (const [key, getDescriptor] of Object.entries(props)) {\n if (key in this) continue;\n const ref = (propRefs[key] = this.#controller.createRef<any>(undefined));\n const descriptor = getDescriptor(ref, this);\n Object.defineProperty(this, key, descriptor);\n }\n }\n\n if (formAssociated) {\n this.attachInternals();\n }\n }\n\n override attachInternals(): ElementInternals {\n return this.#controller.attachInternals();\n }\n\n protected connectedCallback(): void {\n this.#controller.connectedCallback();\n }\n\n protected connectedMoveCallback(): void {\n this.#controller.connectedMoveCallback();\n }\n\n protected disconnectedCallback(): void {\n this.#controller.disconnectedCallback();\n }\n\n protected adoptedCallback(): void {\n this.#controller.adoptedCallback();\n }\n\n protected formDisabledCallback(disabled: boolean): void {\n this.#controller.formDisabledCallback(disabled);\n }\n\n protected formResetCallback(): void {\n this.#controller.formResetCallback();\n }\n\n protected formStateRestoreCallback(state: string | File | FormData, reason: 'restore' | 'autocomplete'): void {\n this.#controller.formStateRestoreCallback(state, reason);\n }\n };\n}\n"],"mappings":";;AAoDA,SAAgB,EACd,GAIA,EAAE,UAAO,WAAQ,oBAAiB,OAAqD,EAAE,EAC/D;AAC1B,QAAO,cAAc,YAAY;EAC/B,OAAgB,iBAAiB;EAEjC;EACA;EACA,KAA8D,EAAE;EAEhE,cAAc;AAeZ,OAdA,OAAO,EAEP,MAAA,IAAe,KAAK,aAAa;IAC/B,GAAG;IACH,MAAM,GAAQ,QAAQ;IACvB,CAAC,EAEF,MAAA,IAAmB,EAAiB;IAClC,MAAM;IACN;IACA,cAAc,EAAO,MAAA,GAAc,MAAA,EAAY;IAC/C,uBAAuB,MAAM,iBAAiB;IAC/C,CAAC,EAEE,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,MAAA,EAAiB,UAAe,KAAA,EAAU,EACjC,KAAK;AAC3C,YAAO,eAAe,MAAM,GAAK,EAAW;;;AAIhD,GAAI,KACF,KAAK,iBAAiB;;EAI1B,kBAA6C;AAC3C,UAAO,MAAA,EAAiB,iBAAiB;;EAG3C,oBAAoC;AAClC,SAAA,EAAiB,mBAAmB;;EAGtC,wBAAwC;AACtC,SAAA,EAAiB,uBAAuB;;EAG1C,uBAAuC;AACrC,SAAA,EAAiB,sBAAsB;;EAGzC,kBAAkC;AAChC,SAAA,EAAiB,iBAAiB;;EAGpC,qBAA+B,GAAyB;AACtD,SAAA,EAAiB,qBAAqB,EAAS;;EAGjD,oBAAoC;AAClC,SAAA,EAAiB,mBAAmB;;EAGtC,yBAAmC,GAAiC,GAA0C;AAC5G,SAAA,EAAiB,yBAAyB,GAAO,EAAO"}
@@ -0,0 +1,11 @@
1
+ import { type ReadonlyRef, type Ref, type RefValues } from './ref.ts';
2
+ export interface AsyncValue<TValue> {
3
+ readonly value: TValue | undefined;
4
+ readonly error: unknown;
5
+ readonly isLoading: boolean;
6
+ }
7
+ export interface AsyncOptions {
8
+ readonly debounceMs?: number;
9
+ }
10
+ /** Use a reference (reactive state) bound to an async loader function. */
11
+ export declare function useAsync<const TDeps extends readonly ReadonlyRef<any>[], TValue>(deps: TDeps, callback: (signal: AbortSignal, ...values: RefValues<TDeps>) => Promise<TValue>, { debounceMs }?: AsyncOptions): Ref<AsyncValue<TValue>>;
@@ -1,6 +1,6 @@
1
1
  import { useRef as e } from "./ref.js";
2
2
  import { useEffect as t } from "./effect.js";
3
- //#region src/hooks/loading.ts
3
+ //#region src/hooks/async.ts
4
4
  function n(n, r, { debounceMs: i } = {}) {
5
5
  let a = e({
6
6
  value: void 0,
@@ -27,6 +27,6 @@ function n(n, r, { debounceMs: i } = {}) {
27
27
  }), t([], () => () => o = !0), a;
28
28
  }
29
29
  //#endregion
30
- export { n as useLoading };
30
+ export { n as useAsync };
31
31
 
32
- //# sourceMappingURL=loading.js.map
32
+ //# sourceMappingURL=async.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async.js","names":[],"sources":["../../src/hooks/async.ts"],"sourcesContent":["import { useEffect } from './effect.ts';\nimport { type ReadonlyRef, type Ref, type RefValues, useRef } from './ref.ts';\n\nexport interface AsyncValue<TValue> {\n readonly value: TValue | undefined;\n readonly error: unknown;\n readonly isLoading: boolean;\n}\n\nexport interface AsyncOptions {\n readonly debounceMs?: number;\n}\n\n/** Use a reference (reactive state) bound to an async loader function. */\nexport function useAsync<const TDeps extends readonly ReadonlyRef<any>[], TValue>(\n deps: TDeps,\n callback: (signal: AbortSignal, ...values: RefValues<TDeps>) => Promise<TValue>,\n { debounceMs }: AsyncOptions = {},\n): Ref<AsyncValue<TValue>> {\n const ref = useRef<AsyncValue<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,kBAA6B,EAAE,EACR;CACzB,IAAM,IAAM,EAA2B;EAAE,OAAO,KAAA;EAAW,OAAO,KAAA;EAAW,WAAW;EAAM,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,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"}
@@ -1,7 +1,7 @@
1
1
  import { useRef as e } from "./ref.js";
2
2
  import { useEffect as t } from "./effect.js";
3
3
  import { useHost as n } from "./host.js";
4
- //#region src/hooks/child-effect.ts
4
+ //#region src/hooks/child.ts
5
5
  function r(r) {
6
6
  let i = n(), a = e(0), o = new MutationObserver((e) => {
7
7
  e.some((e) => e.type === "childList") && (a.value = (a.value + 1) % (2 ** 53 - 1));
@@ -11,4 +11,4 @@ function r(r) {
11
11
  //#endregion
12
12
  export { r as useChildEffect };
13
13
 
14
- //# sourceMappingURL=child-effect.js.map
14
+ //# sourceMappingURL=child.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child.js","names":[],"sources":["../../src/hooks/child.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 Controller } from '../internal/controller.ts';
2
+ /** @internal Get the controller of the currently rendering component. */
3
+ export declare function useController(): Controller;
@@ -0,0 +1,11 @@
1
+ import { getController as e } from "../internal/controller.js";
2
+ //#region src/hooks/controller.ts
3
+ function t() {
4
+ let t = e();
5
+ if (!t) throw Error("hooks must be called by a render function");
6
+ return t;
7
+ }
8
+ //#endregion
9
+ export { t as useController };
10
+
11
+ //# sourceMappingURL=controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.js","names":[],"sources":["../../src/hooks/controller.ts"],"sourcesContent":["import { type Controller, getController } from '../internal/controller.ts';\n\n/** @internal Get the controller of the currently rendering component. */\nexport function useController(): Controller {\n const controller = getController();\n if (!controller) throw new Error('hooks must be called by a render function');\n return controller;\n}\n"],"mappings":";;AAGA,SAAgB,IAA4B;CAC1C,IAAM,IAAa,GAAe;AAClC,KAAI,CAAC,EAAY,OAAU,MAAM,4CAA4C;AAC7E,QAAO"}
@@ -0,0 +1,2 @@
1
+ /** Register a callback for disconnections. */
2
+ export declare function useDisconnectCallback(callback: () => void): void;
@@ -0,0 +1,9 @@
1
+ import { useController as e } from "./controller.js";
2
+ //#region src/hooks/disconnect.ts
3
+ function t(t) {
4
+ e().onDisconnect.push(t);
5
+ }
6
+ //#endregion
7
+ export { t as useDisconnectCallback };
8
+
9
+ //# sourceMappingURL=disconnect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disconnect.js","names":[],"sources":["../../src/hooks/disconnect.ts"],"sourcesContent":["import { useController } from './controller.ts';\n\n/** Register a callback for disconnections. */\nexport function useDisconnectCallback(callback: () => void): void {\n useController().onDisconnect.push(callback);\n}\n"],"mappings":";;AAGA,SAAgB,EAAsB,GAA4B;AAChE,IAAe,CAAC,aAAa,KAAK,EAAS"}
@@ -0,0 +1,3 @@
1
+ import type { ReadonlyRef } from './ref.ts';
2
+ /** Use a reference (reactive state) bound to the owner document. */
3
+ export declare function useDocument(): ReadonlyRef<Document>;
@@ -0,0 +1,9 @@
1
+ import { useController as e } from "./controller.js";
2
+ //#region src/hooks/document.ts
3
+ function t() {
4
+ return e().refDocument;
5
+ }
6
+ //#endregion
7
+ export { t as useDocument };
8
+
9
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","names":[],"sources":["../../src/hooks/document.ts"],"sourcesContent":["import { useController } from './controller.ts';\nimport type { ReadonlyRef } from './ref.ts';\n\n/** Use a reference (reactive state) bound to the owner document. */\nexport function useDocument(): ReadonlyRef<Document> {\n return useController().refDocument;\n}\n"],"mappings":";;AAIA,SAAgB,IAAqC;AACnD,QAAO,GAAe,CAAC"}
@@ -1,9 +1,3 @@
1
1
  import type { ReadonlyRef, RefValues } from './ref.ts';
2
2
  /** React to observable (reference) changes. */
3
3
  export declare function useEffect<const TDeps extends readonly ReadonlyRef<any>[]>(deps: TDeps, callback: (...values: RefValues<TDeps>) => (() => void) | void): void;
4
- /**
5
- * React to document disconnection.
6
- *
7
- * Alias for: `useEffect([], () => callback)`
8
- */
9
- export declare function useDisconnectEffect(callback: () => void): void;
@@ -1,20 +1,17 @@
1
- import { createCallbacks as e } from "../internal/callbacks.js";
2
- import { useContext as t } from "./context.js";
1
+ import { useController as e } from "./controller.js";
2
+ import { useDisconnectCallback as t } from "./disconnect.js";
3
3
  //#region src/hooks/effect.ts
4
4
  function n(n, r) {
5
- let { onNotify: i, onDisconnect: a } = t(), o = e(), s = () => o.runAndClear(), c;
6
- i.push(() => {
5
+ let i, a, o = () => {
6
+ let e = i;
7
+ i = void 0, e?.();
8
+ };
9
+ e().onNotify.push(() => {
7
10
  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
- function r(e) {
15
- n([], () => e);
11
+ a?.length === e.length && a?.every((t, n) => t === e[n]) || (a = e, o(), i = r(...a));
12
+ }), t(o);
16
13
  }
17
14
  //#endregion
18
- export { r as useDisconnectEffect, n as useEffect };
15
+ export { n as useEffect };
19
16
 
20
17
  //# sourceMappingURL=effect.js.map
@@ -1 +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\n/**\n * React to document disconnection.\n *\n * Alias for: `useEffect([], () => callback)`\n */\nexport function useDisconnectEffect(callback: () => void): void {\n useEffect([], () => callback);\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;;AAQ5B,SAAgB,EAAoB,GAA4B;AAC9D,GAAU,EAAE,QAAQ,EAAS"}
1
+ {"version":3,"file":"effect.js","names":[],"sources":["../../src/hooks/effect.ts"],"sourcesContent":["import { useController } from './controller.ts';\nimport { useDisconnectCallback } from './disconnect.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 let cleanupCallback: (() => void) | void;\n let values: any[] | undefined;\n\n const cleanup = () => {\n const callback = cleanupCallback;\n cleanupCallback = undefined;\n callback?.();\n };\n\n useController().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 cleanupCallback = callback(...(values as any));\n });\n\n useDisconnectCallback(cleanup);\n}\n"],"mappings":";;;AAKA,SAAgB,EACd,GACA,GACM;CACN,IAAI,GACA,GAEE,UAAgB;EACpB,IAAM,IAAW;AAEjB,EADA,IAAkB,KAAA,GAClB,KAAY;;AAWd,CARA,GAAe,CAAC,SAAS,WAAiB;EACxC,IAAM,IAAY,EAAK,KAAK,MAAQ,EAAI,MAAM;AAC1C,KAAQ,WAAW,EAAU,UAAU,GAAQ,OAAO,GAAO,MAAM,MAAU,EAAU,GAAG,KAC9F,IAAS,GACT,GAAS,EACT,IAAkB,EAAS,GAAI,EAAe;GAC9C,EAEF,EAAsB,EAAQ"}
@@ -0,0 +1,9 @@
1
+ import type { ReadonlyRef } from './ref.ts';
2
+ /** Use a reference (reactive state) bound to the associated form. */
3
+ export declare function useForm(): ReadonlyRef<HTMLFormElement | null>;
4
+ /** Use a reference (reactive state) bound to the form disabled state. */
5
+ export declare function useFormDisabled(): ReadonlyRef<boolean>;
6
+ /** Register a callback for form resets. */
7
+ export declare function useFormResetCallback(callback: () => void): void;
8
+ /** Register a callback for form restorations. */
9
+ export declare function useFormRestoreCallback(callback: (state: string | File | FormData, reason: 'restore' | 'autocomplete') => void): void;
@@ -0,0 +1,23 @@
1
+ import { useController as e } from "./controller.js";
2
+ //#region src/hooks/form.ts
3
+ function t() {
4
+ return a().refForm;
5
+ }
6
+ function n() {
7
+ return a().refDisabled;
8
+ }
9
+ function r(e) {
10
+ a().onReset.push(e);
11
+ }
12
+ function i(e) {
13
+ a().onRestore.push(e);
14
+ }
15
+ function a() {
16
+ let { formAssociated: t } = e();
17
+ if (!t) throw Error("form hooks must be called in a form-associated component");
18
+ return t;
19
+ }
20
+ //#endregion
21
+ export { t as useForm, n as useFormDisabled, r as useFormResetCallback, i as useFormRestoreCallback };
22
+
23
+ //# sourceMappingURL=form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.js","names":[],"sources":["../../src/hooks/form.ts"],"sourcesContent":["import type { Controller } from '../internal/controller.ts';\nimport { useController } from './controller.ts';\nimport type { ReadonlyRef } from './ref.ts';\n\n/** Use a reference (reactive state) bound to the associated form. */\nexport function useForm(): ReadonlyRef<HTMLFormElement | null> {\n return useFormAssociated().refForm;\n}\n\n/** Use a reference (reactive state) bound to the form disabled state. */\nexport function useFormDisabled(): ReadonlyRef<boolean> {\n return useFormAssociated().refDisabled;\n}\n\n/** Register a callback for form resets. */\nexport function useFormResetCallback(callback: () => void): void {\n useFormAssociated().onReset.push(callback);\n}\n\n/** Register a callback for form restorations. */\nexport function useFormRestoreCallback(\n callback: (state: string | File | FormData, reason: 'restore' | 'autocomplete') => void,\n): void {\n useFormAssociated().onRestore.push(callback);\n}\n\nfunction useFormAssociated(): Controller['formAssociated'] & {} {\n const { formAssociated } = useController();\n if (!formAssociated) throw new Error('form hooks must be called in a form-associated component');\n return formAssociated;\n}\n"],"mappings":";;AAKA,SAAgB,IAA+C;AAC7D,QAAO,GAAmB,CAAC;;AAI7B,SAAgB,IAAwC;AACtD,QAAO,GAAmB,CAAC;;AAI7B,SAAgB,EAAqB,GAA4B;AAC/D,IAAmB,CAAC,QAAQ,KAAK,EAAS;;AAI5C,SAAgB,EACd,GACM;AACN,IAAmB,CAAC,UAAU,KAAK,EAAS;;AAG9C,SAAS,IAAuD;CAC9D,IAAM,EAAE,sBAAmB,GAAe;AAC1C,KAAI,CAAC,EAAgB,OAAU,MAAM,2DAA2D;AAChG,QAAO"}
@@ -1,2 +1,2 @@
1
- /** Get the component host element. */
1
+ /** Use the component host element. */
2
2
  export declare function useHost(): HTMLElement;
@@ -1,4 +1,4 @@
1
- import { useContext as e } from "./context.js";
1
+ import { useController as e } from "./controller.js";
2
2
  //#region src/hooks/host.ts
3
3
  function t() {
4
4
  return e().host;
@@ -1 +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
+ {"version":3,"file":"host.js","names":[],"sources":["../../src/hooks/host.ts"],"sourcesContent":["import { useController } from './controller.ts';\n\n/** Use the component host element. */\nexport function useHost(): HTMLElement {\n return useController().host;\n}\n"],"mappings":";;AAGA,SAAgB,IAAuB;AACrC,QAAO,GAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ /** Use the element internals. */
2
+ export declare function useElementInternals(): ElementInternals;
@@ -0,0 +1,9 @@
1
+ import { useController as e } from "./controller.js";
2
+ //#region src/hooks/internals.ts
3
+ function t() {
4
+ return e().attachInternals();
5
+ }
6
+ //#endregion
7
+ export { t as useElementInternals };
8
+
9
+ //# sourceMappingURL=internals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internals.js","names":[],"sources":["../../src/hooks/internals.ts"],"sourcesContent":["import { useController } from './controller.ts';\n\n/** Use the element internals. */\nexport function useElementInternals(): ElementInternals {\n return useController().attachInternals();\n}\n"],"mappings":";;AAGA,SAAgB,IAAwC;AACtD,QAAO,GAAe,CAAC,iBAAiB"}
@@ -0,0 +1,3 @@
1
+ import type { ReadonlyRef } from './ref.ts';
2
+ /** Use a reference (reactive state) bound to the parent node. */
3
+ export declare function useParent(): ReadonlyRef<ParentNode | null>;
@@ -0,0 +1,9 @@
1
+ import { useController as e } from "./controller.js";
2
+ //#region src/hooks/parent.ts
3
+ function t() {
4
+ return e().refParent;
5
+ }
6
+ //#endregion
7
+ export { t as useParent };
8
+
9
+ //# sourceMappingURL=parent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parent.js","names":[],"sources":["../../src/hooks/parent.ts"],"sourcesContent":["import { useController } from './controller.ts';\nimport type { ReadonlyRef } from './ref.ts';\n\n/** Use a reference (reactive state) bound to the parent node. */\nexport function useParent(): ReadonlyRef<ParentNode | null> {\n return useController().refParent;\n}\n"],"mappings":";;AAIA,SAAgB,IAA4C;AAC1D,QAAO,GAAe,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { $$ref } from '../internal/controller.ts';
1
2
  export interface Ref<T> extends ReadonlyRef<T> {
2
3
  value: T;
3
4
  }
@@ -9,7 +10,5 @@ export interface ReadonlyRef<T> {
9
10
  export type RefValues<T> = T extends readonly any[] ? {
10
11
  [K in keyof T]: T[K] extends ReadonlyRef<infer V> ? V : never;
11
12
  } : never;
12
- declare const $$ref: unique symbol;
13
13
  /** Use a reference (reactive state) value. */
14
14
  export declare function useRef<T>(initialValue: T, onChange?: (value: T) => void): Ref<T>;
15
- export {};
package/dist/hooks/ref.js CHANGED
@@ -1,19 +1,9 @@
1
- import { useContext as e } from "./context.js";
1
+ import { useController as e } from "./controller.js";
2
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
- };
3
+ function t(t, n) {
4
+ return e().createRef(t, n);
15
5
  }
16
6
  //#endregion
17
- export { n as useRef };
7
+ export { t as useRef };
18
8
 
19
9
  //# sourceMappingURL=ref.js.map
@@ -1 +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
+ {"version":3,"file":"ref.js","names":[],"sources":["../../src/hooks/ref.ts"],"sourcesContent":["import type { $$ref } from '../internal/controller.ts';\nimport { useController } from './controller.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\n/** Use a reference (reactive state) value. */\nexport function useRef<T>(initialValue: T, onChange?: (value: T) => void): Ref<T> {\n return useController().createRef(initialValue, onChange);\n}\n"],"mappings":";;AAkBA,SAAgB,EAAU,GAAiB,GAAuC;AAChF,QAAO,GAAe,CAAC,UAAU,GAAc,EAAS"}
@@ -3,8 +3,8 @@ export interface RouteOptions {
3
3
  readonly match?: 'prefix' | 'exact' | RegExp;
4
4
  readonly source?: 'pathname' | 'hash';
5
5
  }
6
- export type RouteMatchArray = readonly [string, ...string[]] & {
6
+ export type RouteMatch = readonly [string, ...string[]] & {
7
7
  readonly groups: Record<string, string>;
8
8
  };
9
9
  /** Use a reference (reactive state) bound to route matching. */
10
- export declare function useRoute(path: string | readonly string[], { match, source }?: RouteOptions): Ref<RouteMatchArray | null>;
10
+ export declare function useRoute(path: string | readonly string[], { match, source }?: RouteOptions): Ref<RouteMatch | null>;
@@ -1 +1 @@
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
+ {"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 RouteMatch = 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<RouteMatch | 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<RouteMatch | 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): RouteMatch | 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 RouteMatch | null;\n }\n}\n"],"mappings":";;;;;AAaA,SAAgB,EACd,GACA,EAAE,WAAQ,UAAU,YAAS,eAA6B,EAAE,EACpC;CACxB,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,EAA0B,EAAS,EAAS,MAAM,CAAC;AAMpE,QAJA,EAAU,CAAC,EAAS,GAAG,MAAU;AAC/B,IAAS,QAAQ,EAAS,EAAM;GAChC,EAEK;CAEP,SAAS,EAAS,GAAkC;EAClD,IAAM,IAAS,EAAM,MAAM,MAAS,EAAM,SAAS,EAAK,CAAC,IAAI;AAG7D,SAFI,KAAU,OAAa,QAC3B,IAAQ,EAAM,MAAM,EAAO,OAAO,EAC3B,EAAM,MAAM,EAAQ"}
package/dist/index.d.ts CHANGED
@@ -1,9 +1,14 @@
1
1
  export * from './component.ts';
2
+ export * from './hooks/async.ts';
2
3
  export * from './hooks/attributes.ts';
3
- export * from './hooks/child-effect.ts';
4
+ export * from './hooks/child.ts';
5
+ export * from './hooks/disconnect.ts';
6
+ export * from './hooks/document.ts';
4
7
  export * from './hooks/effect.ts';
8
+ export * from './hooks/form.ts';
5
9
  export * from './hooks/host.ts';
6
- export * from './hooks/loading.ts';
10
+ export * from './hooks/internals.ts';
11
+ export * from './hooks/parent.ts';
7
12
  export * from './hooks/ref.ts';
8
13
  export * from './hooks/route.ts';
9
14
  export * from './hooks/store.ts';
package/dist/index.js CHANGED
@@ -1,13 +1,18 @@
1
1
  import { useRef as e } from "./hooks/ref.js";
2
2
  import { defineComponent as t } from "./component.js";
3
- import { useDisconnectEffect as n, useEffect as r } from "./hooks/effect.js";
4
- import { useHost as i } from "./hooks/host.js";
5
- import { useAttributes as a } from "./hooks/attributes.js";
6
- import { useChildEffect as o } from "./hooks/child-effect.js";
7
- import { useLoading as s } from "./hooks/loading.js";
8
- import { createStore as c } from "./store.js";
9
- import { getRouter as l } from "./router.js";
10
- import { useStore as u } from "./hooks/store.js";
11
- import { useRoute as d } from "./hooks/route.js";
12
- import { html as f } from "./html.js";
13
- export { c as createStore, t as defineComponent, l as getRouter, f as h, f as html, a as useAttributes, o as useChildEffect, n as useDisconnectEffect, r as useEffect, i as useHost, s as useLoading, e as useRef, d as useRoute, u as useStore };
3
+ import { useDisconnectCallback as n } from "./hooks/disconnect.js";
4
+ import { useEffect as r } from "./hooks/effect.js";
5
+ import { useAsync as i } from "./hooks/async.js";
6
+ import { useHost as a } from "./hooks/host.js";
7
+ import { useAttributes as o } from "./hooks/attributes.js";
8
+ import { useChildEffect as s } from "./hooks/child.js";
9
+ import { useDocument as c } from "./hooks/document.js";
10
+ import { useForm as l, useFormDisabled as u, useFormResetCallback as d, useFormRestoreCallback as f } from "./hooks/form.js";
11
+ import { useElementInternals as p } from "./hooks/internals.js";
12
+ import { useParent as m } from "./hooks/parent.js";
13
+ import { createStore as h } from "./store.js";
14
+ import { getRouter as g } from "./router.js";
15
+ import { useStore as _ } from "./hooks/store.js";
16
+ import { useRoute as v } from "./hooks/route.js";
17
+ import { html as y } from "./html.js";
18
+ export { h as createStore, t as defineComponent, g as getRouter, y as h, y as html, i as useAsync, o as useAttributes, s as useChildEffect, n as useDisconnectCallback, c as useDocument, r as useEffect, p as useElementInternals, l as useForm, u as useFormDisabled, d as useFormResetCallback, f as useFormRestoreCallback, a as useHost, m as useParent, e as useRef, v as useRoute, _ as useStore };
@@ -0,0 +1,33 @@
1
+ import { type ReadonlyRef, type Ref } from '../hooks/ref.ts';
2
+ import { type Callbacks } from './callbacks.ts';
3
+ export interface Controller {
4
+ readonly host: HTMLElement;
5
+ readonly onNotify: Callbacks;
6
+ readonly onDisconnect: Callbacks;
7
+ readonly refDocument: ReadonlyRef<Document>;
8
+ readonly refParent: ReadonlyRef<ParentNode | null>;
9
+ readonly formAssociated: {
10
+ readonly refForm: ReadonlyRef<HTMLFormElement | null>;
11
+ readonly refDisabled: ReadonlyRef<boolean>;
12
+ readonly onReset: Callbacks;
13
+ readonly onRestore: Callbacks<[state: string | File | FormData, reason: 'restore' | 'autocomplete']>;
14
+ } | undefined;
15
+ readonly createRef: <T>(value: T, onChange?: (value: T) => void) => Ref<T>;
16
+ readonly attachInternals: () => ElementInternals;
17
+ readonly connectedCallback: () => void;
18
+ readonly connectedMoveCallback: () => void;
19
+ readonly disconnectedCallback: () => void;
20
+ readonly adoptedCallback: () => void;
21
+ readonly formDisabledCallback: (disabled: boolean) => void;
22
+ readonly formResetCallback: () => void;
23
+ readonly formStateRestoreCallback: (state: string | File | FormData, reason: 'restore' | 'autocomplete') => void;
24
+ }
25
+ export interface ControllerConfig {
26
+ readonly host: HTMLElement;
27
+ readonly formAssociated: boolean;
28
+ readonly render: (controller: Controller) => void;
29
+ readonly attachInternals: () => ElementInternals;
30
+ }
31
+ export declare const $$ref: unique symbol;
32
+ export declare function getController(): Controller | undefined;
33
+ export declare function createController({ host, formAssociated, render, attachInternals }: ControllerConfig): Controller;
@@ -0,0 +1,72 @@
1
+ import { createCallbacks as e } from "./callbacks.js";
2
+ //#region src/internal/controller.ts
3
+ var t = Symbol(), n = [];
4
+ function r() {
5
+ return n.at(-1);
6
+ }
7
+ function i({ host: r, formAssociated: i, render: a, attachInternals: o }) {
8
+ let s = !1, c = !1, l, u, d = e(), f = (e, n) => {
9
+ let r = e;
10
+ return {
11
+ [t]: !0,
12
+ get value() {
13
+ return r;
14
+ },
15
+ set value(e) {
16
+ e !== r && (r = e, n?.(r), !(!c || s) && (s = !0, queueMicrotask(() => {
17
+ s = !1, d.run();
18
+ })));
19
+ }
20
+ };
21
+ }, p = {
22
+ host: r,
23
+ onNotify: d,
24
+ onDisconnect: e(),
25
+ refDocument: f(r.ownerDocument),
26
+ refParent: f(r.parentNode),
27
+ formAssociated: i ? {
28
+ refForm: f(null),
29
+ refDisabled: f(!1),
30
+ onReset: e(),
31
+ onRestore: e()
32
+ } : void 0,
33
+ createRef: f,
34
+ attachInternals: () => l ??= o(),
35
+ connectedCallback: () => {
36
+ c = !0, p.refParent.value = r.parentNode;
37
+ try {
38
+ n.push(p), a(p);
39
+ } finally {
40
+ n.pop();
41
+ }
42
+ u?.type === "reset" ? p.formAssociated?.onReset.run() : u?.type === "restore" && p.formAssociated?.onRestore.run(u.state, u.reason), p.onNotify.run();
43
+ },
44
+ connectedMoveCallback: () => {
45
+ p.refParent.value = r.parentNode, p.onNotify.run();
46
+ },
47
+ disconnectedCallback: () => {
48
+ p.onNotify.clear(), p.formAssociated?.onReset.clear(), p.formAssociated?.onRestore.clear(), p.onDisconnect.runAndClear();
49
+ },
50
+ adoptedCallback: () => {
51
+ p.refDocument.value = r.ownerDocument, p.onNotify.run();
52
+ },
53
+ formDisabledCallback: (e) => {
54
+ p.formAssociated && (p.formAssociated.refDisabled.value = e, p.onNotify.run());
55
+ },
56
+ formResetCallback: () => {
57
+ p.formAssociated && (c ? p.formAssociated.onReset.run() : u = { type: "reset" });
58
+ },
59
+ formStateRestoreCallback: (e, t) => {
60
+ p.formAssociated && (c ? p.formAssociated.onRestore.run(e, t) : u = {
61
+ type: "restore",
62
+ state: e,
63
+ reason: t
64
+ });
65
+ }
66
+ };
67
+ return p;
68
+ }
69
+ //#endregion
70
+ export { i as createController, r as getController };
71
+
72
+ //# sourceMappingURL=controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.js","names":[],"sources":["../../src/internal/controller.ts"],"sourcesContent":["import { type ReadonlyRef, type Ref } from '../hooks/ref.ts';\nimport { type Callbacks, createCallbacks } from './callbacks.ts';\n\nexport interface Controller {\n readonly host: HTMLElement;\n readonly onNotify: Callbacks;\n readonly onDisconnect: Callbacks;\n readonly refDocument: ReadonlyRef<Document>;\n readonly refParent: ReadonlyRef<ParentNode | null>;\n readonly formAssociated:\n | {\n readonly refForm: ReadonlyRef<HTMLFormElement | null>;\n readonly refDisabled: ReadonlyRef<boolean>;\n readonly onReset: Callbacks;\n readonly onRestore: Callbacks<[state: string | File | FormData, reason: 'restore' | 'autocomplete']>;\n }\n | undefined;\n readonly createRef: <T>(value: T, onChange?: (value: T) => void) => Ref<T>;\n readonly attachInternals: () => ElementInternals;\n readonly connectedCallback: () => void;\n readonly connectedMoveCallback: () => void;\n readonly disconnectedCallback: () => void;\n readonly adoptedCallback: () => void;\n readonly formDisabledCallback: (disabled: boolean) => void;\n readonly formResetCallback: () => void;\n readonly formStateRestoreCallback: (state: string | File | FormData, reason: 'restore' | 'autocomplete') => void;\n}\n\nexport interface ControllerConfig {\n readonly host: HTMLElement;\n readonly formAssociated: boolean;\n readonly render: (controller: Controller) => void;\n readonly attachInternals: () => ElementInternals;\n}\n\nexport const $$ref = Symbol();\n\nconst controllers: Controller[] = [];\n\nexport function getController(): Controller | undefined {\n return controllers.at(-1);\n}\n\nexport function createController({ host, formAssociated, render, attachInternals }: ControllerConfig): Controller {\n let notifying = false;\n let connected = false;\n let internals: ElementInternals | undefined;\n\n let deferredFormUpdate:\n | { readonly type: 'reset' }\n | { readonly type: 'restore'; state: string | File | FormData; reason: 'restore' | 'autocomplete' }\n | undefined;\n\n const onNotify = createCallbacks();\n\n const createRef = <T>(initialValue: T, onChange?: (value: T) => void): Ref<T> => {\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 if (!connected || notifying) return;\n notifying = true;\n\n queueMicrotask(() => {\n notifying = false;\n onNotify.run();\n });\n },\n };\n };\n\n const controller = {\n host,\n onNotify,\n onDisconnect: createCallbacks(),\n refDocument: createRef(host.ownerDocument),\n refParent: createRef(host.parentNode),\n formAssociated: formAssociated\n ? {\n refForm: createRef<HTMLFormElement | null>(null),\n refDisabled: createRef(false),\n onReset: createCallbacks(),\n onRestore: createCallbacks(),\n }\n : undefined,\n createRef,\n attachInternals: () => {\n return (internals ??= attachInternals());\n },\n connectedCallback: () => {\n connected = true;\n controller.refParent.value = host.parentNode;\n\n try {\n controllers.push(controller);\n render(controller);\n } finally {\n controllers.pop();\n }\n\n if (deferredFormUpdate?.type === 'reset') {\n controller.formAssociated?.onReset.run();\n } else if (deferredFormUpdate?.type === 'restore') {\n controller.formAssociated?.onRestore.run(deferredFormUpdate.state, deferredFormUpdate.reason);\n }\n\n controller.onNotify.run();\n },\n connectedMoveCallback: () => {\n controller.refParent.value = host.parentNode;\n controller.onNotify.run();\n },\n disconnectedCallback: () => {\n controller.onNotify.clear();\n controller.formAssociated?.onReset.clear();\n controller.formAssociated?.onRestore.clear();\n controller.onDisconnect.runAndClear();\n },\n adoptedCallback: () => {\n controller.refDocument.value = host.ownerDocument;\n controller.onNotify.run();\n },\n formDisabledCallback: (disabled: boolean) => {\n if (!controller.formAssociated) return;\n controller.formAssociated.refDisabled.value = disabled;\n controller.onNotify.run();\n },\n formResetCallback: () => {\n if (!controller.formAssociated) return;\n if (connected) controller.formAssociated.onReset.run();\n else deferredFormUpdate = { type: 'reset' };\n },\n formStateRestoreCallback: (state, reason) => {\n if (!controller.formAssociated) return;\n if (connected) controller.formAssociated.onRestore.run(state, reason);\n else deferredFormUpdate = { type: 'restore', state, reason };\n },\n } satisfies Controller;\n\n return controller;\n}\n"],"mappings":";;AAmCA,IAAa,IAAQ,QAAQ,EAEvB,IAA4B,EAAE;AAEpC,SAAgB,IAAwC;AACtD,QAAO,EAAY,GAAG,GAAG;;AAG3B,SAAgB,EAAiB,EAAE,SAAM,mBAAgB,WAAQ,sBAAiD;CAChH,IAAI,IAAY,IACZ,IAAY,IACZ,GAEA,GAKE,IAAW,GAAiB,EAE5B,KAAgB,GAAiB,MAA0C;EAC/E,IAAI,IAAQ;AAEZ,SAAO;IACJ,IAAQ;GACT,IAAI,QAAQ;AACV,WAAO;;GAET,IAAI,MAAM,GAAU;AACd,UAAa,MACjB,IAAQ,GACR,IAAW,EAAM,EACb,GAAC,KAAa,OAClB,IAAY,IAEZ,qBAAqB;AAEnB,KADA,IAAY,IACZ,EAAS,KAAK;MACd;;GAEL;IAGG,IAAa;EACjB;EACA;EACA,cAAc,GAAiB;EAC/B,aAAa,EAAU,EAAK,cAAc;EAC1C,WAAW,EAAU,EAAK,WAAW;EACrC,gBAAgB,IACZ;GACE,SAAS,EAAkC,KAAK;GAChD,aAAa,EAAU,GAAM;GAC7B,SAAS,GAAiB;GAC1B,WAAW,GAAiB;GAC7B,GACD,KAAA;EACJ;EACA,uBACU,MAAc,GAAiB;EAEzC,yBAAyB;AAEvB,GADA,IAAY,IACZ,EAAW,UAAU,QAAQ,EAAK;AAElC,OAAI;AAEF,IADA,EAAY,KAAK,EAAW,EAC5B,EAAO,EAAW;aACV;AACR,MAAY,KAAK;;AASnB,GANI,GAAoB,SAAS,UAC/B,EAAW,gBAAgB,QAAQ,KAAK,GAC/B,GAAoB,SAAS,aACtC,EAAW,gBAAgB,UAAU,IAAI,EAAmB,OAAO,EAAmB,OAAO,EAG/F,EAAW,SAAS,KAAK;;EAE3B,6BAA6B;AAE3B,GADA,EAAW,UAAU,QAAQ,EAAK,YAClC,EAAW,SAAS,KAAK;;EAE3B,4BAA4B;AAI1B,GAHA,EAAW,SAAS,OAAO,EAC3B,EAAW,gBAAgB,QAAQ,OAAO,EAC1C,EAAW,gBAAgB,UAAU,OAAO,EAC5C,EAAW,aAAa,aAAa;;EAEvC,uBAAuB;AAErB,GADA,EAAW,YAAY,QAAQ,EAAK,eACpC,EAAW,SAAS,KAAK;;EAE3B,uBAAuB,MAAsB;AACtC,KAAW,mBAChB,EAAW,eAAe,YAAY,QAAQ,GAC9C,EAAW,SAAS,KAAK;;EAE3B,yBAAyB;AAClB,KAAW,mBACZ,IAAW,EAAW,eAAe,QAAQ,KAAK,GACjD,IAAqB,EAAE,MAAM,SAAS;;EAE7C,2BAA2B,GAAO,MAAW;AACtC,KAAW,mBACZ,IAAW,EAAW,eAAe,UAAU,IAAI,GAAO,EAAO,GAChE,IAAqB;IAAE,MAAM;IAAW;IAAO;IAAQ;;EAE/D;AAED,QAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seahax/elemental",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Functional, reactive, web component base library.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1 +0,0 @@
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"}
@@ -1,3 +0,0 @@
1
- import { type Context } from '../internal/context.ts';
2
- /** @internal Get the context of the currently rendering component. */
3
- export declare function useContext(): Context;
@@ -1,11 +0,0 @@
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
@@ -1 +0,0 @@
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"}
@@ -1,11 +0,0 @@
1
- import { type ReadonlyRef, type Ref, type RefValues } from './ref.ts';
2
- export interface LoadingValue<TValue> {
3
- readonly value: TValue | undefined;
4
- readonly error: unknown;
5
- readonly isLoading: boolean;
6
- }
7
- export interface LoadingOptions {
8
- readonly debounceMs?: number;
9
- }
10
- /** Use a reference (reactive state) bound to an async loader function. */
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 +0,0 @@
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"}
@@ -1,13 +0,0 @@
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;
@@ -1,32 +0,0 @@
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
@@ -1 +0,0 @@
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"}
File without changes