@adobe/data-lit 0.10.30 → 0.10.32

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 (90) hide show
  1. package/README.md +22 -12
  2. package/dist/elements/application-host.d.ts +6 -2
  3. package/dist/elements/application-host.js +17 -1
  4. package/dist/elements/application-host.js.map +1 -1
  5. package/dist/functions/iterate-self-and-ancestors.d.ts +1 -0
  6. package/dist/functions/iterate-self-and-ancestors.js +2 -2
  7. package/dist/functions/iterate-self-and-ancestors.js.map +1 -1
  8. package/dist/hooks/component/hooks-controller.js +16 -5
  9. package/dist/hooks/component/hooks-controller.js.map +1 -1
  10. package/dist/hooks/use-drag-observe.js +2 -1
  11. package/dist/hooks/use-drag-observe.js.map +1 -1
  12. package/dist/hooks/use-drag-transaction.d.ts +5 -0
  13. package/dist/hooks/use-drag-transaction.js +42 -22
  14. package/dist/hooks/use-drag-transaction.js.map +1 -1
  15. package/dist/hooks/use-draggable.js +70 -48
  16. package/dist/hooks/use-draggable.js.map +1 -1
  17. package/dist/hooks/use-effect.js +6 -2
  18. package/dist/hooks/use-effect.js.map +1 -1
  19. package/dist/hooks/use-element.d.ts +4 -2
  20. package/dist/hooks/use-element.js +10 -51
  21. package/dist/hooks/use-element.js.map +1 -1
  22. package/dist/hooks/use-memo.js +11 -9
  23. package/dist/hooks/use-memo.js.map +1 -1
  24. package/dist/hooks/use-observable.d.ts +4 -0
  25. package/dist/hooks/use-observable.js +13 -8
  26. package/dist/hooks/use-observable.js.map +1 -1
  27. package/dist/hooks/use-ref.js +4 -3
  28. package/dist/hooks/use-ref.js.map +1 -1
  29. package/dist/hooks/use-resize-observer.js +5 -2
  30. package/dist/hooks/use-resize-observer.js.map +1 -1
  31. package/dist/hooks/use-resize-observer.test.d.ts +1 -0
  32. package/dist/hooks/use-resize-observer.test.js +102 -0
  33. package/dist/hooks/use-resize-observer.test.js.map +1 -0
  34. package/dist/hooks/use-state.js +9 -7
  35. package/dist/hooks/use-state.js.map +1 -1
  36. package/dist/hooks/use-state.test.d.ts +1 -0
  37. package/dist/hooks/use-state.test.js +83 -0
  38. package/dist/hooks/use-state.test.js.map +1 -0
  39. package/dist/hooks/use-window-event.js +1 -1
  40. package/dist/hooks/use-window-event.js.map +1 -1
  41. package/package.json +3 -3
  42. package/src/decorators/apply-decorator.test.ts +37 -0
  43. package/src/decorators/apply-service-decorators.test.ts +53 -0
  44. package/src/decorators/require-service.test.ts +31 -0
  45. package/src/elements/application-element.test.ts +80 -0
  46. package/src/elements/application-host.test.ts +57 -0
  47. package/src/elements/application-host.ts +16 -3
  48. package/src/elements/database-element.test.ts +111 -0
  49. package/src/functions/iterate-self-and-ancestors.test.ts +29 -0
  50. package/src/functions/iterate-self-and-ancestors.ts +2 -2
  51. package/src/hooks/attach-decorator.test.ts +45 -0
  52. package/src/hooks/component/hooks-controller.cleanup.test.ts +65 -0
  53. package/src/hooks/component/hooks-controller.ts +15 -5
  54. package/src/hooks/component/stack.test.ts +37 -0
  55. package/src/hooks/test-host.test-utils.ts +43 -0
  56. package/src/hooks/use-connected.test.ts +88 -0
  57. package/src/hooks/use-debounce.test.ts +69 -0
  58. package/src/hooks/use-drag-generator.test.ts +67 -0
  59. package/src/hooks/use-drag-observe.test.ts +74 -0
  60. package/src/hooks/use-drag-observe.ts +2 -1
  61. package/src/hooks/use-drag-transaction.test.ts +109 -0
  62. package/src/hooks/use-drag-transaction.ts +48 -27
  63. package/src/hooks/use-draggable.test.ts +148 -0
  64. package/src/hooks/use-draggable.ts +73 -48
  65. package/src/hooks/use-effect.test.ts +51 -0
  66. package/src/hooks/use-effect.ts +5 -2
  67. package/src/hooks/use-element.test.ts +82 -0
  68. package/src/hooks/use-element.ts +17 -60
  69. package/src/hooks/use-memo.test.ts +66 -0
  70. package/src/hooks/use-memo.ts +16 -11
  71. package/src/hooks/use-observable-values.test.ts +53 -0
  72. package/src/hooks/use-observable.test.ts +63 -0
  73. package/src/hooks/use-observable.ts +13 -8
  74. package/src/hooks/use-pointer-observe.test.ts +59 -0
  75. package/src/hooks/use-ref.test.ts +50 -0
  76. package/src/hooks/use-ref.ts +4 -3
  77. package/src/hooks/use-resize-observer.test.ts +114 -0
  78. package/src/hooks/use-resize-observer.ts +5 -2
  79. package/src/hooks/use-state.test.ts +108 -0
  80. package/src/hooks/use-state.ts +14 -9
  81. package/src/hooks/use-updated.test.ts +98 -0
  82. package/src/hooks/use-window-event.test.ts +61 -0
  83. package/src/hooks/use-window-event.ts +1 -1
  84. package/src/hooks/with-hooks.test.ts +45 -0
  85. package/src/index.test.ts +26 -0
  86. package/src/public-types.type-test.ts +17 -0
  87. package/tsconfig.json +1 -0
  88. package/tsconfig.test.json +9 -0
  89. package/tsconfig.tsbuildinfo +1 -1
  90. package/vitest.config.ts +7 -0
