schema-components 1.3.3 → 1.4.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 CHANGED
@@ -1,3 +1,13 @@
1
+ ## [1.4.0](https://github.com/Mearman/schema-components/compare/v1.3.3...v1.4.0) (2026-05-14)
2
+
3
+ ### Features
4
+
5
+ * add scoped widget resolution at instance, context, and global levels ([b330baf](https://github.com/Mearman/schema-components/commit/b330baf183d00bebf999108f16c9dabd40c243be))
6
+
7
+ ### Documentation
8
+
9
+ * update README with scoped widget resolution documentation ([21ac50d](https://github.com/Mearman/schema-components/commit/21ac50dc9a55144fc319fcf482ffad6a16585a27))
10
+
1
11
  ## [1.3.3](https://github.com/Mearman/schema-components/compare/v1.3.2...v1.3.3) (2026-05-14)
2
12
 
3
13
  ### Refactoring
package/README.md CHANGED
@@ -434,7 +434,15 @@ Custom resolvers fall back to the default for any type you don't override.
434
434
 
435
435
  ## Custom widgets
436
436
 
437
- Register widgets by `.meta({ component })` hint:
437
+ Widgets let you override rendering for specific fields using `.meta({ component: name })`. Three scopes are available, checked in order:
438
+
439
+ 1. **Instance** — `widgets` prop on `<SchemaComponent>`
440
+ 2. **Context** — `widgets` prop on `<SchemaProvider>`
441
+ 3. **Global** — `registerWidget()` for app-wide defaults
442
+
443
+ If none match, the theme adapter or headless default handles the field.
444
+
445
+ ### Global registration
438
446
 
439
447
  ```tsx
440
448
  import { registerWidget } from "schema-components/react/SchemaComponent";
@@ -443,13 +451,65 @@ registerWidget("richtext", ({ value, onChange }) => (
443
451
  <RichTextEditor value={value} onChange={onChange} />
444
452
  ));
445
453
 
446
- // In schema
447
454
  const schema = z.object({
448
455
  bio: z.string().meta({ component: "richtext" }),
449
456
  });
450
457
  ```
451
458
 
452
- Resolution order: `.meta({ component })` → registered widget → theme adapter → headless default.
459
+ ### Context-scoped widgets
460
+
461
+ Share widgets across a subtree via `<SchemaProvider>`:
462
+
463
+ ```tsx
464
+ import { SchemaProvider } from "schema-components/react/SchemaComponent";
465
+ import type { WidgetMap } from "schema-components/react/SchemaComponent";
466
+
467
+ const adminWidgets: WidgetMap = new Map([
468
+ ["richtext", ({ value, onChange }) => <RichTextEditor value={value} onChange={onChange} />],
469
+ ["avatar", ({ value, onChange }) => <AvatarUploader value={value} onChange={onChange} />],
470
+ ]);
471
+
472
+ <SchemaProvider resolver={shadcnResolver} widgets={adminWidgets}>
473
+ <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
474
+ <SchemaComponent schema={profileSchema} value={profile} onChange={setProfile} />
475
+ </SchemaProvider>
476
+ ```
477
+
478
+ ### Instance-scoped widgets
479
+
480
+ Override widgets for a single form:
481
+
482
+ ```tsx
483
+ const formWidgets: WidgetMap = new Map([
484
+ ["richtext", ({ value, onChange }) => <SimpleTextarea value={value} onChange={onChange} />],
485
+ ]);
486
+
487
+ <SchemaComponent schema={formSchema} value={form} widgets={formWidgets} />
488
+ ```
489
+
490
+ ### Resolution order
491
+
492
+ ```.meta({ component }) hint → instance widgets → context widgets → global registerWidget() → theme adapter → headless default
493
+ ```
494
+
495
+ Instance overrides context. Context overrides global. Unhinted fields skip the widget layer entirely.
496
+
497
+ ### `WidgetMap` type
498
+
499
+ ```tsx
500
+ import type { WidgetMap } from "schema-components/react/SchemaComponent";
501
+
502
+ // ReadonlyMap<string, (props: RenderProps) => unknown>
503
+ const widgets: WidgetMap = new Map([
504
+ ["name", (props) => <MyInput {...props} />],
505
+ ]);
506
+ ```
507
+
508
+ Server Components: `<SchemaView>` accepts a `widgets` prop directly (no React context available):
509
+
510
+ ```tsx
511
+ <SchemaView schema={schema} value={data} widgets={serverWidgets} />
512
+ ```
453
513
 
454
514
  ## Validation
455
515
 
@@ -1,4 +1,5 @@
1
1
  import { c as InferResponseFields, m as SchemaMeta, o as InferParameterOverrides, r as FieldOverride, s as InferRequestBodyFields } from "../types-DDCD6Xnx.mjs";
2
+ import { WidgetMap } from "../react/SchemaComponent.mjs";
2
3
  import { ReactNode } from "react";
3
4
 
4
5
  //#region src/openapi/components.d.ts
@@ -11,6 +12,8 @@ interface ApiOperationProps<Doc = unknown, Path extends string = string, Method
11
12
  responseValue?: unknown;
12
13
  meta?: SchemaMeta;
13
14
  requestBodyFields?: Doc extends Record<string, unknown> ? InferRequestBodyFields<Doc, Path, Method> : Record<string, FieldOverride>;
15
+ /** Instance-scoped widgets. */
16
+ widgets?: WidgetMap;
14
17
  }
15
18
  declare function ApiOperation<Doc = unknown, Path extends string = string, Method extends string = string>({
16
19
  schema: doc,
@@ -20,7 +23,8 @@ declare function ApiOperation<Doc = unknown, Path extends string = string, Metho
20
23
  onRequestBodyChange,
21
24
  responseValue,
22
25
  meta,
23
- requestBodyFields
26
+ requestBodyFields,
27
+ widgets
24
28
  }: ApiOperationProps<Doc, Path, Method>): ReactNode;
25
29
  interface ApiParametersProps<Doc = unknown, Path extends string = string, Method extends string = string> {
26
30
  schema: Doc;
@@ -28,13 +32,16 @@ interface ApiParametersProps<Doc = unknown, Path extends string = string, Method
28
32
  method: Method;
29
33
  meta?: SchemaMeta;
30
34
  overrides?: Doc extends Record<string, unknown> ? InferParameterOverrides<Doc, Path, Method> : Record<string, FieldOverride>;
35
+ /** Instance-scoped widgets. */
36
+ widgets?: WidgetMap;
31
37
  }
32
38
  declare function ApiParameters<Doc = unknown, Path extends string = string, Method extends string = string>({
33
39
  schema: doc,
34
40
  path,
35
41
  method,
36
42
  meta,
37
- overrides
43
+ overrides,
44
+ widgets
38
45
  }: ApiParametersProps<Doc, Path, Method>): ReactNode;
39
46
  interface ApiRequestBodyProps<Doc = unknown, Path extends string = string, Method extends string = string> {
40
47
  schema: Doc;
@@ -44,6 +51,8 @@ interface ApiRequestBodyProps<Doc = unknown, Path extends string = string, Metho
44
51
  onChange?: (value: unknown) => void;
45
52
  meta?: SchemaMeta;
46
53
  fields?: Doc extends Record<string, unknown> ? InferRequestBodyFields<Doc, Path, Method> : Record<string, FieldOverride>;
54
+ /** Instance-scoped widgets. */
55
+ widgets?: WidgetMap;
47
56
  }
48
57
  declare function ApiRequestBody<Doc = unknown, Path extends string = string, Method extends string = string>({
49
58
  schema: doc,
@@ -52,7 +61,8 @@ declare function ApiRequestBody<Doc = unknown, Path extends string = string, Met
52
61
  value,
53
62
  onChange,
54
63
  meta,
55
- fields
64
+ fields,
65
+ widgets
56
66
  }: ApiRequestBodyProps<Doc, Path, Method>): ReactNode;
57
67
  interface ApiResponseProps<Doc = unknown, Path extends string = string, Method extends string = string, Status extends string = string> {
58
68
  schema: Doc;
@@ -62,6 +72,8 @@ interface ApiResponseProps<Doc = unknown, Path extends string = string, Method e
62
72
  value?: unknown;
63
73
  meta?: SchemaMeta;
64
74
  fields?: Doc extends Record<string, unknown> ? InferResponseFields<Doc, Path, Method, Status> : Record<string, FieldOverride>;
75
+ /** Instance-scoped widgets. */
76
+ widgets?: WidgetMap;
65
77
  }
66
78
  declare function ApiResponse<Doc = unknown, Path extends string = string, Method extends string = string, Status extends string = string>({
67
79
  schema: doc,
@@ -70,7 +82,8 @@ declare function ApiResponse<Doc = unknown, Path extends string = string, Method
70
82
  status,
71
83
  value,
72
84
  meta,
73
- fields
85
+ fields,
86
+ widgets
74
87
  }: ApiResponseProps<Doc, Path, Method, Status>): ReactNode;
75
88
  //#endregion
76
89
  export { ApiOperation, ApiOperationProps, ApiParameters, ApiParametersProps, ApiRequestBody, ApiRequestBodyProps, ApiResponse, ApiResponseProps };
@@ -27,10 +27,10 @@ function renderSchema(schema, rootDocument, options) {
27
27
  rootDocument
28
28
  };
29
29
  const tree = walk(jsonSchema, walkOpts);
30
- const renderChild = (childTree, childValue, childOnChange) => renderField(childTree, childValue, childOnChange, void 0, renderChild);
31
- return renderField(tree, options.value, options.onChange ?? noop, void 0, renderChild);
30
+ const renderChild = (childTree, childValue, childOnChange) => renderField(childTree, childValue, childOnChange, void 0, renderChild, options.widgets);
31
+ return renderField(tree, options.value, options.onChange ?? noop, void 0, renderChild, options.widgets);
32
32
  }
33
- function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBodyChange, responseValue, meta, requestBodyFields }) {
33
+ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBodyChange, responseValue, meta, requestBodyFields, widgets }) {
34
34
  const rootDoc = toDoc(doc);
35
35
  const resolved = resolveOperation(rootDoc, path, method);
36
36
  return /* @__PURE__ */ jsxs("section", {
@@ -42,7 +42,8 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
42
42
  children: [/* @__PURE__ */ jsx("h4", { children: "Parameters" }), /* @__PURE__ */ jsx(ParameterList, {
43
43
  parameters: resolved.parameters,
44
44
  rootDoc,
45
- meta
45
+ meta,
46
+ widgets
46
47
  })]
47
48
  }),
48
49
  resolved.requestBody?.schema !== void 0 && /* @__PURE__ */ jsxs("section", {
@@ -61,7 +62,8 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
61
62
  value: requestBodyValue,
62
63
  onChange: onRequestBodyChange,
63
64
  fields: requestBodyFields,
64
- meta
65
+ meta,
66
+ widgets
65
67
  })
66
68
  ]
