schema-components 1.16.2 → 1.17.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.
@@ -1,6 +1,6 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { isValidElement, useCallback, useRef } from "react";
3
+ import { isValidElement, useCallback, useEffect, useRef } from "react";
4
4
  //#region src/react/headlessRenderers.tsx
5
5
  /**
6
6
  * Headless renderer functions — one per schema type.
@@ -57,12 +57,20 @@ function dateInputType(format) {
57
57
  if (format === "date-time" || format === "datetime") return "datetime-local";
58
58
  }
59
59
  /**
60
- * Build a stable, unique-ish input ID from the path.
60
+ * Build a stable, unique input ID from the path.
61
61
  * Used for `htmlFor`/`id` association between labels and inputs.
62
+ *
63
+ * Throws on an empty path: the previous "sc-field" fallback caused every
64
+ * input across a form to share the same id, breaking label-input pairing
65
+ * and screen reader navigation. Callers must thread a non-empty path
66
+ * (see `ROOT_PATH` and `joinPath` in `SchemaComponent.tsx`).
67
+ *
68
+ * Dots and bracket indices in paths are converted to hyphens to keep the
69
+ * id valid as a CSS selector and predictable in test queries.
62
70
  */
63
71
  function inputId(path) {
64
- if (path.length === 0) return "sc-field";
65
- return `sc-${path}`;
72
+ if (path.length === 0) throw new Error("inputId requires a non-empty path. Pass ROOT_PATH for the root field and use renderChild's pathSuffix to derive child paths.");
73
+ return `sc-${path.replace(/[.[\]]+/g, "-").replace(/-+$/g, "")}`;
66
74
  }
67
75
  function renderString(props) {
68
76
  const id = inputId(props.path);
@@ -245,14 +253,14 @@ function renderObject(props) {
245
253
  });
246
254
  return /* @__PURE__ */ jsxs("fieldset", { children: [typeof props.meta.description === "string" && /* @__PURE__ */ jsx("legend", { children: props.meta.description }), sortedEntries.filter(([, field]) => field.meta.visible !== false).map(([key, field]) => {
247
255
  const childValue = obj[key];
248
- const childId = inputId(props.path ? `${props.path}.${key}` : key);
256
+ const childId = inputId(`${props.path}.${key}`);
249
257
  const childOnChange = (v) => {
250
258
  const updated = {};
251
259
  for (const [k, val] of Object.entries(obj)) updated[k] = val;
252
260
  updated[key] = v;
253
261
  props.onChange(updated);
254
262
  };
255
- const child = toReactNode(props.renderChild(field, childValue, childOnChange));
263
+ const child = toReactNode(props.renderChild(field, childValue, childOnChange, key));
256
264
  if (child === null || child === void 0) return null;
257
265
  return /* @__PURE__ */ jsxs("div", { children: [typeof field.meta.description === "string" && /* @__PURE__ */ jsxs("label", {
258
266
  htmlFor: childId,
@@ -264,31 +272,123 @@ function renderObject(props) {
264
272
  }), child] }, key);
265
273
  })] });
266
274
  }
