schema-components 1.28.2 → 2.0.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.
Files changed (105) hide show
  1. package/README.md +38 -16
  2. package/dist/core/adapter.d.mts +213 -3
  3. package/dist/core/adapter.mjs +21 -2
  4. package/dist/core/constraintHint.d.mts +15 -0
  5. package/dist/core/constraintHint.mjs +24 -0
  6. package/dist/core/constraints.d.mts +34 -2
  7. package/dist/core/constraints.mjs +33 -1
  8. package/dist/core/cssClasses.d.mts +1 -0
  9. package/dist/core/diagnostics.d.mts +1 -1
  10. package/dist/core/errors.d.mts +1 -1
  11. package/dist/core/errors.mjs +22 -12
  12. package/dist/core/fieldOrder.d.mts +1 -1
  13. package/dist/core/formats.d.mts +7 -1
  14. package/dist/core/formats.mjs +6 -0
  15. package/dist/core/idPath.d.mts +35 -5
  16. package/dist/core/idPath.mjs +79 -7
  17. package/dist/core/inferValue.d.mts +2 -0
  18. package/dist/core/inferValue.mjs +1 -0
  19. package/dist/core/limits.d.mts +1 -1
  20. package/dist/core/limits.mjs +6 -0
  21. package/dist/core/merge.d.mts +22 -1
  22. package/dist/core/merge.mjs +66 -3
  23. package/dist/core/normalise.d.mts +17 -2
  24. package/dist/core/normalise.mjs +1 -1
  25. package/dist/core/openapi30.mjs +1 -1
  26. package/dist/core/openapiConstants.d.mts +1 -0
  27. package/dist/core/ref.d.mts +1 -1
  28. package/dist/core/refChain.d.mts +3 -4
  29. package/dist/core/refChain.mjs +2 -3
  30. package/dist/core/renderer.d.mts +199 -2
  31. package/dist/core/renderer.mjs +5 -0
  32. package/dist/core/swagger2.d.mts +1 -1
  33. package/dist/core/swagger2.mjs +1 -1
  34. package/dist/core/typeInference.d.mts +3 -3
  35. package/dist/core/types.d.mts +1 -1
  36. package/dist/core/types.mjs +17 -0
  37. package/dist/core/unionMatch.d.mts +1 -1
  38. package/dist/core/uri.d.mts +12 -4
  39. package/dist/core/uri.mjs +30 -4
  40. package/dist/core/version.d.mts +1 -1
  41. package/dist/core/walkBuilders.d.mts +63 -6
  42. package/dist/core/walkBuilders.mjs +33 -1
  43. package/dist/core/walker.d.mts +14 -1
  44. package/dist/core/walker.mjs +18 -0
  45. package/dist/{diagnostics-Cbwak-ZX.d.mts → diagnostics-BTrm3O6J.d.mts} +9 -1
  46. package/dist/{errors-DQSIK4n1.d.mts → errors-Dki7tji4.d.mts} +23 -13
  47. package/dist/html/a11y.d.mts +3 -7
  48. package/dist/html/a11y.mjs +1 -16
  49. package/dist/html/html.d.mts +11 -0
  50. package/dist/html/html.mjs +11 -0
  51. package/dist/html/renderToHtml.d.mts +45 -12
  52. package/dist/html/renderToHtml.mjs +20 -4
  53. package/dist/html/renderToHtmlStream.d.mts +63 -18
  54. package/dist/html/renderToHtmlStream.mjs +34 -8
  55. package/dist/html/renderers.d.mts +6 -31
  56. package/dist/html/renderers.mjs +45 -91
  57. package/dist/html/streamRenderers.d.mts +31 -3
  58. package/dist/html/streamRenderers.mjs +41 -8
  59. package/dist/inferValue-PPXWJpbN.d.mts +77 -0
  60. package/dist/{limits-DJhgx5Ay.d.mts → limits-x4OiyJxh.d.mts} +6 -0
  61. package/dist/{normalise-Db1xaxgx.mjs → normalise-DB-Xtjmn.mjs} +43 -2
  62. package/dist/openapi/ApiCallbacks.d.mts +13 -1
  63. package/dist/openapi/ApiCallbacks.mjs +7 -0
  64. package/dist/openapi/ApiLinks.d.mts +13 -1
  65. package/dist/openapi/ApiLinks.mjs +7 -0
  66. package/dist/openapi/ApiResponseHeaders.d.mts +13 -1
  67. package/dist/openapi/ApiResponseHeaders.mjs +7 -0
  68. package/dist/openapi/ApiSecurity.d.mts +14 -1
  69. package/dist/openapi/ApiSecurity.mjs +29 -8
  70. package/dist/openapi/bundle.d.mts +31 -0
  71. package/dist/openapi/components.d.mts +135 -20
  72. package/dist/openapi/components.mjs +90 -15
  73. package/dist/openapi/parser.d.mts +140 -13
  74. package/dist/openapi/parser.mjs +84 -12
  75. package/dist/openapi/resolve.d.mts +42 -47
  76. package/dist/openapi/resolve.mjs +62 -56
  77. package/dist/react/SchemaComponent.d.mts +90 -88
  78. package/dist/react/SchemaComponent.mjs +74 -2
  79. package/dist/react/SchemaErrorBoundary.d.mts +18 -1
  80. package/dist/react/SchemaErrorBoundary.mjs +13 -1
  81. package/dist/react/SchemaView.d.mts +39 -11
  82. package/dist/react/SchemaView.mjs +23 -6
  83. package/dist/react/a11y.d.mts +74 -7
  84. package/dist/react/a11y.mjs +67 -6
  85. package/dist/react/fieldPath.d.mts +16 -1
  86. package/dist/react/fieldPath.mjs +25 -1
  87. package/dist/react/fieldShell.d.mts +49 -0
  88. package/dist/react/fieldShell.mjs +37 -0
  89. package/dist/react/headless.d.mts +1 -1
  90. package/dist/react/headlessRenderers.d.mts +13 -2
  91. package/dist/react/headlessRenderers.mjs +134 -54
  92. package/dist/{ref-TdeMfaV_.d.mts → ref-DdsbekXX.d.mts} +33 -1
  93. package/dist/themes/mantine.d.mts +54 -12
  94. package/dist/themes/mantine.mjs +195 -140
  95. package/dist/themes/mui.d.mts +64 -11
  96. package/dist/themes/mui.mjs +277 -213
  97. package/dist/themes/radix.d.mts +67 -15
  98. package/dist/themes/radix.mjs +235 -170
  99. package/dist/themes/shadcn.d.mts +25 -1
  100. package/dist/themes/shadcn.mjs +112 -91
  101. package/dist/{types-BTB73MB8.d.mts → types-BrYbjC7_.d.mts} +30 -0
  102. package/dist/{version-ZzL5R6cS.d.mts → version-DL8U5RuA.d.mts} +6 -0
  103. package/package.json +8 -1
  104. package/dist/adapter-DqlAnZ_w.d.mts +0 -172
  105. package/dist/renderer-Ul9taFYp.d.mts +0 -169
