@adobe/data 0.9.36 → 0.9.39
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/AGENTS.md +91 -0
- package/dist/ecs/database/observe-select-deep.d.ts +23 -0
- package/dist/ecs/database/observe-select-deep.js +15 -0
- package/dist/ecs/database/observe-select-deep.js.map +1 -0
- package/dist/ecs/database/observe-select-deep.type-test.js +111 -0
- package/dist/ecs/database/observe-select-deep.type-test.js.map +1 -0
- package/dist/ecs/persistence-service/create-storage-persistence-service.js +4 -3
- package/dist/ecs/persistence-service/create-storage-persistence-service.js.map +1 -1
- package/dist/ecs/persistence-service/create-storage-persistence-service.test.js +52 -0
- package/dist/ecs/persistence-service/create-storage-persistence-service.test.js.map +1 -0
- package/dist/service/agentic-service/link.d.ts +15 -0
- package/dist/service/agentic-service/link.js +3 -0
- package/dist/service/agentic-service/link.js.map +1 -0
- package/dist/service/async-data-service/is-valid.d.ts +3 -1
- package/dist/service/async-data-service/is-valid.type-test.d.ts +1 -0
- package/dist/service/async-data-service/is-valid.type-test.js +3 -0
- package/dist/service/async-data-service/is-valid.type-test.js.map +1 -0
- package/dist/service/dynamic-service/semantic-service.d.ts +19 -0
- package/dist/service/dynamic-service/semantic-service.js +2 -0
- package/dist/service/dynamic-service/semantic-service.js.map +1 -0
- package/dist/service/semantic-service/semantic-service.d.ts +19 -0
- package/dist/service/semantic-service/semantic-service.js +2 -0
- package/dist/service/semantic-service/semantic-service.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -1
- package/references/data-lit/README.md +18 -0
- package/references/data-lit/package.json +35 -0
- package/references/data-lit/src/decorators/apply-decorator.ts +26 -0
- package/references/data-lit/src/decorators/apply-service-decorators.ts +15 -0
- package/references/data-lit/src/decorators/index.ts +4 -0
- package/references/data-lit/src/decorators/require-service.ts +20 -0
- package/references/data-lit/src/elements/application-element.ts +42 -0
- package/references/data-lit/src/elements/application-host.ts +43 -0
- package/references/data-lit/src/elements/database-element.ts +42 -0
- package/references/data-lit/src/elements/index.ts +5 -0
- package/references/data-lit/src/functions/index.ts +3 -0
- package/references/data-lit/src/functions/iterate-self-and-ancestors.ts +23 -0
- package/references/data-lit/src/hooks/attach-decorator.ts +32 -0
- package/references/data-lit/src/hooks/component/component.ts +12 -0
- package/references/data-lit/src/hooks/component/stack.ts +19 -0
- package/references/data-lit/src/hooks/index.ts +21 -0
- package/references/data-lit/src/hooks/use-connected.ts +41 -0
- package/references/data-lit/src/hooks/use-debounce.ts +26 -0
- package/references/data-lit/src/hooks/use-drag-observe.ts +59 -0
- package/references/data-lit/src/hooks/use-drag-transaction.ts +46 -0
- package/references/data-lit/src/hooks/use-draggable.ts +112 -0
- package/references/data-lit/src/hooks/use-effect.ts +19 -0
- package/references/data-lit/src/hooks/use-element.ts +83 -0
- package/references/data-lit/src/hooks/use-memo.ts +16 -0
- package/references/data-lit/src/hooks/use-observable-values.ts +10 -0
- package/references/data-lit/src/hooks/use-observable.ts +15 -0
- package/references/data-lit/src/hooks/use-ref.ts +8 -0
- package/references/data-lit/src/hooks/use-resize-observer.ts +31 -0
- package/references/data-lit/src/hooks/use-state.ts +19 -0
- package/references/data-lit/src/hooks/use-updated.ts +56 -0
- package/references/data-lit/src/hooks/use-window-event.ts +16 -0
- package/references/data-lit/src/hooks/with-hooks.ts +22 -0
- package/references/data-lit/src/index.ts +6 -0
- package/references/data-lit/tsconfig.json +11 -0
- package/references/data-lit-todo/package.json +30 -0
- package/references/data-lit-todo/src/elements/todo-list/todo-list-presentation.ts +22 -0
- package/references/data-lit-todo/src/elements/todo-list/todo-list.css.ts +12 -0
- package/references/data-lit-todo/src/elements/todo-list/todo-list.ts +45 -0
- package/references/data-lit-todo/src/elements/todo-row/index.ts +2 -0
- package/references/data-lit-todo/src/elements/todo-row/todo-row-presentation.ts +68 -0
- package/references/data-lit-todo/src/elements/todo-row/todo-row.css.ts +49 -0
- package/references/data-lit-todo/src/elements/todo-row/todo-row.ts +46 -0
- package/references/data-lit-todo/src/elements/todo-toolbar/index.ts +2 -0
- package/references/data-lit-todo/src/elements/todo-toolbar/todo-toolbar-presentation.ts +55 -0
- package/references/data-lit-todo/src/elements/todo-toolbar/todo-toolbar.css.ts +34 -0
- package/references/data-lit-todo/src/elements/todo-toolbar/todo-toolbar.ts +62 -0
- package/references/data-lit-todo/src/elements/todo-undo-redo/index.ts +3 -0
- package/references/data-lit-todo/src/elements/todo-undo-redo/todo-undo-redo-presentation.ts +41 -0
- package/references/data-lit-todo/src/elements/todo-undo-redo/todo-undo-redo.css.ts +12 -0
- package/references/data-lit-todo/src/elements/todo-undo-redo/todo-undo-redo.ts +37 -0
- package/references/data-lit-todo/src/index.ts +9 -0
- package/references/data-lit-todo/src/main.ts +29 -0
- package/references/data-lit-todo/src/sample-types.ts +14 -0
- package/references/data-lit-todo/src/services/dependent-state-service/create-dependent-state-service.ts +8 -0
- package/references/data-lit-todo/src/services/dependent-state-service/dependent-state/all-todos.ts +5 -0
- package/references/data-lit-todo/src/services/dependent-state-service/dependent-state/complete-todos.ts +5 -0
- package/references/data-lit-todo/src/services/dependent-state-service/dependent-state/incomplete-todos.ts +5 -0
- package/references/data-lit-todo/src/services/dependent-state-service/dependent-state/index.ts +4 -0
- package/references/data-lit-todo/src/services/dependent-state-service/dependent-state-service.ts +4 -0
- package/references/data-lit-todo/src/services/main-service/create-main-service.ts +53 -0
- package/references/data-lit-todo/src/services/main-service/todo-main-service.ts +20 -0
- package/references/data-lit-todo/src/services/state-service/create-todo-database.ts +16 -0
- package/references/data-lit-todo/src/services/state-service/create-todo-store.ts +28 -0
- package/references/data-lit-todo/src/services/state-service/todo-state-service.ts +9 -0
- package/references/data-lit-todo/src/services/state-service/transactions/create-bulk-todos.ts +12 -0
- package/references/data-lit-todo/src/services/state-service/transactions/create-todo.test.ts +17 -0
- package/references/data-lit-todo/src/services/state-service/transactions/create-todo.ts +15 -0
- package/references/data-lit-todo/src/services/state-service/transactions/delete-all-todos.ts +18 -0
- package/references/data-lit-todo/src/services/state-service/transactions/delete-todo.test.ts +25 -0
- package/references/data-lit-todo/src/services/state-service/transactions/delete-todo.ts +11 -0
- package/references/data-lit-todo/src/services/state-service/transactions/drag-todo.ts +19 -0
- package/references/data-lit-todo/src/services/state-service/transactions/index.ts +8 -0
- package/references/data-lit-todo/src/services/state-service/transactions/reorder-todos.ts +13 -0
- package/references/data-lit-todo/src/services/state-service/transactions/toggle-complete.test.ts +78 -0
- package/references/data-lit-todo/src/services/state-service/transactions/toggle-complete.ts +15 -0
- package/references/data-lit-todo/src/todo-element.ts +6 -0
- package/references/data-lit-todo/src/todo-host.ts +29 -0
- package/references/data-lit-todo/src/todo-main-element.ts +40 -0
- package/references/data-lit-todo/src/todo-sample.ts +21 -0
- package/references/data-lit-todo/tsconfig.json +10 -0
- package/references/data-react/README.md +47 -0
- package/references/data-react/package.json +36 -0
- package/references/data-react/src/context/database-context.tsx +41 -0
- package/references/data-react/src/hooks/index.ts +5 -0
- package/references/data-react/src/hooks/use-database.ts +13 -0
- package/references/data-react/src/hooks/use-observable-values.ts +15 -0
- package/references/data-react/src/hooks/use-observable.ts +14 -0
- package/references/data-react/src/index.ts +7 -0
- package/references/data-react/tsconfig.json +12 -0
- package/references/data-react-hello/package.json +25 -0
- package/references/data-react-hello/src/App.tsx +13 -0
- package/references/data-react-hello/src/Counter.tsx +21 -0
- package/references/data-react-hello/src/counter-plugin.ts +14 -0
- package/references/data-react-hello/src/main.tsx +9 -0
- package/references/data-react-hello/tsconfig.json +16 -0
- package/references/data-react-pixie/package.json +28 -0
- package/references/data-react-pixie/src/App.tsx +21 -0
- package/references/data-react-pixie/src/bunny.png +0 -0
- package/references/data-react-pixie/src/filter-toggle.tsx +46 -0
- package/references/data-react-pixie/src/filters.ts +22 -0
- package/references/data-react-pixie/src/fox.png +0 -0
- package/references/data-react-pixie/src/hooks/use-database.ts +6 -0
- package/references/data-react-pixie/src/hooks/use-texture.ts +22 -0
- package/references/data-react-pixie/src/main.tsx +14 -0
- package/references/data-react-pixie/src/pixi-react.d.ts +1 -0
- package/references/data-react-pixie/src/pixie-plugin.ts +100 -0
- package/references/data-react-pixie/src/sprite-container.tsx +23 -0
- package/references/data-react-pixie/src/sprite-urls.ts +9 -0
- package/references/data-react-pixie/src/sprite.tsx +35 -0
- package/references/data-react-pixie/src/tick.tsx +10 -0
- package/references/data-react-pixie/src/vite-env.d.ts +6 -0
- package/references/data-react-pixie/tsconfig.json +16 -0
- package/dist/LICENSE +0 -21
- package/dist/README.md +0 -296
- package/dist/package.json +0 -183
- package/dist/service/agentic-service/create-from-config.d.ts +0 -45
- package/dist/service/agentic-service/create-from-config.js +0 -85
- package/dist/service/agentic-service/create-from-config.js.map +0 -1
- package/dist/service/agentic-service/create.interface-first-spike.type-test.js +0 -119
- package/dist/service/agentic-service/create.interface-first-spike.type-test.js.map +0 -1
- package/dist/service/agentic-service/index.d.ts +0 -1
- package/dist/service/agentic-service/index.js +0 -3
- package/dist/service/agentic-service/index.js.map +0 -1
- package/dist/service/dynamic-service/create.interface-first-spike.type-test.js +0 -118
- package/dist/service/dynamic-service/create.interface-first-spike.type-test.js.map +0 -1
- /package/dist/{service/agentic-service/create.interface-first-spike.type-test.d.ts → ecs/database/observe-select-deep.type-test.d.ts} +0 -0
- /package/dist/{service/dynamic-service/create.interface-first-spike.type-test.d.ts → ecs/persistence-service/create-storage-persistence-service.test.d.ts} +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { Vec2 } from '@adobe/data/math';
|
|
4
|
+
import { useEffect } from './use-effect.js';
|
|
5
|
+
|
|
6
|
+
function toCssUnitString(value: number): string {
|
|
7
|
+
return `${value}px`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface DraggableProps {
|
|
11
|
+
// onDragStart should return the initial position of the element
|
|
12
|
+
onDragStart: (e: PointerEvent) => Vec2 | void;
|
|
13
|
+
onDrag: (e: PointerEvent, newPosition: Vec2, delta: Vec2) => void;
|
|
14
|
+
onDragEnd?: (e: PointerEvent, newPosition: Vec2, delta: Vec2) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Called if this hook is destroyed before the drag is completed.
|
|
17
|
+
*/
|
|
18
|
+
onDragCancel?: () => void;
|
|
19
|
+
minDragDistance?: number;
|
|
20
|
+
dragCursor?: string;
|
|
21
|
+
addPlaceholder?: boolean;
|
|
22
|
+
stopPropagation?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useDraggable(element: HTMLElement, props: DraggableProps, dependencies: unknown[]) {
|
|
26
|
+
const { minDragDistance = 10, dragCursor = 'grab', addPlaceholder = false, stopPropagation = false } = props;
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
let downPosition: Vec2 | null = null;
|
|
29
|
+
// the bounds of the element when the pointer was first pressed down.
|
|
30
|
+
let dragStartOffset: Vec2 | null = null;
|
|
31
|
+
let originalCursor = '';
|
|
32
|
+
let placeholder: HTMLElement | null = null;
|
|
33
|
+
let movePosition: Vec2 = [0, 0];
|
|
34
|
+
function notify(e: PointerEvent, dragListener: DraggableProps["onDrag"]) {
|
|
35
|
+
const delta = Vec2.subtract(movePosition, downPosition!);
|
|
36
|
+
dragListener(e, Vec2.add(dragStartOffset!, delta), delta);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function onPointerMove(e: PointerEvent) {
|
|
40
|
+
movePosition = [e.clientX, e.clientY];
|
|
41
|
+
if (Vec2.length(Vec2.subtract(movePosition, downPosition!)) >= minDragDistance) {
|
|
42
|
+
if (!dragStartOffset) {
|
|
43
|
+
dragStartOffset = [element.offsetLeft, element.offsetTop];
|
|
44
|
+
props.onDragStart(e);
|
|
45
|
+
if (dragCursor) {
|
|
46
|
+
originalCursor = element.style.cursor;
|
|
47
|
+
element.style.cursor = dragCursor;
|
|
48
|
+
}
|
|
49
|
+
// add a placeholder so the parent element doesn't change size when the element is dragged.
|
|
50
|
+
if (addPlaceholder) {
|
|
51
|
+
placeholder = document.createElement('div');
|
|
52
|
+
Object.assign(placeholder.style, {
|
|
53
|
+
position: 'absolute',
|
|
54
|
+
backgroundColor: 'pink',
|
|
55
|
+
left: toCssUnitString(element.offsetLeft),
|
|
56
|
+
top: toCssUnitString(element.offsetTop),
|
|
57
|
+
width: toCssUnitString(element.offsetWidth),
|
|
58
|
+
height: toCssUnitString(element.offsetHeight),
|
|
59
|
+
visibility: 'hidden',
|
|
60
|
+
});
|
|
61
|
+
element.parentElement?.appendChild(placeholder);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (dragStartOffset) {
|
|
66
|
+
notify(e, props.onDrag);
|
|
67
|
+
}
|
|
68
|
+
if (stopPropagation) e.stopPropagation();
|
|
69
|
+
}
|
|
70
|
+
function cleanup() {
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
72
|
+
window.removeEventListener('pointerup', onPointerUp);
|
|
73
|
+
window.removeEventListener('pointermove', onPointerMove);
|
|
74
|
+
if (dragCursor) {
|
|
75
|
+
element.style.cursor = originalCursor;
|
|
76
|
+
}
|
|
77
|
+
if (placeholder) {
|
|
78
|
+
placeholder.remove();
|
|
79
|
+
placeholder = null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function onPointerUp(e: PointerEvent) {
|
|
83
|
+
cleanup();
|
|
84
|
+
if (dragStartOffset && props.onDragEnd) {
|
|
85
|
+
notify(e, props.onDragEnd);
|
|
86
|
+
}
|
|
87
|
+
if (stopPropagation) e.stopPropagation();
|
|
88
|
+
downPosition = null;
|
|
89
|
+
dragStartOffset = null;
|
|
90
|
+
}
|
|
91
|
+
function onPointerDown(e: PointerEvent) {
|
|
92
|
+
// Only start drag transaction for left mouse button clicks
|
|
93
|
+
if (e.button !== 0) return;
|
|
94
|
+
|
|
95
|
+
window.addEventListener('pointermove', onPointerMove);
|
|
96
|
+
window.addEventListener('pointerup', onPointerUp);
|
|
97
|
+
downPosition = [e.clientX, e.clientY];
|
|
98
|
+
if (stopPropagation) e.stopPropagation();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
element?.addEventListener('pointerdown', onPointerDown);
|
|
102
|
+
return () => {
|
|
103
|
+
element?.removeEventListener('pointerdown', onPointerDown);
|
|
104
|
+
if (downPosition) {
|
|
105
|
+
cleanup();
|
|
106
|
+
}
|
|
107
|
+
if (dragStartOffset) {
|
|
108
|
+
props.onDragCancel?.();
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}, [element, ...(dependencies ?? [])]);
|
|
112
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { equalsShallow } from "@adobe/data/equals-shallow";
|
|
4
|
+
import type { Component } from "./component/component.js"
|
|
5
|
+
import { Component_stack } from "./component/stack.js";
|
|
6
|
+
|
|
7
|
+
export type EffectCallback = () => (void | (() => void))
|
|
8
|
+
type EffectHookState = { dispose?: () => void, dependencies: unknown[] };
|
|
9
|
+
|
|
10
|
+
export function useEffect<T extends Component>(callback: EffectCallback, dependencies: unknown[] = []) {
|
|
11
|
+
const component = Component_stack.active() as T;
|
|
12
|
+
const hookIndex = component.hookIndex++;
|
|
13
|
+
const oldHookState = component.hooks[hookIndex] as EffectHookState | undefined;
|
|
14
|
+
const rerunEffect = !oldHookState || !equalsShallow(dependencies, oldHookState.dependencies);
|
|
15
|
+
if (rerunEffect) {
|
|
16
|
+
oldHookState?.dispose?.();
|
|
17
|
+
component.hooks[hookIndex] = { dispose: callback.call(component) ?? undefined, dependencies };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { Component_stack } from "./component/stack.js";
|
|
4
|
+
import { useEffect } from "./use-effect.js";
|
|
5
|
+
import { useState } from "./use-state.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Hook that returns the currently active component.
|
|
9
|
+
* If a querySelector is provided, it dynamically observes for child elements
|
|
10
|
+
* matching the selector and triggers rerender when found.
|
|
11
|
+
*/
|
|
12
|
+
export function useElement<K extends keyof HTMLElementTagNameMap>(querySelector: K): HTMLElementTagNameMap[K] | null
|
|
13
|
+
export function useElement<T = HTMLElement>(): T
|
|
14
|
+
export function useElement<T = HTMLElement>(querySelector?: string): T | null {
|
|
15
|
+
const component = Component_stack.active();
|
|
16
|
+
|
|
17
|
+
// If no querySelector provided, return the active component
|
|
18
|
+
if (!querySelector) {
|
|
19
|
+
return component as unknown as T;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Use state to track the found element
|
|
23
|
+
const [foundElement, setFoundElement] = useState<T | null>(null);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const element = component as unknown as HTMLElement;
|
|
27
|
+
|
|
28
|
+
// Check if element already exists
|
|
29
|
+
const existingElement = element.querySelector(querySelector) as T | null;
|
|
30
|
+
if (existingElement) {
|
|
31
|
+
setFoundElement(existingElement);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Set up MutationObserver to watch for new child elements
|
|
36
|
+
const observer = new MutationObserver((mutations) => {
|
|
37
|
+
for (const mutation of mutations) {
|
|
38
|
+
if (mutation.type === 'childList') {
|
|
39
|
+
// Check if any added nodes match the selector
|
|
40
|
+
for (const node of mutation.addedNodes) {
|
|
41
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
42
|
+
const element = node as Element;
|
|
43
|
+
// Check if the added node itself matches
|
|
44
|
+
if (element.matches(querySelector)) {
|
|
45
|
+
setFoundElement(element as T);
|
|
46
|
+
observer.disconnect();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// Check if any descendant matches
|
|
50
|
+
const descendant = element.querySelector(querySelector) as T | null;
|
|
51
|
+
if (descendant) {
|
|
52
|
+
setFoundElement(descendant);
|
|
53
|
+
observer.disconnect();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Start observing both element and shadowRoot
|
|
63
|
+
for (const observe of [element.shadowRoot, element]) {
|
|
64
|
+
if (observe) {
|
|
65
|
+
observer.observe(observe, {
|
|
66
|
+
childList: true,
|
|
67
|
+
subtree: true
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
observer.observe(element.shadowRoot ?? element, {
|
|
72
|
+
childList: true,
|
|
73
|
+
subtree: true
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Cleanup function
|
|
77
|
+
return () => {
|
|
78
|
+
observer.disconnect();
|
|
79
|
+
};
|
|
80
|
+
}, [querySelector]);
|
|
81
|
+
|
|
82
|
+
return foundElement;
|
|
83
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { useEffect } from "./use-effect.js";
|
|
4
|
+
import { useState } from "./use-state.js";
|
|
5
|
+
|
|
6
|
+
export function useMemo<T>(calculateValue: () => T, dependencies: unknown[] = []): T {
|
|
7
|
+
const [state, setState] = useState<T | undefined>(undefined);
|
|
8
|
+
let currentValue = state;
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
currentValue = calculateValue();
|
|
12
|
+
setState(currentValue);
|
|
13
|
+
}, dependencies);
|
|
14
|
+
|
|
15
|
+
return currentValue!;
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { Observe } from "@adobe/data/observe";
|
|
4
|
+
import { useMemo } from "./use-memo.js";
|
|
5
|
+
import { useObservable } from "./use-observable.js";
|
|
6
|
+
|
|
7
|
+
export function useObservableValues<T extends Record<string, Observe<unknown>>>(factory: () => T, deps: unknown[] = []): { [K in keyof T]: T[K] extends Observe<infer U> ? U : never } | undefined {
|
|
8
|
+
const observable = useMemo(() => Observe.fromProperties(factory()), deps);
|
|
9
|
+
return useObservable(observable);
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { useState } from "./use-state.js";
|
|
4
|
+
import { useEffect } from "./use-effect.js";
|
|
5
|
+
import { Observe } from "@adobe/data/observe";
|
|
6
|
+
|
|
7
|
+
export function useObservable<T>(observable: Observe<T>): T | undefined {
|
|
8
|
+
let [value, setValue] = useState<T | undefined>(undefined);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
return observable(newValue => {
|
|
11
|
+
setValue(value = newValue);
|
|
12
|
+
});
|
|
13
|
+
}, [observable]);
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { useEffect } from "./use-effect.js";
|
|
4
|
+
import { useElement } from "./use-element.js";
|
|
5
|
+
|
|
6
|
+
export interface ResizeInfo {
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
entry: ResizeObserverEntry;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Hook to observe element resizes. Calls callback with width, height, and the original entry
|
|
14
|
+
* when the element's size changes.
|
|
15
|
+
*
|
|
16
|
+
* @param onResize - Callback invoked when element size changes
|
|
17
|
+
*/
|
|
18
|
+
export function useResizeObserver(onResize: (info: ResizeInfo) => void) {
|
|
19
|
+
const element = useElement();
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const observer = new ResizeObserver(entries => {
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
const { width, height } = entry.contentRect;
|
|
25
|
+
onResize({ width, height, entry });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
observer.observe(element);
|
|
29
|
+
return () => observer.disconnect();
|
|
30
|
+
}, [element, onResize]);
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { Component_stack } from "./component/stack.js";
|
|
4
|
+
|
|
5
|
+
export function useState<T>(initializer: () => T): [T, (value: T) => void]
|
|
6
|
+
export function useState<T>(initialValue: T): [T, (value: T) => void]
|
|
7
|
+
export function useState<T>(initial: () => T) {
|
|
8
|
+
const component = Component_stack.active();
|
|
9
|
+
const hookIndex = component.hookIndex++;
|
|
10
|
+
const value = component.hooks[hookIndex] ??= (typeof initial === "function" ? initial() : initial);
|
|
11
|
+
return [
|
|
12
|
+
value,
|
|
13
|
+
(newValue: T) => {
|
|
14
|
+
component.hooks[hookIndex] = newValue;
|
|
15
|
+
component.requestUpdate();
|
|
16
|
+
}
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
import { useElement } from "./use-element.js";
|
|
3
|
+
import { useEffect } from "./use-effect.js";
|
|
4
|
+
|
|
5
|
+
export interface UpdateEventHost {
|
|
6
|
+
updateListeners: Set<() => void>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function hasUpdateListeners(el: unknown): el is UpdateEventHost {
|
|
10
|
+
return (
|
|
11
|
+
typeof el === 'object' &&
|
|
12
|
+
el !== null &&
|
|
13
|
+
'updateListeners' in el &&
|
|
14
|
+
el['updateListeners'] instanceof Set
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function makeUpdateEventHost<T extends Element>(
|
|
19
|
+
el: T
|
|
20
|
+
): T & UpdateEventHost {
|
|
21
|
+
if (hasUpdateListeners(el)) return el as T & UpdateEventHost;
|
|
22
|
+
|
|
23
|
+
const updateListeners = new Set<() => void>();
|
|
24
|
+
Object.defineProperty(el, 'updateListeners', {
|
|
25
|
+
value: updateListeners,
|
|
26
|
+
writable: false,
|
|
27
|
+
enumerable: false,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!('updated' in el)) {
|
|
31
|
+
throw new Error('Element does not have an updated method');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const element = el as unknown as T & {
|
|
35
|
+
updated: (changedProps: Map<PropertyKey, unknown>) => void;
|
|
36
|
+
} & UpdateEventHost;
|
|
37
|
+
const orig = element.updated.bind(el);
|
|
38
|
+
|
|
39
|
+
element.updated = (changedProps) => {
|
|
40
|
+
orig(changedProps);
|
|
41
|
+
for (const fn of updateListeners) fn();
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return element;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
export function useUpdated(listener: () => void, dependencies: unknown[] = []) {
|
|
49
|
+
const element = makeUpdateEventHost(useElement());
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
element.updateListeners.add(listener);
|
|
52
|
+
return () => {
|
|
53
|
+
element.updateListeners.delete(listener);
|
|
54
|
+
}
|
|
55
|
+
}, dependencies);
|
|
56
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { useEffect } from "./use-effect.js";
|
|
4
|
+
import { useMemo } from "./use-memo.js";
|
|
5
|
+
|
|
6
|
+
export function useWindowEvent<K extends keyof WindowEventMap>(
|
|
7
|
+
event: K,
|
|
8
|
+
callbackFactory: () => (e: WindowEventMap[K]) => void,
|
|
9
|
+
deps: any[] = []
|
|
10
|
+
) {
|
|
11
|
+
const callback = useMemo(callbackFactory, deps);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
window.addEventListener(event, callback);
|
|
14
|
+
return () => window.removeEventListener(event, callback);
|
|
15
|
+
}, [callback]);
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import type { Component } from "./component/component.js";
|
|
4
|
+
import { Component_stack } from "./component/stack.js";
|
|
5
|
+
|
|
6
|
+
export function withHooks<This extends Component, Args extends any[], Return>(
|
|
7
|
+
target: object,
|
|
8
|
+
propertyKey: string,
|
|
9
|
+
descriptor: TypedPropertyDescriptor<(this: This, ...args: Args) => Return>
|
|
10
|
+
): TypedPropertyDescriptor<(this: This, ...args: Args) => Return> {
|
|
11
|
+
const originalMethod = descriptor.value!;
|
|
12
|
+
descriptor.value = function (this: This, ...args: Args): Return {
|
|
13
|
+
Component_stack.push(this);
|
|
14
|
+
try {
|
|
15
|
+
return originalMethod.apply(this, args);
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
Component_stack.pop();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return descriptor;
|
|
22
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "data-lit-todo",
|
|
3
|
+
"version": "0.9.39",
|
|
4
|
+
"description": "Todo sample app demonstrating @adobe/data with Lit",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": true,
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"dev": "vite",
|
|
10
|
+
"publish-public": "true"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@adobe/data": "workspace:*",
|
|
14
|
+
"@adobe/data-lit": "workspace:*",
|
|
15
|
+
"@spectrum-web-components/action-button": "^1.7.0",
|
|
16
|
+
"@spectrum-web-components/action-group": "^1.7.0",
|
|
17
|
+
"@spectrum-web-components/button": "^1.7.0",
|
|
18
|
+
"@spectrum-web-components/card": "^1.7.0",
|
|
19
|
+
"@spectrum-web-components/checkbox": "^1.7.0",
|
|
20
|
+
"@spectrum-web-components/icons-workflow": "^1.7.0",
|
|
21
|
+
"@spectrum-web-components/styles": "^1.7.0",
|
|
22
|
+
"@spectrum-web-components/theme": "^1.7.0",
|
|
23
|
+
"lit": "^3.3.1"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"typescript": "^5.8.3",
|
|
27
|
+
"vite": "^6.4.0",
|
|
28
|
+
"vitest": "^1.6.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
import type { Entity } from "@adobe/data/ecs";
|
|
3
|
+
import { html } from 'lit';
|
|
4
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
5
|
+
import '@spectrum-web-components/action-button/sp-action-button.js';
|
|
6
|
+
import '../todo-row/todo-row.js';
|
|
7
|
+
|
|
8
|
+
type RenderArgs = {
|
|
9
|
+
todos: readonly Entity[];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function render(args: RenderArgs) {
|
|
13
|
+
return html`
|
|
14
|
+
<div class="todo-list">
|
|
15
|
+
${repeat(
|
|
16
|
+
args.todos,
|
|
17
|
+
todo => todo,
|
|
18
|
+
(todo, index) => html` <data-todo-row .entity=${todo} .index=${index}></data-todo-row> `
|
|
19
|
+
)}
|
|
20
|
+
</div>
|
|
21
|
+
`;
|
|
22
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
import * as presentation from './todo-list-presentation.js';
|
|
3
|
+
import { styles } from './todo-list.css.js';
|
|
4
|
+
import { customElement } from 'lit/decorators.js';
|
|
5
|
+
import { css } from 'lit';
|
|
6
|
+
|
|
7
|
+
import { useObservableValues } from "@adobe/data-lit";
|
|
8
|
+
import { TodoElement } from '../../todo-element.js';
|
|
9
|
+
import '../todo-row/todo-row.js';
|
|
10
|
+
|
|
11
|
+
export const tagName = 'data-todo-list';
|
|
12
|
+
|
|
13
|
+
declare global {
|
|
14
|
+
interface HTMLElementTagNameMap {
|
|
15
|
+
[tagName]: TodoList;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@customElement(tagName)
|
|
20
|
+
export class TodoList extends TodoElement {
|
|
21
|
+
static styles = [
|
|
22
|
+
styles,
|
|
23
|
+
css`
|
|
24
|
+
:host {
|
|
25
|
+
display: block;
|
|
26
|
+
height: 100%;
|
|
27
|
+
width: 100%;
|
|
28
|
+
overflow-y: auto;
|
|
29
|
+
position: relative;
|
|
30
|
+
}
|
|
31
|
+
`
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
render() {
|
|
35
|
+
const values = useObservableValues(() => ({
|
|
36
|
+
todos: this.service.dependentState.allTodos,
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
if (!values) return;
|
|
40
|
+
|
|
41
|
+
return presentation.render({
|
|
42
|
+
...values,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
|
|
4
|
+
import { useDragTransaction } from "@adobe/data-lit";
|
|
5
|
+
import '@spectrum-web-components/action-button/sp-action-button.js';
|
|
6
|
+
import '@spectrum-web-components/checkbox/sp-checkbox.js';
|
|
7
|
+
import '@spectrum-web-components/icons-workflow/icons/sp-icon-delete.js';
|
|
8
|
+
|
|
9
|
+
// Temporarily disable localization for the sample
|
|
10
|
+
// import { Localized, Unlocalized } from '../../../../services/locale-service/locale-service.js';
|
|
11
|
+
import { DragTodoFunction, Todo } from '../../services/state-service/todo-state-service.js';
|
|
12
|
+
|
|
13
|
+
const TODO_ROW_HEIGHT = 56;
|
|
14
|
+
|
|
15
|
+
// Simplified localization for sample - using static strings
|
|
16
|
+
const localizedStrings = {
|
|
17
|
+
deleteTodo: 'Delete',
|
|
18
|
+
toggleComplete: 'Toggle complete',
|
|
19
|
+
} as const;
|
|
20
|
+
|
|
21
|
+
type RenderArgs = {
|
|
22
|
+
localized: typeof localizedStrings;
|
|
23
|
+
todo: Todo;
|
|
24
|
+
toggleComplete: () => void;
|
|
25
|
+
deleteTodo: () => void;
|
|
26
|
+
dragTodo: DragTodoFunction;
|
|
27
|
+
index: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function render(args: RenderArgs) {
|
|
31
|
+
const { localized, todo, toggleComplete, deleteTodo, index, dragTodo } = args;
|
|
32
|
+
|
|
33
|
+
useDragTransaction({
|
|
34
|
+
transaction: dragTodo,
|
|
35
|
+
update: (value) => {
|
|
36
|
+
if (value.type === 'move') {
|
|
37
|
+
return {
|
|
38
|
+
todo: todo.id,
|
|
39
|
+
dragPosition: value.delta[1],
|
|
40
|
+
};
|
|
41
|
+
} else if (value.type === 'end') {
|
|
42
|
+
const finalIndex = index + Math.round(value.position[1] / TODO_ROW_HEIGHT);
|
|
43
|
+
return {
|
|
44
|
+
todo: todo.id,
|
|
45
|
+
dragPosition: index,
|
|
46
|
+
finalIndex,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
}, [dragTodo, todo.id, index]);
|
|
51
|
+
|
|
52
|
+
const dragging = todo.dragPosition !== null;
|
|
53
|
+
const position = index * TODO_ROW_HEIGHT + (todo.dragPosition ?? 0);
|
|
54
|
+
|
|
55
|
+
return html`
|
|
56
|
+
<div
|
|
57
|
+
class="todo-row ${dragging ? 'dragging' : ''}"
|
|
58
|
+
style="height: ${TODO_ROW_HEIGHT}px; position: absolute; top: ${position}px; left: 0; right: 0;">
|
|
59
|
+
<sp-checkbox @change=${toggleComplete} title=${localized.toggleComplete} ?checked=${todo.complete}> </sp-checkbox>
|
|
60
|
+
|
|
61
|
+
<span class="todo-name"> ${index}: ${todo.name} </span>
|
|
62
|
+
|
|
63
|
+
<sp-action-button @click=${deleteTodo} quiet title=${localized.deleteTodo}>
|
|
64
|
+
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
65
|
+
</sp-action-button>
|
|
66
|
+
</div>
|
|
67
|
+
`;
|
|
68
|
+
}
|