react-luna-form 0.0.25 → 0.0.27

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.
Files changed (41) hide show
  1. package/dist/client/cjs/index.js +927 -447
  2. package/dist/client/esm/index.js +916 -436
  3. package/dist/server/cjs/index.js +462 -101
  4. package/dist/server/esm/index.js +463 -102
  5. package/dist/types/luna-core/src/handle/state-event.d.ts +1 -1
  6. package/dist/types/luna-core/src/index.d.ts +1 -0
  7. package/dist/types/luna-core/src/type.d.ts +17 -2
  8. package/dist/types/luna-core/src/util/constant.d.ts +4 -0
  9. package/dist/types/luna-core/src/util/extract.d.ts +2 -1
  10. package/dist/types/luna-core/src/util/is-input.d.ts +4 -3
  11. package/dist/types/luna-core/src/util/is-type.d.ts +1 -0
  12. package/dist/types/luna-core/src/util/list.d.ts +4 -0
  13. package/dist/types/luna-core/src/util/prepare.d.ts +2 -2
  14. package/dist/types/luna-core/src/util/string.d.ts +1 -0
  15. package/dist/types/luna-react/src/client/component/field/field-list.d.ts +2 -0
  16. package/dist/types/luna-react/src/client/component/{wrapper/with-error.d.ts → field/field-with-error.d.ts} +1 -1
  17. package/dist/types/luna-react/src/client/component/{wrapper/with-field-state.d.ts → field/field-with-state.d.ts} +1 -1
  18. package/dist/types/luna-react/src/client/component/guard/list-guard.d.ts +2 -0
  19. package/dist/types/luna-react/src/client/component/guard/visibility-guard.d.ts +6 -0
  20. package/dist/types/luna-react/src/client/component/slot/slot.d.ts +7 -0
  21. package/dist/types/luna-react/src/client/hook/use-field-list.d.ts +2 -0
  22. package/dist/types/luna-react/src/component/chevron-icon.d.ts +3 -0
  23. package/dist/types/luna-react/src/component/column.d.ts +2 -3
  24. package/dist/types/luna-react/src/component/field/field-base.d.ts +4 -2
  25. package/dist/types/luna-react/src/component/field/field-horizontal.d.ts +2 -8
  26. package/dist/types/luna-react/src/component/field/field-list-item.d.ts +8 -0
  27. package/dist/types/luna-react/src/component/field/field-list.d.ts +7 -0
  28. package/dist/types/luna-react/src/component/field/field-set-advanced.d.ts +5 -0
  29. package/dist/types/luna-react/src/component/field/field-set-base.d.ts +7 -0
  30. package/dist/types/luna-react/src/component/field/field-vertical.d.ts +2 -8
  31. package/dist/types/luna-react/src/component/list.d.ts +5 -0
  32. package/dist/types/luna-react/src/component/slot/list-slot.d.ts +2 -0
  33. package/dist/types/luna-react/src/component/slot/slot-base.d.ts +8 -6
  34. package/dist/types/luna-react/src/component/slot/slot-create.d.ts +4 -3
  35. package/dist/types/luna-react/src/component/slot/slot-list.d.ts +12 -0
  36. package/dist/types/luna-react/src/component/slot/slot.d.ts +1 -0
  37. package/package.json +5 -5
  38. package/dist/types/luna-react/src/component/input/input-attributes.d.ts +0 -3
  39. package/dist/types/luna-react/src/component/input/input-common.d.ts +0 -2
  40. package/dist/types/luna-react/src/component/input/input-option-select.d.ts +0 -5
  41. /package/dist/types/luna-react/src/client/component/{wrapper/index.d.ts → field/field.d.ts} +0 -0
@@ -34,18 +34,34 @@ __export(index_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
- // src/component/field/field-error.tsx
37
+ // src/component/control.tsx
38
38
  var import_jsx_runtime = require("react/jsx-runtime");
39
- function FieldError(props) {
40
- if (!props.errors || props.errors.length === 0) {
41
- return null;
42
- }
43
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
44
- "ul",
39
+ function Control(props) {
40
+ const content = typeof props.children === "function" ? props.children({ isPending: props.isPending }) : props.children;
41
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-slot": "field-control", className: "w-full", children: content });
42
+ }
43
+
44
+ // src/component/field/field-set-advanced.tsx
45
+ var import_react = require("react");
46
+
47
+ // src/component/chevron-icon.tsx
48
+ var import_jsx_runtime2 = require("react/jsx-runtime");
49
+ function ChevronIcon(props) {
50
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
51
+ "svg",
45
52
  {
46
- className: "text-sm text-red-600 dark:text-red-500",
47
- id: props.name ? `${props.name}-error` : void 0,
48
- children: props.errors?.map((error, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: error }, props.name ? `${props.name}-error-${index}` : index))
53
+ xmlns: "http://www.w3.org/2000/svg",
54
+ viewBox: "0 0 20 20",
55
+ fill: "currentColor",
56
+ className: `size-4 transition-transform duration-200 ${props.expanded ? "rotate-90" : ""}`,
57
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
58
+ "path",
59
+ {
60
+ fillRule: "evenodd",
61
+ d: "M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z",
62
+ clipRule: "evenodd"
63
+ }
64
+ )
49
65
  }
50
66
  );
51
67
  }
@@ -57,10 +73,12 @@ var INPUT_NUMBER = "input/number";
57
73
  var TEXTAREA = "textarea";
58
74
  var RADIO = "radio";
59
75
  var CHECKBOX = "checkbox";
76
+ var LIST = "list";
60
77
  var SELECT = "select";
61
78
  var SELECT_MONTH = "select/month";
62
79
  var SELECT_YEAR = "select/year";
63
80
  var COLUMN = "column";
81
+ var FIELDS = "fields";
64
82
  var LABEL = "label";
65
83
  var VALUE = "value";
66
84
  var OPTIONS = "options";
@@ -85,6 +103,7 @@ var STATE = "state";
85
103
  var COMMON_URL = "http://luna.internal";
86
104
  var VERTICAL = "vertical";
87
105
  var HORIZONTAL = "horizontal";
106
+ var TYPE = "type";
88
107
 
89
108
  // ../luna-core/src/util/is-type.ts