@@ -1,244 +1,308 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
2
  import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
3
+ import { isFieldRequired } from "../react/a11y.mjs";
3
4
  import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
4
- import { headlessResolver } from "../react/headless.mjs";
5
- import { isValidElement } from "react";
5
+ import { FieldShell } from "../react/fieldShell.mjs";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { isValidElement } from "react";
7
8
  //#region src/themes/mui.tsx
8
- function ariaRequired(tree) {
9
- return { required: tree.isOptional === false };
10
- }
11
- function renderStringInput(props) {
12
- const strValue = typeof props.value === "string" ? props.value : "";
13
- const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
14
- const id = inputId(props.path);
15
- if (props.readOnly) return /* @__PURE__ */ jsx(MuiTypography, {
16
- id,
17
- variant: "body2",
18
- children: strValue || "—"
19
- });
20
- return /* @__PURE__ */ jsx(MuiTextField, {
21
- id,
22
- label,
23
- type: props.constraints.format === "email" ? "email" : props.constraints.format === "uri" ? "url" : "text",
24
- value: props.writeOnly ? "" : strValue,
25
- onChange: (e) => {
26
- props.onChange(e.target.value);
27
- },
28
- fullWidth: true,
29
- size: "small",
30
- variant: "outlined",
31
- inputProps: {
32
- minLength: props.constraints.minLength,
33
- maxLength: props.constraints.maxLength
34
- },
35
- ...ariaRequired(props.tree)
36
- });
9
+ /**
10
+ * MUI's TextField / Checkbox use the `required` prop to surface the
11
+ * native asterisk and `aria-required` together. Mirror it from the
12
+ * walked field so callers do not need a second check.
13
+ */
14
+ function muiRequired(props) {
15
+ return { required: isFieldRequired(props.tree) };
37
16
  }
38
- function renderNumberInput(props) {
39
- const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
40
- const id = inputId(props.path);
41
- if (props.readOnly) {
42
- if (typeof props.value !== "number") return /* @__PURE__ */ jsx(MuiTypography, {
17
+ function makeRenderStringInput(components) {
18
+ const { TextField, Typography } = components;
19
+ return function renderStringInput(props) {
20
+ const strValue = typeof props.value === "string" ? props.value : "";
21
+ const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
22
+ const id = inputId(props.path);
23
+ if (props.readOnly) return /* @__PURE__ */ jsx(Typography, {
43
24
  id,
44
25
  variant: "body2",
45
- children: "—"
26
+ children: strValue || "—"
46
27
  });
47
- return /* @__PURE__ */ jsx(MuiTypography, {
48
- id,
49
- variant: "body2",
50
- children: props.value.toLocaleString()
28
+ return /* @__PURE__ */ jsx(FieldShell, {
29
+ props,
30
+ inputId: id,
31
+ hideLabel: true,
32
+ children: (aria) => /* @__PURE__ */ jsx(TextField, {
33
+ id,
34
+ label,
35
+ type: props.constraints.format === "email" ? "email" : props.constraints.format === "uri" ? "url" : "text",
36
+ value: props.writeOnly ? "" : strValue,
37
+ onChange: (e) => {
38
+ props.onChange(e.target.value);
39
+ },
40
+ fullWidth: true,
41
+ size: "small",
42
+ variant: "outlined",
43
+ inputProps: {
44
+ minLength: props.constraints.minLength,
45
+ maxLength: props.constraints.maxLength,
46
+ ...aria
47
+ },
48
+ ...muiRequired(props)
49
+ })
51
50
  });
52
- }
53
- return /* @__PURE__ */ jsx(MuiTextField, {
54
- id,
55
- label,
56
- type: "number",
57
- value: props.writeOnly ? "" : typeof props.value === "number" ? props.value : "",
58
- onChange: (e) => {
59
- props.onChange(Number(e.target.value));
60
- },
61
- fullWidth: true,
62
- size: "small",
63
- variant: "outlined",
64
- inputProps: {
65
- min: props.constraints.minimum,
66
- max: props.constraints.maximum
67
- },
68
- ...ariaRequired(props.tree)
69
- });
51
+ };
70
52
  }
71
- function renderBooleanInput(props) {
72
- const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
73
- const id = inputId(props.path);
74
- if (props.readOnly) {
75
- if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(MuiTypography, {
76
- id,
77
- variant: "body2",
78
- children: "—"
53
+ function makeRenderNumberInput(components) {
54
+ const { TextField, Typography } = components;
55
+ return function renderNumberInput(props) {
56
+ const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
57
+ const id = inputId(props.path);
58
+ if (props.readOnly) {
59
+ if (typeof props.value !== "number") return /* @__PURE__ */ jsx(Typography, {
60
+ id,
61
+ variant: "body2",
62
+ children: "—"
63
+ });
64
+ return /* @__PURE__ */ jsx(Typography, {
65
+ id,
66
+ variant: "body2",
67
+ children: props.value.toLocaleString()
68
+ });
69
+ }
70
+ return /* @__PURE__ */ jsx(FieldShell, {
71
+ props,
72
+ inputId: id,
73
+ hideLabel: true,
74
+ children: (aria) => /* @__PURE__ */ jsx(TextField, {
75
+ id,
76
+ label,
77
+ type: "number",
78
+ value: props.writeOnly ? "" : typeof props.value === "number" ? props.value : "",
79
+ onChange: (e) => {
80
+ props.onChange(Number(e.target.value));
81
+ },
82
+ fullWidth: true,
83
+ size: "small",
84
+ variant: "outlined",
85
+ inputProps: {
86
+ min: props.constraints.minimum,
87
+ max: props.constraints.maximum,
88
+ ...aria
89
+ },
90
+ ...muiRequired(props)
91
+ })
79
92
  });
80
- return /* @__PURE__ */ jsx(MuiTypography, {
93
+ };
94
+ }
95
+ function makeRenderBooleanInput(components) {
96
+ const { Checkbox, Typography, FormControlLabel } = components;
97
+ return function renderBooleanInput(props) {
98
+ const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
99
+ const id = inputId(props.path);
100
+ if (props.readOnly) {
101
+ if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(Typography, {
102
+ id,
103
+ variant: "body2",
104
+ children: "—"
105
+ });
106
+ return /* @__PURE__ */ jsx(Typography, {
107
+ id,
108
+ variant: "body2",
109
+ children: props.value ? "Yes" : "No"
110
+ });
111
+ }
112
+ return /* @__PURE__ */ jsx(FieldShell, {
113
+ props,
114
+ inputId: id,
115
+ hideLabel: true,
116
+ children: (aria) => /* @__PURE__ */ jsx(FormControlLabel, {
117
+ control: /* @__PURE__ */ jsx(Checkbox, {
118
+ id,
119
+ checked: props.writeOnly ? false : props.value === true,
120
+ onChange: (e) => {
121
+ props.onChange(e.target.checked);
122
+ },
123
+ ...aria
124
+ }),
125
+ label
126
+ })
127
+ });
128
+ };
129
+ }
130
+ function makeRenderEnumInput(components) {
131
+ const { TextField, Typography, MenuItem } = components;
132
+ return function renderEnumInput(props) {
133
+ const enumValue = typeof props.value === "string" ? props.value : "";
134
+ const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
135
+ const id = inputId(props.path);
136
+ if (props.readOnly) return /* @__PURE__ */ jsx(Typography, {
81
137
  id,
82
138
  variant: "body2",
83
- children: props.value ? "Yes" : "No"
139
+ children: enumValue || ""
84
140
  });
85
- }
86
- return /* @__PURE__ */ jsx(MuiFormControlLabel, {
87
- control: /* @__PURE__ */ jsx(MuiCheckbox, {
88
- id,
89
- checked: props.writeOnly ? false : props.value === true,
90
- onChange: (e) => {
91
- props.onChange(e.target.checked);
92
- }
93
- }),
94
- label
95
- });
96
- }
97
- function renderEnumInput(props) {
98
- const enumValue = typeof props.value === "string" ? props.value : "";
99
- const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
100
- const id = inputId(props.path);
101
- if (props.readOnly) return /* @__PURE__ */ jsx(MuiTypography, {
102
- id,
103
- variant: "body2",
104
- children: enumValue || ""
105
- });
106
- return /* @__PURE__ */ jsxs(MuiTextField, {
107
- id,
108
- select: true,
109
- label,
110
- value: props.writeOnly ? "" : enumValue,
111
- onChange: (e) => {
112
- props.onChange(e.target.value);
113
- },
114
- fullWidth: true,
115
- size: "small",
116
- variant: "outlined",
117
- ...ariaRequired(props.tree),
118
- children: [/* @__PURE__ */ jsxs(MuiMenuItem, {
119
- value: "",
120
- children: ["Select", "…"]
121
- }), (props.tree.type === "enum" ? props.tree.enumValues : []).map((v) => /* @__PURE__ */ jsx(MuiMenuItem, {
122
- value: v,
123
- children: v
124
- }, v))]
125
- });
141
+ return /* @__PURE__ */ jsx(FieldShell, {
142
+ props,
143
+ inputId: id,
144
+ hideLabel: true,
145
+ children: (aria) => /* @__PURE__ */ jsxs(TextField, {
146
+ id,
147
+ select: true,
148
+ label,
149
+ value: props.writeOnly ? "" : enumValue,
150
+ onChange: (e) => {
151
+ props.onChange(e.target.value);
152
+ },
153
+ fullWidth: true,
154
+ size: "small",
155
+ variant: "outlined",
156
+ inputProps: aria,
157
+ ...muiRequired(props),
158
+ children: [/* @__PURE__ */ jsxs(MenuItem, {
159
+ value: "",
160
+ children: ["Select", ""]
161
+ }), (props.tree.type === "enum" ? props.tree.enumValues : []).map((v) => /* @__PURE__ */ jsx(MenuItem, {
162
+ value: v,
163
+ children: v
164
+ }, v))]
165
+ })
166
+ });
167
+ };
126
168
  }
127
- function renderObjectContainer(props) {
128
- if (props.tree.type !== "object") return null;
129
- const fields = props.tree.fields;
130
- const obj = isObject(props.value) ? props.value : {};
131
- return /* @__PURE__ */ jsxs(MuiBox, {
132
- sx: {
133
- display: "flex",
134
- flexDirection: "column",
135
- gap: 2
136
- },
137
- children: [typeof props.meta.description === "string" && /* @__PURE__ */ jsx(MuiTypography, {
138
- variant: "h6",
139
- children: props.meta.description
140
- }), sortFieldsByOrder(fields).map(([key, field]) => {
141
- const childValue = obj[key];
142
- const childOnChange = (v) => {
143
- const updated = {};
144
- for (const [k, val] of Object.entries(obj)) updated[k] = val;
145
- updated[key] = v;
146
- props.onChange(updated);
147
- };
148
- return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(field, childValue, childOnChange, key)) }, key);
149
- })]
150
- });
169
+ function makeRenderObjectContainer(components) {
170
+ const { Box, Typography } = components;
171
+ return function renderObjectContainer(props) {
172
+ if (props.tree.type !== "object") return null;
173
+ const fields = props.tree.fields;
174
+ const obj = isObject(props.value) ? props.value : {};
175
+ return /* @__PURE__ */ jsxs(Box, {
176
+ sx: {
177
+ display: "flex",
178
+ flexDirection: "column",
179
+ gap: 2
180
+ },
181
+ children: [typeof props.meta.description === "string" && /* @__PURE__ */ jsx(Typography, {
182
+ variant: "h6",
183
+ children: props.meta.description
184
+ }), sortFieldsByOrder(fields).map(([key, field]) => {
185
+ const childValue = obj[key];
186
+ const childOnChange = (v) => {
187
+ const updated = {};
188
+ for (const [k, val] of Object.entries(obj)) updated[k] = val;
189
+ updated[key] = v;
190
+ props.onChange(updated);
191
+ };
192
+ return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(field, childValue, childOnChange, key)) }, key);
193
+ })]
194
+ });
195
+ };
151
196
  }
152
- function renderArrayContainer(props) {
153
- const arr = Array.isArray(props.value) ? props.value : [];
154
- if (props.tree.type !== "array") return null;
155
- const element = props.tree.element;
156
- if (element === void 0) return null;
157
- return /* @__PURE__ */ jsx(MuiBox, {
158
- sx: {
159
- display: "flex",
160
- flexDirection: "column",
161
- gap: 1
162
- },
163
- children: arr.map((item, i) => {
164
- const childOnChange = (v) => {
165
- const next = arr.slice();
166
- next[i] = v;
167
- props.onChange(next);
168
- };
169
- return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange, `[${String(i)}]`)) }, String(i));
170
- })
171
- });
197
+ function makeRenderArrayContainer(components) {
198
+ const { Box } = components;
199
+ return function renderArrayContainer(props) {
200
+ const arr = Array.isArray(props.value) ? props.value : [];
201
+ if (props.tree.type !== "array") return null;
202
+ const element = props.tree.element;
203
+ if (element === void 0) return null;
204
+ return /* @__PURE__ */ jsx(Box, {
205
+ sx: {
206
+ display: "flex",
207
+ flexDirection: "column",
208
+ gap: 1
209
+ },
210
+ children: arr.map((item, i) => {
211
+ const childOnChange = (v) => {
212
+ const next = arr.slice();
213
+ next[i] = v;
214
+ props.onChange(next);
215
+ };
216
+ return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange, `[${String(i)}]`)) }, String(i));
217
+ })
218
+ });
219
+ };
172
220
  }