275
+ /**
276
+ * Compute the default value for a freshly added record entry based on the
277
+ * record's value-type schema. Mirrors the read of `defaultValue` used
278
+ * elsewhere in the renderer, falling back to a type-appropriate empty value.
279
+ */
280
+ function defaultRecordValue(valueType) {
281
+ if (valueType.defaultValue !== void 0) return valueType.defaultValue;
282
+ switch (valueType.type) {
283
+ case "string": return "";
284
+ case "number": return 0;
285
+ case "boolean": return false;
286
+ case "array": return [];
287
+ case "object":
288
+ case "record": return {};
289
+ default: return;
290
+ }
291
+ }
292
+ /**
293
+ * Generate a unique, currently-unused key for a new record entry.
294
+ * Picks the first of `key`, `key-1`, `key-2`, … that is not in `existing`.
295
+ */
296
+ function nextRecordKey(existing, base = "key") {
297
+ if (!existing.includes(base)) return base;
298
+ let i = 1;
299
+ while (existing.includes(`${base}-${String(i)}`)) i += 1;
300
+ return `${base}-${String(i)}`;
301
+ }
302
+ /**
303
+ * Rename a key in an object while preserving insertion order. Returns the
304
+ * original object reference when the rename is a no-op (oldKey === newKey)
305
+ * or when newKey collides with an existing key.
306
+ */
307
+ function renameRecordKey(obj, oldKey, newKey) {
308
+ if (oldKey === newKey) return obj;
309
+ if (newKey in obj && newKey !== oldKey) return obj;
310
+ const renamed = {};
311
+ for (const [k, v] of Object.entries(obj)) renamed[k === oldKey ? newKey : k] = v;
312
+ return renamed;
313
+ }
267
314
  function renderRecord(props) {
268
315
  const obj = isObject(props.value) ? props.value : {};
269
316
  const valueType = props.valueType;
270
317
  if (valueType === void 0) return null;
271
318
  const entries = Object.entries(obj);
272
- if (entries.length === 0) return /* @__PURE__ */ jsx("span", {
273
- "aria-readonly": props.readOnly ? "true" : void 0,
274
- children: ""
275
- });
276
- return /* @__PURE__ */ jsx("div", {
319
+ if (props.readOnly) {
320
+ if (entries.length === 0) return /* @__PURE__ */ jsx("span", {
321
+ "aria-readonly": "true",
322
+ children: "—"
323
+ });
324
+ return /* @__PURE__ */ jsx("div", {
325
+ role: "group",
326
+ "aria-label": props.meta.description ?? "Record",
327
+ children: entries.map(([key, value]) => {
328
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
329
+ htmlFor: inputId(`${props.path}.${key}`),
330
+ children: key
331
+ }), toReactNode(props.renderChild(valueType, value, () => {}, key))] }, key);
332
+ })
333
+ });
334
+ }
335
+ const handleRename = (oldKey, newKey) => {
336
+ const renamed = renameRecordKey(obj, oldKey, newKey);
337
+ if (renamed === obj) return;
338
+ props.onChange(renamed);
339
+ };
340
+ const handleValueChange = (key, nextValue) => {
341
+ const updated = {};
342
+ for (const [k, val] of Object.entries(obj)) updated[k] = val;
343
+ updated[key] = nextValue;
344
+ props.onChange(updated);
345
+ };
346
+ const handleRemove = (key) => {
347
+ const next = {};
348
+ for (const [k, v] of Object.entries(obj)) {
349
+ if (k === key) continue;
350
+ next[k] = v;
351
+ }
352
+ props.onChange(next);
353
+ };
354
+ const handleAdd = () => {
355
+ const newKey = nextRecordKey(Object.keys(obj));
356
+ const next = { ...obj };
357
+ next[newKey] = defaultRecordValue(valueType);
358
+ props.onChange(next);
359
+ };
360
+ return /* @__PURE__ */ jsxs("div", {
277
361
  role: "group",
278
362
  "aria-label": props.meta.description ?? "Record",
279
- children: entries.map(([key, value]) => {
280
- const childId = inputId(props.path ? `${props.path}.${key}` : key);
281
- const childOnChange = (nextValue) => {
282
- const updated = {};
283
- for (const [k, val] of Object.entries(obj)) updated[k] = val;
284
- updated[key] = nextValue;
285
- props.onChange(updated);
286
- };
287
- return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
288
- htmlFor: childId,
289
- children: key
290
- }), toReactNode(props.renderChild(valueType, value, childOnChange))] }, key);
291
- })
363
+ children: [entries.map(([key, value]) => {
364
+ return /* @__PURE__ */ jsxs("div", { children: [
365
+ /* @__PURE__ */ jsx("input", {
366
+ id: `${inputId(`${props.path}.${key}`)}-key`,
367
+ type: "text",
368
+ "aria-label": "Entry key",
369
+ defaultValue: key,
370
+ onBlur: (e) => {
371
+ handleRename(key, e.target.value);
372
+ }
373
+ }),
374
+ toReactNode(props.renderChild(valueType, value, (nextValue) => {
375
+ handleValueChange(key, nextValue);
376
+ }, key)),
377
+ /* @__PURE__ */ jsx("button", {
378
+ type: "button",
379
+ "aria-label": `Remove entry ${key}`,
380
+ onClick: () => {
381
+ handleRemove(key);
382
+ },
383
+ children: "Remove"
384
+ })
385
+ ] }, key);
386
+ }), /* @__PURE__ */ jsx("button", {
387
+ type: "button",
388
+ "aria-label": "Add entry",
389
+ onClick: handleAdd,
390
+ children: "Add"
391
+ })]
292
392
  });