90
109
  function isObject(value) {
@@ -94,7 +113,7 @@ function isEmpty(value) {
94
113
  return value === null || value === void 0 || value === "";
95
114
  }
96
115
  function isValue(value) {
97
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
116
+ return isString(value) || typeof value === "number" || isBoolean(value);
98
117
  }
99
118
  function isString(value) {
100
119
  return typeof value === "string";
@@ -102,9 +121,13 @@ function isString(value) {
102
121
  function isDataSource(value) {
103
122
  return isObject(value) && "url" in value;
104
123
  }
124
+ function isBoolean(value) {
125
+ return typeof value === "boolean";
126
+ }
105
127
 
106
128
  // ../luna-core/src/util/extract.ts
107
129
  var REGEX_TYPE = /[^/]+$/;
130
+ var REGEX_NUMERIC = /^\d+$/;
108
131
  function getEntity(selected, collection = [], entity = VALUE) {
109
132
  if (Array.isArray(collection)) {
110
133
  return collection.find((item) => {
@@ -114,6 +137,7 @@ function getEntity(selected, collection = [], entity = VALUE) {
114
137
  }
115
138
  }) ?? { value: selected };
116
139
  }
140
+ return { value: selected };
117
141
  }
118
142
  function getCurrentValue(value, entity = VALUE) {
119
143
  if (value !== null && value !== void 0) {
@@ -198,6 +222,46 @@ function getFormData(formData) {
198
222
  }
199
223
  return data;
200
224
  }
225
+ function unflatten(data) {
226
+ const result = {};
227
+ for (const key in data) {
228
+ const parts = key.split(".");
229
+ if (parts.length === 1) {
230
+ result[key] = data[key];
231
+ continue;
232
+ }
233
+ let current = result;
234
+ for (let i = 0; i < parts.length - 1; i++) {
235
+ const part = parts[i];
236
+ const next = parts[i + 1];
237
+ const isNextIndex = REGEX_NUMERIC.test(next);
238
+ if (!(part in current)) {
239
+ current[part] = isNextIndex ? [] : {};
240
+ }
241
+ current = current[part];
242
+ }
243
+ const last = parts[parts.length - 1];
244
+ current[last] = data[key];
245
+ }
246
+ compactArrays(result);
247
+ return result;
248
+ }
249
+ function compactArrays(obj) {
250
+ for (const key in obj) {
251
+ const value = obj[key];
252
+ if (Array.isArray(value)) {
253
+ const compacted = value.filter((item) => item !== void 0);
254
+ compacted.forEach((item) => {
255
+ if (item !== null && typeof item === "object" && !Array.isArray(item)) {
256
+ compactArrays(item);
257
+ }
258
+ });
259
+ obj[key] = compacted;
260
+ } else if (value !== null && typeof value === "object") {
261
+ compactArrays(value);
262
+ }
263
+ }
264
+ }
201
265
 
202
266
  // ../luna-core/src/util/string.ts
203
267
  var REGEX_MARKDOWN_LINK = /\[([^\]]+)\]\(([^)]+)\)/g;
@@ -219,6 +283,9 @@ function interpolate(template, values = {}) {
219
283
  }
220
284
  return template;
221
285
  }
286
+ function interpolateIfNeeded(template, values = {}) {
287
+ return isInterpolated(template) ? interpolate(template, values) : template;
288
+ }
222
289
  function isInterpolated(template) {
223
290
  if (isString(template)) {
224
291
  return /{([^}]+)}/.test(template);
@@ -295,7 +362,7 @@ function handleProxyEvent(events = [], callback) {
295
362
  const values = [];
296
363
  const sources = [];
297
364
  const states = [];
298
- events.forEach((event) => {
365
+ for (const event of events) {
299
366
  if (event.action === VALUE) {
300
367
  values.push(event);
301
368
  }
@@ -305,17 +372,17 @@ function handleProxyEvent(events = [], callback) {
305
372
  if (event.action === STATE) {
306
373
  states.push(event);
307
374
  }
308
- });
375
+ }
309
376
  callback({ sources, states, values });
310
377
  }
311
378
 
312
379
  // ../luna-core/src/handle/source-event.ts
313
380
  function handleSourceEvent(selected = null, events = [], setSource) {
314
- events.forEach((event) => {
381
+ for (const event of events) {
315
382
  const { target, source } = event;
316
383
  if (!selected) {
317
384
  setSource(target, void 0);
318
- return;
385
+ continue;
319
386
  }
320
387
  if (isDataSource(source)) {
321
388
  const newUrl = interpolate(source.url, selected);
@@ -326,7 +393,7 @@ function handleSourceEvent(selected = null, events = [], setSource) {
326
393
  body: newBody
327
394
  });
328
395
  }
329
- });
396
+ }
330
397
  }
331
398
 
332
399
  // ../luna-core/src/util/operator.ts
@@ -399,31 +466,39 @@ function lte(current, value) {
399
466
 
400
467
  // ../luna-core/src/handle/state-event.ts
401
468
  function handleStateEvent(selected = null, events = [], setState) {
402
- events.forEach((event) => {
469
+ for (const event of events) {
403
470
  const { target, state, when } = event;
471
+ const targets = Array.isArray(target) ? target : [target];
404
472
  if (!selected) {
405
- setState(target);
406
- return;
473
+ setState(targets);
474
+ continue;
407
475
  }
476
+ logger.info(`Selected value for state event: ${JSON.stringify(selected)}, evaluating condition: ${JSON.stringify(when)}`);
408
477
  const matches = evaluateCondition(selected, when);
409
- setState(target, matches ? state : void 0);
410
- });
478
+ logger.info(`Condition evaluated to: ${matches}, setting state for targets: ${targets.join(", ")}`);
479
+ setState(targets, matches ? state : void 0);
480
+ }
411
481
  }
412
482
  function evaluateCondition(selected, when) {
413
483
  if (when === void 0) {
414
484
  return true;
415
485
  }
416
486
  if (isString(when)) {
417
- return getValue2(selected, "value") === when;
487
+ logger.info(`Evaluating string condition: ${when} against selected value: ${JSON.stringify(selected)} using VALUE field`);
488
+ logger.info(`Extracted value for comparison: ${JSON.stringify(getValue2(selected, VALUE))}`);
489
+ logger.info(`Comparison result: ${getValue2(selected, VALUE) === when}`);
490
+ return getValue2(selected, VALUE) === when;
491
+ }
492
+ if (isBoolean(when)) {
493
+ return Boolean(getValue2(selected, VALUE)) === when;
418
494
  }
419
495
  if (Array.isArray(when)) {
420
- const current = getValue2(selected, "value");
421
- return when.includes(String(current));
496
+ return when.includes(String(getValue2(selected, VALUE)));
422
497
  }
423
498
  return evaluateOperator(selected, when);
424
499
  }
425
500
  function evaluateOperator(selected = null, condition) {
426
- const current = getValue2(selected, condition.field ?? "value");
501
+ const current = getValue2(selected, condition.field ?? VALUE);
427
502
  const { operator = "eq", value } = condition;
428
503
  const operation = operators[operator];
429
504
  if (operation) {
@@ -433,18 +508,18 @@ function evaluateOperator(selected = null, condition) {
433
508
  }
434
509
  function getValue2(selected, field) {
435
510
  if (isObject(selected)) {
436
- return field.includes(".") ? extract(selected, field) : selected[field];
511
+ return extract(selected, field);
437
512
  }
438
513
  return selected;
439
514
  }
440
515
 
441
516
  // ../luna-core/src/handle/value-event.ts
442
517
  function handleValueEvent(selected = null, events = [], setValue) {
443
- events.forEach((event) => {
444
- Object.entries(event.value).forEach(([target, value]) => {
518
+ for (const event of events) {
519
+ for (const [target, value] of Object.entries(event.value)) {
445
520
  setValue(target, selected ? interpolate(value, selected) : void 0);
446
- });
447
- });
521
+ }
522
+ }
448
523
  }
449
524
 
450
525
  // ../luna-core/src/util/is-input.ts
@@ -466,11 +541,14 @@ var isNumber = createTypeChecker(INPUT_NUMBER, TYPE_NUMBER);
466
541
  function isClickable(field) {
467
542
  return isRadio(field) || isCheckbox(field);
468
543
  }
544
+ function isList(slot) {
545
+ return slot.type === LIST;
546
+ }
469
547
  function isColumn(slot) {
470
548
  return slot.type === COLUMN;
471
549
  }
472
550
  function isField(slot) {
473
- return slot.type !== COLUMN;
551
+ return slot.type !== COLUMN && slot.type !== LIST;
474
552
  }
475
553
  function isOptions(field) {
476
554
  return isSelect(field) || isRadio(field);
@@ -735,7 +813,7 @@ function prepareInputValue(field, value) {
735
813
  var REGEX_REF = /^#\/definition\//;
736
814
  function prepare(base = [], definition) {
737
815
  const resolved = resolveRefs(base, definition);
738
- return Array.isArray(resolved) ? resolved.sort((a, b) => getOrder(a) - getOrder(b)) : [];
816
+ return Array.isArray(resolved) ? resolved.filter(filter).sort((a, b) => getOrder(a) - getOrder(b)) : [];
739
817
  }
740
818
  function resolveRefs(base, definition, cache = /* @__PURE__ */ new Map(), visited = /* @__PURE__ */ new WeakSet()) {
741
819
  if (!isDefinition(definition) || !base || typeof base !== "object") {
@@ -776,6 +854,15 @@ function getOrder(item) {
776
854
  function isDefinition(definition) {
777
855
  return definition !== void 0 && isObject(definition) && Object.keys(definition).length > 0;
778
856
  }
857
+ function filter(base) {
858
+ if (TYPE in base) {
859
+ return true;
860
+ }
861
+ if (Array.isArray(base[FIELDS])) {
862
+ return base[FIELDS].length > 0;
863
+ }
864
+ return true;
865
+ }
779
866
 
780
867
  // ../luna-core/src/util/attributes.ts
781
868
  function getPrefixedAttributes(prefix, record) {
@@ -823,6 +910,31 @@ function getSpan(value) {
823
910
  }
824
911
  }
825
912
 
913
+ // ../luna-core/src/util/list.ts
914
+ function getInitialCount(list, value) {
915
+ const min2 = list.advanced?.length?.min ?? 1;
916
+ if (value) {
917
+ const data = extract(value, list.name);
918
+ if (Array.isArray(data)) {
919
+ return Math.max(data.length, min2);
920
+ }
921
+ }
922
+ return Math.max(min2, 0);
923
+ }
924
+ function isMultiFieldList(list) {
925
+ if (!Array.isArray(list.fields) || list.fields.length === 0) {
926
+ return false;
927
+ }
928
+ return list.fields.length > 1 || list.fields[0].type === COLUMN;
929
+ }
930
+ function getInitialList(list, value) {
931
+ const count = getInitialCount(list, value);
932
+ return Array.from({ length: count }, (_, index) => index);
933
+ }
934
+ function getLabel(list) {
935
+ return list.label ?? list.name;
936
+ }
937
+
826
938
  // ../luna-core/src/util/schema.ts
827
939
  var import_zod = require("zod");
828
940
 
@@ -1115,161 +1227,140 @@ function mergeStyle(globalStyle, localStyle) {
1115
1227
  return { ...globalStyle, ...localStyle };
1116
1228
  }
1117
1229
 
1118
- // src/component/field/field-base.tsx
1119
- var import_tailwind_merge = require("tailwind-merge");
1120
- var import_jsx_runtime2 = require("react/jsx-runtime");
1121
- function FieldBase(props) {
1122
- const errors = props.errors && props.errors.length > 0;
1123
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1124
- "div",
1125
- {
1126
- "data-slot": "field",
1127
- "data-clickable": props.isClickable ? "true" : "false",
1128
- ...errors && { [DATA_INVALID]: "true" },
1129
- ...props.disabled && { [DATA_READONLY]: "true" },
1130
- "data-orientation": props.orientation,
1131
- className: (0, import_tailwind_merge.twMerge)(
1132
- "group flex w-full flex-col items-center data-[invalid=true]:text-red-600 data-[invalid=true]:dark:text-red-500",
1133
- "data-[clickable=true]:items-start",
1134
- props.isCheckbox && (props.isReversed ? "flex-row-reverse!" : "flex-row!"),
1135
- "data-[clickable=true]:has-[>[data-slot=field-content]]:[&>:first-child]:mt-px",
1136
- props.className
1137
- ),
1138
- children: props.children
1230
+ // src/lib/string.tsx
1231
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1232
+ function formatMarkdown2(text) {
1233
+ return formatMarkdown(
1234
+ text,
1235
+ (index, url, text2) => {
1236
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1237
+ "a",
1238
+ {
1239
+ className: "underline",
1240
+ href: url,
1241
+ rel: "noopener noreferrer",
1242
+ target: "_blank",
1243
+ children: text2
1244
+ },
1245
+ `${url}-${index}`
1246
+ );
1139
1247
  }
1140
1248
  );
1141
1249
  }
1142
1250
 
1143
- // src/component/field/field-vertical.tsx
1144
- var import_tailwind_merge2 = require("tailwind-merge");
1145
- var import_jsx_runtime3 = require("react/jsx-runtime");
1146
- function FieldVertical(props) {
1147
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1148
- FieldBase,
1251
+ // src/component/field/field-set-advanced.tsx
1252
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1253
+ function FieldSetAdvanced(props) {
1254
+ const { fields = [] } = props.section;
1255
+ const [isOpen, setIsOpen] = (0, import_react.useState)(false);
1256
+ const handleOpen = (0, import_react.useCallback)(() => setIsOpen((previous) => !previous), []);
1257
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1258
+ "fieldset",
1149
1259
  {
1150
- ...props,
1151
- orientation: VERTICAL,
1152
- className: (0, import_tailwind_merge2.twMerge)(
1153
- "gap-3 has-[>[data-slot=field-content]]:items-start",
1154
- !props.isClickable && "[&>*]:w-full"
1155
- ),
1156
- children: props.children
1260
+ "data-slot": "field-set",
1261
+ "data-advanced": "true",
1262
+ "data-expanded": isOpen,
1263
+ "data-empty": fields.length === 0,
1264
+ className: "flex flex-col",
1265
+ id: props.section.id?.toString(),
1266
+ children: [
1267
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("legend", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1268
+ "button",
1269
+ {
1270
+ className: "flex cursor-pointer items-center gap-2 text-base font-medium text-slate-600 dark:text-slate-400",
1271
+ onClick: handleOpen,
1272
+ type: "button",
1273
+ children: [
1274
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChevronIcon, { expanded: isOpen }),
1275
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: formatMarkdown2(props.section.title) })
1276
+ ]
1277
+ }
1278
+ ) }),
1279
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react.Activity, { mode: isOpen ? "visible" : "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1280
+ "div",
1281
+ {
1282
+ className: "mt-3 ml-1.5 flex flex-col gap-4 border-l-2 border-slate-300 pl-4 dark:border-slate-600",
1283
+ "data-slot": "field-set-content",
1284
+ children: [
1285
+ props.section.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-sm leading-normal font-normal text-slate-600 dark:text-slate-400", children: formatMarkdown2(props.section.description) }),
1286
+ props.group
1287
+ ]
1288
+ }
1289
+ ) })
1290
+ ]
1157
1291
  }
1158
1292
  );
1159
1293
  }
1160
1294
 
1161
- // src/component/field/field-horizontal.tsx
1162
- var import_tailwind_merge3 = require("tailwind-merge");
1163
- var import_jsx_runtime4 = require("react/jsx-runtime");
1164
- function FieldHorizontal(props) {
1165
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1166
- FieldBase,
1295
+ // src/component/legend.tsx
1296
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1297
+ function Legend(props) {
1298
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1299
+ props.title && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("legend", { className: "mb-3 font-medium text-slate-800 dark:text-slate-200", children: formatMarkdown2(props.title) }),
1300
+ props.description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "-mt-2 text-sm leading-normal font-normal text-slate-600 dark:text-slate-400", children: formatMarkdown2(props.description) })
1301
+ ] });
1302
+ }
1303
+
1304
+ // src/component/field/field-set-base.tsx
1305
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1306
+ function FieldSetBase(props) {
1307
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1308
+ "fieldset",
1167
1309
  {
1168
- ...props,
1169
- orientation: HORIZONTAL,
1170
- className: (0, import_tailwind_merge3.twMerge)(
1171
- "gap-2 md:flex-row md:gap-4",
1172
- "[&>[data-slot=field-content]]:min-w-0 [&>[data-slot=field-content]]:flex-grow [&>[data-slot=field-content]]:self-start",
1173
- "[&_[role=checkbox]]:mt-[1.5px]",
1174
- props.isClickable && "md:flex-col",
1175
- !props.isClickable && [
1176
- "md:justify-between",
1177
- "[&>*:not([data-slot=field-content])]:w-full",
1178
- "[&>*:not([data-slot=field-content])]:md:w-1/2",
1179
- "[&>*:not([data-slot=field-content])]:xl:w-2/5"
1180
- ]
1181
- ),
1182
- children: props.children
1310
+ "data-slot": "field-set",
1311
+ "data-empty": props.empty,
1312
+ className: "flex flex-col data-[empty=false]:gap-6",
1313
+ id: props.id,
1314
+ children: [
1315
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Legend, { description: props.description, title: props.title }),
1316
+ props.children
1317
+ ]
1183
1318
  }
