sygnal 7.0.0 → 7.2.0-preview.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/CHANGELOG.md +632 -0
- package/dist/agent-ui.cjs.js +592 -0
- package/dist/agent-ui.cjs.js.map +1 -0
- package/dist/agent-ui.esm.js +582 -0
- package/dist/agent-ui.esm.js.map +1 -0
- package/dist/api.cjs.js +63 -0
- package/dist/api.cjs.js.map +1 -0
- package/dist/api.esm.js +2 -0
- package/dist/api.esm.js.map +1 -0
- package/dist/astro/index.cjs.js +968 -4
- package/dist/astro/index.cjs.js.map +1 -1
- package/dist/astro/index.mjs +968 -4
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/server.cjs.js +73 -45
- package/dist/astro/server.cjs.js.map +1 -1
- package/dist/astro/server.mjs +73 -45
- package/dist/astro/server.mjs.map +1 -1
- package/dist/canvas.cjs.js +2919 -0
- package/dist/canvas.cjs.js.map +1 -0
- package/dist/canvas.esm.js +2866 -0
- package/dist/canvas.esm.js.map +1 -0
- package/dist/devtools.cjs.js +38 -6
- package/dist/devtools.cjs.js.map +1 -1
- package/dist/devtools.esm.js +38 -6
- package/dist/devtools.esm.js.map +1 -1
- package/dist/diagnostics.cjs.js +224 -20
- package/dist/diagnostics.cjs.js.map +1 -1
- package/dist/diagnostics.esm.js +224 -20
- package/dist/diagnostics.esm.js.map +1 -1
- package/dist/guide/accessibility.md +1 -1
- package/dist/guide/ai-compose.md +150 -0
- package/dist/guide/behaviors.md +2 -1
- package/dist/guide/collaborative-state.md +265 -0
- package/dist/guide/element-commands.md +36 -2
- package/dist/guide/forms-reference.md +5 -5
- package/dist/guide/forms.md +3 -2
- package/dist/guide/http.md +28 -1
- package/dist/guide/islands.md +240 -0
- package/dist/guide/recipes/code-editor.md +1 -1
- package/dist/guide/renderers/agent-ui.md +138 -0
- package/dist/guide/renderers/custom-targets.md +126 -0
- package/dist/guide/renderers/overview.md +120 -0
- package/dist/guide/renderers/react-native.md +128 -0
- package/dist/guide/renderers/terminal.md +306 -0
- package/dist/guide/renderers/three.md +332 -0
- package/dist/guide/renderers/write-once.md +137 -0
- package/dist/guide/resources.md +2 -0
- package/dist/guide/sandboxed-components.md +223 -0
- package/dist/guide/timers.md +32 -5
- package/dist/guide/typed-apis.md +442 -0
- package/dist/guide/ui/accordion.md +11 -0
- package/dist/guide/ui/combobox.md +112 -8
- package/dist/guide/ui/dialog.md +96 -20
- package/dist/guide/ui/menu.md +57 -5
- package/dist/guide/ui/overview.md +8 -4
- package/dist/guide/ui/popover.md +69 -9
- package/dist/guide/ui/select.md +54 -4
- package/dist/guide/ui/tabs.md +1 -1
- package/dist/guide/ui/tooltip.md +7 -5
- package/dist/guide/virtual-collections.md +3 -0
- package/dist/guide/widgets.md +90 -1
- package/dist/host.cjs.js +2181 -0
- package/dist/host.cjs.js.map +1 -0
- package/dist/host.esm.js +2146 -0
- package/dist/host.esm.js.map +1 -0
- package/dist/index.cjs.js +3948 -586
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +584 -44
- package/dist/index.esm.js +3918 -586
- package/dist/index.esm.js.map +1 -1
- package/dist/primitives.cjs.js +143 -0
- package/dist/primitives.cjs.js.map +1 -0
- package/dist/primitives.esm.js +133 -0
- package/dist/primitives.esm.js.map +1 -0
- package/dist/react-native.cjs.js +248 -0
- package/dist/react-native.cjs.js.map +1 -0
- package/dist/react-native.esm.js +244 -0
- package/dist/react-native.esm.js.map +1 -0
- package/dist/replay.cjs.js +39 -0
- package/dist/replay.cjs.js.map +1 -0
- package/dist/replay.esm.js +2 -0
- package/dist/replay.esm.js.map +1 -0
- package/dist/sandbox-build.cjs.js +140 -0
- package/dist/sandbox-build.cjs.js.map +1 -0
- package/dist/sandbox-build.esm.js +137 -0
- package/dist/sandbox-build.esm.js.map +1 -0
- package/dist/sandbox-kernel.esm.js +212 -0
- package/dist/sandbox-kernel.esm.js.map +1 -0
- package/dist/sandbox-worker.cjs.js +857 -0
- package/dist/sandbox-worker.cjs.js.map +1 -0
- package/dist/sandbox-worker.esm.js +854 -0
- package/dist/sandbox-worker.esm.js.map +1 -0
- package/dist/sandbox.cjs.js +1425 -0
- package/dist/sandbox.cjs.js.map +1 -0
- package/dist/sandbox.esm.js +1406 -0
- package/dist/sandbox.esm.js.map +1 -0
- package/dist/sygnal.min.js +1 -1
- package/dist/sygnal.min.js.map +1 -1
- package/dist/terminal.cjs.js +1314 -0
- package/dist/terminal.cjs.js.map +1 -0
- package/dist/terminal.esm.js +1304 -0
- package/dist/terminal.esm.js.map +1 -0
- package/dist/three.cjs.js +1111 -0
- package/dist/three.cjs.js.map +1 -0
- package/dist/three.esm.js +1079 -0
- package/dist/three.esm.js.map +1 -0
- package/dist/ui-combobox.cjs.js +256 -18
- package/dist/ui-combobox.cjs.js.map +1 -1
- package/dist/ui-combobox.esm.js +256 -18
- package/dist/ui-combobox.esm.js.map +1 -1
- package/dist/ui-menu.cjs.js +150 -16
- package/dist/ui-menu.cjs.js.map +1 -1
- package/dist/ui-menu.esm.js +151 -17
- package/dist/ui-menu.esm.js.map +1 -1
- package/dist/ui-select.cjs.js +95 -9
- package/dist/ui-select.cjs.js.map +1 -1
- package/dist/ui-select.esm.js +95 -9
- package/dist/ui-select.esm.js.map +1 -1
- package/dist/ui.cjs.js +679 -173
- package/dist/ui.cjs.js.map +1 -1
- package/dist/ui.esm.js +679 -175
- package/dist/ui.esm.js.map +1 -1
- package/dist/vike/config/package.json +1 -1
- package/dist/vite/plugin.cjs.js +968 -4
- package/dist/vite/plugin.cjs.js.map +1 -1
- package/dist/vite/plugin.mjs +968 -4
- package/dist/vite/plugin.mjs.map +1 -1
- package/dist/worker-page.cjs.js +669 -0
- package/dist/worker-page.cjs.js.map +1 -0
- package/dist/worker-page.esm.js +665 -0
- package/dist/worker-page.esm.js.map +1 -0
- package/dist/worker.cjs.js +377 -0
- package/dist/worker.cjs.js.map +1 -0
- package/dist/worker.esm.js +374 -0
- package/dist/worker.esm.js.map +1 -0
- package/dist/yjs-testing.cjs.js +98 -0
- package/dist/yjs-testing.cjs.js.map +1 -0
- package/dist/yjs-testing.esm.js +77 -0
- package/dist/yjs-testing.esm.js.map +1 -0
- package/dist/yjs.cjs.js +923 -0
- package/dist/yjs.cjs.js.map +1 -0
- package/dist/yjs.esm.js +900 -0
- package/dist/yjs.esm.js.map +1 -0
- package/dist/zag.cjs.js +11 -1
- package/dist/zag.cjs.js.map +1 -1
- package/dist/zag.esm.js +11 -1
- package/dist/zag.esm.js.map +1 -1
- package/llms/ai.md +65 -0
- package/llms/collaboration.md +5 -0
- package/llms/graphics.md +10 -0
- package/llms/renderers.md +7 -0
- package/llms/replay.md +5 -0
- package/llms/sandbox.md +5 -0
- package/llms/typed-apis.md +5 -0
- package/llms/ui.md +10 -0
- package/llms-full.txt +444 -0
- package/llms.txt +37 -35
- package/package.json +139 -3
- package/src/agent-ui/mcp.ts +152 -0
- package/src/agent-ui/snapshot.ts +212 -0
- package/src/agent-ui.d.ts +106 -0
- package/src/agent-ui.ts +249 -0
- package/src/ai.d.ts +95 -2
- package/src/api.d.ts +303 -0
- package/src/api.ts +9 -0
- package/src/canvas/Canvas.ts +585 -0
- package/src/canvas/backend.ts +627 -0
- package/src/canvas/geometry.ts +187 -0
- package/src/canvas/gesture.ts +23 -0
- package/src/canvas/image.ts +197 -0
- package/src/canvas/kit.ts +130 -0
- package/src/canvas/paint.ts +260 -0
- package/src/canvas/pointer.ts +25 -0
- package/src/canvas/snap.ts +68 -0
- package/src/canvas/svg.ts +99 -0
- package/src/canvas.d.ts +373 -0
- package/src/canvas.ts +46 -0
- package/src/core/actions.ts +14 -3
- package/src/core/cell.ts +12 -4
- package/src/core/debug.ts +8 -3
- package/src/core/hooks.ts +16 -2
- package/src/core/hosts/collection.ts +1 -1
- package/src/core/hosts/switchable.ts +26 -8
- package/src/core/instance.ts +98 -45
- package/src/core/markers/portal.ts +4 -0
- package/src/core/registry.ts +4 -0
- package/src/core/runtime.ts +34 -25
- package/src/core/statics.ts +5 -5
- package/src/cycle/dom/EventDelegator.ts +37 -33
- package/src/cycle/dom/MainDOMSource.ts +16 -14
- package/src/cycle/dom/enrichEventStream.ts +13 -9
- package/src/cycle/dom/fromEvent.ts +6 -10
- package/src/cycle/dom/index.ts +11 -7
- package/src/cycle/dom/isolate.ts +13 -6
- package/src/cycle/dom/makeDOMDriver.ts +6 -2
- package/src/cycle/dom/utils.ts +9 -2
- package/src/cycle/run/adapt.ts +4 -12
- package/src/devtools.d.ts +9 -0
- package/src/devtools.ts +2 -1
- package/src/extra/ai/compose/catalog.ts +2 -1
- package/src/extra/ai/compose/decisions.ts +1 -2
- package/src/extra/ai/compose/generated.ts +234 -0
- package/src/extra/ai/compose/index.ts +6 -2
- package/src/extra/ai/compose/layout.ts +3 -1
- package/src/extra/ai/compose/llm.ts +7 -3
- package/src/extra/ai/compose/types.ts +2 -2
- package/src/extra/ai/transports/shared.ts +3 -1
- package/src/extra/api/contract.ts +485 -0
- package/src/extra/api/index.ts +26 -0
- package/src/extra/behaviors.ts +3 -3
- package/src/extra/controlled.ts +44 -0
- package/src/extra/copyAsTest.ts +22 -0
- package/src/extra/devtools.ts +5 -3
- package/src/extra/devtoolsActions.ts +8 -0
- package/src/extra/devtoolsNext.ts +1 -1
- package/src/extra/diagnostics/checks/actionLog.ts +8 -3
- package/src/extra/diagnostics/checks/behaviors.ts +25 -0
- package/src/extra/diagnostics/checks/elementCommands.ts +17 -5
- package/src/extra/diagnostics/checks/index.ts +1 -1
- package/src/extra/diagnostics/checks/next.ts +78 -4
- package/src/extra/diagnostics/checks/props.ts +6 -1
- package/src/extra/diagnostics/checks/replies.ts +11 -7
- package/src/extra/diagnostics/checks/timers.ts +8 -2
- package/src/extra/diagnostics/checks/virtual.ts +11 -0
- package/src/extra/diagnostics/codes.ts +51 -1
- package/src/extra/elementCommands.ts +20 -4
- package/src/extra/fetchDriver.ts +4 -1
- package/src/extra/fetchEventSource.ts +6 -2
- package/src/extra/field.ts +29 -0
- package/src/extra/form.ts +42 -16
- package/src/extra/frame.ts +54 -0
- package/src/extra/keymap.ts +41 -0
- package/src/extra/pager.ts +2 -0
- package/src/extra/persist.ts +2 -1
- package/src/extra/pointer.ts +262 -0
- package/src/extra/processForm.ts +34 -1
- package/src/extra/replay/address.ts +49 -0
- package/src/extra/replay/entropy.ts +99 -0
- package/src/extra/replay/format.ts +263 -0
- package/src/extra/replay/index.ts +8 -0
- package/src/extra/replay/inert.ts +22 -0
- package/src/extra/replay/recorder.ts +247 -0
- package/src/extra/replay/replayer.ts +234 -0
- package/src/extra/replay/report.ts +126 -0
- package/src/extra/replay/schema.ts +52 -0
- package/src/extra/replay/version.ts +3 -0
- package/src/extra/router.ts +7 -3
- package/src/extra/selection.ts +2 -0
- package/src/extra/socketDriver.ts +51 -6
- package/src/extra/ssr.ts +128 -42
- package/src/extra/testing.ts +295 -39
- package/src/extra/timers.ts +96 -7
- package/src/extra/undo.ts +2 -0
- package/src/extra/virtual.ts +25 -2
- package/src/extra/widget.ts +106 -17
- package/src/host/actions.ts +383 -0
- package/src/host/delegation.ts +101 -0
- package/src/host/dom.ts +553 -0
- package/src/host/driver.ts +185 -0
- package/src/host/selector.ts +119 -0
- package/src/host/style.ts +51 -0
- package/src/host.d.ts +298 -0
- package/src/host.ts +46 -0
- package/src/index.d.ts +302 -38
- package/src/index.ts +15 -3
- package/src/pragma/blocks.ts +211 -0
- package/src/pragma/index.ts +44 -10
- package/src/primitives.d.ts +54 -0
- package/src/primitives.ts +136 -0
- package/src/react-native.d.ts +57 -0
- package/src/react-native.ts +285 -0
- package/src/react-peers.d.ts +21 -3
- package/src/replay.d.ts +185 -0
- package/src/replay.ts +6 -0
- package/src/sandbox/build.ts +119 -0
- package/src/sandbox/create.ts +187 -0
- package/src/sandbox/engine.ts +267 -0
- package/src/sandbox/generated.ts +195 -0
- package/src/sandbox/kernel.ts +180 -0
- package/src/sandbox/mirror.ts +269 -0
- package/src/sandbox/protocol.ts +128 -0
- package/src/sandbox/quickjs.ts +74 -0
- package/src/sandbox/ssr.ts +41 -0
- package/src/sandbox/widget.ts +60 -0
- package/src/sandbox/worker-entry.ts +59 -0
- package/src/sandbox/worker.ts +137 -0
- package/src/sandbox-build.d.ts +12 -0
- package/src/sandbox-build.ts +6 -0
- package/src/sandbox.d.ts +299 -0
- package/src/sandbox.ts +25 -0
- package/src/shared.ts +12 -0
- package/src/terminal/paint.ts +712 -0
- package/src/terminal.d.ts +167 -0
- package/src/terminal.ts +530 -0
- package/src/three/assets.ts +170 -0
- package/src/three/host.ts +36 -0
- package/src/three/scene.ts +217 -0
- package/src/three/target.ts +252 -0
- package/src/three.d.ts +221 -0
- package/src/three.ts +30 -0
- package/src/ui/accordion.ts +21 -7
- package/src/ui/dialog.ts +192 -90
- package/src/ui/disclosure.ts +2 -0
- package/src/ui/dom.ts +30 -0
- package/src/ui/layers.ts +132 -0
- package/src/ui/popover.ts +120 -21
- package/src/ui/shared.ts +98 -12
- package/src/ui/stack.ts +18 -0
- package/src/ui/tabs.ts +2 -0
- package/src/ui/toaster.ts +16 -15
- package/src/ui/tooltip.ts +24 -8
- package/src/ui/tooltips.ts +26 -14
- package/src/ui/zag/combobox.ts +180 -13
- package/src/ui/zag/menu.ts +97 -17
- package/src/ui/zag/select.ts +15 -4
- package/src/ui/zag/shared.ts +60 -8
- package/src/ui-combobox.d.ts +16 -1
- package/src/ui-menu.d.ts +22 -7
- package/src/ui-zag-types.d.ts +13 -5
- package/src/ui.d.ts +62 -7
- package/src/ui.ts +2 -2
- package/src/vite/api.ts +186 -0
- package/src/vite/compile.ts +280 -0
- package/src/vite/plugin.d.ts +34 -0
- package/src/vite/plugin.ts +78 -4
- package/src/vite/sandbox.ts +16 -0
- package/src/worker/page.ts +396 -0
- package/src/worker/protocol.ts +48 -0
- package/src/worker/target.ts +282 -0
- package/src/worker-page.d.ts +42 -0
- package/src/worker-page.ts +12 -0
- package/src/worker.d.ts +65 -0
- package/src/worker.ts +16 -0
- package/src/yjs/driver.ts +253 -0
- package/src/yjs/mapping.ts +389 -0
- package/src/yjs/shared.ts +138 -0
- package/src/yjs/testing.ts +91 -0
- package/src/yjs-testing.d.ts +34 -0
- package/src/yjs-testing.ts +7 -0
- package/src/yjs.d.ts +109 -0
- package/src/yjs.ts +18 -0
- package/src/zag.d.ts +2 -0
- package/src/zag.ts +8 -1
package/dist/guide/widgets.md
CHANGED
|
@@ -62,8 +62,10 @@ Task.model = {
|
|
|
62
62
|
| `hostProps` | More prop names to put on the host element. |
|
|
63
63
|
| `ownProps` | Prop names that stay off the host although they would go there (the widget applies them itself, e.g. `aria-label` on its own control). |
|
|
64
64
|
| `name` | A name for diagnostics (`'DatePicker'`). |
|
|
65
|
+
| `children`, `sync`, `focus`, `tags` | Declarative [children](#children-islands): the tag's JSX children (a scene's objects, a map's markers, a chart's series), rendered as elements the widget reads in `sync`. |
|
|
66
|
+
| `value` | Experimental: a [controlled value](#controlled-values-experimental) both the app and the user change (an editor's text). |
|
|
65
67
|
|
|
66
|
-
**The host is the widget's.** Sygnal renders the host element and never its content: the widget can add, move and remove elements inside it, and a re-render doesn't touch them. Children passed to the tag are ignored.
|
|
68
|
+
**The host is the widget's.** Sygnal renders the host element and never its content: the widget can add, move and remove elements inside it, and a re-render doesn't touch them. Children passed to the tag are ignored, unless the widget declares [`children`](#children-islands).
|
|
67
69
|
|
|
68
70
|
**Props.** Every prop the view passes reaches `mount` and `update` (except `key` and `ref`). The ones that describe the element also go on the host: `id`, `className`/`class`, `style`, `title`, `name`, `placeholder`, `role`, `tabindex`, `hidden`, `lang`, `dir`, `attrs`, `aria-*` and `data-*`, plus the names in `hostProps`. Other props (`value` above) are the widget's alone, so the widget, not Sygnal, decides what the input shows. Sygnal toggles only its own `className` tokens on the host, so the classes a library adds to it (flatpickr's `flatpickr-input`) stay when the `className` changes. A `ref` gets the host element.
|
|
69
71
|
|
|
@@ -130,6 +132,93 @@ export const Sparkline = defineWidget({
|
|
|
130
132
|
|
|
131
133
|
A failure during `mount` itself is thrown, not passed to `error` (it is [SYG660](https://sygnal.js.org/reference/errors/#syg660)); an `error` call after the widget unmounted does nothing.
|
|
132
134
|
|
|
135
|
+
## Children (islands)
|
|
136
|
+
|
|
137
|
+
Some widgets are made of parts the app thinks of as a list: the objects of a 3D scene, the markers of a map, the series of a chart, the shapes on a canvas. With `children: true`, the tag's JSX children are rendered by Sygnal, as ordinary elements, into a container inside the host. The widget reads them in `sync` and draws them its own way. Because they are elements of the app's own tree, everything works inside: components with their own intent and model, `<Collection>`, isolation, context, and `DOM.click('.marker')` in an intent.
|
|
138
|
+
|
|
139
|
+
<!-- docs-check: skip -->
|
|
140
|
+
```jsx
|
|
141
|
+
// the view: the stops are a Collection of components, each with its own intent
|
|
142
|
+
<RouteMap className="map" center={state.center}>
|
|
143
|
+
<Collection of={Stop} from="stops" />
|
|
144
|
+
</RouteMap>
|
|
145
|
+
|
|
146
|
+
// Stop.jsx
|
|
147
|
+
function Stop({ state }) {
|
|
148
|
+
return <mapMarker className="stop" position={[state.lat, state.lng]} role="button" tabIndex={0} aria-label={state.name} />
|
|
149
|
+
}
|
|
150
|
+
Stop.intent = ({ DOM }) => ({ OPEN: DOM.click('.stop') })
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
| Field | Meaning |
|
|
154
|
+
|-------|---------|
|
|
155
|
+
| `children` | `true`: the children's container is visually hidden but in the accessibility tree; `'hidden'`: hidden from everything. |
|
|
156
|
+
| `sync(instance, children, el)` | Called after `mount`, and after each patch in which the children changed. `children` is the container's vnode: each child's `sel` (its tag), its props as the view wrote them (`data.props`, and `data.attrs` for ARIA and SVG tags; arrays and functions stay as they are), its own `children`, and its element in `.elm`. Keep a `Map` from element to the library's object, as you would from a key. |
|
|
157
|
+
| `focus(instance, child, el)` | A child got keyboard focus (`null`: none has it). Draw a focus ring where the child is drawn. |
|
|
158
|
+
| `tags` | Tag names of your children's vocabulary that the JSX pragma takes for SVG (`marker`, `line`, `text`, `filter`, `mask`): they become plain elements app-wide, so their props stay props. Children of a literal `<svg>` stay SVG. |
|
|
159
|
+
|
|
160
|
+
**Events.** The widget turns its own input into DOM events on the child elements: a click on a map marker, a raycast hit in a 3D scene, a hit test on a canvas. `el.dispatchEvent(new MouseEvent('click', { bubbles: true }))` on the child's element reaches `DOM.click('.stop')` in that item's own intent, and bubbles to its ancestors, as a click on a button does. Put what the event knows on it (`Object.defineProperty(event, 'point', { value })`) so the model can read it.
|
|
161
|
+
|
|
162
|
+
**Accessibility.** The children are in the accessibility tree by default. A child with a `role`, an accessible name and `tabIndex={0}` is a control a screen reader announces and Tab reaches, though it is drawn on a canvas. Enter and Space on a focused child click it (a keyboard click carries no position), and `focus` tells the widget where to draw the ring. `children: 'hidden'` opts out, for decoration.
|
|
163
|
+
|
|
164
|
+
**When `sync` runs.** It runs when the children's vnodes changed: a patch elsewhere in the app doesn't call it. A child's component that didn't render again keeps its vnode, so `sync` can skip unchanged subtrees by comparing vnodes.
|
|
165
|
+
|
|
166
|
+
[Imperative islands](./islands.md) builds three widgets this way: a three.js scene with raycast events, a canvas 2D painter with hit testing, and a CodeMirror editor.
|
|
167
|
+
|
|
168
|
+
## Controlled values (experimental)
|
|
169
|
+
|
|
170
|
+
An editor's text, a camera, a map's viewport: the user changes it in the widget, the app keeps it in state and sometimes sets it (a reset, a loaded document, a clamp). Writing the prop into the widget on every change makes echo loops (the write fires the widget's change event) and loses keystrokes when the state is a step behind the user. With `value`, Sygnal decides when to write:
|
|
171
|
+
|
|
172
|
+
<!-- docs-check: skip -->
|
|
173
|
+
```jsx
|
|
174
|
+
export const Editor = defineWidget({
|
|
175
|
+
events: ['change'],
|
|
176
|
+
mount: (el, props, dispatch) => makeEditor(el, props.value, (text) => dispatch('change', text)),
|
|
177
|
+
value: {
|
|
178
|
+
write: (editor, text) => editor.replaceText(text), // only the app's own changes get here
|
|
179
|
+
read: (editor) => editor.text, // the host's value: .value() in intents
|
|
180
|
+
},
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
// <Editor className="notes" value={state.text} /> EDIT: DOM.select('.notes').events('change').value()
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
- The widget reports the user's changes with `dispatch('change', value)` (or `value.event`). Sygnal remembers them, so when the same value comes back as the `value` prop, it is not written: no loop, and a late echo while the user types on doesn't reset the field.
|
|
187
|
+
- `write` runs only when the app changed `value` to something the widget didn't report: a reset, a loaded document, a value the model clamped or transformed. A render for any other reason never writes.
|
|
188
|
+
- `strict` (in `value`, or the `strict` prop) also writes back an edit the model didn't take: when its `value` prop never moved (the reducer returned `ABORT`), the widget shows the state again after the app's turn.
|
|
189
|
+
- `equals(a, b)` compares values (default `Object.is`); give one for objects (a camera's position).
|
|
190
|
+
- `update` isn't called for a change of `value` alone.
|
|
191
|
+
|
|
192
|
+
`write` must not report back: apply it the way the library marks a programmatic change (CodeMirror: a transaction annotation the update listener skips; Leaflet: a flag around `setView`). This API may change before it is stable.
|
|
193
|
+
|
|
194
|
+
## Form fields
|
|
195
|
+
|
|
196
|
+
A widget that edits one value (a date picker, a color picker, a rich select) can be a form field: declare `field`, and give the tag a `name`. The [`form` behavior](./forms.md) then reads its value and its touched state from it, as it does for a native input, and [`processForm`](./forms-reference.md#processform) entries get its value:
|
|
197
|
+
|
|
198
|
+
<!-- docs-check: skip -->
|
|
199
|
+
```jsx
|
|
200
|
+
export const DatePicker = defineWidget({
|
|
201
|
+
tag: 'div',
|
|
202
|
+
events: ['pick'],
|
|
203
|
+
field: { event: 'pick', read: (fp) => fp.selectedDates[0] ?? null },
|
|
204
|
+
mount: (el, props, dispatch) => flatpickr(el, { inline: true, defaultDate: props.value, onChange: ([d]) => dispatch('pick', d ?? null) }),
|
|
205
|
+
update: (fp, props) => fp.setDate(props.value ?? null, false),
|
|
206
|
+
unmount: (fp) => fp.destroy(),
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
// <form><DatePicker className="due" name="due" value={state.form.values.due} /></form>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
- When the widget dispatches the field's event (`field.event`; default: `value.event`, else `'change'`), its host also fires a bubbling `input` event: the form behavior's change of the field `name`, with the event's detail as the value. An intent listening for the widget's own event still gets it.
|
|
213
|
+
- The library's own controls inside the host (flatpickr's text input) don't count: their `input` events are not changes of the form's fields, unless they have a `name` of their own.
|
|
214
|
+
- The field is touched when the focus leaves the host; moving between the widget's own parts doesn't count.
|
|
215
|
+
- `read(instance, host)` is the field's value between events: what processForm entries get (default: the last value dispatched, else the `value` prop). FormData has what the widget's own named elements hold (a hidden `<input name>` it renders for native submission).
|
|
216
|
+
- A widget with a `disabled` prop of `true` has no entry.
|
|
217
|
+
- `field: true` takes the defaults.
|
|
218
|
+
- In tests, `t.widget('.due').dispatch('pick', date)` reaches the form behavior in the mock DOM too.
|
|
219
|
+
|
|
220
|
+
The [Select](./ui/select.md) and [Combobox](./ui/combobox.md) parts are fields this way.
|
|
221
|
+
|
|
133
222
|
## Accessibility
|
|
134
223
|
|
|
135
224
|
The host is a real element in your markup, so the usual rules apply to it. An `input`-hosted widget needs a label, like any input: wrap it in a `<label>`, or give it an `aria-label` (sygnal-check reports an unlabelled one, [SYG702](https://sygnal.js.org/reference/errors/#syg702)). For a `div`-hosted widget, give the host a `role` and an accessible name when the library doesn't: `<Chart className="sales" role="img" aria-label="Sales by month" />`.
|