@@ -1,13 +1,15 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
- import { useEffect } from "./use-effect.js";
3
- import { useState } from "./use-state.js";
2
+ import { equalsShallow } from "@adobe/data/equals-shallow";
3
+ import { Component_stack } from "./component/stack.js";
4
4
  export function useMemo(calculateValue, dependencies = []) {
5
- const [state, setState] = useState(undefined);
6
- let currentValue = state;
7
- useEffect(() => {
8
- currentValue = calculateValue();
9
- setState(currentValue);
10
- }, dependencies);
11
- return currentValue;
5
+ const component = Component_stack.active();
6
+ const hookIndex = component.hookIndex++;
7
+ const memo = component.hooks[hookIndex];
8
+ if (memo && equalsShallow(dependencies, memo.dependencies)) {
9
+ return memo.value;
10
+ }
11
+ const value = calculateValue();
12
+ component.hooks[hookIndex] = { value, dependencies };
13
+ return value;
12
14
  }
13
15
  //# sourceMappingURL=use-memo.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-memo.js","sourceRoot":"","sources":["../../src/hooks/use-memo.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,UAAU,OAAO,CAAI,cAAuB,EAAE,eAA0B,EAAE;IAC5E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,SAAS,CAAC,CAAC;IAC7D,IAAI,YAAY,GAAG,KAAK,CAAC;IAEzB,SAAS,CAAC,GAAG,EAAE;QACX,YAAY,GAAG,cAAc,EAAE,CAAC;QAChC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC3B,CAAC,EAAE,YAAY,CAAC,CAAC;IAEjB,OAAO,YAAa,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"use-memo.js","sourceRoot":"","sources":["../../src/hooks/use-memo.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAOvD,MAAM,UAAU,OAAO,CAAI,cAAuB,EAAE,eAA0B,EAAE;IAC5E,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC;IAC3C,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,SAAS,CAAwB,CAAC;IAC/D,IAAI,IAAI,IAAI,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QACzD,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IACD,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,EAAE,YAAY,EAAoB,CAAC;IACvE,OAAO,KAAK,CAAC;AACjB,CAAC"}
@@ -1,2 +1,6 @@
1
1
  import { Observe } from "@adobe/data/observe";
2
+ /**
3
+ * Subscribes to `observable` and returns its latest value. Every notification
4
+ * requests a render, including one that repeats the previous value or reference.
5
+ */
2
6
  export declare function useObservable<T>(observable: Observe<T>): T | undefined;
@@ -1,13 +1,18 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
- import { useState } from "./use-state.js";
2
+ import { Component_stack } from "./component/stack.js";
3
3
  import { useEffect } from "./use-effect.js";