1184
1319
  );
1185
1320
  }
1186
1321
 
1187
- // src/component/field/field-group.tsx
1188
- var import_jsx_runtime5 = require("react/jsx-runtime");
1189
- function FieldGroup(props) {
1190
- const clickable = isClickable(props.field);
1191
- const checkbox = isCheckbox(props.field);
1192
- const reversed = buildReverse(props.field);
1193
- if (props.orientation === VERTICAL) {
1194
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1195
- FieldVertical,
1196
- {
1197
- disabled: props.disabled,
1198
- errors: props.errors,
1199
- isCheckbox: checkbox,
1200
- isReversed: reversed,
1201
- isClickable: clickable,
1202
- children: props.children
1203
- }
1204
- );
1205
- }
1206
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1207
- FieldHorizontal,
1322
+ // src/component/group.tsx
1323
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1324
+ function Group(props) {
1325
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1326
+ "div",
1208
1327
  {
1209
- disabled: props.disabled,
1210
- errors: props.errors,
1211
- isCheckbox: checkbox,
1212
- isReversed: reversed,
1213
- isClickable: clickable,
1328
+ "data-slot": "field-group",
1329
+ "data-compact": props.compact,
1330
+ className: "flex w-full flex-col gap-8 data-[compact=true]:gap-3",
1214
1331
  children: props.children
1215
1332
  }
1216
1333
  );
1217
1334
  }
1218
1335
 
1219
- // src/component/input/input-base.tsx
1220
- function InputBase(props) {
1221
- if (!props.field.type) {
1222
- return null;
1223
- }
1224
- const commonProps = buildCommon(props.field, props.disabled);
1225
- const dataAttributes = buildDataAttributes(props.field);
1226
- const ariaAttributes = buildAriaAttributes(props.field, props.errors);
1227
- const field = {
1228
- ...props.field,
1229
- disabled: commonProps.disabled
1230
- };
1231
- return props.children({
1232
- ariaAttributes,
1233
- commonProps,
1234
- dataAttributes,
1235
- field,
1236
- orientation: props.orientation
1336
+ // src/component/field/field-set.tsx
1337
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1338
+ function FieldSet(props) {
1339
+ const { fields = [] } = props.section;
1340
+ const { compact } = mergeStyle(props.style, {
1341
+ compact: props.section.compact
1237
1342
  });
1343
+ const group = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Group, { compact, children: props.children });
1344
+ if (!props.section.title && !props.section.description) {
1345
+ return group;
1346
+ }
1347
+ if (props.section.advanced) {
1348
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(FieldSetAdvanced, { section: props.section, group });
1349
+ }
1350
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1351
+ FieldSetBase,
1352
+ {
1353
+ description: props.section.description,
1354
+ empty: fields.length === 0,
1355
+ id: props.section.id?.toString(),
1356
+ title: props.section.title,
1357
+ children: group
1358
+ }
1359
+ );
1238
1360
  }
1239
1361
 
1240
- // src/component/field/field.tsx
1241
- var import_tailwind_merge4 = require("tailwind-merge");
1242
- var import_jsx_runtime6 = require("react/jsx-runtime");
1243
- function Field(props) {
1244
- const cols2 = props.field.advanced?.cols;
1245
- const errors = props.field.name ? props.errors?.[props.field.name] : void 0;
1246
- const { orientation } = mergeStyle(props.style, {
1247
- orientation: buildOrientation(props.field)
1248
- });
1249
- const disabled = buildDisabled(props.field, props.disabled);
1250
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: (0, import_tailwind_merge4.twMerge)("flex flex-col gap-3", getSpan(cols2)), children: [
1251
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1252
- FieldGroup,
1253
- {
1254
- disabled,
1255
- errors,
1256
- field: props.field,
1257
- orientation,
1258
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1259
- InputBase,
1260
- {
1261
- disabled,
1262
- errors,
1263
- field: props.field,
1264
- orientation,
1265
- children: props.children
1266
- }
1267
- )
1268
- }
1269
- ),
1270
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FieldError, { errors, name: props.field.name })
1271
- ] });
1272
- }
1362
+ // src/client/component/guard/visibility-guard.tsx
1363
+ var import_jotai2 = require("jotai");
1273
1364
 
1274
1365
  // src/client/lib/store-helper.ts
1275
1366
  var import_jotai = require("jotai");
@@ -1415,188 +1506,89 @@ function createAtomStore(initialValue = {}) {
1415
1506
  };
1416
1507
  }
1417
1508
 
1418
- // src/client/lib/error-store.ts
1509
+ // src/client/lib/state-store.ts
1419
1510
  var store = createAtomStore();
1420
- var clearInputErrorAtom = store.clear;
1421
- var reportErrorAtom = store.bulkReport;
1422
- var reportInputErrorAtom = store.report;
1511
+ var fieldStateAtom = store.atom;
1512
+ var reportFieldStateAtom = store.report;
1423
1513
 
1424
- // src/client/component/wrapper/with-error.tsx
1425
- var import_jotai2 = require("jotai");
1426
- var import_jsx_runtime7 = require("react/jsx-runtime");
1427
- function withErrors(Component) {
1428
- const WithErrors = (props) => {
1429
- const errors = (0, import_jotai2.useAtomValue)(reportInputErrorAtom(props.field.name));
1430
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1431
- Component,
1432
- {
1433
- ...props,
1434
- errors: errors ? { [props.field.name]: errors } : void 0
1435
- }
1436
- );
1437
- };
1438
- return WithErrors;
1514
+ // src/client/component/guard/visibility-guard.tsx
1515
+ function isColumnHidden(column, states) {
1516
+ return column.fields.every((field) => isFieldHidden(field, states));
1439
1517
  }
1440
-
1441
- // src/client/lib/state-store.ts
1442
- var store2 = createAtomStore();
1443
- var fieldStateAtom = store2.atom;
1444
- var reportFieldStateAtom = store2.report;
1445
-
1446
- // src/client/component/wrapper/with-field-state.tsx
1447
- var import_jotai3 = require("jotai");
1448
- var import_jsx_runtime8 = require("react/jsx-runtime");
1449
- function withFieldState(Component) {
1450
- const WithFieldState = (props) => {
1451
- const fieldState = (0, import_jotai3.useAtomValue)(reportFieldStateAtom(props.field.name));
1452
- const hidden = fieldState?.hidden ?? props.field.hidden ?? false;
1453
- if (hidden) {
1454
- return null;
1455
- }
1456
- const disabled = fieldState?.disabled ?? props.disabled;
1457
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Component, { ...props, disabled });
1458
- };
1459
- return WithFieldState;
1518
+ function isFieldHidden(field, states) {
1519
+ return states[field.name]?.hidden ?? field.hidden ?? false;
1460
1520
  }
1461
-
1462
- // src/client/component/wrapper/index.tsx
1463
- var Field2 = withFieldState(withErrors(Field));
1464
-
1465
- // src/component/control.tsx
1466
- var import_jsx_runtime9 = require("react/jsx-runtime");
1467
- function Control(props) {
1468
- const content = typeof props.children === "function" ? props.children({ isPending: props.isPending }) : props.children;
1469
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "data-slot": "field-control", className: "w-full", children: content });
1470
- }
1471
-
1472
- // src/component/group.tsx
1473
- var import_jsx_runtime10 = require("react/jsx-runtime");
1474
- function Group(props) {
1475
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1476
- "div",
1477
- {
1478
- "data-slot": "field-group",
1479
- "data-compact": props.compact,
1480
- className: "flex w-full flex-col gap-8 data-[compact=true]:gap-3",
1481
- children: props.children
1482
- }
1483
- );
1521
+ function isEntryHidden(entry, states) {
1522
+ return isColumn(entry) ? isColumnHidden(entry, states) : isFieldHidden(entry, states);
1484
1523
  }
