@adobe/data 0.9.56 → 0.9.58
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/dist/cache/blob-store.js +12 -7
- package/dist/cache/blob-store.js.map +1 -1
- package/dist/ecs/database/database.d.ts +59 -2
- package/dist/ecs/database/database.js.map +1 -1
- package/dist/ecs/database/database.reset.test.js +200 -0
- package/dist/ecs/database/database.reset.test.js.map +1 -0
- package/dist/ecs/database/deep-extends-chain.type-test.d.ts +208 -208
- package/dist/ecs/database/observed/create-observed-database.js +4 -0
- package/dist/ecs/database/observed/create-observed-database.js.map +1 -1
- package/dist/ecs/database/observed/observed-database.d.ts +5 -3
- package/dist/ecs/database/public/create-database.d.ts +32 -3
- package/dist/ecs/database/public/create-database.js +12 -69
- package/dist/ecs/database/public/create-database.js.map +1 -1
- package/dist/ecs/database/public/create-database.test.js +86 -4
- package/dist/ecs/database/public/create-database.test.js.map +1 -1
- package/dist/ecs/database/public/create-transaction-dispatcher.d.ts +60 -0
- package/dist/ecs/database/public/create-transaction-dispatcher.js +119 -0
- package/dist/ecs/database/public/create-transaction-dispatcher.js.map +1 -0
- package/dist/ecs/database/reconciling/create-reconciling-database.js +52 -18
- package/dist/ecs/database/reconciling/create-reconciling-database.js.map +1 -1
- package/dist/ecs/database/reconciling/create-reconciling-database.test.js +144 -0
- package/dist/ecs/database/reconciling/create-reconciling-database.test.js.map +1 -1
- package/dist/ecs/database/reconciling/reconciling-database.d.ts +8 -1
- package/dist/ecs/database/reconciling/reconciling-entry.d.ts +9 -3
- package/dist/ecs/database/reconciling/reconciling-entry.js.map +1 -1
- package/dist/ecs/database/transactional-store/create-transactional-store.js +3 -1
- package/dist/ecs/database/transactional-store/create-transactional-store.js.map +1 -1
- package/dist/ecs/database/transactional-store/transactional-store.d.ts +14 -1
- package/dist/ecs/entity-location-table/create-entity-location-table.js +6 -1
- package/dist/ecs/entity-location-table/create-entity-location-table.js.map +1 -1
- package/dist/ecs/entity-location-table/entity-location-table.d.ts +2 -0
- package/dist/ecs/store/core/core.d.ts +2 -0
- package/dist/ecs/store/core/create-core.js +8 -0
- package/dist/ecs/store/core/create-core.js.map +1 -1
- package/dist/ecs/store/public/create-store.js +6 -0
- package/dist/ecs/store/public/create-store.js.map +1 -1
- package/dist/ecs/store/store.d.ts +2 -0
- package/dist/ecs/store/store.js.map +1 -1
- package/dist/ecs/store/transaction-functions.d.ts +36 -4
- package/dist/ecs/store/transaction-functions.type-test.js +50 -0
- package/dist/ecs/store/transaction-functions.type-test.js.map +1 -0
- package/dist/observe/to-async-generator.js +18 -8
- package/dist/observe/to-async-generator.js.map +1 -1
- package/dist/observe/to-async-generator.test.js +24 -0
- package/dist/observe/to-async-generator.test.js.map +1 -1
- package/dist/service/async-data-service/async-data-service.d.ts +1 -0
- package/dist/service/async-data-service/async-data-service.js +3 -0
- package/dist/service/async-data-service/async-data-service.js.map +1 -0
- package/dist/service/async-data-service/example.js +1 -1
- package/dist/service/async-data-service/example.js.map +1 -1
- package/dist/service/index.d.ts +2 -1
- package/dist/service/index.js +2 -1
- package/dist/service/index.js.map +1 -1
- package/dist/service/{agentic-service/link.js → ui-service/example.js} +1 -1
- package/dist/service/ui-service/example.js.map +1 -0
- package/dist/service/ui-service/from-service.d.ts +35 -0
- package/dist/service/{async-data-service/is-valid.type-test.js → ui-service/from-service.js} +1 -1
- package/dist/service/ui-service/from-service.js.map +1 -0
- package/dist/service/ui-service/is-valid.d.ts +11 -0
- package/dist/service/ui-service/is-valid.js +3 -0
- package/dist/service/ui-service/is-valid.js.map +1 -0
- package/dist/service/ui-service/public.d.ts +2 -0
- package/dist/service/ui-service/public.js +4 -0
- package/dist/service/ui-service/public.js.map +1 -0
- package/dist/service/ui-service/ui-service.d.ts +1 -0
- package/dist/service/ui-service/ui-service.js +3 -0
- package/dist/service/ui-service/ui-service.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/references/data-lit/package.json +1 -1
- package/references/data-lit/src/elements/application-element.ts +11 -1
- package/references/data-lit/src/elements/database-element.ts +3 -2
- package/references/data-lit/src/elements/database-element.type-test.ts +85 -0
- package/references/data-lit/src/hooks/index.ts +2 -0
- package/references/data-lit/src/hooks/use-drag-generator.ts +43 -0
- package/references/data-lit/src/hooks/use-drag-observe.ts +8 -2
- package/references/data-lit/src/hooks/use-pointer-observe.ts +79 -0
- package/references/data-lit-tictactoe/package.json +4 -1
- package/references/data-lit-tictactoe/src/elements/tictactoe-app/tictactoe-app-element.ts +23 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-app/tictactoe-app-presentation.ts +12 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-app/tictactoe-app.ts +14 -18
- package/references/data-lit-tictactoe/src/elements/tictactoe-board/tictactoe-board-element.ts +23 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-board/tictactoe-board-presentation.ts +6 -7
- package/references/data-lit-tictactoe/src/elements/tictactoe-board/tictactoe-board.ts +5 -15
- package/references/data-lit-tictactoe/src/elements/tictactoe-cell/tictactoe-cell-element.ts +45 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-cell/tictactoe-cell-presentation.ts +2 -1
- package/references/data-lit-tictactoe/src/elements/tictactoe-cell/tictactoe-cell.ts +5 -38
- package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud-element.ts +57 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud-presentation.ts +10 -2
- package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud.css.ts +36 -1
- package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud.ts +5 -41
- package/references/data-lit-tictactoe/src/index.ts +16 -0
- package/references/data-lit-tictactoe/src/main.ts +9 -3
- package/references/data-lit-tictactoe/src/state/tictactoe-plugin.ts +18 -10
- package/references/data-lit-tictactoe/src/tictactoe-element.ts +8 -3
- package/references/data-lit-tictactoe/src/types/board-state/current-player.ts +2 -2
- package/references/data-lit-tictactoe/src/types/player-mark/is.ts +8 -0
- package/references/data-lit-tictactoe/src/types/player-mark/mark-color.ts +12 -0
- package/references/data-lit-tictactoe/src/types/player-mark/opponent.ts +12 -0
- package/references/data-lit-tictactoe/src/types/player-mark/public.ts +4 -0
- package/references/data-lit-tictactoe/src/types/player-mark/values.ts +6 -0
- package/references/data-react/package.json +1 -1
- package/references/data-react-hello/package.json +1 -1
- package/references/data-react-pixie/package.json +1 -1
- package/dist/cache/blob-store.test.js +0 -122
- package/dist/cache/blob-store.test.js.map +0 -1
- package/dist/cache/data-cache.test.js +0 -41
- package/dist/cache/data-cache.test.js.map +0 -1
- package/dist/cache/expiring-data-cache.test.js +0 -42
- package/dist/cache/expiring-data-cache.test.js.map +0 -1
- package/dist/cache/functions/functions.test.d.ts +0 -1
- package/dist/cache/functions/functions.test.js +0 -72
- package/dist/cache/functions/functions.test.js.map +0 -1
- package/dist/ecs/database/observe-select-deep.d.ts +0 -23
- package/dist/ecs/database/observe-select-deep.js +0 -15
- package/dist/ecs/database/observe-select-deep.js.map +0 -1
- package/dist/ecs/database/observe-select-deep.type-test.d.ts +0 -1
- package/dist/ecs/database/observe-select-deep.type-test.js +0 -111
- package/dist/ecs/database/observe-select-deep.type-test.js.map +0 -1
- package/dist/ecs/database/public/create-database-scheduler.test.d.ts +0 -1
- package/dist/ecs/database/public/create-database-scheduler.test.js +0 -67
- package/dist/ecs/database/public/create-database-scheduler.test.js.map +0 -1
- package/dist/ecs/database/public/create-database.type-test.d.ts +0 -1
- package/dist/ecs/database/public/create-database.type-test.js +0 -52
- package/dist/ecs/database/public/create-database.type-test.js.map +0 -1
- package/dist/ecs/entity.d.ts +0 -11
- package/dist/ecs/entity.js +0 -7
- package/dist/ecs/entity.js.map +0 -1
- package/dist/ecs/persistence-service/create-storage-persistence-service.test.d.ts +0 -1
- package/dist/ecs/persistence-service/create-storage-persistence-service.test.js +0 -52
- package/dist/ecs/persistence-service/create-storage-persistence-service.test.js.map +0 -1
- package/dist/functions/serialization/serialize-to-storage.test.d.ts +0 -1
- package/dist/functions/serialization/serialize-to-storage.test.js +0 -161
- package/dist/functions/serialization/serialize-to-storage.test.js.map +0 -1
- package/dist/math/aabb/face/aabb-face.test.d.ts +0 -1
- package/dist/math/aabb/face/aabb-face.test.js +0 -35
- package/dist/math/aabb/face/aabb-face.test.js.map +0 -1
- package/dist/math/aabb/face/functions.d.ts +0 -47
- package/dist/math/aabb/face/functions.js +0 -142
- package/dist/math/aabb/face/functions.js.map +0 -1
- package/dist/math/aabb/face/index.d.ts +0 -4
- package/dist/math/aabb/face/index.js +0 -3
- package/dist/math/aabb/face/index.js.map +0 -1
- package/dist/perftest/ecs-perf.d.ts +0 -49
- package/dist/perftest/ecs-perf.js +0 -210
- package/dist/perftest/ecs-perf.js.map +0 -1
- package/dist/service/agentic-service/link.d.ts +0 -15
- package/dist/service/agentic-service/link.js.map +0 -1
- package/dist/service/async-data-service/is-valid.type-test.d.ts +0 -1
- package/dist/service/async-data-service/is-valid.type-test.js.map +0 -1
- package/dist/service/dynamic-service/create-from-ecs.d.ts +0 -17
- package/dist/service/dynamic-service/create-from-ecs.js +0 -56
- package/dist/service/dynamic-service/create-from-ecs.js.map +0 -1
- package/dist/service/dynamic-service/create-from-ecs.test.d.ts +0 -1
- package/dist/service/dynamic-service/create-from-ecs.test.js +0 -636
- package/dist/service/dynamic-service/create-from-ecs.test.js.map +0 -1
- package/dist/service/dynamic-service/create.d.ts +0 -84
- package/dist/service/dynamic-service/create.js +0 -97
- package/dist/service/dynamic-service/create.js.map +0 -1
- package/dist/service/dynamic-service/create.test.d.ts +0 -1
- package/dist/service/dynamic-service/create.test.js +0 -688
- package/dist/service/dynamic-service/create.test.js.map +0 -1
- package/dist/service/dynamic-service/dynamic-service.d.ts +0 -29
- package/dist/service/dynamic-service/dynamic-service.js +0 -2
- package/dist/service/dynamic-service/dynamic-service.js.map +0 -1
- package/dist/service/dynamic-service/index.d.ts +0 -1
- package/dist/service/dynamic-service/index.js +0 -3
- package/dist/service/dynamic-service/index.js.map +0 -1
- package/dist/service/dynamic-service/public.d.ts +0 -2
- package/dist/service/dynamic-service/public.js +0 -3
- package/dist/service/dynamic-service/public.js.map +0 -1
- package/dist/service/dynamic-service/semantic-service.d.ts +0 -19
- package/dist/service/dynamic-service/semantic-service.js +0 -2
- package/dist/service/dynamic-service/semantic-service.js.map +0 -1
- package/dist/service/semantic-service/semantic-service.d.ts +0 -19
- package/dist/service/semantic-service/semantic-service.js +0 -2
- package/dist/service/semantic-service/semantic-service.js.map +0 -1
- /package/dist/{cache/blob-store.test.d.ts → ecs/database/database.reset.test.d.ts} +0 -0
- /package/dist/{cache/data-cache.test.d.ts → ecs/store/transaction-functions.type-test.d.ts} +0 -0
- /package/dist/{cache/expiring-data-cache.test.d.ts → service/ui-service/example.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -10,6 +10,15 @@ export class ApplicationElement<MainService extends Service> extends LitElement
|
|
|
10
10
|
@property({ type: Object, reflect: false })
|
|
11
11
|
service!: MainService;
|
|
12
12
|
|
|
13
|
+
// Defaults to `isService`: any Service in the ancestor chain is acceptable.
|
|
14
|
+
// Subclasses with a more specific MainService should override this with a
|
|
15
|
+
// narrower guard so unrelated services in the chain are skipped over. The
|
|
16
|
+
// cast is sound because callers that *don't* override are by definition
|
|
17
|
+
// accepting any Service as a valid MainService.
|
|
18
|
+
@property({ attribute: false })
|
|
19
|
+
typeGuard: (service: unknown) => service is MainService =
|
|
20
|
+
isService as (service: unknown) => service is MainService;
|
|
21
|
+
|
|
13
22
|
constructor() {
|
|
14
23
|
super();
|
|
15
24
|
attachDecorator(this, 'render', withHooks);
|
|
@@ -27,9 +36,10 @@ export class ApplicationElement<MainService extends Service> extends LitElement
|
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
protected findAncestorService(): MainService | void {
|
|
39
|
+
const { typeGuard } = this;
|
|
30
40
|
for (const element of iterateSelfAndAncestors(this)) {
|
|
31
41
|
const { service } = element as Partial<ApplicationElement<MainService>>;
|
|
32
|
-
if (
|
|
42
|
+
if (typeGuard(service)) {
|
|
33
43
|
return service;
|
|
34
44
|
}
|
|
35
45
|
}
|
|
@@ -4,12 +4,13 @@ import { LitElement } from 'lit';
|
|
|
4
4
|
import { property } from 'lit/decorators.js';
|
|
5
5
|
import { iterateSelfAndAncestors } from '../functions/index.js';
|
|
6
6
|
import { Database } from '@adobe/data/ecs';
|
|
7
|
+
import { UIService } from '@adobe/data/service';
|
|
7
8
|
import { attachDecorator, withHooks } from '../index.js';
|
|
8
9
|
|
|
9
10
|
export abstract class DatabaseElement<P extends Database.Plugin> extends LitElement {
|
|
10
11
|
|
|
11
12
|
@property({ type: Object, reflect: false })
|
|
12
|
-
service!: Database.Plugin.ToDatabase<P
|
|
13
|
+
service!: UIService.FromService<Database.Plugin.ToDatabase<P>>;
|
|
13
14
|
|
|
14
15
|
constructor() {
|
|
15
16
|
super();
|
|
@@ -21,7 +22,7 @@ export abstract class DatabaseElement<P extends Database.Plugin> extends LitElem
|
|
|
21
22
|
connectedCallback(): void {
|
|
22
23
|
if (!this.service) {
|
|
23
24
|
const service = this.findAncestorDatabase();
|
|
24
|
-
this.service = (service?.extend(this.plugin) ?? Database.create(this.plugin)) as unknown as Database.Plugin.ToDatabase<P
|
|
25
|
+
this.service = (service?.extend(this.plugin) ?? Database.create(this.plugin)) as unknown as UIService.FromService<Database.Plugin.ToDatabase<P>>;
|
|
25
26
|
}
|
|
26
27
|
super.connectedCallback();
|
|
27
28
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Compile-time tests proving that `DatabaseElement.service` exposes a
|
|
5
|
+
* UI-safe view of the underlying Database — every Database method that
|
|
6
|
+
* originally returned a non-`void`, non-`Observe` result has its return
|
|
7
|
+
* type rewritten to `void`, while Observe surfaces pass through unchanged.
|
|
8
|
+
*
|
|
9
|
+
* If `UIService.FromService` ever stops applying to the inferred
|
|
10
|
+
* `service` type, one or more of these assertions will fail at build time.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
14
|
+
|
|
15
|
+
import { Database } from "@adobe/data/ecs";
|
|
16
|
+
import { Observe } from "@adobe/data/observe";
|
|
17
|
+
import { UIService } from "@adobe/data/service";
|
|
18
|
+
import { DatabaseElement } from "./database-element.js";
|
|
19
|
+
|
|
20
|
+
const plugin = Database.Plugin.create({
|
|
21
|
+
resources: {
|
|
22
|
+
count: { default: 0 as number },
|
|
23
|
+
},
|
|
24
|
+
transactions: {
|
|
25
|
+
increment: (t) => {
|
|
26
|
+
t.resources.count = t.resources.count + 1;
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
class _CountElement extends DatabaseElement<typeof plugin> {
|
|
32
|
+
get plugin() {
|
|
33
|
+
return plugin;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type ServiceType = _CountElement["service"];
|
|
38
|
+
type RawDatabase = Database.Plugin.ToDatabase<typeof plugin>;
|
|
39
|
+
|
|
40
|
+
// 1. The exposed service type is the UIService-restricted view of the database.
|
|
41
|
+
type _CheckRestricted = Assert<Equal<ServiceType, UIService.FromService<RawDatabase>>>;
|
|
42
|
+
|
|
43
|
+
// 2. Observe surfaces survive the restriction.
|
|
44
|
+
type _CheckResourceObserveSurvives = Assert<
|
|
45
|
+
Equal<ServiceType["observe"]["resources"]["count"], Observe<number>>
|
|
46
|
+
>;
|
|
47
|
+
type _CheckEnvelopeObserveSurvives = Assert<
|
|
48
|
+
Equal<ServiceType["observe"]["envelopes"], RawDatabase["observe"]["envelopes"]>
|
|
49
|
+
>;
|
|
50
|
+
|
|
51
|
+
// 3. `apply` raw return type is non-void; restriction rewrites it to void.
|
|
52
|
+
type RawApply = RawDatabase["apply"];
|
|
53
|
+
type RestrictedApply = ServiceType["apply"];
|
|
54
|
+
type _CheckRawApplyIsNotVoid = Assert<NotEqual<ReturnType<RawApply>, void>>;
|
|
55
|
+
type _CheckRestrictedApplyIsVoid = Assert<Equal<ReturnType<RestrictedApply>, void>>;
|
|
56
|
+
type _CheckRestrictedApplyKeepsArgs = Assert<
|
|
57
|
+
Equal<Parameters<RestrictedApply>, Parameters<RawApply>>
|
|
58
|
+
>;
|
|
59
|
+
|
|
60
|
+
// 4. `toData` raw return is `unknown`; restriction rewrites to void.
|
|
61
|
+
type _CheckRawToDataNotVoid = Assert<NotEqual<ReturnType<RawDatabase["toData"]>, void>>;
|
|
62
|
+
type _CheckRestrictedToDataVoid = Assert<Equal<ReturnType<ServiceType["toData"]>, void>>;
|
|
63
|
+
|
|
64
|
+
// 5. Already-void functions pass through unchanged.
|
|
65
|
+
type _CheckResetStillVoid = Assert<Equal<ReturnType<ServiceType["reset"]>, void>>;
|
|
66
|
+
type _CheckCancelStillVoid = Assert<Equal<ReturnType<ServiceType["cancel"]>, void>>;
|
|
67
|
+
|
|
68
|
+
// 6. Transaction functions (nested under `transactions`) get their return
|
|
69
|
+
// rewritten to void by the same recursion. The plugin's `increment`
|
|
70
|
+
// happens to already be void, so we check the structure stays a function
|
|
71
|
+
// returning void after the restriction.
|
|
72
|
+
type _CheckIncrementReturnsVoid = Assert<
|
|
73
|
+
Equal<ReturnType<ServiceType["transactions"]["increment"]>, void>
|
|
74
|
+
>;
|
|
75
|
+
|
|
76
|
+
// ----------------------------------------------------------------------------
|
|
77
|
+
// Local helpers (kept inline so this file stays a self-contained type test).
|
|
78
|
+
// ----------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
type Assert<T extends true> = T;
|
|
81
|
+
type Equal<X, Y> =
|
|
82
|
+
(<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2)
|
|
83
|
+
? true
|
|
84
|
+
: false;
|
|
85
|
+
type NotEqual<X, Y> = Equal<X, Y> extends true ? false : true;
|
|
@@ -17,5 +17,7 @@ export * from "./with-hooks.js";
|
|
|
17
17
|
export * from "./attach-decorator.js";
|
|
18
18
|
export * from "./use-drag-transaction.js";
|
|
19
19
|
export * from "./use-drag-observe.js";
|
|
20
|
+
export * from "./use-drag-generator.js";
|
|
20
21
|
export * from "./use-draggable.js";
|
|
21
22
|
export * from "./use-updated.js";
|
|
23
|
+
export * from "./use-pointer-observe.js";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { Observe } from '@adobe/data/observe';
|
|
4
|
+
import { DragObserveProps, DragState, useDragObserve } from './use-drag-observe.js';
|
|
5
|
+
import { useEffect } from './use-effect.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* When a drag starts, invokes `callback` with an async generator that yields
|
|
9
|
+
* {@link DragState} values until the drag ends or is cancelled. Built on
|
|
10
|
+
* {@link useDragObserve}.
|
|
11
|
+
*
|
|
12
|
+
* The callback also receives the `PointerEvent` that triggered `'start'` —
|
|
13
|
+
* i.e. the `pointermove` whose cumulative distance first exceeded
|
|
14
|
+
* `minDragDistance`. Callers that need the initial `clientX` / `clientY`
|
|
15
|
+
* synchronously (before any transaction runs or any Lit re-render has flushed)
|
|
16
|
+
* can use this event without awaiting the generator.
|
|
17
|
+
*
|
|
18
|
+
* The generator pairs naturally with an ECS transaction that accepts an
|
|
19
|
+
* `AsyncArgsProvider`: feed each yielded `DragState` into a transient envelope
|
|
20
|
+
* by returning the desired transaction args from the generator, and the ECS
|
|
21
|
+
* (and any attached sync transport) replicates each intermediate state to
|
|
22
|
+
* peers. The transaction commits when the drag ends and is cancelled when the
|
|
23
|
+
* drag is cancelled.
|
|
24
|
+
*/
|
|
25
|
+
export function useDragGenerator(
|
|
26
|
+
props: DragObserveProps,
|
|
27
|
+
dependencies: unknown[],
|
|
28
|
+
callback: (drag: AsyncGenerator<DragState>, startEvent: PointerEvent) => void,
|
|
29
|
+
) {
|
|
30
|
+
const dragObserve = useDragObserve(props, dependencies);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
return dragObserve(value => {
|
|
34
|
+
if (value.type === 'start') {
|
|
35
|
+
const generator = Observe.toAsyncGenerator(
|
|
36
|
+
dragObserve,
|
|
37
|
+
v => v.type === 'end' || v.type === 'cancel',
|
|
38
|
+
);
|
|
39
|
+
callback(generator, value.event!);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}, dependencies);
|
|
43
|
+
}
|
|
@@ -11,6 +11,12 @@ export type DragObserveProps = Pick<
|
|
|
11
11
|
>;
|
|
12
12
|
export type DragStart = {
|
|
13
13
|
readonly type: 'start' | 'cancel';
|
|
14
|
+
/**
|
|
15
|
+
* The `PointerEvent` whose cumulative drag distance first exceeded
|
|
16
|
+
* `minDragDistance`. Only present when `type === 'start'`. Useful for
|
|
17
|
+
* callers that need the originating `clientX`/`clientY` synchronously.
|
|
18
|
+
*/
|
|
19
|
+
readonly event?: PointerEvent;
|
|
14
20
|
};
|
|
15
21
|
export type DragMove = {
|
|
16
22
|
readonly type: 'move';
|
|
@@ -36,8 +42,8 @@ export function useDragObserve(props: DragObserveProps, dependencies: unknown[])
|
|
|
36
42
|
useDraggable(element,
|
|
37
43
|
{
|
|
38
44
|
...props,
|
|
39
|
-
onDragStart:
|
|
40
|
-
setDragState({ type: 'start' });
|
|
45
|
+
onDragStart: e => {
|
|
46
|
+
setDragState({ type: 'start', event: e });
|
|
41
47
|
},
|
|
42
48
|
onDrag: (_e, position, delta) => {
|
|
43
49
|
setDragState({
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { Observe } from "@adobe/data/observe";
|
|
4
|
+
import { Vec2 } from "@adobe/data/math";
|
|
5
|
+
import { useElement } from "./use-element.js";
|
|
6
|
+
import { useEffect } from "./use-effect.js";
|
|
7
|
+
import { useMemo } from "./use-memo.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Returns an `Observe<Vec2>` that fires on every `pointermove` over the host
|
|
11
|
+
* element. Coordinates are relative to the element's bounding box, in pixels.
|
|
12
|
+
*
|
|
13
|
+
* This is the primitive for **continuous** pointer tracking — presence
|
|
14
|
+
* indicators, live cursors, heat-maps, etc. It is intentionally simpler than
|
|
15
|
+
* `useDragObserve`: it emits without a drag start/end lifecycle.
|
|
16
|
+
*
|
|
17
|
+
* ### Driving a never-ending presence transaction
|
|
18
|
+
*
|
|
19
|
+
* Pair with `Observe.toAsyncGenerator` to feed a never-ending async-generator
|
|
20
|
+
* transaction. Each `yield` becomes a transient envelope that the sync
|
|
21
|
+
* service forwards as `kind: "transient"`. The reconciler's `(userId, id)`
|
|
22
|
+
* compound key replaces the previous sample, so each peer has at most one
|
|
23
|
+
* outstanding cursor sample at any time.
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* const pointerPos = usePointerObserve([]);
|
|
27
|
+
*
|
|
28
|
+
* useEffect(() => {
|
|
29
|
+
* const positions = Observe.toAsyncGenerator(pointerPos, () => false);
|
|
30
|
+
*
|
|
31
|
+
* async function* presenceArgs() {
|
|
32
|
+
* for await (const [px, py] of positions) {
|
|
33
|
+
* const { width, height } = element.getBoundingClientRect();
|
|
34
|
+
* if (!width || !height) continue;
|
|
35
|
+
* yield { userId, x: px / width, y: py / height };
|
|
36
|
+
* }
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* // The wrapper returns a Promise when invoked with a generator factory.
|
|
40
|
+
* // It rejects when we `.throw()` on dispose; swallow that — we threw
|
|
41
|
+
* // precisely so the wrapper would cancel the in-flight transient
|
|
42
|
+
* // instead of promoting the last sample to a commit.
|
|
43
|
+
* db.transactions.movePresence(presenceArgs).catch(() => undefined);
|
|
44
|
+
*
|
|
45
|
+
* return () => {
|
|
46
|
+
* void positions.throw(new Error("disposed")).catch(() => undefined);
|
|
47
|
+
* };
|
|
48
|
+
* }, []);
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* ### Why `() => false` as the `finished` predicate?
|
|
52
|
+
*
|
|
53
|
+
* `Observe.toAsyncGenerator` stops when `finished(value)` returns `true`.
|
|
54
|
+
* Passing `() => false` makes the generator run until `gen.throw()` (or
|
|
55
|
+
* `gen.return()`) is called from the effect cleanup — the correct lifetime
|
|
56
|
+
* for a presence stream.
|
|
57
|
+
*/
|
|
58
|
+
export function usePointerObserve(dependencies: unknown[]): Observe<Vec2> {
|
|
59
|
+
// useMemo ensures the same [observe, notify] pair is reused across renders
|
|
60
|
+
// as long as dependencies are stable — critical so subscribers registered
|
|
61
|
+
// in a sibling useEffect don't miss events fired between renders.
|
|
62
|
+
const [pos, setPos] = useMemo(
|
|
63
|
+
() => Observe.createEvent<Vec2>(),
|
|
64
|
+
dependencies,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const element = useElement();
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
const handler = (e: PointerEvent) => {
|
|
71
|
+
const rect = element.getBoundingClientRect();
|
|
72
|
+
setPos([e.clientX - rect.left, e.clientY - rect.top]);
|
|
73
|
+
};
|
|
74
|
+
element.addEventListener("pointermove", handler);
|
|
75
|
+
return () => element.removeEventListener("pointermove", handler);
|
|
76
|
+
}, [element, setPos]);
|
|
77
|
+
|
|
78
|
+
return pos;
|
|
79
|
+
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "data-lit-tictactoe",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.58",
|
|
4
4
|
"description": "Tic-Tac-Toe sample - Lit web components with @adobe/data-lit and AgenticService",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": true,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts"
|
|
9
|
+
},
|
|
7
10
|
"scripts": {
|
|
8
11
|
"build": "vite build",
|
|
9
12
|
"dev": "vite",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { customElement } from "lit/decorators.js";
|
|
4
|
+
import { TictactoeElement } from "../../tictactoe-element.js";
|
|
5
|
+
import { styles } from "./tictactoe-app.css.js";
|
|
6
|
+
import * as presentation from "./tictactoe-app-presentation.js";
|
|
7
|
+
|
|
8
|
+
const tagName = "tictactoe-app";
|
|
9
|
+
|
|
10
|
+
declare global {
|
|
11
|
+
interface HTMLElementTagNameMap {
|
|
12
|
+
[tagName]: TictactoeAppElement;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@customElement(tagName)
|
|
17
|
+
export class TictactoeAppElement extends TictactoeElement {
|
|
18
|
+
static styles = styles;
|
|
19
|
+
|
|
20
|
+
render() {
|
|
21
|
+
return presentation.render();
|
|
22
|
+
}
|
|
23
|
+
}
|
package/references/data-lit-tictactoe/src/elements/tictactoe-app/tictactoe-app-presentation.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { html } from "lit";
|
|
4
|
+
import { TictactoeBoard } from "../tictactoe-board/tictactoe-board.js";
|
|
5
|
+
import { TictactoeHud } from "../tictactoe-hud/tictactoe-hud.js";
|
|
6
|
+
|
|
7
|
+
export function render() {
|
|
8
|
+
return html`
|
|
9
|
+
${TictactoeBoard()}
|
|
10
|
+
${TictactoeHud()}
|
|
11
|
+
`;
|
|
12
|
+
}
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { styles } from "./tictactoe-app.css.js";
|
|
7
|
-
import "../tictactoe-board/tictactoe-board.js";
|
|
8
|
-
import "../tictactoe-hud/tictactoe-hud.js";
|
|
3
|
+
import { html, type TemplateResult } from "lit";
|
|
4
|
+
import type { Database } from "@adobe/data/ecs";
|
|
5
|
+
import type { tictactoePlugin } from "../../state/tictactoe-plugin.js";
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
type TictactoeService = Database.Plugin.ToDatabase<typeof tictactoePlugin>;
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
9
|
+
/**
|
|
10
|
+
* Generic over `S` so callers may pass a database built from any plugin
|
|
11
|
+
* that extends `tictactoePlugin` (for example one that adds AI agents
|
|
12
|
+
* or peer presence). The element class itself is typed on the minimal
|
|
13
|
+
* `tictactoePlugin` surface and ignores the extra capabilities.
|
|
14
|
+
*/
|
|
15
|
+
export const Tictactoe = <S extends TictactoeService>(args: { service: S }): TemplateResult => {
|
|
16
|
+
void import("./tictactoe-app-element.js");
|
|
17
|
+
return html`<tictactoe-app .service=${args.service}></tictactoe-app>`;
|
|
18
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { customElement } from "lit/decorators.js";
|
|
4
|
+
import { TictactoeElement } from "../../tictactoe-element.js";
|
|
5
|
+
import { styles } from "./tictactoe-board.css.js";
|
|
6
|
+
import * as presentation from "./tictactoe-board-presentation.js";
|
|
7
|
+
|
|
8
|
+
const tagName = "tictactoe-board";
|
|
9
|
+
|
|
10
|
+
declare global {
|
|
11
|
+
interface HTMLElementTagNameMap {
|
|
12
|
+
[tagName]: TictactoeBoardElement;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@customElement(tagName)
|
|
17
|
+
export class TictactoeBoardElement extends TictactoeElement {
|
|
18
|
+
static styles = styles;
|
|
19
|
+
|
|
20
|
+
render() {
|
|
21
|
+
return presentation.render();
|
|
22
|
+
}
|
|
23
|
+
}
|
package/references/data-lit-tictactoe/src/elements/tictactoe-board/tictactoe-board-presentation.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
2
|
|
|
3
3
|
import { html } from "lit";
|
|
4
|
+
import { TictactoeCell } from "../tictactoe-cell/tictactoe-cell.js";
|
|
4
5
|
|
|
5
6
|
export function render() {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
</div>
|
|
12
|
-
`;
|
|
7
|
+
return html`
|
|
8
|
+
<div class="board">
|
|
9
|
+
${[0, 1, 2, 3, 4, 5, 6, 7, 8].map((index) => TictactoeCell({ index }))}
|
|
10
|
+
</div>
|
|
11
|
+
`;
|
|
13
12
|
}
|
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { TictactoeElement } from "../../tictactoe-element.js";
|
|
5
|
-
import { styles } from "./tictactoe-board.css.js";
|
|
6
|
-
import * as presentation from "./tictactoe-board-presentation.js";
|
|
7
|
-
import "../tictactoe-cell/tictactoe-cell.js";
|
|
3
|
+
import { html, type TemplateResult } from "lit";
|
|
8
4
|
|
|
9
|
-
export const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
static styles = styles;
|
|
14
|
-
|
|
15
|
-
render() {
|
|
16
|
-
return presentation.render();
|
|
17
|
-
}
|
|
18
|
-
}
|
|
5
|
+
export const TictactoeBoard = (): TemplateResult => {
|
|
6
|
+
void import("./tictactoe-board-element.js");
|
|
7
|
+
return html`<tictactoe-board></tictactoe-board>`;
|
|
8
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { customElement, property } from "lit/decorators.js";
|
|
4
|
+
import { useObservableValues } from "@adobe/data-lit";
|
|
5
|
+
import { BoardState } from "../../types/board-state/board-state.js";
|
|
6
|
+
import { TictactoeElement } from "../../tictactoe-element.js";
|
|
7
|
+
import { styles } from "./tictactoe-cell.css.js";
|
|
8
|
+
import * as presentation from "./tictactoe-cell-presentation.js";
|
|
9
|
+
|
|
10
|
+
const tagName = "tictactoe-cell";
|
|
11
|
+
|
|
12
|
+
declare global {
|
|
13
|
+
interface HTMLElementTagNameMap {
|
|
14
|
+
[tagName]: TictactoeCellElement;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@customElement(tagName)
|
|
19
|
+
export class TictactoeCellElement extends TictactoeElement {
|
|
20
|
+
static styles = styles;
|
|
21
|
+
|
|
22
|
+
@property({ type: Number })
|
|
23
|
+
declare index: number;
|
|
24
|
+
|
|
25
|
+
render() {
|
|
26
|
+
const values = useObservableValues(
|
|
27
|
+
() => ({ board: this.service.observe.resources.board }),
|
|
28
|
+
[],
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const board = values?.board ?? " ";
|
|
32
|
+
const cell = BoardState.getCell(board, this.index);
|
|
33
|
+
const isWinning = BoardState.isCellWinning(board, this.index);
|
|
34
|
+
const isPlayable = BoardState.isCellPlayable(board, this.index);
|
|
35
|
+
|
|
36
|
+
return presentation.render({
|
|
37
|
+
cell,
|
|
38
|
+
isWinning,
|
|
39
|
+
isPlayable,
|
|
40
|
+
playMove: () => {
|
|
41
|
+
this.service.transactions.playMove({ index: this.index });
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
package/references/data-lit-tictactoe/src/elements/tictactoe-cell/tictactoe-cell-presentation.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
2
|
|
|
3
3
|
import { html } from "lit";
|
|
4
|
+
import { PlayerMark } from "../../types/player-mark/player-mark.js";
|
|
4
5
|
|
|
5
6
|
export function render(args: {
|
|
6
7
|
cell: string;
|
|
@@ -9,7 +10,7 @@ export function render(args: {
|
|
|
9
10
|
playMove: () => void;
|
|
10
11
|
}) {
|
|
11
12
|
const { cell, isWinning, isPlayable, playMove } = args;
|
|
12
|
-
const hasMark = cell
|
|
13
|
+
const hasMark = PlayerMark.is(cell);
|
|
13
14
|
|
|
14
15
|
return html`
|
|
15
16
|
<div
|
|
@@ -1,41 +1,8 @@
|
|
|
1
1
|
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { useObservableValues } from "@adobe/data-lit";
|
|
5
|
-
import { BoardState } from "../../types/board-state/board-state.js";
|
|
6
|
-
import { TictactoeElement } from "../../tictactoe-element.js";
|
|
7
|
-
import { styles } from "./tictactoe-cell.css.js";
|
|
8
|
-
import * as presentation from "./tictactoe-cell-presentation.js";
|
|
3
|
+
import { html, type TemplateResult } from "lit";
|
|
9
4
|
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
static styles = styles;
|
|
15
|
-
|
|
16
|
-
@property({ type: Number })
|
|
17
|
-
declare index: number;
|
|
18
|
-
|
|
19
|
-
render() {
|
|
20
|
-
const values = useObservableValues(
|
|
21
|
-
() => ({
|
|
22
|
-
board: this.service.observe.resources.board,
|
|
23
|
-
}),
|
|
24
|
-
[],
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const board = values?.board ?? " ";
|
|
28
|
-
const cell = BoardState.getCell(board, this.index);
|
|
29
|
-
const isWinning = BoardState.isCellWinning(board, this.index);
|
|
30
|
-
const isPlayable = BoardState.isCellPlayable(board, this.index);
|
|
31
|
-
|
|
32
|
-
return presentation.render({
|
|
33
|
-
cell,
|
|
34
|
-
isWinning,
|
|
35
|
-
isPlayable,
|
|
36
|
-
playMove: () => {
|
|
37
|
-
this.service.transactions.playMove({ index: this.index });
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
}
|
|
5
|
+
export const TictactoeCell = (args: { index: number }): TemplateResult => {
|
|
6
|
+
void import("./tictactoe-cell-element.js");
|
|
7
|
+
return html`<tictactoe-cell .index=${args.index}></tictactoe-cell>`;
|
|
8
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { customElement } from "lit/decorators.js";
|
|
4
|
+
import { useObservableValues } from "@adobe/data-lit";
|
|
5
|
+
import { BoardState } from "../../types/board-state/board-state.js";
|
|
6
|
+
import { PlayerMark } from "../../types/player-mark/player-mark.js";
|
|
7
|
+
import { TictactoeElement } from "../../tictactoe-element.js";
|
|
8
|
+
import { styles } from "./tictactoe-hud.css.js";
|
|
9
|
+
import * as presentation from "./tictactoe-hud-presentation.js";
|
|
10
|
+
|
|
11
|
+
const tagName = "tictactoe-hud";
|
|
12
|
+
|
|
13
|
+
declare global {
|
|
14
|
+
interface HTMLElementTagNameMap {
|
|
15
|
+
[tagName]: TictactoeHudElement;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@customElement(tagName)
|
|
20
|
+
export class TictactoeHudElement extends TictactoeElement {
|
|
21
|
+
static styles = styles;
|
|
22
|
+
|
|
23
|
+
render() {
|
|
24
|
+
const values = useObservableValues(
|
|
25
|
+
() => ({
|
|
26
|
+
board: this.service.observe.resources.board,
|
|
27
|
+
firstPlayer: this.service.observe.resources.firstPlayer,
|
|
28
|
+
xWins: this.service.observe.resources.xWins,
|
|
29
|
+
oWins: this.service.observe.resources.oWins,
|
|
30
|
+
draws: this.service.observe.resources.draws,
|
|
31
|
+
}),
|
|
32
|
+
[],
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const board = values?.board ?? " ";
|
|
36
|
+
const firstPlayer = values?.firstPlayer ?? PlayerMark.values[0];
|
|
37
|
+
|
|
38
|
+
const currentPlayer = BoardState.currentPlayer(board, firstPlayer);
|
|
39
|
+
const status = BoardState.deriveStatus(board);
|
|
40
|
+
const winner = BoardState.getWinner(board);
|
|
41
|
+
|
|
42
|
+
const statusText =
|
|
43
|
+
status === "won" && winner !== null
|
|
44
|
+
? `${winner} wins!`
|
|
45
|
+
: status === "draw"
|
|
46
|
+
? "Draw!"
|
|
47
|
+
: `${currentPlayer}'s turn`;
|
|
48
|
+
|
|
49
|
+
return presentation.render({
|
|
50
|
+
statusText,
|
|
51
|
+
xWins: values?.xWins ?? 0,
|
|
52
|
+
oWins: values?.oWins ?? 0,
|
|
53
|
+
draws: values?.draws ?? 0,
|
|
54
|
+
restartGame: () => this.service.transactions.restartGame(),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|