4
+ import { useRef } from "./use-ref.js";
5
+ /**
6
+ * Subscribes to `observable` and returns its latest value. Every notification
7
+ * requests a render, including one that repeats the previous value or reference.
8
+ */
4
9
  export function useObservable(observable) {
5
- let [value, setValue] = useState(undefined);
6
- useEffect(() => {
7
- return observable(newValue => {
8
- setValue(value = newValue);
9
- });
10
- }, [observable]);
11
- return value;
10
+ const component = Component_stack.active();
11
+ const latest = useRef(undefined);
12
+ useEffect(() => observable(value => {
13
+ latest.current = value;
14
+ component.requestUpdate();
15
+ }), [observable]);
16
+ return latest.current;
12
17
  }
13
18
  //# sourceMappingURL=use-observable.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-observable.js","sourceRoot":"","sources":["../../src/hooks/use-observable.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,UAAU,aAAa,CAAI,UAAsB;IACnD,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,SAAS,CAAC,CAAC;IAC3D,SAAS,CAAC,GAAG,EAAE;QACX,OAAO,UAAU,CAAC,QAAQ,CAAC,EAAE;YACzB,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACP,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IACjB,OAAO,KAAK,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"use-observable.js","sourceRoot":"","sources":["../../src/hooks/use-observable.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAGtC;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAI,UAAsB;IACnD,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,MAAM,CAAgB,SAAS,CAAC,CAAC;IAChD,SAAS,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;QAC/B,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,SAAS,CAAC,aAAa,EAAE,CAAC;IAC9B,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAClB,OAAO,MAAM,CAAC,OAAO,CAAC;AAC1B,CAAC"}
@@ -1,7 +1,8 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
- import { useState } from "./use-state.js";
2
+ import { Component_stack } from "./component/stack.js";
3
3
  export function useRef(initialValue) {
4
- const [state] = useState({ current: initialValue });
5
- return state;
4
+ const component = Component_stack.active();
5
+ const hookIndex = component.hookIndex++;
6
+ return component.hooks[hookIndex] ??= { current: initialValue };
6
7
  }
7
8
  //# sourceMappingURL=use-ref.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-ref.js","sourceRoot":"","sources":["../../src/hooks/use-ref.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,UAAU,MAAM,CAAI,YAAe;IACrC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"use-ref.js","sourceRoot":"","sources":["../../src/hooks/use-ref.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,UAAU,MAAM,CAAI,YAAe;IACrC,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC;IAC3C,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC;IACxC,OAAO,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;AACpE,CAAC"}
@@ -1,6 +1,7 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
  import { useEffect } from "./use-effect.js";
3
3
  import { useElement } from "./use-element.js";
4
+ import { useRef } from "./use-ref.js";
4
5
  /**
5
6
  * Hook to observe element resizes. Calls callback with width, height, and the original entry
6
7
  * when the element's size changes.
@@ -9,15 +10,17 @@ import { useElement } from "./use-element.js";
9
10
  */
10
11
  export function useResizeObserver(onResize) {
11
12
  const element = useElement();
13
+ const latestCallback = useRef(onResize);
14
+ latestCallback.current = onResize;
12
15
  useEffect(() => {
13
16
  const observer = new ResizeObserver(entries => {
14
17
  for (const entry of entries) {
15
18
  const { width, height } = entry.contentRect;
16
- onResize({ width, height, entry });
19
+ latestCallback.current({ width, height, entry });
17
20
  }
18
21
  });
19
22
  observer.observe(element);
20
23
  return () => observer.disconnect();
21
- }, [element, onResize]);
24
+ }, [element]);
22
25
  }
23
26
  //# sourceMappingURL=use-resize-observer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-resize-observer.js","sourceRoot":"","sources":["../../src/hooks/use-resize-observer.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQ9C;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAoC;IAClE,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE;YAC1C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC1B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC;gBAC5C,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;YACvC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACvC,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"use-resize-observer.js","sourceRoot":"","sources":["../../src/hooks/use-resize-observer.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAQtC;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAoC;IAClE,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,cAAc,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACxC,cAAc,CAAC,OAAO,GAAG,QAAQ,CAAC;IAElC,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE;YAC1C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC1B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC;gBAC5C,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;YACrD,CAAC;QACL,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACvC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,102 @@