173
221
  /**
174
- * MUI components are not bundled with this adapter.
175
- * Instead, the resolver uses thin wrapper components that delegate to
176
- * the consuming project's MUI installation.
177
- *
178
- * This avoids a hard dependency on @mui/material while providing
179
- * type-safe rendering. If MUI is not installed, these wrappers
180
- * render basic HTML elements as fallback.
181
- *
182
- * To use real MUI components, wrap your app with MuiProvider:
183
- * import { MuiProvider } from "schema-components/themes/mui";
184
- * import { TextField, Checkbox, ... } from "@mui/material";
185
- *
186
- * <MuiProvider
187
- * TextField={TextField}
188
- * Checkbox={Checkbox}
189
- * ...
190
- * >
191
- * <SchemaComponent ... />
192
- * </MuiProvider>
222
+ * The default `muiResolver` export below is built against these plain
223
+ * HTML stubs so consumers can pull `muiResolver` into a story or test
224
+ * without first wiring up `@mui/material`. Production usage should call
225
+ * `createMuiResolver(...)` directly with the real MUI element types.
193
226
  */
194
227
  function stripChildren(props) {
195
228
  const rest = { ...props };
196
229
  if ("children" in rest) delete rest.children;
197
230
  return rest;
198
231
  }
199
- let MuiTextField = (props) => /* @__PURE__ */ jsx("input", { ...stripChildren(props) });
200
- let MuiCheckbox = (props) => /* @__PURE__ */ jsx("input", {
201
- type: "checkbox",
202
- ...stripChildren(props)
203
- });
204
- let MuiTypography = (props) => /* @__PURE__ */ jsx("span", { ...props });
205
- let MuiBox = (props) => /* @__PURE__ */ jsx("div", { ...props });
206
- let MuiMenuItem = (props) => /* @__PURE__ */ jsx("option", { ...props });
207
- let MuiFormControlLabel = (props) => {
208
- const { control, label, ...rest } = props;
209
- return /* @__PURE__ */ jsxs("label", {
210
- ...rest,
211
- children: [isValidElement(control) ? control : null, typeof label === "string" ? label : null]
212
- });
232
+ const STUB_COMPONENTS = {
233
+ TextField: (props) => /* @__PURE__ */ jsx("input", { ...stripChildren(props) }),
234
+ Checkbox: (props) => /* @__PURE__ */ jsx("input", {
235
+ type: "checkbox",
236
+ ...stripChildren(props)
237
+ }),
238
+ Typography: (props) => /* @__PURE__ */ jsx("span", { ...props }),
239
+ Box: (props) => /* @__PURE__ */ jsx("div", { ...props }),
240
+ MenuItem: (props) => /* @__PURE__ */ jsx("option", { ...props }),
241
+ FormControlLabel: (props) => {
242
+ const { control, label, ...rest } = props;
243
+ return /* @__PURE__ */ jsxs("label", {
244
+ ...rest,
245
+ children: [isValidElement(control) ? control : null, typeof label === "string" ? label : null]
246
+ });
247
+ }
213
248
  };
214
249
  /**
215
- * Register real MUI components. Call once at app startup.
250
+ * Build a MUI-flavoured {@link ComponentResolver} bound to the supplied
251
+ * element types. Each render function captures the supplied components
252
+ * in a closure so two consumers can build different resolvers from the
253
+ * same package without leaking element types through module-level
254
+ * mutable state — making the adapter safe to use in SSR and multi-tenant
255
+ * environments.
256
+ *
257
+ * Returns only the keys this theme actually overrides. The runtime
258
+ * `mergeResolvers` call inside `<SchemaComponent>` / `<SchemaView>`
259
+ * fills any unset keys from `headlessResolver`, so consumers never see
260
+ * an unhandled field type even though this resolver leaves variants
261
+ * like `union`, `discriminatedUnion`, `record`, `file`, and `unknown`
262
+ * unset on purpose.
263
+ *
264
+ * @group Themes
216
265
  */
217
- function registerMuiComponents(components) {
218
- MuiTextField = components.TextField;
219
- MuiCheckbox = components.Checkbox;
220
- MuiTypography = components.Typography;
221
- MuiBox = components.Box;
222
- MuiMenuItem = components.MenuItem;
223
- MuiFormControlLabel = components.FormControlLabel;
224
- }
225
- function buildResolver() {
226
- const resolver = {
227
- string: renderStringInput,
228
- number: renderNumberInput,
229
- boolean: renderBooleanInput,
230
- enum: renderEnumInput,
231
- object: renderObjectContainer,
232
- array: renderArrayContainer
266
+ function createMuiResolver(components) {
267
+ return {
268
+ string: makeRenderStringInput(components),
269
+ number: makeRenderNumberInput(components),
270
+ boolean: makeRenderBooleanInput(components),
271
+ enum: makeRenderEnumInput(components),
272
+ object: makeRenderObjectContainer(components),
273
+ array: makeRenderArrayContainer(components)
233
274
  };
234
- if (headlessResolver.literal !== void 0) resolver.literal = headlessResolver.literal;
235
- if (headlessResolver.union !== void 0) resolver.union = headlessResolver.union;
236
- if (headlessResolver.discriminatedUnion !== void 0) resolver.discriminatedUnion = headlessResolver.discriminatedUnion;
237
- if (headlessResolver.record !== void 0) resolver.record = headlessResolver.record;
238
- if (headlessResolver.file !== void 0) resolver.file = headlessResolver.file;
239
- if (headlessResolver.unknown !== void 0) resolver.unknown = headlessResolver.unknown;
240
- return resolver;
241
275
  }
242
- const muiResolver = buildResolver();
276
+ /**
277
+ * Component resolver mapping schema field types to MUI (Material UI)
278
+ * primitives — `TextField`, `Checkbox`, `Typography`, etc.
279
+ *
280
+ * This default export is built against minimal HTML stubs so it is
281
+ * usable without wiring up `@mui/material` first — handy for tests,
282
+ * stories, and tree-shaken bundles that want the resolver shape
283
+ * without the runtime dependency. For production usage call
284
+ * {@link createMuiResolver} with the real MUI element types.
285
+ *
286
+ * @group Themes
287
+ * @example
288
+ * ```tsx
289
+ * import TextField from "@mui/material/TextField";
290
+ * import Checkbox from "@mui/material/Checkbox";
291
+ * import Typography from "@mui/material/Typography";
292
+ * import Box from "@mui/material/Box";
293
+ * import MenuItem from "@mui/material/MenuItem";
294
+ * import FormControlLabel from "@mui/material/FormControlLabel";
295
+ * import { createMuiResolver } from "schema-components/themes/mui";
296
+ *
297
+ * const muiResolver = createMuiResolver({
298
+ * TextField, Checkbox, Typography, Box, MenuItem, FormControlLabel,
299
+ * });
300
+ *
301
+ * <SchemaProvider resolver={muiResolver}>
302
+ * <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
303
+ * </SchemaProvider>
304
+ * ```
305
+ */
306
+ const muiResolver = createMuiResolver(STUB_COMPONENTS);
243
307
  //#endregion
244
- export { muiResolver, registerMuiComponents };
308
+ export { createMuiResolver, muiResolver };
@@ -1,21 +1,73 @@
1
- import { r as ComponentResolver } from "../renderer-Ul9taFYp.mjs";
1
+ import { ComponentResolver } from "../core/renderer.mjs";
2
+ import { ElementType } from "react";
2
3
 
3
4
  //#region src/themes/radix.d.ts
4
5
  /**
5
- * Register real Radix Themes components for the resolver to use.
6
- * Call once at app startup before rendering.
6
+ * Element types the Radix resolver renders into. Each Select.* slot is
7
+ * passed in separately because Radix exposes those parts as nested
8
+ * properties on the `Select` namespace; flattening them into the
9
+ * dependency bag lets the resolver pass them straight to JSX without
10
+ * touching `Select.Root`-style member expressions at render time.
11
+ */
12
+ interface RadixComponents {
13
+ Box: ElementType;
14
+ Checkbox: ElementType;
15
+ Flex: ElementType;
16
+ SelectRoot: ElementType;
17
+ SelectTrigger: ElementType;
18
+ SelectContent: ElementType;
19
+ SelectItem: ElementType;
20
+ Text: ElementType;
21
+ TextField: ElementType;
22
+ }
23
+ /**
24
+ * Build a Radix-flavoured {@link ComponentResolver} bound to the
25
+ * supplied element types. Each render function captures the supplied
26
+ * components in a closure so two consumers can build different
27
+ * resolvers from the same package without leaking element types
28
+ * through module-level mutable state.
29
+ *
30
+ * Returns only the keys this theme actually overrides. The runtime
31
+ * `mergeResolvers` call inside `<SchemaComponent>` / `<SchemaView>`
32
+ * fills unset keys from `headlessResolver`, so variants this adapter
33
+ * leaves unset (literal, union, discriminatedUnion, array, record,
34
+ * file, unknown, …) still render via the headless fallback.
35
+ *
36
+ * @group Themes
37
+ */
38
+ declare function createRadixResolver(components: RadixComponents): ComponentResolver;
39
+ /**
40
+ * Component resolver mapping schema field types to Radix Themes
41
+ * primitives — `Box`, `Checkbox`, `Flex`, `Select.*`, `Text`,
42
+ * `TextField`.
43
+ *
44
+ * Built against minimal HTML stubs so the resolver is usable without
45
+ * wiring up `@radix-ui/themes` first — production usage should call
46
+ * {@link createRadixResolver} with real Radix element types.
47
+ *
48
+ * @group Themes
49
+ * @example
50
+ * ```tsx
51
+ * import * as Radix from "@radix-ui/themes";
52
+ * import { createRadixResolver } from "schema-components/themes/radix";
53
+ *
54
+ * const radixResolver = createRadixResolver({
55
+ * Box: Radix.Box,
56
+ * Checkbox: Radix.Checkbox,
57
+ * Flex: Radix.Flex,
58
+ * SelectRoot: Radix.Select.Root,
59
+ * SelectTrigger: Radix.Select.Trigger,
60
+ * SelectContent: Radix.Select.Content,
61
+ * SelectItem: Radix.Select.Item,
62
+ * Text: Radix.Text,
63
+ * TextField: Radix.TextField.Root,
64
+ * });
65
+ *
66
+ * <SchemaProvider resolver={radixResolver}>
67
+ * <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
68
+ * </SchemaProvider>
69
+ * ```
7
70
  */
8
- declare function registerRadixComponents(components: {
9
- Box: React.ElementType;
10
- Checkbox: React.ElementType;
11
- Flex: React.ElementType;
12
- SelectRoot: React.ElementType;
13
- SelectTrigger: React.ElementType;
14
- SelectContent: React.ElementType;
15
- SelectItem: React.ElementType;
16
- Text: React.ElementType;
17
- TextField: React.ElementType;
18
- }): void;
19
71
  declare const radixResolver: ComponentResolver;
20
72
  //#endregion
21
- export { radixResolver, registerRadixComponents };
73
+ export { RadixComponents, createRadixResolver, radixResolver };