@texaryn/react 0.2.4 → 0.4.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +24 -2
  3. package/dist/components/ErrorSummary.d.ts +6 -0
  4. package/dist/components/ErrorSummary.d.ts.map +1 -1
  5. package/dist/components/ErrorSummary.js +9 -1
  6. package/dist/components/ErrorSummary.js.map +1 -1
  7. package/dist/components/FieldErrors.d.ts +6 -1
  8. package/dist/components/FieldErrors.d.ts.map +1 -1
  9. package/dist/components/FieldErrors.js +9 -5
  10. package/dist/components/FieldErrors.js.map +1 -1
  11. package/dist/components/FieldLabelContent.d.ts +22 -0
  12. package/dist/components/FieldLabelContent.d.ts.map +1 -0
  13. package/dist/components/FieldLabelContent.js +21 -0
  14. package/dist/components/FieldLabelContent.js.map +1 -0
  15. package/dist/context.d.ts +15 -1
  16. package/dist/context.d.ts.map +1 -1
  17. package/dist/context.js +15 -1
  18. package/dist/context.js.map +1 -1
  19. package/dist/hooks/use-array-actions.d.ts +15 -0
  20. package/dist/hooks/use-array-actions.d.ts.map +1 -0
  21. package/dist/hooks/use-array-actions.js +21 -0
  22. package/dist/hooks/use-array-actions.js.map +1 -0
  23. package/dist/hooks/use-field-binding.d.ts +8 -0
  24. package/dist/hooks/use-field-binding.d.ts.map +1 -1
  25. package/dist/hooks/use-field-binding.js +28 -7
  26. package/dist/hooks/use-field-binding.js.map +1 -1
  27. package/dist/hooks/use-object-group.d.ts +22 -0
  28. package/dist/hooks/use-object-group.d.ts.map +1 -0
  29. package/dist/hooks/use-object-group.js +18 -0
  30. package/dist/hooks/use-object-group.js.map +1 -0
  31. package/dist/id-prefix.d.ts +10 -0
  32. package/dist/id-prefix.d.ts.map +1 -0
  33. package/dist/id-prefix.js +31 -0
  34. package/dist/id-prefix.js.map +1 -0
  35. package/dist/index.d.ts +10 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +6 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/props/action-names.d.ts +19 -0
  40. package/dist/props/action-names.d.ts.map +1 -0
  41. package/dist/props/action-names.js +30 -0
  42. package/dist/props/action-names.js.map +1 -0
  43. package/dist/props/field-props.d.ts +24 -7
  44. package/dist/props/field-props.d.ts.map +1 -1
  45. package/dist/props/field-props.js +30 -14
  46. package/dist/props/field-props.js.map +1 -1
  47. package/dist/props/index.d.ts +1 -1
  48. package/dist/props/index.d.ts.map +1 -1
  49. package/dist/props/index.js +1 -1
  50. package/dist/props/index.js.map +1 -1
  51. package/dist/widgets/ArrayControl.d.ts.map +1 -1
  52. package/dist/widgets/ArrayControl.js +8 -2
  53. package/dist/widgets/ArrayControl.js.map +1 -1
  54. package/dist/widgets/Checkbox.d.ts.map +1 -1
  55. package/dist/widgets/Checkbox.js +2 -1
  56. package/dist/widgets/Checkbox.js.map +1 -1
  57. package/dist/widgets/NumberInput.d.ts.map +1 -1
  58. package/dist/widgets/NumberInput.js +2 -1
  59. package/dist/widgets/NumberInput.js.map +1 -1
  60. package/dist/widgets/ObjectLayout.d.ts.map +1 -1
  61. package/dist/widgets/ObjectLayout.js +12 -9
  62. package/dist/widgets/ObjectLayout.js.map +1 -1
  63. package/dist/widgets/Select.d.ts.map +1 -1
  64. package/dist/widgets/Select.js +2 -1
  65. package/dist/widgets/Select.js.map +1 -1
  66. package/dist/widgets/TextInput.d.ts.map +1 -1
  67. package/dist/widgets/TextInput.js +2 -1
  68. package/dist/widgets/TextInput.js.map +1 -1
  69. package/dist/widgets/Textarea.d.ts.map +1 -1
  70. package/dist/widgets/Textarea.js +2 -1
  71. package/dist/widgets/Textarea.js.map +1 -1
  72. package/package.json +4 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # @texaryn/react
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 04270e8: Give every array action control an accessible name that says which row it acts on.
8
+
9
+ Five buttons all announced as "Remove" leave a screen reader user guessing. Each now carries the current 1-based position and whatever schema titles exist, so "Remove Contact 2 from Contacts", degrading to "Remove item 2" when nothing is titled. Reorder controls read "Move up Contact 2 in Contacts", and the per-array control reads "Add item to Contacts". The visible words stay "Remove", "Up" and "Add", with the longer string in `aria-label`, which contains the visible text so speech input still works.
10
+
11
+ The row's own value is deliberately not used: it is mutable, often blank, frequently duplicated, and sometimes sensitive. The position is recomputed on every render rather than captured when a row is created, because a row survives a move while its position does not.
12
+
13
+ The add control is named from the item template rather than from the first row, so an empty array still says "Add Tag" where naming it matters most. That needed the template's annotations, which the projection did not carry: `NodeProjection.itemAnnotations` is new and optional, so an adapter that cannot supply it stays valid, and `ArrayMeta.itemTitle` carries the result to renderers.
14
+
15
+ React exposes the decision as `useArrayActions` so its three widget sets cannot drift, and the name builders are exported for custom widgets. Reorder-control parity is unchanged: the three React sets still expose no reorder button.
16
+ - c9b930f: Show a required field's requirement to a sighted user, without announcing it twice.
17
+
18
+ Required was conveyed only through `aria-required`, so a sighted user had nothing to tell a required field from an optional one. Every required label now carries a visible `(required)`.
19
+
20
+ Three channels carry that one fact and are deliberately kept separate. The visible label reads "Full Name (required)". The accessible name stays exactly "Full Name", because the indicator is `aria-hidden` and `aria-required` already reports the state; putting it in the name as well makes some screen readers say it twice. WCAG's Label in Name allows exactly this omission when the state is surfaced programmatically.
21
+
22
+ The indicator is the word rather than an asterisk, so nothing has to be explained elsewhere on the form. `field.label` and `fieldLabel()` still return the raw schema title: the decoration lives in the label rendering, so a visual convention does not become the canonical field title a custom widget sees. React exposes `FieldLabelContent` so its three widget sets cannot drift, and each binding exports `REQUIRED_INDICATOR` so a custom widget can match the wording.
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies [04270e8]
27
+ - @texaryn/core@0.7.0
28
+
29
+ ## 0.3.0
30
+
31
+ ### Minor Changes
32
+
33
+ - 3545378: Expose a titled nested object as a named group in React and Vue, closing the last gap the DOM accessibility contract declared.
34
+
35
+ A nested object rendered as a plain `div` gives no indication where one group of fields ends and the next begins. It is now a `fieldset` named by its `legend`, matching what `@texaryn/web-components` already did. The element is chosen once at mount and only the grouping semantics are re-derived, because a conditional subschema can add or drop a title on any recompile and swapping `div` for `fieldset` at that moment would remount the subtree and take the caret with it. An untitled nested object stays a `fieldset` but carries `role="none"`, since an unnamed group is noise in the accessibility tree.
36
+
37
+ The decision is shared through a new `useObjectGroup` hook, so the three React widget sets cannot drift apart on it. The root object is still a plain container: it is the form itself, not a group inside one.
38
+ - 3e53ca9: Keep each field's error container mounted as a live region instead of inserting it with the message already inside.
39
+
40
+ A region that appears together with its content is not reliably announced, which is what every binding did: React and Vue rendered nothing until an error existed, and Web Components kept the node but hid it, which takes it out of the accessibility tree just the same. The container now exists from mount, stays empty while the field is valid, and only its contents change.
41
+
42
+ It is `aria-live="polite" aria-atomic="true"` rather than `role="alert"`. This is a public semantic change: `ErrorProps` promised `role: 'alert'` and now carries the live-region attributes instead. Alert is assertive, and validation runs on change, blur and submit, so keeping it would have interrupted typing and spoken once per failing field on submit. `aria-describedby` stays conditional, pointing at the region only while it has something to say. `ErrorSummary` loses its `role="alert"` and keeps rendering conditionally, because the fields now announce their own errors and an aggregate region would repeat the same validation event.
43
+ - 4a161b2: Give every rendering surface its own DOM id namespace, so two forms on one page no longer share ids or reference each other's elements.
44
+
45
+ `FormProvider` becomes a real component rather than an alias for `FormContext.Provider`, and owns the namespace for everything under it, `ErrorSummary` included. Rendering through `FormContext.Provider` now throws, because the context alone carries no namespace. `makeId`, `getInputProps`, `getLabelProps`, `getErrorProps` and `getDescriptionProps` take the prefix explicitly; there is no unprefixed fallback, which would let a custom widget reintroduce the collision. `@texaryn/vue` takes the prefix from `provideFormRuntime` and now requires Vue 3.5 for `useId`. Generated ids are opaque relationship identifiers rather than styling hooks.
46
+ - e7c00ce: Render a schema's `readOnly` as read-only rather than disabled, and enforce it in the runtime.
47
+
48
+ A disabled control is not focusable and is announced differently; a read-only one stays focusable and selectable, which is what `readOnly` means. Compiled nodes now carry a resolved `readOnly` that a read-only object or array passes to everything beneath it, because editing a descendant changes the ancestor's value. The runtime refuses `SetValue`, `InsertItem`, `RemoveItem` and `MoveItem` on a read-only node, so a custom widget set cannot write past the restriction; `Reset` stays allowed as the owning authority replacing state. Renderers use the native `readonly` attribute where HTML has one and `aria-readonly` plus refusing the change where it does not, which is select and checkbox. `disabled` remains part of the node API, but the compiler no longer derives it from `readOnly`, so compiled nodes currently resolve it to false.
49
+
50
+ ### Patch Changes
51
+
52
+ - Updated dependencies [e7c00ce]
53
+ - @texaryn/core@0.6.0
54
+
3
55
  ## 0.2.4
