@texaryn/react 0.2.3 → 0.3.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 (42) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +16 -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/context.d.ts +15 -1
  12. package/dist/context.d.ts.map +1 -1
  13. package/dist/context.js +15 -1
  14. package/dist/context.js.map +1 -1
  15. package/dist/hooks/use-field-binding.d.ts +8 -0
  16. package/dist/hooks/use-field-binding.d.ts.map +1 -1
  17. package/dist/hooks/use-field-binding.js +28 -7
  18. package/dist/hooks/use-field-binding.js.map +1 -1
  19. package/dist/hooks/use-object-group.d.ts +22 -0
  20. package/dist/hooks/use-object-group.d.ts.map +1 -0
  21. package/dist/hooks/use-object-group.js +18 -0
  22. package/dist/hooks/use-object-group.js.map +1 -0
  23. package/dist/id-prefix.d.ts +10 -0
  24. package/dist/id-prefix.d.ts.map +1 -0
  25. package/dist/id-prefix.js +31 -0
  26. package/dist/id-prefix.js.map +1 -0
  27. package/dist/index.d.ts +5 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +3 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/props/field-props.d.ts +24 -7
  32. package/dist/props/field-props.d.ts.map +1 -1
  33. package/dist/props/field-props.js +30 -14
  34. package/dist/props/field-props.js.map +1 -1
  35. package/dist/props/index.d.ts +1 -1
  36. package/dist/props/index.d.ts.map +1 -1
  37. package/dist/props/index.js +1 -1
  38. package/dist/props/index.js.map +1 -1
  39. package/dist/widgets/ObjectLayout.d.ts.map +1 -1
  40. package/dist/widgets/ObjectLayout.js +12 -9
  41. package/dist/widgets/ObjectLayout.js.map +1 -1
  42. package/package.json +4 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @texaryn/react
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3545378: Expose a titled nested object as a named group in React and Vue, closing the last gap the DOM accessibility contract declared.
8
+
9
+ 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.
10
+
11
+ 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.
12
+ - 3e53ca9: Keep each field's error container mounted as a live region instead of inserting it with the message already inside.
13
+
14
+ 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.
15
+
16
+ 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.
17
+ - 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.
18
+
19
+ `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.
20
+ - e7c00ce: Render a schema's `readOnly` as read-only rather than disabled, and enforce it in the runtime.
21
+
22
+ 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.
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies [e7c00ce]
27
+ - @texaryn/core@0.6.0
28
+
29
+ ## 0.2.4
30
+
31
+ ### Patch Changes
32
+
33
+ - Updated dependencies [8b20b6e]
34
+ - @texaryn/core@0.5.0
35
+
3
36
  ## 0.2.3
4
37
 
5
38
  ### 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,8 @@ 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
+ - `ObjectGroup`
260
272
  - `FieldBinding`
261
273
  - `DomValueInputProps`
262
274
  - `DomCheckedInputProps`
@@ -267,6 +279,8 @@ The schema adapter interprets data semantics. The core compiles and runs the for
267
279
 
268
280
  - `FormContext`
269
281
  - `FormProvider`
282
+ - `FormProviderProps`
283
+ - `useFormIdPrefix`
270
284
  - `FormRoot`
271
285
  - `FormRootProps`
272
286
  - `NodeRenderer`
@@ -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"}
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"}
@@ -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":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,mBAAmB,GACpB,MAAM,yBAAyB,CAAA;AAyDhC,MAAM,SAAS,GAA+B,MAAM,CAAC,MAAM,CAAoB,EAAE,CAAC,CAAA;AAIlF,mEAAmE;AACnE,SAAS,SAAS,CAAC,IAAe;IAChC,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAA;IACxE,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAClD,IAAI,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAA;IAChF,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,SAAS,YAAY,CAAC,IAAe,EAAE,KAAc;IACnD,IAAI,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAChE,IAAI,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAChE,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC3C,CAAC;AAED,SAAS,MAAM,CAAC,IAAe,EAAE,IAAe,EAAE,GAAW;IAC3D,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAClE,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAA;QAC3E,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAA;IACpC,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAe;IAC7C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC/B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACxC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAE5B,MAAM,IAAI,GAAsB;QAC9B,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,eAAe,EAAE,KAAK,CAAC,eAAe,CAAC;QACvC,cAAc,EAAE,KAAK,CAAC,cAAc,CAAC;QACrC,kBAAkB,EAAE,KAAK,CAAC,kBAAkB,CAAC;QAC7C,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAA;IAED,MAAM,aAAa,GAAuB;QACxC,GAAG,IAAI;QACP,KAAK,EAAE,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC;QACtC,QAAQ,EAAE,CAAC,KAAoC,EAAE,EAAE,CACjD,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;KACzD,CAAA;IAED,MAAM,gBAAgB,GAAyB;QAC7C,GAAG,IAAI;QACP,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;QAC7B,QAAQ,EAAE,CAAC,KAAuC,EAAE,EAAE,CACpD,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;KACvC,CAAA;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;IAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IAEvB,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;QAC5C,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,KAAK,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,EAAE;QAC5D,WAAW,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,WAAW,CAAC,WAAW;QAC1D,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,MAAM;QACN,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;QACzD,OAAO,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC;QAC1B,UAAU,EAAE,aAAa,CAAC,IAAI,CAAC;QAC/B,gBAAgB,EAAE,mBAAmB,CAAC,IAAI,CAAC;QAC3C,UAAU,EAAE,aAAa,CAAC,IAAI,CAAC;QAC/B,aAAa;QACb,gBAAgB;KACjB,CAAA;AACH,CAAC"}
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"}
package/dist/index.d.ts CHANGED
@@ -6,9 +6,13 @@ export type { UseFieldReturn } from './hooks/use-field.js';
6
6
  export { useFieldArray } from './hooks/use-field-array.js';
7
7
  export type { UseFieldArrayReturn } from './hooks/use-field-array.js';
8
8
  export { useFieldBinding } from './hooks/use-field-binding.js';
9
+ export { useObjectGroup } from './hooks/use-object-group.js';
10
+ export type { ObjectGroup } from './hooks/use-object-group.js';
9
11
  export type { FieldBinding, DomValueInputProps, DomCheckedInputProps, DomInputBaseProps, } from './hooks/use-field-binding.js';
10
12
  export { FormContext, FormProvider, useFormContext } from './context.js';
11
- export { getInputProps, getLabelProps, getErrorProps, getDescriptionProps } from './props/index.js';
13
+ export type { FormProviderProps } from './context.js';
14
+ export { useFormIdPrefix } from './id-prefix.js';
15
+ export { getInputProps, getLabelProps, getErrorProps, getDescriptionProps, hasNativeReadOnly, } from './props/index.js';
12
16
  export type { InputProps, LabelProps, ErrorProps, DescriptionProps, FieldState } from './props/index.js';
13
17
  export { FormRoot } from './components/FormRoot.js';
14
18
  export type { FormRootProps } from './components/FormRoot.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AACxE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACnG,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AACxG,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACnD,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,EACL,SAAS,EACT,WAAW,EACX,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,qBAAqB,GACtB,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAC9D,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AACxE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AACxG,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACnD,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,EACL,SAAS,EACT,WAAW,EACX,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,qBAAqB,GACtB,MAAM,oBAAoB,CAAA"}
package/dist/index.js CHANGED
@@ -3,8 +3,10 @@ export { useForm } from './hooks/use-form.js';
3
3
  export { useField } from './hooks/use-field.js';
4
4
  export { useFieldArray } from './hooks/use-field-array.js';
5
5
  export { useFieldBinding } from './hooks/use-field-binding.js';
6
+ export { useObjectGroup } from './hooks/use-object-group.js';
6
7
  export { FormContext, FormProvider, useFormContext } from './context.js';
7
- export { getInputProps, getLabelProps, getErrorProps, getDescriptionProps } from './props/index.js';
8
+ export { useFormIdPrefix } from './id-prefix.js';
9
+ export { getInputProps, getLabelProps, getErrorProps, getDescriptionProps, hasNativeReadOnly, } from './props/index.js';
8
10
  export { FormRoot } from './components/FormRoot.js';
9
11
  export { NodeRenderer } from './components/NodeRenderer.js';
10
12
  export { FieldErrors } from './components/FieldErrors.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAE/C,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAO9D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AACxE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAEnG,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAEnD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAErE,OAAO,EACL,SAAS,EACT,WAAW,EACX,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,qBAAqB,GACtB,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAE/C,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAQ5D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAExE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAEnD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAErE,OAAO,EACL,SAAS,EACT,WAAW,EACX,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,qBAAqB,GACtB,MAAM,oBAAoB,CAAA"}
@@ -26,6 +26,10 @@ export interface InputProps extends Record<string, unknown> {
26
26
  name: string;
27
27
  value: unknown;
28
28
  disabled: boolean;
29
+ /** Native `readonly`, set only on a control HTML honours it for. */
30
+ readOnly?: boolean;
31
+ /** Set instead where HTML has no native read-only, so select and checkbox. */
32
+ 'aria-readonly'?: true;
29
33
  'aria-required': boolean;
30
34
  'aria-invalid'?: boolean;
31
35
  'aria-describedby'?: string;
@@ -41,11 +45,18 @@ export interface LabelProps extends Record<string, unknown> {
41
45
  htmlFor: string;
42
46
  }
43
47
  /**
44
- * HTML/ARIA attributes for an error container
48
+ * HTML/ARIA attributes for an error container.
49
+ *
50
+ * The container is a live region that exists from mount and is empty while the
51
+ * field is valid, because a region inserted with its content already in place
52
+ * is not reliably announced. It is polite rather than an alert: validation runs
53
+ * on change, blur and submit, so assertive would interrupt typing and would
54
+ * speak once per failing field on submit.
45
55
  */
46
56
  export interface ErrorProps extends Record<string, unknown> {
47
57
  id: string;
48
- role: 'alert';
58
+ 'aria-live': 'polite';
59
+ 'aria-atomic': true;
49
60
  }
50
61
  /**
51
62
  * HTML/ARIA attributes for a description element
@@ -53,22 +64,28 @@ export interface ErrorProps extends Record<string, unknown> {
53
64
  export interface DescriptionProps extends Record<string, unknown> {
54
65
  id: string;
55
66
  }
56
- export declare function makeId(nodeId: string, suffix: string): string;
67
+ export declare function makeId(idPrefix: string, nodeId: string, suffix: string): string;
68
+ /**
69
+ * HTML honours `readonly` on text, number and textarea only. A select or a
70
+ * checkbox says so through ARIA instead, and the widget refuses the change.
71
+ * Shared with useFieldBinding so the two public surfaces cannot drift apart.
72
+ */
73
+ export declare function hasNativeReadOnly(node: FieldNode): boolean;
57
74
  /**
58
75
  * getInputProps: Returns ARIA-correct props for an input element
59
76
  * Includes id, name, value, disabled, aria-required, aria-invalid, aria-describedby
60
77
  */
61
- export declare function getInputProps(node: FieldNode, fieldState: FieldState): InputProps;
78
+ export declare function getInputProps(node: FieldNode, fieldState: FieldState, idPrefix: string): InputProps;
62
79
  /**
63
80
  * getLabelProps: Returns ARIA-correct props for a label element
64
81
  */
65
- export declare function getLabelProps(node: FieldNode): LabelProps;
82
+ export declare function getLabelProps(node: FieldNode, idPrefix: string): LabelProps;
66
83
  /**
67
84
  * getErrorProps: Returns ARIA-correct props for an error container
68
85
  */
69
- export declare function getErrorProps(node: FieldNode): ErrorProps;
86
+ export declare function getErrorProps(node: FieldNode, idPrefix: string): ErrorProps;
70
87
  /**
71
88
  * getDescriptionProps: Returns ARIA-correct props for a description element
72
89
  */
73
- export declare function getDescriptionProps(node: FieldNode): DescriptionProps;
90
+ export declare function getDescriptionProps(node: FieldNode, idPrefix: string): DescriptionProps;
74
91
  //# sourceMappingURL=field-props.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"field-props.d.ts","sourceRoot":"","sources":["../../src/props/field-props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE9C;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAA;IAC9G,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAW,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzD,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,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,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAW,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzD,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,UAAW,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzD,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,OAAO,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/D,EAAE,EAAE,MAAM,CAAA;CACX;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,GAAG,UAAU,CAiCjF;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,UAAU,CAKzD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,UAAU,CAKzD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,GAAG,gBAAgB,CAIrE"}
1
+ {"version":3,"file":"field-props.d.ts","sourceRoot":"","sources":["../../src/props/field-props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE9C;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAA;IAC9G,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAW,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzD,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;IACjB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,8EAA8E;IAC9E,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,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAW,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzD,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAW,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzD,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,QAAQ,CAAA;IACrB,aAAa,EAAE,IAAI,CAAA;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/D,EAAE,EAAE,MAAM,CAAA;CACX;AAED,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAG1D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,GACf,UAAU,CAsCZ;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAK3E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAM3E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAIvF"}
@@ -1,26 +1,41 @@
1
- export function makeId(nodeId, suffix) {
2
- return `texaryn-${nodeId}-${suffix}`;
1
+ export function makeId(idPrefix, nodeId, suffix) {
2
+ return `${idPrefix}-${nodeId}-${suffix}`;
3
+ }
4
+ /**
5
+ * HTML honours `readonly` on text, number and textarea only. A select or a
6
+ * checkbox says so through ARIA instead, and the widget refuses the change.
7
+ * Shared with useFieldBinding so the two public surfaces cannot drift apart.
8
+ */
9
+ export function hasNativeReadOnly(node) {
10
+ if (node.enumValues != null && node.enumValues.length > 0)
11
+ return false;
12
+ return node.fieldType !== 'boolean';
3
13
  }
4
14
  /**
5
15
  * getInputProps: Returns ARIA-correct props for an input element
6
16
  * Includes id, name, value, disabled, aria-required, aria-invalid, aria-describedby
7
17
  */
8
- export function getInputProps(node, fieldState) {
9
- const id = makeId(node.id, 'input');
18
+ export function getInputProps(node, fieldState, idPrefix) {
19
+ const id = makeId(idPrefix, node.id, 'input');
10
20
  const hasDescription = Boolean(node.helpText ?? node.annotations?.description);
11
21
  // Build aria-describedby from description and error IDs, in that order
12
22
  const describedByIds = [];
13
23
  if (hasDescription) {
14
- describedByIds.push(makeId(node.id, 'description'));
24
+ describedByIds.push(makeId(idPrefix, node.id, 'description'));
15
25
  }
16
26
  if (fieldState.showErrors && fieldState.errors.length > 0) {
17
- describedByIds.push(makeId(node.id, 'error'));
27
+ describedByIds.push(makeId(idPrefix, node.id, 'error'));
18
28
  }
19
29
  const props = {
20
30
  id,
21
31
  name: node.dataPointer || node.id,
22
32
  value: fieldState.value,
23
33
  disabled: fieldState.disabled,
34
+ ...(node.readOnly
35
+ ? hasNativeReadOnly(node)
36
+ ? { readOnly: true }
37
+ : { 'aria-readonly': true }
38
+ : {}),
24
39
  'aria-required': Boolean(node.constraints?.required),
25
40
  'aria-describedby': describedByIds.length > 0 ? describedByIds.join(' ') : undefined,
26
41
  onChange: fieldState.onChange,
@@ -37,27 +52,28 @@ export function getInputProps(node, fieldState) {
37
52
  /**
38
53
  * getLabelProps: Returns ARIA-correct props for a label element
39
54
  */
40
- export function getLabelProps(node) {
55
+ export function getLabelProps(node, idPrefix) {
41
56
  return {
42
- id: makeId(node.id, 'label'),
43
- htmlFor: makeId(node.id, 'input'),
57
+ id: makeId(idPrefix, node.id, 'label'),
58
+ htmlFor: makeId(idPrefix, node.id, 'input'),
44
59
  };
45
60
  }
46
61
  /**
47
62
  * getErrorProps: Returns ARIA-correct props for an error container
48
63
  */
49
- export function getErrorProps(node) {
64
+ export function getErrorProps(node, idPrefix) {
50
65
  return {
51
- id: makeId(node.id, 'error'),
52
- role: 'alert',
66
+ id: makeId(idPrefix, node.id, 'error'),
67
+ 'aria-live': 'polite',
68
+ 'aria-atomic': true,
53
69
  };
54
70
  }
55
71
  /**
56
72
  * getDescriptionProps: Returns ARIA-correct props for a description element
57
73
  */
58
- export function getDescriptionProps(node) {
74
+ export function getDescriptionProps(node, idPrefix) {
59
75
  return {
60
- id: makeId(node.id, 'description'),
76
+ id: makeId(idPrefix, node.id, 'description'),
61
77
  };
62
78
  }
63
79
  //# sourceMappingURL=field-props.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"field-props.js","sourceRoot":"","sources":["../../src/props/field-props.ts"],"names":[],"mappings":"AAwDA,MAAM,UAAU,MAAM,CAAC,MAAc,EAAE,MAAc;IACnD,OAAO,WAAW,MAAM,IAAI,MAAM,EAAE,CAAA;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAe,EAAE,UAAsB;IACnE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IACnC,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA;IAE9E,uEAAuE;IACvE,MAAM,cAAc,GAAa,EAAE,CAAA;IACnC,IAAI,cAAc,EAAE,CAAC;QACnB,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC,CAAA;IACrD,CAAC;IACD,IAAI,UAAU,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1D,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAA;IAC/C,CAAC;IAED,MAAM,KAAK,GAAe;QACxB,EAAE;QACF,IAAI,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,EAAE;QACjC,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,eAAe,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC;QACpD,kBAAkB,EAAE,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;QACpF,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,MAAM,EAAE,UAAU,CAAC,MAAM;KAC1B,CAAA;IAED,IAAI,UAAU,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1D,KAAK,CAAC,cAAc,CAAC,GAAG,IAAI,CAAA;IAC9B,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC;QAC7B,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAA;IACtC,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAe;IAC3C,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC;QAC5B,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC;KAClC,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAe;IAC3C,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC;QAC5B,IAAI,EAAE,OAAO;KACd,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAe;IACjD,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC;KACnC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"field-props.js","sourceRoot":"","sources":["../../src/props/field-props.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- export { getInputProps, getLabelProps, getErrorProps, getDescriptionProps } from './field-props.js';
1
+ export { getInputProps, getLabelProps, getErrorProps, getDescriptionProps, hasNativeReadOnly, } from './field-props.js';
2
2
  export type { InputProps, LabelProps, ErrorProps, DescriptionProps, FieldState } from './field-props.js';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/props/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACnG,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/props/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA"}
@@ -1,2 +1,2 @@
1
- export { getInputProps, getLabelProps, getErrorProps, getDescriptionProps } from './field-props.js';
1
+ export { getInputProps, getLabelProps, getErrorProps, getDescriptionProps, hasNativeReadOnly, } from './field-props.js';
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/props/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/props/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,kBAAkB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectLayout.d.ts","sourceRoot":"","sources":["../../src/widgets/ObjectLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,KAAK,EAAiB,MAAM,EAAE,MAAM,eAAe,CAAA;AAI1D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;CACb;AAED,iBAAS,gBAAgB,CAAC,EAAE,IAAI,EAAE,EAAE,WAAW,qBAe9C;AAED,eAAO,MAAM,YAAY,oDAGxB,CAAA"}
1
+ {"version":3,"file":"ObjectLayout.d.ts","sourceRoot":"","sources":["../../src/widgets/ObjectLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAK3C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;CACb;AAED,iBAAS,gBAAgB,CAAC,EAAE,IAAI,EAAE,EAAE,WAAW,qBAsB9C;AAED,eAAO,MAAM,YAAY,oDAGxB,CAAA"}
@@ -1,17 +1,20 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { NodeRenderer } from '../components/NodeRenderer.js';
4
4
  import { useRendererContext } from '../components/renderer-context.js';
5
+ import { useObjectGroup } from '../hooks/use-object-group.js';
5
6
  function ObjectLayoutImpl({ node }) {
6
- const containerNode = node;
7
7
  const { document, registry } = useRendererContext();
8
- return (_jsx("div", { children: containerNode.children.map((childId) => {
9
- const child = document.nodes[childId];
10
- if (!child) {
11
- return null;
12
- }
13
- return _jsx(NodeRenderer, { node: child, document: document, registry: registry }, childId);
14
- }) }));
8
+ const { nested, title, children } = useObjectGroup(node);
9
+ const rendered = children.map((child) => (_jsx(NodeRenderer, { node: child, document: document, registry: registry }, child.id)));
10
+ if (!nested) {
11
+ return _jsx("div", { children: rendered });
12
+ }
13
+ // The element is chosen once and only the grouping semantics are re-derived:
14
+ // swapping div for fieldset when a title appears would remount the subtree
15
+ // and drop focus. Children sit in their own element so the legend is never a
16
+ // candidate for reorder.
17
+ return (_jsxs("fieldset", { role: title === undefined ? 'none' : undefined, children: [title === undefined ? null : _jsx("legend", { children: title }), _jsx("div", { children: rendered })] }));
15
18
  }
16
19
  export const ObjectLayout = React.memo(ObjectLayoutImpl, (prev, next) => prev.node.id === next.node.id);
17
20
  //# sourceMappingURL=ObjectLayout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectLayout.js","sourceRoot":"","sources":["../../src/widgets/ObjectLayout.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AAMtE,SAAS,gBAAgB,CAAC,EAAE,IAAI,EAAe;IAC7C,MAAM,aAAa,GAAG,IAAqB,CAAA;IAC3C,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,kBAAkB,EAAE,CAAA;IAEnD,OAAO,CACL,wBACG,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;YACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YACrC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,IAAI,CAAA;YACb,CAAC;YACD,OAAO,KAAC,YAAY,IAAe,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,IAA5D,OAAO,CAAyD,CAAA;QAC5F,CAAC,CAAC,GACE,CACP,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CACpC,gBAAgB,EAChB,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9C,CAAA"}
1
+ {"version":3,"file":"ObjectLayout.js","sourceRoot":"","sources":["../../src/widgets/ObjectLayout.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAM7D,SAAS,gBAAgB,CAAC,EAAE,IAAI,EAAe;IAC7C,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,kBAAkB,EAAE,CAAA;IACnD,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,CAAA;IAExD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACvC,KAAC,YAAY,IAAgB,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,IAA7D,KAAK,CAAC,EAAE,CAAyD,CACrF,CAAC,CAAA;IAEF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,wBAAM,QAAQ,GAAO,CAAA;IAC9B,CAAC;IAED,6EAA6E;IAC7E,2EAA2E;IAC3E,6EAA6E;IAC7E,yBAAyB;IACzB,OAAO,CACL,oBAAU,IAAI,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,aACrD,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,2BAAS,KAAK,GAAU,EACtD,wBAAM,QAAQ,GAAO,IACZ,CACZ,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CACpC,gBAAgB,EAChB,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9C,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@texaryn/react",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "React bindings, hooks, prop getters, and default widgets for Texaryn",
5
5
  "keywords": [
6
6
  "react",
@@ -44,7 +44,7 @@
44
44
  "CHANGELOG.md"
45
45
  ],
46
46
  "dependencies": {
47
- "@texaryn/core": "^0.4.0"
47
+ "@texaryn/core": "^0.6.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "react": ">=18.0.0"
@@ -52,10 +52,11 @@
52
52
  "devDependencies": {
53
53
  "@testing-library/react": "^16.0.0",
54
54
  "@types/react": "^19.0.0",
55
+ "@types/react-dom": "^19.0.0",
55
56
  "jsdom": "^25.0.0",
56
57
  "react": "^19.0.0",
57
58
  "react-dom": "^19.0.0",
58
- "@texaryn/schema-json": "0.2.3"
59
+ "@texaryn/schema-json": "0.2.5"
59
60
  },
60
61
  "scripts": {
61
62
  "build": "tsc --build"