67
69
  }),
@@ -71,13 +73,14 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
71
73
  response,
72
74
  rootDoc,
73
75
  value: responseValue,
74
- meta
76
+ meta,
77
+ widgets
75
78
  }, response.statusCode))]
76
79
  })
77
80
  ]
78
81
  });
79
82
  }
80
- function ApiParameters({ schema: doc, path, method, meta, overrides }) {
83
+ function ApiParameters({ schema: doc, path, method, meta, overrides, widgets }) {
81
84
  const rootDoc = toDoc(doc);
82
85
  const params = resolveParameters(rootDoc, path, method);
83
86
  if (params.length === 0) return null;
@@ -87,11 +90,12 @@ function ApiParameters({ schema: doc, path, method, meta, overrides }) {
87
90
  parameters: params,
88
91
  rootDoc,
89
92
  overrides,
90
- meta
93
+ meta,
94
+ widgets
91
95
  })]
92
96
  });
93
97
  }
94
- function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fields }) {
98
+ function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fields, widgets }) {
95
99
  const rootDoc = toDoc(doc);
96
100
  const requestBody = resolveRequestBody(rootDoc, path, method);
97
101
  if (requestBody?.schema === void 0) return null;
@@ -111,12 +115,13 @@ function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fiel
111
115
  value,
112
116
  onChange,
113
117
  fields,
114
- meta
118
+ meta,
119
+ widgets
115
120
  })