1485
-
1486
- // src/lib/string.tsx
1487
- var import_jsx_runtime11 = require("react/jsx-runtime");
1488
- function formatMarkdown2(text) {
1489
- return formatMarkdown(
1490
- text,
1491
- (index, url, text2) => {
1492
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1493
- "a",
1494
- {
1495
- className: "underline",
1496
- href: url,
1497
- rel: "noopener noreferrer",
1498
- target: "_blank",
1499
- children: text2
1500
- },
1501
- `${url}-${index}`
1502
- );
1524
+ function VisibilityGuard(props) {
1525
+ const states = (0, import_jotai2.useAtomValue)(fieldStateAtom);
1526
+ if (props.container) {
1527
+ const hidden = states[props.container.name]?.hidden ?? props.container.hidden ?? false;
1528
+ if (hidden) {
1529
+ return null;
1503
1530
  }
1504
- );
1505
- }
1506
-
1507
- // src/component/legend.tsx
1508
- var import_jsx_runtime12 = require("react/jsx-runtime");
1509
- function Legend(props) {
1510
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1511
- props.title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("legend", { className: "mb-3 font-medium text-slate-800 dark:text-slate-200", children: formatMarkdown2(props.title) }),
1512
- props.description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "-mt-2 text-sm leading-normal font-normal text-slate-600 dark:text-slate-400", children: formatMarkdown2(props.description) })
1513
- ] });
1514
- }
1515
-
1516
- // src/component/field/field-set.tsx
1517
- var import_jsx_runtime13 = require("react/jsx-runtime");
1518
- function FieldSet(props) {
1519
- const fields = props.section.fields || [];
1520
- const { compact } = mergeStyle(props.style, {
1521
- compact: props.section.compact
1522
- });
1523
- if (!props.section.title && !props.section.description) {
1524
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Group, { compact, children: props.children });
1525
1531
  }
1526
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1527
- "fieldset",
1528
- {
1529
- "data-slot": "field-set",
1530
- "data-empty": fields.length === 0,
1531
- className: "flex flex-col data-[empty=false]:gap-6",
1532
- id: props.section.id?.toString(),
1533
- children: [
1534
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1535
- Legend,
1536
- {
1537
- description: props.section.description,
1538
- title: props.section.title
1539
- }
1540
- ),
1541
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Group, { compact, children: props.children })
1542
- ]
1543
- }
1544
- );
1532
+ if (props.fields.length === 0) {
1533
+ return null;
1534
+ }
1535
+ const allHidden = props.fields.every((entry) => isEntryHidden(entry, states));
1536
+ if (allHidden) {
1537
+ return null;
1538
+ }
1539
+ return props.children;
1545
1540
  }
1546
1541
 
1547
- // src/component/form.tsx
1548
- var import_react = require("react");
1549
-
1550
1542
  // src/component/separator.tsx
1551
- var import_jsx_runtime14 = require("react/jsx-runtime");
1543
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1552
1544
  function Separator() {
1553
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { "data-slot": "field-separator", className: "relative -my-2 h-5 text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "absolute inset-0 top-1/2 h-px w-full bg-slate-200 dark:bg-slate-800" }) });
1545
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "data-slot": "field-separator", className: "relative -my-2 h-5 text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "absolute inset-0 top-1/2 h-px w-full bg-slate-200 dark:bg-slate-800" }) });
1554
1546
  }
1555
1547
 
1556
1548
  // src/component/form.tsx
1557
- var import_jsx_runtime15 = require("react/jsx-runtime");
1549
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1558
1550
  function Form(props) {
1559
1551
  const sections = prepare(props.sections, props.definition);
1560
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "h-full w-full", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("form", { noValidate: props.noValidate, action: props.action, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Group, { children: [
1561
- sections.map((section, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react.Fragment, { children: [
1562
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FieldSet, { section, style: props.config.style, children: props.children({
1552
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-full w-full", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("form", { noValidate: props.noValidate, action: props.action, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Group, { children: [
1553
+ sections.map((section, index) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(VisibilityGuard, { fields: section.fields ?? [], children: [
1554
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldSet, { section, style: props.config.style, children: props.children({
1563
1555
  disabled: props.readOnly,
1564
1556
  fields: section.fields
1565
1557
  }) }),
1566
- section.separator && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Separator, {})
1558
+ section.separator && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Separator, {})
1567
1559
  ] }, index)),
1568
- props.control && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Control, { isPending: props.isPending, children: props.control })
1560
+ props.control && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Control, { isPending: props.isPending, children: props.control })
1569
1561
  ] }) }) });
1570
1562
  }
1571
1563
 
1572
1564
  // src/component/description.tsx
1573
- var import_jsx_runtime16 = require("react/jsx-runtime");
1565
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1574
1566
  function Description(props) {
1575
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "-mt-2 text-xs leading-normal font-normal text-slate-600 dark:text-slate-400", children: props.children });
1567
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "-mt-2 text-xs leading-normal font-normal text-slate-600 dark:text-slate-400", children: props.children });
1576
1568
  }
1577
1569
 
1578
1570
  // src/component/formatted-description.tsx
1579
- var import_jsx_runtime17 = require("react/jsx-runtime");
1571
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1580
1572
  function FormattedDescription(props) {
1581
1573
  const content = formatMarkdown2(props.text);
1582
1574
  if (content) {
1583
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Description, { children: content });
1575
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Description, { children: content });
1584
1576
  }
1585
1577
  return null;
1586
1578
  }
1587
1579
 
1588
1580
  // src/component/label.tsx