4
56
 
5
57
  ### Patch Changes
package/README.md CHANGED
@@ -90,15 +90,25 @@ The runtime is destroyed automatically when the hook unmounts.
90
90
 
91
91
  ### `FormProvider`
92
92
 
93
- Provides a `FormRuntime` to renderer components and hooks:
93
+ Provides a `FormRuntime` to renderer components and hooks, and owns the DOM id
94
+ namespace for that rendering surface:
94
95
 
95
96
  ```tsx
96
97
  <FormProvider value={form.runtime}>
98
+ <ErrorSummary />
97
99
  <FormRoot registry={registry} />
98
100
  </FormProvider>
99
101
  ```
100
102
 
101
- `useFormContext()` returns the current runtime.
103
+ `useFormContext()` returns the current runtime and `useFormIdPrefix()` returns
104
+ the namespace. Rendering through `FormContext.Provider` instead throws: the
105
+ context alone carries no namespace, which is what let two forms on one page
106
+ share ids.
107
+
108
+ Generated ids are opaque relationship identifiers rather than styling hooks.
109
+ Their shape is not part of the public contract; use classes or data attributes
110
+ for CSS. Uniqueness is per React application, so two independent roots on one
111
+ page need distinct `identifierPrefix` options.
102
112
 
103
113
  ### `FormRoot`
