@intent-framework/dom 0.1.0-alpha.1 → 0.1.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/dist/dom-router.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +30 -7
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @intent-framework/dom
|
|
2
|
+
|
|
3
|
+
DOM materializer for Intent screens and router.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @intent-framework/core@0.1.0-alpha.1 @intent-framework/dom@0.1.0-alpha.1
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @intent-framework/core@0.1.0-alpha.1 @intent-framework/dom@0.1.0-alpha.1
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## What it provides
|
|
16
|
+
|
|
17
|
+
- `renderDom()` — materialize a screen into semantic HTML
|
|
18
|
+
- `renderRouter()` — materialize a router into navigable DOM pages
|
|
19
|
+
- Real HTML labels, inputs, buttons, and `aria-live` output
|
|
20
|
+
- Reactive action enablement and blocked reasons
|
|
21
|
+
- Enter key triggers the default action when unambiguous
|
|
22
|
+
- Opt-in screen-name heading via `showScreenName`
|
|
23
|
+
- Opt-in semantic data attributes via `showSemanticIds`
|
|
24
|
+
|
|
25
|
+
## Minimal example
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { screen } from "@intent-framework/core"
|
|
29
|
+
import { renderDom } from "@intent-framework/dom"
|
|
30
|
+
|
|
31
|
+
const InviteMember = screen("InviteMember", $ => {
|
|
32
|
+
const email = $.state.text("email")
|
|
33
|
+
|
|
34
|
+
const emailAsk = $.ask("Email", email)
|
|
35
|
+
.required()
|
|
36
|
+
.validate(value => value.includes("@") ? true : "Enter a valid email")
|
|
37
|
+
|
|
38
|
+
const invite = $.act("Invite member")
|
|
39
|
+
.primary()
|
|
40
|
+
.when(emailAsk.valid, "Enter a valid email first")
|
|
41
|
+
|
|
42
|
+
$.surface("main").contains(emailAsk, invite)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const cleanup = renderDom(InviteMember, {
|
|
46
|
+
target: document.getElementById("root")!,
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The renderer produces real DOM — labels, inputs, buttons, and an `aria-live` output. No JSX required.
|
|
51
|
+
|
|
52
|
+
## Where this fits
|
|
53
|
+
|
|
54
|
+
DOM is a renderer for Intent screens. It depends on `@intent-framework/core` and can optionally integrate with `@intent-framework/router` via `renderRouter()`. It is not the source of truth — the screen definition is.
|
|
55
|
+
|
|
56
|
+
## Learn more
|
|
57
|
+
|
|
58
|
+
- [Root README](../../README.md) — project overview and philosophy
|
|
59
|
+
- [Quickstart](../../docs/Quickstart.md) — step-by-step guide with DOM rendering
|
|
60
|
+
- [Canonical runnable example](../../examples/canonical-invite) — matches the Quickstart one-to-one
|
|
61
|
+
|
|
62
|
+
## Status
|
|
63
|
+
|
|
64
|
+
Experimental alpha. Version `0.1.0-alpha.1`. APIs may change. Not recommended for production use.
|
package/dist/dom-router.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type RenderRouterOptions<TServices extends object = DefaultScreenServices
|
|
|
13
13
|
notFound?: ScreenDefinition<TServices> | ((pathname: string) => ScreenDefinition<TServices>);
|
|
14
14
|
services?: Omit<TServices, "navigate" | "route">;
|
|
15
15
|
showScreenName?: boolean;
|
|
16
|
+
showSemanticIds?: boolean;
|
|
16
17
|
};
|
|
17
18
|
export declare function renderRouter<Routes extends Record<string, {
|
|
18
19
|
path: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type DomRendererOptions<TServices extends object = DefaultScreenServices>
|
|
|
3
3
|
target: HTMLElement;
|
|
4
4
|
services?: TServices;
|
|
5
5
|
showScreenName?: boolean;
|
|
6
|
+
showSemanticIds?: boolean;
|
|
6
7
|
};
|
|
7
8
|
export { renderRouter } from "./dom-router.js";
|
|
8
9
|
export type { RouterDomHandle, RenderRouterOptions } from "./dom-router.js";
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createScreenRuntime } from "@intent-framework/core";
|
|
1
|
+
import { createScreenRuntime, inspectScreen } from "@intent-framework/core";
|
|
2
2
|
|
|
3
3
|
//#region src/dom-router.ts
|
|
4
4
|
function renderRouter(router, options) {
|
|
5
|
-
const { showScreenName } = options;
|
|
5
|
+
const { showScreenName, showSemanticIds } = options;
|
|
6
6
|
const win = options.window ?? window;
|
|
7
7
|
let currentCleanup;
|
|
8
8
|
const navigate = (name, ...args) => {
|
|
@@ -30,7 +30,8 @@ function renderRouter(router, options) {
|
|
|
30
30
|
currentCleanup = renderDom(match.screen, {
|
|
31
31
|
target: options.target,
|
|
32
32
|
services: mergedServices,
|
|
33
|
-
showScreenName
|
|
33
|
+
showScreenName,
|
|
34
|
+
showSemanticIds
|
|
34
35
|
});
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
@@ -39,7 +40,8 @@ function renderRouter(router, options) {
|
|
|
39
40
|
currentCleanup = renderDom(screen, {
|
|
40
41
|
target: options.target,
|
|
41
42
|
services: mergedServices,
|
|
42
|
-
showScreenName
|
|
43
|
+
showScreenName,
|
|
44
|
+
showSemanticIds
|
|
43
45
|
});
|
|
44
46
|
} else options.target.textContent = "Not found";
|
|
45
47
|
}
|
|
@@ -77,9 +79,9 @@ function findDefaultAction(acts) {
|
|
|
77
79
|
return void 0;
|
|
78
80
|
}
|
|
79
81
|
function renderDom(screenDef, options) {
|
|
80
|
-
const { target, services, showScreenName } = options;
|
|
82
|
+
const { target, services, showScreenName, showSemanticIds } = options;
|
|
81
83
|
target.innerHTML = "";
|
|
82
|
-
const root = buildDom(screenDef, showScreenName);
|
|
84
|
+
const root = buildDom(screenDef, showScreenName, showSemanticIds);
|
|
83
85
|
target.appendChild(root);
|
|
84
86
|
const runtime = createScreenRuntime(screenDef, { services });
|
|
85
87
|
runtime.start();
|
|
@@ -169,10 +171,19 @@ function renderDom(screenDef, options) {
|
|
|
169
171
|
runtime.dispose();
|
|
170
172
|
};
|
|
171
173
|
}
|
|
172
|
-
function buildDom(screenDef, showScreenName) {
|
|
174
|
+
function buildDom(screenDef, showScreenName, showSemanticIds) {
|
|
175
|
+
let inspected;
|
|
176
|
+
let askSemanticIds;
|
|
177
|
+
let actSemanticIds;
|
|
178
|
+
if (showSemanticIds) {
|
|
179
|
+
inspected = inspectScreen(screenDef);
|
|
180
|
+
askSemanticIds = new Map(inspected.asks.map((a) => [a.id, a.semanticId]));
|
|
181
|
+
actSemanticIds = new Map(inspected.acts.map((a) => [a.id, a.semanticId]));
|
|
182
|
+
}
|
|
173
183
|
const surface = screenDef.surfaces[0];
|
|
174
184
|
const main = document.createElement("main");
|
|
175
185
|
if (surface) main.id = surface.id;
|
|
186
|
+
if (showSemanticIds && inspected) main.setAttribute("data-intent-screen", inspected.semanticId);
|
|
176
187
|
if (showScreenName) {
|
|
177
188
|
const heading = document.createElement("h1");
|
|
178
189
|
heading.textContent = screenDef.name;
|
|
@@ -187,10 +198,18 @@ function buildDom(screenDef, showScreenName) {
|
|
|
187
198
|
const label = document.createElement("label");
|
|
188
199
|
label.textContent = ask.label;
|
|
189
200
|
label.htmlFor = ask.id;
|
|
201
|
+
if (showSemanticIds && askSemanticIds) {
|
|
202
|
+
const sid = askSemanticIds.get(ask.id);
|
|
203
|
+
if (sid) label.setAttribute("data-intent-ask", sid);
|
|
204
|
+
}
|
|
190
205
|
container.appendChild(label);
|
|
191
206
|
const input = createInputForAsk(ask);
|
|
192
207
|
input.id = ask.id;
|
|
193
208
|
input.name = ask.id;
|
|
209
|
+
if (showSemanticIds && askSemanticIds) {
|
|
210
|
+
const sid = askSemanticIds.get(ask.id);
|
|
211
|
+
if (sid) input.setAttribute("data-intent-ask", sid);
|
|
212
|
+
}
|
|
194
213
|
if (ask.required) input.required = true;
|
|
195
214
|
if (ask.kind === "contact" && ask.contactKind) input.setAttribute("autocomplete", ask.contactKind);
|
|
196
215
|
input.addEventListener("input", () => {
|
|
@@ -225,6 +244,10 @@ function buildDom(screenDef, showScreenName) {
|
|
|
225
244
|
button.id = act.id;
|
|
226
245
|
button.type = "button";
|
|
227
246
|
button.textContent = act.label;
|
|
247
|
+
if (showSemanticIds && actSemanticIds) {
|
|
248
|
+
const sid = actSemanticIds.get(act.id);
|
|
249
|
+
if (sid) button.setAttribute("data-intent-action", sid);
|
|
250
|
+
}
|
|
228
251
|
if (act.primary) button.className = "primary";
|
|
229
252
|
if (!act.enabled.current) {
|
|
230
253
|
button.disabled = true;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.1.0-alpha.
|
|
6
|
+
"version": "0.1.0-alpha.2",
|
|
7
7
|
"description": "DOM materializer for Intent screens and router",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"repository": {
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"dist"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@intent-framework/
|
|
30
|
-
"@intent-framework/
|
|
29
|
+
"@intent-framework/router": "0.1.0-alpha.1",
|
|
30
|
+
"@intent-framework/core": "0.1.0-alpha.1"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"jsdom": "^29.1.1",
|