schema-components 1.5.1 → 1.7.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/CHANGELOG.md +16 -0
- package/dist/core/adapter.d.mts +1 -1
- package/dist/core/renderer.d.mts +1 -1
- package/dist/core/types.d.mts +1 -1
- package/dist/core/walker.d.mts +1 -1
- package/dist/core/walker.mjs +3 -1
- package/dist/html/a11y.d.mts +1 -1
- package/dist/html/renderToHtml.d.mts +1 -1
- package/dist/html/renderToHtml.mjs +6 -2
- package/dist/openapi/components.d.mts +1 -1
- package/dist/openapi/parser.d.mts +1 -1
- package/dist/react/SchemaComponent.d.mts +1 -1
- package/dist/react/SchemaComponent.mjs +46 -10
- package/dist/react/SchemaView.d.mts +1 -1
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headless.mjs +4 -1
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/{types-DDCD6Xnx.d.mts → types-BJzEgJdX.d.mts} +12 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## [1.7.0](https://github.com/Mearman/schema-components/compare/v1.6.0...v1.7.0) (2026-05-14)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* add field visibility and ordering controls ([27cb3e1](https://github.com/Mearman/schema-components/commit/27cb3e19ba930200116c79293078fa6fa2743723))
|
|
6
|
+
|
|
7
|
+
## [1.6.0](https://github.com/Mearman/schema-components/compare/v1.5.1...v1.6.0) (2026-05-14)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* add per-field onValidationError and writeOnly tests ([bc65589](https://github.com/Mearman/schema-components/commit/bc655893601b8e015dd3c717eccc9bf9dffbbb42))
|
|
12
|
+
|
|
13
|
+
### Tests
|
|
14
|
+
|
|
15
|
+
* add writeOnly behaviour tests across all renderers ([abfb293](https://github.com/Mearman/schema-components/commit/abfb293498efc4d4095dec072fd1e5a9c3239035))
|
|
16
|
+
|
|
1
17
|
## [1.5.1](https://github.com/Mearman/schema-components/compare/v1.5.0...v1.5.1) (2026-05-14)
|
|
2
18
|
|
|
3
19
|
### Bug Fixes
|
package/dist/core/adapter.d.mts
CHANGED
package/dist/core/renderer.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as mergeResolvers, C as HtmlResolver, D as getHtmlRenderFn, E as RenderProps, O as getRenderFunction, S as HtmlRenderProps, T as RenderFunction, b as ComponentResolver, j as typeToKey, k as mergeHtmlResolvers, w as RESOLVER_KEYS, x as HtmlRenderFunction, y as BaseFieldProps } from "../types-
|
|
1
|
+
import { A as mergeResolvers, C as HtmlResolver, D as getHtmlRenderFn, E as RenderProps, O as getRenderFunction, S as HtmlRenderProps, T as RenderFunction, b as ComponentResolver, j as typeToKey, k as mergeHtmlResolvers, w as RESOLVER_KEYS, x as HtmlRenderFunction, y as BaseFieldProps } from "../types-BJzEgJdX.mjs";
|
|
2
2
|
export { BaseFieldProps, ComponentResolver, HtmlRenderFunction, HtmlRenderProps, HtmlResolver, RESOLVER_KEYS, RenderFunction, RenderProps, getHtmlRenderFn, getRenderFunction, mergeHtmlResolvers, mergeResolvers, typeToKey };
|
package/dist/core/types.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as HtmlResolver, E as RenderProps, S as HtmlRenderProps, T as RenderFunction, _ as WalkedField, a as FromJSONSchema, b as ComponentResolver, c as InferResponseFields, d as OpenAPIResponseType, f as PathOfType, g as TypeAtPath, h as SchemaType, i as FieldOverrides, l as JsonObject, m as SchemaMeta, n as FieldConstraints, o as InferParameterOverrides, p as ResolveOpenAPIRef, r as FieldOverride, s as InferRequestBodyFields, t as Editability, u as OpenAPIRequestBodyType, v as resolveEditability, x as HtmlRenderFunction, y as BaseFieldProps } from "../types-
|
|
1
|
+
import { C as HtmlResolver, E as RenderProps, S as HtmlRenderProps, T as RenderFunction, _ as WalkedField, a as FromJSONSchema, b as ComponentResolver, c as InferResponseFields, d as OpenAPIResponseType, f as PathOfType, g as TypeAtPath, h as SchemaType, i as FieldOverrides, l as JsonObject, m as SchemaMeta, n as FieldConstraints, o as InferParameterOverrides, p as ResolveOpenAPIRef, r as FieldOverride, s as InferRequestBodyFields, t as Editability, u as OpenAPIRequestBodyType, v as resolveEditability, x as HtmlRenderFunction, y as BaseFieldProps } from "../types-BJzEgJdX.mjs";
|
|
2
2
|
import { i as SchemaRenderError, n as SchemaFieldError, r as SchemaNormalisationError, t as SchemaError } from "../errors-DIKI2C78.mjs";
|
|
3
3
|
export { BaseFieldProps, ComponentResolver, Editability, FieldConstraints, FieldOverride, FieldOverrides, FromJSONSchema, HtmlRenderFunction, HtmlRenderProps, HtmlResolver, InferParameterOverrides, InferRequestBodyFields, InferResponseFields, JsonObject, OpenAPIRequestBodyType, OpenAPIResponseType, PathOfType, RenderFunction, RenderProps, ResolveOpenAPIRef, SchemaError, SchemaFieldError, SchemaMeta, SchemaNormalisationError, SchemaRenderError, SchemaType, TypeAtPath, WalkedField, resolveEditability };
|
package/dist/core/walker.d.mts
CHANGED
package/dist/core/walker.mjs
CHANGED
|
@@ -160,7 +160,9 @@ const OVERRIDE_META_KEYS = new Set([
|
|
|
160
160
|
"description",
|
|
161
161
|
"title",
|
|
162
162
|
"deprecated",
|
|
163
|
-
"component"
|
|
163
|
+
"component",
|
|
164
|
+
"visible",
|
|
165
|
+
"order"
|
|
164
166
|
]);
|
|
165
167
|
function extractSchemaMetaFields(overrides) {
|
|
166
168
|
if (overrides === void 0) return void 0;
|
package/dist/html/a11y.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as HtmlResolver, S as HtmlRenderProps, m as SchemaMeta, x as HtmlRenderFunction } from "../types-
|
|
1
|
+
import { C as HtmlResolver, S as HtmlRenderProps, m as SchemaMeta, x as HtmlRenderFunction } from "../types-BJzEgJdX.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/html/renderToHtml.d.ts
|
|
4
4
|
interface RenderToHtmlOptions {
|
|
@@ -180,10 +180,13 @@ function renderObjectNode(props) {
|
|
|
180
180
|
const obj = isRecord(props.value) ? props.value : {};
|
|
181
181
|
const descriptionText = typeof props.meta.description === "string" ? props.meta.description : void 0;
|
|
182
182
|
const legend = descriptionText !== void 0 ? h("legend", {}, descriptionText) : void 0;
|
|
183
|
+
const sortedEntries = Object.entries(fields).sort((a, b) => {
|
|
184
|
+
return (typeof a[1].meta.order === "number" ? a[1].meta.order : Infinity) - (typeof b[1].meta.order === "number" ? b[1].meta.order : Infinity);
|
|
185
|
+
}).filter(([, field]) => field.meta.visible !== false);
|
|
183
186
|
if (props.readOnly) {
|
|
184
187
|
const children = [];
|
|
185
188
|
if (legend !== void 0) children.push(legend);
|
|
186
|
-
for (const [key, field] of
|
|
189
|
+
for (const [key, field] of sortedEntries) {
|
|
187
190
|
const label = typeof field.meta.description === "string" ? field.meta.description : key;
|
|
188
191
|
const childValue = obj[key];
|
|
189
192
|
const childHtml = props.renderChild(field, childValue, props.path ? `${props.path}.${key}` : key);
|
|
@@ -196,7 +199,7 @@ function renderObjectNode(props) {
|
|
|
196
199
|
}
|
|
197
200
|
const children = [];
|
|
198
201
|
if (legend !== void 0) children.push(legend);
|
|
199
|
-
for (const [key, field] of
|
|
202
|
+
for (const [key, field] of sortedEntries) {
|
|
200
203
|
const label = typeof field.meta.description === "string" ? field.meta.description : key;
|
|
201
204
|
const fieldId = buildInputId(props.path, key);
|
|
202
205
|
const childPath = props.path ? `${props.path}.${key}` : key;
|
|
@@ -413,6 +416,7 @@ function renderToHtml(schema, options = {}) {
|
|
|
413
416
|
return renderFieldHtml(tree, options.value ?? tree.defaultValue, resolver, "", renderChild);
|
|
414
417
|
}
|
|
415
418
|
function renderFieldHtml(tree, value, resolver, path, renderChild) {
|
|
419
|
+
if (tree.meta.visible === false) return "";
|
|
416
420
|
const effectiveValue = value ?? tree.defaultValue;
|
|
417
421
|
const mergedResolver = mergeHtmlResolvers(resolver, defaultHtmlResolver);
|
|
418
422
|
const renderFn = getHtmlRenderFn(tree.type, mergedResolver);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as InferResponseFields, m as SchemaMeta, o as InferParameterOverrides, r as FieldOverride, s as InferRequestBodyFields } from "../types-
|
|
1
|
+
import { c as InferResponseFields, m as SchemaMeta, o as InferParameterOverrides, r as FieldOverride, s as InferRequestBodyFields } from "../types-BJzEgJdX.mjs";
|
|
2
2
|
import { WidgetMap } from "../react/SchemaComponent.mjs";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { E as RenderProps, _ as WalkedField, a as FromJSONSchema, b as ComponentResolver, f as PathOfType, i as FieldOverrides, m as SchemaMeta, p as ResolveOpenAPIRef, r as FieldOverride } from "../types-
|
|
1
|
+
import { E as RenderProps, _ as WalkedField, a as FromJSONSchema, b as ComponentResolver, f as PathOfType, i as FieldOverrides, m as SchemaMeta, p as ResolveOpenAPIRef, r as FieldOverride } from "../types-BJzEgJdX.mjs";
|
|
2
2
|
import { t as SchemaError } from "../errors-DIKI2C78.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { ReactNode } from "react";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { isObject, toRecord } from "../core/guards.mjs";
|
|
2
|
+
import { isObject, toRecord, toRecordOrUndefined } from "../core/guards.mjs";
|
|
3
3
|
import { normaliseSchema } from "../core/adapter.mjs";
|
|
4
4
|
import { SchemaFieldError, SchemaNormalisationError, SchemaRenderError } from "../core/errors.mjs";
|
|
5
5
|
import { getRenderFunction, mergeResolvers } from "../core/renderer.mjs";
|
|
@@ -79,14 +79,21 @@ function SchemaComponent({ schema: schemaInput, ref: refInput, value, onChange,
|
|
|
79
79
|
throw error;
|
|
80
80
|
}
|
|
81
81
|
const handleChange = useCallback((nextValue) => {
|
|
82
|
-
if (validate)
|
|
82
|
+
if (validate) {
|
|
83
|
+
const error = runValidation(zodSchema, jsonSchema, nextValue);
|
|
84
|
+
if (error !== void 0) {
|
|
85
|
+
onValidationError?.(error);
|
|
86
|
+
dispatchFieldErrors(fields, error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
83
89
|
onChange?.(nextValue);
|
|
84
90
|
}, [
|
|
85
91
|
validate,
|
|
86
92
|
zodSchema,
|
|
87
93
|
jsonSchema,
|
|
88
94
|
onChange,
|
|
89
|
-
onValidationError
|
|
95
|
+
onValidationError,
|
|
96
|
+
fields
|
|
90
97
|
]);
|
|
91
98
|
const tree = walk(jsonSchema, {
|
|
92
99
|
componentMeta: mergedMeta,
|
|
@@ -99,15 +106,12 @@ function SchemaComponent({ schema: schemaInput, ref: refInput, value, onChange,
|
|
|
99
106
|
};
|
|
100
107
|
return renderField(tree, value ?? tree.defaultValue, handleChange, userResolver, renderChild, instanceWidgets, contextWidgets);
|
|
101
108
|
}
|
|
102
|
-
function runValidation(zodSchema, jsonSchema, value
|
|
109
|
+
function runValidation(zodSchema, jsonSchema, value) {
|
|
103
110
|
if (zodSchema !== void 0 && isObject(zodSchema)) {
|
|
104
111
|
const safeParseFn = zodSchema.safeParse;
|
|
105
112
|
if (isCallable(safeParseFn)) {
|
|
106
113
|
const result = safeParseFn(value);
|
|
107
|
-
if (isObject(result) && "success" in result && result.success !== true)
|
|
108
|
-
onError?.(result.error);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
114
|
+
if (isObject(result) && "success" in result && result.success !== true) return result.error;
|
|
111
115
|
return;
|
|
112
116
|
}
|
|
113
117
|
}
|
|
@@ -116,11 +120,12 @@ function runValidation(zodSchema, jsonSchema, value, onError) {
|
|
|
116
120
|
const safeParseFn = parsed.safeParse;
|
|
117
121
|
if (isCallable(safeParseFn)) {
|
|
118
122
|
const result = safeParseFn(value);
|
|
119
|
-
if (isObject(result) && "success" in result && result.success !== true)
|
|
123
|
+
if (isObject(result) && "success" in result && result.success !== true) return result.error;
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
function renderField(tree, value, onChange, userResolver, renderChild, instanceWidgets, contextWidgets) {
|
|
128
|
+
if (tree.meta.visible === false) return null;
|
|
124
129
|
const componentHint = tree.meta.component;
|
|
125
130
|
if (typeof componentHint === "string") {
|
|
126
131
|
const widget = instanceWidgets?.get(componentHint) ?? contextWidgets?.get(componentHint) ?? globalWidgets.get(componentHint);
|
|
@@ -197,7 +202,8 @@ function SchemaField({ path, schema: schemaInput, ref: refInput, value, onChange
|
|
|
197
202
|
const handleChange = useCallback((nextFieldValue) => {
|
|
198
203
|
if (validate) {
|
|
199
204
|
const newRootValue = setNestedValue(value, path, nextFieldValue);
|
|
200
|
-
runValidation(zodSchema, jsonSchema, newRootValue
|
|
205
|
+
const error = runValidation(zodSchema, jsonSchema, newRootValue);
|
|
206
|
+
if (error !== void 0) onValidationError?.(error);
|
|
201
207
|
}
|
|
202
208
|
const newRootValue = setNestedValue(value, path, nextFieldValue);
|
|
203
209
|
onChange?.(newRootValue);
|
|
@@ -280,6 +286,36 @@ function setNestedValue(root, path, leafValue) {
|
|
|
280
286
|
}
|
|
281
287
|
return result;
|
|
282
288
|
}
|
|
289
|
+
function isFieldErrorCallback(value) {
|
|
290
|
+
return typeof value === "function";
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Dispatch Zod errors to per-field onValidationError callbacks.
|
|
294
|
+
* Walks the fields override tree and matches errors by path prefix.
|
|
295
|
+
*/
|
|
296
|
+
function dispatchFieldErrors(fields, error) {
|
|
297
|
+
if (fields === void 0 || !isObject(error)) return;
|
|
298
|
+
if (!("issues" in error)) return;
|
|
299
|
+
const issues = error.issues;
|
|
300
|
+
if (!Array.isArray(issues)) return;
|
|
301
|
+
const overrides = toRecordOrUndefined(fields);
|
|
302
|
+
if (overrides === void 0) return;
|
|
303
|
+
for (const [key, override] of Object.entries(overrides)) {
|
|
304
|
+
if (override === void 0 || typeof override !== "object") continue;
|
|
305
|
+
if (override === null) continue;
|
|
306
|
+
if (!("onValidationError" in override)) continue;
|
|
307
|
+
const fieldCallback = override.onValidationError;
|
|
308
|
+
if (typeof fieldCallback !== "function") continue;
|
|
309
|
+
const fieldErrors = issues.filter((issue) => {
|
|
310
|
+
if (!isObject(issue)) return false;
|
|
311
|
+
if (!("path" in issue)) return false;
|
|
312
|
+
const path = issue.path;
|
|
313
|
+
if (!Array.isArray(path)) return false;
|
|
314
|
+
return path[0] === key;
|
|
315
|
+
});
|
|
316
|
+
if (fieldErrors.length > 0 && isFieldErrorCallback(fieldCallback)) fieldCallback({ issues: fieldErrors });
|
|
317
|
+
}
|
|
318
|
+
}
|
|
283
319
|
function isCallable(value) {
|
|
284
320
|
return typeof value === "function";
|
|
285
321
|
}
|
package/dist/react/headless.mjs
CHANGED
|
@@ -237,7 +237,10 @@ function renderObject(props) {
|
|
|
237
237
|
const obj = isObject(props.value) ? props.value : {};
|
|
238
238
|
const fields = props.fields;
|
|
239
239
|
if (fields === void 0) return null;
|
|
240
|
-
|
|
240
|
+
const sortedEntries = Object.entries(fields).sort((a, b) => {
|
|
241
|
+
return (typeof a[1].meta.order === "number" ? a[1].meta.order : Infinity) - (typeof b[1].meta.order === "number" ? b[1].meta.order : Infinity);
|
|
242
|
+
});
|
|
243
|
+
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]) => {
|
|
241
244
|
const childValue = obj[key];
|
|
242
245
|
const childId = inputId(props.path ? `${props.path}.${key}` : key);
|
|
243
246
|
const childOnChange = (v) => {
|
package/dist/themes/mui.d.mts
CHANGED
package/dist/themes/shadcn.d.mts
CHANGED
|
@@ -147,6 +147,8 @@ interface SchemaMeta {
|
|
|
147
147
|
deprecated?: boolean;
|
|
148
148
|
/** Component hint — resolved before theme adapter. */
|
|
149
149
|
component?: string;
|
|
150
|
+
/** Sort order for object fields. Lower values render first. */
|
|
151
|
+
order?: number;
|
|
150
152
|
/** Arbitrary UI hints passed through to theme adapters. */
|
|
151
153
|
[key: string]: unknown;
|
|
152
154
|
}
|
|
@@ -166,14 +168,19 @@ type Editability = "presentation" | "input" | "editable";
|
|
|
166
168
|
declare function resolveEditability(propertyMeta: SchemaMeta | undefined, componentMeta: SchemaMeta | undefined, rootMeta: SchemaMeta | undefined): Editability;
|
|
167
169
|
/**
|
|
168
170
|
* Recursive mapped type that mirrors a schema's shape for per-field
|
|
169
|
-
*
|
|
170
|
-
* and also accept
|
|
171
|
+
* overrides. Each leaf accepts schema meta overrides and an optional
|
|
172
|
+
* per-field validation error callback. Objects recurse and also accept
|
|
173
|
+
* their own overrides.
|
|
171
174
|
*/
|
|
172
|
-
type FieldOverrides<T> = { [K in keyof T]?: T[K] extends object ? FieldOverrides<T[K]> &
|
|
175
|
+
type FieldOverrides<T> = { [K in keyof T]?: T[K] extends object ? FieldOverrides<T[K]> & FieldOverride : FieldOverride };
|
|
173
176
|
/**
|
|
174
|
-
*
|
|
177
|
+
* Per-field override. Extends SchemaMeta with rendering controls
|
|
178
|
+
* and a per-field validation error callback.
|
|
175
179
|
*/
|
|
176
|
-
type FieldOverride = Partial<SchemaMeta
|
|
180
|
+
type FieldOverride = Partial<SchemaMeta> & {
|
|
181
|
+
/** Called with the ZodError when this field fails validation. */onValidationError?: (error: unknown) => void; /** Hide this field when false. Defaults to true (visible). */
|
|
182
|
+
visible?: boolean;
|
|
183
|
+
};
|
|
177
184
|
type SchemaType = "string" | "number" | "boolean" | "null" | "enum" | "literal" | "object" | "array" | "record" | "union" | "discriminatedUnion" | "optional" | "nullable" | "default" | "readonly" | "pipe" | "lazy" | "file" | "unknown";
|
|
178
185
|
interface WalkedField {
|
|
179
186
|
type: SchemaType;
|