1
+ // @vitest-environment happy-dom
2
+ import { afterEach, describe, it, expect, vi } from "vitest";
3
+ import { LitElement, html } from "lit";
4
+ import { attachDecorator } from "./attach-decorator.js";
5
+ import { withHooks } from "./with-hooks.js";
6
+ import { useMemo } from "./use-memo.js";
7
+ import { useResizeObserver } from "./use-resize-observer.js";
8
+ import { useState } from "./use-state.js";
9
+ class ResizeProbeElement extends LitElement {
10
+ constructor() {
11
+ super();
12
+ this.renders = 0;
13
+ this.offset = 0;
14
+ this.widths = [];
15
+ this.calculateCallback = vi.fn((setWidth) => setWidth);
16
+ attachDecorator(this, "render", withHooks);
17
+ }
18
+ render() {
19
+ this.renders++;
20
+ const [width, setWidth] = useState(0);
21
+ const memoizedSetter = useMemo(() => this.calculateCallback(setWidth), [setWidth]);
22
+ const offset = this.offset;
23
+ useResizeObserver(info => {
24
+ this.widths.push(info.width + offset);
25
+ memoizedSetter(Math.floor(info.width + offset));
26
+ });
27
+ return html `<span>${width}</span>`;
28
+ }
29
+ }
30
+ customElements.define("resize-state-probe", ResizeProbeElement);
31
+ function mockResizeObserver() {
32
+ const observers = [];
33
+ vi.stubGlobal("ResizeObserver", class {
34
+ constructor(callback) {
35
+ this.observe = vi.fn();
36
+ this.disconnect = vi.fn();
37
+ observers.push({
38
+ emit: width => callback([
39
+ { contentRect: { width, height: 20 } },
40
+ ], this),
41
+ observe: this.observe,
42
+ disconnect: this.disconnect,
43
+ });
44
+ }
45
+ });
46
+ return observers;
47
+ }
48
+ afterEach(async () => {
49
+ document.body.replaceChildren();
50
+ await new Promise(resolve => queueMicrotask(resolve));
51
+ vi.unstubAllGlobals();
52
+ });
53
+ describe("useResizeObserver with state", () => {
54
+ it("keeps one observer, uses the latest inline callback, and skips unchanged widths", async () => {
55
+ const observers = mockResizeObserver();
56
+ const element = document.createElement("resize-state-probe");
57
+ document.body.appendChild(element);
58
+ await element.updateComplete;
59
+ const observer = observers[0];
60
+ expect(observer.observe).toHaveBeenCalledExactlyOnceWith(element);
61
+ observer.emit(100.2);
62
+ await element.updateComplete;
63
+ expect(element.shadowRoot?.textContent).toBe("100");
64
+ expect(element.renders).toBe(2);
65
+ expect(observers).toHaveLength(1);
66
+ expect(observer.disconnect).not.toHaveBeenCalled();
67
+ expect(element.calculateCallback).toHaveBeenCalledTimes(1);
68
+ const requestUpdate = vi.spyOn(element, "requestUpdate");
69
+ observer.emit(100.8);
70
+ await element.updateComplete;
71
+ expect(requestUpdate).not.toHaveBeenCalled();
72
+ expect(element.renders).toBe(2);
73
+ element.offset = 10;
74
+ element.requestUpdate();
75
+ await element.updateComplete;
76
+ observer.emit(200);
77
+ await element.updateComplete;
78
+ expect(element.widths).toEqual([100.2, 100.8, 210]);
79
+ expect(element.shadowRoot?.textContent).toBe("210");
80
+ expect(observers).toHaveLength(1);
81
+ expect(observer.disconnect).not.toHaveBeenCalled();
82
+ expect(element.calculateCallback).toHaveBeenCalledTimes(1);
83
+ });
84
+ it("disconnects on unmount and creates fresh state and observer on remount", async () => {
85
+ const observers = mockResizeObserver();
86
+ const element = document.createElement("resize-state-probe");
87
+ document.body.appendChild(element);
88
+ await element.updateComplete;
89
+ observers[0].emit(100);
90
+ await element.updateComplete;
91
+ element.remove();
92
+ await new Promise(resolve => queueMicrotask(resolve));
93
+ expect(observers[0].disconnect).toHaveBeenCalledTimes(1);
94
+ document.body.appendChild(element);
95
+ await element.updateComplete;
96
+ expect(element.shadowRoot?.textContent).toBe("0");
97
+ expect(observers).toHaveLength(2);
98
+ expect(observers[1].observe).toHaveBeenCalledExactlyOnceWith(element);
99
+ expect(element.calculateCallback).toHaveBeenCalledTimes(2);
100
+ });
101
+ });
102
+ //# sourceMappingURL=use-resize-observer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-resize-observer.test.js","sourceRoot":"","sources":["../../src/hooks/use-resize-observer.test.ts"],"names":[],"mappings":"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"}
@@ -3,13 +3,15 @@ import { Component_stack } from "./component/stack.js";
3
3
  export function useState(initial) {
4
4
  const component = Component_stack.active();
5
5
  const hookIndex = component.hookIndex++;
6
- const value = component.hooks[hookIndex] ??= (typeof initial === "function" ? initial() : initial);
7
- return [
8
- value,
9
- (newValue) => {
10
- component.hooks[hookIndex] = newValue;
6
+ const state = component.hooks[hookIndex] ??= {
7
+ value: typeof initial === "function" ? initial() : initial,
8
+ setValue: (newValue) => {
9
+ if (Object.is(state.value, newValue))
10
+ return;
11
+ state.value = newValue;
11
12
  component.requestUpdate();
12
- }
13
- ];
13
+ },
14
+ };
15
+ return [state.value, state.setValue];
14
16
  }