116
121
  ]
117
122
  });
118
123
  }
119
- function ApiResponse({ schema: doc, path, method, status, value, meta, fields }) {
124
+ function ApiResponse({ schema: doc, path, method, status, value, meta, fields, widgets }) {
120
125
  const rootDoc = toDoc(doc);
121
126
  const response = resolveResponse(rootDoc, path, method, status);
122
127
  if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
@@ -132,7 +137,8 @@ function ApiResponse({ schema: doc, path, method, status, value, meta, fields })
132
137
  rootDoc,
133
138
  value,
134
139
  fields,
135
- meta
140
+ meta,
141
+ widgets
136
142
  });
137
143
  }
138
144
  function OperationHeader({ operation }) {
@@ -149,7 +155,7 @@ function OperationHeader({ operation }) {
149
155
  })
150
156
  ] });
151
157
  }
152
- function ParameterList({ parameters, rootDoc, overrides, meta }) {
158
+ function ParameterList({ parameters, rootDoc, overrides, meta, widgets }) {
153
159
  return /* @__PURE__ */ jsx(Fragment, { children: parameters.map((param) => /* @__PURE__ */ jsxs("div", {
154
160
  "data-parameter": param.name,
155
161
  children: [
@@ -161,11 +167,14 @@ function ParameterList({ parameters, rootDoc, overrides, meta }) {
161
167
  "data-description": true,
162
168
  children: param.description
163
169
  }),
164
- renderSchema(param.schema ?? { type: "string" }, rootDoc, { meta: buildParamMeta(param, overrides, meta) })
170
+ renderSchema(param.schema ?? { type: "string" }, rootDoc, {
171
+ meta: buildParamMeta(param, overrides, meta),
172
+ widgets
173
+ })
165
174
  ]
166
175
  }, param.name)) });
167
176
  }
