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