schema-components 1.16.3 → 1.18.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/dist/core/adapter.d.mts +1 -1
- package/dist/core/constraints.d.mts +1 -1
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/merge.d.mts +14 -8
- package/dist/core/merge.mjs +81 -12
- package/dist/core/normalise.d.mts +1 -1
- package/dist/core/ref.d.mts +1 -1
- package/dist/core/renderer.d.mts +2 -2
- package/dist/core/renderer.mjs +50 -1
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/typeInference.d.mts +2 -2
- package/dist/core/walkBuilders.d.mts +2 -2
- package/dist/core/walker.mjs +2 -2
- package/dist/{diagnostics-DzbZmcLI.d.mts → diagnostics-BYk63jsC.d.mts} +1 -1
- package/dist/html/a11y.d.mts +13 -2
- package/dist/html/a11y.mjs +26 -2
- package/dist/html/renderToHtml.d.mts +1 -1
- package/dist/html/renderToHtml.mjs +5 -3
- package/dist/html/renderToHtmlStream.d.mts +1 -1
- package/dist/html/renderers.d.mts +4 -3
- package/dist/html/renderers.mjs +9 -13
- package/dist/html/streamRenderers.d.mts +1 -1
- package/dist/openapi/bundle.d.mts +9 -4
- package/dist/openapi/bundle.mjs +73 -15
- package/dist/openapi/components.d.mts +1 -1
- package/dist/openapi/components.mjs +61 -27
- package/dist/openapi/parser.mjs +8 -8
- package/dist/openapi/resolve.d.mts +13 -2
- package/dist/openapi/resolve.mjs +19 -3
- package/dist/react/SchemaComponent.d.mts +35 -7
- package/dist/react/SchemaComponent.mjs +49 -43
- package/dist/react/SchemaView.d.mts +12 -4
- package/dist/react/SchemaView.mjs +24 -49
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +15 -2
- package/dist/react/headlessRenderers.mjs +52 -37
- package/dist/{ref-DvWoULcy.d.mts → ref-Ckt5liZs.d.mts} +1 -1
- package/dist/{renderer-BdSqllx5.d.mts → renderer-DXo-rXHJ.d.mts} +28 -2
- package/dist/themes/mantine.d.mts +6 -1
- package/dist/themes/mantine.mjs +44 -11
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/mui.mjs +23 -8
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/radix.mjs +43 -11
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/themes/shadcn.mjs +28 -10
- package/dist/{typeInference-k7FXfTVO.d.mts → typeInference-5JiqIZ8t.d.mts} +57 -4
- package/package.json +5 -2
package/dist/themes/mantine.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isObject } from "../core/guards.mjs";
|
|
2
|
-
import { toReactNode } from "../react/headlessRenderers.mjs";
|
|
2
|
+
import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
|
|
3
3
|
import { headlessResolver } from "../react/headless.mjs";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
//#region src/themes/mantine.tsx
|
|
@@ -17,9 +17,14 @@ let MantineSwitch = (props) => /* @__PURE__ */ jsx("input", {
|
|
|
17
17
|
});
|
|
18
18
|
let MantineSelect = (props) => /* @__PURE__ */ jsx("select", { ...props });
|
|
19
19
|
let MantineFieldset = (props) => /* @__PURE__ */ jsx("fieldset", { ...props });
|
|
20
|
+
let MantineText = (props) => /* @__PURE__ */ jsx("span", { ...props });
|
|
20
21
|
/**
|
|
21
22
|
* Register real Mantine components for the resolver to use.
|
|
22
23
|
* Call once at app startup before rendering.
|
|
24
|
+
*
|
|
25
|
+
* `Text` is required so read-only scalars render as a styled Mantine
|
|
26
|
+
* `<Text>` element instead of a bare `<span>`, matching the visual
|
|
27
|
+
* weight of the editable variants.
|
|
23
28
|
*/
|
|
24
29
|
function registerMantineComponents(components) {
|
|
25
30
|
MantineTextInput = components.TextInput;
|
|
@@ -27,12 +32,18 @@ function registerMantineComponents(components) {
|
|
|
27
32
|
MantineSwitch = components.Switch;
|
|
28
33
|
MantineSelect = components.Select;
|
|
29
34
|
MantineFieldset = components.Fieldset;
|
|
35
|
+
MantineText = components.Text;
|
|
30
36
|
}
|
|
31
37
|
function renderStringInput(props) {
|
|
32
38
|
const strValue = typeof props.value === "string" ? props.value : "";
|
|
33
39
|
const label = getLabel(props);
|
|
34
|
-
|
|
40
|
+
const id = inputId(props.path);
|
|
41
|
+
if (props.readOnly) return /* @__PURE__ */ jsx(MantineText, {
|
|
42
|
+
id,
|
|
43
|
+
children: strValue || "—"
|
|
44
|
+
});
|
|
35
45
|
return /* @__PURE__ */ jsx(MantineTextInput, {
|
|
46
|
+
id,
|
|
36
47
|
label,
|
|
37
48
|
value: props.writeOnly ? "" : strValue,
|
|
38
49
|
onChange: (e) => {
|
|
@@ -42,11 +53,19 @@ function renderStringInput(props) {
|
|
|
42
53
|
}
|
|
43
54
|
function renderNumberInput(props) {
|
|
44
55
|
const label = getLabel(props);
|
|
56
|
+
const id = inputId(props.path);
|
|
45
57
|
if (props.readOnly) {
|
|
46
|
-
if (typeof props.value !== "number") return /* @__PURE__ */ jsx(
|
|
47
|
-
|
|
58
|
+
if (typeof props.value !== "number") return /* @__PURE__ */ jsx(MantineText, {
|
|
59
|
+
id,
|
|
60
|
+
children: "—"
|
|
61
|
+
});
|
|
62
|
+
return /* @__PURE__ */ jsx(MantineText, {
|
|
63
|
+
id,
|
|
64
|
+
children: props.value.toLocaleString()
|
|
65
|
+
});
|
|
48
66
|
}
|
|
49
67
|
return /* @__PURE__ */ jsx(MantineNumberInput, {
|
|
68
|
+
id,
|
|
50
69
|
label,
|
|
51
70
|
value: props.writeOnly ? void 0 : typeof props.value === "number" ? props.value : void 0,
|
|
52
71
|
onChange: (v) => {
|
|
@@ -56,11 +75,19 @@ function renderNumberInput(props) {
|
|
|
56
75
|
}
|
|
57
76
|
function renderBooleanInput(props) {
|
|
58
77
|
const label = getLabel(props);
|
|
78
|
+
const id = inputId(props.path);
|
|
59
79
|
if (props.readOnly) {
|
|
60
|
-
if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(
|
|
61
|
-
|
|
80
|
+
if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(MantineText, {
|
|
81
|
+
id,
|
|
82
|
+
children: "—"
|
|
83
|
+
});
|
|
84
|
+
return /* @__PURE__ */ jsx(MantineText, {
|
|
85
|
+
id,
|
|
86
|
+
children: props.value ? "Yes" : "No"
|
|
87
|
+
});
|
|
62
88
|
}
|
|
63
89
|
return /* @__PURE__ */ jsx(MantineSwitch, {
|
|
90
|
+
id,
|
|
64
91
|
label,
|
|
65
92
|
checked: props.writeOnly ? false : props.value === true,
|
|
66
93
|
onChange: (e) => {
|
|
@@ -71,22 +98,28 @@ function renderBooleanInput(props) {
|
|
|
71
98
|
function renderEnumInput(props) {
|
|
72
99
|
const enumValue = typeof props.value === "string" ? props.value : "";
|
|
73
100
|
const label = getLabel(props);
|
|
74
|
-
|
|
101
|
+
const id = inputId(props.path);
|
|
102
|
+
if (props.readOnly) return /* @__PURE__ */ jsx(MantineText, {
|
|
103
|
+
id,
|
|
104
|
+
children: enumValue || "—"
|
|
105
|
+
});
|
|
106
|
+
const enumValues = props.tree.type === "enum" ? props.tree.enumValues : [];
|
|
75
107
|
return /* @__PURE__ */ jsx(MantineSelect, {
|
|
108
|
+
id,
|
|
76
109
|
label,
|
|
77
110
|
value: props.writeOnly ? null : enumValue || null,
|
|
78
111
|
onChange: (v) => {
|
|
79
112
|
if (typeof v === "string") props.onChange(v);
|
|
80
113
|
},
|
|
81
|
-
data:
|
|
114
|
+
data: enumValues.map((v) => ({
|
|
82
115
|
value: v,
|
|
83
116
|
label: v
|
|
84
117
|
}))
|
|
85
118
|
});
|
|
86
119
|
}
|
|
87
120
|
function renderObjectContainer(props) {
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
if (props.tree.type !== "object") return null;
|
|
122
|
+
const fields = props.tree.fields;
|
|
90
123
|
const obj = isObject(props.value) ? props.value : {};
|
|
91
124
|
return /* @__PURE__ */ jsx(MantineFieldset, {
|
|
92
125
|
legend: getLabel(props),
|
|
@@ -100,7 +133,7 @@ function renderObjectContainer(props) {
|
|
|
100
133
|
};
|
|
101
134
|
return /* @__PURE__ */ jsx("div", {
|
|
102
135
|
style: { marginBottom: "0.5rem" },
|
|
103
|
-
children: toReactNode(props.renderChild(field, childValue, childOnChange))
|
|
136
|
+
children: toReactNode(props.renderChild(field, childValue, childOnChange, key))
|
|
104
137
|
}, key);
|
|
105
138
|
})
|
|
106
139
|
});
|
package/dist/themes/mui.d.mts
CHANGED
package/dist/themes/mui.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isObject } from "../core/guards.mjs";
|
|
2
|
-
import { toReactNode } from "../react/headlessRenderers.mjs";
|
|
2
|
+
import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
|
|
3
3
|
import { headlessResolver } from "../react/headless.mjs";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
4
|
import { isValidElement } from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
//#region src/themes/mui.tsx
|
|
7
7
|
function ariaRequired(tree) {
|
|
8
8
|
return { required: tree.isOptional === false };
|
|
@@ -10,11 +10,14 @@ function ariaRequired(tree) {
|
|
|
10
10
|
function renderStringInput(props) {
|
|
11
11
|
const strValue = typeof props.value === "string" ? props.value : "";
|
|
12
12
|
const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
|
|
13
|
+
const id = inputId(props.path);
|
|
13
14
|
if (props.readOnly) return /* @__PURE__ */ jsx(MuiTypography, {
|
|
15
|
+
id,
|
|
14
16
|
variant: "body2",
|
|
15
17
|
children: strValue || "—"
|
|
16
18
|
});
|
|
17
19
|
return /* @__PURE__ */ jsx(MuiTextField, {
|
|
20
|
+
id,
|
|
18
21
|
label,
|
|
19
22
|
type: props.constraints.format === "email" ? "email" : props.constraints.format === "uri" ? "url" : "text",
|
|
20
23
|
value: props.writeOnly ? "" : strValue,
|
|
@@ -33,17 +36,21 @@ function renderStringInput(props) {
|
|
|
33
36
|
}
|
|
34
37
|
function renderNumberInput(props) {
|
|
35
38
|
const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
|
|
39
|
+
const id = inputId(props.path);
|
|
36
40
|
if (props.readOnly) {
|
|
37
41
|
if (typeof props.value !== "number") return /* @__PURE__ */ jsx(MuiTypography, {
|
|
42
|
+
id,
|
|
38
43
|
variant: "body2",
|
|
39
44
|
children: "—"
|
|
40
45
|
});
|
|
41
46
|
return /* @__PURE__ */ jsx(MuiTypography, {
|
|
47
|
+
id,
|
|
42
48
|
variant: "body2",
|
|
43
49
|
children: props.value.toLocaleString()
|
|
44
50
|
});
|
|
45
51
|
}
|
|
46
52
|
return /* @__PURE__ */ jsx(MuiTextField, {
|
|
53
|
+
id,
|
|
47
54
|
label,
|
|
48
55
|
type: "number",
|
|
49
56
|
value: props.writeOnly ? "" : typeof props.value === "number" ? props.value : "",
|
|
@@ -62,18 +69,22 @@ function renderNumberInput(props) {
|
|
|
62
69
|
}
|
|
63
70
|
function renderBooleanInput(props) {
|
|
64
71
|
const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
|
|
72
|
+
const id = inputId(props.path);
|
|
65
73
|
if (props.readOnly) {
|
|
66
74
|
if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(MuiTypography, {
|
|
75
|
+
id,
|
|
67
76
|
variant: "body2",
|
|
68
77
|
children: "—"
|
|
69
78
|
});
|
|
70
79
|
return /* @__PURE__ */ jsx(MuiTypography, {
|
|
80
|
+
id,
|
|
71
81
|
variant: "body2",
|
|
72
82
|
children: props.value ? "Yes" : "No"
|
|
73
83
|
});
|
|
74
84
|
}
|
|
75
85
|
return /* @__PURE__ */ jsx(MuiFormControlLabel, {
|
|
76
86
|
control: /* @__PURE__ */ jsx(MuiCheckbox, {
|
|
87
|
+
id,
|
|
77
88
|
checked: props.writeOnly ? false : props.value === true,
|
|
78
89
|
onChange: (e) => {
|
|
79
90
|
props.onChange(e.target.checked);
|
|
@@ -85,11 +96,14 @@ function renderBooleanInput(props) {
|
|
|
85
96
|
function renderEnumInput(props) {
|
|
86
97
|
const enumValue = typeof props.value === "string" ? props.value : "";
|
|
87
98
|
const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
|
|
99
|
+
const id = inputId(props.path);
|
|
88
100
|
if (props.readOnly) return /* @__PURE__ */ jsx(MuiTypography, {
|
|
101
|
+
id,
|
|
89
102
|
variant: "body2",
|
|
90
103
|
children: enumValue || "—"
|
|
91
104
|
});
|
|
92
105
|
return /* @__PURE__ */ jsxs(MuiTextField, {
|
|
106
|
+
id,
|
|
93
107
|
select: true,
|
|
94
108
|
label,
|
|
95
109
|
value: props.writeOnly ? "" : enumValue,
|
|
@@ -103,15 +117,15 @@ function renderEnumInput(props) {
|
|
|
103
117
|
children: [/* @__PURE__ */ jsxs(MuiMenuItem, {
|
|
104
118
|
value: "",
|
|
105
119
|
children: ["Select", "…"]
|
|
106
|
-
}), (props.enumValues
|
|
120
|
+
}), (props.tree.type === "enum" ? props.tree.enumValues : []).map((v) => /* @__PURE__ */ jsx(MuiMenuItem, {
|
|
107
121
|
value: v,
|
|
108
122
|
children: v
|
|
109
123
|
}, v))]
|
|
110
124
|
});
|
|
111
125
|
}
|
|
112
126
|
function renderObjectContainer(props) {
|
|
113
|
-
|
|
114
|
-
|
|
127
|
+
if (props.tree.type !== "object") return null;
|
|
128
|
+
const fields = props.tree.fields;
|
|
115
129
|
const obj = isObject(props.value) ? props.value : {};
|
|
116
130
|
return /* @__PURE__ */ jsxs(MuiBox, {
|
|
117
131
|
sx: {
|
|
@@ -130,13 +144,14 @@ function renderObjectContainer(props) {
|
|
|
130
144
|
updated[key] = v;
|
|
131
145
|
props.onChange(updated);
|
|
132
146
|
};
|
|
133
|
-
return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(field, childValue, childOnChange)) }, key);
|
|
147
|
+
return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(field, childValue, childOnChange, key)) }, key);
|
|
134
148
|
})]
|
|
135
149
|
});
|
|
136
150
|
}
|
|
137
151
|
function renderArrayContainer(props) {
|
|
138
152
|
const arr = Array.isArray(props.value) ? props.value : [];
|
|
139
|
-
|
|
153
|
+
if (props.tree.type !== "array") return null;
|
|
154
|
+
const element = props.tree.element;
|
|
140
155
|
if (element === void 0) return null;
|
|
141
156
|
return /* @__PURE__ */ jsx(MuiBox, {
|
|
142
157
|
sx: {
|
|
@@ -150,7 +165,7 @@ function renderArrayContainer(props) {
|
|
|
150
165
|
next[i] = v;
|
|
151
166
|
props.onChange(next);
|
|
152
167
|
};
|
|
153
|
-
return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange)) }, String(i));
|
|
168
|
+
return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange, `[${String(i)}]`)) }, String(i));
|
|
154
169
|
})
|
|
155
170
|
});
|
|
156
171
|
}
|
package/dist/themes/radix.d.mts
CHANGED
package/dist/themes/radix.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isObject } from "../core/guards.mjs";
|
|
2
|
-
import { toReactNode } from "../react/headlessRenderers.mjs";
|
|
2
|
+
import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
|
|
3
3
|
import { headlessResolver } from "../react/headless.mjs";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/themes/radix.tsx
|
|
@@ -41,13 +41,19 @@ function registerRadixComponents(components) {
|
|
|
41
41
|
function renderStringInput(props) {
|
|
42
42
|
const strValue = typeof props.value === "string" ? props.value : "";
|
|
43
43
|
const label = getLabel(props);
|
|
44
|
-
|
|
44
|
+
const id = inputId(props.path);
|
|
45
|
+
if (props.readOnly) return /* @__PURE__ */ jsx(RadixText, {
|
|
46
|
+
id,
|
|
47
|
+
children: strValue || "—"
|
|
48
|
+
});
|
|
45
49
|
return /* @__PURE__ */ jsxs(RadixBox, { children: [label !== void 0 && /* @__PURE__ */ jsx(RadixText, {
|
|
46
50
|
as: "label",
|
|
47
51
|
size: "2",
|
|
48
52
|
weight: "medium",
|
|
53
|
+
htmlFor: id,
|
|
49
54
|
children: label
|
|
50
55
|
}), /* @__PURE__ */ jsx(RadixTextField, {
|
|
56
|
+
id,
|
|
51
57
|
type: props.constraints.format === "email" ? "email" : props.constraints.format === "uri" ? "url" : "text",
|
|
52
58
|
value: props.writeOnly ? "" : strValue,
|
|
53
59
|
onChange: (e) => {
|
|
@@ -58,16 +64,25 @@ function renderStringInput(props) {
|
|
|
58
64
|
}
|
|
59
65
|
function renderNumberInput(props) {
|
|
60
66
|
const label = getLabel(props);
|
|
67
|
+
const id = inputId(props.path);
|
|
61
68
|
if (props.readOnly) {
|
|
62
|
-
if (typeof props.value !== "number") return /* @__PURE__ */ jsx(RadixText, {
|
|
63
|
-
|
|
69
|
+
if (typeof props.value !== "number") return /* @__PURE__ */ jsx(RadixText, {
|
|
70
|
+
id,
|
|
71
|
+
children: "—"
|
|
72
|
+
});
|
|
73
|
+
return /* @__PURE__ */ jsx(RadixText, {
|
|
74
|
+
id,
|
|
75
|
+
children: props.value.toLocaleString()
|
|
76
|
+
});
|
|
64
77
|
}
|
|
65
78
|
return /* @__PURE__ */ jsxs(RadixBox, { children: [label !== void 0 && /* @__PURE__ */ jsx(RadixText, {
|
|
66
79
|
as: "label",
|
|
67
80
|
size: "2",
|
|
68
81
|
weight: "medium",
|
|
82
|
+
htmlFor: id,
|
|
69
83
|
children: label
|
|
70
84
|
}), /* @__PURE__ */ jsx(RadixTextField, {
|
|
85
|
+
id,
|
|
71
86
|
type: "number",
|
|
72
87
|
value: props.writeOnly ? "" : typeof props.value === "number" ? props.value : "",
|
|
73
88
|
onChange: (e) => {
|
|
@@ -78,20 +93,29 @@ function renderNumberInput(props) {
|
|
|
78
93
|
}
|
|
79
94
|
function renderBooleanInput(props) {
|
|
80
95
|
const label = getLabel(props);
|
|
96
|
+
const id = inputId(props.path);
|
|
81
97
|
if (props.readOnly) {
|
|
82
|
-
if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(RadixText, {
|
|
83
|
-
|
|
98
|
+
if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(RadixText, {
|
|
99
|
+
id,
|
|
100
|
+
children: "—"
|
|
101
|
+
});
|
|
102
|
+
return /* @__PURE__ */ jsx(RadixText, {
|
|
103
|
+
id,
|
|
104
|
+
children: props.value ? "Yes" : "No"
|
|
105
|
+
});
|
|
84
106
|
}
|
|
85
107
|
return /* @__PURE__ */ jsxs(RadixFlex, {
|
|
86
108
|
align: "center",
|
|
87
109
|
gap: "2",
|
|
88
110
|
children: [/* @__PURE__ */ jsx(RadixCheckbox, {
|
|
111
|
+
id,
|
|
89
112
|
checked: props.writeOnly ? false : props.value === true,
|
|
90
113
|
onCheckedChange: (checked) => {
|
|
91
114
|
if (typeof checked === "boolean") props.onChange(checked);
|
|
92
115
|
}
|
|
93
116
|
}), label !== void 0 && /* @__PURE__ */ jsx(RadixText, {
|
|
94
117
|
as: "label",
|
|
118
|
+
htmlFor: id,
|
|
95
119
|
children: label
|
|
96
120
|
})]
|
|
97
121
|
});
|
|
@@ -99,26 +123,34 @@ function renderBooleanInput(props) {
|
|
|
99
123
|
function renderEnumInput(props) {
|
|
100
124
|
const enumValue = typeof props.value === "string" ? props.value : "";
|
|
101
125
|
const label = getLabel(props);
|
|
102
|
-
|
|
126
|
+
const id = inputId(props.path);
|
|
127
|
+
if (props.readOnly) return /* @__PURE__ */ jsx(RadixText, {
|
|
128
|
+
id,
|
|
129
|
+
children: enumValue || "—"
|
|
130
|
+
});
|
|
103
131
|
return /* @__PURE__ */ jsxs(RadixBox, { children: [label !== void 0 && /* @__PURE__ */ jsx(RadixText, {
|
|
104
132
|
as: "label",
|
|
105
133
|
size: "2",
|
|
106
134
|
weight: "medium",
|
|
135
|
+
htmlFor: id,
|
|
107
136
|
children: label
|
|
108
137
|
}), /* @__PURE__ */ jsxs(RadixSelectRoot, {
|
|
109
138
|
value: props.writeOnly ? "" : enumValue,
|
|
110
139
|
onValueChange: (value) => {
|
|
111
140
|
props.onChange(value);
|
|
112
141
|
},
|
|
113
|
-
children: [/* @__PURE__ */ jsx(RadixSelectTrigger, {
|
|
142
|
+
children: [/* @__PURE__ */ jsx(RadixSelectTrigger, {
|
|
143
|
+
id,
|
|
144
|
+
mt: "1"
|
|
145
|
+
}), /* @__PURE__ */ jsx(RadixSelectContent, { children: (props.tree.type === "enum" ? props.tree.enumValues : []).map((value) => /* @__PURE__ */ jsx(RadixSelectItem, {
|
|
114
146
|
value,
|
|
115
147
|
children: value
|
|
116
148
|
}, value)) })]
|
|
117
149
|
})] });
|
|
118
150
|
}
|
|
119
151
|
function renderObjectContainer(props) {
|
|
120
|
-
|
|
121
|
-
|
|
152
|
+
if (props.tree.type !== "object") return null;
|
|
153
|
+
const fields = props.tree.fields;
|
|
122
154
|
const obj = isObject(props.value) ? props.value : {};
|
|
123
155
|
return /* @__PURE__ */ jsxs(RadixBox, { children: [typeof props.meta.description === "string" && /* @__PURE__ */ jsx(RadixText, {
|
|
124
156
|
as: "div",
|
|
@@ -137,7 +169,7 @@ function renderObjectContainer(props) {
|
|
|
137
169
|
updated[key] = v;
|
|
138
170
|
props.onChange(updated);
|
|
139
171
|
};
|
|
140
|
-
return /* @__PURE__ */ jsx(RadixBox, { children: toReactNode(props.renderChild(field, childValue, childOnChange)) }, key);
|
|
172
|
+
return /* @__PURE__ */ jsx(RadixBox, { children: toReactNode(props.renderChild(field, childValue, childOnChange, key)) }, key);
|
|
141
173
|
})
|
|
142
174
|
})] });
|
|
143
175
|
}
|
package/dist/themes/shadcn.d.mts
CHANGED
package/dist/themes/shadcn.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { toRecord } from "../core/guards.mjs";
|
|
2
|
-
import { toReactNode } from "../react/headlessRenderers.mjs";
|
|
2
|
+
import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
|
|
3
3
|
import { headlessResolver } from "../react/headless.mjs";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/themes/shadcn.tsx
|
|
@@ -11,12 +11,15 @@ function buildClassNames(...classes) {
|
|
|
11
11
|
}
|
|
12
12
|
function renderStringInput(props) {
|
|
13
13
|
const strValue = typeof props.value === "string" ? props.value : "";
|
|
14
|
+
const id = inputId(props.path);
|
|
14
15
|
const className = buildClassNames("flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors", "file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground", "placeholder:text-muted-foreground", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
|
|
15
16
|
if (props.readOnly) return /* @__PURE__ */ jsx("span", {
|
|
17
|
+
id,
|
|
16
18
|
className: "text-sm",
|
|
17
19
|
children: strValue || "—"
|
|
18
20
|
});
|
|
19
21
|
if (props.writeOnly) return /* @__PURE__ */ jsx("input", {
|
|
22
|
+
id,
|
|
20
23
|
type: props.constraints.format === "email" ? "email" : "text",
|
|
21
24
|
className,
|
|
22
25
|
placeholder: typeof props.meta.description === "string" ? props.meta.description : void 0,
|
|
@@ -26,6 +29,7 @@ function renderStringInput(props) {
|
|
|
26
29
|
}
|
|
27
30
|
});
|
|
28
31
|
return /* @__PURE__ */ jsx("input", {
|
|
32
|
+
id,
|
|
29
33
|
type: props.constraints.format === "email" ? "email" : "text",
|
|
30
34
|
className,
|
|
31
35
|
value: strValue,
|
|
@@ -38,18 +42,22 @@ function renderStringInput(props) {
|
|
|
38
42
|
});
|
|
39
43
|
}
|
|
40
44
|
function renderNumberInput(props) {
|
|
45
|
+
const id = inputId(props.path);
|
|
41
46
|
const className = buildClassNames("flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors", "placeholder:text-muted-foreground", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
|
|
42
47
|
if (props.readOnly) {
|
|
43
48
|
if (typeof props.value !== "number") return /* @__PURE__ */ jsx("span", {
|
|
49
|
+
id,
|
|
44
50
|
className: "text-sm",
|
|
45
51
|
children: "—"
|
|
46
52
|
});
|
|
47
53
|
return /* @__PURE__ */ jsx("span", {
|
|
54
|
+
id,
|
|
48
55
|
className: "text-sm",
|
|
49
56
|
children: props.value.toLocaleString()
|
|
50
57
|
});
|
|
51
58
|
}
|
|
52
59
|
return /* @__PURE__ */ jsx("input", {
|
|
60
|
+
id,
|
|
53
61
|
type: "number",
|
|
54
62
|
className,
|
|
55
63
|
value: props.writeOnly ? "" : typeof props.value === "number" ? props.value : "",
|
|
@@ -61,18 +69,22 @@ function renderNumberInput(props) {
|
|
|
61
69
|
});
|
|
62
70
|
}
|
|
63
71
|
function renderBooleanInput(props) {
|
|
72
|
+
const id = inputId(props.path);
|
|
64
73
|
const className = buildClassNames("h-4 w-4 rounded border border-primary shadow", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
|
|
65
74
|
if (props.readOnly) {
|
|
66
75
|
if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx("span", {
|
|
76
|
+
id,
|
|
67
77
|
className: "text-sm",
|
|
68
78
|
children: "—"
|
|
69
79
|
});
|
|
70
80
|
return /* @__PURE__ */ jsx("span", {
|
|
81
|
+
id,
|
|
71
82
|
className: "text-sm",
|
|
72
83
|
children: props.value ? "Yes" : "No"
|
|
73
84
|
});
|
|
74
85
|
}
|
|
75
86
|
return /* @__PURE__ */ jsx("input", {
|
|
87
|
+
id,
|
|
76
88
|
type: "checkbox",
|
|
77
89
|
className,
|
|
78
90
|
checked: props.writeOnly ? false : props.value === true,
|
|
@@ -82,8 +94,8 @@ function renderBooleanInput(props) {
|
|
|
82
94
|
});
|
|
83
95
|
}
|
|
84
96
|
function renderObjectContainer(props) {
|
|
85
|
-
|
|
86
|
-
|
|
97
|
+
if (props.tree.type !== "object") return null;
|
|
98
|
+
const fields = props.tree.fields;
|
|
87
99
|
const obj = typeof props.value === "object" && props.value !== null && !Array.isArray(props.value) ? props.value : {};
|
|
88
100
|
return /* @__PURE__ */ jsxs("div", {
|
|
89
101
|
className: "space-y-4",
|
|
@@ -92,6 +104,7 @@ function renderObjectContainer(props) {
|
|
|
92
104
|
children: props.meta.description
|
|
93
105
|
}), Object.entries(fields).map(([key, field]) => {
|
|
94
106
|
const childValue = toRecord(obj)[key];
|
|
107
|
+
const childId = inputId(`${props.path}.${key}`);
|
|
95
108
|
const childOnChange = (v) => {
|
|
96
109
|
const updated = {};
|
|
97
110
|
for (const [k, val] of Object.entries(obj)) updated[k] = val;
|
|
@@ -101,16 +114,18 @@ function renderObjectContainer(props) {
|
|
|
101
114
|
return /* @__PURE__ */ jsxs("div", {
|
|
102
115
|
className: "space-y-1",
|
|
103
116
|
children: [/* @__PURE__ */ jsx("label", {
|
|
117
|
+
htmlFor: childId,
|
|
104
118
|
className: "text-sm font-medium leading-none",
|
|
105
119
|
children: field.meta.description ?? key
|
|
106
|
-
}), toReactNode(props.renderChild(field, childValue, childOnChange))]
|
|
120
|
+
}), toReactNode(props.renderChild(field, childValue, childOnChange, key))]
|
|
107
121
|
}, key);
|
|
108
122
|
})]
|
|
109
123
|
});
|
|
110
124
|
}
|
|
111
125
|
function renderArrayContainer(props) {
|
|
112
126
|
const arr = Array.isArray(props.value) ? props.value : [];
|
|
113
|
-
|
|
127
|
+
if (props.tree.type !== "array") return null;
|
|
128
|
+
const element = props.tree.element;
|
|
114
129
|
if (element === void 0) return null;
|
|
115
130
|
return /* @__PURE__ */ jsx("div", {
|
|
116
131
|
className: "space-y-2",
|
|
@@ -120,33 +135,36 @@ function renderArrayContainer(props) {
|
|
|
120
135
|
next[i] = v;
|
|
121
136
|
props.onChange(next);
|
|
122
137
|
};
|
|
123
|
-
return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange)) }, String(i));
|
|
138
|
+
return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange, `[${String(i)}]`)) }, String(i));
|
|
124
139
|
})
|
|
125
140
|
});
|
|
126
141
|
}
|
|
127
142
|
function renderEnumInput(props) {
|
|
143
|
+
const id = inputId(props.path);
|
|
128
144
|
const className = buildClassNames("flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm", "focus:outline-none focus:ring-1 focus:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
|
|
129
145
|
const enumValue = typeof props.value === "string" ? props.value : "";
|
|
130
146
|
if (props.readOnly) return /* @__PURE__ */ jsx("span", {
|
|
147
|
+
id,
|
|
131
148
|
className: "text-sm",
|
|
132
149
|
children: enumValue || "—"
|
|
133
150
|
});
|
|
134
151
|
return /* @__PURE__ */ jsxs("select", {
|
|
152
|
+
id,
|
|
135
153
|
className,
|
|
136
154
|
value: props.writeOnly ? "" : enumValue,
|
|
137
155
|
onChange: (e) => {
|
|
138
156
|
props.onChange(e.target.value);
|
|
139
157
|
},
|
|
140
|
-
children: [/* @__PURE__ */
|
|
158
|
+
children: [/* @__PURE__ */ jsxs("option", {
|
|
141
159
|
value: "",
|
|
142
|
-
children: "Select
|
|
143
|
-
}), props.enumValues
|
|
160
|
+
children: ["Select", "…"]
|
|
161
|
+
}), props.tree.type === "enum" ? props.tree.enumValues.map((v) => {
|
|
144
162
|
const display = v === null ? "null" : typeof v === "string" ? v : String(v);
|
|
145
163
|
return /* @__PURE__ */ jsx("option", {
|
|
146
164
|
value: display,
|
|
147
165
|
children: display
|
|
148
166
|
}, display);
|
|
149
|
-
})]
|
|
167
|
+
}) : null]
|
|
150
168
|
});
|
|
151
169
|
}
|
|
152
170
|
function buildResolver() {
|
|
@@ -84,26 +84,71 @@ type UnsafeFields = Record<string, FieldOverride> & {
|
|
|
84
84
|
* Returns the original type when the schema is non-recursive.
|
|
85
85
|
*/
|
|
86
86
|
type DetectRecursiveFallback<T> = unknown extends T ? __SchemaInferenceFellBack : T;
|
|
87
|
+
/**
|
|
88
|
+
* Type-level recursion bound for $ref resolution.
|
|
89
|
+
*
|
|
90
|
+
* The TypeScript type system imposes its own recursion limit; without an
|
|
91
|
+
* explicit bound a cyclic schema graph would exhaust it and degrade to
|
|
92
|
+
* `any`/`unknown` silently. Ten levels is the runtime walker's parallel
|
|
93
|
+
* — see `countDistinctRefs` in `ref.ts` (lines 52-55), which derives its
|
|
94
|
+
* bound from the number of distinct `$ref` strings in the document.
|
|
95
|
+
*
|
|
96
|
+
* A fixed bound is used here rather than a derived one because the type
|
|
97
|
+
* system has no way to count distinct strings across a recursive `Defs`
|
|
98
|
+
* map without itself recursing — which is the problem the bound exists
|
|
99
|
+
* to solve. Ten covers every realistic schema graph encountered in
|
|
100
|
+
* practice; deeper graphs surface as `__SchemaInferenceFellBack` so
|
|
101
|
+
* consumers can detect the limit explicitly.
|
|
102
|
+
*/
|
|
103
|
+
type DEFAULT_MAX_DEPTH = 10;
|
|
87
104
|
/**
|
|
88
105
|
* Resolve a $ref against the local definitions context.
|
|
106
|
+
*
|
|
107
|
+
* SOURCE-OF-TRUTH: mirrors runtime `resolveRef` in
|
|
108
|
+
* `packages/core/src/core/ref.ts` (line 90). Any change to the runtime
|
|
109
|
+
* ref-resolution rules (new ref forms, different cycle handling) must be
|
|
110
|
+
* reflected here and pinned in
|
|
111
|
+
* `packages/core/tests/typeInference-walker-parity.test.ts`.
|
|
112
|
+
*
|
|
89
113
|
* Supports:
|
|
90
114
|
* - `#` (root)
|
|
91
115
|
* - `#/$defs/Name` and `#/definitions/Name` (named definitions)
|
|
92
116
|
* - `#SomeName` ($anchor, $dynamicAnchor resolved from definitions)
|
|
93
117
|
*/
|
|
94
|
-
type ResolveSchemaRef<R extends string, Defs extends Record<string, unknown>, Depth extends number = 0> = Depth extends
|
|
118
|
+
type ResolveSchemaRef<R extends string, Defs extends Record<string, unknown>, Depth extends number = 0> = Depth extends DEFAULT_MAX_DEPTH ? __SchemaInferenceFellBack : R extends "#" ? unknown : R extends `#/$defs/${infer Name}` ? Name extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[Name], Defs>> : unknown : R extends `#/definitions/${infer Name}` ? Name extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[Name], Defs>> : unknown : R extends `#${infer AnchorName}` ? AnchorName extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[AnchorName], Defs>> : unknown : unknown;
|
|
95
119
|
/**
|
|
96
120
|
* Merge an allOf array into an intersection type.
|
|
97
121
|
*/
|
|
98
122
|
type AllOfToType<A, Defs extends Record<string, unknown>> = A extends readonly unknown[] ? UnionToIntersection<FromJSONSchema<A[number], Defs>> : unknown;
|
|
99
123
|
/**
|
|
100
124
|
* Convert an anyOf/oneOf array into a union type.
|
|
101
|
-
*
|
|
102
|
-
*
|
|
125
|
+
*
|
|
126
|
+
* SOURCE-OF-TRUTH: mirrors runtime `walkUnion` (and the
|
|
127
|
+
* `walkDiscriminatedUnion` fast path) in
|
|
128
|
+
* `packages/core/src/core/walker.ts` (lines 723-752), together with
|
|
129
|
+
* `detectDiscriminated` and `normaliseAnyOf` in
|
|
130
|
+
* `packages/core/src/core/merge.ts` (lines 190-260).
|
|
131
|
+
*
|
|
132
|
+
* Deliberate divergence: the walker collapses qualifying `oneOf` members
|
|
133
|
+
* into a `discriminatedUnion` field at runtime. The type-level helper
|
|
134
|
+
* produces a plain TypeScript union because a discriminated union and a
|
|
135
|
+
* plain union over the same members are structurally indistinguishable
|
|
136
|
+
* at the type level. Parity is pinned in
|
|
137
|
+
* `packages/core/tests/typeInference-walker-parity.test.ts`.
|
|
138
|
+
*
|
|
139
|
+
* Filters out `{ type: "null" }` members and instead makes the result
|
|
140
|
+
* nullable when at least one null member is present — mirrors the
|
|
141
|
+
* walker's `normaliseAnyOf`.
|
|
103
142
|
*/
|
|
104
143
|
type UnionOfMembers<A, Defs extends Record<string, unknown>> = A extends readonly unknown[] ? HasNullMember<A> extends true ? Exclude<FromJSONSchema<A[number], Defs>, null> | null : FromJSONSchema<A[number], Defs> : unknown;
|
|
105
144
|
/**
|
|
106
145
|
* Check whether an anyOf/oneOf array contains a `{ type: "null" }` member.
|
|
146
|
+
*
|
|
147
|
+
* SOURCE-OF-TRUTH: mirrors runtime `normaliseAnyOf` in
|
|
148
|
+
* `packages/core/src/core/merge.ts` (lines 190-209). Both implementations
|
|
149
|
+
* only recognise schema-shaped null members (`{ type: "null" }`); a bare
|
|
150
|
+
* `null` literal in the array is treated as non-nullable. Parity is
|
|
151
|
+
* pinned in `packages/core/tests/typeInference-walker-parity.test.ts`.
|
|
107
152
|
*/
|
|
108
153
|
type HasNullMember<A> = A extends readonly unknown[] ? null extends A[number] ? false : {
|
|
109
154
|
type: "null";
|
|
@@ -214,6 +259,14 @@ type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) exten
|
|
|
214
259
|
/**
|
|
215
260
|
* Resolves an OpenAPI `ref` string to its JSON Schema, then parses it.
|
|
216
261
|
*
|
|
262
|
+
* SOURCE-OF-TRUTH: mirrors runtime `resolveRef` in
|
|
263
|
+
* `packages/core/src/core/ref.ts` (line 90), which is invoked by the
|
|
264
|
+
* walker entry point in `packages/core/src/core/walker.ts` (lines
|
|
265
|
+
* 144-154) for OpenAPI documents. Any change to the runtime ref-resolution
|
|
266
|
+
* rules (new ref forms, different cycle handling, JSON Pointer decoding)
|
|
267
|
+
* must be reflected here and pinned in
|
|
268
|
+
* `packages/core/tests/typeInference-walker-parity.test.ts`.
|
|
269
|
+
*
|
|
217
270
|
* Handles:
|
|
218
271
|
* - `#/components/schemas/Name` (OpenAPI 3.x)
|
|
219
272
|
* - `#/definitions/Name` (Swagger 2.0)
|
|
@@ -332,4 +385,4 @@ type PathOfType<T, Prefix extends string = ""> = IsNarrowObject<T> extends true
|
|
|
332
385
|
*/
|
|
333
386
|
type TypeAtPath<T, P extends string> = P extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? TypeAtPath<T[Key], Rest> : unknown : P extends keyof T ? T[P] : unknown;
|
|
334
387
|
//#endregion
|
|
335
|
-
export {
|
|
388
|
+
export { InferResponseFields as a, PathOfType as c, UnsafeFields as d, __SchemaInferenceFellBack as f, InferRequestBodyFields as i, ResolveOpenAPIRef as l, FromJSONSchema as n, OpenAPIRequestBodyType as o, InferParameterOverrides as r, OpenAPIResponseType as s, DEFAULT_MAX_DEPTH as t, TypeAtPath as u };
|