168
- function ResponseCard({ response, rootDoc, value, fields, meta }) {
177
+ function ResponseCard({ response, rootDoc, value, fields, meta, widgets }) {
169
178
  if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
170
179
  "data-status": response.statusCode,
171
180
  children: [
@@ -185,7 +194,8 @@ function ResponseCard({ response, rootDoc, value, fields, meta }) {
185
194
  meta: {
186
195
  readOnly: true,
187
196
  ...meta
188
- }
197
+ },
198
+ widgets
189
199
  })
190
200
  ]
191
201
  });
@@ -5,13 +5,33 @@ import { ReactNode } from "react";
5
5
  import * as _$react_jsx_runtime0 from "react/jsx-runtime";
6
6
 
7
7
  //#region src/react/SchemaComponent.d.ts
8
+ /**
9
+ * Widget map — maps component hints (from `.meta({ component })`) to render
10
+ * functions. Scoped at three levels:
11
+ *
12
+ * 1. **Per-instance** — `widgets` prop on `<SchemaComponent>`
13
+ * 2. **Context-scoped** — `widgets` prop on `<SchemaProvider>`
14
+ * 3. **Global** — `registerWidget()` (app-wide defaults)
15
+ *
16
+ * Resolution order: instance → context → global → resolver → headless.
17
+ */
18
+ type WidgetMap = ReadonlyMap<string, (props: RenderProps) => unknown>;
8
19
  declare function SchemaProvider({
9
20
  resolver,
21
+ widgets,
10
22
  children
11
23
  }: {
12
- resolver: ComponentResolver;
24
+ resolver: ComponentResolver; /** Scoped widgets available to all SchemaComponents in this subtree. */
25
+ widgets?: WidgetMap;
13
26
  children: ReactNode;
14
27
  }): _$react_jsx_runtime0.JSX.Element;