15
17
  //# sourceMappingURL=use-state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-state.js","sourceRoot":"","sources":["../../src/hooks/use-state.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAIvD,MAAM,UAAU,QAAQ,CAAI,OAAgB;IACxC,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC;IAC3C,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACnG,OAAO;QACH,KAAK;QACL,CAAC,QAAW,EAAE,EAAE;YACZ,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAC;YACtC,SAAS,CAAC,aAAa,EAAE,CAAC;QAC9B,CAAC;KACJ,CAAC;AAEN,CAAC"}
1
+ {"version":3,"file":"use-state.js","sourceRoot":"","sources":["../../src/hooks/use-state.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AASvD,MAAM,UAAU,QAAQ,CAAI,OAAsB;IAC9C,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC;IAC3C,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC;IACxC,MAAM,KAAK,GAAa,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK;QACnD,KAAK,EAAE,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAE,OAAmB,EAAE,CAAC,CAAC,CAAC,OAAO;QACvE,QAAQ,EAAE,CAAC,QAAW,EAAE,EAAE;YACtB,IAAI,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC;gBAAE,OAAO;YAC7C,KAAK,CAAC,KAAK,GAAG,QAAQ,CAAC;YACvB,SAAS,CAAC,aAAa,EAAE,CAAC;QAC9B,CAAC;KACJ,CAAC;IACF,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,83 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { Component_stack } from "./component/stack.js";
3
+ import { useState } from "./use-state.js";
4
+ function createHost() {
5
+ return Object.assign(new EventTarget(), {
6
+ isConnected: true,
7
+ hookIndex: 0,
8
+ hooks: [],
9
+ updatedListeners: new Set(),
10
+ requestUpdate: vi.fn(),
11
+ });
12
+ }
13
+ function render(host, callback) {
14
+ Component_stack.push(host);
15
+ try {
16
+ return callback();
17
+ }
18
+ finally {
19
+ Component_stack.pop();
20
+ }
21
+ }
22
+ describe("useState", () => {
23
+ it("keeps the setter stable across renders and updates", () => {
24
+ const host = createHost();
25
+ const [, setValue] = render(host, () => useState(0));
26
+ expect(render(host, () => useState(0))[1]).toBe(setValue);
27
+ setValue(1);
28
+ const [value, nextSetter] = render(host, () => useState(0));
29
+ expect(value).toBe(1);
30
+ expect(nextSetter).toBe(setValue);
31
+ expect(host.requestUpdate).toHaveBeenCalledTimes(1);
32
+ });
33
+ it.each([0, NaN, null, undefined, "value", false, {}, []])("does not request an update for an Object.is-equal value: %s", initial => {
34
+ const host = createHost();
35
+ const [, setValue] = render(host, () => useState(initial));
36
+ setValue(initial);
37
+ setValue(initial);
38
+ expect(host.requestUpdate).not.toHaveBeenCalled();
39
+ });
40
+ it("compares against the latest value before another render", () => {
41
+ const host = createHost();
42
+ const [, setValue] = render(host, () => useState(0));
43
+ setValue(1);
44
+ setValue(1);
45
+ setValue(2);
46
+ expect(host.requestUpdate).toHaveBeenCalledTimes(2);
47
+ expect(render(host, () => useState(0))[0]).toBe(2);
48
+ });
49
+ it("distinguishes positive and negative zero", () => {
50
+ const host = createHost();
51
+ const [, setValue] = render(host, () => useState(0));
52
+ setValue(-0);
53
+ expect(host.requestUpdate).toHaveBeenCalledTimes(1);
54
+ expect(render(host, () => useState(0))[0]).toBe(-0);
55
+ });
56
+ it.each([null, undefined])("initializes a nullish value only once: %s", initial => {
57
+ const host = createHost();
58
+ const initialize = vi.fn(() => initial);
59
+ render(host, () => useState(initialize));
60
+ render(host, () => useState(initialize));
61
+ expect(initialize).toHaveBeenCalledTimes(1);
62
+ });
63
+ it.each([null, undefined])("preserves a nullish update: %s", nextValue => {
64
+ const host = createHost();
65
+ const [, setValue] = render(host, () => useState(1));
66
+ setValue(nextValue);
67
+ expect(render(host, () => useState(1))[0]).toBe(nextValue);
68
+ });
69
+ it("keeps state slots and hosts independent", () => {
70
+ const host = createHost();
71
+ const otherHost = createHost();
72
+ const read = () => [useState(0), useState(10)];
73
+ const [first, second] = render(host, read);
74
+ const [other] = render(otherHost, read);
75
+ first[1](1);
76
+ expect(render(host, read).map(([value]) => value)).toEqual([1, 10]);
77
+ expect(render(otherHost, read).map(([value]) => value)).toEqual([0, 10]);
78
+ expect(first[1]).not.toBe(second[1]);
79
+ expect(first[1]).not.toBe(other[1]);
80
+ expect(otherHost.requestUpdate).not.toHaveBeenCalled();
81
+ });
82
+ });
83
+ //# sourceMappingURL=use-state.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-state.test.js","sourceRoot":"","sources":["../../src/hooks/use-state.test.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,6 @@ export function useWindowEvent(event, callbackFactory, deps = []) {
6
6
  useEffect(() => {
7
7
  window.addEventListener(event, callback);
8
8
  return () => window.removeEventListener(event, callback);
9
- }, [callback]);
9
+ }, [event, callback]);
10
10
  }
