@seahax/elemental 0.5.19 → 0.5.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -131,6 +131,9 @@ export const MyComponent = defineComponent((shadow) => {
131
131
 
132
132
  // Use the component host element.
133
133
  const host = useHost();
134
+
135
+ // Use the component shadow root.
136
+ const shadow = useShadow();
134
137
  });
135
138
  ```
136
139
 
@@ -14,6 +14,7 @@ function n(n, { tagName: r, formAssociated: i = !1, props: a, shadow: o, styles:
14
14
  mode: o?.mode ?? "open"
15
15
  }), this.#t = e({
16
16
  host: this,
17
+ shadow: this.#e,
17
18
  formAssociated: i,
18
19
  render: () => n(this.#e, this.#n),
19
20
  attachInternals: () => super.attachInternals()
@@ -1 +1 @@
1
- {"version":3,"file":"defineComponent.js","names":["#shadow","#controller","#props"],"sources":["../src/defineComponent.ts"],"sourcesContent":["import { type Ref } from './hooks/useRef.ts';\nimport { type Controller, createController } from './internal/createController.ts';\nimport { createStyleSheet } from './internal/createStyleSheet.ts';\n\ntype UnsafeProps =\n | 'connectedCallback'\n | 'connectedMoveCallback'\n | 'disconnectedCallback'\n | 'adoptedCallback'\n | 'formResetCallback'\n | 'formStateRestoreCallback'\n | 'formDisabledCallback'\n | 'setAdoptedStyleSheets';\n\ntype SafePropKeys<TProps> = Exclude<keyof TProps, keyof HTMLElement | UnsafeProps>;\n\nexport interface ComponentConstructor<TProps extends object> {\n /**\n * True to mark the component as form-associated.\n */\n readonly formAssociated: boolean;\n\n new (): ComponentWithProps<TProps>;\n}\n\nexport interface ComponentOptions<TProps extends object> {\n /** Register the component as a custom element with this tag name. */\n readonly tagName?: `${string}-${string}`;\n /** True to mark the component as form-associated. */\n readonly formAssociated?: boolean;\n /** Component custom property descriptors. */\n readonly props?: ComponentPropDescriptors<TProps>;\n /** Shadow root attachment options. */\n readonly shadow?: Partial<ShadowRootInit>;\n /** Styles to be adopted by the shadow root after creation. */\n readonly styles?: readonly (string | CSSStyleSheet)[];\n /** Called after the component instance is created, before rendering. */\n readonly init?: (shadow: ComponentShadowRoot<TProps>) => void;\n}\n\nexport type ComponentPropDescriptors<TProps extends object> = {\n readonly [P in SafePropKeys<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 ComponentRender<TProps extends object> = (\n shadowRoot: ComponentShadowRoot<TProps>,\n props: ComponentPropRefs<TProps>,\n) => void;\n\nexport type ComponentShadowRoot<TProps extends object> = Omit<ShadowRoot, 'host'> & {\n readonly host: ComponentWithProps<TProps>;\n};\n\nexport type ComponentWithProps<TProps extends object> = HTMLElement & {\n -readonly [P in SafePropKeys<TProps>]: TProps[P];\n};\n\nexport type ComponentPropRefs<TProps extends object> = {\n readonly [P in SafePropKeys<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: ComponentRender<TProps>,\n { tagName, formAssociated = false, props, shadow, styles = [], init }: ComponentOptions<TProps> = {},\n): ComponentConstructor<TProps> {\n const styleSheets = styles.map((style) => (typeof style === 'string' ? createStyleSheet(style) : style));\n\n class ComponentElement extends HTMLElement {\n static readonly formAssociated = formAssociated;\n\n readonly #shadow: ComponentShadowRoot<TProps>;\n readonly #controller: Controller;\n readonly #props = {} as ComponentPropRefs<TProps>;\n\n constructor() {\n super();\n\n this.#shadow = this.attachShadow({\n ...shadow,\n mode: shadow?.mode ?? 'open',\n }) as ComponentShadowRoot<TProps>;\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 (formAssociated) {\n this.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 as ComponentPropDescriptors<Record<string, unknown>>)) {\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 this.#shadow.adoptedStyleSheets = styleSheets;\n\n init?.(this.#shadow);\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 if (tagName) {\n customElements.define(tagName, ComponentElement);\n }\n\n return ComponentElement as unknown as ComponentConstructor<TProps>;\n}\n"],"mappings":";;;AAwEA,SAAgB,EACd,GACA,EAAE,YAAS,oBAAiB,IAAO,UAAO,WAAQ,YAAS,EAAE,EAAE,YAAmC,EAAE,EACtE;CAC9B,IAAM,IAAc,EAAO,KAAK,MAAW,OAAO,KAAU,WAAW,EAAiB,EAAM,GAAG,EAAO;CAExG,MAAM,UAAyB,YAAY;EACzC,OAAgB,iBAAiB;EAEjC;EACA;EACA,KAAkB,EAAE;EAEpB,cAAc;AAmBZ,OAlBA,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,KACF,KAAK,iBAAiB,EAGpB,GAAO;IACT,IAAM,IAAyC,MAAA;AAE/C,SAAK,IAAM,CAAC,GAAK,MAAkB,OAAO,QAAQ,EAA2D,EAAE;AAC7G,SAAI,KAAO,KAAM;KAEjB,IAAM,IAAa,EAAc,EADX,KAAO,MAAA,EAAiB,UAAe,KAAA,EAAU,EACjC,KAAK;AAC3C,YAAO,eAAe,MAAM,GAAK,EAAW;;;AAMhD,GAFA,MAAA,EAAa,qBAAqB,GAElC,IAAO,MAAA,EAAa;;EAGtB,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;;;AAQ5D,QAJI,KACF,eAAe,OAAO,GAAS,EAAiB,EAG3C"}
1
+ {"version":3,"file":"defineComponent.js","names":["#shadow","#controller","#props"],"sources":["../src/defineComponent.ts"],"sourcesContent":["import { type Ref } from './hooks/useRef.ts';\nimport { type Controller, createController } from './internal/createController.ts';\nimport { createStyleSheet } from './internal/createStyleSheet.ts';\n\ntype UnsafeProps =\n | 'connectedCallback'\n | 'connectedMoveCallback'\n | 'disconnectedCallback'\n | 'adoptedCallback'\n | 'formResetCallback'\n | 'formStateRestoreCallback'\n | 'formDisabledCallback'\n | 'setAdoptedStyleSheets';\n\ntype SafePropKeys<TProps> = Exclude<keyof TProps, keyof HTMLElement | UnsafeProps>;\n\nexport interface ComponentConstructor<TProps extends object> {\n /**\n * True to mark the component as form-associated.\n */\n readonly formAssociated: boolean;\n\n new (): ComponentWithProps<TProps>;\n}\n\nexport interface ComponentOptions<TProps extends object> {\n /** Register the component as a custom element with this tag name. */\n readonly tagName?: `${string}-${string}`;\n /** True to mark the component as form-associated. */\n readonly formAssociated?: boolean;\n /** Component custom property descriptors. */\n readonly props?: ComponentPropDescriptors<TProps>;\n /** Shadow root attachment options. */\n readonly shadow?: Partial<ShadowRootInit>;\n /** Styles to be adopted by the shadow root after creation. */\n readonly styles?: readonly (string | CSSStyleSheet)[];\n /** Called after the component instance is created, before rendering. */\n readonly init?: (shadow: ComponentShadowRoot<TProps>) => void;\n}\n\nexport type ComponentPropDescriptors<TProps extends object> = {\n readonly [P in SafePropKeys<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 ComponentRender<TProps extends object> = (\n shadowRoot: ComponentShadowRoot<TProps>,\n props: ComponentPropRefs<TProps>,\n) => void;\n\nexport type ComponentShadowRoot<TProps extends object> = Omit<ShadowRoot, 'host'> & {\n readonly host: ComponentWithProps<TProps>;\n};\n\nexport type ComponentWithProps<TProps extends object> = HTMLElement & {\n -readonly [P in SafePropKeys<TProps>]: TProps[P];\n};\n\nexport type ComponentPropRefs<TProps extends object> = {\n readonly [P in SafePropKeys<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: ComponentRender<TProps>,\n { tagName, formAssociated = false, props, shadow, styles = [], init }: ComponentOptions<TProps> = {},\n): ComponentConstructor<TProps> {\n const styleSheets = styles.map((style) => (typeof style === 'string' ? createStyleSheet(style) : style));\n\n class ComponentElement extends HTMLElement {\n static readonly formAssociated = formAssociated;\n\n readonly #shadow: ComponentShadowRoot<TProps>;\n readonly #controller: Controller;\n readonly #props = {} as ComponentPropRefs<TProps>;\n\n constructor() {\n super();\n\n this.#shadow = this.attachShadow({\n ...shadow,\n mode: shadow?.mode ?? 'open',\n }) as ComponentShadowRoot<TProps>;\n\n this.#controller = createController({\n host: this,\n shadow: this.#shadow,\n formAssociated,\n render: () => render(this.#shadow, this.#props),\n attachInternals: () => super.attachInternals(),\n });\n\n if (formAssociated) {\n this.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 as ComponentPropDescriptors<Record<string, unknown>>)) {\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 this.#shadow.adoptedStyleSheets = styleSheets;\n\n init?.(this.#shadow);\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 if (tagName) {\n customElements.define(tagName, ComponentElement);\n }\n\n return ComponentElement as unknown as ComponentConstructor<TProps>;\n}\n"],"mappings":";;;AAwEA,SAAgB,EACd,GACA,EAAE,YAAS,oBAAiB,IAAO,UAAO,WAAQ,YAAS,EAAE,EAAE,YAAmC,EAAE,EACtE;CAC9B,IAAM,IAAc,EAAO,KAAK,MAAW,OAAO,KAAU,WAAW,EAAiB,EAAM,GAAG,EAAO;CAExG,MAAM,UAAyB,YAAY;EACzC,OAAgB,iBAAiB;EAEjC;EACA;EACA,KAAkB,EAAE;EAEpB,cAAc;AAoBZ,OAnBA,OAAO,EAEP,MAAA,IAAe,KAAK,aAAa;IAC/B,GAAG;IACH,MAAM,GAAQ,QAAQ;IACvB,CAAC,EAEF,MAAA,IAAmB,EAAiB;IAClC,MAAM;IACN,QAAQ,MAAA;IACR;IACA,cAAc,EAAO,MAAA,GAAc,MAAA,EAAY;IAC/C,uBAAuB,MAAM,iBAAiB;IAC/C,CAAC,EAEE,KACF,KAAK,iBAAiB,EAGpB,GAAO;IACT,IAAM,IAAyC,MAAA;AAE/C,SAAK,IAAM,CAAC,GAAK,MAAkB,OAAO,QAAQ,EAA2D,EAAE;AAC7G,SAAI,KAAO,KAAM;KAEjB,IAAM,IAAa,EAAc,EADX,KAAO,MAAA,EAAiB,UAAe,KAAA,EAAU,EACjC,KAAK;AAC3C,YAAO,eAAe,MAAM,GAAK,EAAW;;;AAMhD,GAFA,MAAA,EAAa,qBAAqB,GAElC,IAAO,MAAA,EAAa;;EAGtB,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;;;AAQ5D,QAJI,KACF,eAAe,OAAO,GAAS,EAAiB,EAG3C"}
@@ -0,0 +1 @@
1
+ export declare function useShadow(): ShadowRoot;
@@ -2,6 +2,7 @@ import { type ReadonlyRef, type Ref } from '../hooks/useRef.ts';
2
2
  import { type Callbacks } from './createCallbacks.ts';
3
3
  export interface Controller {
4
4
  readonly host: HTMLElement;
5
+ readonly shadow: ShadowRoot;
5
6
  readonly onNotify: Callbacks;
6
7
  readonly onAfterRender: Callbacks;
7
8
  readonly onDisconnect: Callbacks;
@@ -25,9 +26,10 @@ export interface Controller {
25
26
  }
26
27
  interface ControllerConfig {
27
28
  readonly host: HTMLElement;
29
+ readonly shadow: ShadowRoot;
28
30
  readonly formAssociated: boolean;
29
31
  readonly render: (controller: Controller) => void;
30
32
  readonly attachInternals: () => ElementInternals;
31
33
  }
32
- export declare function createController({ host, formAssociated, render, attachInternals }: ControllerConfig): Controller;
34
+ export declare function createController({ host, shadow, formAssociated, render, attachInternals, }: ControllerConfig): Controller;
33
35
  export {};
@@ -2,8 +2,8 @@ import { createCallbacks as e } from "./createCallbacks.js";
2
2
  import { controllers as t } from "./controllers.js";
3
3
  import { $$ref as n } from "./constants.js";
4
4
  //#region src/internal/createController.ts
5
- function r({ host: r, formAssociated: i, render: a, attachInternals: o }) {
6
- let s = !1, c = !1, l, u, d = e(), f = (e, t) => {
5
+ function r({ host: r, shadow: i, formAssociated: a, render: o, attachInternals: s }) {
6
+ let c = !1, l = !1, u, d, f = e(), p = (e, t) => {
7
7
  let r = e;
8
8
  return {
9
9
  [n]: !0,
@@ -11,61 +11,62 @@ function r({ host: r, formAssociated: i, render: a, attachInternals: o }) {
11
11
  return r;
12
12
  },
13
13
  set value(e) {
14
- e !== r && (r = e, t?.(r), !(!c || s) && (s = !0, queueMicrotask(() => {
15
- s = !1, d.run();
14
+ e !== r && (r = e, t?.(r), !(!l || c) && (c = !0, queueMicrotask(() => {
15
+ c = !1, f.run();
16
16
  })));
17
17
  }
18
18
  };
19
- }, p = {
19
+ }, m = {
20
20
  host: r,
21
- onNotify: d,
21
+ shadow: i,
22
+ onNotify: f,
22
23
  onAfterRender: e(),
23
24
  onDisconnect: e(),
24
- refDocument: f(r.ownerDocument),
25
- refParent: f(r.parentNode),
26
- formAssociated: i ? {
27
- refForm: f(null),
28
- refDisabled: f(!1),
25
+ refDocument: p(r.ownerDocument),
26
+ refParent: p(r.parentNode),
27
+ formAssociated: a ? {
28
+ refForm: p(null),
29
+ refDisabled: p(!1),
29
30
  onReset: e(),
30
31
  onRestore: e()
31
32
  } : void 0,
32
- createRef: f,
33
- attachInternals: () => l ??= o(),
33
+ createRef: p,
34
+ attachInternals: () => u ??= s(),
34
35
  connectedCallback: () => {
35
- if (!c) {
36
- c = !0, p.refParent.value = r.parentNode;
36
+ if (!l) {
37
+ l = !0, m.refParent.value = r.parentNode;
37
38
  try {
38
- t.push(p), a(p);
39
+ t.push(m), o(m);
39
40
  } finally {
40
41
  t.pop();
41
42
  }
42
- p.onAfterRender.runAndClear(), u?.type === "reset" ? p.formAssociated?.onReset.run() : u?.type === "restore" && p.formAssociated?.onRestore.run(u.state, u.reason), p.onNotify.run();
43
+ m.onAfterRender.runAndClear(), d?.type === "reset" ? m.formAssociated?.onReset.run() : d?.type === "restore" && m.formAssociated?.onRestore.run(d.state, d.reason), m.onNotify.run();
43
44
  }
44
45
  },
45
46
  connectedMoveCallback: () => {
46
- c && (p.refParent.value = r.parentNode, p.onNotify.run());
47
+ l && (m.refParent.value = r.parentNode, m.onNotify.run());
47
48
  },
48
49
  disconnectedCallback: () => {
49
- c && (p.onNotify.clear(), p.formAssociated?.onReset.clear(), p.formAssociated?.onRestore.clear(), p.onDisconnect.runAndClear());
50
+ l && (m.onNotify.clear(), m.formAssociated?.onReset.clear(), m.formAssociated?.onRestore.clear(), m.onDisconnect.runAndClear());
50
51
  },
51
52
  adoptedCallback: () => {
52
- p.refDocument.value = r.ownerDocument, p.onNotify.run();
53
+ m.refDocument.value = r.ownerDocument, m.onNotify.run();
53
54
  },
54
55
  formDisabledCallback: (e) => {
55
- p.formAssociated && (p.formAssociated.refDisabled.value = e, p.onNotify.run());
56
+ m.formAssociated && (m.formAssociated.refDisabled.value = e, m.onNotify.run());
56
57
  },
57
58
  formResetCallback: () => {
58
- p.formAssociated && (c ? p.formAssociated.onReset.run() : u = { type: "reset" });
59
+ m.formAssociated && (l ? m.formAssociated.onReset.run() : d = { type: "reset" });
59
60
  },
60
61
  formStateRestoreCallback: (e, t) => {
61
- p.formAssociated && (c ? p.formAssociated.onRestore.run(e, t) : u = {
62
+ m.formAssociated && (l ? m.formAssociated.onRestore.run(e, t) : d = {
62
63
  type: "restore",
63
64
  state: e,
64
65
  reason: t
65
66
  });
66
67
  }
67
68
  };
68
- return p;
69
+ return m;
69
70
  }
70
71
  //#endregion
71
72
  export { r as createController };
@@ -1 +1 @@
1
- {"version":3,"file":"createController.js","names":[],"sources":["../../src/internal/createController.ts"],"sourcesContent":["import { type ReadonlyRef, type Ref } from '../hooks/useRef.ts';\nimport { $$ref } from './constants.ts';\nimport { controllers } from './controllers.ts';\nimport { type Callbacks, createCallbacks } from './createCallbacks.ts';\n\nexport interface Controller {\n readonly host: HTMLElement;\n readonly onNotify: Callbacks;\n readonly onAfterRender: 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\ninterface ControllerConfig {\n readonly host: HTMLElement;\n readonly formAssociated: boolean;\n readonly render: (controller: Controller) => void;\n readonly attachInternals: () => ElementInternals;\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 onAfterRender: createCallbacks(),\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 if (connected) return;\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 controller.onAfterRender.runAndClear();\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 if (!connected) return;\n controller.refParent.value = host.parentNode;\n controller.onNotify.run();\n },\n disconnectedCallback: () => {\n if (!connected) return;\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":";;;;AAsCA,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,eAAe,GAAiB;EAChC,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;AACnB,WAEJ;IADA,IAAY,IACZ,EAAW,UAAU,QAAQ,EAAK;AAElC,QAAI;AAEF,KADA,EAAY,KAAK,EAAW,EAC5B,EAAO,EAAW;cACV;AACR,OAAY,KAAK;;AAWnB,IARA,EAAW,cAAc,aAAa,EAElC,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;AACtB,SACL,EAAW,UAAU,QAAQ,EAAK,YAClC,EAAW,SAAS,KAAK;;EAE3B,4BAA4B;AACrB,SACL,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"}
1
+ {"version":3,"file":"createController.js","names":[],"sources":["../../src/internal/createController.ts"],"sourcesContent":["import { type ReadonlyRef, type Ref } from '../hooks/useRef.ts';\nimport { $$ref } from './constants.ts';\nimport { controllers } from './controllers.ts';\nimport { type Callbacks, createCallbacks } from './createCallbacks.ts';\n\nexport interface Controller {\n readonly host: HTMLElement;\n readonly shadow: ShadowRoot;\n readonly onNotify: Callbacks;\n readonly onAfterRender: 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\ninterface ControllerConfig {\n readonly host: HTMLElement;\n readonly shadow: ShadowRoot;\n readonly formAssociated: boolean;\n readonly render: (controller: Controller) => void;\n readonly attachInternals: () => ElementInternals;\n}\n\nexport function createController({\n host,\n shadow,\n formAssociated,\n render,\n attachInternals,\n}: 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 shadow,\n onNotify,\n onAfterRender: createCallbacks(),\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 if (connected) return;\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 controller.onAfterRender.runAndClear();\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 if (!connected) return;\n controller.refParent.value = host.parentNode;\n controller.onNotify.run();\n },\n disconnectedCallback: () => {\n if (!connected) return;\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":";;;;AAwCA,SAAgB,EAAiB,EAC/B,SACA,WACA,mBACA,WACA,sBAC+B;CAC/B,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;EACA,eAAe,GAAiB;EAChC,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;AACnB,WAEJ;IADA,IAAY,IACZ,EAAW,UAAU,QAAQ,EAAK;AAElC,QAAI;AAEF,KADA,EAAY,KAAK,EAAW,EAC5B,EAAO,EAAW;cACV;AACR,OAAY,KAAK;;AAWnB,IARA,EAAW,cAAc,aAAa,EAElC,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;AACtB,SACL,EAAW,UAAU,QAAQ,EAAK,YAClC,EAAW,SAAS,KAAK;;EAE3B,4BAA4B;AACrB,SACL,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
@@ -16,10 +16,10 @@
16
16
  ],
17
17
  "scripts": {
18
18
  "build": "vite build && tsc -b -f tsconfig.build.json",
19
- "test": "eslint src && vitest run"
19
+ "test": "prettier -c src && eslint src && vitest run"
20
20
  },
21
21
  "publishConfig": {
22
22
  "access": "public"
23
23
  },
24
- "version": "0.5.19"
24
+ "version": "0.5.20"
25
25
  }