104
114
 
@@ -257,6 +267,13 @@ The schema adapter interprets data semantics. The core compiles and runs the for
257
267
  - `useFieldArray`
258
268
  - `UseFieldArrayReturn`
259
269
  - `useFieldBinding`
270
+ - `useObjectGroup`
271
+ - `useArrayActions`
272
+ - `ArrayActions`
273
+ - `removeActionName`
274
+ - `moveUpActionName`
275
+ - `addActionName`
276
+ - `ObjectGroup`
260
277
  - `FieldBinding`
261
278
  - `DomValueInputProps`
262
279
  - `DomCheckedInputProps`
@@ -267,6 +284,8 @@ The schema adapter interprets data semantics. The core compiles and runs the for
267
284
 
268
285
  - `FormContext`
269
286
  - `FormProvider`
287
+ - `FormProviderProps`
288
+ - `useFormIdPrefix`
270
289
  - `FormRoot`
271
290
  - `FormRootProps`
272
291
  - `NodeRenderer`
@@ -278,6 +297,9 @@ The schema adapter interprets data semantics. The core compiles and runs the for
278
297
  ### Error display
279
298
 
280
299
  - `FieldErrors`
300
+ - `FieldLabelContent`
301
+ - `FieldLabelContentProps`
302
+ - `REQUIRED_INDICATOR`
281
303
  - `FieldErrorsProps`
282
304
  - `ErrorSummary`
283
305
 
@@ -1,2 +1,8 @@
1
+ /**
2
+ * Deliberately not a live region. The fields already announce their own
3
+ * errors, so an aggregate one would speak the same validation event twice.
4
+ * What this needs instead is to become a focus destination after a failed
5
+ * submit, which is separate work.
6
+ */
1
7
  export declare function ErrorSummary(): import("react").JSX.Element | null;
2
8
  //# sourceMappingURL=ErrorSummary.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorSummary.d.ts","sourceRoot":"","sources":["../../src/components/ErrorSummary.tsx"],"names":[],"mappings":"AAKA,wBAAgB,YAAY,uCAuB3B"}
