schema-components 1.29.0 → 2.0.1
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/README.md +38 -16
- package/dist/core/adapter.d.mts +213 -3
- package/dist/core/adapter.mjs +1 -1
- package/dist/core/constraintHint.d.mts +15 -0
- package/dist/core/constraintHint.mjs +24 -0
- package/dist/core/constraints.d.mts +2 -2
- package/dist/core/constraints.mjs +1 -1
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/core/formats.d.mts +1 -1
- package/dist/core/idPath.d.mts +35 -5
- package/dist/core/idPath.mjs +79 -7
- package/dist/core/inferValue.d.mts +2 -0
- package/dist/core/inferValue.mjs +1 -0
- package/dist/core/limits.d.mts +1 -1
- package/dist/core/limits.mjs +5 -0
- package/dist/core/merge.d.mts +12 -1
- package/dist/core/merge.mjs +66 -3
- package/dist/core/normalise.d.mts +1 -1
- package/dist/core/normalise.mjs +1 -1
- package/dist/core/openapi30.mjs +1 -1
- package/dist/core/ref.d.mts +1 -1
- package/dist/core/refChain.d.mts +3 -4
- package/dist/core/refChain.mjs +2 -3
- package/dist/core/renderer.d.mts +199 -2
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/swagger2.mjs +1 -1
- package/dist/core/typeInference.d.mts +3 -3
- package/dist/core/types.d.mts +1 -1
- package/dist/core/unionMatch.d.mts +1 -1
- package/dist/core/uri.d.mts +12 -4
- package/dist/core/uri.mjs +30 -4
- package/dist/core/walkBuilders.d.mts +18 -6
- package/dist/core/walkBuilders.mjs +3 -1
- package/dist/core/walker.d.mts +1 -1
- package/dist/core/walker.mjs +5 -0
- package/dist/{diagnostics-ByEzkjrA.d.mts → diagnostics-BTrm3O6J.d.mts} +1 -1
- package/dist/{errors-D8JndRwI.d.mts → errors-Dki7tji4.d.mts} +1 -1
- package/dist/html/a11y.d.mts +3 -7
- package/dist/html/a11y.mjs +1 -16
- package/dist/html/renderToHtml.d.mts +22 -9
- package/dist/html/renderToHtml.mjs +2 -1
- package/dist/html/renderToHtmlStream.d.mts +24 -11
- package/dist/html/renderToHtmlStream.mjs +2 -1
- package/dist/html/renderers.d.mts +2 -33
- package/dist/html/renderers.mjs +39 -91
- package/dist/html/streamRenderers.d.mts +3 -3
- package/dist/html/streamRenderers.mjs +13 -8
- package/dist/inferValue-PPXWJpbN.d.mts +77 -0
- package/dist/{limits-DswmqWuy.d.mts → limits-x4OiyJxh.d.mts} +5 -0
- package/dist/{normalise-Db1xaxgx.mjs → normalise-DB-Xtjmn.mjs} +43 -2
- package/dist/openapi/ApiCallbacks.d.mts +1 -1
- package/dist/openapi/ApiLinks.d.mts +1 -1
- package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
- package/dist/openapi/ApiSecurity.d.mts +1 -1
- package/dist/openapi/ApiSecurity.mjs +21 -8
- package/dist/openapi/bundle.d.mts +31 -0
- package/dist/openapi/components.d.mts +41 -10
- package/dist/openapi/components.mjs +19 -13
- package/dist/openapi/parser.d.mts +13 -13
- package/dist/openapi/parser.mjs +12 -12
- package/dist/openapi/resolve.d.mts +38 -49
- package/dist/openapi/resolve.mjs +62 -56
- package/dist/react/SchemaComponent.d.mts +19 -95
- package/dist/react/SchemaComponent.mjs +12 -1
- package/dist/react/SchemaView.d.mts +11 -7
- package/dist/react/SchemaView.mjs +3 -1
- package/dist/react/a11y.d.mts +74 -7
- package/dist/react/a11y.mjs +67 -6
- package/dist/react/fieldPath.d.mts +16 -1
- package/dist/react/fieldPath.mjs +25 -1
- package/dist/react/fieldShell.d.mts +49 -0
- package/dist/react/fieldShell.mjs +37 -0
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +2 -2
- package/dist/react/headlessRenderers.mjs +123 -54
- package/dist/{ref-CPh8rKQ3.d.mts → ref-DdsbekXX.d.mts} +33 -1
- package/dist/themes/mantine.d.mts +36 -20
- package/dist/themes/mantine.mjs +179 -150
- package/dist/themes/mui.d.mts +47 -21
- package/dist/themes/mui.mjs +259 -222
- package/dist/themes/radix.d.mts +38 -23
- package/dist/themes/radix.mjs +208 -180
- package/dist/themes/shadcn.d.mts +6 -3
- package/dist/themes/shadcn.mjs +93 -93
- package/dist/{types-C2Ay1FEh.d.mts → types-BrYbjC7_.d.mts} +7 -0
- package/package.json +5 -1
- package/dist/adapter-DcWi4XXn.d.mts +0 -223
- package/dist/renderer-OaOz-n6-.d.mts +0 -185
|
@@ -6,8 +6,8 @@ import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
|
|
|
6
6
|
import { fieldDomId, panelIdFor, tabIdFor } from "../core/idPath.mjs";
|
|
7
7
|
import { matchUnionOption, resolveDiscriminatedActive } from "../core/unionMatch.mjs";
|
|
8
8
|
import { displayJsonValue } from "../core/walkBuilders.mjs";
|
|
9
|
-
import { buildAriaAttrs } from "./a11y.mjs";
|
|
10
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { ariaLabel, buildAriaAttrs, buildHintInfo } from "./a11y.mjs";
|
|
10
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
11
|
import { isValidElement, useCallback, useEffect, useRef } from "react";
|
|
12
12
|
//#region src/react/headlessRenderers.tsx
|
|
13
13
|
/**
|
|
@@ -68,7 +68,6 @@ function renderString(props) {
|
|
|
68
68
|
const strValue = typeof props.value === "string" ? props.value : void 0;
|
|
69
69
|
if (strValue === void 0 || strValue.length === 0) return /* @__PURE__ */ jsx("span", {
|
|
70
70
|
id,
|
|
71
|
-
"aria-readonly": "true",
|
|
72
71
|
children: "—"
|
|
73
72
|
});
|
|
74
73
|
const format = props.constraints.format;
|
|
@@ -86,45 +85,53 @@ function renderString(props) {
|
|
|
86
85
|
});
|
|
87
86
|
if (format === "date") return /* @__PURE__ */ jsx("span", {
|
|
88
87
|
id,
|
|
89
|
-
"aria-readonly": "true",
|
|
90
88
|
children: formatDate(strValue) ?? strValue
|
|
91
89
|
});
|
|
92
90
|
if (format === "time") return /* @__PURE__ */ jsx("span", {
|
|
93
91
|
id,
|
|
94
|
-
"aria-readonly": "true",
|
|
95
92
|
children: formatTime(strValue) ?? strValue
|
|
96
93
|
});
|
|
97
94
|
if (format === "date-time" || format === "datetime") return /* @__PURE__ */ jsx("span", {
|
|
98
95
|
id,
|
|
99
|
-
"aria-readonly": "true",
|
|
100
96
|
children: formatDateTime(strValue) ?? strValue
|
|
101
97
|
});
|
|
102
98
|
return /* @__PURE__ */ jsx("span", {
|
|
103
99
|
id,
|
|
104
|
-
"aria-readonly": "true",
|
|
105
100
|
children: strValue
|
|
106
101
|
});
|
|
107
102
|
}
|
|
108
103
|
const strValue = typeof props.value === "string" ? props.value : "";
|
|
109
104
|
const dateType = dateInputType(props.constraints.format);
|
|
110
105
|
const ariaAttrs = buildAriaAttrs(props.tree);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
props.onChange(e.target.value);
|
|
117
|
-
},
|
|
118
|
-
...ariaAttrs
|
|
106
|
+
const hintInfo = buildHintInfo(id, props.constraints);
|
|
107
|
+
const renderHint = () => hintInfo === void 0 ? null : /* @__PURE__ */ jsx("small", {
|
|
108
|
+
id: hintInfo.id,
|
|
109
|
+
className: "sc-hint",
|
|
110
|
+
children: hintInfo.hint
|
|
119
111
|
});
|
|
112
|
+
if (dateType !== void 0) {
|
|
113
|
+
const dateInput = /* @__PURE__ */ jsx("input", {
|
|
114
|
+
id,
|
|
115
|
+
type: dateType,
|
|
116
|
+
value: props.writeOnly ? "" : strValue,
|
|
117
|
+
onChange: (e) => {
|
|
118
|
+
props.onChange(e.target.value);
|
|
119
|
+
},
|
|
120
|
+
"aria-describedby": hintInfo?.ariaDescribedBy,
|
|
121
|
+
...ariaAttrs
|
|
122
|
+
});
|
|
123
|
+
if (hintInfo === void 0) return dateInput;
|
|
124
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [dateInput, renderHint()] });
|
|
125
|
+
}
|
|
120
126
|
if (props.tree.type === "enum" && props.tree.enumValues.length > 0) {
|
|
121
127
|
const enumValues = props.tree.enumValues;
|
|
122
|
-
|
|
128
|
+
const select = /* @__PURE__ */ jsxs("select", {
|
|
123
129
|
id,
|
|
124
130
|
value: strValue,
|
|
125
131
|
onChange: (e) => {
|
|
126
132
|
props.onChange(e.target.value);
|
|
127
133
|
},
|
|
134
|
+
"aria-describedby": hintInfo?.ariaDescribedBy,
|
|
128
135
|
...ariaAttrs,
|
|
129
136
|
children: [/* @__PURE__ */ jsxs("option", {
|
|
130
137
|
value: "",
|
|
@@ -137,10 +144,14 @@ function renderString(props) {
|
|
|
137
144
|
}, display);
|
|
138
145
|
})]
|
|
139
146
|
});
|
|
147
|
+
if (hintInfo === void 0) return select;
|
|
148
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [select, renderHint()] });
|
|
140
149
|
}
|
|
141
|
-
|
|
150
|
+
const isCredential = props.writeOnly && props.constraints.format === "password";
|
|
151
|
+
const input = /* @__PURE__ */ jsx("input", {
|
|
142
152
|
id,
|
|
143
|
-
type: props.constraints.format === "email" ? "email" : props.constraints.format === "uri" ? "url" : "text",
|
|
153
|
+
type: isCredential ? "password" : props.constraints.format === "email" ? "email" : props.constraints.format === "uri" ? "url" : "text",
|
|
154
|
+
autoComplete: isCredential ? strValue.length > 0 ? "current-password" : "new-password" : void 0,
|
|
144
155
|
value: props.writeOnly ? "" : strValue,
|
|
145
156
|
onChange: (e) => {
|
|
146
157
|
props.onChange(e.target.value);
|
|
@@ -148,8 +159,11 @@ function renderString(props) {
|
|
|
148
159
|
placeholder: typeof props.meta.description === "string" ? props.meta.description : void 0,
|
|
149
160
|
minLength: props.constraints.minLength,
|
|
150
161
|
maxLength: props.constraints.maxLength,
|
|
162
|
+
"aria-describedby": hintInfo?.ariaDescribedBy,
|
|
151
163
|
...ariaAttrs
|
|
152
164
|
});
|
|
165
|
+
if (hintInfo === void 0) return input;
|
|
166
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [input, renderHint()] });
|
|
153
167
|
}
|
|
154
168
|
/** Headless renderer for `NumberField` — plain `<input type="number">`. */
|
|
155
169
|
function renderNumber(props) {
|
|
@@ -157,28 +171,39 @@ function renderNumber(props) {
|
|
|
157
171
|
if (props.readOnly) {
|
|
158
172
|
if (typeof props.value !== "number") return /* @__PURE__ */ jsx("span", {
|
|
159
173
|
id,
|
|
160
|
-
"aria-readonly": "true",
|
|
161
174
|
children: "—"
|
|
162
175
|
});
|
|
163
176
|
return /* @__PURE__ */ jsx("span", {
|
|
164
177
|
id,
|
|
165
|
-
"aria-readonly": "true",
|
|
166
178
|
children: props.value.toLocaleString()
|
|
167
179
|
});
|
|
168
180
|
}
|
|
169
181
|
const numValue = typeof props.value === "number" ? props.value : "";
|
|
170
182
|
const ariaAttrs = buildAriaAttrs(props.tree);
|
|
171
|
-
|
|
183
|
+
const hintInfo = buildHintInfo(id, props.constraints);
|
|
184
|
+
const isInteger = props.tree.type === "number" ? props.tree.isInteger : false;
|
|
185
|
+
const inputMode = isInteger ? "numeric" : "decimal";
|
|
186
|
+
const multipleOf = props.constraints.multipleOf;
|
|
187
|
+
const numberInput = /* @__PURE__ */ jsx("input", {
|
|
172
188
|
id,
|
|
173
189
|
type: "number",
|
|
190
|
+
inputMode,
|
|
191
|
+
step: multipleOf !== void 0 ? String(multipleOf) : isInteger ? "1" : void 0,
|
|
174
192
|
value: props.writeOnly ? "" : numValue,
|
|
175
193
|
onChange: (e) => {
|
|
176
194
|
props.onChange(Number(e.target.value));
|
|
177
195
|
},
|
|
178
196
|
min: props.constraints.minimum,
|
|
179
197
|
max: props.constraints.maximum,
|
|
198
|
+
"aria-describedby": hintInfo?.ariaDescribedBy,
|
|
180
199
|
...ariaAttrs
|
|
181
200
|
});
|
|
201
|
+
if (hintInfo === void 0) return numberInput;
|
|
202
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [numberInput, /* @__PURE__ */ jsx("small", {
|
|
203
|
+
id: hintInfo.id,
|
|
204
|
+
className: "sc-hint",
|
|
205
|
+
children: hintInfo.hint
|
|
206
|
+
})] });
|
|
182
207
|
}
|
|
183
208
|
/** Headless renderer for `BooleanField` — plain `<input type="checkbox">`. */
|
|
184
209
|
function renderBoolean(props) {
|
|
@@ -186,12 +211,10 @@ function renderBoolean(props) {
|
|
|
186
211
|
if (props.readOnly) {
|
|
187
212
|
if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx("span", {
|
|
188
213
|
id,
|
|
189
|
-
"aria-readonly": "true",
|
|
190
214
|
children: "—"
|
|
191
215
|
});
|
|
192
216
|
return /* @__PURE__ */ jsx("span", {
|
|
193
217
|
id,
|
|
194
|
-
"aria-readonly": "true",
|
|
195
218
|
children: props.value ? "Yes" : "No"
|
|
196
219
|
});
|
|
197
220
|
}
|
|
@@ -212,17 +235,18 @@ function renderEnum(props) {
|
|
|
212
235
|
const enumValue = typeof props.value === "string" ? props.value : "";
|
|
213
236
|
if (props.readOnly) return /* @__PURE__ */ jsx("span", {
|
|
214
237
|
id,
|
|
215
|
-
"aria-readonly": "true",
|
|
216
238
|
children: enumValue || "—"
|
|
217
239
|
});
|
|
218
240
|
const ariaAttrs = buildAriaAttrs(props.tree);
|
|
241
|
+
const hintInfo = buildHintInfo(id, props.constraints);
|
|
219
242
|
const enumValues = props.tree.type === "enum" ? props.tree.enumValues : [];
|
|
220
|
-
|
|
243
|
+
const select = /* @__PURE__ */ jsxs("select", {
|
|
221
244
|
id,
|
|
222
245
|
value: props.writeOnly ? "" : enumValue,
|
|
223
246
|
onChange: (e) => {
|
|
224
247
|
props.onChange(e.target.value);
|
|
225
248
|
},
|
|
249
|
+
"aria-describedby": hintInfo?.ariaDescribedBy,
|
|
226
250
|
...ariaAttrs,
|
|
227
251
|
children: [/* @__PURE__ */ jsxs("option", {
|
|
228
252
|
value: "",
|
|
@@ -235,6 +259,12 @@ function renderEnum(props) {
|
|
|
235
259
|
}, display);
|
|
236
260
|
})]
|
|
237
261
|
});
|
|
262
|
+
if (hintInfo === void 0) return select;
|
|
263
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [select, /* @__PURE__ */ jsx("small", {
|
|
264
|
+
id: hintInfo.id,
|
|
265
|
+
className: "sc-hint",
|
|
266
|
+
children: hintInfo.hint
|
|
267
|
+
})] });
|
|
238
268
|
}
|
|
239
269
|
/** Headless renderer for `ObjectField` — `<fieldset>` per object with one child per property. */
|
|
240
270
|
function renderObject(props) {
|
|
@@ -253,9 +283,9 @@ function renderObject(props) {
|
|
|
253
283
|
};
|
|
254
284
|
const child = toReactNode(props.renderChild(field, childValue, childOnChange, key));
|
|
255
285
|
if (child === null || child === void 0) return null;
|
|
256
|
-
return /* @__PURE__ */ jsxs("div", { children: [
|
|
286
|
+
return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("label", {
|
|
257
287
|
htmlFor: childId,
|
|
258
|
-
children: [field.meta.description, field.isOptional === false && /* @__PURE__ */ jsxs("span", {
|
|
288
|
+
children: [typeof field.meta.description === "string" ? field.meta.description : key, field.isOptional === false && /* @__PURE__ */ jsxs("span", {
|
|
259
289
|
"aria-hidden": "true",
|
|
260
290
|
style: { color: "#dc2626" },
|
|
261
291
|
children: [" ", "*"]
|
|
@@ -277,7 +307,17 @@ function defaultRecordValue(valueType) {
|
|
|
277
307
|
case "array": return [];
|
|
278
308
|
case "object":
|
|
279
309
|
case "record": return {};
|
|
280
|
-
|
|
310
|
+
case "null": return null;
|
|
311
|
+
case "unknown":
|
|
312
|
+
case "enum":
|
|
313
|
+
case "literal":
|
|
314
|
+
case "tuple":
|
|
315
|
+
case "union":
|
|
316
|
+
case "discriminatedUnion":
|
|
317
|
+
case "conditional":
|
|
318
|
+
case "negation":
|
|
319
|
+
case "file":
|
|
320
|
+
case "never": return;
|
|
281
321
|
}
|
|
282
322
|
}
|
|
283
323
|
/**
|
|
@@ -309,13 +349,10 @@ function renderRecord(props) {
|
|
|
309
349
|
const valueType = props.tree.valueType;
|
|
310
350
|
const entries = Object.entries(obj);
|
|
311
351
|
if (props.readOnly) {
|
|
312
|
-
if (entries.length === 0) return /* @__PURE__ */ jsx("span", {
|
|
313
|
-
"aria-readonly": "true",
|
|
314
|
-
children: "—"
|
|
315
|
-
});
|
|
352
|
+
if (entries.length === 0) return /* @__PURE__ */ jsx("span", { children: "—" });
|
|
316
353
|
return /* @__PURE__ */ jsx("div", {
|
|
317
354
|
role: "group",
|
|
318
|
-
"aria-label":
|
|
355
|
+
"aria-label": ariaLabel(props.meta.description),
|
|
319
356
|
children: entries.map(([key, value]) => {
|
|
320
357
|
return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
321
358
|
htmlFor: inputId(`${props.path}.${key}`),
|
|
@@ -351,7 +388,7 @@ function renderRecord(props) {
|
|
|
351
388
|
};
|
|
352
389
|
return /* @__PURE__ */ jsxs("div", {
|
|
353
390
|
role: "group",
|
|
354
|
-
"aria-label": props.meta.description
|
|
391
|
+
"aria-label": ariaLabel(props.meta.description),
|
|
355
392
|
children: [entries.map(([key, value]) => {
|
|
356
393
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
357
394
|
/* @__PURE__ */ jsx("input", {
|
|
@@ -389,18 +426,47 @@ function renderArray(props) {
|
|
|
389
426
|
const arr = Array.isArray(props.value) ? props.value : [];
|
|
390
427
|
const element = props.tree.element;
|
|
391
428
|
if (element === void 0) return null;
|
|
392
|
-
if (
|
|
393
|
-
|
|
429
|
+
if (props.readOnly) {
|
|
430
|
+
if (arr.length === 0) return null;
|
|
431
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
432
|
+
role: "group",
|
|
433
|
+
"aria-label": ariaLabel(props.meta.description),
|
|
434
|
+
children: arr.map((item, i) => /* @__PURE__ */ jsx("li", { children: toReactNode(props.renderChild(element, item, () => {}, `[${String(i)}]`)) }, String(i)))
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
const handleRemove = (index) => {
|
|
438
|
+
const next = arr.slice();
|
|
439
|
+
next.splice(index, 1);
|
|
440
|
+
props.onChange(next);
|
|
441
|
+
};
|
|
442
|
+
const handleAdd = () => {
|
|
443
|
+
const next = arr.slice();
|
|
444
|
+
next.push(defaultRecordValue(element));
|
|
445
|
+
props.onChange(next);
|
|
446
|
+
};
|
|
447
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
394
448
|
role: "group",
|
|
395
|
-
"aria-label": props.meta.description
|
|
396
|
-
children: arr.map((item, i) => {
|
|
449
|
+
"aria-label": ariaLabel(props.meta.description),
|
|
450
|
+
children: [/* @__PURE__ */ jsx("ul", { children: arr.map((item, i) => {
|
|
397
451
|
const childOnChange = (v) => {
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
props.onChange(
|
|
452
|
+
const nextArr = arr.slice();
|
|
453
|
+
nextArr[i] = v;
|
|
454
|
+
props.onChange(nextArr);
|
|
401
455
|
};
|
|
402
|
-
return /* @__PURE__ */
|
|
403
|
-
|
|
456
|
+
return /* @__PURE__ */ jsxs("li", { children: [toReactNode(props.renderChild(element, item, childOnChange, `[${String(i)}]`)), /* @__PURE__ */ jsx("button", {
|
|
457
|
+
type: "button",
|
|
458
|
+
"aria-label": `Remove item ${String(i)}`,
|
|
459
|
+
onClick: () => {
|
|
460
|
+
handleRemove(i);
|
|
461
|
+
},
|
|
462
|
+
children: "Remove"
|
|
463
|
+
})] }, String(i));
|
|
464
|
+
}) }), /* @__PURE__ */ jsx("button", {
|
|
465
|
+
type: "button",
|
|
466
|
+
"aria-label": "Add item",
|
|
467
|
+
onClick: handleAdd,
|
|
468
|
+
children: "Add"
|
|
469
|
+
})]
|
|
404
470
|
});
|
|
405
471
|
}
|
|
406
472
|
/** Headless renderer for plain `UnionField` — picks the matching option and renders it. */
|
|
@@ -506,6 +572,7 @@ function DiscriminatedUnionTabs({ options, optionLabels, activeIndex, path, disc
|
|
|
506
572
|
return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
507
573
|
role: "tablist",
|
|
508
574
|
"aria-label": "Select variant",
|
|
575
|
+
"aria-orientation": "horizontal",
|
|
509
576
|
style: {
|
|
510
577
|
display: "flex",
|
|
511
578
|
gap: "0.25rem",
|
|
@@ -519,7 +586,7 @@ function DiscriminatedUnionTabs({ options, optionLabels, activeIndex, path, disc
|
|
|
519
586
|
type: "button",
|
|
520
587
|
role: "tab",
|
|
521
588
|
id: tabIdFor(path, i),
|
|
522
|
-
"aria-selected": i === activeIndex ? "true" :
|
|
589
|
+
"aria-selected": i === activeIndex ? "true" : "false",
|
|
523
590
|
"aria-controls": panelId,
|
|
524
591
|
tabIndex: i === activeIndex ? 0 : -1,
|
|
525
592
|
onClick: () => {
|
|
@@ -548,10 +615,11 @@ function renderFile(props) {
|
|
|
548
615
|
const accept = props.constraints.mimeTypes?.join(",");
|
|
549
616
|
if (props.readOnly) return /* @__PURE__ */ jsx("span", {
|
|
550
617
|
id,
|
|
551
|
-
"aria-readonly": "true",
|
|
552
618
|
children: "File field"
|
|
553
619
|
});
|
|
554
|
-
|
|
620
|
+
const ariaAttrs = buildAriaAttrs(props.tree, props.meta.description);
|
|
621
|
+
const hintInfo = buildHintInfo(id, props.constraints);
|
|
622
|
+
const fileInput = /* @__PURE__ */ jsx("input", {
|
|
555
623
|
id,
|
|
556
624
|
type: "file",
|
|
557
625
|
accept,
|
|
@@ -559,8 +627,15 @@ function renderFile(props) {
|
|
|
559
627
|
const file = e.target.files?.[0];
|
|
560
628
|
if (file !== void 0) props.onChange(file);
|
|
561
629
|
},
|
|
562
|
-
|
|
630
|
+
"aria-describedby": hintInfo?.ariaDescribedBy,
|
|
631
|
+
...ariaAttrs
|
|
563
632
|
});
|
|
633
|
+
if (hintInfo === void 0) return fileInput;
|
|
634
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [fileInput, /* @__PURE__ */ jsx("small", {
|
|
635
|
+
id: hintInfo.id,
|
|
636
|
+
className: "sc-hint",
|
|
637
|
+
children: hintInfo.hint
|
|
638
|
+
})] });
|
|
564
639
|
}
|
|
565
640
|
/**
|
|
566
641
|
* Render a literal field — `z.literal("a")` or `{ const: 5 }`.
|
|
@@ -575,12 +650,10 @@ function renderLiteral(props) {
|
|
|
575
650
|
const values = props.tree.literalValues;
|
|
576
651
|
if (values.length === 0) return /* @__PURE__ */ jsx("span", {
|
|
577
652
|
id,
|
|
578
|
-
"aria-readonly": "true",
|
|
579
653
|
children: "—"
|
|
580
654
|
});
|
|
581
655
|
return /* @__PURE__ */ jsx("span", {
|
|
582
656
|
id,
|
|
583
|
-
"aria-readonly": "true",
|
|
584
657
|
children: values.map((v) => displayJsonValue(v)).join(", ")
|
|
585
658
|
});
|
|
586
659
|
}
|
|
@@ -593,7 +666,6 @@ function renderLiteral(props) {
|
|
|
593
666
|
function renderNull(props) {
|
|
594
667
|
return /* @__PURE__ */ jsx("span", {
|
|
595
668
|
id: inputId(props.path),
|
|
596
|
-
"aria-readonly": "true",
|
|
597
669
|
children: "—"
|
|
598
670
|
});
|
|
599
671
|
}
|
|
@@ -609,7 +681,6 @@ function renderNull(props) {
|
|
|
609
681
|
function renderNever(props) {
|
|
610
682
|
return /* @__PURE__ */ jsx("span", {
|
|
611
683
|
id: inputId(props.path),
|
|
612
|
-
"aria-readonly": "true",
|
|
613
684
|
className: SC_CLASSES.never,
|
|
614
685
|
children: /* @__PURE__ */ jsx("em", { children: "never matches" })
|
|
615
686
|
});
|
|
@@ -631,7 +702,7 @@ function renderTuple(props) {
|
|
|
631
702
|
const restCount = restItems !== void 0 ? Math.max(arr.length - prefixItems.length, 0) : 0;
|
|
632
703
|
return /* @__PURE__ */ jsxs("div", {
|
|
633
704
|
role: "group",
|
|
634
|
-
"aria-label": props.meta.description
|
|
705
|
+
"aria-label": ariaLabel(props.meta.description),
|
|
635
706
|
children: [prefixItems.map((element, i) => {
|
|
636
707
|
const itemValue = arr[i];
|
|
637
708
|
const childOnChange = (v) => {
|
|
@@ -717,12 +788,10 @@ function renderUnknown(props) {
|
|
|
717
788
|
if (props.readOnly) {
|
|
718
789
|
if (props.value === void 0 || props.value === null) return /* @__PURE__ */ jsx("span", {
|
|
719
790
|
id,
|
|
720
|
-
"aria-readonly": "true",
|
|
721
791
|
children: "—"
|
|
722
792
|
});
|
|
723
793
|
return /* @__PURE__ */ jsx("span", {
|
|
724
794
|
id,
|
|
725
|
-
"aria-readonly": "true",
|
|
726
795
|
children: typeof props.value === "string" ? props.value : JSON.stringify(props.value)
|
|
727
796
|
});
|
|
728
797
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as DiagnosticsOptions } from "./diagnostics-
|
|
1
|
+
import { i as DiagnosticsOptions } from "./diagnostics-BTrm3O6J.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/core/ref.d.ts
|
|
4
4
|
/**
|
|
@@ -12,6 +12,38 @@ declare const RECURSIVE_ANCHOR_SENTINEL = "__recursive__";
|
|
|
12
12
|
* Resolver function for external $ref URIs.
|
|
13
13
|
* Called with the URI portion (everything before `#`) of an external ref.
|
|
14
14
|
* Returns the parsed document (JSON object) or undefined.
|
|
15
|
+
*
|
|
16
|
+
* ### Security warning — SSRF and local-file disclosure
|
|
17
|
+
*
|
|
18
|
+
* Consumers MUST validate the URI before fetching the target document.
|
|
19
|
+
* schema-components hands the resolver the raw `$ref` URI from the
|
|
20
|
+
* document — which is typically user-controlled — and any network or
|
|
21
|
+
* filesystem access the resolver performs runs with the host
|
|
22
|
+
* application's full privileges. An attacker-crafted schema that
|
|
23
|
+
* references an internal endpoint or a local filesystem path will
|
|
24
|
+
* happily exfiltrate or expose data the application never intended to
|
|
25
|
+
* surface.
|
|
26
|
+
*
|
|
27
|
+
* At a minimum the resolver should:
|
|
28
|
+
*
|
|
29
|
+
* - Refuse non-`https:` schemes by default. Permit `http:` only on an
|
|
30
|
+
* explicit allow-list. Refuse `file:`, `data:`, `javascript:`,
|
|
31
|
+
* `ftp:`, `gopher:`, and every other scheme outright.
|
|
32
|
+
* - Resolve the URI's hostname and refuse loopback addresses
|
|
33
|
+
* (`127.0.0.0/8`, `::1`), link-local addresses (`169.254.0.0/16`,
|
|
34
|
+
* `fe80::/10`), private ranges (`10.0.0.0/8`, `172.16.0.0/12`,
|
|
35
|
+
* `192.168.0.0/16`, `fc00::/7`), and cloud-metadata IPs
|
|
36
|
+
* (`169.254.169.254`, `fd00:ec2::254`).
|
|
37
|
+
* - Apply a strict allow-list of permitted hosts where possible.
|
|
38
|
+
* - Set request timeouts and a maximum response size.
|
|
39
|
+
* - Disable HTTP redirects, or re-validate the redirected URL against
|
|
40
|
+
* the same denylist before following.
|
|
41
|
+
* - Reject responses that are not `application/json` or
|
|
42
|
+
* `application/yaml`.
|
|
43
|
+
*
|
|
44
|
+
* schema-components performs no validation itself — that responsibility
|
|
45
|
+
* sits exclusively with the resolver implementation supplied by the
|
|
46
|
+
* caller.
|
|
15
47
|
*/
|
|
16
48
|
type ExternalResolver = (uri: string) => unknown;
|
|
17
49
|
/**
|
|
@@ -1,42 +1,58 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ComponentResolver } from "../core/renderer.mjs";
|
|
2
|
+
import { ElementType } from "react";
|
|
2
3
|
|
|
3
4
|
//#region src/themes/mantine.d.ts
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Element types the Mantine resolver renders into. Every slot is the
|
|
7
|
+
* Mantine component the corresponding render function expects to find
|
|
8
|
+
* at the call site; consumers wire these in once via
|
|
9
|
+
* `createMantineResolver`.
|
|
9
10
|
*
|
|
10
11
|
* `Text` is required so read-only scalars render as a styled Mantine
|
|
11
12
|
* `<Text>` element instead of a bare `<span>`, matching the visual
|
|
12
13
|
* weight of the editable variants.
|
|
14
|
+
*/
|
|
15
|
+
interface MantineComponents {
|
|
16
|
+
TextInput: ElementType;
|
|
17
|
+
NumberInput: ElementType;
|
|
18
|
+
Switch: ElementType;
|
|
19
|
+
Select: ElementType;
|
|
20
|
+
Fieldset: ElementType;
|
|
21
|
+
Text: ElementType;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build a Mantine-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.
|
|
13
35
|
*
|
|
14
36
|
* @group Themes
|
|
15
37
|
*/
|
|
16
|
-
declare function
|
|
17
|
-
TextInput: React.ElementType;
|
|
18
|
-
NumberInput: React.ElementType;
|
|
19
|
-
Switch: React.ElementType;
|
|
20
|
-
Select: React.ElementType;
|
|
21
|
-
Fieldset: React.ElementType;
|
|
22
|
-
Text: React.ElementType;
|
|
23
|
-
}): void;
|
|
38
|
+
declare function createMantineResolver(components: MantineComponents): ComponentResolver;
|
|
24
39
|
/**
|
|
25
40
|
* Component resolver mapping schema field types to Mantine primitives —
|
|
26
41
|
* `TextInput`, `NumberInput`, `Switch`, `Select`, `Fieldset`, `Text`.
|
|
27
42
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* actual Mantine element types.
|
|
43
|
+
* Built against minimal HTML stubs so the resolver is usable without
|
|
44
|
+
* wiring up `@mantine/core` first — production usage should call
|
|
45
|
+
* {@link createMantineResolver} with real Mantine element types.
|
|
32
46
|
*
|
|
33
47
|
* @group Themes
|
|
34
48
|
* @example
|
|
35
49
|
* ```tsx
|
|
36
50
|
* import { TextInput, NumberInput, Switch, Select, Fieldset, Text } from "@mantine/core";
|
|
37
|
-
* import {
|
|
51
|
+
* import { createMantineResolver } from "schema-components/themes/mantine";
|
|
38
52
|
*
|
|
39
|
-
*
|
|
53
|
+
* const mantineResolver = createMantineResolver({
|
|
54
|
+
* TextInput, NumberInput, Switch, Select, Fieldset, Text,
|
|
55
|
+
* });
|
|
40
56
|
*
|
|
41
57
|
* <SchemaProvider resolver={mantineResolver}>
|
|
42
58
|
* <SchemaComponent schema={userSchema} value={user} onChange={setUser} />
|
|
@@ -45,4 +61,4 @@ declare function registerMantineComponents(components: {
|
|
|
45
61
|
*/
|
|
46
62
|
declare const mantineResolver: ComponentResolver;
|
|
47
63
|
//#endregion
|
|
48
|
-
export {
|
|
64
|
+
export { MantineComponents, createMantineResolver, mantineResolver };
|