1589
- var import_tailwind_merge5 = require("tailwind-merge");
1590
- var import_jsx_runtime18 = require("react/jsx-runtime");
1581
+ var import_tailwind_merge = require("tailwind-merge");
1582
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1591
1583
  function Label(props) {
1592
1584
  const showOptionalLabel = props.style?.showOptionalLabel ?? true;
1593
1585
  const normal = isRadio(props.field) || isCheckbox(props.field);
1594
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1586
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1595
1587
  "label",
1596
1588
  {
1597
1589
  "data-slot": "field-label",
1598
1590
  "data-normal": normal,
1599
- className: (0, import_tailwind_merge5.twMerge)(
1591
+ className: (0, import_tailwind_merge.twMerge)(
1600
1592
  "flex w-fit items-center gap-2 text-sm leading-snug font-medium select-none",
1601
1593
  "data-[normal=true]:font-normal",
1602
1594
  "group-data-[readonly=true]:cursor-not-allowed group-data-[readonly=true]:opacity-50"
@@ -1604,30 +1596,31 @@ function Label(props) {
1604
1596
  htmlFor: props.field.name,
1605
1597
  children: [
1606
1598
  props.children,
1607
- showOptionalLabel && !props.field.required && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-sm text-slate-600 dark:text-slate-400", children: translate("(Optional)", props.translations) })
1599
+ showOptionalLabel && !props.field.required && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm text-slate-600 dark:text-slate-400", children: translate("(Optional)", props.translations) })
1608
1600
  ]
1609
1601
  }
1610
1602
  );
1611
1603
  }
1612
1604
 
1613
1605
  // src/component/input-label.tsx
1614
- var import_jsx_runtime19 = require("react/jsx-runtime");
1606
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1615
1607
  function InputLabel(props) {
1616
- const label = isInterpolated(props.field.label) ? interpolate(props.field.label, {
1617
- context: props.context,
1618
- env: props.config?.env
1619
- }) : props.field.label;
1620
- const description = isInterpolated(props.field.description) ? interpolate(props.field.description, {
1608
+ const interpolateOpts = {
1621
1609
  context: props.context,
1622
1610
  env: props.config?.env
1623
- }) : props.field.description;
1624
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1611
+ };
1612
+ const label = interpolateIfNeeded(props.field.label, interpolateOpts);
1613
+ const description = interpolateIfNeeded(
1614
+ props.field.description,
1615
+ interpolateOpts
1616
+ );
1617
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1625
1618
  "div",
1626
1619
  {
1627
1620
  "data-slot": "field-content",
1628
1621
  className: "flex w-full flex-1 flex-col gap-1.5 leading-snug",
1629
1622
  children: [
1630
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1623
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1631
1624
  Label,
1632
1625
  {
1633
1626
  field: props.field,
@@ -1636,7 +1629,7 @@ function InputLabel(props) {
1636
1629
  children: translate(label, props.translations)
1637
1630
  }
1638
1631
  ),
1639
- props.orientation === HORIZONTAL && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1632
+ props.orientation === HORIZONTAL && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1640
1633
  FormattedDescription,
1641
1634
  {
1642
1635
  text: translate(description, props.translations)
@@ -1648,10 +1641,10 @@ function InputLabel(props) {
1648
1641
  }
1649
1642
 
1650
1643
  // src/component/input-group.tsx
1651
- var import_jsx_runtime20 = require("react/jsx-runtime");
1644
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1652
1645
  function InputGroup(props) {
1653
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
1654
- props.field.name && props.field.label && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1646
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
1647
+ props.field.name && props.field.label && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1655
1648
  InputLabel,
1656
1649
  {
1657
1650
  config: props.config,
@@ -1662,7 +1655,7 @@ function InputGroup(props) {
1662
1655
  }
1663
1656
  ),
1664
1657
  props.children,
1665
- props.orientation === VERTICAL && props.field.description && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1658
+ props.orientation === VERTICAL && props.field.description && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1666
1659
  FormattedDescription,
1667
1660
  {
1668
1661
  text: translate(props.field.description, props.translations)
@@ -1679,11 +1672,17 @@ function renderIfExists(value, render) {
1679
1672
  return render(value);
1680
1673
  }
1681
1674
 
1675
+ // src/client/lib/error-store.ts
1676
+ var store2 = createAtomStore();
1677
+ var clearInputErrorAtom = store2.clear;
1678
+ var reportErrorAtom = store2.bulkReport;
1679
+ var reportInputErrorAtom = store2.report;
1680
+
1682
1681
  // src/client/component/input.tsx
1683
1682
  var import_react5 = require("react");
1684
1683
 
1685
1684
  // src/client/lib/source-store.ts
1686
- var import_jotai4 = require("jotai");
1685
+ var import_jotai3 = require("jotai");
1687
1686
  var merge = (values) => {
1688
1687
  const merged = mergeSource(values);
1689
1688
  if (merged) {
@@ -1692,7 +1691,7 @@ var merge = (values) => {
1692
1691
  return void 0;
1693
1692
  };
1694
1693
  var validate = (target) => target.trim() !== "";
1695
- var sourceAtom = (0, import_jotai4.atom)({});
1694
+ var sourceAtom = (0, import_jotai3.atom)({});
1696
1695
  var reportSourceAtom = createNestedRecordAtomFamily(
1697
1696
  sourceAtom,
1698
1697
  {
@@ -1703,7 +1702,7 @@ var reportSourceAtom = createNestedRecordAtomFamily(
1703
1702
  var clearInputSourceAtom = createNestedClearAtom(sourceAtom);
1704
1703
 
1705
1704
  // src/client/hook/use-data-source.ts
1706
- var import_jotai5 = require("jotai");
1705
+ var import_jotai4 = require("jotai");
1707
1706
 
1708
1707
  // src/client/hook/use-fetch.ts
1709
1708
  var import_swr = __toESM(require("swr"), 1);
@@ -1749,7 +1748,7 @@ function buildSource2(dataSource = null, config, disabled = false) {
1749
1748
  // src/client/hook/use-data-source.ts
1750
1749
  function useDataSource(field, config, value) {
1751
1750
  const dataSource = resolveSource(field, value);
1752
- const [source, setSource] = (0, import_jotai5.useAtom)(reportSourceAtom(field.name));
1751
+ const [source, setSource] = (0, import_jotai4.useAtom)(reportSourceAtom(field.name));
1753
1752
  const currentSource = source ?? dataSource;
1754
1753
  const data = useFetch(currentSource, config, field.disabled);
1755
1754
  return [data, setSource];
@@ -1783,7 +1782,7 @@ function useInput(field, onMount, onUnmount, translations) {
1783
1782
  }
1784
1783
 
1785
1784
  // src/client/component/input.tsx
1786
- var import_jotai7 = require("jotai");
1785
+ var import_jotai6 = require("jotai");
1787
1786
 
1788
1787
  // src/client/hook/use-timeout.ts
1789
1788
  var import_react3 = require("react");
@@ -1796,14 +1795,11 @@ function useTimeout() {
1796
1795
  }
1797
1796
  };
1798
1797
  }, []);
1799
- function clearTimeoutRef() {
1798
+ const setTimeoutRef = (0, import_react3.useCallback)((callback, delay) => {
1800
1799
  if (timeoutRef.current) {
1801
1800
  clearTimeout(timeoutRef.current);
1802
1801
  timeoutRef.current = null;
1803
1802
  }
1804
- }
1805
- const setTimeoutRef = (0, import_react3.useCallback)((callback, delay) => {
1806
- clearTimeoutRef();
1807
1803
  timeoutRef.current = setTimeout(callback, delay);
1808
1804
  }, []);
1809
1805
  return setTimeoutRef;
@@ -1817,26 +1813,27 @@ var clearInputValueAtom = store3.clear;
1817
1813
  var reportValueAtom = store3.report;
1818
1814
 
1819
1815
  // src/client/hook/use-value.ts
1820
- var import_jotai6 = require("jotai");
1816
+ var import_jotai5 = require("jotai");
1821
1817
  var import_react4 = require("react");
1822
1818
  function useValue(field, currentValue) {
1823
1819
  const { name } = field;
1824
1820
  const skipNextOnChangeRef = (0, import_react4.useRef)(false);
1825
- const [value, setValue] = (0, import_jotai6.useAtom)(reportValueAtom(name));
1821
+ const [value, setValue] = (0, import_jotai5.useAtom)(reportValueAtom(name));
1822
+ const onCurrentValueChange = (0, import_react4.useEffectEvent)(
1823
+ (currentValue2) => {
1824
+ const newValue = resolveValue(name, currentValue2);
1825
+ if (isValidValue(newValue)) {
1826
+ skipNextOnChangeRef.current = true;
1827
+ setValue(newValue);
1828
+ }
1829
+ }
1830
+ );
1826
1831
  (0, import_react4.useEffect)(() => {
1827
- if (!currentValue || !(name in currentValue)) {
1832
+ if (!currentValue) {
1828
1833
  return;
1829
1834
  }
1830
- const newValue = currentValue[name];
1831
- if (isValidValue(newValue)) {
1832
- logger.info("useValue: setting skipNextOnChange to true", {
1833
- fieldName: name,
1834
- newValue
1835
- });
1836
- skipNextOnChangeRef.current = true;
1837
- setValue(newValue);
1838
- }
1839
- }, [name, currentValue, setValue]);
1835
+ onCurrentValueChange(currentValue);
1836
+ }, [currentValue]);
1840
1837
  const shouldSkipOnChange = (0, import_react4.useCallback)(() => {
1841
1838
  if (skipNextOnChangeRef.current) {
1842
1839
  skipNextOnChangeRef.current = false;
@@ -1850,14 +1847,38 @@ function useValue(field, currentValue) {
1850
1847
  value
1851
1848
  };
1852
1849
  }
1850
+ function resolveValue(name, currentValue) {
1851
+ if (name in currentValue) {
1852
+ return currentValue[name];
1853
+ }
1854
+ if (!name.includes(".")) {
1855
+ return void 0;
1856
+ }
1857
+ const keys = name.split(".");
1858
+ let result = currentValue;
1859
+ for (const key of keys) {
1860
+ if (result === null || result === void 0) {
1861
+ return void 0;
1862
+ }
1863
+ if (Array.isArray(result)) {
1864
+ const index = Number(key);
1865
+ result = Number.isInteger(index) ? result[index] : void 0;
1866
+ } else if (typeof result === "object") {
1867
+ result = result[key];
1868
+ } else {
1869
+ return void 0;
1870
+ }
1871
+ }
1872
+ return result;
1873
+ }
1853
1874
 
1854
1875
  // src/client/component/input.tsx
1855
- var import_jsx_runtime21 = require("react/jsx-runtime");
1876
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1856
1877
  function Input(props) {
1857
1878
  const entity = props.field.advanced?.entity;
1858
- const store4 = (0, import_jotai7.useStore)();
1879
+ const store4 = (0, import_jotai6.useStore)();
1859
1880
  const setTimeoutRef = useTimeout();
1860
- const [, startTransition3] = (0, import_react5.useTransition)();
1881
+ const [, startTransition2] = (0, import_react5.useTransition)();
1861
1882
  const { setValue, shouldSkipOnChange, value } = useValue(
1862
1883
  props.field,
1863
1884
  props.value
@@ -1868,9 +1889,9 @@ function Input(props) {
1868
1889
  translationsRef.current = props.translations;
1869
1890
  const hasTextable = isTextable(props.field);
1870
1891
  const hasClickable = isClickable(props.field);
1871
- const setValues = (0, import_jotai7.useSetAtom)(valueAtom);
1872
- const setFieldStates = (0, import_jotai7.useSetAtom)(fieldStateAtom);
1873
- const setErrors = (0, import_jotai7.useSetAtom)(reportInputErrorAtom(props.field.name));
1892
+ const setValues = (0, import_jotai6.useSetAtom)(valueAtom);
1893
+ const setFieldStates = (0, import_jotai6.useSetAtom)(fieldStateAtom);
1894
+ const setErrors = (0, import_jotai6.useSetAtom)(reportInputErrorAtom(props.field.name));
1874
1895
  const [data, setSource] = useDataSource(props.field, props.config, value);
1875
1896
  const schema = useInput(
1876
1897
  props.field,
@@ -1923,37 +1944,18 @@ function Input(props) {
1923
1944
  }, 500);
1924
1945
  return;
1925
1946
  }
1926
- const currentValue = getEntity(value2, data, entity);
1927
- callback(currentValue);
1947
+ callback(getEntity(value2, data, entity));
1928
1948
  },
1929
1949
  [data, entity, hasTextable, setTimeoutRef]
1930
1950
  );
1931
1951
  const onChange = (0, import_react5.useCallback)(
1932
1952
  (event) => {
1933
1953
  const inputValue = event.target.value;
1934
- logger.info("onChange fired", {
1935
- fieldName: props.field.name,
1936
- fieldType: props.field.type,
1937
- inputValue,
1938
- currentValue: valueRef.current,
1939
- hasTextable,
1940
- hasClickable
1941
- });
1942
1954
  if (!hasClickable && shouldSkipOnChange()) {
1943
- logger.info("shouldSkipOnChange returned true", {
1944
- fieldName: props.field.name,
1945
- willSkip: !hasTextable || inputValue === valueRef.current,
1946
- reason: !hasTextable ? "not textable (checkbox/radio/select)" : "value unchanged"
1947
- });
1948
1955
  if (!hasTextable || inputValue === valueRef.current) {
1949
- logger.info("SKIPPING onChange", { fieldName: props.field.name });
1950
1956
  return;
1951
1957
  }
1952
1958
  }
1953
- logger.info("onChange processing", {
1954
- fieldName: props.field.name,
1955
- inputValue
1956
- });
1957
1959
  onValueChangeRef.current?.(inputValue);
1958
1960
  if (props.config.validation.change) {
1959
1961
  validated(inputValue);
@@ -1962,19 +1964,32 @@ function Input(props) {
1962
1964
  if (events) {
1963
1965
  handleTriggerEvent(inputValue, (selected) => {
1964
1966
  handleProxyEvent(events, ({ sources, states, values }) => {
1965
- startTransition3(() => {
1967
+ startTransition2(() => {
1966
1968
  handleSourceEvent(
1967
1969
  selected,
1968
1970
  sources,
1969
1971
  (target, source) => setSource(target, source)
1970
1972
  );
1971
- handleStateEvent(selected, states, (target, state) => {
1973
+ handleStateEvent(selected, states, (targets, state) => {
1974
+ logger.info(
1975
+ `Setting field states for targets: ${targets.join(
1976
+ ", "
1977
+ )} with state: ${JSON.stringify(state)}`
1978
+ );
1972
1979
  setFieldStates((prev) => {
1973
1980
  if (state) {
1974
- return { ...prev, [target]: state };
1981
+ return targets.reduce(
1982
+ (acc, target) => ({
1983
+ ...acc,
1984
+ [target]: state
1985
+ }),
1986
+ prev
1987
+ );
1975
1988
  }
1976
- const { [target]: _removed, ...rest } = prev;
1977
- return rest;
1989
+ return targets.reduce((acc, target) => {
1990
+ const { [target]: _removed, ...rest } = acc;
1991
+ return rest;
1992
+ }, prev);
1978
1993
  });
1979
1994
  });
1980
1995
  handleValueEvent(selected, values, (target, value2) => {
@@ -1994,8 +2009,6 @@ function Input(props) {
1994
2009
  hasTextable,
1995
2010
  props.config.validation.change,
1996
2011
  props.field.event?.change,
1997
- props.field.name,
1998
- props.field.type,
1999
2012
  setFieldStates,
2000
2013
  setSource,
2001
2014
  setValues,
@@ -2014,7 +2027,7 @@ function Input(props) {
2014
2027
  },
2015
2028
  [hasClickable, props.config.validation.blur, validated]
2016
2029
  );
2017
- return renderIfExists(props.config.inputs[props.field.type], (Component) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2030
+ return renderIfExists(props.config.inputs[props.field.type], (Component) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2018
2031
  InputGroup,
2019
2032
  {
2020
2033
  config: props.config,
@@ -2022,7 +2035,7 @@ function Input(props) {
2022
2035
  field: props.field,
2023
2036
  orientation: props.orientation,
2024
2037
  translations: props.translations,
2025
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2038
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2026
2039
  Component,
2027
2040
  {
2028
2041
  ...commonPropsWithOptions,
@@ -2037,48 +2050,502 @@ function Input(props) {
2037
2050
  ));
2038
2051
  }
2039
2052
 
2040
- // src/component/column.tsx
2041
- var import_tailwind_merge6 = require("tailwind-merge");
2053
+ // src/component/field/field-error.tsx
2054
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2055
+ function FieldError(props) {
2056
+ if (!props.errors || props.errors.length === 0) {
2057
+ return null;
2058
+ }
2059
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2060
+ "ul",
2061
+ {
2062
+ className: "text-sm text-red-600 dark:text-red-500",
2063
+ id: props.name ? `${props.name}-error` : void 0,
2064
+ children: props.errors?.map((error, index) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("li", { children: error }, props.name ? `${props.name}-error-${index}` : index))
2065
+ }
2066
+ );
2067
+ }
2068
+
2069
+ // src/component/field/field-base.tsx
2070
+ var import_tailwind_merge2 = require("tailwind-merge");
2071
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2072
+ function FieldBase(props) {
2073
+ const errors = props.errors && props.errors.length > 0;
2074
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2075
+ "div",
2076
+ {
2077
+ "data-slot": "field",
2078
+ "data-clickable": props.isClickable ? "true" : "false",
2079
+ ...errors && { [DATA_INVALID]: "true" },
2080
+ ...props.disabled && { [DATA_READONLY]: "true" },
2081
+ "data-orientation": props.orientation,
2082
+ className: (0, import_tailwind_merge2.twMerge)(
2083
+ "group flex w-full flex-col items-center data-[invalid=true]:text-red-600 data-[invalid=true]:dark:text-red-500",
2084
+ "data-[clickable=true]:items-start",
2085
+ props.isCheckbox && (props.isReversed ? "flex-row-reverse!" : "flex-row!"),
2086
+ "data-[clickable=true]:has-[>[data-slot=field-content]]:[&>:first-child]:mt-px",
2087
+ props.className
2088
+ ),
2089
+ children: props.children
2090
+ }
2091
+ );
2092
+ }
2093
+
2094
+ // src/component/field/field-vertical.tsx
2095
+ var import_tailwind_merge3 = require("tailwind-merge");
2096
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2097
+ function FieldVertical(props) {
2098
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2099
+ FieldBase,
2100
+ {
2101
+ ...props,
2102
+ orientation: VERTICAL,
2103
+ className: (0, import_tailwind_merge3.twMerge)(
2104
+ "gap-3 has-[>[data-slot=field-content]]:items-start",
2105
+ !props.isClickable && "[&>*]:w-full"
2106
+ ),
2107
+ children: props.children
2108
+ }
2109
+ );
2110
+ }
2111
+
2112
+ // src/component/field/field-horizontal.tsx
2113
+ var import_tailwind_merge4 = require("tailwind-merge");
2114
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2115
+ function FieldHorizontal(props) {
2116
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2117
+ FieldBase,
2118
+ {
2119
+ ...props,
2120
+ orientation: HORIZONTAL,
2121
+ className: (0, import_tailwind_merge4.twMerge)(
2122
+ "gap-2 md:flex-row md:gap-4",
2123
+ "[&>[data-slot=field-content]]:min-w-0 [&>[data-slot=field-content]]:flex-grow [&>[data-slot=field-content]]:self-start",
2124
+ "[&_[role=checkbox]]:mt-[1.5px]",
2125
+ props.isClickable && "md:flex-col",
2126
+ !props.isClickable && [
2127
+ "md:justify-between",
2128
+ "[&>*:not([data-slot=field-content])]:w-full",
2129
+ "[&>*:not([data-slot=field-content])]:md:w-1/2",
2130
+ "[&>*:not([data-slot=field-content])]:xl:w-2/5"
2131
+ ]
2132
+ ),
2133
+ children: props.children
2134
+ }
2135
+ );
2136
+ }
2137
+
2138
+ // src/component/field/field-group.tsx
2139
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2140
+ function FieldGroup(props) {
2141
+ const clickable = isClickable(props.field);
2142
+ const checkbox = isCheckbox(props.field);
2143
+ const reversed = buildReverse(props.field);
2144
+ if (props.orientation === VERTICAL) {
2145
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2146
+ FieldVertical,
2147
+ {
2148
+ disabled: props.disabled,
2149
+ errors: props.errors,
2150
+ isCheckbox: checkbox,
2151
+ isReversed: reversed,
2152
+ isClickable: clickable,
2153
+ children: props.children
2154
+ }
2155
+ );
2156
+ }
2157
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2158
+ FieldHorizontal,
2159
+ {
2160
+ disabled: props.disabled,
2161
+ errors: props.errors,
2162
+ isCheckbox: checkbox,
2163
+ isReversed: reversed,
2164
+ isClickable: clickable,
2165
+ children: props.children
2166
+ }
2167
+ );
2168
+ }
2169
+
2170
+ // src/component/input/input-base.tsx
2171
+ function InputBase(props) {
2172
+ if (!props.field.type) {
2173
+ return null;
2174
+ }
2175
+ const commonProps = buildCommon(props.field, props.disabled);
2176
+ const dataAttributes = buildDataAttributes(props.field);
2177
+ const ariaAttributes = buildAriaAttributes(props.field, props.errors);
2178
+ const field = {
2179
+ ...props.field,
2180
+ disabled: commonProps.disabled
2181
+ };
2182
+ return props.children({
2183
+ ariaAttributes,
2184
+ commonProps,
2185
+ dataAttributes,
2186
+ field,
2187
+ orientation: props.orientation
2188
+ });
2189
+ }
2190
+
2191
+ // src/component/field/field.tsx
2192
+ var import_tailwind_merge5 = require("tailwind-merge");
2042
2193
  var import_jsx_runtime22 = require("react/jsx-runtime");
2194
+ function Field(props) {
2195
+ const cols2 = props.field.advanced?.cols;
2196
+ const errors = props.field.name ? props.errors?.[props.field.name] : void 0;
2197
+ const { orientation } = mergeStyle(props.style, {
2198
+ orientation: buildOrientation(props.field)
2199
+ });
2200
+ const disabled = buildDisabled(props.field, props.disabled);
2201
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: (0, import_tailwind_merge5.twMerge)("flex flex-col gap-3", getSpan(cols2)), children: [
2202
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2203
+ FieldGroup,
2204
+ {
2205
+ disabled,
2206
+ errors,
2207
+ field: props.field,
2208
+ orientation,
2209
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2210
+ InputBase,
2211
+ {
2212
+ disabled,
2213
+ errors,
2214
+ field: props.field,
2215
+ orientation,
2216
+ children: props.children
2217
+ }
2218
+ )
2219
+ }
2220
+ ),
2221
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FieldError, { errors, name: props.field.name })
2222
+ ] });
2223
+ }
2224
+
2225
+ // src/client/component/field/field-with-error.tsx
2226
+ var import_jotai7 = require("jotai");
2227
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2228
+ function withError(Component) {
2229
+ const WithError = (props) => {
2230
+ const errors = (0, import_jotai7.useAtomValue)(reportInputErrorAtom(props.field.name));
2231
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2232
+ Component,
2233
+ {
2234
+ ...props,
2235
+ errors: errors ? { [props.field.name]: errors } : void 0
2236
+ }
2237
+ );
2238
+ };
2239
+ return WithError;
2240
+ }
2241
+
2242
+ // src/client/component/field/field-with-state.tsx
2243
+ var import_jotai8 = require("jotai");
2244
+ var import_jsx_runtime24 = require("react/jsx-runtime");
2245
+ function withState(Component) {
2246
+ const WithFieldState = (props) => {
2247
+ const fieldState = (0, import_jotai8.useAtomValue)(reportFieldStateAtom(props.field.name));
2248
+ const hidden = fieldState?.hidden ?? props.field.hidden ?? false;
2249
+ if (hidden) {
2250
+ return null;
2251
+ }
2252
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Component, { ...props, disabled: fieldState?.disabled ?? props.disabled });
2253
+ };
2254
+ return WithFieldState;
2255
+ }
2256
+
2257
+ // src/client/component/field/field.tsx
2258
+ var Field2 = withState(withError(Field));
2259
+
2260
+ // src/component/field/field-list-item.tsx
2261
+ var import_tailwind_merge6 = require("tailwind-merge");
2262
+ var import_jsx_runtime25 = require("react/jsx-runtime");
2263
+ function FieldListItem(props) {
2264
+ function handleRemove() {
2265
+ if (props.canRemove && props.onRemove) {
2266
+ props.onRemove(props.index);
2267
+ }
2268
+ }
2269
+ const removeButton = props.canRemove && props.onRemove != null && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2270
+ "button",
2271
+ {
2272
+ "aria-label": `Remove ${props.label} item ${props.index + 1}`,
2273
+ className: (0, import_tailwind_merge6.twMerge)(
2274
+ "rounded p-1 text-xl text-slate-400",
2275
+ "transition-colors duration-150",
2276
+ "hover:text-red-500",
2277
+ "focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:outline-none",
2278
+ "dark:text-slate-500 dark:hover:text-red-400"
2279
+ ),
2280
+ onClick: handleRemove,
2281
+ type: "button",
2282
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": "true", children: "\xD7" })
2283
+ }
2284
+ );
2285
+ if (props.isMultiField) {
2286
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "rounded-lg border border-slate-100 p-4 dark:border-slate-900", children: [
2287
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "mb-3 flex items-center justify-between", children: [
2288
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "text-sm font-medium text-slate-400 dark:text-slate-500", children: [
2289
+ props.label,
2290
+ " ",
2291
+ props.index + 1
2292
+ ] }),
2293
+ removeButton
2294
+ ] }),
2295
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Group, { children: props.children })
2296
+ ] });
2297
+ }
2298
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-start gap-2", children: [
2299
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Group, { children: props.children }),
2300
+ removeButton && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "shrink-0", children: removeButton })
2301
+ ] });
2302
+ }
2303
+
2304
+ // src/client/component/field/field-list.tsx
2305
+ var import_tailwind_merge7 = require("tailwind-merge");
2306
+
2307
+ // src/client/hook/use-field-list.ts
2308
+ var import_jotai9 = require("jotai");
2309
+ var import_react6 = require("react");
2310
+ function useFieldList(field, value) {
2311
+ const min2 = field.advanced?.length?.min ?? 1;
2312
+ const max2 = field.advanced?.length?.max ?? Infinity;
2313
+ const [items, setItems] = (0, import_react6.useState)(
2314
+ () => getInitialList(field, value)
2315
+ );
2316
+ const nextId = (0, import_react6.useRef)(items.length);
2317
+ const itemsRef = (0, import_react6.useRef)(items);
2318
+ itemsRef.current = items;
2319
+ const setValues = (0, import_jotai9.useSetAtom)(valueAtom);
2320
+ const addItem = (0, import_react6.useCallback)(() => {
2321
+ setItems((previous) => {
2322
+ if (previous.length >= max2) {
2323
+ return previous;
2324
+ }
2325
+ const id = nextId.current++;
2326
+ return [...previous, id];
2327
+ });
2328
+ }, [max2]);
2329
+ const handleRemove = (0, import_react6.useCallback)(
2330
+ (index) => {
2331
+ if (items.length <= min2) {
2332
+ return;
2333
+ }
2334
+ const stableId = items[index];
2335
+ setItems((previous) => {
2336
+ if (previous.length <= min2) {
2337
+ return previous;
2338
+ }
2339
+ return previous.filter((_, i) => i !== index);
2340
+ });
2341
+ setValues((current) => {
2342
+ const next = { ...current };
2343
+ const prefix = `${field.name}.`;
2344
+ const leafNames = [];
2345
+ for (const row of field.fields) {
2346
+ if (isColumn(row)) {
2347
+ for (const columnField of row.fields) {
2348
+ leafNames.push(columnField.name);
2349
+ }
2350
+ } else {
2351
+ leafNames.push(row.name);
2352
+ }
2353
+ }
2354
+ for (const name of leafNames) {
2355
+ delete next[`${prefix}${stableId}.${name}`];
2356
+ }
2357
+ return next;
2358
+ });
2359
+ },
2360
+ [field.name, field.fields, items, min2, setValues]
2361
+ );
2362
+ const canAdd = items.length < max2;
2363
+ const canRemove = items.length > min2;
2364
+ return [items, addItem, handleRemove, canAdd, canRemove, max2];
2365
+ }
2366
+
2367
+ // src/client/component/field/field-list.tsx
2368
+ var import_jsx_runtime26 = require("react/jsx-runtime");
2369
+ function FieldList(props) {
2370
+ const [items, addItem, handleRemove, canAdd, canRemove, max2] = useFieldList(
2371
+ props.field,
2372
+ props.value
2373
+ );
2374
+ const label = getLabel(props.field);
2375
+ const action = props.field.advanced?.action ?? "Add item";
2376
+ const hasLimit = max2 !== Infinity;
2377
+ const isMultiField = isMultiFieldList(props.field);
2378
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
2379
+ items.map((key, index) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2380
+ FieldListItem,
2381
+ {
2382
+ canRemove,
2383
+ index,
2384
+ isMultiField,
2385
+ label,
2386
+ onRemove: handleRemove,
2387
+ children: props.children(key)
2388
+ },
2389
+ key
2390
+ )),
2391
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2392
+ "button",
2393
+ {
2394
+ "aria-disabled": !canAdd,
2395
+ "aria-label": hasLimit ? `${action}, ${items.length} of ${max2}` : action,
2396
+ className: (0, import_tailwind_merge7.twMerge)(
2397
+ "flex w-full items-center gap-1.5 rounded py-1",
2398
+ "text-sm font-medium text-slate-500",
2399
+ "transition-colors duration-150",
2400
+ "hover:text-slate-800",
2401
+ "focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 focus-visible:outline-none",
2402
+ "dark:text-slate-400 dark:hover:text-slate-200",
2403
+ !canAdd && "cursor-not-allowed opacity-50 hover:text-slate-500 dark:hover:text-slate-400"
2404
+ ),
2405
+ disabled: !canAdd,
2406
+ onClick: addItem,
2407
+ type: "button",
2408
+ children: [
2409
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": "true", children: "+" }),
2410
+ action,
2411
+ hasLimit && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2412
+ "span",
2413
+ {
2414
+ "aria-hidden": "true",
2415
+ className: "ml-auto text-slate-400 tabular-nums dark:text-slate-500",
2416
+ children: [
2417
+ items.length,
2418
+ " / ",
2419
+ max2
2420
+ ]
2421
+ }
2422
+ )
2423
+ ]
2424
+ }
2425
+ )
2426
+ ] });
2427
+ }
2428
+
2429
+ // src/component/list.tsx
2430
+ var import_jsx_runtime27 = require("react/jsx-runtime");
2431
+ function List(props) {
2432
+ const empty = Array.isArray(props.field.fields) && props.field.fields.length === 0;
2433
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2434
+ FieldSetBase,
2435
+ {
2436
+ description: props.field.description,
2437
+ empty,
2438
+ id: props.field.name,
2439
+ title: props.field.label,
2440
+ children: props.children
2441
+ }
2442
+ );
2443
+ }
2444
+
2445
+ // src/client/component/guard/list-guard.tsx
2446
+ var import_jsx_runtime28 = require("react/jsx-runtime");
2447
+ function ListGuard({ children, field, value }) {
2448
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(VisibilityGuard, { container: field, fields: field.fields, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(List, { field, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(FieldList, { field, value, children }) }) });
2449
+ }
2450
+
2451
+ // src/component/column.tsx
2452
+ var import_tailwind_merge8 = require("tailwind-merge");
2453
+ var import_jsx_runtime29 = require("react/jsx-runtime");
2043
2454
  function Column(props) {
2044
2455
  const cols2 = getColumn(props.column?.advanced?.cols);
2045
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex w-full flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: (0, import_tailwind_merge6.twMerge)("grid grid-cols-1 gap-3 sm:gap-4", cols2), children: props.children }) });
2456
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex w-full flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: (0, import_tailwind_merge8.twMerge)("grid grid-cols-1 gap-3 sm:gap-4", cols2), children: props.children }) });
2046
2457
  }
