@xmachines/play-dom 6.1.0 → 7.0.0
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 +127 -48
- package/dist/PlayRenderer.d.ts +127 -32
- package/dist/PlayRenderer.d.ts.map +1 -1
- package/dist/PlayRenderer.js +584 -679
- package/dist/PlayRenderer.js.map +1 -1
- package/dist/create-play-ui.d.ts +3 -1
- package/dist/create-play-ui.d.ts.map +1 -1
- package/dist/create-play-ui.js +50 -94
- package/dist/create-play-ui.js.map +1 -1
- package/dist/create-renderer.js +37 -87
- package/dist/create-renderer.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -34
- package/dist/types.d.ts +23 -19
- package/dist/types.d.ts.map +1 -1
- package/package.json +19 -19
- package/dist/index.js.map +0 -1
- package/dist/types.js +0 -10
- package/dist/types.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Vanilla DOM renderer for XMachines Play architecture with atom-driven rendering.
|
|
4
4
|
|
|
5
|
-
[](https://opensource.org/licenses/MIT) [](https://opensource.org/licenses/MIT) [](https://www.npmjs.com/package/@xmachines/play-dom)
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -13,9 +13,11 @@ pnpm add @xmachines/play-dom
|
|
|
13
13
|
**Peer dependencies:**
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
pnpm add @xmachines/core @xmachines/play @xmachines/play-atom @xstate/store @xmachines/json-render-core @xmachines/json-render-dom @xmachines/json-render-xstate
|
|
16
|
+
pnpm add @xmachines/core @xmachines/play @xmachines/play-atom @xstate/store @xmachines/json-render-core@0.21.0-xm.6 @xmachines/json-render-dom@0.21.0-xm.6 @xmachines/json-render-xstate@0.21.0-xm.6
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
The json-render packages are exact peers: install the version that this release names, `0.21.0-xm.6`.
|
|
20
|
+
|
|
19
21
|
[`@xmachines/play`](../play/README.md) is the peer of [`@xmachines/play-view`](../play-view/README.md), which this package depends on. Install ONE copy of it.
|
|
20
22
|
|
|
21
23
|
`@xstate/store` is the peer of [`@xmachines/play-atom`](../play-atom/README.md), and not of this
|
|
@@ -104,7 +106,7 @@ disconnect();
|
|
|
104
106
|
|
|
105
107
|
`createRenderer` is the simplest integration path. Call it one time at module scope, with your catalog and your component map. Then call the `mount` function that it returns, one time for each pair of an actor and a container.
|
|
106
108
|
|
|
107
|
-
```typescript
|
|
109
|
+
```typescript fragment
|
|
108
110
|
import { createRenderer, schema } from "@xmachines/play-dom";
|
|
109
111
|
import { defineCatalog } from "@xmachines/json-render-core";
|
|
110
112
|
|
|
@@ -122,9 +124,9 @@ disconnect();
|
|
|
122
124
|
|
|
123
125
|
Use `createPlayUI` when you need a render error handler, a fallback element, a navigation integration, a computed function, or a custom check. Use it also when your code needs the `registryResult` value, for example for `executeAction`.
|
|
124
126
|
|
|
125
|
-
The factory holds the factory options (`functions`, `validationFunctions`, `navigate`, `onRenderError`, `onError`, and `fallback`) from the moment of its creation, and it applies them on every `mount()` call. Give the mount options (`store` and `loading`) to `mount()` itself.
|
|
127
|
+
The factory holds the factory options (`functions`, `directives`, `validationFunctions`, `navigate`, `onConfirm`, `confirmDialog`, `onRenderError`, `onError`, and `fallback`) from the moment of its creation, and it applies them on every `mount()` call. Give the mount options (`store` and `loading`) to `mount()` itself.
|
|
126
128
|
|
|
127
|
-
```typescript
|
|
129
|
+
```typescript fragment
|
|
128
130
|
import { defineRegistry, createPlayUI, schema } from "@xmachines/play-dom";
|
|
129
131
|
import { defineCatalog } from "@xmachines/json-render-core";
|
|
130
132
|
|
|
@@ -159,12 +161,14 @@ disconnect();
|
|
|
159
161
|
Use `PlayRenderer` directly when you need the explicit `connect()` and `disconnect()` control, or when you integrate the renderer into a system that controls its life itself.
|
|
160
162
|
|
|
161
163
|
```typescript
|
|
162
|
-
import { PlayRenderer,
|
|
164
|
+
import { PlayRenderer, type DefineRegistryResult } from "@xmachines/play-dom";
|
|
165
|
+
import type { ViewActor } from "@xmachines/play-view";
|
|
163
166
|
|
|
164
|
-
|
|
165
|
-
|
|
167
|
+
declare const actor: ViewActor; // the actor of the Quick Start
|
|
168
|
+
// The result of defineRegistry(catalog, { components, actions }), as in the createPlayUI example
|
|
169
|
+
declare const registryResult: DefineRegistryResult;
|
|
166
170
|
|
|
167
|
-
|
|
171
|
+
const container = document.getElementById("app")!;
|
|
168
172
|
const renderer = new PlayRenderer(container, actor, registryResult.registry, { registryResult });
|
|
169
173
|
|
|
170
174
|
renderer.connect();
|
|
@@ -172,16 +176,21 @@ renderer.connect();
|
|
|
172
176
|
renderer.disconnect();
|
|
173
177
|
```
|
|
174
178
|
|
|
175
|
-
**Controlled store mode.** Give the renderer an external `StateStore`. The renderer then shares the state with the other parts of your application:
|
|
179
|
+
**Controlled store mode.** Give the renderer an external `StateStore`. The renderer then shares the state with the other parts of your application. The tree lives as long as the store, so a new `viewKey` updates the tree in place, as the framework renderers do:
|
|
176
180
|
|
|
177
181
|
```typescript
|
|
182
|
+
import { PlayRenderer, type DefineRegistryResult } from "@xmachines/play-dom";
|
|
178
183
|
import { createAtom } from "@xmachines/play-atom";
|
|
179
184
|
import { xstateStoreStateStore } from "@xmachines/json-render-xstate";
|
|
185
|
+
import type { ViewActor } from "@xmachines/play-view";
|
|
186
|
+
|
|
187
|
+
declare const actor: ViewActor; // the actor of the Quick Start
|
|
188
|
+
declare const registryResult: DefineRegistryResult; // from the previous example
|
|
180
189
|
|
|
181
190
|
const atom = createAtom<Record<string, unknown>>({ username: "" });
|
|
182
191
|
const store = xstateStoreStateStore({ atom });
|
|
183
192
|
|
|
184
|
-
|
|
193
|
+
const container = document.getElementById("app")!;
|
|
185
194
|
const renderer = new PlayRenderer(container, actor, registryResult.registry, {
|
|
186
195
|
registryResult,
|
|
187
196
|
store,
|
|
@@ -198,6 +207,10 @@ Every entry point (`createPlayUI` and `PlayRenderer`) accepts the same UI-provid
|
|
|
198
207
|
This option permits a dynamic prop value of the form `{ "$computed": "name", "args": {...} }` in a spec. Each function receives the resolved `args` object, and it returns the computed value.
|
|
199
208
|
|
|
200
209
|
```typescript
|
|
210
|
+
import { createPlayUI, type DefineRegistryResult } from "@xmachines/play-dom";
|
|
211
|
+
|
|
212
|
+
declare const registryResult: DefineRegistryResult; // from defineRegistry(catalog, ...)
|
|
213
|
+
|
|
201
214
|
const mount = createPlayUI(registryResult, {
|
|
202
215
|
functions: {
|
|
203
216
|
fullName: (args) => `${args.first} ${args.last}`,
|
|
@@ -215,7 +228,12 @@ This option gives the named check functions for a field check inside a component
|
|
|
215
228
|
The DOM renderer has no `ValidationProvider` tree, and in this it is different from the framework renderers. Each component must call the check itself, with `runValidationCheck` or `runValidation` from `@xmachines/json-render-core`. Give `ctx.ctx.validationFunctions` to the call as `customFunctions`.
|
|
216
229
|
|
|
217
230
|
```typescript
|
|
218
|
-
import {
|
|
231
|
+
import { createPlayUI, schema } from "@xmachines/play-dom";
|
|
232
|
+
import type { ComponentFn, DefineRegistryResult } from "@xmachines/play-dom";
|
|
233
|
+
import { defineCatalog, runValidationCheck } from "@xmachines/json-render-core";
|
|
234
|
+
import { z } from "zod";
|
|
235
|
+
|
|
236
|
+
declare const registryResult: DefineRegistryResult; // from defineRegistry(catalog, ...)
|
|
219
237
|
|
|
220
238
|
const mount = createPlayUI(registryResult, {
|
|
221
239
|
validationFunctions: {
|
|
@@ -224,12 +242,17 @@ const mount = createPlayUI(registryResult, {
|
|
|
224
242
|
},
|
|
225
243
|
});
|
|
226
244
|
|
|
227
|
-
|
|
228
|
-
|
|
245
|
+
const catalog = defineCatalog(schema, {
|
|
246
|
+
components: { Login: { props: z.object({ title: z.string() }) } },
|
|
247
|
+
actions: {},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Inside a ComponentFn:
|
|
251
|
+
const Login: ComponentFn<typeof catalog, "Login"> = (ctx) => {
|
|
229
252
|
const someValue = 42; // the value to validate, e.g. read from an input
|
|
230
253
|
const result = runValidationCheck(
|
|
231
254
|
{ type: "isEven", message: "must be even" },
|
|
232
|
-
{ value: someValue, stateModel: {}, customFunctions: ctx.ctx.validationFunctions },
|
|
255
|
+
{ value: someValue, stateModel: {}, customFunctions: ctx.ctx.validationFunctions ?? {} },
|
|
233
256
|
);
|
|
234
257
|
// result.valid, result.message
|
|
235
258
|
return null;
|
|
@@ -241,7 +264,13 @@ const Login: ComponentFn<typeof catalog, "Login"> = ({ ctx }) => {
|
|
|
241
264
|
The renderer calls this callback when an action binding resolves with `onSuccess: { navigate: "/path" }`. The callback receives the resolved path string as its only argument. Use it with every router:
|
|
242
265
|
|
|
243
266
|
```typescript
|
|
267
|
+
import { createPlayUI, type DefineRegistryResult } from "@xmachines/play-dom";
|
|
268
|
+
|
|
269
|
+
declare const registryResult: DefineRegistryResult; // from defineRegistry(catalog, ...)
|
|
270
|
+
|
|
244
271
|
// React Router / TanStack Router / any push-based router:
|
|
272
|
+
declare const myRouter: { push(path: string): void };
|
|
273
|
+
|
|
245
274
|
const mount = createPlayUI(registryResult, {
|
|
246
275
|
navigate: (path) => myRouter.push(path),
|
|
247
276
|
});
|
|
@@ -264,26 +293,53 @@ With a spec binding:
|
|
|
264
293
|
|
|
265
294
|
A component implementation can also read the function at `ctx.ctx.navigate`. Use this when a navigation must start outside an action binding.
|
|
266
295
|
|
|
296
|
+
### `onConfirm` and `confirmDialog` — the bindings with `confirm`
|
|
297
|
+
|
|
298
|
+
An action binding with a `confirm` field waits for the user. Without `onConfirm`, the default confirm dialog of `@xmachines/json-render-dom` shows the binding on `document.body`, as the `ConfirmDialog` of the framework renderers does. The action runs when the user confirms, and a cancel runs nothing and throws nothing. `disconnect()` and a new view cancel a confirmation that waits.
|
|
299
|
+
|
|
300
|
+
- Give `onConfirm` to decide each binding in your own code. It receives the confirm config and the resolved action, and it returns `true`, or a promise of `true`, to run the action.
|
|
301
|
+
- Give `confirmDialog: false` to turn the default dialog off. The binding then waits for a component of your catalog, which reads and answers it through `ctx.ctx.confirmation`.
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
import { createPlayUI, type DefineRegistryResult } from "@xmachines/play-dom";
|
|
305
|
+
|
|
306
|
+
declare const registryResult: DefineRegistryResult; // from defineRegistry(catalog, ...)
|
|
307
|
+
|
|
308
|
+
const mount = createPlayUI(registryResult, {
|
|
309
|
+
onConfirm: (confirm) => window.confirm(`${confirm.title}\n${confirm.message}`),
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
267
313
|
### `onRenderError` — unified error handler
|
|
268
314
|
|
|
269
|
-
The handler
|
|
315
|
+
The inner renderer calls the handler with `(error, name, info)` one time for each failure that it reports. `info.kind` names the failure:
|
|
316
|
+
|
|
317
|
+
- **`"element"`** — a `ComponentFn` below the root throws, or an effect of it throws. `name` is the catalog component name, for example `"Home"`. The element shows nothing until a new version of it arrives.
|
|
318
|
+
- **`"props"` and `"visible"`** — a prop expression or the `visible` condition of a child throws. The boundary of the parent shows nothing, and `name` is the type of the parent.
|
|
319
|
+
- **`"action"` and `"watch"`** — an `on` chain or a `watch` chain fails, for example an `ActionFn` that throws or rejects. `name` is the catalog action name, for example `"submitForm"`. The chain stops, and the element stays.
|
|
320
|
+
- **`"root"`** — the component, a prop, or the `visible` condition of the root throws. No view stays on the screen, so `PlayRenderer` also contains the failure as a failure of the view: see [`onError`](#onerror-the-failure-of-a-complete-rebuild).
|
|
270
321
|
|
|
271
|
-
|
|
272
|
-
- **An action handler rejection on the emit path** — an `ActionFn` throws, or it returns a rejected promise, during `emit()`. `name` is then the catalog action name, for example `"submitForm"`.
|
|
273
|
-
- **An action handler rejection on the watch path** — an `ActionFn` rejects during a `watch` binding callback. `name` is then the catalog action name.
|
|
322
|
+
`info.key` and `info.type` name the element, and `info.action` names the action of a chain. A handler with the two parameters `(error, name)` stays valid.
|
|
274
323
|
|
|
275
324
|
```typescript
|
|
325
|
+
import { createPlayUI, type DefineRegistryResult } from "@xmachines/play-dom";
|
|
326
|
+
|
|
327
|
+
declare const registryResult: DefineRegistryResult; // from defineRegistry(catalog, ...)
|
|
328
|
+
|
|
329
|
+
// Sentry: your error tracking client
|
|
330
|
+
declare const Sentry: { captureException(error: unknown, context?: object): void };
|
|
331
|
+
|
|
276
332
|
const mount = createPlayUI(registryResult, {
|
|
277
|
-
onRenderError: (err, name) => {
|
|
333
|
+
onRenderError: (err, name, info) => {
|
|
278
334
|
// Route to your application's error tracking
|
|
279
|
-
Sentry.captureException(err, { extra: { name } });
|
|
335
|
+
Sentry.captureException(err, { extra: { name, kind: info.kind, key: info.key } });
|
|
280
336
|
},
|
|
281
337
|
});
|
|
282
338
|
```
|
|
283
339
|
|
|
284
|
-
The
|
|
340
|
+
The type is `OnRenderError` of `@xmachines/json-render-core`, and this package exports it with `RenderErrorInfo` and `RenderErrorKind`. The framework renderers use the same handler, and they contain a failure of the root in the same way.
|
|
285
341
|
|
|
286
|
-
Without `onRenderError`, the renderer
|
|
342
|
+
Without `onRenderError`, the renderer reads the handler that `defineRegistry(catalog, { onRenderError })` put on the registry. Without that handler too, it writes one `console.error` line for each failure, such as `[json-render] element error in <Home> "home":`, and then the error. No exception goes to the caller, and no promise rejection stays unhandled. A failure of the root gets no such line: the containment reports it to `onError`, or to its own console line. A handler that throws gives its error to `reportError` of the page one time.
|
|
287
343
|
|
|
288
344
|
### The renderer contains a failed rebuild
|
|
289
345
|
|
|
@@ -301,6 +357,11 @@ a framework is not an option that a caller turns off. play-dom follows the same
|
|
|
301
357
|
|
|
302
358
|
```typescript
|
|
303
359
|
import { rethrowLater } from "@xmachines/core/utils";
|
|
360
|
+
import { createPlayUI, type DefineRegistryResult } from "@xmachines/play-dom";
|
|
361
|
+
|
|
362
|
+
declare const registryResult: DefineRegistryResult; // from defineRegistry(catalog, ...)
|
|
363
|
+
|
|
364
|
+
declare function reportToSentry(error: unknown): void; // your error tracking
|
|
304
365
|
|
|
305
366
|
const mount = createPlayUI(registryResult, {
|
|
306
367
|
onError: (err) => {
|
|
@@ -314,13 +375,13 @@ A throw that leaves the handler itself reaches no caller. The renderer contains
|
|
|
314
375
|
writes it to `console.error`, exactly as the four framework renderers do. The five hold
|
|
315
376
|
this one rule, so a host learns it one time and writes the same handler for each of them.
|
|
316
377
|
|
|
317
|
-
### `onError
|
|
378
|
+
### `onError`: the failure of a complete rebuild
|
|
318
379
|
|
|
319
|
-
`onRenderError` covers one element: the inner renderer contains a render error of a component, and a rejection of an action handler, per element, and the
|
|
380
|
+
`onRenderError` covers one element: the inner renderer contains a render error of a component below the root, and a rejection of an action handler, per element, and the rest of the view stays. A failure of the ROOT leaves no view: its component throws, or a `$computed` function throws during the resolution of a prop of the root, for example. The resolution of the store of the view can fail too, when an accessor of `spec.state` throws.
|
|
320
381
|
|
|
321
|
-
`onError` receives such a failure. The renderer contains
|
|
382
|
+
`onError` receives such a failure, one time. A failure of the root reaches `onRenderError` too, with `info.kind` `"root"`, so a host that sends both handlers to one tool skips that kind in one of them. The renderer contains the failure with this option and without it: it resets the state of the failed rebuild, so that the next emission makes a complete render again, and it clears the container. `connect()` renders the first view synchronously, and a bad initial view does not make `connect()` throw.
|
|
322
383
|
|
|
323
|
-
A write of the store takes the same path. The renderer resolves the props of every element that the write touched
|
|
384
|
+
A write of the store takes the same path. The renderer shows a write at its commit point, a microtask after the write, and it resolves the props of every element that the write touched there. A `$computed` function of the root that throws on the new state fails the root. The renderer contains such a failure as well, and the action handler that wrote the state receives no exception.
|
|
324
385
|
|
|
325
386
|
The second parameter is the **reset**, for a retry that the host starts. It renders the view that the actor holds at the moment of the call, so a retry cannot rewind the screen to the view that failed. A reset that the host calls from inside the handler does nothing, because no input changed between the two attempts, and a reset after the provider goes away does nothing. The five renderers hold the same three rules.
|
|
326
387
|
|
|
@@ -329,6 +390,12 @@ A reset of `PlayRenderer` belongs to ONE connection. `connect()` starts a connec
|
|
|
329
390
|
A fourth rule holds in all five: each of them CONTAINS a handler that throws, and writes the throw to `console.error`. A host that must escalate raises the failure from a task of its own, as the section above shows.
|
|
330
391
|
|
|
331
392
|
```typescript
|
|
393
|
+
import { createPlayUI, type DefineRegistryResult } from "@xmachines/play-dom";
|
|
394
|
+
|
|
395
|
+
declare const registryResult: DefineRegistryResult; // from defineRegistry(catalog, ...)
|
|
396
|
+
|
|
397
|
+
declare function reportToSentry(error: unknown): void; // your error tracking
|
|
398
|
+
|
|
332
399
|
const mount = createPlayUI(registryResult, {
|
|
333
400
|
onError: (err) => reportToSentry(err),
|
|
334
401
|
fallback: document.getElementById("crashed")!,
|
|
@@ -343,16 +410,26 @@ const mount = createPlayUI(registryResult, {
|
|
|
343
410
|
|
|
344
411
|
**A `fallback` producer that throws is contained too — on a null view and after a failed rebuild alike — and it reports to `console.error` and NOT to `onError`.** After a failed rebuild the renderer calls the producer from inside the containment of the view, so a second trip through `onError` would report the failure of your fallback as a failure of the view. The container stays empty, and the renderer renders the next view that works.
|
|
345
412
|
|
|
346
|
-
A component implementation can also read the handler at `ctx.ctx.onRenderError`. A component therefore sends its own internal errors through the same channel:
|
|
413
|
+
A component implementation can also read the handler at `ctx.ctx.onRenderError`. A component therefore sends its own internal errors through the same channel, with the `info` of the failure:
|
|
347
414
|
|
|
348
415
|
```typescript
|
|
349
|
-
|
|
350
|
-
|
|
416
|
+
import { schema, type ComponentFn } from "@xmachines/play-dom";
|
|
417
|
+
import { defineCatalog } from "@xmachines/json-render-core";
|
|
418
|
+
import { z } from "zod";
|
|
419
|
+
|
|
420
|
+
const catalog = defineCatalog(schema, {
|
|
421
|
+
components: { Home: { props: z.object({ title: z.string() }) } },
|
|
422
|
+
actions: {},
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
declare function doSomethingRisky(): HTMLElement; // your render logic that may throw
|
|
426
|
+
|
|
427
|
+
const Home: ComponentFn<typeof catalog, "Home"> = (ctx) => {
|
|
351
428
|
try {
|
|
352
429
|
const el = doSomethingRisky();
|
|
353
430
|
return el;
|
|
354
431
|
} catch (err) {
|
|
355
|
-
ctx.ctx.onRenderError?.(err, "Home");
|
|
432
|
+
ctx.ctx.onRenderError?.(err, "Home", { kind: "element", type: "Home" });
|
|
356
433
|
return null;
|
|
357
434
|
}
|
|
358
435
|
};
|
|
@@ -373,27 +450,29 @@ const Home: ComponentFn<typeof catalog, "Home"> = ({ ctx }) => {
|
|
|
373
450
|
|
|
374
451
|
### Key Types
|
|
375
452
|
|
|
376
|
-
| Type | Description
|
|
377
|
-
| ------------------------ |
|
|
378
|
-
| `ComponentFn<C, K>` | Catalog-typed component function — returns `HTMLElement \| Text \| null`
|
|
379
|
-
| `ComponentContext<C, K>` | The context of each component: `props`, `children`, `emit`, `on`, `bindings`, and `ctx`
|
|
380
|
-
| `ActionFn<C, K>` | Catalog-typed action function — receives `(params, setState, state)`
|
|
381
|
-
| `EventHandle` | The handle that `on(eventName)` returns. It has `emit()`, `shouldPreventDefault`, and `bound`
|
|
382
|
-
| `SetState` | State updater: `(prev => next) => void`
|
|
383
|
-
| `DefineRegistryResult` | The result of `defineRegistry`. It has `registry`, `handlers`, and `executeAction`
|
|
384
|
-
| `PlayDomOptions` | Options for `PlayRenderer` — extends `UIProviderOptions`, adds `fallback` and `onError`
|
|
385
|
-
| `CreatePlayUIOptions` | Options for `createPlayUI` — extends `UIProviderOptions`, adds `fallback` and `onError`
|
|
386
|
-
| `MountOptions` | Per-mount options for `MountFn`: `store`, `loading`
|
|
387
|
-
| `MountFn` | The mount: `(actor, container, options?) → () => void`. `createPlayUI` returns one, and a consumer may write one
|
|
388
|
-
| `UIProviderOptions` | Shared options: `functions`, `validationFunctions`, `navigate`, `onRenderError`
|
|
389
|
-
| `
|
|
390
|
-
| `
|
|
391
|
-
| `
|
|
453
|
+
| Type | Description |
|
|
454
|
+
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
|
|
455
|
+
| `ComponentFn<C, K>` | Catalog-typed component function — returns `HTMLElement \| Text \| null` |
|
|
456
|
+
| `ComponentContext<C, K>` | The context of each component: `props`, `children`, `emit`, `on`, `bindings`, and `ctx` |
|
|
457
|
+
| `ActionFn<C, K>` | Catalog-typed action function — receives `(params, setState, state)` |
|
|
458
|
+
| `EventHandle` | The handle that `on(eventName)` returns. It has `emit()`, `shouldPreventDefault`, and `bound` |
|
|
459
|
+
| `SetState` | State updater: `(prev => next) => void` |
|
|
460
|
+
| `DefineRegistryResult` | The result of `defineRegistry`. It has `registry`, `handlers`, and `executeAction` |
|
|
461
|
+
| `PlayDomOptions` | Options for `PlayRenderer` — extends `UIProviderOptions`, adds `fallback` and `onError` |
|
|
462
|
+
| `CreatePlayUIOptions` | Options for `createPlayUI` — extends `UIProviderOptions`, adds `fallback` and `onError` |
|
|
463
|
+
| `MountOptions` | Per-mount options for `MountFn`: `store`, `loading` |
|
|
464
|
+
| `MountFn` | The mount: `(actor, container, options?) → () => void`. `createPlayUI` returns one, and a consumer may write one |
|
|
465
|
+
| `UIProviderOptions` | Shared options: `functions`, `directives`, `validationFunctions`, `navigate`, `onRenderError`, `onConfirm`, `confirmDialog` |
|
|
466
|
+
| `OnRenderError` | The `(error, name, info)` handler of `onRenderError`. `RenderErrorInfo` and `RenderErrorKind` type its `info` |
|
|
467
|
+
| `BaseComponentProps<P>` | Catalog-agnostic component props for shared component libraries |
|
|
468
|
+
| `DomRegistry` | Raw registry type: `Record<string, DomComponentRenderer>` |
|
|
469
|
+
| `DomSchema` | Type of the `schema` export |
|
|
392
470
|
|
|
393
471
|
## Rendering Behavior
|
|
394
472
|
|
|
395
473
|
- **The first render is synchronous** — the renderer fills the container before `connect()` returns.
|
|
396
474
|
- **An atom-driven render waits for a microtask** — `watchAtom` puts each update on the next tick of the microtask queue.
|
|
397
475
|
- **A null view** clears the container, and the renderer then shows the `fallback` element when you give one. It shows it for every null view, and not for the first mount only — the four framework providers hold the same rule for their placeholder content.
|
|
476
|
+
- **A store write shows at its commit point** — the inner renderer renders the writes of one task together, in a microtask after the first write. Code that reads the DOM right after a write waits for that microtask.
|
|
398
477
|
- **A second `connect()` is safe** — a `connect()` call on a connected renderer disconnects it first.
|
|
399
|
-
- **`disconnect()` clears the container** and cancels every atom watcher and store watcher.
|
|
478
|
+
- **`disconnect()` clears the container** and cancels every atom watcher and store watcher. It also stops a store write that waits for its commit point, and it cancels a confirmation that waits.
|
package/dist/PlayRenderer.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PlayRenderer.ts — the XMachines wrapper of @xmachines/json-render-dom.
|
|
3
3
|
*
|
|
4
|
-
* It connects actor.currentView, an atom, to the DOM renderer below it,
|
|
5
|
-
*
|
|
4
|
+
* It connects actor.currentView, an atom, to the DOM renderer below it, through the
|
|
5
|
+
* view session of @xmachines/play-view. It gives you the connect() method and the disconnect() method, in
|
|
6
6
|
* the same pattern as the PlayRenderer class of play-react, of play-vue, and of
|
|
7
7
|
* play-solid.
|
|
8
8
|
*
|
|
@@ -26,7 +26,7 @@ import type { PlayDomOptions } from "./types.js";
|
|
|
26
26
|
* - `directives` — your own dynamic values with a `$` prefix, from `defineDirective`. The renderer resolves them with the props
|
|
27
27
|
* - `validationFunctions` — your own check functions. They are available at `ctx.ctx.validationFunctions`
|
|
28
28
|
* - `navigate` — the navigation callback. The renderer calls it for an `onSuccess: { navigate: "..." }` action binding
|
|
29
|
-
* - `onRenderError` — the renderer calls it with `(error, name)` for
|
|
29
|
+
* - `onRenderError` — the renderer calls it with `(error, name, info)` for each failure that the inner renderer reports
|
|
30
30
|
*
|
|
31
31
|
* **The preferred use, with `registryResult`:**
|
|
32
32
|
* ```typescript
|
|
@@ -84,18 +84,16 @@ export declare class PlayRenderer {
|
|
|
84
84
|
*/
|
|
85
85
|
private watchCleanups;
|
|
86
86
|
/**
|
|
87
|
-
* The
|
|
88
|
-
*
|
|
89
|
-
* moved
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* not a new view: the DOM goes away, but the
|
|
93
|
-
* to the same view therefore builds again around the ephemeral
|
|
94
|
-
* in the same way as the framework providers.
|
|
95
|
-
* because a store that stays alive must not outlive the connection.
|
|
87
|
+
* The generation of the store behind the tree on the screen, from the lifecycle. A
|
|
88
|
+
* resolution with the same generation keeps the store: only the /context projection
|
|
89
|
+
* moved, or a composed view changed its page, so the renderer keeps the tree and the
|
|
90
|
+
* ephemeral view state, such as a draft or a toggle. A new generation is a new store,
|
|
91
|
+
* and the renderer builds the tree again. A null emission, from a state with no
|
|
92
|
+
* meta.view, is a GAP, and not a new view: the DOM goes away, but the lifecycle keeps
|
|
93
|
+
* the store. A return to the same view therefore builds again around the ephemeral
|
|
94
|
+
* state that survived, in the same way as the framework providers.
|
|
96
95
|
*/
|
|
97
|
-
private
|
|
98
|
-
private currentStore;
|
|
96
|
+
private treeGeneration;
|
|
99
97
|
/**
|
|
100
98
|
* The incremental renderer of the live tree, and the `root` and `elements` of the
|
|
101
99
|
* spec that it renders. An emission with the same viewKey and the same two
|
|
@@ -110,14 +108,13 @@ export declare class PlayRenderer {
|
|
|
110
108
|
/** The fallback element, built at the first moment that the renderer shows it. */
|
|
111
109
|
private fallbackElement;
|
|
112
110
|
/**
|
|
113
|
-
* The
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* and not about the store.
|
|
111
|
+
* The view session of the connection, from @xmachines/play-view. It resolves the store
|
|
112
|
+
* of each view by the rules of the lifecycle — it seeds the store again on a change of
|
|
113
|
+
* the viewKey, and it refreshes /context in place in every other case — and it hands
|
|
114
|
+
* this renderer one snapshot for each attempt. Its generation decides whether the live
|
|
115
|
+
* tree stays. `connect()` makes one, and `disconnect()` drops it with its store.
|
|
119
116
|
*/
|
|
120
|
-
private
|
|
117
|
+
private session;
|
|
121
118
|
/**
|
|
122
119
|
* `disconnect()` sets this field to `false` before it calls `storeUnsub()`.
|
|
123
120
|
* The `rerender` closure reads the flag at its start. Therefore a synchronous
|
|
@@ -125,6 +122,17 @@ export declare class PlayRenderer {
|
|
|
125
122
|
* call, which is rare but correct, changes no detached DOM tree.
|
|
126
123
|
*/
|
|
127
124
|
private alive;
|
|
125
|
+
/**
|
|
126
|
+
* The depth of the synchronous calls into the inner renderer: `render()`, `setSpec()`
|
|
127
|
+
* and `update()`. A failure of the root that the inner renderer reports inside such a
|
|
128
|
+
* call waits for the end of the call. See {@link PlayRenderer.failRoot}.
|
|
129
|
+
*/
|
|
130
|
+
private innerDepth;
|
|
131
|
+
/**
|
|
132
|
+
* The failure of the root that the inner renderer reported, until the renderer
|
|
133
|
+
* contains it, and the inner renderer that reported it.
|
|
134
|
+
*/
|
|
135
|
+
private rootFailure;
|
|
128
136
|
/**
|
|
129
137
|
* The report guard, from @xmachines/play-view.
|
|
130
138
|
*
|
|
@@ -156,8 +164,8 @@ export declare class PlayRenderer {
|
|
|
156
164
|
* - `directives` — your own dynamic values with a `$` prefix. The renderer resolves them with the props.
|
|
157
165
|
* - `validationFunctions` — your own check functions. They are available at `ctx.ctx.validationFunctions`.
|
|
158
166
|
* - `navigate` — the navigation callback. The renderer calls it for `onSuccess: { navigate: "..." }`.
|
|
159
|
-
* - `onRenderError` — the `(error, name)` handler of
|
|
160
|
-
* - `onError` — the `(error, reset)` handler of a failure of a complete rebuild. The renderer contains such a failure always, and this option says where the report goes.
|
|
167
|
+
* - `onRenderError` — the `(error, name, info)` handler of each failure that the inner renderer reports. It stops the `[json-render]` console line.
|
|
168
|
+
* - `onError` — the `(error, reset)` handler of a failure of the view: a failure of the root, or of a complete rebuild. The renderer contains such a failure always, and this option says where the report goes.
|
|
161
169
|
* - `fallback` — the element to show for every null view, and after a contained failure.
|
|
162
170
|
*/
|
|
163
171
|
constructor(container: HTMLElement, actor: ViewActor, registry: DomRegistry, options?: PlayDomOptions);
|
|
@@ -166,7 +174,7 @@ export declare class PlayRenderer {
|
|
|
166
174
|
* It renders the first view synchronously, then it subscribes to the atom changes.
|
|
167
175
|
*
|
|
168
176
|
* A `connect()` call on a connected renderer, where a `disconnect()` call did not
|
|
169
|
-
* follow the previous `connect()`, installs a second
|
|
177
|
+
* follow the previous `connect()`, installs a second subscription of the view.
|
|
170
178
|
* Each view change then renders two times. Therefore this method disconnects the
|
|
171
179
|
* renderer first.
|
|
172
180
|
*/
|
|
@@ -175,6 +183,20 @@ export declare class PlayRenderer {
|
|
|
175
183
|
* Stops the watch and clears the container.
|
|
176
184
|
*/
|
|
177
185
|
disconnect(): void;
|
|
186
|
+
/**
|
|
187
|
+
* Releases the store subscription and the watch subscriptions of the tree.
|
|
188
|
+
*
|
|
189
|
+
* It captures both and clears both BEFORE a release. Some store implementations fire a
|
|
190
|
+
* synchronous callback during their unsubscribe(). Without this order, that callback
|
|
191
|
+
* sees a live storeUnsubscribe, and it enters the rerender path again with an old view
|
|
192
|
+
* and an old store, or it overwrites watchCleanups before the loop below reaches it.
|
|
193
|
+
*
|
|
194
|
+
* See reportReleaseFailure: a throw of one release must skip none of the work below it.
|
|
195
|
+
* This is code of a caller, and it also runs on the path of a view that WORKS.
|
|
196
|
+
*/
|
|
197
|
+
private releaseTree;
|
|
198
|
+
/** Forgets the tree on the screen, so that the next emission builds it again. */
|
|
199
|
+
private forgetTree;
|
|
178
200
|
/**
|
|
179
201
|
* The number of live watch-subscription cleanup functions now.
|
|
180
202
|
*
|
|
@@ -183,19 +205,90 @@ export declare class PlayRenderer {
|
|
|
183
205
|
* leaks. It is not part of the public API, and it can change without a notice.
|
|
184
206
|
*/
|
|
185
207
|
get watchCleanupCount(): number;
|
|
208
|
+
/**
|
|
209
|
+
* Brings the screen in line with one snapshot of the view session.
|
|
210
|
+
*
|
|
211
|
+
* The session resolved the store already: it refreshed /context in place, or it seeded
|
|
212
|
+
* a new store, or it recorded the failure of `spec.state`. This method decides the
|
|
213
|
+
* tree alone.
|
|
214
|
+
*/
|
|
186
215
|
private render;
|
|
216
|
+
/**
|
|
217
|
+
* Takes the tree of the previous view off the screen, before a rebuild.
|
|
218
|
+
*
|
|
219
|
+
* The watch subscriptions of the previous view must go too. Without this step, they
|
|
220
|
+
* stay active between a view transition and the first store update of the new view.
|
|
221
|
+
* They then hold a reference to a DOM element that is gone, and the garbage collector
|
|
222
|
+
* cannot take it.
|
|
223
|
+
*/
|
|
224
|
+
private clearTree;
|
|
187
225
|
/**
|
|
188
226
|
* The end of a render that failed, in ONE place.
|
|
189
227
|
*
|
|
190
228
|
* Three paths render — the fast path of a slice, the complete rebuild, and the write
|
|
191
|
-
* of the store — and each one ends here
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
229
|
+
* of the store — and each one ends here: through a throw, or through a failure of the
|
|
230
|
+
* root that the inner renderer reports (see {@link PlayRenderer.failRoot}). The ORDER
|
|
231
|
+
* is load-bearing: the reset takes the registrations of the dead tree down BEFORE the
|
|
232
|
+
* report, so a handler of the host that renders again meets a renderer that holds
|
|
233
|
+
* nothing of the failed attempt. A second copy of the pair is a second chance to put
|
|
234
|
+
* the two in the other order.
|
|
195
235
|
*
|
|
196
236
|
* @param error - The failure that the renderer contains.
|
|
197
237
|
*/
|
|
198
238
|
private failView;
|
|
239
|
+
/**
|
|
240
|
+
* Gives a failure that the inner renderer reports to the handler of the host, and
|
|
241
|
+
* contains a failure of the ROOT as a failure of the view.
|
|
242
|
+
*
|
|
243
|
+
* The inner renderer reports each failure one time, with `(error, name, info)`. This
|
|
244
|
+
* method gives each one to `forwardRenderError` of @xmachines/play-view, which holds
|
|
245
|
+
* the rule of the five renderers: the `onRenderError` of the host, or the handler of the
|
|
246
|
+
* registry, or the one `[json-render]` console line.
|
|
247
|
+
*
|
|
248
|
+
* A failure of the root — its component, a prop, or its `visible` condition — leaves
|
|
249
|
+
* no view on the screen. The inner renderer throws it to no caller, so the renderer
|
|
250
|
+
* contains it here, as it contains a complete rebuild that throws: it shows the
|
|
251
|
+
* fallback, and it reports the failure to `onError` one time. A handler of the host
|
|
252
|
+
* also gets it, with `info.kind` `"root"`. Without such a handler, the method writes
|
|
253
|
+
* no `[json-render]` line for it: the containment reports it to `onError`, or to its
|
|
254
|
+
* own console line, and a second line would report one failure two times.
|
|
255
|
+
*
|
|
256
|
+
* @param owner - The inner renderer that this handler belongs to, or `null` before it exists.
|
|
257
|
+
*/
|
|
258
|
+
private reportInner;
|
|
259
|
+
/**
|
|
260
|
+
* Contains a failure of the root that the inner renderer reported.
|
|
261
|
+
*
|
|
262
|
+
* The report comes in the middle of the work of the inner renderer, which writes the
|
|
263
|
+
* container after it. The containment therefore waits for the end of that work. Inside
|
|
264
|
+
* a synchronous call, {@link PlayRenderer.callInner} contains the failure when the call
|
|
265
|
+
* returns. At the commit point of a store write, which no call of this class holds, a
|
|
266
|
+
* microtask contains it.
|
|
267
|
+
*
|
|
268
|
+
* @param owner - The inner renderer that reported the failure.
|
|
269
|
+
* @param error - The failure of the root.
|
|
270
|
+
*/
|
|
271
|
+
private failRoot;
|
|
272
|
+
/**
|
|
273
|
+
* Hands the failure of the root that waits to the session, when it belongs to the
|
|
274
|
+
* live tree.
|
|
275
|
+
*
|
|
276
|
+
* The four framework providers take the same path: `failView` of the session records
|
|
277
|
+
* the failure in a new snapshot, and it gives the next attempt a new generation. The
|
|
278
|
+
* session then notifies this renderer, and `render()` contains the failed snapshot
|
|
279
|
+
* with {@link PlayRenderer.failView}: it clears the tree, shows the fallback, and
|
|
280
|
+
* reports to `onError` one time. The session has no `onFailure` here, so it reports
|
|
281
|
+
* nothing of its own.
|
|
282
|
+
*/
|
|
283
|
+
private settleRoot;
|
|
284
|
+
/**
|
|
285
|
+
* Runs one synchronous call into the inner renderer, and then contains a failure of the
|
|
286
|
+
* root that the call reported.
|
|
287
|
+
*
|
|
288
|
+
* @param owner - The inner renderer of the call.
|
|
289
|
+
* @param call - The call: `render()`, `setSpec()` or `update()`.
|
|
290
|
+
*/
|
|
291
|
+
private callInner;
|
|
199
292
|
/**
|
|
200
293
|
* Clears the screen of the failed view, shows the fallback, and reports the failure.
|
|
201
294
|
*
|
|
@@ -260,10 +353,12 @@ export declare class PlayRenderer {
|
|
|
260
353
|
/**
|
|
261
354
|
* The tail of a complete rebuild, which can throw. See the catch block of render().
|
|
262
355
|
*
|
|
263
|
-
* @param
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
* only
|
|
356
|
+
* @param view - The view to render.
|
|
357
|
+
* @param guardedStore - The store of the view, from the session. Everything that the
|
|
358
|
+
* spec can write through, which includes $bindState, setState, and a chained set,
|
|
359
|
+
* receives the GUARDED store: /context is read-only to the spec, because the machine
|
|
360
|
+
* context changes through an event only.
|
|
361
|
+
* @param generation - The generation of that store, which the tree records.
|
|
267
362
|
*/
|
|
268
363
|
private renderView;
|
|
269
364
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlayRenderer.d.ts","sourceRoot":"","sources":["../src/PlayRenderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"PlayRenderer.d.ts","sourceRoot":"","sources":["../src/PlayRenderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,OAAO,KAAK,EAEX,SAAS,EAGT,MAAM,sBAAsB,CAAC;AAK9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAsDjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,qBAAa,YAAY;IAkHvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IApHzB;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,gBAAgB,CAA6B;IACrD;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa,CAAyB;IAC9C;;;;;;;;;OASG;IACH,OAAO,CAAC,cAAc,CAAiC;IACvD;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW,CAAuC;IAC1D,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,YAAY,CAA+C;IACnE,kFAAkF;IAClF,OAAO,CAAC,eAAe,CAA4B;IACnD;;;;;;OAMG;IACH,OAAO,CAAC,OAAO,CAA4B;IAC3C;;;;;OAKG;IACH,OAAO,CAAC,KAAK,CAAQ;IAErB;;;;OAIG;IACH,OAAO,CAAC,UAAU,CAAK;IAEvB;;;OAGG;IACH,OAAO,CAAC,WAAW,CAAkE;IAErF;;;;;;;;;OASG;IAGH,OAAO,CAAC,KAAK,CAAkD;IAE/D;;;;;;OAMG;IACH,OAAO,CAAC,KAAK,CAAwC;IAErD;;;;;;;;;;;;;;;OAeG;gBAEe,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,WAAW,EACrB,OAAO,GAAE,cAAmB;IAG9C;;;;;;;;OAQG;IACH,OAAO,IAAI,IAAI;IA2Bf;;OAEG;IACH,UAAU,IAAI,IAAI;IAgBlB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,WAAW;IAiBnB,iFAAiF;IACjF,OAAO,CAAC,UAAU;IAOlB;;;;;;OAMG;IACH,IAAI,iBAAiB,IAAI,MAAM,CAE9B;IAED;;;;;;OAMG;IACH,OAAO,CAAC,MAAM;IAiEd;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS;IAUjB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,QAAQ;IAKhB;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,WAAW;IAgBnB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,QAAQ;IAOhB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,UAAU;IAOlB;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;IAUjB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,OAAO;IAiBf;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,OAAO;IAOf;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IA4BpB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;;;;;;;;OASG;IACH,OAAO,CAAC,UAAU;CAuHlB"}
|