schema-components 1.16.1 → 1.16.3
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/walker.mjs +1 -1
- package/dist/html/renderToHtml.mjs +2 -2
- package/dist/html/renderToHtmlStream.mjs +1 -1
- package/dist/html/streamRenderers.mjs +2 -2
- package/dist/openapi/components.mjs +1 -1
- package/dist/react/SchemaComponent.d.mts +1 -1
- package/dist/react/SchemaComponent.mjs +2 -2
- package/dist/react/SchemaView.mjs +2 -2
- package/dist/react/headlessRenderers.d.mts +27 -1
- package/dist/react/headlessRenderers.mjs +127 -23
- package/dist/themes/mui.mjs +1 -1
- package/package.json +1 -1
package/dist/core/walker.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isObject } from "./guards.mjs";
|
|
2
2
|
import { appendPointer, emitDiagnostic } from "./diagnostics.mjs";
|
|
3
3
|
import { countDistinctRefs, resolveRef } from "./ref.mjs";
|
|
4
|
-
import { ANNOTATION_SIBLINGS, detectDiscriminated, mergeAllOf, mergeRefSiblings, normaliseAnyOf } from "./merge.mjs";
|
|
5
4
|
import { extractArrayConstraints, extractObjectConstraints, stripInapplicableConstraints } from "./constraints.mjs";
|
|
5
|
+
import { ANNOTATION_SIBLINGS, detectDiscriminated, mergeAllOf, mergeRefSiblings, normaliseAnyOf } from "./merge.mjs";
|
|
6
6
|
import { buildBase, buildBooleanField, buildFileField, buildNullField, buildNumberField, buildStringField, buildUnknownField, extractChildOverride, extractSchemaMetaFields, getArray, getObject, getString, isPrimitive, walkDependentRequiredMap, walkSubSchemaMap, withoutKeys } from "./walkBuilders.mjs";
|
|
7
7
|
//#region src/core/walker.ts
|
|
8
8
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { h, serialize } from "./html.mjs";
|
|
2
1
|
import { normaliseSchema } from "../core/adapter.mjs";
|
|
3
|
-
import { walk } from "../core/walker.mjs";
|
|
4
2
|
import { getHtmlRenderFn, mergeHtmlResolvers } from "../core/renderer.mjs";
|
|
3
|
+
import { walk } from "../core/walker.mjs";
|
|
4
|
+
import { h, serialize } from "./html.mjs";
|
|
5
5
|
import { defaultHtmlResolver } from "./renderers.mjs";
|
|
6
6
|
//#region src/html/renderToHtml.ts
|
|
7
7
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { normaliseSchema } from "../core/adapter.mjs";
|
|
2
|
-
import { walk } from "../core/walker.mjs";
|
|
3
2
|
import { mergeHtmlResolvers } from "../core/renderer.mjs";
|
|
3
|
+
import { walk } from "../core/walker.mjs";
|
|
4
4
|
import { defaultHtmlResolver } from "./renderers.mjs";
|
|
5
5
|
import { streamField } from "./streamRenderers.mjs";
|
|
6
6
|
//#region src/html/renderToHtmlStream.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { VOID_ELEMENTS, h, raw, serialize, serializeAttributes } from "./html.mjs";
|
|
2
|
-
import { ariaLabelAttrs, buildHintElement, buildInputId, requiredIndicator } from "./a11y.mjs";
|
|
3
1
|
import { isObject } from "../core/guards.mjs";
|
|
4
2
|
import { getHtmlRenderFn } from "../core/renderer.mjs";
|
|
3
|
+
import { VOID_ELEMENTS, h, raw, serialize, serializeAttributes } from "./html.mjs";
|
|
4
|
+
import { ariaLabelAttrs, buildHintElement, buildInputId, requiredIndicator } from "./a11y.mjs";
|
|
5
5
|
//#region src/html/streamRenderers.ts
|
|
6
6
|
function yieldOpen(el) {
|
|
7
7
|
const attrStr = serializeAttributes(el.attributes);
|
|
@@ -2,12 +2,12 @@ import { toRecordOrUndefined } from "../core/guards.mjs";
|
|
|
2
2
|
import { SchemaNormalisationError } from "../core/errors.mjs";
|
|
3
3
|
import { normaliseSchema } from "../core/adapter.mjs";
|
|
4
4
|
import { walk } from "../core/walker.mjs";
|
|
5
|
-
import { renderField } from "../react/SchemaComponent.mjs";
|
|
6
5
|
import { ApiCallbacks } from "./ApiCallbacks.mjs";
|
|
7
6
|
import { ApiLinks } from "./ApiLinks.mjs";
|
|
8
7
|
import { ApiResponseHeaders } from "./ApiResponseHeaders.mjs";
|
|
9
8
|
import { ApiSecurity } from "./ApiSecurity.mjs";
|
|
10
9
|
import { getLinks, getSecurityRequirements, getSecuritySchemes, listCallbacks } from "./parser.mjs";
|
|
10
|
+
import { renderField } from "../react/SchemaComponent.mjs";
|
|
11
11
|
import { getParsed, resolveOperation, resolveParameters, resolveRequestBody, resolveResponse, toDoc } from "./resolve.mjs";
|
|
12
12
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
13
|
//#region src/openapi/components.tsx
|
|
@@ -4,8 +4,8 @@ import { t as SchemaError } from "../errors-C5zRC2PU.mjs";
|
|
|
4
4
|
import { l as RenderProps, r as ComponentResolver } from "../renderer-BdSqllx5.mjs";
|
|
5
5
|
import { c as ResolveOpenAPIRef, s as PathOfType, t as FromJSONSchema } from "../typeInference-k7FXfTVO.mjs";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
-
import { ReactNode } from "react";
|
|
8
7
|
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
8
|
+
import { ReactNode } from "react";
|
|
9
9
|
|
|
10
10
|
//#region src/react/SchemaComponent.d.ts
|
|
11
11
|
/**
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { isObject, toRecordOrUndefined } from "../core/guards.mjs";
|
|
3
3
|
import { SchemaFieldError, SchemaNormalisationError, SchemaRenderError } from "../core/errors.mjs";
|
|
4
4
|
import { normaliseSchema } from "../core/adapter.mjs";
|
|
5
|
-
import { walk } from "../core/walker.mjs";
|
|
6
5
|
import { getRenderFunction, mergeResolvers } from "../core/renderer.mjs";
|
|
6
|
+
import { walk } from "../core/walker.mjs";
|
|
7
7
|
import { headlessResolver } from "./headless.mjs";
|
|
8
8
|
import { resolvePath, resolveValue, setNestedValue } from "./fieldPath.mjs";
|
|
9
9
|
import { z } from "zod";
|
|
10
|
-
import { createContext, isValidElement, useCallback, useContext, useMemo } from "react";
|
|
11
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { createContext, isValidElement, useCallback, useContext, useMemo } from "react";
|
|
12
12
|
//#region src/react/SchemaComponent.tsx
|
|
13
13
|
/**
|
|
14
14
|
* <SchemaComponent> — renders UI from Zod, JSON Schema, or OpenAPI schemas.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { SchemaNormalisationError, SchemaRenderError } from "../core/errors.mjs";
|
|
2
2
|
import { normaliseSchema } from "../core/adapter.mjs";
|
|
3
|
-
import { walk } from "../core/walker.mjs";
|
|
4
3
|
import { getRenderFunction, mergeResolvers } from "../core/renderer.mjs";
|
|
4
|
+
import { walk } from "../core/walker.mjs";
|
|
5
5
|
import { headlessResolver } from "./headless.mjs";
|
|
6
|
-
import { createElement, isValidElement } from "react";
|
|
7
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
import { createElement, isValidElement } from "react";
|
|
8
8
|
//#region src/react/SchemaView.tsx
|
|
9
9
|
/**
|
|
10
10
|
* React Server Component for read-only schema rendering.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { M as WalkedField } from "../types-D_5ST7SS.mjs";
|
|
1
2
|
import { l as RenderProps } from "../renderer-BdSqllx5.mjs";
|
|
2
3
|
import { ReactNode } from "react";
|
|
3
4
|
|
|
@@ -12,12 +13,37 @@ declare function renderNumber(props: RenderProps): ReactNode;
|
|
|
12
13
|
declare function renderBoolean(props: RenderProps): ReactNode;
|
|
13
14
|
declare function renderEnum(props: RenderProps): ReactNode;
|
|
14
15
|
declare function renderObject(props: RenderProps): ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* Compute the default value for a freshly added record entry based on the
|
|
18
|
+
* record's value-type schema. Mirrors the read of `defaultValue` used
|
|
19
|
+
* elsewhere in the renderer, falling back to a type-appropriate empty value.
|
|
20
|
+
*/
|
|
21
|
+
declare function defaultRecordValue(valueType: WalkedField): unknown;
|
|
22
|
+
/**
|
|
23
|
+
* Generate a unique, currently-unused key for a new record entry.
|
|
24
|
+
* Picks the first of `key`, `key-1`, `key-2`, … that is not in `existing`.
|
|
25
|
+
*/
|
|
26
|
+
declare function nextRecordKey(existing: readonly string[], base?: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Rename a key in an object while preserving insertion order. Returns the
|
|
29
|
+
* original object reference when the rename is a no-op (oldKey === newKey)
|
|
30
|
+
* or when newKey collides with an existing key.
|
|
31
|
+
*/
|
|
32
|
+
declare function renameRecordKey(obj: Record<string, unknown>, oldKey: string, newKey: string): Record<string, unknown>;
|
|
15
33
|
declare function renderRecord(props: RenderProps): ReactNode;
|
|
16
34
|
declare function renderArray(props: RenderProps): ReactNode;
|
|
17
35
|
declare function renderUnion(props: RenderProps): ReactNode;
|
|
18
36
|
declare function renderDiscriminatedUnion(props: RenderProps): ReactNode;
|
|
37
|
+
/**
|
|
38
|
+
* Pure helper: convert a tab index into the new value the discriminated
|
|
39
|
+
* union should emit. Returns `undefined` when the index is out of bounds.
|
|
40
|
+
*
|
|
41
|
+
* Extracted from `DiscriminatedUnionTabs` so the contract is unit-testable
|
|
42
|
+
* without rendering the tabs component (which relies on React hooks).
|
|
43
|
+
*/
|
|
44
|
+
declare function discriminatedUnionValueForTab(optionLabels: readonly string[], discKey: string, newIndex: number): Record<string, string> | undefined;
|
|
19
45
|
declare function renderFile(props: RenderProps): ReactNode;
|
|
20
46
|
declare function renderRecursive(props: RenderProps): ReactNode;
|
|
21
47
|
declare function renderUnknown(props: RenderProps): ReactNode;
|
|
22
48
|
//#endregion
|
|
23
|
-
export { renderArray, renderBoolean, renderDiscriminatedUnion, renderEnum, renderFile, renderNumber, renderObject, renderRecord, renderRecursive, renderString, renderUnion, renderUnknown, toReactNode };
|
|
49
|
+
export { defaultRecordValue, discriminatedUnionValueForTab, nextRecordKey, renameRecordKey, renderArray, renderBoolean, renderDiscriminatedUnion, renderEnum, renderFile, renderNumber, renderObject, renderRecord, renderRecursive, renderString, renderUnion, renderUnknown, toReactNode };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isObject } from "../core/guards.mjs";
|
|
2
|
-
import { isValidElement, useCallback, useRef } from "react";
|
|
3
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { isValidElement, useCallback, useRef } from "react";
|
|
4
4
|
//#region src/react/headlessRenderers.tsx
|
|
5
5
|
/**
|
|
6
6
|
* Headless renderer functions — one per schema type.
|
|
@@ -264,31 +264,123 @@ function renderObject(props) {
|
|
|
264
264
|
}), child] }, key);
|
|
265
265
|
})] });
|
|
266
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Compute the default value for a freshly added record entry based on the
|
|
269
|
+
* record's value-type schema. Mirrors the read of `defaultValue` used
|
|
270
|
+
* elsewhere in the renderer, falling back to a type-appropriate empty value.
|
|
271
|
+
*/
|
|
272
|
+
function defaultRecordValue(valueType) {
|
|
273
|
+
if (valueType.defaultValue !== void 0) return valueType.defaultValue;
|
|
274
|
+
switch (valueType.type) {
|
|
275
|
+
case "string": return "";
|
|
276
|
+
case "number": return 0;
|
|
277
|
+
case "boolean": return false;
|
|
278
|
+
case "array": return [];
|
|
279
|
+
case "object":
|
|
280
|
+
case "record": return {};
|
|
281
|
+
default: return;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Generate a unique, currently-unused key for a new record entry.
|
|
286
|
+
* Picks the first of `key`, `key-1`, `key-2`, … that is not in `existing`.
|
|
287
|
+
*/
|
|
288
|
+
function nextRecordKey(existing, base = "key") {
|
|
289
|
+
if (!existing.includes(base)) return base;
|
|
290
|
+
let i = 1;
|
|
291
|
+
while (existing.includes(`${base}-${String(i)}`)) i += 1;
|
|
292
|
+
return `${base}-${String(i)}`;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Rename a key in an object while preserving insertion order. Returns the
|
|
296
|
+
* original object reference when the rename is a no-op (oldKey === newKey)
|
|
297
|
+
* or when newKey collides with an existing key.
|
|
298
|
+
*/
|
|
299
|
+
function renameRecordKey(obj, oldKey, newKey) {
|
|
300
|
+
if (oldKey === newKey) return obj;
|
|
301
|
+
if (newKey in obj && newKey !== oldKey) return obj;
|
|
302
|
+
const renamed = {};
|
|
303
|
+
for (const [k, v] of Object.entries(obj)) renamed[k === oldKey ? newKey : k] = v;
|
|
304
|
+
return renamed;
|
|
305
|
+
}
|
|
267
306
|
function renderRecord(props) {
|
|
268
307
|
const obj = isObject(props.value) ? props.value : {};
|
|
269
308
|
const valueType = props.valueType;
|
|
270
309
|
if (valueType === void 0) return null;
|
|
271
310
|
const entries = Object.entries(obj);
|
|
272
|
-
if (
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
311
|
+
if (props.readOnly) {
|
|
312
|
+
if (entries.length === 0) return /* @__PURE__ */ jsx("span", {
|
|
313
|
+
"aria-readonly": "true",
|
|
314
|
+
children: "—"
|
|
315
|
+
});
|
|
316
|
+
return /* @__PURE__ */ jsx("div", {
|
|
317
|
+
role: "group",
|
|
318
|
+
"aria-label": props.meta.description ?? "Record",
|
|
319
|
+
children: entries.map(([key, value]) => {
|
|
320
|
+
return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
321
|
+
htmlFor: inputId(props.path ? `${props.path}.${key}` : key),
|
|
322
|
+
children: key
|
|
323
|
+
}), toReactNode(props.renderChild(valueType, value, () => {}))] }, key);
|
|
324
|
+
})
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
const handleRename = (oldKey, newKey) => {
|
|
328
|
+
const renamed = renameRecordKey(obj, oldKey, newKey);
|
|
329
|
+
if (renamed === obj) return;
|
|
330
|
+
props.onChange(renamed);
|
|
331
|
+
};
|
|
332
|
+
const handleValueChange = (key, nextValue) => {
|
|
333
|
+
const updated = {};
|
|
334
|
+
for (const [k, val] of Object.entries(obj)) updated[k] = val;
|
|
335
|
+
updated[key] = nextValue;
|
|
336
|
+
props.onChange(updated);
|
|
337
|
+
};
|
|
338
|
+
const handleRemove = (key) => {
|
|
339
|
+
const next = {};
|
|
340
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
341
|
+
if (k === key) continue;
|
|
342
|
+
next[k] = v;
|
|
343
|
+
}
|
|
344
|
+
props.onChange(next);
|
|
345
|
+
};
|
|
346
|
+
const handleAdd = () => {
|
|
347
|
+
const newKey = nextRecordKey(Object.keys(obj));
|
|
348
|
+
const next = { ...obj };
|
|
349
|
+
next[newKey] = defaultRecordValue(valueType);
|
|
350
|
+
props.onChange(next);
|
|
351
|
+
};
|
|
352
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
277
353
|
role: "group",
|
|
278
354
|
"aria-label": props.meta.description ?? "Record",
|
|
279
|
-
children: entries.map(([key, value]) => {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
355
|
+
children: [entries.map(([key, value]) => {
|
|
356
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
357
|
+
/* @__PURE__ */ jsx("input", {
|
|
358
|
+
id: `${inputId(props.path ? `${props.path}.${key}` : key)}-key`,
|
|
359
|
+
type: "text",
|
|
360
|
+
"aria-label": "Entry key",
|
|
361
|
+
defaultValue: key,
|
|
362
|
+
onBlur: (e) => {
|
|
363
|
+
handleRename(key, e.target.value);
|
|
364
|
+
}
|
|
365
|
+
}),
|
|
366
|
+
toReactNode(props.renderChild(valueType, value, (nextValue) => {
|
|
367
|
+
handleValueChange(key, nextValue);
|
|
368
|
+
})),
|
|
369
|
+
/* @__PURE__ */ jsx("button", {
|
|
370
|
+
type: "button",
|
|
371
|
+
"aria-label": `Remove entry ${key}`,
|
|
372
|
+
onClick: () => {
|
|
373
|
+
handleRemove(key);
|
|
374
|
+
},
|
|
375
|
+
children: "Remove"
|
|
376
|
+
})
|
|
377
|
+
] }, key);
|
|
378
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
379
|
+
type: "button",
|
|
380
|
+
"aria-label": "Add entry",
|
|
381
|
+
onClick: handleAdd,
|
|
382
|
+
children: "Add"
|
|
383
|
+
})]
|
|
292
384
|
});
|
|
293
385
|
}
|
|
294
386
|
function renderArray(props) {
|
|
@@ -362,6 +454,18 @@ function renderDiscriminatedUnion(props) {
|
|
|
362
454
|
});
|
|
363
455
|
}
|
|
364
456
|
/**
|
|
457
|
+
* Pure helper: convert a tab index into the new value the discriminated
|
|
458
|
+
* union should emit. Returns `undefined` when the index is out of bounds.
|
|
459
|
+
*
|
|
460
|
+
* Extracted from `DiscriminatedUnionTabs` so the contract is unit-testable
|
|
461
|
+
* without rendering the tabs component (which relies on React hooks).
|
|
462
|
+
*/
|
|
463
|
+
function discriminatedUnionValueForTab(optionLabels, discKey, newIndex) {
|
|
464
|
+
const label = optionLabels[newIndex];
|
|
465
|
+
if (label === void 0) return void 0;
|
|
466
|
+
return { [discKey]: label };
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
365
469
|
* WAI-ARIA tabs component for discriminated unions.
|
|
366
470
|
* Implements the full tabs keyboard pattern:
|
|
367
471
|
* - Left/Right arrow keys move between tabs
|
|
@@ -372,9 +476,9 @@ function renderDiscriminatedUnion(props) {
|
|
|
372
476
|
function DiscriminatedUnionTabs({ options, optionLabels, activeIndex, panelId, discKey, props }) {
|
|
373
477
|
const tabRefs = useRef([]);
|
|
374
478
|
const handleTabChange = useCallback((newIndex) => {
|
|
375
|
-
const
|
|
376
|
-
if (
|
|
377
|
-
props.onChange(
|
|
479
|
+
const next = discriminatedUnionValueForTab(optionLabels, discKey, newIndex);
|
|
480
|
+
if (next === void 0) return;
|
|
481
|
+
props.onChange(next);
|
|
378
482
|
}, [
|
|
379
483
|
optionLabels,
|
|
380
484
|
discKey,
|
|
@@ -504,4 +608,4 @@ function matchUnionOption(options, value) {
|
|
|
504
608
|
if (typeof value === "object" && value !== null) return options.find((o) => o.type === "object");
|
|
505
609
|
}
|
|
506
610
|
//#endregion
|
|
507
|
-
export { renderArray, renderBoolean, renderDiscriminatedUnion, renderEnum, renderFile, renderNumber, renderObject, renderRecord, renderRecursive, renderString, renderUnion, renderUnknown, toReactNode };
|
|
611
|
+
export { defaultRecordValue, discriminatedUnionValueForTab, nextRecordKey, renameRecordKey, renderArray, renderBoolean, renderDiscriminatedUnion, renderEnum, renderFile, renderNumber, renderObject, renderRecord, renderRecursive, renderString, renderUnion, renderUnknown, toReactNode };
|
package/dist/themes/mui.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isObject } from "../core/guards.mjs";
|
|
2
2
|
import { toReactNode } from "../react/headlessRenderers.mjs";
|
|
3
3
|
import { headlessResolver } from "../react/headless.mjs";
|
|
4
|
-
import { isValidElement } from "react";
|
|
5
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { isValidElement } from "react";
|
|
6
6
|
//#region src/themes/mui.tsx
|
|
7
7
|
function ariaRequired(tree) {
|
|
8
8
|
return { required: tree.isOptional === false };
|