28
+ /**
29
+ * Register a widget globally. The widget is resolved when a schema field
30
+ * has `.meta({ component: name })`.
31
+ *
32
+ * For scoped registration, use the `widgets` prop on `<SchemaComponent>`
33
+ * or `<SchemaProvider>` instead.
34
+ */
15
35
  declare function registerWidget(name: string, render: (props: RenderProps) => unknown): void;
16
36
  type InferFields<T, Ref extends string | undefined> = T extends z.ZodType ? FieldOverrides<z.infer<T>> : T extends {
17
37
  openapi: unknown;
@@ -41,6 +61,8 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
41
61
  writeOnly?: boolean;
42
62
  /** Convenience: sets description on the root. */
43
63
  description?: string;
64
+ /** Instance-scoped widgets — override context and global widgets. */
65
+ widgets?: WidgetMap;
44
66
  }
45
67
  declare function SchemaComponent<T = unknown, Ref extends string | undefined = undefined>({
46
68
  schema: schemaInput,
@@ -54,9 +76,10 @@ declare function SchemaComponent<T = unknown, Ref extends string | undefined = u
54
76
  meta: componentMeta,
55
77
  readOnly,
56
78
  writeOnly,
57
- description
79
+ description,
80
+ widgets: instanceWidgets
58
81
  }: SchemaComponentProps<T, Ref>): ReactNode;
59
- declare function renderField(tree: WalkedField, value: unknown, onChange: (v: unknown) => void, userResolver: ComponentResolver | undefined, renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void) => ReactNode): ReactNode;
82
+ declare function renderField(tree: WalkedField, value: unknown, onChange: (v: unknown) => void, userResolver: ComponentResolver | undefined, renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void) => ReactNode, instanceWidgets?: WidgetMap, contextWidgets?: WidgetMap): ReactNode;
60
83
  /**
61
84
  * Infer the schema's output type for SchemaField path inference.
62
85
  */
@@ -93,4 +116,4 @@ declare function SchemaField<T = unknown, Ref extends string | undefined = undef
93
116
  onValidationError
94
117
  }: SchemaFieldProps<T, Ref, P>): ReactNode;
95
118
  //#endregion
96
- export { SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, registerWidget, renderField };
119
+ export { SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, registerWidget, renderField };
@@ -23,18 +23,31 @@ import { jsx } from "react/jsx-runtime";
23
23
  * - Runtime schemas → Record<string, FieldOverride> (no autocomplete)
24
24
  */
25
25
  const UserResolverContext = createContext(void 0);