2047
2458
 
2048
2459
  // src/component/slot/slot-base.tsx
2049
- var import_react6 = require("react");
2050
- var import_jsx_runtime23 = require("react/jsx-runtime");
2460
+ var import_react7 = require("react");
2461
+
2462
+ // src/component/slot/slot-list.tsx
2463
+ var import_jsx_runtime30 = require("react/jsx-runtime");
2464
+ function SlotList(props) {
2465
+ const fields = Array.isArray(props.field.fields) ? props.field.fields.map((field) => {
2466
+ if (isField(field)) {
2467
+ return {
2468
+ ...field,
2469
+ name: `${props.field.name}.${props.index}.${field.name}`
2470
+ };
2471
+ }
2472
+ if (isColumn(field)) {
2473
+ return {
2474
+ ...field,
2475
+ fields: field.fields.map((columnField) => ({
2476
+ ...columnField,
2477
+ name: `${props.field.name}.${props.index}.${columnField.name}`
2478
+ }))
2479
+ };
2480
+ }
2481
+ return field;
2482
+ }) : [];
2483
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2484
+ SlotBase,
2485
+ {
2486
+ children: props.children,
2487
+ components: props.components,
2488
+ disabled: props.disabled,
2489
+ fields,
2490
+ style: props.style,
2491
+ value: props.value
2492
+ }
2493
+ );
2494
+ }
2495
+
2496
+ // src/component/slot/slot-base.tsx
2497
+ var import_jsx_runtime31 = require("react/jsx-runtime");
2051
2498
  function SlotBase(props) {
2052
- const { column: Column2, field: Field3 } = props.components;
2053
- return prepare(props.fields).map((field, index) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react6.Fragment, { children: [
2054
- isColumn(field) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Column2, { column: field, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SlotBase, { ...props, fields: field.fields }) }),
2055
- isField(field) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Field3, { disabled: props.disabled, field, style: props.style, children: props.children })
2499
+ const { field: Field3, list: List2 } = props.components;
2500
+ return prepare(props.fields).map((field, index) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_react7.Fragment, { children: [
2501
+ isColumn(field) && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Column, { column: field, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(SlotBase, { ...props, fields: field.fields }) }),
2502
+ isField(field) && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Field3, { disabled: props.disabled, field, style: props.style, children: props.children }),
2503
+ isList(field) && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(List2, { field, value: props.value, children: (index2) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2504
+ SlotList,
2505
+ {
2506
+ children: props.children,
2507
+ components: props.components,
2508
+ disabled: props.disabled,
2509
+ field,
2510
+ index: index2,
2511
+ style: props.style,
2512
+ value: props.value
2513
+ }
2514
+ ) })
2056
2515
  ] }, index));