11
11
  //# sourceMappingURL=use-window-event.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-window-event.js","sourceRoot":"","sources":["../../src/hooks/use-window-event.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,MAAM,UAAU,cAAc,CAC1B,KAAQ,EACR,eAAqD,EACrD,OAAc,EAAE;IAEhB,MAAM,QAAQ,GAAG,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IAChD,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACzC,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC7D,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"use-window-event.js","sourceRoot":"","sources":["../../src/hooks/use-window-event.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,MAAM,UAAU,cAAc,CAC1B,KAAQ,EACR,eAAqD,EACrD,OAAc,EAAE;IAEhB,MAAM,QAAQ,GAAG,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IAChD,SAAS,CAAC,GAAG,EAAE;QACX,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACzC,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC7D,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC1B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/data-lit",
3
- "version": "0.10.30",
3
+ "version": "0.10.32",
4
4
  "description": "Adobe data Lit bindings - hooks, elements, decorators",
5
5
  "type": "module",
6
6
  "private": false,
@@ -12,7 +12,7 @@
12
12
  "access": "public"
13
13
  },
14
14
  "dependencies": {
15
- "@adobe/data": "0.10.30"
15
+ "@adobe/data": "0.10.32"
16
16
  },
17
17
  "peerDependencies": {
18
18
  "lit": "^3.3.1"
@@ -34,6 +34,6 @@
34
34
  "dev": "tsc -b -w",
35
35
  "link": "pnpm build && pnpm link --global",
36
36
  "test": "vitest --run --passWithNoTests",
37
- "typecheck": "tsc -b"
37
+ "typecheck": "tsc -b && tsc -p tsconfig.test.json"
38
38
  }
39
39
  }
