schema-components 1.22.0 → 1.24.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/README.md +3 -1
- package/dist/core/adapter.d.mts +97 -3
- package/dist/core/adapter.mjs +260 -111
- package/dist/core/constraints.d.mts +2 -2
- package/dist/core/constraints.mjs +0 -7
- package/dist/core/cssClasses.d.mts +52 -0
- package/dist/core/cssClasses.mjs +51 -0
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/errors.mjs +5 -13
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/core/formats.d.mts +9 -2
- package/dist/core/formats.mjs +12 -1
- package/dist/core/idPath.d.mts +54 -0
- package/dist/core/idPath.mjs +66 -0
- package/dist/core/merge.d.mts +10 -1
- package/dist/core/merge.mjs +49 -10
- package/dist/core/normalise.d.mts +14 -3
- package/dist/core/normalise.mjs +2 -2
- package/dist/core/openapi30.d.mts +15 -1
- package/dist/core/openapi30.mjs +2 -2
- package/dist/core/openapiConstants.d.mts +67 -0
- package/dist/core/openapiConstants.mjs +90 -0
- package/dist/core/ref.d.mts +2 -2
- package/dist/core/ref.mjs +83 -6
- package/dist/core/refChain.d.mts +70 -0
- package/dist/core/refChain.mjs +44 -0
- package/dist/core/renderer.d.mts +1 -1
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/swagger2.mjs +1 -1
- package/dist/core/typeInference.d.mts +982 -2
- package/dist/core/types.d.mts +1 -1
- package/dist/core/unionMatch.d.mts +36 -0
- package/dist/core/unionMatch.mjs +53 -0
- package/dist/core/version.d.mts +1 -1
- package/dist/core/version.mjs +29 -17
- package/dist/core/walkBuilders.d.mts +23 -4
- package/dist/core/walkBuilders.mjs +27 -7
- package/dist/core/walker.d.mts +1 -1
- package/dist/core/walker.mjs +44 -45
- package/dist/{diagnostics-D0QCYGv0.d.mts → diagnostics-Cbwak-ZX.d.mts} +1 -1
- package/dist/{errors-DpFwqs5C.d.mts → errors-g_MCTQel.d.mts} +9 -15
- package/dist/html/a11y.d.mts +9 -4
- package/dist/html/a11y.mjs +10 -19
- package/dist/html/renderToHtml.d.mts +2 -2
- package/dist/html/renderToHtmlStream.d.mts +2 -2
- package/dist/html/renderToHtmlStream.mjs +12 -1
- package/dist/html/renderers.d.mts +32 -8
- package/dist/html/renderers.mjs +125 -111
- package/dist/html/streamRenderers.d.mts +4 -5
- package/dist/html/streamRenderers.mjs +40 -61
- package/dist/{normalise-DVEJQmF7.mjs → normalise-DCYp06Sr.mjs} +352 -162
- 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/components.d.mts +116 -37
- package/dist/openapi/components.mjs +54 -37
- package/dist/openapi/parser.d.mts +19 -9
- package/dist/openapi/parser.mjs +262 -86
- package/dist/openapi/resolve.d.mts +20 -11
- package/dist/openapi/resolve.mjs +135 -75
- package/dist/react/SchemaComponent.d.mts +32 -7
- package/dist/react/SchemaComponent.mjs +45 -21
- package/dist/react/SchemaView.d.mts +30 -10
- package/dist/react/a11y.d.mts +21 -0
- package/dist/react/a11y.mjs +24 -0
- package/dist/react/fieldPath.d.mts +1 -1
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +8 -9
- package/dist/react/headlessRenderers.mjs +41 -72
- package/dist/{ref-D-_JBZkF.d.mts → ref-DCDuswPe.d.mts} +38 -3
- package/dist/{renderer-BaRlQIuN.d.mts → renderer-CXJ8y0qw.d.mts} +1 -1
- package/dist/themes/mantine.d.mts +1 -1
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/themes/shadcn.mjs +2 -1
- package/dist/{types-BrRMV0en.d.mts → types-BTB73MB8.d.mts} +32 -4
- package/dist/{version-D2jfdX6E.d.mts → version-BFTVLsdb.d.mts} +7 -1
- package/package.json +1 -1
- package/dist/typeInference-DkcUHfaM.d.mts +0 -982
package/dist/html/renderers.mjs
CHANGED
|
@@ -1,20 +1,53 @@
|
|
|
1
|
+
import { isObject } from "../core/guards.mjs";
|
|
2
|
+
import { dateInputType } from "../core/formats.mjs";
|
|
1
3
|
import { isSafeHyperlink, isSafeMailtoAddress } from "../core/uri.mjs";
|
|
4
|
+
import { SC_CLASSES } from "../core/cssClasses.mjs";
|
|
2
5
|
import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
|
|
6
|
+
import { fieldDomId, panelIdFor, tabIdFor } from "../core/idPath.mjs";
|
|
7
|
+
import { matchUnionOption, resolveDiscriminatedActive } from "../core/unionMatch.mjs";
|
|
8
|
+
import { displayJsonValue } from "../core/walkBuilders.mjs";
|
|
3
9
|
import { h, raw, serialize } from "./html.mjs";
|
|
4
10
|
import { ariaDescribedByAttrs, ariaLabelAttrs, ariaReadonlyAttrs, ariaRequiredAttrs, buildHintElement, buildInputId, requiredIndicator } from "./a11y.mjs";
|
|
5
11
|
//#region src/html/renderers.ts
|
|
6
|
-
function dateInputType(format) {
|
|
7
|
-
if (format === "date") return "date";
|
|
8
|
-
if (format === "time") return "time";
|
|
9
|
-
if (format === "date-time" || format === "datetime") return "datetime-local";
|
|
10
|
-
}
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Thin wrapper over `fieldDomId` from `core/idPath.ts`. Every render
|
|
14
|
+
* pipeline must derive ids from the same canonical normaliser so that
|
|
15
|
+
* `aria-controls`, `aria-labelledby`, and `htmlFor` references resolve
|
|
16
|
+
* consistently across the React, sync-HTML, and streaming-HTML outputs.
|
|
17
|
+
*
|
|
18
|
+
* The wrapper tolerates an empty path here (returning `sc-`) so that
|
|
19
|
+
* a leaf renderer at the schema root — `renderToHtml(z.string())` — has
|
|
20
|
+
* a usable id without throwing. Container renderers always thread a
|
|
21
|
+
* non-empty path through `renderChild`, so the empty-id fallback can
|
|
22
|
+
* never produce sibling collisions inside a structured form.
|
|
15
23
|
*/
|
|
16
24
|
function fieldId(path) {
|
|
17
|
-
|
|
25
|
+
if (path.length === 0) return "sc-";
|
|
26
|
+
return fieldDomId(path);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Tab-panel id for a discriminated union at `path`. Delegates to the
|
|
30
|
+
* canonical `panelIdFor` from `core/idPath.ts` for the normal case so
|
|
31
|
+
* the sync, streaming, and React renderers all emit identical ids; falls
|
|
32
|
+
* back to a structurally-equivalent string when the renderer is invoked
|
|
33
|
+
* with an empty root path (a discriminated union at the schema root —
|
|
34
|
+
* see the `fieldId` doc comment for the wider context).
|
|
35
|
+
*
|
|
36
|
+
* Exported because `streamRenderers.ts` needs to derive identical ids
|
|
37
|
+
* — the panel id on the `<div role="tabpanel">` must match the
|
|
38
|
+
* `aria-controls` on every tab regardless of which pipeline rendered it.
|
|
39
|
+
*/
|
|
40
|
+
function panelId(path) {
|
|
41
|
+
if (path.length === 0) return `${fieldId(path)}-panel`;
|
|
42
|
+
return panelIdFor(path);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Tab id for tab `i` within a discriminated union at `path`. Mirror of
|
|
46
|
+
* `panelId` above — see its comment.
|
|
47
|
+
*/
|
|
48
|
+
function tabId(path, i) {
|
|
49
|
+
if (path.length === 0) return `${fieldId(path)}-tab-${String(i)}`;
|
|
50
|
+
return tabIdFor(path, i);
|
|
18
51
|
}
|
|
19
52
|
function renderStringHtml(props) {
|
|
20
53
|
if (props.readOnly) return serialize(renderStringReadOnly(props));
|
|
@@ -23,22 +56,22 @@ function renderStringHtml(props) {
|
|
|
23
56
|
function renderStringReadOnly(props) {
|
|
24
57
|
const strValue = typeof props.value === "string" ? props.value : void 0;
|
|
25
58
|
if (strValue === void 0 || strValue.length === 0) return h("span", {
|
|
26
|
-
class:
|
|
59
|
+
class: SC_CLASSES.valueEmpty,
|
|
27
60
|
...ariaReadonlyAttrs()
|
|
28
61
|
}, "—");
|
|
29
62
|
const format = props.constraints.format;
|
|
30
63
|
if (format === "email" && isSafeMailtoAddress(strValue)) return h("a", {
|
|
31
|
-
class:
|
|
64
|
+
class: SC_CLASSES.value,
|
|
32
65
|
href: `mailto:${strValue}`,
|
|
33
66
|
...ariaReadonlyAttrs()
|
|
34
67
|
}, strValue);
|
|
35
68
|
if ((format === "uri" || format === "url") && isSafeHyperlink(strValue)) return h("a", {
|
|
36
|
-
class:
|
|
69
|
+
class: SC_CLASSES.value,
|
|
37
70
|
href: strValue,
|
|
38
71
|
...ariaReadonlyAttrs()
|
|
39
72
|
}, strValue);
|
|
40
73
|
return h("span", {
|
|
41
|
-
class:
|
|
74
|
+
class: SC_CLASSES.value,
|
|
42
75
|
...ariaReadonlyAttrs()
|
|
43
76
|
}, strValue);
|
|
44
77
|
}
|
|
@@ -48,7 +81,7 @@ function renderStringEditable(props) {
|
|
|
48
81
|
const inputType = dateInputType(format) ?? (format === "email" ? "email" : format === "uri" ? "url" : "text");
|
|
49
82
|
const id = fieldId(props.path);
|
|
50
83
|
const attrs = {
|
|
51
|
-
class:
|
|
84
|
+
class: SC_CLASSES.input,
|
|
52
85
|
id,
|
|
53
86
|
type: inputType,
|
|
54
87
|
name: id
|
|
@@ -67,11 +100,11 @@ function renderNumberHtml(props) {
|
|
|
67
100
|
}
|
|
68
101
|
function renderNumberReadOnly(props) {
|
|
69
102
|
if (typeof props.value !== "number") return h("span", {
|
|
70
|
-
class:
|
|
103
|
+
class: SC_CLASSES.valueEmpty,
|
|
71
104
|
...ariaReadonlyAttrs()
|
|
72
105
|
}, "—");
|
|
73
106
|
return h("span", {
|
|
74
|
-
class:
|
|
107
|
+
class: SC_CLASSES.value,
|
|
75
108
|
...ariaReadonlyAttrs()
|
|
76
109
|
}, props.value.toLocaleString());
|
|
77
110
|
}
|
|
@@ -79,7 +112,7 @@ function renderNumberEditable(props) {
|
|
|
79
112
|
const numValue = typeof props.value === "number" ? String(props.value) : "";
|
|
80
113
|
const id = fieldId(props.path);
|
|
81
114
|
const attrs = {
|
|
82
|
-
class:
|
|
115
|
+
class: SC_CLASSES.input,
|
|
83
116
|
id,
|
|
84
117
|
type: "number",
|
|
85
118
|
name: id
|
|
@@ -97,7 +130,7 @@ function renderBooleanHtml(props) {
|
|
|
97
130
|
}
|
|
98
131
|
function renderBooleanReadOnly(props) {
|
|
99
132
|
if (typeof props.value !== "boolean") return h("span", {
|
|
100
|
-
class:
|
|
133
|
+
class: SC_CLASSES.valueEmpty,
|
|
101
134
|
...ariaReadonlyAttrs()
|
|
102
135
|
}, "—");
|
|
103
136
|
return h("span", {
|
|
@@ -108,7 +141,7 @@ function renderBooleanReadOnly(props) {
|
|
|
108
141
|
function renderBooleanEditable(props) {
|
|
109
142
|
const id = fieldId(props.path);
|
|
110
143
|
const attrs = {
|
|
111
|
-
class:
|
|
144
|
+
class: SC_CLASSES.input,
|
|
112
145
|
id,
|
|
113
146
|
type: "checkbox",
|
|
114
147
|
name: id
|
|
@@ -125,11 +158,11 @@ function renderEnumHtml(props) {
|
|
|
125
158
|
function renderEnumReadOnly(props) {
|
|
126
159
|
const enumValue = typeof props.value === "string" ? props.value : "";
|
|
127
160
|
if (enumValue.length === 0) return h("span", {
|
|
128
|
-
class:
|
|
161
|
+
class: SC_CLASSES.valueEmpty,
|
|
129
162
|
...ariaReadonlyAttrs()
|
|
130
163
|
}, "—");
|
|
131
164
|
return h("span", {
|
|
132
|
-
class:
|
|
165
|
+
class: SC_CLASSES.value,
|
|
133
166
|
...ariaReadonlyAttrs()
|
|
134
167
|
}, enumValue);
|
|
135
168
|
}
|
|
@@ -138,14 +171,14 @@ function renderEnumEditable(props) {
|
|
|
138
171
|
const id = fieldId(props.path);
|
|
139
172
|
const selectedValue = props.writeOnly ? "" : enumValue;
|
|
140
173
|
const enumValues = props.tree.type === "enum" ? props.tree.enumValues : [];
|
|
141
|
-
const optionNodes = [h("option", { value: "" },
|
|
142
|
-
const display =
|
|
174
|
+
const optionNodes = [h("option", { value: "" }, `Select…`), ...enumValues.map((v) => {
|
|
175
|
+
const display = displayJsonValue(v);
|
|
143
176
|
const attrs = { value: display };
|
|
144
177
|
if (display === selectedValue) attrs.selected = true;
|
|
145
178
|
return h("option", attrs, display);
|
|
146
179
|
})];
|
|
147
180
|
const selectAttrs = {
|
|
148
|
-
class:
|
|
181
|
+
class: SC_CLASSES.input,
|
|
149
182
|
id,
|
|
150
183
|
name: id
|
|
151
184
|
};
|
|
@@ -158,8 +191,7 @@ function renderObjectHtml(props) {
|
|
|
158
191
|
function renderObjectNode(props) {
|
|
159
192
|
if (props.tree.type !== "object") return "";
|
|
160
193
|
const fields = props.tree.fields;
|
|
161
|
-
const
|
|
162
|
-
const obj = isRecord(props.value) ? props.value : {};
|
|
194
|
+
const obj = isObject(props.value) ? props.value : {};
|
|
163
195
|
const descriptionText = typeof props.meta.description === "string" ? props.meta.description : void 0;
|
|
164
196
|
const legend = descriptionText !== void 0 ? h("legend", {}, descriptionText) : void 0;
|
|
165
197
|
const sortedEntries = sortFieldsByOrder(fields).filter(([, field]) => field.meta.visible !== false);
|
|
@@ -170,10 +202,10 @@ function renderObjectNode(props) {
|
|
|
170
202
|
const label = typeof field.meta.description === "string" ? field.meta.description : key;
|
|
171
203
|
const childValue = obj[key];
|
|
172
204
|
const childHtml = props.renderChild(field, childValue, key);
|
|
173
|
-
children.push(h("dt", { class:
|
|
174
|
-
children.push(h("dd", { class:
|
|
205
|
+
children.push(h("dt", { class: SC_CLASSES.label }, label));
|
|
206
|
+
children.push(h("dd", { class: SC_CLASSES.value }, raw(childHtml)));
|
|
175
207
|
}
|
|
176
|
-
const dlAttrs = { class:
|
|
208
|
+
const dlAttrs = { class: SC_CLASSES.object };
|
|
177
209
|
Object.assign(dlAttrs, ariaLabelAttrs(descriptionText));
|
|
178
210
|
return h("dl", dlAttrs, ...children);
|
|
179
211
|
}
|
|
@@ -188,14 +220,14 @@ function renderObjectNode(props) {
|
|
|
188
220
|
const labelContent = [label];
|
|
189
221
|
if (required !== void 0) labelContent.push(required);
|
|
190
222
|
const fieldChildren = [h("label", {
|
|
191
|
-
class:
|
|
223
|
+
class: SC_CLASSES.label,
|
|
192
224
|
for: fieldId
|
|
193
225
|
}, ...labelContent), raw(childHtml)];
|
|
194
226
|
const hint = buildHintElement(fieldId, field.constraints);
|
|
195
227
|
if (hint !== void 0) fieldChildren.push(hint);
|
|
196
|
-
children.push(h("div", { class:
|
|
228
|
+
children.push(h("div", { class: SC_CLASSES.field }, ...fieldChildren));
|
|
197
229
|
}
|
|
198
|
-
const fieldsetAttrs = { class:
|
|
230
|
+
const fieldsetAttrs = { class: SC_CLASSES.object };
|
|
199
231
|
Object.assign(fieldsetAttrs, ariaLabelAttrs(descriptionText));
|
|
200
232
|
return h("fieldset", fieldsetAttrs, ...children);
|
|
201
233
|
}
|
|
@@ -207,124 +239,111 @@ function renderArrayNode(props) {
|
|
|
207
239
|
const element = props.tree.type === "array" ? props.tree.element : void 0;
|
|
208
240
|
if (element === void 0) return "";
|
|
209
241
|
const childHtmls = arr.map((item, i) => props.renderChild(element, item, `[${String(i)}]`));
|
|
210
|
-
if (props.readOnly)
|
|
211
|
-
|
|
242
|
+
if (props.readOnly) {
|
|
243
|
+
const items = childHtmls.map((childHtml) => h("li", { class: "sc-item" }, raw(childHtml)));
|
|
244
|
+
return h("ul", { class: SC_CLASSES.array }, ...items);
|
|
245
|
+
}
|
|
246
|
+
const divItems = childHtmls.map((childHtml) => h("div", {}, raw(childHtml)));
|
|
247
|
+
return h("div", { class: SC_CLASSES.array }, ...divItems);
|
|
212
248
|
}
|
|
213
249
|
function renderRecordHtml(props) {
|
|
214
250
|
return serialize(renderRecordNode(props));
|
|
215
251
|
}
|
|
216
252
|
function renderRecordNode(props) {
|
|
217
253
|
if (props.tree.type !== "record") return "";
|
|
218
|
-
const
|
|
219
|
-
const obj = isRecord(props.value) ? props.value : {};
|
|
254
|
+
const obj = isObject(props.value) ? props.value : {};
|
|
220
255
|
const valueType = props.tree.valueType;
|
|
221
256
|
const attrs = {
|
|
222
|
-
class:
|
|
257
|
+
class: SC_CLASSES.record,
|
|
223
258
|
role: "group"
|
|
224
259
|
};
|
|
225
260
|
if (props.readOnly) {
|
|
226
261
|
const children = [];
|
|
227
262
|
for (const [key, val] of Object.entries(obj)) {
|
|
228
263
|
const childHtml = props.renderChild(valueType, val, key);
|
|
229
|
-
children.push(h("dt", { class:
|
|
230
|
-
children.push(h("dd", { class:
|
|
264
|
+
children.push(h("dt", { class: SC_CLASSES.label }, key));
|
|
265
|
+
children.push(h("dd", { class: SC_CLASSES.value }, raw(childHtml)));
|
|
231
266
|
}
|
|
232
267
|
return h("dl", attrs, ...children);
|
|
233
268
|
}
|
|
234
269
|
const children = [];
|
|
235
270
|
for (const [key, val] of Object.entries(obj)) {
|
|
236
271
|
const childHtml = props.renderChild(valueType, val, key);
|
|
237
|
-
children.push(h("div", { class:
|
|
272
|
+
children.push(h("div", { class: SC_CLASSES.field }, h("label", { class: SC_CLASSES.label }, key), raw(childHtml)));
|
|
238
273
|
}
|
|
239
274
|
return h("div", attrs, ...children);
|
|
240
275
|
}
|
|
241
276
|
function renderLiteralHtml(props) {
|
|
242
|
-
if (props.tree.type !== "literal") return serialize(h("span", { class:
|
|
277
|
+
if (props.tree.type !== "literal") return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
|
|
243
278
|
const values = props.tree.literalValues;
|
|
244
|
-
if (values.length === 0) return serialize(h("span", { class:
|
|
245
|
-
|
|
279
|
+
if (values.length === 0) return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
|
|
280
|
+
const display = values.map((v) => displayJsonValue(v)).join(", ");
|
|
281
|
+
return serialize(h("span", { class: SC_CLASSES.value }, display));
|
|
246
282
|
}
|
|
247
283
|
function renderUnionHtml(props) {
|
|
248
284
|
const options = props.tree.type === "union" || props.tree.type === "discriminatedUnion" ? props.tree.options : void 0;
|
|
249
285
|
if (options === void 0 || options.length === 0) {
|
|
250
|
-
if (props.value === void 0 || props.value === null) return serialize(h("span", { class:
|
|
251
|
-
return serialize(h("span", { class:
|
|
286
|
+
if (props.value === void 0 || props.value === null) return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
|
|
287
|
+
return serialize(h("span", { class: SC_CLASSES.value }, JSON.stringify(props.value)));
|
|
252
288
|
}
|
|
253
289
|
const matched = matchUnionOption(options, props.value);
|
|
254
290
|
if (matched !== void 0) return props.renderChild(matched, props.value);
|
|
255
291
|
const firstOption = options[0];
|
|
256
292
|
if (firstOption !== void 0) return props.renderChild(firstOption, props.value);
|
|
257
|
-
return serialize(h("span", { class:
|
|
293
|
+
return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
|
|
258
294
|
}
|
|
259
295
|
function renderDiscriminatedUnionHtml(props) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (props.value === void 0 || props.value === null) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
|
|
264
|
-
return serialize(h("span", { class: "sc-value" }, JSON.stringify(props.value)));
|
|
296
|
+
if (props.tree.type !== "discriminatedUnion") {
|
|
297
|
+
if (props.value === void 0 || props.value === null) return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
|
|
298
|
+
return serialize(h("span", { class: SC_CLASSES.value }, JSON.stringify(props.value)));
|
|
265
299
|
}
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const optionLabels = options.map((opt) => {
|
|
271
|
-
if (opt.type === "object") {
|
|
272
|
-
const discriminatorField = opt.fields[discKey];
|
|
273
|
-
if (discriminatorField?.type === "literal") {
|
|
274
|
-
const constVal = discriminatorField.literalValues[0];
|
|
275
|
-
if (typeof constVal === "string") return constVal;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
return typeof opt.meta.title === "string" ? opt.meta.title : opt.type;
|
|
279
|
-
});
|
|
280
|
-
let activeIndex = 0;
|
|
281
|
-
if (currentDiscriminatorValue !== void 0) {
|
|
282
|
-
const found = optionLabels.indexOf(currentDiscriminatorValue);
|
|
283
|
-
if (found !== -1) activeIndex = found;
|
|
300
|
+
const { options, discriminator } = props.tree;
|
|
301
|
+
if (options.length === 0) {
|
|
302
|
+
if (props.value === void 0 || props.value === null) return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
|
|
303
|
+
return serialize(h("span", { class: SC_CLASSES.value }, JSON.stringify(props.value)));
|
|
284
304
|
}
|
|
285
|
-
const activeOption = options
|
|
305
|
+
const { optionLabels, activeIndex, activeOption } = resolveDiscriminatedActive(options, discriminator, isObject(props.value) ? props.value : void 0);
|
|
286
306
|
if (props.readOnly) {
|
|
287
307
|
if (activeOption !== void 0) return props.renderChild(activeOption, props.value);
|
|
288
|
-
return serialize(h("span", { class:
|
|
308
|
+
return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
|
|
289
309
|
}
|
|
290
|
-
const
|
|
291
|
-
const
|
|
292
|
-
const tabId = (i) => `${baseId}-tab-${String(i)}`;
|
|
293
|
-
const children = [h("div", {
|
|
294
|
-
role: "tablist",
|
|
295
|
-
class: "sc-tabs",
|
|
296
|
-
"aria-label": "Select variant"
|
|
297
|
-
}, ...options.map((_opt, i) => {
|
|
310
|
+
const tabPanelId = panelId(props.path);
|
|
311
|
+
const tabButtons = options.map((_opt, i) => {
|
|
298
312
|
return h("button", {
|
|
299
313
|
type: "button",
|
|
300
314
|
role: "tab",
|
|
301
|
-
class: i === activeIndex ?
|
|
302
|
-
id: tabId(i),
|
|
315
|
+
class: i === activeIndex ? SC_CLASSES.tabActive : SC_CLASSES.tab,
|
|
316
|
+
id: tabId(props.path, i),
|
|
303
317
|
"aria-selected": i === activeIndex ? "true" : void 0,
|
|
304
|
-
"aria-controls":
|
|
318
|
+
"aria-controls": tabPanelId,
|
|
305
319
|
tabindex: i === activeIndex ? "0" : "-1"
|
|
306
320
|
}, optionLabels[i]);
|
|
307
|
-
})
|
|
321
|
+
});
|
|
322
|
+
const children = [h("div", {
|
|
323
|
+
role: "tablist",
|
|
324
|
+
class: SC_CLASSES.tabs,
|
|
325
|
+
"aria-label": "Select variant"
|
|
326
|
+
}, ...tabButtons)];
|
|
308
327
|
if (activeOption !== void 0) {
|
|
309
328
|
const childHtml = props.renderChild(activeOption, props.value);
|
|
310
329
|
children.push(h("div", {
|
|
311
330
|
role: "tabpanel",
|
|
312
|
-
id:
|
|
313
|
-
"aria-labelledby": tabId(activeIndex)
|
|
331
|
+
id: tabPanelId,
|
|
332
|
+
"aria-labelledby": tabId(props.path, activeIndex)
|
|
314
333
|
}, raw(childHtml)));
|
|
315
334
|
}
|
|
316
|
-
return serialize(h("div", { class:
|
|
335
|
+
return serialize(h("div", { class: SC_CLASSES.discriminatedUnion }, ...children));
|
|
317
336
|
}
|
|
318
337
|
function renderFileHtml(props) {
|
|
319
338
|
const id = fieldId(props.path);
|
|
320
339
|
const accept = props.constraints.mimeTypes?.join(",");
|
|
321
340
|
if (props.readOnly) return serialize(h("span", {
|
|
322
|
-
class:
|
|
341
|
+
class: SC_CLASSES.value,
|
|
323
342
|
id,
|
|
324
343
|
...ariaReadonlyAttrs()
|
|
325
344
|
}, "File field"));
|
|
326
345
|
const attrs = {
|
|
327
|
-
class:
|
|
346
|
+
class: SC_CLASSES.input,
|
|
328
347
|
id,
|
|
329
348
|
type: "file",
|
|
330
349
|
name: id
|
|
@@ -336,15 +355,16 @@ function renderFileHtml(props) {
|
|
|
336
355
|
}
|
|
337
356
|
function renderUnknownHtml(props) {
|
|
338
357
|
if (props.readOnly) {
|
|
339
|
-
if (props.value === void 0 || props.value === null) return serialize(h("span", { class:
|
|
340
|
-
if (typeof props.value === "string") return serialize(h("span", { class:
|
|
341
|
-
return serialize(h("span", { class:
|
|
358
|
+
if (props.value === void 0 || props.value === null) return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
|
|
359
|
+
if (typeof props.value === "string") return serialize(h("span", { class: SC_CLASSES.value }, props.value));
|
|
360
|
+
return serialize(h("span", { class: SC_CLASSES.value }, JSON.stringify(props.value)));
|
|
342
361
|
}
|
|
343
362
|
const strValue = typeof props.value === "string" ? props.value : "";
|
|
363
|
+
const name = props.path;
|
|
344
364
|
const attrs = {
|
|
345
|
-
class:
|
|
365
|
+
class: SC_CLASSES.input,
|
|
346
366
|
type: "text",
|
|
347
|
-
name
|
|
367
|
+
name
|
|
348
368
|
};
|
|
349
369
|
if (!props.writeOnly) attrs.value = strValue;
|
|
350
370
|
return serialize(h("input", attrs));
|
|
@@ -360,26 +380,26 @@ function renderTupleHtml(props) {
|
|
|
360
380
|
const element = prefixItems[i];
|
|
361
381
|
if (element === void 0) continue;
|
|
362
382
|
const childHtml = props.renderChild(element, itemValue, `[${String(i)}]`);
|
|
363
|
-
children.push(h("div", { class:
|
|
383
|
+
children.push(h("div", { class: SC_CLASSES.tupleItem }, h("span", { class: SC_CLASSES.tupleIndex }, String(i)), raw(childHtml)));
|
|
364
384
|
}
|
|
365
385
|
if (restItems !== void 0) for (let i = prefixItems.length; i < arr.length; i++) {
|
|
366
386
|
const itemValue = arr[i];
|
|
367
387
|
const childHtml = props.renderChild(restItems, itemValue, `[${String(i)}]`);
|
|
368
|
-
children.push(h("div", { class:
|
|
388
|
+
children.push(h("div", { class: `${SC_CLASSES.tupleItem} ${SC_CLASSES.tupleRest}` }, h("span", { class: SC_CLASSES.tupleIndex }, String(i)), raw(childHtml)));
|
|
369
389
|
}
|
|
370
|
-
return serialize(h("div", { class:
|
|
390
|
+
return serialize(h("div", { class: SC_CLASSES.tuple }, ...children));
|
|
371
391
|
}
|
|
372
392
|
function renderConditionalHtml(props) {
|
|
373
393
|
const children = [];
|
|
374
394
|
if (props.tree.type === "conditional") {
|
|
375
|
-
children.push(h("div", { class:
|
|
376
|
-
if (props.tree.thenClause !== void 0) children.push(h("div", { class:
|
|
377
|
-
if (props.tree.elseClause !== void 0) children.push(h("div", { class:
|
|
395
|
+
children.push(h("div", { class: SC_CLASSES.conditionalIf }, raw("if: ...")));
|
|
396
|
+
if (props.tree.thenClause !== void 0) children.push(h("div", { class: SC_CLASSES.conditionalThen }, raw("then: ...")));
|
|
397
|
+
if (props.tree.elseClause !== void 0) children.push(h("div", { class: SC_CLASSES.conditionalElse }, raw("else: ...")));
|
|
378
398
|
}
|
|
379
|
-
return serialize(h("div", { class:
|
|
399
|
+
return serialize(h("div", { class: SC_CLASSES.conditional }, ...children));
|
|
380
400
|
}
|
|
381
401
|
function renderNegationHtml(props) {
|
|
382
|
-
return serialize(h("div", { class:
|
|
402
|
+
return serialize(h("div", { class: SC_CLASSES.negation }, raw("not: ...")));
|
|
383
403
|
}
|
|
384
404
|
/**
|
|
385
405
|
* Render a null field — `z.null()` or `{ type: "null" }`.
|
|
@@ -387,9 +407,10 @@ function renderNegationHtml(props) {
|
|
|
387
407
|
* The only valid value is `null`, so render an em-dash placeholder.
|
|
388
408
|
*/
|
|
389
409
|
function renderNullHtml(props) {
|
|
410
|
+
const id = fieldId(props.path);
|
|
390
411
|
return serialize(h("span", {
|
|
391
|
-
class:
|
|
392
|
-
id
|
|
412
|
+
class: SC_CLASSES.valueEmpty,
|
|
413
|
+
id,
|
|
393
414
|
...ariaReadonlyAttrs()
|
|
394
415
|
}, "—"));
|
|
395
416
|
}
|
|
@@ -407,13 +428,6 @@ function renderNeverHtml(props) {
|
|
|
407
428
|
...ariaReadonlyAttrs()
|
|
408
429
|
}, h("em", {}, "never matches")));
|
|
409
430
|
}
|
|
410
|
-
function matchUnionOption(options, value) {
|
|
411
|
-
if (typeof value === "string") return options.find((o) => o.type === "string" || o.type === "enum");
|
|
412
|
-
if (typeof value === "number") return options.find((o) => o.type === "number");
|
|
413
|
-
if (typeof value === "boolean") return options.find((o) => o.type === "boolean");
|
|
414
|
-
if (Array.isArray(value)) return options.find((o) => o.type === "array");
|
|
415
|
-
if (typeof value === "object" && value !== null) return options.find((o) => o.type === "object");
|
|
416
|
-
}
|
|
417
431
|
const defaultHtmlResolver = {
|
|
418
432
|
string: renderStringHtml,
|
|
419
433
|
number: renderNumberHtml,
|
|
@@ -434,4 +448,4 @@ const defaultHtmlResolver = {
|
|
|
434
448
|
unknown: renderUnknownHtml
|
|
435
449
|
};
|
|
436
450
|
//#endregion
|
|
437
|
-
export { dateInputType, defaultHtmlResolver, fieldId, matchUnionOption };
|
|
451
|
+
export { dateInputType, defaultHtmlResolver, fieldId, matchUnionOption, panelId, tabId };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { j as WalkedField } from "../types-
|
|
2
|
-
import { i as DiagnosticsOptions } from "../diagnostics-
|
|
3
|
-
import { o as HtmlResolver } from "../renderer-
|
|
1
|
+
import { j as WalkedField } from "../types-BTB73MB8.mjs";
|
|
2
|
+
import { i as DiagnosticsOptions } from "../diagnostics-Cbwak-ZX.mjs";
|
|
3
|
+
import { o as HtmlResolver } from "../renderer-CXJ8y0qw.mjs";
|
|
4
4
|
import { HtmlElement } from "./html.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/html/streamRenderers.d.ts
|
|
@@ -8,7 +8,6 @@ declare function yieldOpen(el: HtmlElement): string;
|
|
|
8
8
|
declare function yieldClose(el: HtmlElement): string;
|
|
9
9
|
declare function renderLeaf(tree: WalkedField, value: unknown, mergedResolver: HtmlResolver, path: string): string;
|
|
10
10
|
declare function renderFieldSync(tree: WalkedField, value: unknown, mergedResolver: HtmlResolver, path: string, rawResolver: HtmlResolver, currentDepth: number, diagnostics: DiagnosticsOptions | undefined): string;
|
|
11
|
-
declare function matchUnionOption(options: WalkedField[], value: unknown): WalkedField | undefined;
|
|
12
11
|
declare function streamField(tree: WalkedField, value: unknown, mergedResolver: HtmlResolver, path: string, rawResolver: HtmlResolver, currentDepth?: number, diagnostics?: DiagnosticsOptions): Iterable<string, void, undefined>;
|
|
13
12
|
//#endregion
|
|
14
|
-
export {
|
|
13
|
+
export { renderFieldSync, renderLeaf, streamField, yieldClose, yieldOpen };
|