293
393
  }
294
394
  function renderArray(props) {
@@ -305,7 +405,7 @@ function renderArray(props) {
305
405
  next[i] = v;
306
406
  props.onChange(next);
307
407
  };
308
- return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange)) }, String(i));
408
+ return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange, `[${String(i)}]`)) }, String(i));
309
409
  })
310
410
  });
311
411
  }
@@ -362,47 +462,65 @@ function renderDiscriminatedUnion(props) {
362
462
  });
363
463
  }
364
464
  /**
465
+ * Pure helper: convert a tab index into the new value the discriminated
466
+ * union should emit. Returns `undefined` when the index is out of bounds.
467
+ *
468
+ * Extracted from `DiscriminatedUnionTabs` so the contract is unit-testable
469
+ * without rendering the tabs component (which relies on React hooks).
470
+ */
471
+ function discriminatedUnionValueForTab(optionLabels, discKey, newIndex) {
472
+ const label = optionLabels[newIndex];
473
+ if (label === void 0) return void 0;
474
+ return { [discKey]: label };
475
+ }
476
+ /**
365
477
  * WAI-ARIA tabs component for discriminated unions.
366
- * Implements the full tabs keyboard pattern:
367
- * - Left/Right arrow keys move between tabs
368
- * - Home/End move to first/last tab
369
- * - Tab moves focus into the active panel
370
- * - aria-selected, aria-controls, role="tablist"/"tab"/"tabpanel"
478
+ *
479
+ * Implements the WAI-ARIA "Tabs with Automatic Activation" pattern
480
+ * (https://www.w3.org/WAI/ARIA/apg/patterns/tabs/):
481
+ * - ArrowRight / ArrowLeft move between tabs, wrapping at the extremes
482
+ * - Home / End jump to the first / last tab
483
+ * - aria-selected, aria-controls, role="tablist" / "tab" / "tabpanel"
484
+ * - Roving tabindex: the active tab has tabindex=0, the rest tabindex=-1
485
+ *
486
+ * "Automatic activation" means each arrow key both moves focus and
487
+ * activates the new tab in one step — selection and focus stay aligned.
371
488
  */