@@ -0,0 +1,37 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { applyDecorator } from "./apply-decorator.js";
4
+
5
+ const double = (_target: unknown, _key: string, descriptor: PropertyDescriptor) => {
6
+ const original = descriptor.value as (this: unknown) => number;
7
+ return { ...descriptor, value(this: unknown) { return original.call(this) * 2; } };
8
+ };
9
+
10
+ describe("applyDecorator", () => {
11
+ it("decorates a method found on the prototype chain and defines it on the target", () => {
12
+ class Base { value = 2; get() { return this.value; } }
13
+ class Derived extends Base {}
14
+ applyDecorator(Derived.prototype, "get", double);
15
+ expect(new Derived().get()).toBe(4);
16
+ expect(new Base().get()).toBe(2);
17
+ expect(Object.prototype.hasOwnProperty.call(Derived.prototype, "get")).toBe(true);
18
+ });
19
+
20
+ it("decorates an own property and passes target, key, and descriptor", () => {
21
+ const method = () => 1;
22
+ const target = { method };
23
+ const decorator = vi.fn((_target: unknown, _key: string, descriptor: PropertyDescriptor) => descriptor);
24
+ applyDecorator(target, "method", decorator);
25
+ expect(decorator).toHaveBeenCalledWith(target, "method", expect.objectContaining({ value: method }));
26
+ });
27
+
28
+ it("defines exactly the descriptor the decorator returns", () => {
29
+ const target = { method: () => 1 };
30
+ applyDecorator(target, "method", () => ({ value: 5, enumerable: false, writable: false, configurable: true }));
31
+ expect(Object.getOwnPropertyDescriptor(target, "method")).toEqual({ value: 5, enumerable: false, writable: false, configurable: true });
32
+ });
33
+
34
+ it("throws when the property is not on the prototype chain", () => {
35
+ expect(() => applyDecorator({}, "missing", double)).toThrow("Property missing not found in prototype chain");
36
+ });
37
+ });
@@ -0,0 +1,53 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ // @vitest-environment happy-dom
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import { applyServiceDecorators } from "./apply-service-decorators.js";
5
+ import { Component_stack } from "../hooks/component/stack.js";
6
+ import { useState } from "../hooks/use-state.js";
7
+
8
+ function createElement(render: (this: unknown) => unknown, service?: unknown) {
9
+ return Object.assign(new EventTarget(), {
10
+ service,
11
+ hookIndex: 0,
12
+ hooks: [] as unknown[],
13
+ updatedListeners: new Set<() => void>(),
14
+ requestUpdate: vi.fn(),
15
+ isConnected: true,
16
+ render,
17
+ });
18
+ }
19
+
20
+ describe("applyServiceDecorators", () => {
21
+ it("renders null without calling render while there is no service", () => {
22
+ const render = vi.fn(() => "rendered");
23
+ const element = createElement(render);
24
+ applyServiceDecorators(element);
25
+ expect(element.render()).toBeNull();
26
+ expect(render).not.toHaveBeenCalled();
27
+ });
28
+
29
+ it("renders with hooks once a service is set", () => {
30
+ const element = createElement(function (this: unknown) {
31
+ expect(Component_stack.active()).toBe(this);
32
+ return useState("value")[0];
33
+ }, {});
34
+ applyServiceDecorators(element);
35
+ expect(element.render()).toBe("value");
36
+ expect(Component_stack.active()).toBeUndefined();
37
+ });
38
+
39
+ it("decorates a render inherited from the prototype", () => {
40
+ class Base { service = {}; render() { return "inherited"; } }
41
+ const element = Object.assign(new Base(), { hooks: [], hookIndex: 0 });
42
+ applyServiceDecorators(element);
43
+ expect(Object.prototype.hasOwnProperty.call(element, "render")).toBe(true);
44
+ expect(element.render()).toBe("inherited");
45
+ });
46
+
47
+ it("restores the hook stack when render throws", () => {
48
+ const element = createElement(() => { throw new Error("render failed"); }, {});
49
+ applyServiceDecorators(element);
50
+ expect(() => element.render()).toThrow("render failed");
51
+ expect(Component_stack.active()).toBeUndefined();
52
+ });
53
+ });
@@ -0,0 +1,31 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { requireService } from "./require-service.js";
4
+
5
+ function guard(render: (this: { service?: unknown }) => unknown) {
6
+ return requireService()({}, "render", { value: render }).value as (this: { service?: unknown }) => unknown;
7
+ }
8
+
9
+ describe("requireService", () => {
10
+ it("returns null without calling render while there is no service", () => {
11
+ const render = vi.fn(() => "rendered");
12
+ expect(guard(render).call({})).toBeNull();
13
+ expect(render).not.toHaveBeenCalled();
14
+ });
15
+
16
+ it("calls render with the receiver once a service is set", () => {
17
+ const host = { service: {} as unknown };
18
+ const render = vi.fn(function (this: unknown) { return this; });
19
+ expect(guard(render).call(host)).toBe(host);
20
+ });
21
+
22
+ it("propagates errors from render", () => {
23
+ const render = () => { throw new Error("render failed"); };
24
+ expect(() => guard(render).call({ service: {} })).toThrow("render failed");
25
+ });
26
+
27
+ it("returns the descriptor it modified", () => {
28
+ const descriptor: PropertyDescriptor = { value: () => 1 };
29
+ expect(requireService()({}, "render", descriptor)).toBe(descriptor);
30
+ });
31
+ });
@@ -0,0 +1,80 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ // @vitest-environment happy-dom
3
+ import { afterEach, describe, expect, it } from "vitest";
4
+ import { html } from "lit";
5
+ import type { Service } from "@adobe/data/service";
6
+ import { ApplicationElement } from "./application-element.js";
7
+ import { useState } from "../hooks/use-state.js";
8
+
9
+ type MainService = Service & { kind: "main" };
10
+ const createService = (kind: string) => ({ serviceName: kind, kind }) as unknown as MainService;
11
+
12
+ class ProbeElement extends ApplicationElement<MainService> {
13
+ override render() {
14
+ const [value] = useState("hooked");
15
+ return html`${value}`;
16
+ }
17
+ }
18
+ customElements.define("application-element-probe", ProbeElement);
19
+
20
+ class UnrenderedElement extends ApplicationElement<MainService> {}
21
+ customElements.define("application-element-unrendered", UnrenderedElement);
22
+
23
+ function under(parent: Element) {
24
+ const element = document.createElement("application-element-probe") as ProbeElement;
25
+ parent.append(element);
26
+ return element;
27
+ }
28
+
29
+ afterEach(async () => {
30
+ document.body.replaceChildren();
31
+ await Promise.resolve();
32
+ });
33
+
34
+ describe("ApplicationElement", () => {
35
+ it("finds the nearest ancestor service on connect", () => {
36
+ const outer = Object.assign(document.createElement("div"), { service: createService("outer") });
37
+ const inner = Object.assign(document.createElement("div"), { service: createService("inner") });
38
+ outer.append(inner);
39
+ document.body.append(outer);
40
+ expect(under(inner).service).toBe(inner.service);
41
+ });
42
+
43
+ it("keeps an injected service", () => {
44
+ const parent = Object.assign(document.createElement("div"), { service: createService("ancestor") });
45
+ document.body.append(parent);
46
+ const injected = createService("injected");
47
+ const element = document.createElement("application-element-probe") as ProbeElement;
48
+ element.service = injected;
49
+ parent.append(element);
50
+ expect(element.service).toBe(injected);
51
+ });
52
+
53
+ it("skips ancestors that fail the type guard", () => {
54
+ const matching = Object.assign(document.createElement("div"), { service: createService("main") });
55
+ const other = Object.assign(document.createElement("div"), { service: { serviceName: "other" } });
56
+ matching.append(other);
57
+ document.body.append(matching);
58
+ const element = document.createElement("application-element-probe") as ProbeElement;
59
+ element.typeGuard = (service): service is MainService => (service as MainService | undefined)?.kind === "main";
60
+ other.append(element);
61
+ expect(element.service).toBe(matching.service);
62
+ });
63
+
64
+ it("skips values that are not services and leaves service unset without a provider", () => {
65
+ const parent = Object.assign(document.createElement("div"), { service: { notAService: true } });
66
+ document.body.append(parent);
67
+ expect(under(parent).service).toBeUndefined();
68
+ });
69
+
70
+ it("renders with hooks", async () => {
71
+ const element = under(document.body);
72
+ await element.updateComplete;
73
+ expect(element.shadowRoot?.textContent).toBe("hooked");
74
+ });
75
+
76
+ it("throws from the default render", () => {
77
+ const element = new UnrenderedElement();
78
+ expect(() => element.render()).toThrow("render function must be overridden");
79
+ });
80
+ });