@xmachines/play-dom 1.0.0-beta.16
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/PlayRenderer.d.ts +58 -0
- package/dist/PlayRenderer.d.ts.map +1 -0
- package/dist/PlayRenderer.js +91 -0
- package/dist/PlayRenderer.js.map +1 -0
- package/dist/connect-renderer.d.ts +44 -0
- package/dist/connect-renderer.d.ts.map +1 -0
- package/dist/connect-renderer.js +54 -0
- package/dist/connect-renderer.js.map +1 -0
- package/dist/dom-renderer.d.ts +23 -0
- package/dist/dom-renderer.d.ts.map +1 -0
- package/dist/dom-renderer.js +103 -0
- package/dist/dom-renderer.js.map +1 -0
- package/dist/errors.d.ts +46 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +52 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/resolve-state.d.ts +12 -0
- package/dist/resolve-state.d.ts.map +1 -0
- package/dist/resolve-state.js +41 -0
- package/dist/resolve-state.js.map +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlayRenderer.ts — XMachines wrapper around the pure dom-renderer layer.
|
|
3
|
+
*
|
|
4
|
+
* Bridges actor.currentView (TC39 Signal) to the inner DOM renderer using watchSignal.
|
|
5
|
+
* Exposes connect() / disconnect() methods following the same pattern as
|
|
6
|
+
* play-react, play-vue, and play-solid PlayRenderer classes.
|
|
7
|
+
*
|
|
8
|
+
* State store: uses external `store` option if provided (controlled mode); otherwise
|
|
9
|
+
* creates a fresh @xstate/store atom per view transition seeded from spec.state.
|
|
10
|
+
*/
|
|
11
|
+
import type { AbstractActor, Viewable } from "@xmachines/play-actor";
|
|
12
|
+
import type { AnyActorLogic } from "xstate";
|
|
13
|
+
import type { DomRegistry, PlayDomOptions } from "./types.js";
|
|
14
|
+
/**
|
|
15
|
+
* PlayRenderer connects an actor's currentView signal to the DOM renderer.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const renderer = new PlayRenderer(container, actor, registry, { actions: { ... } });
|
|
20
|
+
* renderer.connect();
|
|
21
|
+
*
|
|
22
|
+
* // Controlled mode — bring your own store:
|
|
23
|
+
* import { createAtom } from "@xstate/store";
|
|
24
|
+
* import { xstateStoreStateStore } from "@json-render/xstate";
|
|
25
|
+
* const store = xstateStoreStateStore({ atom: createAtom({ username: "" }) });
|
|
26
|
+
* const renderer = new PlayRenderer(container, actor, registry, { store, actions: { ... } });
|
|
27
|
+
*
|
|
28
|
+
* // Later:
|
|
29
|
+
* renderer.disconnect();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare class PlayRenderer {
|
|
33
|
+
private readonly container;
|
|
34
|
+
private readonly actor;
|
|
35
|
+
private readonly registry;
|
|
36
|
+
private readonly options;
|
|
37
|
+
private _unwatch;
|
|
38
|
+
/**
|
|
39
|
+
* @param container - The `HTMLElement` to render into. Cleared and repopulated on every view transition.
|
|
40
|
+
* @param actor - Actor instance providing the `currentView` signal (must implement `Viewable`).
|
|
41
|
+
* @param registry - Map of component type names to `DomComponentRenderer` functions.
|
|
42
|
+
* @param options - Optional configuration: `actions` map (action name → XState event type) and
|
|
43
|
+
* optional external `store` (controlled mode — when omitted, a fresh `@xstate/store` atom is
|
|
44
|
+
* created per view transition seeded from `spec.state`).
|
|
45
|
+
*/
|
|
46
|
+
constructor(container: HTMLElement, actor: AbstractActor<AnyActorLogic> & Viewable, registry: DomRegistry, options?: PlayDomOptions);
|
|
47
|
+
/**
|
|
48
|
+
* Start watching actor.currentView and render to container.
|
|
49
|
+
* Renders the initial view synchronously, then subscribes to signal changes.
|
|
50
|
+
*/
|
|
51
|
+
connect(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Stop watching and clear the container.
|
|
54
|
+
*/
|
|
55
|
+
disconnect(): void;
|
|
56
|
+
private _render;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=PlayRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlayRenderer.d.ts","sourceRoot":"","sources":["../src/PlayRenderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAgB,MAAM,uBAAuB,CAAC;AACnF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE9D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,YAAY;IAYvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAdzB,OAAO,CAAC,QAAQ,CAA6B;IAE7C;;;;;;;OAOG;gBAEe,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ,EAC9C,QAAQ,EAAE,WAAW,EACrB,OAAO,GAAE,cAAmB;IAG9C;;;OAGG;IACH,OAAO,IAAI,IAAI;IAKf;;OAEG;IACH,UAAU,IAAI,IAAI;IAMlB,OAAO,CAAC,OAAO;CA8Bf"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlayRenderer.ts — XMachines wrapper around the pure dom-renderer layer.
|
|
3
|
+
*
|
|
4
|
+
* Bridges actor.currentView (TC39 Signal) to the inner DOM renderer using watchSignal.
|
|
5
|
+
* Exposes connect() / disconnect() methods following the same pattern as
|
|
6
|
+
* play-react, play-vue, and play-solid PlayRenderer classes.
|
|
7
|
+
*
|
|
8
|
+
* State store: uses external `store` option if provided (controlled mode); otherwise
|
|
9
|
+
* creates a fresh @xstate/store atom per view transition seeded from spec.state.
|
|
10
|
+
*/
|
|
11
|
+
import { watchSignal } from "@xmachines/play-signals";
|
|
12
|
+
import { createAtom } from "@xstate/store";
|
|
13
|
+
import { xstateStoreStateStore } from "@json-render/xstate";
|
|
14
|
+
import { renderSpec } from "./dom-renderer.js";
|
|
15
|
+
/**
|
|
16
|
+
* PlayRenderer connects an actor's currentView signal to the DOM renderer.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const renderer = new PlayRenderer(container, actor, registry, { actions: { ... } });
|
|
21
|
+
* renderer.connect();
|
|
22
|
+
*
|
|
23
|
+
* // Controlled mode — bring your own store:
|
|
24
|
+
* import { createAtom } from "@xstate/store";
|
|
25
|
+
* import { xstateStoreStateStore } from "@json-render/xstate";
|
|
26
|
+
* const store = xstateStoreStateStore({ atom: createAtom({ username: "" }) });
|
|
27
|
+
* const renderer = new PlayRenderer(container, actor, registry, { store, actions: { ... } });
|
|
28
|
+
*
|
|
29
|
+
* // Later:
|
|
30
|
+
* renderer.disconnect();
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class PlayRenderer {
|
|
34
|
+
container;
|
|
35
|
+
actor;
|
|
36
|
+
registry;
|
|
37
|
+
options;
|
|
38
|
+
_unwatch = null;
|
|
39
|
+
/**
|
|
40
|
+
* @param container - The `HTMLElement` to render into. Cleared and repopulated on every view transition.
|
|
41
|
+
* @param actor - Actor instance providing the `currentView` signal (must implement `Viewable`).
|
|
42
|
+
* @param registry - Map of component type names to `DomComponentRenderer` functions.
|
|
43
|
+
* @param options - Optional configuration: `actions` map (action name → XState event type) and
|
|
44
|
+
* optional external `store` (controlled mode — when omitted, a fresh `@xstate/store` atom is
|
|
45
|
+
* created per view transition seeded from `spec.state`).
|
|
46
|
+
*/
|
|
47
|
+
constructor(container, actor, registry, options = {}) {
|
|
48
|
+
this.container = container;
|
|
49
|
+
this.actor = actor;
|
|
50
|
+
this.registry = registry;
|
|
51
|
+
this.options = options;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Start watching actor.currentView and render to container.
|
|
55
|
+
* Renders the initial view synchronously, then subscribes to signal changes.
|
|
56
|
+
*/
|
|
57
|
+
connect() {
|
|
58
|
+
this._render(this.actor.currentView.get());
|
|
59
|
+
this._unwatch = watchSignal(this.actor.currentView, (view) => this._render(view));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Stop watching and clear the container.
|
|
63
|
+
*/
|
|
64
|
+
disconnect() {
|
|
65
|
+
this._unwatch?.();
|
|
66
|
+
this._unwatch = null;
|
|
67
|
+
this.container.replaceChildren();
|
|
68
|
+
}
|
|
69
|
+
_render(view) {
|
|
70
|
+
this.container.replaceChildren();
|
|
71
|
+
if (!view)
|
|
72
|
+
return;
|
|
73
|
+
// Resolve the store: external (controlled) or fresh per-view atom
|
|
74
|
+
const store = this.options.store
|
|
75
|
+
? this.options.store
|
|
76
|
+
: xstateStoreStateStore({
|
|
77
|
+
atom: createAtom(view.spec.state ?? {}),
|
|
78
|
+
});
|
|
79
|
+
const send = this.actor.send.bind(this.actor);
|
|
80
|
+
const actorActions = this.options.actions ?? {};
|
|
81
|
+
const rerender = () => {
|
|
82
|
+
this.container.replaceChildren();
|
|
83
|
+
const node = renderSpec(view.spec, store, this.registry, send, this.actor, actorActions);
|
|
84
|
+
if (node)
|
|
85
|
+
this.container.appendChild(node);
|
|
86
|
+
};
|
|
87
|
+
store.subscribe(rerender);
|
|
88
|
+
rerender();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=PlayRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlayRenderer.js","sourceRoot":"","sources":["../src/PlayRenderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG/C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,YAAY;IAYN;IACA;IACA;IACA;IAdV,QAAQ,GAAwB,IAAI,CAAC;IAE7C;;;;;;;OAOG;IACH,YACkB,SAAsB,EACtB,KAA8C,EAC9C,QAAqB,EACrB,UAA0B,EAAE;QAH5B,cAAS,GAAT,SAAS,CAAa;QACtB,UAAK,GAAL,KAAK,CAAyC;QAC9C,aAAQ,GAAR,QAAQ,CAAa;QACrB,YAAO,GAAP,OAAO,CAAqB;IAC3C,CAAC;IAEJ;;;OAGG;IACH,OAAO;QACN,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACnF,CAAC;IAED;;OAEG;IACH,UAAU;QACT,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;QAClB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,CAAC;IAClC,CAAC;IAEO,OAAO,CAAC,IAAyB;QACxC,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,CAAC;QACjC,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,kEAAkE;QAClE,MAAM,KAAK,GAAe,IAAI,CAAC,OAAO,CAAC,KAAK;YAC3C,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK;YACpB,CAAC,CAAC,qBAAqB,CAAC;gBACtB,IAAI,EAAE,UAAU,CAAE,IAAI,CAAC,IAAI,CAAC,KAAiC,IAAI,EAAE,CAAC;aACpE,CAAC,CAAC;QAEL,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;QAEhD,MAAM,QAAQ,GAAG,GAAS,EAAE;YAC3B,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,UAAU,CACtB,IAAI,CAAC,IAAI,EACT,KAAK,EACL,IAAI,CAAC,QAAQ,EACb,IAAI,EACJ,IAAI,CAAC,KAAK,EACV,YAAY,CACZ,CAAC;YACF,IAAI,IAAI;gBAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC,CAAC;QAEF,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC1B,QAAQ,EAAE,CAAC;IACZ,CAAC;CACD"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connect-renderer.ts — Backward compatibility wrapper around PlayRenderer.
|
|
3
|
+
*
|
|
4
|
+
* Provides the original `connectRenderer()` function API for consumers that
|
|
5
|
+
* have not yet migrated to the PlayRenderer class-based API.
|
|
6
|
+
*
|
|
7
|
+
* The implementation delegates entirely to PlayRenderer.
|
|
8
|
+
*/
|
|
9
|
+
import type { ConnectRendererOptions } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Connect a signal-driven DOM renderer to an actor's currentView signal.
|
|
12
|
+
*
|
|
13
|
+
* Subscribes to `actor.currentView` via TC39 Signals and renders `DomComponentRenderer`
|
|
14
|
+
* functions into the provided `container` element on every state change.
|
|
15
|
+
*
|
|
16
|
+
* - Renders the initial view immediately (synchronous)
|
|
17
|
+
* - Re-renders when `actor.currentView` signal changes (via microtask)
|
|
18
|
+
* - Clears container when view is `null` (optionally appends fallback)
|
|
19
|
+
*
|
|
20
|
+
* @param options - ConnectRendererOptions
|
|
21
|
+
* @returns A cleanup function that stops all DOM mutations
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { connectRenderer, type DomComponentRenderer } from "@xmachines/play-dom";
|
|
26
|
+
*
|
|
27
|
+
* const unwatch = connectRenderer({
|
|
28
|
+
* actor,
|
|
29
|
+
* registry: {
|
|
30
|
+
* Home: (element, ctx) => {
|
|
31
|
+
* const el = document.createElement("div");
|
|
32
|
+
* el.textContent = element.props.title as string;
|
|
33
|
+
* return el;
|
|
34
|
+
* },
|
|
35
|
+
* },
|
|
36
|
+
* container: document.getElementById("app")!,
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* // Later: stop watching
|
|
40
|
+
* unwatch();
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function connectRenderer(options: ConnectRendererOptions): () => void;
|
|
44
|
+
//# sourceMappingURL=connect-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect-renderer.d.ts","sourceRoot":"","sources":["../src/connect-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,IAAI,CAiB3E"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connect-renderer.ts — Backward compatibility wrapper around PlayRenderer.
|
|
3
|
+
*
|
|
4
|
+
* Provides the original `connectRenderer()` function API for consumers that
|
|
5
|
+
* have not yet migrated to the PlayRenderer class-based API.
|
|
6
|
+
*
|
|
7
|
+
* The implementation delegates entirely to PlayRenderer.
|
|
8
|
+
*/
|
|
9
|
+
import { PlayRenderer } from "./PlayRenderer.js";
|
|
10
|
+
/**
|
|
11
|
+
* Connect a signal-driven DOM renderer to an actor's currentView signal.
|
|
12
|
+
*
|
|
13
|
+
* Subscribes to `actor.currentView` via TC39 Signals and renders `DomComponentRenderer`
|
|
14
|
+
* functions into the provided `container` element on every state change.
|
|
15
|
+
*
|
|
16
|
+
* - Renders the initial view immediately (synchronous)
|
|
17
|
+
* - Re-renders when `actor.currentView` signal changes (via microtask)
|
|
18
|
+
* - Clears container when view is `null` (optionally appends fallback)
|
|
19
|
+
*
|
|
20
|
+
* @param options - ConnectRendererOptions
|
|
21
|
+
* @returns A cleanup function that stops all DOM mutations
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { connectRenderer, type DomComponentRenderer } from "@xmachines/play-dom";
|
|
26
|
+
*
|
|
27
|
+
* const unwatch = connectRenderer({
|
|
28
|
+
* actor,
|
|
29
|
+
* registry: {
|
|
30
|
+
* Home: (element, ctx) => {
|
|
31
|
+
* const el = document.createElement("div");
|
|
32
|
+
* el.textContent = element.props.title as string;
|
|
33
|
+
* return el;
|
|
34
|
+
* },
|
|
35
|
+
* },
|
|
36
|
+
* container: document.getElementById("app")!,
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* // Later: stop watching
|
|
40
|
+
* unwatch();
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export function connectRenderer(options) {
|
|
44
|
+
const playDomOptions = options.actions ? { actions: options.actions } : {};
|
|
45
|
+
const renderer = new PlayRenderer(options.container, options.actor, options.registry, playDomOptions);
|
|
46
|
+
renderer.connect();
|
|
47
|
+
// Handle fallback for null view (backward compat)
|
|
48
|
+
// If container is empty after initial render, append fallback
|
|
49
|
+
if (options.fallback && options.container.children.length === 0) {
|
|
50
|
+
options.container.appendChild(options.fallback);
|
|
51
|
+
}
|
|
52
|
+
return () => renderer.disconnect();
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=connect-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect-renderer.js","sourceRoot":"","sources":["../src/connect-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,eAAe,CAAC,OAA+B;IAC9D,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3E,MAAM,QAAQ,GAAG,IAAI,YAAY,CAChC,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,KAAK,EACb,OAAO,CAAC,QAAQ,EAChB,cAAc,CACd,CAAC;IACF,QAAQ,CAAC,OAAO,EAAE,CAAC;IAEnB,kDAAkD;IAClD,8DAA8D;IAC9D,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjE,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dom-renderer.ts — Pure json-render-core compatible DOM renderer.
|
|
3
|
+
*
|
|
4
|
+
* Zero XMachines dependencies. Takes a Spec + StateStore + DomRegistry
|
|
5
|
+
* and produces DOM nodes.
|
|
6
|
+
*
|
|
7
|
+
* This layer is upstreamable to @json-render/dom as a PR to vercel-labs/json-render.
|
|
8
|
+
*/
|
|
9
|
+
import type { Spec, StateStore } from "@json-render/core";
|
|
10
|
+
import type { DomRegistry, DomRenderContext } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Render a Spec tree into DOM nodes using the provided DomRegistry.
|
|
13
|
+
*
|
|
14
|
+
* @param spec - The json-render Spec describing the UI tree
|
|
15
|
+
* @param store - StateStore holding the current state values
|
|
16
|
+
* @param registry - Map of element type names to DomComponentRenderer functions
|
|
17
|
+
* @param send - Function to dispatch XState events from UI interactions
|
|
18
|
+
* @param actor - Raw actor — available to DOM components for state reading, complex events
|
|
19
|
+
* @param actorActions - Map of json-render action names → XState event types
|
|
20
|
+
* @returns The root DOM node, or null if root element is invisible or unknown
|
|
21
|
+
*/
|
|
22
|
+
export declare function renderSpec(spec: Spec, store: StateStore, registry: DomRegistry, send: DomRenderContext["send"], actor: DomRenderContext["actor"], actorActions: Record<string, string>): Node | null;
|
|
23
|
+
//# sourceMappingURL=dom-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-renderer.d.ts","sourceRoot":"","sources":["../src/dom-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAa,MAAM,mBAAmB,CAAC;AAErE,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEhE;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CACzB,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,WAAW,EACrB,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAC9B,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAChC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAClC,IAAI,GAAG,IAAI,CAsDb"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dom-renderer.ts — Pure json-render-core compatible DOM renderer.
|
|
3
|
+
*
|
|
4
|
+
* Zero XMachines dependencies. Takes a Spec + StateStore + DomRegistry
|
|
5
|
+
* and produces DOM nodes.
|
|
6
|
+
*
|
|
7
|
+
* This layer is upstreamable to @json-render/dom as a PR to vercel-labs/json-render.
|
|
8
|
+
*/
|
|
9
|
+
import { evaluateVisibility, resolveDynamicValue } from "@json-render/core";
|
|
10
|
+
import { resolveProps } from "./resolve-state.js";
|
|
11
|
+
/**
|
|
12
|
+
* Render a Spec tree into DOM nodes using the provided DomRegistry.
|
|
13
|
+
*
|
|
14
|
+
* @param spec - The json-render Spec describing the UI tree
|
|
15
|
+
* @param store - StateStore holding the current state values
|
|
16
|
+
* @param registry - Map of element type names to DomComponentRenderer functions
|
|
17
|
+
* @param send - Function to dispatch XState events from UI interactions
|
|
18
|
+
* @param actor - Raw actor — available to DOM components for state reading, complex events
|
|
19
|
+
* @param actorActions - Map of json-render action names → XState event types
|
|
20
|
+
* @returns The root DOM node, or null if root element is invisible or unknown
|
|
21
|
+
*/
|
|
22
|
+
export function renderSpec(spec, store, registry, send, actor, actorActions) {
|
|
23
|
+
function renderElement(key) {
|
|
24
|
+
const element = spec.elements[key];
|
|
25
|
+
if (!element)
|
|
26
|
+
return null;
|
|
27
|
+
// Evaluate visibility condition
|
|
28
|
+
if (element.visible !== undefined) {
|
|
29
|
+
const stateModel = store.getSnapshot();
|
|
30
|
+
const visible = evaluateVisibility(element.visible, { stateModel });
|
|
31
|
+
if (!visible)
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
// Resolve $state expressions in props
|
|
35
|
+
const resolvedProps = resolveProps(element.props ?? {}, store);
|
|
36
|
+
// Build context for this element
|
|
37
|
+
const ctx = {
|
|
38
|
+
spec,
|
|
39
|
+
store,
|
|
40
|
+
send,
|
|
41
|
+
actor,
|
|
42
|
+
actorActions,
|
|
43
|
+
renderChildren: (keys) => keys.flatMap((k) => {
|
|
44
|
+
const n = renderElement(k);
|
|
45
|
+
return n ? [n] : [];
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
48
|
+
// Look up renderer in registry
|
|
49
|
+
const renderer = registry[element.type];
|
|
50
|
+
if (!renderer)
|
|
51
|
+
return null; // unknown type → null (matches json-render behavior)
|
|
52
|
+
// Create resolved element to pass to renderer
|
|
53
|
+
const resolvedElement = { ...element, props: resolvedProps };
|
|
54
|
+
const node = renderer(resolvedElement, ctx);
|
|
55
|
+
if (!node)
|
|
56
|
+
return null;
|
|
57
|
+
// Wire event handlers from element.on
|
|
58
|
+
if (element.on && node instanceof HTMLElement) {
|
|
59
|
+
wireEventHandlers(node, element.on, ctx);
|
|
60
|
+
}
|
|
61
|
+
// Append children into the node if it has children and is an HTMLElement
|
|
62
|
+
const childKeys = element.children ?? [];
|
|
63
|
+
if (childKeys.length > 0 && node instanceof HTMLElement) {
|
|
64
|
+
const childNodes = ctx.renderChildren(childKeys);
|
|
65
|
+
childNodes.forEach((child) => node.appendChild(child));
|
|
66
|
+
}
|
|
67
|
+
return node;
|
|
68
|
+
}
|
|
69
|
+
return renderElement(spec.root);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Wire event handlers from element.on bindings to DOM event listeners.
|
|
73
|
+
*
|
|
74
|
+
* Maps json-render event names to DOM events (e.g. "click" → "click").
|
|
75
|
+
* For each binding, checks actorActions map to find the XState event type.
|
|
76
|
+
* If found, calls send() with that event type on the DOM event.
|
|
77
|
+
*/
|
|
78
|
+
function wireEventHandlers(node, on, ctx) {
|
|
79
|
+
if (!on)
|
|
80
|
+
return;
|
|
81
|
+
for (const [domEvent, binding] of Object.entries(on)) {
|
|
82
|
+
// binding can be ActionBinding | ActionBinding[]
|
|
83
|
+
const bindings = Array.isArray(binding) ? binding : [binding];
|
|
84
|
+
node.addEventListener(domEvent, () => {
|
|
85
|
+
for (const b of bindings) {
|
|
86
|
+
const eventType = ctx.actorActions[b.action];
|
|
87
|
+
if (eventType) {
|
|
88
|
+
// Resolve DynamicValue params ($state:/ references) against current store
|
|
89
|
+
const stateModel = ctx.store.getSnapshot();
|
|
90
|
+
const resolvedParams = {};
|
|
91
|
+
if (b.params) {
|
|
92
|
+
for (const [key, val] of Object.entries(b.params)) {
|
|
93
|
+
resolvedParams[key] = resolveDynamicValue(val, stateModel);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
ctx.send({ type: eventType, ...resolvedParams });
|
|
97
|
+
}
|
|
98
|
+
// Unknown action → no-op (matches json-render silent behavior)
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=dom-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-renderer.js","sourceRoot":"","sources":["../src/dom-renderer.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { PlayError } from "@xmachines/play";
|
|
2
|
+
/**
|
|
3
|
+
* Thrown by `connectRenderer` when the `components` option is `null` or `undefined`.
|
|
4
|
+
*
|
|
5
|
+
* A missing catalog is a programmer error — the components map must be provided
|
|
6
|
+
* to the renderer. The `componentName` that was being looked up is encoded in
|
|
7
|
+
* the error message.
|
|
8
|
+
*
|
|
9
|
+
* **Error code:** `PLAY_RENDERER_MISSING_CATALOG`
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { MissingCatalogError } from "@xmachines/play-dom";
|
|
14
|
+
*
|
|
15
|
+
* // In a catch block:
|
|
16
|
+
* if (err instanceof MissingCatalogError) {
|
|
17
|
+
* console.error("components option was not provided to connectRenderer()");
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare class MissingCatalogError extends PlayError {
|
|
22
|
+
constructor(componentName: string, catalogValue: null | undefined);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Thrown by `connectRenderer` when the component name from `actor.currentView` does
|
|
26
|
+
* not exist as a key in the `components` catalog.
|
|
27
|
+
*
|
|
28
|
+
* This is a programmer error — either the machine's `meta.view.component` name is
|
|
29
|
+
* misspelled, or the component was not registered in the catalog passed to the renderer.
|
|
30
|
+
*
|
|
31
|
+
* **Error code:** `PLAY_RENDERER_MISSING_COMPONENT`
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* import { MissingComponentError } from "@xmachines/play-dom";
|
|
36
|
+
*
|
|
37
|
+
* if (err instanceof MissingComponentError) {
|
|
38
|
+
* // err.message lists both the missing name and available components
|
|
39
|
+
* console.error(err.message);
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare class MissingComponentError extends PlayError {
|
|
44
|
+
constructor(componentName: string, availableComponents: string[]);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;gBACrC,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,GAAG,SAAS;CAQjE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;gBACvC,aAAa,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE;CAQhE"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { PlayError } from "@xmachines/play";
|
|
2
|
+
/**
|
|
3
|
+
* Thrown by `connectRenderer` when the `components` option is `null` or `undefined`.
|
|
4
|
+
*
|
|
5
|
+
* A missing catalog is a programmer error — the components map must be provided
|
|
6
|
+
* to the renderer. The `componentName` that was being looked up is encoded in
|
|
7
|
+
* the error message.
|
|
8
|
+
*
|
|
9
|
+
* **Error code:** `PLAY_RENDERER_MISSING_CATALOG`
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { MissingCatalogError } from "@xmachines/play-dom";
|
|
14
|
+
*
|
|
15
|
+
* // In a catch block:
|
|
16
|
+
* if (err instanceof MissingCatalogError) {
|
|
17
|
+
* console.error("components option was not provided to connectRenderer()");
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export class MissingCatalogError extends PlayError {
|
|
22
|
+
constructor(componentName, catalogValue) {
|
|
23
|
+
super("PlayRenderer", "PLAY_RENDERER_MISSING_CATALOG", `Components catalog is ${catalogValue === null ? "null" : "undefined"}. Cannot render component "${componentName}".`);
|
|
24
|
+
this.name = "MissingCatalogError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Thrown by `connectRenderer` when the component name from `actor.currentView` does
|
|
29
|
+
* not exist as a key in the `components` catalog.
|
|
30
|
+
*
|
|
31
|
+
* This is a programmer error — either the machine's `meta.view.component` name is
|
|
32
|
+
* misspelled, or the component was not registered in the catalog passed to the renderer.
|
|
33
|
+
*
|
|
34
|
+
* **Error code:** `PLAY_RENDERER_MISSING_COMPONENT`
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* import { MissingComponentError } from "@xmachines/play-dom";
|
|
39
|
+
*
|
|
40
|
+
* if (err instanceof MissingComponentError) {
|
|
41
|
+
* // err.message lists both the missing name and available components
|
|
42
|
+
* console.error(err.message);
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export class MissingComponentError extends PlayError {
|
|
47
|
+
constructor(componentName, availableComponents) {
|
|
48
|
+
super("PlayRenderer", "PLAY_RENDERER_MISSING_COMPONENT", `Component "${componentName}" not found in catalog. Available components: ${availableComponents.join(", ")}`);
|
|
49
|
+
this.name = "MissingComponentError";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,mBAAoB,SAAQ,SAAS;IACjD,YAAY,aAAqB,EAAE,YAA8B;QAChE,KAAK,CACJ,cAAc,EACd,+BAA+B,EAC/B,yBAAyB,YAAY,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,8BAA8B,aAAa,IAAI,CACpH,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACnC,CAAC;CACD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,qBAAsB,SAAQ,SAAS;IACnD,YAAY,aAAqB,EAAE,mBAA6B;QAC/D,KAAK,CACJ,cAAc,EACd,iCAAiC,EACjC,cAAc,aAAa,iDAAiD,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5G,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACrC,CAAC;CACD"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @xmachines/play-dom - Vanilla DOM renderer for XMachines Play architecture
|
|
3
|
+
*
|
|
4
|
+
* Provides a two-layer architecture:
|
|
5
|
+
* - Inner layer (dom-renderer): Pure json-render-core compatible DOM renderer.
|
|
6
|
+
* Zero XMachines dependencies. Upstreamable to @json-render/dom.
|
|
7
|
+
* - Outer layer (PlayRenderer): XMachines wrapper. Bridges actor.currentView
|
|
8
|
+
* (TC39 Signal) to the inner DOM renderer using watchSignal.
|
|
9
|
+
*
|
|
10
|
+
* Also provides connectRenderer() as a backward-compat convenience function.
|
|
11
|
+
*
|
|
12
|
+
* No framework required. Pure DOM APIs only.
|
|
13
|
+
*
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
export { connectRenderer } from "./connect-renderer.js";
|
|
17
|
+
export { PlayRenderer } from "./PlayRenderer.js";
|
|
18
|
+
export { renderSpec } from "./dom-renderer.js";
|
|
19
|
+
export type { DomComponentRenderer, DomRegistry, DomRenderContext, ConnectRendererOptions, PlayDomOptions, } from "./types.js";
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,YAAY,EACX,oBAAoB,EACpB,WAAW,EACX,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,GACd,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @xmachines/play-dom - Vanilla DOM renderer for XMachines Play architecture
|
|
3
|
+
*
|
|
4
|
+
* Provides a two-layer architecture:
|
|
5
|
+
* - Inner layer (dom-renderer): Pure json-render-core compatible DOM renderer.
|
|
6
|
+
* Zero XMachines dependencies. Upstreamable to @json-render/dom.
|
|
7
|
+
* - Outer layer (PlayRenderer): XMachines wrapper. Bridges actor.currentView
|
|
8
|
+
* (TC39 Signal) to the inner DOM renderer using watchSignal.
|
|
9
|
+
*
|
|
10
|
+
* Also provides connectRenderer() as a backward-compat convenience function.
|
|
11
|
+
*
|
|
12
|
+
* No framework required. Pure DOM APIs only.
|
|
13
|
+
*
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
export { connectRenderer } from "./connect-renderer.js";
|
|
17
|
+
export { PlayRenderer } from "./PlayRenderer.js";
|
|
18
|
+
export { renderSpec } from "./dom-renderer.js";
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resolve-state.ts — Resolves json-render dynamic value expressions against a StateStore.
|
|
3
|
+
*
|
|
4
|
+
* Pure function — no XMachines dependencies. Upstreamable to @json-render/dom.
|
|
5
|
+
*/
|
|
6
|
+
import type { StateStore } from "@json-render/core";
|
|
7
|
+
/**
|
|
8
|
+
* Resolve all props in an element's props object.
|
|
9
|
+
* Returns a new props object with all $state expressions resolved against the store.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveProps(props: Record<string, unknown>, store: StateStore): Record<string, unknown>;
|
|
12
|
+
//# sourceMappingURL=resolve-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-state.d.ts","sourceRoot":"","sources":["../src/resolve-state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,KAAK,EAAE,UAAU,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resolve-state.ts — Resolves json-render dynamic value expressions against a StateStore.
|
|
3
|
+
*
|
|
4
|
+
* Pure function — no XMachines dependencies. Upstreamable to @json-render/dom.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Resolve all props in an element's props object.
|
|
8
|
+
* Returns a new props object with all $state expressions resolved against the store.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveProps(props, store) {
|
|
11
|
+
const result = {};
|
|
12
|
+
for (const [key, value] of Object.entries(props)) {
|
|
13
|
+
result[key] = resolveValue(value, store);
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a single value that may contain json-render dynamic expressions.
|
|
19
|
+
* - `{ "$state": "/path" }` → store.get("/path")
|
|
20
|
+
* - Arrays → each element resolved recursively
|
|
21
|
+
* - Nested objects → each value resolved recursively
|
|
22
|
+
* - Plain values → returned as-is
|
|
23
|
+
*/
|
|
24
|
+
function resolveValue(value, store) {
|
|
25
|
+
if (value === null || typeof value !== "object")
|
|
26
|
+
return value;
|
|
27
|
+
if (Array.isArray(value))
|
|
28
|
+
return value.map((v) => resolveValue(v, store));
|
|
29
|
+
const obj = value;
|
|
30
|
+
// $state binding: { "$state": "/path" }
|
|
31
|
+
if ("$state" in obj && typeof obj["$state"] === "string") {
|
|
32
|
+
return store.get(obj["$state"]);
|
|
33
|
+
}
|
|
34
|
+
// Nested plain object — recurse
|
|
35
|
+
const resolved = {};
|
|
36
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
37
|
+
resolved[k] = resolveValue(v, store);
|
|
38
|
+
}
|
|
39
|
+
return resolved;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=resolve-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-state.js","sourceRoot":"","sources":["../src/resolve-state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC3B,KAA8B,EAC9B,KAAiB;IAEjB,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,KAAc,EAAE,KAAiB;IACtD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IAE1E,MAAM,GAAG,GAAG,KAAgC,CAAC;IAE7C,wCAAwC;IACxC,IAAI,QAAQ,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjC,CAAC;IAED,gCAAgC;IAChC,MAAM,QAAQ,GAA4B,EAAE,CAAC;IAC7C,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1C,QAAQ,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript type definitions for play-dom
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
import type { Spec, UIElement, StateStore } from "@json-render/core";
|
|
7
|
+
import type { AbstractActor, Viewable } from "@xmachines/play-actor";
|
|
8
|
+
import type { AnyActorLogic } from "xstate";
|
|
9
|
+
/**
|
|
10
|
+
* Context passed to each DomComponentRenderer when rendering an element.
|
|
11
|
+
*/
|
|
12
|
+
export interface DomRenderContext {
|
|
13
|
+
/** Full spec tree */
|
|
14
|
+
spec: Spec;
|
|
15
|
+
/** State store bound to spec.state */
|
|
16
|
+
store: StateStore;
|
|
17
|
+
/** Send an event to the actor */
|
|
18
|
+
send: (event: {
|
|
19
|
+
type: string;
|
|
20
|
+
} & Record<string, unknown>) => void;
|
|
21
|
+
/** Raw actor — access from any DOM component for state reading, complex events, lifecycle */
|
|
22
|
+
actor: AbstractActor<AnyActorLogic> & Viewable;
|
|
23
|
+
/** Map of json-render action names → XState event types */
|
|
24
|
+
actorActions: Record<string, string>;
|
|
25
|
+
/** Recursively render child elements by key */
|
|
26
|
+
renderChildren: (keys: string[]) => Node[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Pure DOM component renderer — receives a UIElement + context and returns a DOM node.
|
|
30
|
+
* No framework required. Return null for unknown/unsupported elements.
|
|
31
|
+
*/
|
|
32
|
+
export type DomComponentRenderer = (element: UIElement, ctx: DomRenderContext) => HTMLElement | Text | null;
|
|
33
|
+
/**
|
|
34
|
+
* Registry of component renderers keyed by element type.
|
|
35
|
+
*/
|
|
36
|
+
export type DomRegistry = Record<string, DomComponentRenderer>;
|
|
37
|
+
/**
|
|
38
|
+
* Options for PlayRenderer.
|
|
39
|
+
*/
|
|
40
|
+
export interface PlayDomOptions {
|
|
41
|
+
/** Map of json-render actionName → XState event type */
|
|
42
|
+
actions?: Record<string, string>;
|
|
43
|
+
/**
|
|
44
|
+
* Optional external StateStore (e.g. from `xstateStoreStateStore` in @json-render/xstate).
|
|
45
|
+
* When provided, PlayRenderer operates in controlled mode — spec.state is ignored and
|
|
46
|
+
* this store is the single source of truth for UI state (form values, etc.).
|
|
47
|
+
* When omitted, a fresh @xstate/store atom is created internally per view transition,
|
|
48
|
+
* seeded from spec.state.
|
|
49
|
+
*/
|
|
50
|
+
store?: StateStore;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Options for connectRenderer() — backward compat API.
|
|
54
|
+
*/
|
|
55
|
+
export interface ConnectRendererOptions {
|
|
56
|
+
/** Actor instance with currentView signal (requires Viewable capability) */
|
|
57
|
+
actor: AbstractActor<AnyActorLogic> & Viewable;
|
|
58
|
+
/** Registry of component renderers — replaces old `components` map */
|
|
59
|
+
registry: DomRegistry;
|
|
60
|
+
/** Container element to render into */
|
|
61
|
+
container: HTMLElement;
|
|
62
|
+
/** Optional element shown when currentView is null (defaults to nothing — clears container) */
|
|
63
|
+
fallback?: HTMLElement | null;
|
|
64
|
+
/** Map of json-render actionName → XState event type */
|
|
65
|
+
actions?: Record<string, string>;
|
|
66
|
+
/**
|
|
67
|
+
* Optional external StateStore (e.g. from `xstateStoreStateStore` in @json-render/xstate).
|
|
68
|
+
* When provided, PlayRenderer operates in controlled mode — spec.state is ignored.
|
|
69
|
+
* When omitted, a fresh @xstate/store atom is created internally per view transition.
|
|
70
|
+
*/
|
|
71
|
+
store?: StateStore;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,qBAAqB;IACrB,IAAI,EAAE,IAAI,CAAC;IACX,sCAAsC;IACtC,KAAK,EAAE,UAAU,CAAC;IAClB,iCAAiC;IACjC,IAAI,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAClE,6FAA6F;IAC7F,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ,CAAC;IAC/C,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,+CAA+C;IAC/C,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;CAC3C;AAED;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAClC,OAAO,EAAE,SAAS,EAClB,GAAG,EAAE,gBAAgB,KACjB,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/B;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACtC,4EAA4E;IAC5E,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ,CAAC;IAE/C,sEAAsE;IACtE,QAAQ,EAAE,WAAW,CAAC;IAEtB,uCAAuC;IACvC,SAAS,EAAE,WAAW,CAAC;IAEvB,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAE9B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;;;OAIG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;CACnB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xmachines/play-dom",
|
|
3
|
+
"version": "1.0.0-beta.16",
|
|
4
|
+
"description": "Vanilla DOM renderer for XMachines Play architecture with signal-driven rendering",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"actor",
|
|
7
|
+
"dom",
|
|
8
|
+
"renderer",
|
|
9
|
+
"signals",
|
|
10
|
+
"state-machine",
|
|
11
|
+
"ui",
|
|
12
|
+
"xmachines"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "XMachines Contributors",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git@gitlab.com:xmachin-es/xmachines-js.git",
|
|
19
|
+
"directory": "packages/play-dom"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc --build",
|
|
38
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"test": "vitest",
|
|
41
|
+
"prepublishOnly": "npm run build"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@xmachines/play": "1.0.0-beta.16",
|
|
45
|
+
"@xmachines/play-actor": "1.0.0-beta.16",
|
|
46
|
+
"@xmachines/play-signals": "1.0.0-beta.16"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@json-render/core": "^0.16.0",
|
|
50
|
+
"@json-render/xstate": "^0.16.0",
|
|
51
|
+
"@types/node": "^25.5.0",
|
|
52
|
+
"@xmachines/shared": "1.0.0-beta.16",
|
|
53
|
+
"@xstate/store": ">=3.17.0",
|
|
54
|
+
"typescript": "^5.9.3 || ^6.0.2",
|
|
55
|
+
"vitest": "^4.1.2"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@json-render/core": "^0.16.0",
|
|
59
|
+
"@json-render/xstate": "^0.16.0",
|
|
60
|
+
"@xstate/store": ">=3.17.0"
|
|
61
|
+
}
|
|
62
|
+
}
|