372
489
  function DiscriminatedUnionTabs({ options, optionLabels, activeIndex, panelId, discKey, props }) {
373
490
  const tabRefs = useRef([]);
491
+ const pendingFocusRef = useRef(false);
374
492
  const handleTabChange = useCallback((newIndex) => {
375
- const label = optionLabels[newIndex];
376
- if (label === void 0) return;
377
- props.onChange({ [discKey]: label });
493
+ const next = discriminatedUnionValueForTab(optionLabels, discKey, newIndex);
494
+ if (next === void 0) return;
495
+ props.onChange(next);
378
496
  }, [
379
497
  optionLabels,
380
498
  discKey,
381
499
  props
382
500
  ]);
383
- const focusTab = useCallback((index) => {
384
- const clamped = (index % options.length + options.length) % options.length;
385
- tabRefs.current[clamped]?.focus();
386
- }, [options.length]);
501
+ const wrapIndex = useCallback((index) => (index % options.length + options.length) % options.length, [options.length]);
387
502
  const handleKeyDown = useCallback((e) => {
388
- if (e.key === "ArrowRight") {
389
- e.preventDefault();
390
- focusTab(activeIndex + 1);
391
- } else if (e.key === "ArrowLeft") {
392
- e.preventDefault();
393
- focusTab(activeIndex - 1);
394
- } else if (e.key === "Home") {
395
- e.preventDefault();
396
- focusTab(0);
397
- } else if (e.key === "End") {
398
- e.preventDefault();
399
- focusTab(options.length - 1);
400
- }
503
+ let target;
504
+ if (e.key === "ArrowRight") target = wrapIndex(activeIndex + 1);
505
+ else if (e.key === "ArrowLeft") target = wrapIndex(activeIndex - 1);
506
+ else if (e.key === "Home") target = 0;
507
+ else if (e.key === "End") target = options.length - 1;
508
+ if (target === void 0) return;
509
+ e.preventDefault();
510
+ if (target === activeIndex) return;
511
+ pendingFocusRef.current = true;
512
+ handleTabChange(target);
401
513
  }, [
402
514
  activeIndex,
403
- focusTab,
404
- options.length
515
+ handleTabChange,
516
+ options.length,
517
+ wrapIndex
405
518
  ]);
519
+ useEffect(() => {
520
+ if (!pendingFocusRef.current) return;
521
+ pendingFocusRef.current = false;
522
+ tabRefs.current[activeIndex]?.focus();
523
+ }, [activeIndex]);
406
524
  const activeOption = options[activeIndex];
407
525
  return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
408
526
  role: "tablist",
@@ -504,4 +622,4 @@ function matchUnionOption(options, value) {
504
622
  if (typeof value === "object" && value !== null) return options.find((o) => o.type === "object");
505
623
  }
506
624
  //#endregion
507
- export { renderArray, renderBoolean, renderDiscriminatedUnion, renderEnum, renderFile, renderNumber, renderObject, renderRecord, renderRecursive, renderString, renderUnion, renderUnknown, toReactNode };
625
+ export { defaultRecordValue, discriminatedUnionValueForTab, inputId, nextRecordKey, renameRecordKey, renderArray, renderBoolean, renderDiscriminatedUnion, renderEnum, renderFile, renderNumber, renderObject, renderRecord, renderRecursive, renderString, renderUnion, renderUnknown, toReactNode };
@@ -69,8 +69,17 @@ interface RenderProps extends BaseFieldProps {
69
69
  * Render a child field. Theme adapters call this to recursively render
70
70
  * nested structures (object fields, array elements, union options).
71
71
  * The resolver and rendering context are already wired in.
72
+ *
73
+ * @param tree - The walked field tree for the child
74
+ * @param value - The child's current value
75
+ * @param onChange - Callback receiving the child's next value
76
+ * @param pathSuffix - Path segment from the parent (e.g. "city",
77
+ * "[0]"). Joined to the parent's path with a dot, or substituted
78
+ * when the parent acts as a transparent wrapper (union options).
79
+ * Required for every container — without it children inherit no
80
+ * path and `inputId()` will throw.
72
81
  */
73
- renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void) => unknown;
82
+ renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void, pathSuffix?: string) => unknown;
74
83
  }
75
84
  /**
76
85
  * Props for HTML render functions. Extends BaseFieldProps with:
@@ -1,9 +1,13 @@
1
- import { r as ComponentResolver } from "../renderer-BdSqllx5.mjs";
1
+ import { r as ComponentResolver } from "../renderer-B3s8o2B8.mjs";
2
2
 
3
3
  //#region src/themes/mantine.d.ts
4
4
  /**
5
5
  * Register real Mantine components for the resolver to use.
6
6
  * Call once at app startup before rendering.
7
+ *
8
+ * `Text` is required so read-only scalars render as a styled Mantine
9
+ * `<Text>` element instead of a bare `<span>`, matching the visual
10
+ * weight of the editable variants.
7
11
  */