2057
2516
  }
2058
2517
 
2059
2518
  // src/component/slot/slot-create.tsx
2060
- var import_jsx_runtime24 = require("react/jsx-runtime");
2061
- function createSlot(Field3) {
2062
- const CreateSlot = (props) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SlotBase, { ...props, components: { column: Column, field: Field3 } });
2519
+ var import_jsx_runtime32 = require("react/jsx-runtime");
2520
+ function createSlot(components) {
2521
+ const CreateSlot = (props) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(SlotBase, { ...props, components });
2063
2522
  return CreateSlot;
2064
2523
  }
2065
2524
 
2525
+ // src/client/component/slot/slot.tsx
2526
+ var Slot = createSlot({ field: Field2, list: ListGuard });
2527
+
2066
2528
  // src/client/hook/use-form-action.ts
2067
- var import_jotai8 = require("jotai");
2068
- var import_react7 = require("react");
2529
+ var import_jotai10 = require("jotai");
2530
+ var import_react8 = require("react");
2069
2531
  function useFormState(getSchema2, action, options) {
2070
- const { onSuccess, preserveValues = false, validation = true } = options ?? {};
2071
- const setError = (0, import_jotai8.useSetAtom)(reportErrorAtom);
2072
- const clearValues = (0, import_jotai8.useSetAtom)(clearAllValueAtom);
2532
+ const {
2533
+ onSuccess,
2534
+ preserveValues = false,
2535
+ validation = true,
2536
+ translations
2537
+ } = options ?? {};
2538
+ const setError = (0, import_jotai10.useSetAtom)(reportErrorAtom);
2539
+ const clearValues = (0, import_jotai10.useSetAtom)(clearAllValueAtom);
2073
2540
  const initialState = {
2074
2541
  data: null,
2075
2542
  error: null,
2076
2543
  success: false
2077
2544
  };
2078
- const [state, formAction, isPending] = (0, import_react7.useActionState)(
2545
+ const [state, formAction, isPending] = (0, import_react8.useActionState)(
2079
2546
  async (prevState, formData) => {
2080
2547
  const [schemas, fields] = getSchema2();
2081
- const schema = buildSchema(schemas, fields, options?.translations);
2548
+ const schema = buildSchema(schemas, fields, translations);
2082
2549
  if (validation === false) {
2083
2550
  if (action) {
2084
2551
  return await action(formData, schema);
@@ -2089,33 +2556,43 @@ function useFormState(getSchema2, action, options) {
2089
2556
  const validated = schema.safeParse(form);
2090
2557
  if (!validated.success) {
2091
2558
  const errors = flatten(validated.error);
2092
- (0, import_react7.startTransition)(() => {
2559
+ (0, import_react8.startTransition)(() => {
2093
2560
  setError(errors);
2094
2561
  });
2095
2562
  return failure(form, {
2096
- description: "Please correct the errors and try again.",
2563
+ description: translate(
2564
+ "Please correct the errors and try again.",
2565
+ translations
2566
+ ),
2097
2567
  details: [],
2098
- title: "There were validation errors submitting the form."
2568
+ title: translate(
2569
+ "There were validation errors submitting the form.",
2570
+ translations
2571
+ )
2099
2572
  });
2100
2573
  }
2574
+ const unflattened = unflatten(form);
2101
2575
  if (action) {
2102
2576
  try {
2103
- const result = await action(form, schema);
2577
+ const result = await action(unflattened, schema);
2104
2578
  if (!result.success) {
2105
- return failure(form, result.error);
2579
+ return failure(unflattened, result.error);
2106
2580
  }
2107
2581
  onSuccess?.(result.data);
2108
2582
  if (!preserveValues) {
2109
- (0, import_react7.startTransition)(() => {
2583
+ (0, import_react8.startTransition)(() => {
2110
2584
  clearValues();
2111
2585
  });
2112
2586
  }
2113
- return success(form, preserveValues);
2587
+ return success(unflattened, preserveValues);
2114
2588
  } catch (error) {
2115
2589
  logger.error("Error executing form action:", error);
2116
- return failure(form, {
2117
- title: "An unexpected error occurred submitting the form.",
2118
- details: buildError(error)
2590
+ return failure(unflattened, {
2591
+ title: translate(
2592
+ "An unexpected error occurred submitting the form.",
2593
+ translations
2594
+ ),
2595
+ details: buildError(error, translations)
2119
2596
  });
2120
2597
  }
2121
2598
  }
@@ -2125,9 +2602,11 @@ function useFormState(getSchema2, action, options) {
2125
2602
  );
2126
2603
  return [formAction, state, isPending];
2127
2604
  }
2128
- function buildError(error) {
2129
- const detail = error instanceof Error ? error.message : ["Unknown error"];
2130
- return Array.isArray(detail) ? detail : [detail];
2605
+ function buildError(error, translations) {
2606
+ if (error instanceof Error) {
2607
+ return [error.message];
2608
+ }
2609
+ return [translate("Unknown error", translations)];
2131
2610
  }
2132
2611
  function success(value, preserveValues = false) {
2133
2612
  const data = preserveValues ? value : null;
@@ -2146,16 +2625,16 @@ function failure(value, error) {
2146
2625
  }
2147
2626
 
2148
2627
  // src/client/hook/use-schema.ts
2149
- var import_react9 = require("react");
2628
+ var import_react10 = require("react");
2150
2629
 
2151
2630
  // src/client/hook/use-store.ts
2152
- var import_react8 = require("react");
2153
- var import_jotai9 = require("jotai");
2631
+ var import_react9 = require("react");
2632
+ var import_jotai11 = require("jotai");
2154
2633
  function useStore2() {
2155
- const clearErrors = (0, import_jotai9.useSetAtom)(clearInputErrorAtom);
2156
- const clearSources = (0, import_jotai9.useSetAtom)(clearInputSourceAtom);
2157
- const clearValues = (0, import_jotai9.useSetAtom)(clearInputValueAtom);
2158
- return (0, import_react8.useCallback)(
2634
+ const clearErrors = (0, import_jotai11.useSetAtom)(clearInputErrorAtom);
2635
+ const clearValues = (0, import_jotai11.useSetAtom)(clearInputValueAtom);
2636
+ const clearSources = (0, import_jotai11.useSetAtom)(clearInputSourceAtom);
2637
+ return (0, import_react9.useCallback)(
2159
2638
  (names) => {
2160
2639
  const target = Array.isArray(names) ? names : [names];
2161
2640
  clearErrors(target);
@@ -2169,42 +2648,34 @@ function useStore2() {
2169
2648
  // src/client/hook/use-schema.ts
2170
2649
  function useSchema() {
2171
2650
  const clear = useStore2();
2172
- const schemaRef = (0, import_react9.useRef)({});
2173
- const fieldsRef = (0, import_react9.useRef)([]);
2174
- const pendingUnmounts = (0, import_react9.useRef)(/* @__PURE__ */ new Set());
2175
- const onMount = (0, import_react9.useCallback)((name, schema, field) => {
2651
+ const schemaRef = (0, import_react10.useRef)({});
2652
+ const fieldsRef = (0, import_react10.useRef)([]);
2653
+ const onMount = (0, import_react10.useCallback)((name, schema, field) => {
2176
2654
  if (!(name in schemaRef.current)) {
2177
2655
  schemaRef.current[name] = schema;
2178
2656
  fieldsRef.current.push(field);
2179
2657
  }
2180
2658
  }, []);
2181
- const onUnmount = (0, import_react9.useCallback)(
2659
+ const onUnmount = (0, import_react10.useCallback)(
2182
2660
  (name) => {
2183
2661
  if (schemaRef.current[name]) {
2184
2662
  delete schemaRef.current[name];
2185
2663
  fieldsRef.current = fieldsRef.current.filter((field) => {
2186
2664
  return field.name !== name;
2187
2665
  });
2188
- pendingUnmounts.current.add(name);
2189
- (0, import_react9.startTransition)(() => {
2190
- if (pendingUnmounts.current.size > 0) {
2191
- clear(Array.from(pendingUnmounts.current));
2192
- pendingUnmounts.current.clear();
2193
- }
2194
- });
2666
+ clear([name]);
2195
2667
  }
2196
2668
  },
2197
2669
  [clear]
2198
2670
  );
2199
- function getSchema2() {
2671
+ const getSchema2 = (0, import_react10.useCallback)(() => {
2200
2672
  return [schemaRef.current, fieldsRef.current];
2201
- }
2673
+ }, []);
2202
2674
  return [getSchema2, onMount, onUnmount];
2203
2675
  }
2204
2676
 
2205
2677
  // src/client/component/form-content.tsx
2206
- var import_jsx_runtime25 = require("react/jsx-runtime");
2207
- var Slot = createSlot(Field2);
2678
+ var import_jsx_runtime33 = require("react/jsx-runtime");
2208
2679
  function FormContent(props) {
2209
2680
  const translations = props.translations?.[props.lang ?? ""];
2210
2681
  const [schema, onMount, onUnmount] = useSchema();
@@ -2215,8 +2686,8 @@ function FormContent(props) {
2215
2686
  });
2216
2687
  const isShowingError = props.config.validation.showError && !state.success && state.error;
2217
2688
  const value = state.data ?? props.value;
2218
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
2219
- isShowingError && renderIfExists(props.config.alert, (Alert) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "mb-4 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2689
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
2690
+ isShowingError && renderIfExists(props.config.alert, (Alert) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mb-4 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2220
2691
  Alert,
2221
2692
  {
2222
2693
  description: state.error?.description,
@@ -2224,7 +2695,7 @@ function FormContent(props) {
2224
2695
  title: state.error.title
2225
2696
  }
2226
2697
  ) })),
2227
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2698
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2228
2699
  Form,
2229
2700
  {
2230
2701
  action,
@@ -2235,27 +2706,36 @@ function FormContent(props) {
2235
2706
  noValidate: true,
2236
2707
  readOnly: props.readOnly,
2237
2708
  sections: props.sections,
2238
- children: ({ disabled, fields }) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Slot, { disabled, fields, style: props.config.style, children: (internal) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2239
- Input,
2709
+ children: ({ disabled, fields }) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2710
+ Slot,
2240
2711
  {
2241
- ...internal,
2242
- config: props.config,
2243
- context: props.context,
2244
- onMount,
2245
- onUnmount,
2246
- onValueChange: props.onValueChange,
2247
- translations,
2248
- value
2712
+ disabled,
2713
+ fields,
2714
+ style: props.config.style,
2715
+ value,
2716
+ children: (internal) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2717
+ Input,
2718
+ {
2719
+ ...internal,
2720
+ config: props.config,
2721
+ context: props.context,
2722
+ onMount,
2723
+ onUnmount,
2724
+ onValueChange: props.onValueChange,
2725
+ translations,
2726
+ value
2727
+ }
2728
+ )
2249
2729
  }
2250
- ) })
2730
+ )
2251
2731
  }
2252
2732
  )
2253
2733
  ] });
2254
2734
  }
2255
2735
 
2256
2736
  // src/client/component/form.tsx
2257
- var import_jotai10 = require("jotai");
2258
- var import_jsx_runtime26 = require("react/jsx-runtime");
2737
+ var import_jotai12 = require("jotai");
2738
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2259
2739
  function Form2(props) {
2260
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_jotai10.Provider, { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(FormContent, { ...props }) });
2740
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_jotai12.Provider, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(FormContent, { ...props }) });
2261
2741
  }