@lssm/lib.design-system 0.0.0-canary-20251217062139 → 0.0.0-canary-20251217072406
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/components/molecules/Breadcrumbs.d.ts +2 -2
- package/dist/components/molecules/CommandSearchTrigger.d.ts +2 -2
- package/dist/contracts/dist/client/react/drivers/shadcn.js +7 -3
- package/dist/contracts/dist/client/react/form-render.js +238 -201
- package/dist/contracts/dist/forms.js +74 -64
- package/dist/renderers/form-contract.js +6 -6
- package/package.json +7 -7
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React$1 from "react";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime43 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/components/molecules/Breadcrumbs.d.ts
|
|
5
5
|
interface BreadcrumbItemDef {
|
|
@@ -10,6 +10,6 @@ declare function Breadcrumbs({
|
|
|
10
10
|
items
|
|
11
11
|
}: {
|
|
12
12
|
items: BreadcrumbItemDef[];
|
|
13
|
-
}):
|
|
13
|
+
}): react_jsx_runtime43.JSX.Element | null;
|
|
14
14
|
//#endregion
|
|
15
15
|
export { Breadcrumbs };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CommandPalette } from "./CommandPalette.js";
|
|
2
2
|
import * as React$1 from "react";
|
|
3
|
-
import * as
|
|
3
|
+
import * as react_jsx_runtime44 from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/components/molecules/CommandSearchTrigger.d.ts
|
|
6
6
|
declare function CommandSearchTrigger({
|
|
@@ -13,6 +13,6 @@ declare function CommandSearchTrigger({
|
|
|
13
13
|
className?: string;
|
|
14
14
|
placeholder?: string;
|
|
15
15
|
compact?: boolean;
|
|
16
|
-
}):
|
|
16
|
+
}): react_jsx_runtime44.JSX.Element;
|
|
17
17
|
//#endregion
|
|
18
18
|
export { CommandSearchTrigger };
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
//#region ../contracts/dist/client/react/drivers/shadcn.js
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Create a shadcn/ui driver by mapping required slots to components.
|
|
4
|
+
* Host apps should import their shadcn primitives and pass them here.
|
|
5
|
+
*/
|
|
6
|
+
function shadcnDriver(slots) {
|
|
7
|
+
return slots;
|
|
4
8
|
}
|
|
5
9
|
|
|
6
10
|
//#endregion
|
|
7
|
-
export {
|
|
11
|
+
export { shadcnDriver };
|
|
@@ -1,261 +1,298 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildZodWithRelations, evalPredicate } from "../../forms.js";
|
|
2
2
|
import React, { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
|
4
4
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
|
|
7
7
|
//#region ../contracts/dist/client/react/form-render.js
|
|
8
|
-
function
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
function toOptionsArray(src) {
|
|
9
|
+
if (!src) return void 0;
|
|
10
|
+
if (Array.isArray(src)) return {
|
|
11
|
+
kind: "static",
|
|
12
|
+
options: src
|
|
13
|
+
};
|
|
14
|
+
return src;
|
|
13
15
|
}
|
|
14
|
-
function
|
|
15
|
-
if (!
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
function getAtPath(values, path) {
|
|
17
|
+
if (!path) return void 0;
|
|
18
|
+
const segs = path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
|
|
19
|
+
let cur = values;
|
|
20
|
+
for (const s of segs) {
|
|
21
|
+
if (cur == null) return void 0;
|
|
22
|
+
cur = cur[s];
|
|
20
23
|
}
|
|
21
|
-
return
|
|
24
|
+
return cur;
|
|
22
25
|
}
|
|
23
|
-
function
|
|
24
|
-
if (!
|
|
26
|
+
function makeDepsKey(values, deps) {
|
|
27
|
+
if (!deps || deps.length === 0) return "[]";
|
|
25
28
|
try {
|
|
26
|
-
return JSON.stringify(
|
|
29
|
+
return JSON.stringify(deps.map((d) => getAtPath(values, d)));
|
|
27
30
|
} catch {
|
|
28
|
-
return
|
|
31
|
+
return "[]";
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
let
|
|
35
|
-
|
|
36
|
-
if (!
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
if (!
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
})(), () => {
|
|
43
|
-
r$1 = !1;
|
|
34
|
+
function useResolvedOptions(values, source, resolvers) {
|
|
35
|
+
const [opts, setOpts] = useState([]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
let mounted = true;
|
|
38
|
+
const run = async () => {
|
|
39
|
+
if (!source) return setOpts([]);
|
|
40
|
+
if (source.kind === "static") return setOpts([...source.options ?? []]);
|
|
41
|
+
const fn = resolvers?.[source.resolverKey];
|
|
42
|
+
if (!fn) return setOpts([]);
|
|
43
|
+
const res = await fn(values, source.args);
|
|
44
|
+
if (mounted) setOpts([...res ?? []]);
|
|
44
45
|
};
|
|
45
|
-
|
|
46
|
+
run();
|
|
47
|
+
return () => {
|
|
48
|
+
mounted = false;
|
|
49
|
+
};
|
|
50
|
+
}, [useMemo(() => {
|
|
51
|
+
if (!source) return "nil";
|
|
52
|
+
if (source.kind === "static") return JSON.stringify(source.options ?? []);
|
|
53
|
+
return makeDepsKey(values, source.deps);
|
|
54
|
+
}, [source, values]), source && source.resolverKey]);
|
|
55
|
+
return opts;
|
|
46
56
|
}
|
|
47
|
-
function
|
|
48
|
-
if (!
|
|
49
|
-
|
|
50
|
-
return
|
|
57
|
+
function fieldPath(parent, name, arrayIndex) {
|
|
58
|
+
if (!name) return parent ?? "";
|
|
59
|
+
const child = typeof arrayIndex === "number" ? `${name.replace(/^\$index$/, String(arrayIndex))}` : name;
|
|
60
|
+
return parent ? `${parent}${typeof arrayIndex === "number" ? `.${arrayIndex}` : ""}.${child}`.replace(/\.+/g, ".") : child;
|
|
51
61
|
}
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
62
|
+
function createFormRenderer(base) {
|
|
63
|
+
const conf = base;
|
|
64
|
+
const { driver } = conf;
|
|
65
|
+
function InternalForm(props) {
|
|
66
|
+
const { spec, options, merged } = props;
|
|
67
|
+
const baseZod = useMemo(() => buildZodWithRelations(spec), [spec]);
|
|
68
|
+
const form = useForm({
|
|
69
|
+
...merged.formOptions,
|
|
70
|
+
resolver: zodResolver(baseZod),
|
|
71
|
+
defaultValues: options?.defaultValues
|
|
72
|
+
});
|
|
73
|
+
const values = form.watch();
|
|
74
|
+
const renderOne = (f, parent, arrayIndex) => {
|
|
75
|
+
const DriverField = driver.Field;
|
|
76
|
+
const DriverLabel = driver.FieldLabel;
|
|
77
|
+
const DriverDesc = driver.FieldDescription;
|
|
78
|
+
const DriverError = driver.FieldError;
|
|
79
|
+
const name = fieldPath(parent, f.name, arrayIndex);
|
|
80
|
+
const visible = evalPredicate(values, f.visibleWhen);
|
|
81
|
+
const enabled = evalPredicate(values, f.enabledWhen);
|
|
82
|
+
const invalid = Boolean(form.getFieldState(name)?.invalid);
|
|
83
|
+
if (!visible) return null;
|
|
84
|
+
const id = name?.replace(/\./g, "-");
|
|
85
|
+
const commonWrapProps = {
|
|
86
|
+
"data-invalid": invalid,
|
|
87
|
+
hidden: !visible,
|
|
88
|
+
disabled: !enabled
|
|
89
|
+
};
|
|
90
|
+
const labelNode = f.labelI18n ? /* @__PURE__ */ jsx(DriverLabel, {
|
|
91
|
+
htmlFor: id,
|
|
92
|
+
children: f.labelI18n
|
|
93
|
+
}) : null;
|
|
94
|
+
const descNode = f.descriptionI18n ? /* @__PURE__ */ jsx(DriverDesc, { children: f.descriptionI18n }) : null;
|
|
95
|
+
if (f.kind === "group") {
|
|
96
|
+
const children = f.fields.map((c, i) => /* @__PURE__ */ jsx(React.Fragment, { children: renderOne(c, name, arrayIndex) }, `${name}-${i}`));
|
|
97
|
+
return /* @__PURE__ */ jsxs(DriverField, {
|
|
98
|
+
...commonWrapProps,
|
|
74
99
|
children: [
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
100
|
+
labelNode,
|
|
101
|
+
children,
|
|
102
|
+
descNode
|
|
78
103
|
]
|
|
79
104
|
});
|
|
80
105
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
106
|
+
if (f.kind === "array") return renderArray(f, parent);
|
|
107
|
+
return /* @__PURE__ */ jsx(Controller, {
|
|
108
|
+
name,
|
|
109
|
+
control: form.control,
|
|
110
|
+
render: ({ field, fieldState }) => {
|
|
111
|
+
const err = fieldState.error ? [fieldState.error] : [];
|
|
112
|
+
const ariaInvalid = fieldState.invalid || void 0;
|
|
113
|
+
if (f.kind === "text") {
|
|
114
|
+
const Input = driver.Input;
|
|
115
|
+
return /* @__PURE__ */ jsxs(DriverField, {
|
|
116
|
+
...commonWrapProps,
|
|
90
117
|
children: [
|
|
91
|
-
|
|
92
|
-
jsx(
|
|
93
|
-
id
|
|
94
|
-
"aria-invalid":
|
|
95
|
-
placeholder:
|
|
96
|
-
autoComplete:
|
|
97
|
-
inputMode:
|
|
98
|
-
maxLength:
|
|
99
|
-
minLength:
|
|
100
|
-
disabled: !
|
|
101
|
-
...
|
|
102
|
-
...
|
|
103
|
-
keyboard:
|
|
104
|
-
autoComplete:
|
|
118
|
+
labelNode,
|
|
119
|
+
/* @__PURE__ */ jsx(Input, {
|
|
120
|
+
id,
|
|
121
|
+
"aria-invalid": ariaInvalid,
|
|
122
|
+
placeholder: f.placeholderI18n,
|
|
123
|
+
autoComplete: f.autoComplete,
|
|
124
|
+
inputMode: f.inputMode,
|
|
125
|
+
maxLength: f.maxLength,
|
|
126
|
+
minLength: f.minLength,
|
|
127
|
+
disabled: !enabled,
|
|
128
|
+
...field,
|
|
129
|
+
...f.uiProps,
|
|
130
|
+
keyboard: f.keyboard,
|
|
131
|
+
autoComplete: f.keyboard?.autoComplete ?? f.autoComplete
|
|
105
132
|
}),
|
|
106
|
-
|
|
107
|
-
|
|
133
|
+
descNode,
|
|
134
|
+
fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
|
|
108
135
|
]
|
|
109
136
|
});
|
|
110
137
|
}
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
return jsxs(
|
|
114
|
-
...
|
|
138
|
+
if (f.kind === "textarea") {
|
|
139
|
+
const Textarea = driver.Textarea;
|
|
140
|
+
return /* @__PURE__ */ jsxs(DriverField, {
|
|
141
|
+
...commonWrapProps,
|
|
115
142
|
children: [
|
|
116
|
-
|
|
117
|
-
jsx(
|
|
118
|
-
id
|
|
119
|
-
"aria-invalid":
|
|
120
|
-
placeholder:
|
|
121
|
-
rows:
|
|
122
|
-
maxLength:
|
|
123
|
-
disabled: !
|
|
124
|
-
...
|
|
125
|
-
...
|
|
126
|
-
keyboard:
|
|
127
|
-
autoComplete:
|
|
143
|
+
labelNode,
|
|
144
|
+
/* @__PURE__ */ jsx(Textarea, {
|
|
145
|
+
id,
|
|
146
|
+
"aria-invalid": ariaInvalid,
|
|
147
|
+
placeholder: f.placeholderI18n,
|
|
148
|
+
rows: f.rows,
|
|
149
|
+
maxLength: f.maxLength,
|
|
150
|
+
disabled: !enabled,
|
|
151
|
+
...field,
|
|
152
|
+
...f.uiProps,
|
|
153
|
+
keyboard: f.keyboard,
|
|
154
|
+
autoComplete: f.keyboard?.autoComplete ?? f.autoComplete
|
|
128
155
|
}),
|
|
129
|
-
|
|
130
|
-
|
|
156
|
+
descNode,
|
|
157
|
+
fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
|
|
131
158
|
]
|
|
132
159
|
});
|
|
133
160
|
}
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
161
|
+
if (f.kind === "select") {
|
|
162
|
+
const Select = driver.Select;
|
|
163
|
+
const opts = useResolvedOptions(values, toOptionsArray(f.options), merged.resolvers);
|
|
164
|
+
return /* @__PURE__ */ jsxs(DriverField, {
|
|
165
|
+
...commonWrapProps,
|
|
138
166
|
children: [
|
|
139
|
-
|
|
140
|
-
jsx(
|
|
141
|
-
id
|
|
142
|
-
name
|
|
143
|
-
"aria-invalid":
|
|
144
|
-
disabled: !
|
|
145
|
-
value:
|
|
146
|
-
onChange: (
|
|
147
|
-
options:
|
|
148
|
-
...
|
|
167
|
+
labelNode,
|
|
168
|
+
/* @__PURE__ */ jsx(Select, {
|
|
169
|
+
id,
|
|
170
|
+
name,
|
|
171
|
+
"aria-invalid": ariaInvalid,
|
|
172
|
+
disabled: !enabled,
|
|
173
|
+
value: field.value,
|
|
174
|
+
onChange: (v) => field.onChange(v),
|
|
175
|
+
options: opts,
|
|
176
|
+
...f.uiProps
|
|
149
177
|
}),
|
|
150
|
-
|
|
151
|
-
|
|
178
|
+
descNode,
|
|
179
|
+
fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
|
|
152
180
|
]
|
|
153
181
|
});
|
|
154
182
|
}
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
return jsxs(
|
|
158
|
-
...
|
|
183
|
+
if (f.kind === "checkbox") {
|
|
184
|
+
const Checkbox = driver.Checkbox;
|
|
185
|
+
return /* @__PURE__ */ jsxs(DriverField, {
|
|
186
|
+
...commonWrapProps,
|
|
159
187
|
children: [
|
|
160
|
-
|
|
161
|
-
jsx(
|
|
162
|
-
id
|
|
163
|
-
name
|
|
164
|
-
disabled: !
|
|
165
|
-
checked: !!
|
|
166
|
-
onCheckedChange: (
|
|
167
|
-
...
|
|
188
|
+
labelNode,
|
|
189
|
+
/* @__PURE__ */ jsx(Checkbox, {
|
|
190
|
+
id,
|
|
191
|
+
name,
|
|
192
|
+
disabled: !enabled,
|
|
193
|
+
checked: !!field.value,
|
|
194
|
+
onCheckedChange: (v) => field.onChange(v),
|
|
195
|
+
...f.uiProps
|
|
168
196
|
}),
|
|
169
|
-
|
|
170
|
-
|
|
197
|
+
descNode,
|
|
198
|
+
fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
|
|
171
199
|
]
|
|
172
200
|
});
|
|
173
201
|
}
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
202
|
+
if (f.kind === "radio") {
|
|
203
|
+
const RadioGroup = driver.RadioGroup;
|
|
204
|
+
const opts = useResolvedOptions(values, toOptionsArray(f.options), merged.resolvers);
|
|
205
|
+
return /* @__PURE__ */ jsxs(DriverField, {
|
|
206
|
+
...commonWrapProps,
|
|
178
207
|
children: [
|
|
179
|
-
|
|
180
|
-
jsx(
|
|
181
|
-
id
|
|
182
|
-
name
|
|
183
|
-
disabled: !
|
|
184
|
-
value:
|
|
185
|
-
onValueChange: (
|
|
186
|
-
options:
|
|
187
|
-
...
|
|
208
|
+
labelNode,
|
|
209
|
+
/* @__PURE__ */ jsx(RadioGroup, {
|
|
210
|
+
id,
|
|
211
|
+
name,
|
|
212
|
+
disabled: !enabled,
|
|
213
|
+
value: field.value,
|
|
214
|
+
onValueChange: (v) => field.onChange(v),
|
|
215
|
+
options: opts,
|
|
216
|
+
...f.uiProps
|
|
188
217
|
}),
|
|
189
|
-
|
|
190
|
-
|
|
218
|
+
descNode,
|
|
219
|
+
fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
|
|
191
220
|
]
|
|
192
221
|
});
|
|
193
222
|
}
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
return jsxs(
|
|
197
|
-
...
|
|
223
|
+
if (f.kind === "switch") {
|
|
224
|
+
const Switch = driver.Switch;
|
|
225
|
+
return /* @__PURE__ */ jsxs(DriverField, {
|
|
226
|
+
...commonWrapProps,
|
|
198
227
|
children: [
|
|
199
|
-
|
|
200
|
-
jsx(
|
|
201
|
-
id
|
|
202
|
-
name
|
|
203
|
-
disabled: !
|
|
204
|
-
checked: !!
|
|
205
|
-
onCheckedChange: (
|
|
206
|
-
...
|
|
228
|
+
labelNode,
|
|
229
|
+
/* @__PURE__ */ jsx(Switch, {
|
|
230
|
+
id,
|
|
231
|
+
name,
|
|
232
|
+
disabled: !enabled,
|
|
233
|
+
checked: !!field.value,
|
|
234
|
+
onCheckedChange: (v) => field.onChange(v),
|
|
235
|
+
...f.uiProps
|
|
207
236
|
}),
|
|
208
|
-
|
|
209
|
-
|
|
237
|
+
descNode,
|
|
238
|
+
fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
|
|
210
239
|
]
|
|
211
240
|
});
|
|
212
241
|
}
|
|
213
|
-
return jsx(Fragment, {});
|
|
242
|
+
return /* @__PURE__ */ jsx(Fragment, {});
|
|
214
243
|
}
|
|
215
|
-
},
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
244
|
+
}, name);
|
|
245
|
+
};
|
|
246
|
+
const renderArray = (f, parent) => {
|
|
247
|
+
const name = fieldPath(parent, f.name);
|
|
248
|
+
const { fields, append, remove } = useFieldArray({
|
|
249
|
+
control: form.control,
|
|
250
|
+
name
|
|
251
|
+
});
|
|
252
|
+
const canAdd = f.max == null || fields.length < f.max;
|
|
253
|
+
const canRemove = (idx) => (f.min == null ? fields.length > 0 : fields.length > f.min) && idx >= 0;
|
|
254
|
+
const Button$1 = driver.Button;
|
|
255
|
+
const Label = driver.FieldLabel;
|
|
256
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
257
|
+
f.labelI18n ? /* @__PURE__ */ jsx(Label, { children: f.labelI18n }) : null,
|
|
258
|
+
fields.map((row, idx) => /* @__PURE__ */ jsxs("div", { children: [renderOne(f.of, name, idx), canRemove(idx) ? /* @__PURE__ */ jsx(Button$1, {
|
|
259
|
+
type: "button",
|
|
260
|
+
variant: "ghost",
|
|
261
|
+
size: "sm",
|
|
262
|
+
onClick: () => remove(idx),
|
|
263
|
+
children: "Remove"
|
|
264
|
+
}) : null] }, row.id ?? idx)),
|
|
265
|
+
canAdd ? /* @__PURE__ */ jsx(Button$1, {
|
|
266
|
+
type: "button",
|
|
267
|
+
variant: "outline",
|
|
268
|
+
size: "sm",
|
|
269
|
+
onClick: () => append({}),
|
|
270
|
+
children: "Add"
|
|
236
271
|
}) : null
|
|
237
|
-
] },
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
272
|
+
] }, name);
|
|
273
|
+
};
|
|
274
|
+
const onSubmit = async (data) => {
|
|
275
|
+
const actionKey = spec.actions?.[0]?.key ?? "submit";
|
|
276
|
+
if (merged.onSubmitOverride) return merged.onSubmitOverride(data, actionKey);
|
|
277
|
+
};
|
|
278
|
+
const Button = driver.Button;
|
|
279
|
+
return /* @__PURE__ */ jsxs("form", {
|
|
280
|
+
onSubmit: form.handleSubmit(onSubmit),
|
|
281
|
+
children: [(spec.fields || []).map((f, i) => /* @__PURE__ */ jsx(React.Fragment, { children: renderOne(f) }, i)), spec.actions && spec.actions.length ? /* @__PURE__ */ jsx("div", { children: spec.actions.map((a) => /* @__PURE__ */ jsx(Button, {
|
|
282
|
+
type: "submit",
|
|
283
|
+
children: a.labelI18n
|
|
284
|
+
}, a.key)) }) : null]
|
|
248
285
|
});
|
|
249
286
|
}
|
|
250
|
-
return { render: (
|
|
251
|
-
spec
|
|
252
|
-
options
|
|
287
|
+
return { render: (spec, options) => /* @__PURE__ */ jsx(InternalForm, {
|
|
288
|
+
spec,
|
|
289
|
+
options,
|
|
253
290
|
merged: {
|
|
254
|
-
...
|
|
255
|
-
...
|
|
291
|
+
...conf,
|
|
292
|
+
...options?.overrides ?? {}
|
|
256
293
|
}
|
|
257
294
|
}) };
|
|
258
295
|
}
|
|
259
296
|
|
|
260
297
|
//#endregion
|
|
261
|
-
export {
|
|
298
|
+
export { createFormRenderer };
|
|
@@ -1,78 +1,88 @@
|
|
|
1
1
|
//#region ../contracts/dist/forms.js
|
|
2
|
-
function
|
|
3
|
-
if (!
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
function getAtPath(values, path) {
|
|
3
|
+
if (!path) return void 0;
|
|
4
|
+
const segs = path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
|
|
5
|
+
let cur = values;
|
|
6
|
+
for (const s of segs) {
|
|
7
|
+
if (cur == null) return void 0;
|
|
8
|
+
cur = cur[s];
|
|
8
9
|
}
|
|
9
|
-
return
|
|
10
|
+
return cur;
|
|
10
11
|
}
|
|
11
|
-
function
|
|
12
|
-
if (!
|
|
13
|
-
if (
|
|
14
|
-
if (
|
|
15
|
-
if (
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
case
|
|
21
|
-
case
|
|
22
|
-
case
|
|
23
|
-
case
|
|
24
|
-
case
|
|
25
|
-
case
|
|
26
|
-
case
|
|
27
|
-
case
|
|
28
|
-
case
|
|
29
|
-
case
|
|
30
|
-
case
|
|
31
|
-
case
|
|
32
|
-
case
|
|
33
|
-
|
|
12
|
+
function evalPredicate(values, pred) {
|
|
13
|
+
if (!pred) return true;
|
|
14
|
+
if (pred.not) return !evalPredicate(values, pred.not);
|
|
15
|
+
if (pred.all && pred.all.length) return pred.all.every((p) => evalPredicate(values, p));
|
|
16
|
+
if (pred.any && pred.any.length) return pred.any.some((p) => evalPredicate(values, p));
|
|
17
|
+
if (pred.when) {
|
|
18
|
+
const { path, op = "truthy", value } = pred.when;
|
|
19
|
+
const v = getAtPath(values, path);
|
|
20
|
+
switch (op) {
|
|
21
|
+
case "equals": return v === value;
|
|
22
|
+
case "notEquals": return v !== value;
|
|
23
|
+
case "in": return Array.isArray(value) && value.includes(v);
|
|
24
|
+
case "notIn": return Array.isArray(value) && !value.includes(v);
|
|
25
|
+
case "gt": return Number(v) > Number(value);
|
|
26
|
+
case "gte": return Number(v) >= Number(value);
|
|
27
|
+
case "lt": return Number(v) < Number(value);
|
|
28
|
+
case "lte": return Number(v) <= Number(value);
|
|
29
|
+
case "empty": return v == null || (Array.isArray(v) ? v.length === 0 : String(v).length === 0);
|
|
30
|
+
case "lengthGt": return (Array.isArray(v) || typeof v === "string") && v.length > Number(value ?? 0);
|
|
31
|
+
case "lengthGte": return (Array.isArray(v) || typeof v === "string") && v.length >= Number(value ?? 0);
|
|
32
|
+
case "lengthLt": return (Array.isArray(v) || typeof v === "string") && v.length < Number(value ?? 0);
|
|
33
|
+
case "lengthLte": return (Array.isArray(v) || typeof v === "string") && v.length <= Number(value ?? 0);
|
|
34
|
+
case "truthy":
|
|
35
|
+
default: return Boolean(v);
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
|
-
return
|
|
38
|
+
return true;
|
|
37
39
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Wrap the base zod schema with relation-driven refinements (requiredWhen, array min/max)
|
|
42
|
+
* and optional custom constraints. Call this when wiring RHF resolver.
|
|
43
|
+
*/
|
|
44
|
+
function buildZodWithRelations(spec, handlers) {
|
|
45
|
+
return spec.model.getZod().superRefine((values, ctx) => {
|
|
46
|
+
const visit = (field, parentPath) => {
|
|
47
|
+
const path = field.name ? parentPath ? `${parentPath}.${field.name}` : field.name : parentPath ?? "";
|
|
48
|
+
if (field.requiredWhen) {
|
|
49
|
+
if (evalPredicate(values, field.requiredWhen)) {
|
|
50
|
+
const v = getAtPath(values, path);
|
|
51
|
+
if (v == null || typeof v === "string" && v.trim().length === 0 || Array.isArray(v) && v.length === 0) ctx.addIssue({
|
|
52
|
+
code: "custom",
|
|
53
|
+
path: path.split("."),
|
|
54
|
+
message: "required"
|
|
55
|
+
});
|
|
56
|
+
}
|
|
49
57
|
}
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
code:
|
|
54
|
-
path:
|
|
55
|
-
message: `min:${
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
if (field.kind === "array") {
|
|
59
|
+
const arr = getAtPath(values, path);
|
|
60
|
+
if (field.min != null && Array.isArray(arr) && arr.length < field.min) ctx.addIssue({
|
|
61
|
+
code: "custom",
|
|
62
|
+
path: path.split("."),
|
|
63
|
+
message: `min:${field.min}`
|
|
64
|
+
});
|
|
65
|
+
if (field.max != null && Array.isArray(arr) && arr.length > field.max) ctx.addIssue({
|
|
66
|
+
code: "custom",
|
|
67
|
+
path: path.split("."),
|
|
68
|
+
message: `max:${field.max}`
|
|
69
|
+
});
|
|
70
|
+
visit(field.of, path);
|
|
71
|
+
} else if (field.kind === "group") for (const child of field.fields) visit(child, path);
|
|
62
72
|
};
|
|
63
|
-
for (
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
if (!
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
code:
|
|
70
|
-
path: (
|
|
71
|
-
message:
|
|
73
|
+
for (const f of spec.fields) visit(f);
|
|
74
|
+
if (spec.constraints && handlers) for (const c of spec.constraints) {
|
|
75
|
+
const fn = handlers[c.key];
|
|
76
|
+
if (!fn) continue;
|
|
77
|
+
const res = fn(values, c.paths, c.args);
|
|
78
|
+
if (!res.ok) ctx.addIssue({
|
|
79
|
+
code: "custom",
|
|
80
|
+
path: (res.path ?? c.paths[0] ?? "").split(".").filter(Boolean),
|
|
81
|
+
message: res.message ?? c.messageI18n
|
|
72
82
|
});
|
|
73
83
|
}
|
|
74
84
|
});
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
//#endregion
|
|
78
|
-
export {
|
|
88
|
+
export { buildZodWithRelations, evalPredicate };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { createFormRenderer } from "../contracts/dist/client/react/form-render.js";
|
|
4
|
+
import { shadcnDriver } from "../contracts/dist/client/react/drivers/shadcn.js";
|
|
5
5
|
import { Label } from "../ui-kit-web/dist/ui/label.js";
|
|
6
6
|
import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from "../ui-kit-web/dist/ui/field.js";
|
|
7
7
|
import { Button } from "../components/atoms/Button.js";
|
|
@@ -19,7 +19,7 @@ const Select$1 = (props) => {
|
|
|
19
19
|
const { options, value, onChange, ...rest } = props;
|
|
20
20
|
return /* @__PURE__ */ jsxs(Select, {
|
|
21
21
|
value: value ?? "",
|
|
22
|
-
onValueChange: (v
|
|
22
|
+
onValueChange: (v) => onChange?.(v),
|
|
23
23
|
...rest,
|
|
24
24
|
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
25
25
|
className: "w-[180px]",
|
|
@@ -33,7 +33,7 @@ const Select$1 = (props) => {
|
|
|
33
33
|
};
|
|
34
34
|
const Checkbox$1 = (props) => /* @__PURE__ */ jsx(Checkbox, {
|
|
35
35
|
checked: !!props.checked,
|
|
36
|
-
onCheckedChange: (v
|
|
36
|
+
onCheckedChange: (v) => props.onCheckedChange?.(v),
|
|
37
37
|
...props
|
|
38
38
|
});
|
|
39
39
|
const RadioGroup$1 = (props) => /* @__PURE__ */ jsx(RadioGroup, {
|
|
@@ -51,10 +51,10 @@ const RadioGroup$1 = (props) => /* @__PURE__ */ jsx(RadioGroup, {
|
|
|
51
51
|
});
|
|
52
52
|
const Switch$1 = (props) => /* @__PURE__ */ jsx(Switch, {
|
|
53
53
|
checked: !!props.checked,
|
|
54
|
-
onCheckedChange: (v
|
|
54
|
+
onCheckedChange: (v) => props.onCheckedChange?.(v),
|
|
55
55
|
...props
|
|
56
56
|
});
|
|
57
|
-
const formRenderer =
|
|
57
|
+
const formRenderer = createFormRenderer({ driver: shadcnDriver({
|
|
58
58
|
Field,
|
|
59
59
|
FieldLabel,
|
|
60
60
|
FieldDescription,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lssm/lib.design-system",
|
|
3
|
-
"version": "0.0.0-canary-
|
|
3
|
+
"version": "0.0.0-canary-20251217072406",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
|
|
6
6
|
"publish:pkg:canary": "bun publish:pkg --tag canary",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"tree-shake": true,
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@hookform/resolvers": "5.2.2",
|
|
24
|
-
"@lssm/lib.ai-agent": "0.0.0-canary-
|
|
25
|
-
"@lssm/lib.contracts": "0.0.0-canary-
|
|
26
|
-
"@lssm/lib.ui-kit": "0.0.0-canary-
|
|
27
|
-
"@lssm/lib.ui-kit-web": "0.0.0-canary-
|
|
24
|
+
"@lssm/lib.ai-agent": "0.0.0-canary-20251217072406",
|
|
25
|
+
"@lssm/lib.contracts": "0.0.0-canary-20251217072406",
|
|
26
|
+
"@lssm/lib.ui-kit": "0.0.0-canary-20251217072406",
|
|
27
|
+
"@lssm/lib.ui-kit-web": "0.0.0-canary-20251217072406",
|
|
28
28
|
"class-variance-authority": "^0.7.1",
|
|
29
29
|
"clsx": "^2.1.1",
|
|
30
30
|
"lucide-react": "^0.535.0",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"zod": "^4.1.13"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@lssm/tool.typescript": "0.0.0-canary-
|
|
39
|
-
"@lssm/tool.tsdown": "0.0.0-canary-
|
|
38
|
+
"@lssm/tool.typescript": "0.0.0-canary-20251217072406",
|
|
39
|
+
"@lssm/tool.tsdown": "0.0.0-canary-20251217072406",
|
|
40
40
|
"@types/node": "^24.9.0",
|
|
41
41
|
"@types/react-dom": "^19.0.14",
|
|
42
42
|
"postcss": "^8.5",
|