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.
- package/dist/core/renderer.d.mts +1 -1
- package/dist/html/a11y.d.mts +1 -1
- package/dist/html/renderToHtml.d.mts +1 -1
- package/dist/html/renderToHtmlStream.d.mts +1 -1
- package/dist/html/renderers.d.mts +1 -1
- package/dist/html/streamRenderers.d.mts +1 -1
- package/dist/openapi/components.mjs +42 -13
- package/dist/react/SchemaComponent.d.mts +32 -4
- package/dist/react/SchemaComponent.mjs +50 -15
- package/dist/react/SchemaView.d.mts +9 -2
- package/dist/react/SchemaView.mjs +17 -11
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +41 -2
- package/dist/react/headlessRenderers.mjs +171 -53
- package/dist/{renderer-BdSqllx5.d.mts → renderer-B3s8o2B8.d.mts} +10 -1
- package/dist/themes/mantine.d.mts +6 -1
- package/dist/themes/mantine.mjs +40 -8
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/mui.mjs +17 -3
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/radix.mjs +41 -9
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/themes/shadcn.mjs +22 -5
- package/package.json +5 -2
|
@@ -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
|
|
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)
|
|
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(
|
|
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 (
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
*
|
|
367
|
-
* -
|
|
368
|
-
*
|
|
369
|
-
* -
|
|
370
|
-
* -
|
|
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
|
|
376
|
-
if (
|
|
377
|
-
props.onChange(
|
|
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
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
package/dist/themes/mantine.mjs
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
47
|
-
|
|
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(
|
|
61
|
-
|
|
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
|
-
|
|
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
|
});
|
package/dist/themes/mui.d.mts
CHANGED
package/dist/themes/mui.mjs
CHANGED
|
@@ -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
|
}
|
package/dist/themes/radix.d.mts
CHANGED