@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.
@@ -1,5 +1,5 @@
1
1
  import * as React$1 from "react";
2
- import * as react_jsx_runtime44 from "react/jsx-runtime";
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
- }): react_jsx_runtime44.JSX.Element | null;
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 react_jsx_runtime43 from "react/jsx-runtime";
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
- }): react_jsx_runtime43.JSX.Element;
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
- function e(e$1) {
3
- return e$1;
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 { e };
11
+ export { shadcnDriver };
@@ -1,261 +1,298 @@
1
- import { i, r } from "../../forms.js";
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 p(e) {
9
- if (e) return Array.isArray(e) ? {
10
- kind: `static`,
11
- options: e
12
- } : e;
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 m(e, t) {
15
- if (!t) return;
16
- let n = t.replace(/\[(\d+)\]/g, `.$1`).split(`.`).filter(Boolean), r$1 = e;
17
- for (let e$1 of n) {
18
- if (r$1 == null) return;
19
- r$1 = r$1[e$1];
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 r$1;
24
+ return cur;
22
25
  }
23
- function h(e, t) {
24
- if (!t || t.length === 0) return `[]`;
26
+ function makeDepsKey(values, deps) {
27
+ if (!deps || deps.length === 0) return "[]";
25
28
  try {
26
- return JSON.stringify(t.map((t$1) => m(e, t$1)));
29
+ return JSON.stringify(deps.map((d) => getAtPath(values, d)));
27
30
  } catch {
28
- return `[]`;
31
+ return "[]";
29
32
  }
30
33
  }
31
- function g(e, t, n) {
32
- let [o, s] = useState([]);
33
- return useEffect(() => {
34
- let r$1 = !0;
35
- return (async () => {
36
- if (!t) return s([]);
37
- if (t.kind === `static`) return s([...t.options ?? []]);
38
- let i$1 = n?.[t.resolverKey];
39
- if (!i$1) return s([]);
40
- let a = await i$1(e, t.args);
41
- r$1 && s([...a ?? []]);
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
- }, [useMemo(() => t ? t.kind === `static` ? JSON.stringify(t.options ?? []) : h(e, t.deps) : `nil`, [t, e]), t && t.resolverKey]), o;
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 _(e, t, n) {
48
- if (!t) return e ?? ``;
49
- let r$1 = typeof n == `number` ? `${t.replace(/^\$index$/, String(n))}` : t;
50
- return e ? `${e}${typeof n == `number` ? `.${n}` : ``}.${r$1}`.replace(/\.+/g, `.`) : r$1;
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 v(r$1) {
53
- let a = r$1, { driver: m$1 } = a;
54
- function h$1(r$2) {
55
- let { spec: a$1, options: h$2, merged: v$1 } = r$2, y = useMemo(() => i(a$1), [a$1]), b = useForm({
56
- ...v$1.formOptions,
57
- resolver: zodResolver(y),
58
- defaultValues: h$2?.defaultValues
59
- }), x = b.watch(), S = (e, r$3, i$1) => {
60
- let a$2 = m$1.Field, s = m$1.FieldLabel, c = m$1.FieldDescription, l = m$1.FieldError, h$3 = _(r$3, e.name, i$1), y$1 = r(x, e.visibleWhen), w$1 = r(x, e.enabledWhen), T$1 = !!b.getFieldState(h$3)?.invalid;
61
- if (!y$1) return null;
62
- let E = h$3?.replace(/\./g, `-`), D = {
63
- "data-invalid": T$1,
64
- hidden: !y$1,
65
- disabled: !w$1
66
- }, O = e.labelI18n ? jsx(s, {
67
- htmlFor: E,
68
- children: e.labelI18n
69
- }) : null, k = e.descriptionI18n ? jsx(c, { children: e.descriptionI18n }) : null;
70
- if (e.kind === `group`) {
71
- let t = e.fields.map((e$1, t$1) => jsx(React.Fragment, { children: S(e$1, h$3, i$1) }, `${h$3}-${t$1}`));
72
- return jsxs(a$2, {
73
- ...D,
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
- O,
76
- t,
77
- k
100
+ labelNode,
101
+ children,
102
+ descNode
78
103
  ]
79
104
  });
80
105
  }
81
- return e.kind === `array` ? C(e, r$3) : jsx(Controller, {
82
- name: h$3,
83
- control: b.control,
84
- render: ({ field: t, fieldState: n }) => {
85
- let r$4 = n.error ? [n.error] : [], i$2 = n.invalid || void 0;
86
- if (e.kind === `text`) {
87
- let o = m$1.Input;
88
- return jsxs(a$2, {
89
- ...D,
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
- O,
92
- jsx(o, {
93
- id: E,
94
- "aria-invalid": i$2,
95
- placeholder: e.placeholderI18n,
96
- autoComplete: e.autoComplete,
97
- inputMode: e.inputMode,
98
- maxLength: e.maxLength,
99
- minLength: e.minLength,
100
- disabled: !w$1,
101
- ...t,
102
- ...e.uiProps,
103
- keyboard: e.keyboard,
104
- autoComplete: e.keyboard?.autoComplete ?? e.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
- k,
107
- n.invalid ? jsx(l, { errors: r$4 }) : null
133
+ descNode,
134
+ fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
108
135
  ]
109
136
  });
110
137
  }
111
- if (e.kind === `textarea`) {
112
- let o = m$1.Textarea;
113
- return jsxs(a$2, {
114
- ...D,
138
+ if (f.kind === "textarea") {
139
+ const Textarea = driver.Textarea;
140
+ return /* @__PURE__ */ jsxs(DriverField, {
141
+ ...commonWrapProps,
115
142
  children: [
116
- O,
117
- jsx(o, {
118
- id: E,
119
- "aria-invalid": i$2,
120
- placeholder: e.placeholderI18n,
121
- rows: e.rows,
122
- maxLength: e.maxLength,
123
- disabled: !w$1,
124
- ...t,
125
- ...e.uiProps,
126
- keyboard: e.keyboard,
127
- autoComplete: e.keyboard?.autoComplete ?? e.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
- k,
130
- n.invalid ? jsx(l, { errors: r$4 }) : null
156
+ descNode,
157
+ fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
131
158
  ]
132
159
  });
133
160
  }
134
- if (e.kind === `select`) {
135
- let o = m$1.Select, s$1 = g(x, p(e.options), v$1.resolvers);
136
- return jsxs(a$2, {
137
- ...D,
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
- O,
140
- jsx(o, {
141
- id: E,
142
- name: h$3,
143
- "aria-invalid": i$2,
144
- disabled: !w$1,
145
- value: t.value,
146
- onChange: (e$1) => t.onChange(e$1),
147
- options: s$1,
148
- ...e.uiProps
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
- k,
151
- n.invalid ? jsx(l, { errors: r$4 }) : null
178
+ descNode,
179
+ fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
152
180
  ]
153
181
  });
154
182
  }
155
- if (e.kind === `checkbox`) {
156
- let i$3 = m$1.Checkbox;
157
- return jsxs(a$2, {
158
- ...D,
183
+ if (f.kind === "checkbox") {
184
+ const Checkbox = driver.Checkbox;
185
+ return /* @__PURE__ */ jsxs(DriverField, {
186
+ ...commonWrapProps,
159
187
  children: [
160
- O,
161
- jsx(i$3, {
162
- id: E,
163
- name: h$3,
164
- disabled: !w$1,
165
- checked: !!t.value,
166
- onCheckedChange: (e$1) => t.onChange(e$1),
167
- ...e.uiProps
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
- k,
170
- n.invalid ? jsx(l, { errors: r$4 }) : null
197
+ descNode,
198
+ fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
171
199
  ]
172
200
  });
173
201
  }
174
- if (e.kind === `radio`) {
175
- let i$3 = m$1.RadioGroup, o = g(x, p(e.options), v$1.resolvers);
176
- return jsxs(a$2, {
177
- ...D,
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
- O,
180
- jsx(i$3, {
181
- id: E,
182
- name: h$3,
183
- disabled: !w$1,
184
- value: t.value,
185
- onValueChange: (e$1) => t.onChange(e$1),
186
- options: o,
187
- ...e.uiProps
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
- k,
190
- n.invalid ? jsx(l, { errors: r$4 }) : null
218
+ descNode,
219
+ fieldState.invalid ? /* @__PURE__ */ jsx(DriverError, { errors: err }) : null
191
220
  ]
192
221
  });
193
222
  }
194
- if (e.kind === `switch`) {
195
- let i$3 = m$1.Switch;
196
- return jsxs(a$2, {
197
- ...D,
223
+ if (f.kind === "switch") {
224
+ const Switch = driver.Switch;
225
+ return /* @__PURE__ */ jsxs(DriverField, {
226
+ ...commonWrapProps,
198
227
  children: [
199
- O,
200
- jsx(i$3, {
201
- id: E,
202
- name: h$3,
203
- disabled: !w$1,
204
- checked: !!t.value,
205
- onCheckedChange: (e$1) => t.onChange(e$1),
206
- ...e.uiProps
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
- k,
209
- n.invalid ? jsx(l, { errors: r$4 }) : null
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
- }, h$3);
216
- }, C = (e, t) => {
217
- let n = _(t, e.name), { fields: r$3, append: i$1, remove: a$2 } = useFieldArray({
218
- control: b.control,
219
- name: n
220
- }), o = e.max == null || r$3.length < e.max, c = (t$1) => (e.min == null ? r$3.length > 0 : r$3.length > e.min) && t$1 >= 0, l = m$1.Button, u = m$1.FieldLabel;
221
- return jsxs(`div`, { children: [
222
- e.labelI18n ? jsx(u, { children: e.labelI18n }) : null,
223
- r$3.map((t$1, r$4) => jsxs(`div`, { children: [S(e.of, n, r$4), c(r$4) ? jsx(l, {
224
- type: `button`,
225
- variant: `ghost`,
226
- size: `sm`,
227
- onClick: () => a$2(r$4),
228
- children: `Remove`
229
- }) : null] }, t$1.id ?? r$4)),
230
- o ? jsx(l, {
231
- type: `button`,
232
- variant: `outline`,
233
- size: `sm`,
234
- onClick: () => i$1({}),
235
- children: `Add`
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
- ] }, n);
238
- }, w = async (e) => {
239
- let t = a$1.actions?.[0]?.key ?? `submit`;
240
- if (v$1.onSubmitOverride) return v$1.onSubmitOverride(e, t);
241
- }, T = m$1.Button;
242
- return jsxs(`form`, {
243
- onSubmit: b.handleSubmit(w),
244
- children: [(a$1.fields || []).map((e, t) => jsx(React.Fragment, { children: S(e) }, t)), a$1.actions && a$1.actions.length ? jsx(`div`, { children: a$1.actions.map((e) => jsx(T, {
245
- type: `submit`,
246
- children: e.labelI18n
247
- }, e.key)) }) : null]
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: (e, t) => jsx(h$1, {
251
- spec: e,
252
- options: t,
287
+ return { render: (spec, options) => /* @__PURE__ */ jsx(InternalForm, {
288
+ spec,
289
+ options,
253
290
  merged: {
254
- ...a,
255
- ...t?.overrides ?? {}
291
+ ...conf,
292
+ ...options?.overrides ?? {}
256
293
  }
257
294
  }) };
258
295
  }
259
296
 
260
297
  //#endregion
261
- export { v };
298
+ export { createFormRenderer };
@@ -1,78 +1,88 @@
1
1
  //#region ../contracts/dist/forms.js
2
- function n(e, t) {
3
- if (!t) return;
4
- let n$1 = t.replace(/\[(\d+)\]/g, `.$1`).split(`.`).filter(Boolean), r$1 = e;
5
- for (let e$1 of n$1) {
6
- if (r$1 == null) return;
7
- r$1 = r$1[e$1];
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 r$1;
10
+ return cur;
10
11
  }
11
- function r(e, t) {
12
- if (!t) return !0;
13
- if (t.not) return !r(e, t.not);
14
- if (t.all && t.all.length) return t.all.every((t$1) => r(e, t$1));
15
- if (t.any && t.any.length) return t.any.some((t$1) => r(e, t$1));
16
- if (t.when) {
17
- let { path: r$1, op: i$1 = `truthy`, value: a } = t.when, o = n(e, r$1);
18
- switch (i$1) {
19
- case `equals`: return o === a;
20
- case `notEquals`: return o !== a;
21
- case `in`: return Array.isArray(a) && a.includes(o);
22
- case `notIn`: return Array.isArray(a) && !a.includes(o);
23
- case `gt`: return Number(o) > Number(a);
24
- case `gte`: return Number(o) >= Number(a);
25
- case `lt`: return Number(o) < Number(a);
26
- case `lte`: return Number(o) <= Number(a);
27
- case `empty`: return o == null || (Array.isArray(o) ? o.length === 0 : String(o).length === 0);
28
- case `lengthGt`: return (Array.isArray(o) || typeof o == `string`) && o.length > Number(a ?? 0);
29
- case `lengthGte`: return (Array.isArray(o) || typeof o == `string`) && o.length >= Number(a ?? 0);
30
- case `lengthLt`: return (Array.isArray(o) || typeof o == `string`) && o.length < Number(a ?? 0);
31
- case `lengthLte`: return (Array.isArray(o) || typeof o == `string`) && o.length <= Number(a ?? 0);
32
- case `truthy`:
33
- default: return !!o;
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 !0;
38
+ return true;
37
39
  }
38
- function i(e, t) {
39
- return e.model.getZod().superRefine((i$1, a) => {
40
- let o = (e$1, t$1) => {
41
- let s = e$1.name ? t$1 ? `${t$1}.${e$1.name}` : e$1.name : t$1 ?? ``;
42
- if (e$1.requiredWhen && r(i$1, e$1.requiredWhen)) {
43
- let e$2 = n(i$1, s);
44
- (e$2 == null || typeof e$2 == `string` && e$2.trim().length === 0 || Array.isArray(e$2) && e$2.length === 0) && a.addIssue({
45
- code: `custom`,
46
- path: s.split(`.`),
47
- message: `required`
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 (e$1.kind === `array`) {
51
- let t$2 = n(i$1, s);
52
- e$1.min != null && Array.isArray(t$2) && t$2.length < e$1.min && a.addIssue({
53
- code: `custom`,
54
- path: s.split(`.`),
55
- message: `min:${e$1.min}`
56
- }), e$1.max != null && Array.isArray(t$2) && t$2.length > e$1.max && a.addIssue({
57
- code: `custom`,
58
- path: s.split(`.`),
59
- message: `max:${e$1.max}`
60
- }), o(e$1.of, s);
61
- } else if (e$1.kind === `group`) for (let t$2 of e$1.fields) o(t$2, s);
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 (let t$1 of e.fields) o(t$1);
64
- if (e.constraints && t) for (let n$1 of e.constraints) {
65
- let e$1 = t[n$1.key];
66
- if (!e$1) continue;
67
- let r$1 = e$1(i$1, n$1.paths, n$1.args);
68
- r$1.ok || a.addIssue({
69
- code: `custom`,
70
- path: (r$1.path ?? n$1.paths[0] ?? ``).split(`.`).filter(Boolean),
71
- message: r$1.message ?? n$1.messageI18n
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 { i, r };
88
+ export { buildZodWithRelations, evalPredicate };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
- import { v } from "../contracts/dist/client/react/form-render.js";
4
- import { e } from "../contracts/dist/client/react/drivers/shadcn.js";
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$1) => onChange?.(v$1),
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$1) => props.onCheckedChange?.(v$1),
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$1) => props.onCheckedChange?.(v$1),
54
+ onCheckedChange: (v) => props.onCheckedChange?.(v),
55
55
  ...props
56
56
  });
57
- const formRenderer = v({ driver: e({
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-20251217062139",
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-20251217062139",
25
- "@lssm/lib.contracts": "0.0.0-canary-20251217062139",
26
- "@lssm/lib.ui-kit": "0.0.0-canary-20251217062139",
27
- "@lssm/lib.ui-kit-web": "0.0.0-canary-20251217062139",
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-20251217062139",
39
- "@lssm/tool.tsdown": "0.0.0-canary-20251217062139",
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",