8
12
  declare function registerMantineComponents(components: {
9
13
  TextInput: React.ElementType;
@@ -11,6 +15,7 @@ declare function registerMantineComponents(components: {
11
15
  Switch: React.ElementType;
12
16
  Select: React.ElementType;
13
17
  Fieldset: React.ElementType;
18
+ Text: React.ElementType;
14
19
  }): void;
15
20
  declare const mantineResolver: ComponentResolver;
16
21
  //#endregion
@@ -1,5 +1,5 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
- import { toReactNode } from "../react/headlessRenderers.mjs";
2
+ import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
3
3
  import { headlessResolver } from "../react/headless.mjs";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/themes/mantine.tsx
@@ -17,9 +17,14 @@ let MantineSwitch = (props) => /* @__PURE__ */ jsx("input", {
17
17
  });
18
18
  let MantineSelect = (props) => /* @__PURE__ */ jsx("select", { ...props });
19
19
  let MantineFieldset = (props) => /* @__PURE__ */ jsx("fieldset", { ...props });
20
+ let MantineText = (props) => /* @__PURE__ */ jsx("span", { ...props });
20
21
  /**
21
22
  * Register real Mantine components for the resolver to use.
22
23
  * Call once at app startup before rendering.
24
+ *
25
+ * `Text` is required so read-only scalars render as a styled Mantine
26
+ * `<Text>` element instead of a bare `<span>`, matching the visual
27
+ * weight of the editable variants.
23
28
  */
24
29
  function registerMantineComponents(components) {
25
30
  MantineTextInput = components.TextInput;
@@ -27,12 +32,18 @@ function registerMantineComponents(components) {
27
32
  MantineSwitch = components.Switch;
28
33
  MantineSelect = components.Select;
29
34
  MantineFieldset = components.Fieldset;
35
+ MantineText = components.Text;
30
36
  }
31
37
  function renderStringInput(props) {
32
38
  const strValue = typeof props.value === "string" ? props.value : "";
33
39
  const label = getLabel(props);
34
- if (props.readOnly) return /* @__PURE__ */ jsx("span", { children: strValue || "—" });
40
+ const id = inputId(props.path);
41
+ if (props.readOnly) return /* @__PURE__ */ jsx(MantineText, {
42
+ id,
43
+ children: strValue || "—"
44
+ });
35
45
  return /* @__PURE__ */ jsx(MantineTextInput, {
46
+ id,
36
47
  label,
37
48
  value: props.writeOnly ? "" : strValue,
38
49
  onChange: (e) => {
@@ -42,11 +53,19 @@ function renderStringInput(props) {
42
53
  }
43
54
  function renderNumberInput(props) {
44
55
  const label = getLabel(props);
56
+ const id = inputId(props.path);
45
57
  if (props.readOnly) {
46
- if (typeof props.value !== "number") return /* @__PURE__ */ jsx("span", { children: "—" });
47
- return /* @__PURE__ */ jsx("span", { children: props.value.toLocaleString() });
58
+ if (typeof props.value !== "number") return /* @__PURE__ */ jsx(MantineText, {
59
+ id,
60
+ children: "—"
61
+ });
62
+ return /* @__PURE__ */ jsx(MantineText, {
63
+ id,
64
+ children: props.value.toLocaleString()
65
+ });
48
66
  }
49
67
  return /* @__PURE__ */ jsx(MantineNumberInput, {
68
+ id,
50
69
  label,
51
70
  value: props.writeOnly ? void 0 : typeof props.value === "number" ? props.value : void 0,
52
71
  onChange: (v) => {
@@ -56,11 +75,19 @@ function renderNumberInput(props) {
56
75
  }
57
76
  function renderBooleanInput(props) {
58
77
  const label = getLabel(props);
78
+ const id = inputId(props.path);
59
79
  if (props.readOnly) {
60
- if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx("span", { children: "—" });
61
- return /* @__PURE__ */ jsx("span", { children: props.value ? "Yes" : "No" });
80
+ if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(MantineText, {
81
+ id,
82
+ children: "—"
83
+ });
84
+ return /* @__PURE__ */ jsx(MantineText, {
85
+ id,
86
+ children: props.value ? "Yes" : "No"
87
+ });
62
88
  }
63
89
  return /* @__PURE__ */ jsx(MantineSwitch, {
90
+ id,
64
91
  label,
65
92
  checked: props.writeOnly ? false : props.value === true,
66
93
  onChange: (e) => {
@@ -71,8 +98,13 @@ function renderBooleanInput(props) {
71
98
  function renderEnumInput(props) {
72
99
  const enumValue = typeof props.value === "string" ? props.value : "";
73
100
  const label = getLabel(props);
74
- if (props.readOnly) return /* @__PURE__ */ jsx("span", { children: enumValue || "—" });
101
+ const id = inputId(props.path);
102
+ if (props.readOnly) return /* @__PURE__ */ jsx(MantineText, {
103
+ id,
104
+ children: enumValue || "—"
105
+ });
75
106
  return /* @__PURE__ */ jsx(MantineSelect, {
107
+ id,
76
108
  label,
77
109
  value: props.writeOnly ? null : enumValue || null,
78
110
  onChange: (v) => {
@@ -100,7 +132,7 @@ function renderObjectContainer(props) {
100
132
  };
101
133
  return /* @__PURE__ */ jsx("div", {
102
134
  style: { marginBottom: "0.5rem" },
103
- children: toReactNode(props.renderChild(field, childValue, childOnChange))
135
+ children: toReactNode(props.renderChild(field, childValue, childOnChange, key))
104
136
  }, key);
105
137
  })
106
138
  });
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-BdSqllx5.mjs";
1
+ import { r as ComponentResolver } from "../renderer-B3s8o2B8.mjs";
2
2
 
3
3
  //#region src/themes/mui.d.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
- import { toReactNode } from "../react/headlessRenderers.mjs";
2
+ import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
3
3
  import { headlessResolver } from "../react/headless.mjs";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { isValidElement } from "react";
@@ -10,11 +10,14 @@ function ariaRequired(tree) {
10
10
  function renderStringInput(props) {
11
11
  const strValue = typeof props.value === "string" ? props.value : "";
12
12
  const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
13
+ const id = inputId(props.path);
13
14
  if (props.readOnly) return /* @__PURE__ */ jsx(MuiTypography, {
15
+ id,
14
16
  variant: "body2",
15
17
  children: strValue || "—"
16
18
  });
17
19
  return /* @__PURE__ */ jsx(MuiTextField, {
20
+ id,
18
21
  label,
19
22
  type: props.constraints.format === "email" ? "email" : props.constraints.format === "uri" ? "url" : "text",
20
23
  value: props.writeOnly ? "" : strValue,
@@ -33,17 +36,21 @@ function renderStringInput(props) {
33
36
  }
34
37
  function renderNumberInput(props) {
35
38
  const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
39
+ const id = inputId(props.path);
36
40
  if (props.readOnly) {
37
41
  if (typeof props.value !== "number") return /* @__PURE__ */ jsx(MuiTypography, {
42
+ id,
38
43
  variant: "body2",
39
44
  children: "—"
40
45
  });
41
46
  return /* @__PURE__ */ jsx(MuiTypography, {
47
+ id,
42
48
  variant: "body2",
43
49
  children: props.value.toLocaleString()
44
50
  });
45
51
  }
46
52
  return /* @__PURE__ */ jsx(MuiTextField, {
53
+ id,
47
54
  label,
48
55
  type: "number",
49
56
  value: props.writeOnly ? "" : typeof props.value === "number" ? props.value : "",
@@ -62,18 +69,22 @@ function renderNumberInput(props) {
62
69
  }
63
70
  function renderBooleanInput(props) {
64
71
  const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
72
+ const id = inputId(props.path);
65
73
  if (props.readOnly) {
66
74
  if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(MuiTypography, {
75
+ id,
67
76
  variant: "body2",
68
77
  children: "—"
69
78
  });
70
79
  return /* @__PURE__ */ jsx(MuiTypography, {
80
+ id,
71
81
  variant: "body2",
72
82
  children: props.value ? "Yes" : "No"
73
83
  });
74
84
  }
75
85
  return /* @__PURE__ */ jsx(MuiFormControlLabel, {
76
86
  control: /* @__PURE__ */ jsx(MuiCheckbox, {
87
+ id,
77
88
  checked: props.writeOnly ? false : props.value === true,
78
89
  onChange: (e) => {
79
90
  props.onChange(e.target.checked);
@@ -85,11 +96,14 @@ function renderBooleanInput(props) {
85
96
  function renderEnumInput(props) {
86
97
  const enumValue = typeof props.value === "string" ? props.value : "";
87
98
  const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
99
+ const id = inputId(props.path);
88
100
  if (props.readOnly) return /* @__PURE__ */ jsx(MuiTypography, {
101
+ id,
89
102
  variant: "body2",
90
103
  children: enumValue || "—"
91
104
  });
92
105
  return /* @__PURE__ */ jsxs(MuiTextField, {
106
+ id,
93
107
  select: true,
94
108
  label,
95
109
  value: props.writeOnly ? "" : enumValue,
@@ -130,7 +144,7 @@ function renderObjectContainer(props) {
130
144
  updated[key] = v;
131
145
  props.onChange(updated);
132
146
  };
133
- return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(field, childValue, childOnChange)) }, key);
147
+ return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(field, childValue, childOnChange, key)) }, key);
134
148
  })]
135
149
  });
136
150
  }
@@ -150,7 +164,7 @@ function renderArrayContainer(props) {
150
164
  next[i] = v;
151
165
  props.onChange(next);
152
166
  };
153
- return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange)) }, String(i));
167
+ return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange, `[${String(i)}]`)) }, String(i));
154
168
  })
155
169
  });
156
170
  }
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-BdSqllx5.mjs";
1
+ import { r as ComponentResolver } from "../renderer-B3s8o2B8.mjs";
2
2
 
3
3
  //#region src/themes/radix.d.ts
4
4
  /**