26
- function SchemaProvider({ resolver, children }) {
26
+ const WidgetsContext = createContext(void 0);
27
+ function SchemaProvider({ resolver, widgets, children }) {
27
28
  return /* @__PURE__ */ jsx(UserResolverContext.Provider, {
28
29
  value: resolver,
29
- children
30
+ children: /* @__PURE__ */ jsx(WidgetsContext.Provider, {
31
+ value: widgets,
32
+ children
33
+ })
30
34
  });
31
35
  }
32
- const widgetRegistry = /* @__PURE__ */ new Map();
36
+ /** Global widget registry app-wide defaults. */
37
+ const globalWidgets = /* @__PURE__ */ new Map();
38
+ /**
39
+ * Register a widget globally. The widget is resolved when a schema field
40
+ * has `.meta({ component: name })`.
41
+ *
42
+ * For scoped registration, use the `widgets` prop on `<SchemaComponent>`
43
+ * or `<SchemaProvider>` instead.
44
+ */
33
45
  function registerWidget(name, render) {
34
- widgetRegistry.set(name, render);
46
+ globalWidgets.set(name, render);
35
47
  }
36
- function SchemaComponent({ schema: schemaInput, ref: refInput, value, onChange, validate, onValidationError, onError, fields, meta: componentMeta, readOnly, writeOnly, description }) {
48
+ function SchemaComponent({ schema: schemaInput, ref: refInput, value, onChange, validate, onValidationError, onError, fields, meta: componentMeta, readOnly, writeOnly, description, widgets: instanceWidgets }) {
37
49
  const userResolver = useContext(UserResolverContext);
50
+ const contextWidgets = useContext(WidgetsContext);
38
51
  const mergedMeta = useMemo(() => {
39
52
  const merged = { ...componentMeta };
40
53
  if (readOnly === true) merged.readOnly = true;
@@ -82,9 +95,9 @@ function SchemaComponent({ schema: schemaInput, ref: refInput, value, onChange,
82
95
  rootDocument
83
96
  });
84
97
  const renderChild = (childTree, childValue, childOnChange) => {
85
- return renderField(childTree, childValue, childOnChange, userResolver, renderChild);
98
+ return renderField(childTree, childValue, childOnChange, userResolver, renderChild, instanceWidgets, contextWidgets);
86
99
  };
87
- return renderField(tree, value ?? tree.defaultValue, handleChange, userResolver, renderChild);
100
+ return renderField(tree, value ?? tree.defaultValue, handleChange, userResolver, renderChild, instanceWidgets, contextWidgets);
88
101
  }
89
102
  function runValidation(zodSchema, jsonSchema, value, onError) {
90
103
  if (zodSchema !== void 0 && isObject(zodSchema)) {
@@ -107,10 +120,10 @@ function runValidation(zodSchema, jsonSchema, value, onError) {
107
120
  }
108
121
  }
109
122
  }
110
- function renderField(tree, value, onChange, userResolver, renderChild) {
123
+ function renderField(tree, value, onChange, userResolver, renderChild, instanceWidgets, contextWidgets) {
111
124
  const componentHint = tree.meta.component;
112
125
  if (typeof componentHint === "string") {
113
- const widget = widgetRegistry.get(componentHint);
126
+ const widget = instanceWidgets?.get(componentHint) ?? contextWidgets?.get(componentHint) ?? globalWidgets.get(componentHint);
114
127
  if (widget !== void 0) {
115
128
  const result = widget(buildRenderProps(tree, value, onChange, renderChild));
116
129
  if (result !== void 0 && result !== null) {
@@ -160,6 +173,7 @@ function buildRenderProps(tree, value, onChange, renderChild) {
160
173
  }
161
174
  function SchemaField({ path, schema: schemaInput, ref: refInput, value, onChange, meta: fieldMeta, validate, onValidationError }) {
162
175
  const userResolver = useContext(UserResolverContext);
176
+ const contextWidgets = useContext(WidgetsContext);
163
177
  let jsonSchema;
164
178
  let zodSchema;
165
179
  let rootMeta;
@@ -197,9 +211,9 @@ function SchemaField({ path, schema: schemaInput, ref: refInput, value, onChange
197
211
  onValidationError
198
212
  ]);
199
213
  const renderChild = (childTree, childValue, childOnChange) => {
200
- return renderField(childTree, childValue, childOnChange, userResolver, renderChild);
214
+ return renderField(childTree, childValue, childOnChange, userResolver, renderChild, void 0, contextWidgets);
201
215
  };
202
- return renderField(fieldTree, fieldValue, handleChange, userResolver, renderChild);
216
+ return renderField(fieldTree, fieldValue, handleChange, userResolver, renderChild, void 0, contextWidgets);
203
217
  }
204
218
  function resolvePath(tree, path) {
205
219
  if (path.length === 0) return tree;
@@ -1,4 +1,5 @@
1
1
  import { b as ComponentResolver, m as SchemaMeta } from "../types-DDCD6Xnx.mjs";
2
+ import { WidgetMap } from "./SchemaComponent.mjs";
2
3
  import { ReactNode } from "react";
3
4
 
4
5
  //#region src/react/SchemaView.d.ts
@@ -21,6 +22,8 @@ interface SchemaViewProps {
21
22
  * Falls back to the headless resolver if omitted.
22
23
  */
23
24
  resolver?: ComponentResolver;
25
+ /** Instance-scoped widgets. */
26
+ widgets?: WidgetMap;
24
27
  }
25
28
  /**
26
29
  * Server-safe schema renderer — no hooks, no context, no state.
@@ -35,7 +38,8 @@ declare function SchemaView({
35
38
  fields,
36
39
  meta: componentMeta,
37
40
  description,
38
- resolver
41
+ resolver,
42
+ widgets
39
43
  }: SchemaViewProps): ReactNode;
40
44
  //#endregion
41
45
  export { SchemaView, SchemaViewProps };
@@ -37,7 +37,7 @@ function noop() {}
37
37
  * Always renders in read-only mode. For editable forms, use
38
38
  * `<SchemaComponent>` with `"use client"`.
39
39
  */
40
- function SchemaView({ schema: schemaInput, ref: refInput, value, fields, meta: componentMeta, description, resolver }) {
40
+ function SchemaView({ schema: schemaInput, ref: refInput, value, fields, meta: componentMeta, description, resolver, widgets }) {
41
41
  const mergedMeta = {
42
42
  ...componentMeta,
43
43
  readOnly: true
@@ -61,10 +61,31 @@ function SchemaView({ schema: schemaInput, ref: refInput, value, fields, meta: c
61
61
  rootDocument
62
62
  });
63
63
  const userResolver = resolver !== void 0 ? mergeResolvers(resolver, headlessResolver) : headlessResolver;
64
- const renderChild = (childTree, childValue) => renderFieldServer(childTree, childValue, userResolver, renderChild);
65
- return renderFieldServer(tree, value ?? tree.defaultValue, userResolver, renderChild);
64
+ const renderChild = (childTree, childValue) => renderFieldServer(childTree, childValue, userResolver, renderChild, widgets);
65
+ return renderFieldServer(tree, value ?? tree.defaultValue, userResolver, renderChild, widgets);
66
66
  }
67
- function renderFieldServer(tree, value, resolver, renderChild) {
67
+ function renderFieldServer(tree, value, resolver, renderChild, widgets) {
68
+ const componentHint = tree.meta.component;
69
+ if (typeof componentHint === "string") {
70
+ const widget = widgets?.get(componentHint);
71
+ if (widget !== void 0) {
72
+ const result = widget({
73
+ value,
74
+ onChange: noop,
75
+ readOnly: true,
76
+ writeOnly: false,
77
+ meta: tree.meta,
78
+ constraints: tree.constraints,
79
+ path: "",
80
+ tree,
81
+ renderChild: (childTree, childValue) => renderChild(childTree, childValue)
82
+ });
83
+ if (result !== void 0 && result !== null) {
84
+ if (isValidElement(result)) return result;
85
+ if (typeof result === "string" || typeof result === "number") return result;
86
+ }
87
+ }
88
+ }
68
89
  const renderFn = getRenderFunction(tree.type, resolver);
69
90
  if (renderFn !== void 0) {
70
91
  const props = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "schema-components",
3
- "version": "1.3.3",
3
+ "version": "1.4.0",
4
4
  "description": "React components that render UI from Zod schemas, JSON Schema, and OpenAPI documents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",