1
+ {"version":3,"file":"ErrorSummary.d.ts","sourceRoot":"","sources":["../../src/components/ErrorSummary.tsx"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,wBAAgB,YAAY,uCAwB3B"}
@@ -1,13 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useFormContext } from '../context.js';
3
3
  import { useStore } from '../hooks/use-store.js';
4
+ import { useFormIdPrefix } from '../id-prefix.js';
4
5
  import { makeId } from '../props/field-props.js';
6
+ /**
7
+ * Deliberately not a live region. The fields already announce their own
8
+ * errors, so an aggregate one would speak the same validation event twice.
9
+ * What this needs instead is to become a focus destination after a failed
10
+ * submit, which is separate work.
11
+ */
5
12
  export function ErrorSummary() {
6
13
  const runtime = useFormContext();
14
+ const idPrefix = useFormIdPrefix();
7
15
  const visibleErrors = useStore(runtime.visibleErrors);
8
16
  if (visibleErrors.length === 0) {
9
17
  return null;
10
18
  }
11
- return (_jsx("div", { role: "alert", children: _jsx("ul", { children: visibleErrors.map((entry) => (_jsxs("li", { children: [_jsx("a", { href: `#${makeId(entry.nodeId, 'input')}`, children: entry.fieldTitle ?? entry.pointer ?? entry.nodeId }), ': ', entry.errors.map((error) => error.message ?? error.keyword).join(', ')] }, entry.nodeId))) }) }));
19
+ return (_jsx("div", { children: _jsx("ul", { children: visibleErrors.map((entry) => (_jsxs("li", { children: [_jsx("a", { href: `#${makeId(idPrefix, entry.nodeId, 'input')}`, children: entry.fieldTitle ?? entry.pointer ?? entry.nodeId }), ': ', entry.errors.map((error) => error.message ?? error.keyword).join(', ')] }, entry.nodeId))) }) }));
12
20
  }
13
21
  //# sourceMappingURL=ErrorSummary.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorSummary.js","sourceRoot":"","sources":["../../src/components/ErrorSummary.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAEhD,MAAM,UAAU,YAAY;IAC1B,MAAM,OAAO,GAAG,cAAc,EAAE,CAAA;IAChC,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;IAErD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,CACL,cAAK,IAAI,EAAC,OAAO,YACf,uBACG,aAAa,CAAC,GAAG,CAAC,CAAC,KAAmB,EAAE,EAAE,CAAC,CAC1C,yBACE,YAAG,IAAI,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YACzC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,GAChD,EACH,IAAI,EACJ,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KALhE,KAAK,CAAC,MAAM,CAMhB,CACN,CAAC,GACC,GACD,CACP,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"ErrorSummary.js","sourceRoot":"","sources":["../../src/components/ErrorSummary.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAEhD;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,OAAO,GAAG,cAAc,EAAE,CAAA;IAChC,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAA;IAClC,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;IAErD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,CACL,wBACE,uBACG,aAAa,CAAC,GAAG,CAAC,CAAC,KAAmB,EAAE,EAAE,CAAC,CAC1C,yBACE,YAAG,IAAI,EAAE,IAAI,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YACnD,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,GAChD,EACH,IAAI,EACJ,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KALhE,KAAK,CAAC,MAAM,CAMhB,CACN,CAAC,GACC,GACD,CACP,CAAA;AACH,CAAC"}
@@ -4,5 +4,10 @@ export interface FieldErrorsProps {
4
4
  errors: readonly ValidationError[];
5
5
  showErrors: boolean;
6
6
  }
7
- export declare function FieldErrors({ node, errors, showErrors }: FieldErrorsProps): import("react").JSX.Element | null;
7
+ /**
8
+ * Always rendered, and empty while there is nothing to say. Returning null
9
+ * until an error exists meant the region arrived already populated, which is
10
+ * the case screen readers do not reliably announce.
11
+ */
12
+ export declare function FieldErrors({ node, errors, showErrors }: FieldErrorsProps): import("react").JSX.Element;
8
13
  //# sourceMappingURL=FieldErrors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FieldErrors.d.ts","sourceRoot":"","sources":["../../src/components/FieldErrors.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAG/D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,SAAS,CAAA;IACf,MAAM,EAAE,SAAS,eAAe,EAAE,CAAA;IAClC,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,gBAAgB,sCAczE"}
1
+ {"version":3,"file":"FieldErrors.d.ts","sourceRoot":"","sources":["../../src/components/FieldErrors.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAI/D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,SAAS,CAAA;IACf,MAAM,EAAE,SAAS,eAAe,EAAE,CAAA;IAClC,UAAU,EAAE,OAAO,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,gBAAgB,+BAWzE"}
@@ -1,10 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getErrorProps } from '../props/index.js';
3
+ import { useFormIdPrefix } from '../id-prefix.js';
4
+ /**
5
+ * Always rendered, and empty while there is nothing to say. Returning null
6
+ * until an error exists meant the region arrived already populated, which is
7
+ * the case screen readers do not reliably announce.
8
+ */
3
9
  export function FieldErrors({ node, errors, showErrors }) {
4
- if (!showErrors || errors.length === 0) {
5
- return null;
6
- }
7
- const errorProps = getErrorProps(node);
8
- return (_jsx("div", { ...errorProps, children: errors.map((error, index) => (_jsx("div", { children: error.message ?? error.keyword }, index))) }));
10
+ const idPrefix = useFormIdPrefix();
11
+ const visible = showErrors ? errors : [];
12
+ return (_jsx("div", { ...getErrorProps(node, idPrefix), children: visible.map((error, index) => (_jsx("div", { children: error.message ?? error.keyword }, index))) }));
9
13
  }
10
14
  //# sourceMappingURL=FieldErrors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FieldErrors.js","sourceRoot":"","sources":["../../src/components/FieldErrors.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAQjD,MAAM,UAAU,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAoB;IACxE,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAA;IAEtC,OAAO,CACL,iBAAS,UAAU,YAChB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAC5B,wBAAkB,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,IAAtC,KAAK,CAAwC,CACxD,CAAC,GACE,CACP,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"FieldErrors.js","sourceRoot":"","sources":["../../src/components/FieldErrors.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAQjD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAoB;IACxE,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAA;IAClC,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;IAExC,OAAO,CACL,iBAAS,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,YACnC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAC7B,wBAAkB,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,IAAtC,KAAK,CAAwC,CACxD,CAAC,GACE,CACP,CAAA;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ /** The wording every binding uses, so a custom widget can match it. */
3
+ export declare const REQUIRED_INDICATOR = "(required)";
4
+ export interface FieldLabelContentProps {
5
+ label: string;
6
+ required: boolean;
7
+ }
8
+ /**
9
+ * Label content for a field: the schema's label, plus a visible required
10
+ * indicator that is kept out of the accessible name.
11
+ *
12
+ * Three separate channels carry one fact, and they must not be collapsed. The
13
+ * sighted user reads "(required)". The accessible name stays the label alone,
14
+ * because `aria-required` already reports the state and putting it in the name
15
+ * as well makes some screen readers say it twice. The indicator says the word
16
+ * rather than an asterisk, so nobody has to be told elsewhere what a marker
17
+ * means.
18
+ *
19
+ * Shared by the three React widget sets so they cannot drift on it.
20
+ */
21
+ export declare function FieldLabelContent({ label, required }: FieldLabelContentProps): React.JSX.Element;
22
+ //# sourceMappingURL=FieldLabelContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldLabelContent.d.ts","sourceRoot":"","sources":["../../src/components/FieldLabelContent.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,uEAAuE;AACvE,eAAO,MAAM,kBAAkB,eAAe,CAAA;AAE9C,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,sBAAsB,qBAO5E"}
@@ -0,0 +1,21 @@
1
+ import { jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ /** The wording every binding uses, so a custom widget can match it. */
4
+ export const REQUIRED_INDICATOR = '(required)';
5
+ /**
6
+ * Label content for a field: the schema's label, plus a visible required
7
+ * indicator that is kept out of the accessible name.
8
+ *
9
+ * Three separate channels carry one fact, and they must not be collapsed. The
10
+ * sighted user reads "(required)". The accessible name stays the label alone,
11
+ * because `aria-required` already reports the state and putting it in the name
12
+ * as well makes some screen readers say it twice. The indicator says the word
13
+ * rather than an asterisk, so nobody has to be told elsewhere what a marker
14
+ * means.
15
+ *
16
+ * Shared by the three React widget sets so they cannot drift on it.
17
+ */
18
+ export function FieldLabelContent({ label, required }) {
19
+ return (_jsxs(_Fragment, { children: [label, required ? _jsxs("span", { "aria-hidden": "true", children: [" ", REQUIRED_INDICATOR] }) : null] }));
20
+ }
21
+ //# sourceMappingURL=FieldLabelContent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldLabelContent.js","sourceRoot":"","sources":["../../src/components/FieldLabelContent.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,uEAAuE;AACvE,MAAM,CAAC,MAAM,kBAAkB,GAAG,YAAY,CAAA;AAO9C;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAA0B;IAC3E,OAAO,CACL,8BACG,KAAK,EACL,QAAQ,CAAC,CAAC,CAAC,+BAAkB,MAAM,kBAAG,kBAAkB,IAAQ,CAAC,CAAC,CAAC,IAAI,IACvE,CACJ,CAAA;AACH,CAAC"}
package/dist/context.d.ts CHANGED
@@ -1,5 +1,19 @@
1
+ import type { ReactNode } from 'react';
1
2
  import type { FormRuntime } from '@texaryn/core';
2
3
  export declare const FormContext: import("react").Context<FormRuntime | null>;
3
- export declare const FormProvider: import("react").Provider<FormRuntime | null>;
4
+ export interface FormProviderProps {
5
+ value: FormRuntime | null;
6
+ children?: ReactNode;
7
+ }
8
+ /**
9
+ * The rendering surface, and therefore the DOM namespace, for one form.
10
+ *
11
+ * The prefix is owned here rather than by useForm because one runtime can
12
+ * legitimately be rendered twice; a prefix taken from the runtime would give
13
+ * both surfaces the same ids. It is provided above the children so that
14
+ * siblings of FormRoot, ErrorSummary in particular, resolve the same namespace
15
+ * the fields do.
16
+ */
17
+ export declare function FormProvider({ value, children }: FormProviderProps): import("react").JSX.Element;
4
18
  export declare function useFormContext(): FormRuntime;
5
19
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,eAAO,MAAM,WAAW,6CAA0C,CAAA;AAElE,eAAO,MAAM,YAAY,8CAAuB,CAAA;AAEhD,wBAAgB,cAAc,IAAI,WAAW,CAM5C"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAGhD,eAAO,MAAM,WAAW,6CAA0C,CAAA;AAElE,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACzB,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,iBAAiB,+BAOlE;AAED,wBAAgB,cAAc,IAAI,WAAW,CAM5C"}
package/dist/context.js CHANGED
@@ -1,6 +1,20 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
1
2
  import { createContext, useContext } from 'react';
3
+ import { IdPrefixProvider, useGeneratedIdPrefix } from './id-prefix.js';
2
4
  export const FormContext = createContext(null);
3
- export const FormProvider = FormContext.Provider;
5
+ /**
6
+ * The rendering surface, and therefore the DOM namespace, for one form.
7
+ *
8
+ * The prefix is owned here rather than by useForm because one runtime can
9
+ * legitimately be rendered twice; a prefix taken from the runtime would give
10
+ * both surfaces the same ids. It is provided above the children so that
11
+ * siblings of FormRoot, ErrorSummary in particular, resolve the same namespace
12
+ * the fields do.
13
+ */
14
+ export function FormProvider({ value, children }) {
15
+ const idPrefix = useGeneratedIdPrefix();
16
+ return (_jsx(FormContext.Provider, { value: value, children: _jsx(IdPrefixProvider, { value: idPrefix, children: children }) }));
17
+ }
4
18
  export function useFormContext() {
5
19
  const runtime = useContext(FormContext);
6
20
  if (!runtime) {
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAGjD,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAqB,IAAI,CAAC,CAAA;AAElE,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC,QAAQ,CAAA;AAEhD,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACtE,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAGjD,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAEvE,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAqB,IAAI,CAAC,CAAA;AAOlE;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAqB;IACjE,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAA;IACvC,OAAO,CACL,KAAC,WAAW,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAChC,KAAC,gBAAgB,IAAC,KAAK,EAAE,QAAQ,YAAG,QAAQ,GAAoB,GAC3C,CACxB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACtE,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { UINode } from '@texaryn/core';
2
+ export interface ArrayActions {
3
+ /** Accessible name for the control that removes the row at this position. */
4
+ removeName(position: number, item: UINode | undefined): string;
5
+ /** Accessible name for the control that appends a row. */
6
+ addName: string;
7
+ }
8
+ /**
9
+ * Shared by the three React widget sets so their action names cannot drift.
10
+ * The array title is read from the document rather than the node prop: these
11
+ * widgets are memoized on node id, so a conditional annotation added by a
12
+ * recompile would otherwise never reach the name.
13
+ */
14
+ export declare function useArrayActions(node: UINode): ArrayActions;
15
+ //# sourceMappingURL=use-array-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-array-actions.d.ts","sourceRoot":"","sources":["../../src/hooks/use-array-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,MAAM,EAAE,MAAM,eAAe,CAAA;AAI1D,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;IAC9D,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAW1D"}
@@ -0,0 +1,21 @@
1
+ import { useRendererContext } from '../components/renderer-context.js';
2
+ import { addActionName, removeActionName } from '../props/action-names.js';
3
+ /**
4
+ * Shared by the three React widget sets so their action names cannot drift.
5
+ * The array title is read from the document rather than the node prop: these
6
+ * widgets are memoized on node id, so a conditional annotation added by a
7
+ * recompile would otherwise never reach the name.
8
+ */
9
+ export function useArrayActions(node) {
10
+ const { document } = useRendererContext();
11
+ const current = (document.nodes[node.id] ?? node);
12
+ const arrayTitle = current.annotations.title;
13
+ return {
14
+ removeName: (position, item) => removeActionName(position, item?.annotations.title, arrayTitle),
15
+ // The item template's title, not the first row's. A row may not exist yet,
16
+ // which is exactly when naming the add control matters most, and a row's
17
+ // annotations can in principle depend on its data.
18
+ addName: addActionName(current.arrayMeta?.itemTitle, arrayTitle),
19
+ };
20
+ }
21
+ //# sourceMappingURL=use-array-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-array-actions.js","sourceRoot":"","sources":["../../src/hooks/use-array-actions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AACtE,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAS1E;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,EAAE,QAAQ,EAAE,GAAG,kBAAkB,EAAE,CAAA;IACzC,MAAM,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,CAAkB,CAAA;IAClE,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,CAAA;IAC5C,OAAO;QACL,UAAU,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC;QAC/F,2EAA2E;QAC3E,yEAAyE;QACzE,mDAAmD;QACnD,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC;KACjE,CAAA;AACH,CAAC"}
@@ -4,6 +4,12 @@ export interface DomInputBaseProps {
4
4
  id: string;
5
5
  name: string;
6
6
  disabled: boolean;
7
+ /**
8
+ * Set only where HTML has no native `readonly`, which is every control but
9
+ * text, number and textarea. Those carry the native attribute instead, on
10
+ * the value surface below.
11
+ */
12
+ 'aria-readonly'?: true;
7
13
  'aria-required': boolean;
8
14
  'aria-invalid'?: boolean;
9
15
  'aria-describedby'?: string;
@@ -13,6 +19,7 @@ export interface DomInputBaseProps {
13
19
  /** Value-shaped surface for text, number, textarea and select controls. */
14
20
  export interface DomValueInputProps extends DomInputBaseProps {
15
21
  value: string | number;
22
+ readOnly?: boolean;
16
23
  onChange(event: {
17
24
  target: {
18
25
  value: string;
@@ -36,6 +43,7 @@ export interface FieldBinding {
36
43
  touched: boolean;
37
44
  dirty: boolean;
38
45
  disabled: boolean;
46
+ readOnly: boolean;
39
47
  visible: boolean;
40
48
  /** Derived from the node for adapters. `aria-required` stays owned by getInputProps. */
41
49
  required: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"use-field-binding.d.ts","sourceRoot":"","sources":["../../src/hooks/use-field-binding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAQ/D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAEvF,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB,eAAe,EAAE,OAAO,CAAA;IACxB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,IAAI,IAAI,CAAA;CACf;AAED,2EAA2E;AAC3E,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,CAAA;CACrD;AAED,oDAAoD;AACpD,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE;YAAE,OAAO,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,CAAA;CACxD;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAA;IAEf,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IAC9B,MAAM,IAAI,IAAI,CAAA;IAEd,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,wFAAwF;IACxF,QAAQ,EAAE,OAAO,CAAA;IAEjB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IAE/B,yEAAyE;IACzE,MAAM,EAAE,SAAS,eAAe,EAAE,CAAA;IAClC,mDAAmD;IACnD,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,OAAO,EAAE,OAAO,CAAA;IAEhB,UAAU,EAAE,UAAU,CAAA;IACtB,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,UAAU,EAAE,UAAU,CAAA;IACtB,aAAa,EAAE,kBAAkB,CAAA;IACjC,gBAAgB,EAAE,oBAAoB,CAAA;CACvC;AA6BD,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,YAAY,CAuD7D"}
1
+ {"version":3,"file":"use-field-binding.d.ts","sourceRoot":"","sources":["../../src/hooks/use-field-binding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAU/D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAEvF,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,IAAI,CAAA;IACtB,eAAe,EAAE,OAAO,CAAA;IACxB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,IAAI,IAAI,CAAA;CACf;AAED,2EAA2E;AAC3E,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,CAAA;CACrD;AAED,oDAAoD;AACpD,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE;YAAE,OAAO,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,CAAA;CACxD;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAA;IAEf,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IAC9B,MAAM,IAAI,IAAI,CAAA;IAEd,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,wFAAwF;IACxF,QAAQ,EAAE,OAAO,CAAA;IAEjB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IAE/B,yEAAyE;IACzE,MAAM,EAAE,SAAS,eAAe,EAAE,CAAA;IAClC,mDAAmD;IACnD,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,OAAO,EAAE,OAAO,CAAA;IAEhB,UAAU,EAAE,UAAU,CAAA;IACtB,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,UAAU,EAAE,UAAU,CAAA;IACtB,aAAa,EAAE,kBAAkB,CAAA;IACjC,gBAAgB,EAAE,oBAAoB,CAAA;CACvC;AA6BD,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,YAAY,CAwE7D"}
@@ -1,5 +1,6 @@
1
1
  import { useField } from './use-field.js';
2
- import { getInputProps, getLabelProps, getErrorProps, getDescriptionProps, } from '../props/field-props.js';
2
+ import { useFormIdPrefix } from '../id-prefix.js';
3
+ import { getInputProps, getLabelProps, getErrorProps, getDescriptionProps, hasNativeReadOnly, } from '../props/field-props.js';
3
4
  const NO_ERRORS = Object.freeze([]);
4
5
  // Mirrors createDefaultRegistry: enum outranks the primitive kind.
5
6
  function fieldKind(node) {
@@ -29,7 +30,8 @@ function coerce(kind, node, raw) {
29
30
  }
30
31
  export function useFieldBinding(node) {
31
32
  const field = useField(node.id);
32
- const input = getInputProps(node, field);
33
+ const idPrefix = useFormIdPrefix();
34
+ const input = getInputProps(node, field, idPrefix);
33
35
  const kind = fieldKind(node);
34
36
  const base = {
35
37
  id: input.id,
@@ -41,15 +43,33 @@ export function useFieldBinding(node) {
41
43
  placeholder: input.placeholder,
42
44
  onBlur: input.onBlur,
43
45
  };
46
+ // Select and checkbox have no native readonly, so they say so through ARIA
47
+ // and drop the change. Text-like controls keep the native attribute, which
48
+ // the browser enforces before an event is ever raised.
49
+ const valueReadOnly = node.readOnly
50
+ ? hasNativeReadOnly(node)
51
+ ? { readOnly: true }
52
+ : { 'aria-readonly': true }
53
+ : {};
44
54
  const domInputProps = {
45
55
  ...base,
56
+ ...valueReadOnly,
46
57
  value: displayValue(kind, field.value),
47
- onChange: (event) => field.onChange(coerce(kind, node, event.target.value)),
58
+ onChange: (event) => {
59
+ if (node.readOnly)
60
+ return;
61
+ field.onChange(coerce(kind, node, event.target.value));
62
+ },
48
63
  };
49
64
  const domCheckboxProps = {
50
65
  ...base,
51
66
  checked: Boolean(field.value),
52
- onChange: (event) => field.onChange(event.target.checked),
67
+ ...(node.readOnly ? { 'aria-readonly': true } : {}),
68
+ onChange: (event) => {
69
+ if (node.readOnly)
70
+ return;
71
+ field.onChange(event.target.checked);
72
+ },
53
73
  };
54
74
  const errors = field.showErrors ? field.errors : NO_ERRORS;
55
75
  const first = errors[0];
@@ -61,6 +81,7 @@ export function useFieldBinding(node) {
61
81
  touched: field.touched,
62
82
  dirty: field.dirty,
63
83
  disabled: field.disabled,
84
+ readOnly: node.readOnly,
64
85
  visible: field.visible,
65
86
  required: Boolean(node.constraints.required),
66
87
  label: node.annotations.title ?? node.dataPointer ?? node.id,
@@ -69,9 +90,9 @@ export function useFieldBinding(node) {
69
90
  errors,
70
91
  error: first ? first.message ?? first.keyword : undefined,
71
92
  invalid: errors.length > 0,
72
- labelProps: getLabelProps(node),
73
- descriptionProps: getDescriptionProps(node),
74
- errorProps: getErrorProps(node),
93
+ labelProps: getLabelProps(node, idPrefix),
94
+ descriptionProps: getDescriptionProps(node, idPrefix),
95
+ errorProps: getErrorProps(node, idPrefix),
75
96
  domInputProps,
76
97
  domCheckboxProps,
77
98
  };
@@ -1 +1 @@
1
- {"version":3,"file":"use-field-binding.js","sourceRoot":"","sources":["../../src/hooks/use-field-binding.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-field-binding.js","sourceRoot":"","sources":["../../src/hooks/use-field-binding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { UINode } from '@texaryn/core';
2
+ export interface ObjectGroup {
3
+ /**
4
+ * Fixed for the widget's lifetime. The root object is the form itself, so
5
+ * only a nested object is a candidate for being a named group.
6
+ */
7
+ nested: boolean;
8
+ /**
9
+ * Live, because a conditional subschema can add or drop a title on any
10
+ * recompile while this widget survives. Undefined means the group has no
11
+ * name, and an unnamed group is noise.
12
+ */
13
+ title: string | undefined;
14
+ children: UINode[];
15
+ }
16
+ /**
17
+ * The grouping decision, shared by every React widget set so the three cannot
18
+ * drift apart. The node comes from the document rather than the prop, because
19
+ * a memo bailout on an unchanged id would otherwise freeze the title.
20
+ */
21
+ export declare function useObjectGroup(node: UINode): ObjectGroup;
22
+ //# sourceMappingURL=use-object-group.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-object-group.d.ts","sourceRoot":"","sources":["../../src/hooks/use-object-group.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,MAAM,EAAE,MAAM,eAAe,CAAA;AAG1D,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAUxD"}
@@ -0,0 +1,18 @@
1
+ import { useRendererContext } from '../components/renderer-context.js';
2
+ /**
3
+ * The grouping decision, shared by every React widget set so the three cannot
4
+ * drift apart. The node comes from the document rather than the prop, because
5
+ * a memo bailout on an unchanged id would otherwise freeze the title.
6
+ */
7
+ export function useObjectGroup(node) {
8
+ const { document } = useRendererContext();
9
+ const current = (document.nodes[node.id] ?? node);
10
+ return {
11
+ nested: node.parentId !== null,
12
+ title: current.annotations.title,
13
+ children: current.children
14
+ .map((id) => document.nodes[id])
15
+ .filter((child) => child != null),
16
+ };
17
+ }
18
+ //# sourceMappingURL=use-object-group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-object-group.js","sourceRoot":"","sources":["../../src/hooks/use-object-group.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AAiBtE;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,EAAE,QAAQ,EAAE,GAAG,kBAAkB,EAAE,CAAA;IACzC,MAAM,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,CAAkB,CAAA;IAClE,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,QAAQ,KAAK,IAAI;QAC9B,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK;QAChC,QAAQ,EAAE,OAAO,CAAC,QAAQ;aACvB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;aAC/B,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC;KACrD,CAAA;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ export declare const IdPrefixProvider: import("react").Provider<string | null>;
2
+ export declare function useGeneratedIdPrefix(): string;
3
+ /**
4
+ * The DOM namespace of one rendering surface.
5
+ *
6
+ * Uniqueness is per React application, not per page: two independent roots
7
+ * need distinct `identifierPrefix` options to stay disjoint.
8
+ */
9
+ export declare function useFormIdPrefix(): string;
10
+ //# sourceMappingURL=id-prefix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id-prefix.d.ts","sourceRoot":"","sources":["../src/id-prefix.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,gBAAgB,yCAA2B,CAAA;AAaxD,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,MAAM,CASxC"}
@@ -0,0 +1,31 @@
1
+ import { createContext, useContext, useId } from 'react';
2
+ const IdPrefixContext = createContext(null);
3
+ export const IdPrefixProvider = IdPrefixContext.Provider;
4
+ /**
5
+ * React's useId output format is not part of its contract, and React 18 emits
6
+ * colons, which are legal in an id but must be escaped before they reach
7
+ * querySelector or a stylesheet. Encoding each code point to base 36 keeps the
8
+ * seed injective and server/client deterministic while restricting it to
9
+ * characters that need no escaping, so a generated id stays usable as written.
10
+ */
11
+ function encodeSeed(seed) {
12
+ return Array.from(seed, (char) => char.codePointAt(0).toString(36)).join('_');
13
+ }
14
+ export function useGeneratedIdPrefix() {
15
+ return `texaryn-${encodeSeed(useId())}`;
16
+ }
17
+ /**
18
+ * The DOM namespace of one rendering surface.
19
+ *
20
+ * Uniqueness is per React application, not per page: two independent roots
21
+ * need distinct `identifierPrefix` options to stay disjoint.
22
+ */
23
+ export function useFormIdPrefix() {
24
+ const prefix = useContext(IdPrefixContext);
25
+ if (!prefix) {
26
+ throw new Error('No id prefix in scope. Render the form under <FormProvider value={runtime}>; ' +
27
+ 'FormContext.Provider alone cannot namespace ids, which is what makes two forms collide.');
28
+ }
29
+ return prefix;
30
+ }
31
+ //# sourceMappingURL=id-prefix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id-prefix.js","sourceRoot":"","sources":["../src/id-prefix.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAExD,MAAM,eAAe,GAAG,aAAa,CAAgB,IAAI,CAAC,CAAA;AAE1D,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC,QAAQ,CAAA;AAExD;;;;;;GAMG;AACH,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAChF,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,WAAW,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,CAAA;AACzC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAAG,UAAU,CAAC,eAAe,CAAC,CAAA;IAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,+EAA+E;YAC7E,yFAAyF,CAC5F,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}