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
@@ -1,15 +1,31 @@
1
- // src/component/field/field-error.tsx
1
+ // src/component/control.tsx
2
2
  import { jsx } from "react/jsx-runtime";
3
- function FieldError(props) {
4
- if (!props.errors || props.errors.length === 0) {
5
- return null;
6
- }
7
- return /* @__PURE__ */ jsx(
8
- "ul",
3
+ function Control(props) {
4
+ const content = typeof props.children === "function" ? props.children({ isPending: props.isPending }) : props.children;
5
+ return /* @__PURE__ */ jsx("div", { "data-slot": "field-control", className: "w-full", children: content });
6
+ }
7
+
8
+ // src/component/field/field-set-advanced.tsx
9
+ import { Activity, useCallback, useState } from "react";
10
+
11
+ // src/component/chevron-icon.tsx
12
+ import { jsx as jsx2 } from "react/jsx-runtime";
13
+ function ChevronIcon(props) {
14
+ return /* @__PURE__ */ jsx2(
15
+ "svg",
9
16
  {
10
- className: "text-sm text-red-600 dark:text-red-500",
11
- id: props.name ? `${props.name}-error` : void 0,
12
- children: props.errors?.map((error, index) => /* @__PURE__ */ jsx("li", { children: error }, props.name ? `${props.name}-error-${index}` : index))
17
+ xmlns: "http://www.w3.org/2000/svg",
18
+ viewBox: "0 0 20 20",
19
+ fill: "currentColor",
20
+ className: `size-4 transition-transform duration-200 ${props.expanded ? "rotate-90" : ""}`,
21
+ children: /* @__PURE__ */ jsx2(
22
+ "path",
23
+ {
24
+ fillRule: "evenodd",
25
+ 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",
26
+ clipRule: "evenodd"
27
+ }
28
+ )
13
29
  }
14
30
  );
15
31
  }
@@ -21,10 +37,12 @@ var INPUT_NUMBER = "input/number";
21
37
  var TEXTAREA = "textarea";
22
38
  var RADIO = "radio";
23
39
  var CHECKBOX = "checkbox";
40
+ var LIST = "list";
24
41
  var SELECT = "select";
25
42
  var SELECT_MONTH = "select/month";
26
43
  var SELECT_YEAR = "select/year";
27
44
  var COLUMN = "column";
45
+ var FIELDS = "fields";
28
46
  var LABEL = "label";
29
47
  var VALUE = "value";
30
48
  var OPTIONS = "options";
@@ -49,6 +67,7 @@ var STATE = "state";
49
67
  var COMMON_URL = "http://luna.internal";
50
68
  var VERTICAL = "vertical";
51
69
  var HORIZONTAL = "horizontal";
70
+ var TYPE = "type";
52
71
 
53
72
  // ../luna-core/src/util/is-type.ts
54
73
  function isObject(value) {
@@ -58,7 +77,7 @@ function isEmpty(value) {
58
77
  return value === null || value === void 0 || value === "";
59
78
  }
60
79
  function isValue(value) {
61
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
80
+ return isString(value) || typeof value === "number" || isBoolean(value);
62
81
  }
63
82
  function isString(value) {
64
83
  return typeof value === "string";
@@ -66,9 +85,13 @@ function isString(value) {
66
85
  function isDataSource(value) {
67
86
  return isObject(value) && "url" in value;
68
87
  }
88
+ function isBoolean(value) {
89
+ return typeof value === "boolean";
90
+ }
69
91
 
70
92
  // ../luna-core/src/util/extract.ts
71
93
  var REGEX_TYPE = /[^/]+$/;
94
+ var REGEX_NUMERIC = /^\d+$/;
72
95
  function getEntity(selected, collection = [], entity = VALUE) {
73
96
  if (Array.isArray(collection)) {
74
97
  return collection.find((item) => {
@@ -78,6 +101,7 @@ function getEntity(selected, collection = [], entity = VALUE) {
78
101
  }
79
102
  }) ?? { value: selected };
80
103
  }
104
+ return { value: selected };
81
105
  }
82
106
  function getCurrentValue(value, entity = VALUE) {
83
107
  if (value !== null && value !== void 0) {
@@ -162,6 +186,46 @@ function getFormData(formData) {
162
186
  }
163
187
  return data;
164
188
  }
189
+ function unflatten(data) {
190
+ const result = {};
191
+ for (const key in data) {
192
+ const parts = key.split(".");
193
+ if (parts.length === 1) {
194
+ result[key] = data[key];
195
+ continue;
196
+ }
197
+ let current = result;
198
+ for (let i = 0; i < parts.length - 1; i++) {
199
+ const part = parts[i];
200
+ const next = parts[i + 1];
201
+ const isNextIndex = REGEX_NUMERIC.test(next);
202
+ if (!(part in current)) {
203
+ current[part] = isNextIndex ? [] : {};
204
+ }
205
+ current = current[part];
206
+ }
207
+ const last = parts[parts.length - 1];
208
+ current[last] = data[key];
209
+ }
210
+ compactArrays(result);
211
+ return result;
212
+ }
213
+ function compactArrays(obj) {
214
+ for (const key in obj) {
215
+ const value = obj[key];
216
+ if (Array.isArray(value)) {
217
+ const compacted = value.filter((item) => item !== void 0);
218
+ compacted.forEach((item) => {
219
+ if (item !== null && typeof item === "object" && !Array.isArray(item)) {
220
+ compactArrays(item);
221
+ }
222
+ });
223
+ obj[key] = compacted;
224
+ } else if (value !== null && typeof value === "object") {
225
+ compactArrays(value);
226
+ }
227
+ }
228
+ }
165
229
 
166
230
  // ../luna-core/src/util/string.ts
167
231
  var REGEX_MARKDOWN_LINK = /\[([^\]]+)\]\(([^)]+)\)/g;
@@ -183,6 +247,9 @@ function interpolate(template, values = {}) {
183
247
  }
184
248
  return template;
185
249
  }
250
+ function interpolateIfNeeded(template, values = {}) {
251
+ return isInterpolated(template) ? interpolate(template, values) : template;
252
+ }
186
253
  function isInterpolated(template) {
187
254
  if (isString(template)) {
188
255
  return /{([^}]+)}/.test(template);
@@ -259,7 +326,7 @@ function handleProxyEvent(events = [], callback) {
259
326
  const values = [];
260
327
  const sources = [];
261
328
  const states = [];
262
- events.forEach((event) => {
329
+ for (const event of events) {
263
330
  if (event.action === VALUE) {
264
331
  values.push(event);
265
332
  }
@@ -269,17 +336,17 @@ function handleProxyEvent(events = [], callback) {
269
336
  if (event.action === STATE) {
270
337
  states.push(event);
271
338
  }
272
- });
339
+ }
273
340
  callback({ sources, states, values });
274
341
  }
275
342
 
276
343
  // ../luna-core/src/handle/source-event.ts
277
344
  function handleSourceEvent(selected = null, events = [], setSource) {
278
- events.forEach((event) => {
345
+ for (const event of events) {
279
346
  const { target, source } = event;
280
347
  if (!selected) {
281
348
  setSource(target, void 0);
282
- return;
349
+ continue;
283
350
  }
284
351
  if (isDataSource(source)) {
285
352
  const newUrl = interpolate(source.url, selected);
@@ -290,7 +357,7 @@ function handleSourceEvent(selected = null, events = [], setSource) {
290
357
  body: newBody
291
358
  });
292
359
  }
293
- });
360
+ }
294
361
  }
295
362
 
296
363
  // ../luna-core/src/util/operator.ts
@@ -363,31 +430,39 @@ function lte(current, value) {
363
430
 
364
431
  // ../luna-core/src/handle/state-event.ts
365
432
  function handleStateEvent(selected = null, events = [], setState) {
366
- events.forEach((event) => {
433
+ for (const event of events) {
367
434
  const { target, state, when } = event;
435
+ const targets = Array.isArray(target) ? target : [target];
368
436
  if (!selected) {
369
- setState(target);
370
- return;
437
+ setState(targets);
438
+ continue;
371
439
  }
440
+ logger.info(`Selected value for state event: ${JSON.stringify(selected)}, evaluating condition: ${JSON.stringify(when)}`);
372
441
  const matches = evaluateCondition(selected, when);
373
- setState(target, matches ? state : void 0);
374
- });
442
+ logger.info(`Condition evaluated to: ${matches}, setting state for targets: ${targets.join(", ")}`);
443
+ setState(targets, matches ? state : void 0);
444
+ }
375
445
  }
376
446
  function evaluateCondition(selected, when) {
377
447
  if (when === void 0) {
378
448
  return true;
379
449
  }
380
450
  if (isString(when)) {
381
- return getValue2(selected, "value") === when;
451
+ logger.info(`Evaluating string condition: ${when} against selected value: ${JSON.stringify(selected)} using VALUE field`);
452
+ logger.info(`Extracted value for comparison: ${JSON.stringify(getValue2(selected, VALUE))}`);
453
+ logger.info(`Comparison result: ${getValue2(selected, VALUE) === when}`);
454
+ return getValue2(selected, VALUE) === when;
455
+ }
456
+ if (isBoolean(when)) {
457
+ return Boolean(getValue2(selected, VALUE)) === when;
382
458
  }
383
459
  if (Array.isArray(when)) {
384
- const current = getValue2(selected, "value");
385
- return when.includes(String(current));
460
+ return when.includes(String(getValue2(selected, VALUE)));
386
461
  }
387
462
  return evaluateOperator(selected, when);
388
463
  }
389
464
  function evaluateOperator(selected = null, condition) {
390
- const current = getValue2(selected, condition.field ?? "value");
465
+ const current = getValue2(selected, condition.field ?? VALUE);
391
466
  const { operator = "eq", value } = condition;
392
467
  const operation = operators[operator];
393
468
  if (operation) {
@@ -397,18 +472,18 @@ function evaluateOperator(selected = null, condition) {
397
472
  }
398
473
  function getValue2(selected, field) {
399
474
  if (isObject(selected)) {
400
- return field.includes(".") ? extract(selected, field) : selected[field];
475
+ return extract(selected, field);
401
476
  }
402
477
  return selected;
403
478
  }
404
479
 
405
480
  // ../luna-core/src/handle/value-event.ts
406
481
  function handleValueEvent(selected = null, events = [], setValue) {
407
- events.forEach((event) => {
408
- Object.entries(event.value).forEach(([target, value]) => {
482
+ for (const event of events) {
483
+ for (const [target, value] of Object.entries(event.value)) {
409
484
  setValue(target, selected ? interpolate(value, selected) : void 0);
410
- });
411
- });
485
+ }
486
+ }
412
487
  }
413
488
 
414
489
  // ../luna-core/src/util/is-input.ts
@@ -430,11 +505,14 @@ var isNumber = createTypeChecker(INPUT_NUMBER, TYPE_NUMBER);
430
505
  function isClickable(field) {
431
506
  return isRadio(field) || isCheckbox(field);
432
507
  }
508
+ function isList(slot) {
509
+ return slot.type === LIST;
510
+ }
433
511
  function isColumn(slot) {
434
512
  return slot.type === COLUMN;
435
513
  }
436
514
  function isField(slot) {
437
- return slot.type !== COLUMN;
515
+ return slot.type !== COLUMN && slot.type !== LIST;
438
516
  }
439
517
  function isOptions(field) {
440
518
  return isSelect(field) || isRadio(field);
@@ -699,7 +777,7 @@ function prepareInputValue(field, value) {
699
777
  var REGEX_REF = /^#\/definition\//;
700
778
  function prepare(base = [], definition) {
701
779
  const resolved = resolveRefs(base, definition);
702
- return Array.isArray(resolved) ? resolved.sort((a, b) => getOrder(a) - getOrder(b)) : [];
780
+ return Array.isArray(resolved) ? resolved.filter(filter).sort((a, b) => getOrder(a) - getOrder(b)) : [];
703
781
  }
704
782
  function resolveRefs(base, definition, cache = /* @__PURE__ */ new Map(), visited = /* @__PURE__ */ new WeakSet()) {
705
783
  if (!isDefinition(definition) || !base || typeof base !== "object") {
@@ -740,6 +818,15 @@ function getOrder(item) {
740
818
  function isDefinition(definition) {
741
819
  return definition !== void 0 && isObject(definition) && Object.keys(definition).length > 0;
742
820
  }
821
+ function filter(base) {
822
+ if (TYPE in base) {
823
+ return true;
824
+ }
825
+ if (Array.isArray(base[FIELDS])) {
826
+ return base[FIELDS].length > 0;
827
+ }
828
+ return true;
829
+ }
743
830
 
744
831
  // ../luna-core/src/util/attributes.ts
745
832
  function getPrefixedAttributes(prefix, record) {
@@ -787,6 +874,31 @@ function getSpan(value) {
787
874
  }
788
875
  }
789
876
 
877
+ // ../luna-core/src/util/list.ts
878
+ function getInitialCount(list, value) {
879
+ const min2 = list.advanced?.length?.min ?? 1;
880
+ if (value) {
881
+ const data = extract(value, list.name);
882
+ if (Array.isArray(data)) {
883
+ return Math.max(data.length, min2);
884
+ }
885
+ }
886
+ return Math.max(min2, 0);
887
+ }
888
+ function isMultiFieldList(list) {
889
+ if (!Array.isArray(list.fields) || list.fields.length === 0) {
890
+ return false;
891
+ }
892
+ return list.fields.length > 1 || list.fields[0].type === COLUMN;
893
+ }
894
+ function getInitialList(list, value) {
895
+ const count = getInitialCount(list, value);
896
+ return Array.from({ length: count }, (_, index) => index);
897
+ }
898
+ function getLabel(list) {
899
+ return list.label ?? list.name;
900
+ }
901
+
790
902
  // ../luna-core/src/util/schema.ts
791
903
  import { z } from "zod";
792
904
 
@@ -1079,161 +1191,140 @@ function mergeStyle(globalStyle, localStyle) {
1079
1191
  return { ...globalStyle, ...localStyle };
1080
1192
  }
1081
1193
 
1082
- // src/component/field/field-base.tsx
1083
- import { twMerge } from "tailwind-merge";
1084
- import { jsx as jsx2 } from "react/jsx-runtime";
1085
- function FieldBase(props) {
1086
- const errors = props.errors && props.errors.length > 0;
1087
- return /* @__PURE__ */ jsx2(
1088
- "div",
1089
- {
1090
- "data-slot": "field",
1091
- "data-clickable": props.isClickable ? "true" : "false",
1092
- ...errors && { [DATA_INVALID]: "true" },
1093
- ...props.disabled && { [DATA_READONLY]: "true" },
1094
- "data-orientation": props.orientation,
1095
- className: twMerge(
1096
- "group flex w-full flex-col items-center data-[invalid=true]:text-red-600 data-[invalid=true]:dark:text-red-500",
1097
- "data-[clickable=true]:items-start",
1098
- props.isCheckbox && (props.isReversed ? "flex-row-reverse!" : "flex-row!"),
1099
- "data-[clickable=true]:has-[>[data-slot=field-content]]:[&>:first-child]:mt-px",
1100
- props.className
1101
- ),
1102
- children: props.children
1194
+ // src/lib/string.tsx
1195
+ import { jsx as jsx3 } from "react/jsx-runtime";
1196
+ function formatMarkdown2(text) {
1197
+ return formatMarkdown(
1198
+ text,
1199
+ (index, url, text2) => {
1200
+ return /* @__PURE__ */ jsx3(
1201
+ "a",
1202
+ {
1203
+ className: "underline",
1204
+ href: url,
1205
+ rel: "noopener noreferrer",
1206
+ target: "_blank",
1207
+ children: text2
1208
+ },
1209
+ `${url}-${index}`
1210
+ );
1103
1211
  }
1104
1212
  );
1105
1213
  }
1106
1214
 
1107
- // src/component/field/field-vertical.tsx
1108
- import { twMerge as twMerge2 } from "tailwind-merge";
1109
- import { jsx as jsx3 } from "react/jsx-runtime";
1110
- function FieldVertical(props) {
1111
- return /* @__PURE__ */ jsx3(
1112
- FieldBase,
1215
+ // src/component/field/field-set-advanced.tsx
1216
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
1217
+ function FieldSetAdvanced(props) {
1218
+ const { fields = [] } = props.section;
1219
+ const [isOpen, setIsOpen] = useState(false);
1220
+ const handleOpen = useCallback(() => setIsOpen((previous) => !previous), []);
1221
+ return /* @__PURE__ */ jsxs(
1222
+ "fieldset",
1113
1223
  {
1114
- ...props,
1115
- orientation: VERTICAL,
1116
- className: twMerge2(
1117
- "gap-3 has-[>[data-slot=field-content]]:items-start",
1118
- !props.isClickable && "[&>*]:w-full"
1119
- ),
1120
- children: props.children
1224
+ "data-slot": "field-set",
1225
+ "data-advanced": "true",
1226
+ "data-expanded": isOpen,
1227
+ "data-empty": fields.length === 0,
1228
+ className: "flex flex-col",
1229
+ id: props.section.id?.toString(),
1230
+ children: [
1231
+ /* @__PURE__ */ jsx4("legend", { children: /* @__PURE__ */ jsxs(
1232
+ "button",
1233
+ {
1234
+ className: "flex cursor-pointer items-center gap-2 text-base font-medium text-slate-600 dark:text-slate-400",
1235
+ onClick: handleOpen,
1236
+ type: "button",
1237
+ children: [
1238
+ /* @__PURE__ */ jsx4(ChevronIcon, { expanded: isOpen }),
1239
+ /* @__PURE__ */ jsx4("span", { children: formatMarkdown2(props.section.title) })
1240
+ ]
1241
+ }
1242
+ ) }),
1243
+ /* @__PURE__ */ jsx4(Activity, { mode: isOpen ? "visible" : "hidden", children: /* @__PURE__ */ jsxs(
1244
+ "div",
1245
+ {
1246
+ className: "mt-3 ml-1.5 flex flex-col gap-4 border-l-2 border-slate-300 pl-4 dark:border-slate-600",
1247
+ "data-slot": "field-set-content",
1248
+ children: [
1249
+ props.section.description && /* @__PURE__ */ jsx4("p", { className: "text-sm leading-normal font-normal text-slate-600 dark:text-slate-400", children: formatMarkdown2(props.section.description) }),
1250
+ props.group
1251
+ ]
1252
+ }
1253
+ ) })
1254
+ ]
1121
1255
  }
1122
1256
  );
1123
1257
  }
1124
1258
 
1125
- // src/component/field/field-horizontal.tsx
1126
- import { twMerge as twMerge3 } from "tailwind-merge";
1127
- import { jsx as jsx4 } from "react/jsx-runtime";
1128
- function FieldHorizontal(props) {
1129
- return /* @__PURE__ */ jsx4(
1130
- FieldBase,
1259
+ // src/component/legend.tsx
1260
+ import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
1261
+ function Legend(props) {
1262
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
1263
+ props.title && /* @__PURE__ */ jsx5("legend", { className: "mb-3 font-medium text-slate-800 dark:text-slate-200", children: formatMarkdown2(props.title) }),
1264
+ props.description && /* @__PURE__ */ jsx5("p", { className: "-mt-2 text-sm leading-normal font-normal text-slate-600 dark:text-slate-400", children: formatMarkdown2(props.description) })
1265
+ ] });
1266
+ }
1267
+
1268
+ // src/component/field/field-set-base.tsx
1269
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
1270
+ function FieldSetBase(props) {
1271
+ return /* @__PURE__ */ jsxs3(
1272
+ "fieldset",
1131
1273
  {
1132
- ...props,
1133
- orientation: HORIZONTAL,
1134
- className: twMerge3(
1135
- "gap-2 md:flex-row md:gap-4",
1136
- "[&>[data-slot=field-content]]:min-w-0 [&>[data-slot=field-content]]:flex-grow [&>[data-slot=field-content]]:self-start",
1137
- "[&_[role=checkbox]]:mt-[1.5px]",
1138
- props.isClickable && "md:flex-col",
1139
- !props.isClickable && [
1140
- "md:justify-between",
1141
- "[&>*:not([data-slot=field-content])]:w-full",
1142
- "[&>*:not([data-slot=field-content])]:md:w-1/2",
1143
- "[&>*:not([data-slot=field-content])]:xl:w-2/5"
1144
- ]
1145
- ),
1146
- children: props.children
1274
+ "data-slot": "field-set",
1275
+ "data-empty": props.empty,
1276
+ className: "flex flex-col data-[empty=false]:gap-6",
1277
+ id: props.id,
1278
+ children: [
1279
+ /* @__PURE__ */ jsx6(Legend, { description: props.description, title: props.title }),
1280
+ props.children
1281
+ ]
1147
1282
  }
1148
1283
  );
1149
1284
  }
1150
1285
 
1151
- // src/component/field/field-group.tsx
1152
- import { jsx as jsx5 } from "react/jsx-runtime";
1153
- function FieldGroup(props) {
1154
- const clickable = isClickable(props.field);
1155
- const checkbox = isCheckbox(props.field);
1156
- const reversed = buildReverse(props.field);
1157
- if (props.orientation === VERTICAL) {
1158
- return /* @__PURE__ */ jsx5(
1159
- FieldVertical,
1160
- {
1161
- disabled: props.disabled,
1162
- errors: props.errors,
1163
- isCheckbox: checkbox,
1164
- isReversed: reversed,
1165
- isClickable: clickable,
1166
- children: props.children
1167
- }
1168
- );
1169
- }
1170
- return /* @__PURE__ */ jsx5(
1171
- FieldHorizontal,
1286
+ // src/component/group.tsx
1287
+ import { jsx as jsx7 } from "react/jsx-runtime";
1288
+ function Group(props) {
1289
+ return /* @__PURE__ */ jsx7(
1290
+ "div",
1172
1291
  {
1173
- disabled: props.disabled,
1174
- errors: props.errors,
1175
- isCheckbox: checkbox,
1176
- isReversed: reversed,
1177
- isClickable: clickable,
1292
+ "data-slot": "field-group",
1293
+ "data-compact": props.compact,
1294
+ className: "flex w-full flex-col gap-8 data-[compact=true]:gap-3",
1178
1295
  children: props.children
1179
1296
  }
1180
1297
  );
1181
1298
  }
1182
1299
 
1183
- // src/component/input/input-base.tsx
1184
- function InputBase(props) {
1185
- if (!props.field.type) {
1186
- return null;
1187
- }
1188
- const commonProps = buildCommon(props.field, props.disabled);
1189
- const dataAttributes = buildDataAttributes(props.field);
1190
- const ariaAttributes = buildAriaAttributes(props.field, props.errors);
1191
- const field = {
1192
- ...props.field,
1193
- disabled: commonProps.disabled
1194
- };
1195
- return props.children({
1196
- ariaAttributes,
1197
- commonProps,
1198
- dataAttributes,
1199
- field,
1200
- orientation: props.orientation
1300
+ // src/component/field/field-set.tsx
1301
+ import { jsx as jsx8 } from "react/jsx-runtime";
1302
+ function FieldSet(props) {
1303
+ const { fields = [] } = props.section;
1304
+ const { compact } = mergeStyle(props.style, {
1305
+ compact: props.section.compact
1201
1306
  });
1307
+ const group = /* @__PURE__ */ jsx8(Group, { compact, children: props.children });
1308
+ if (!props.section.title && !props.section.description) {
1309
+ return group;
1310
+ }
1311
+ if (props.section.advanced) {
1312
+ return /* @__PURE__ */ jsx8(FieldSetAdvanced, { section: props.section, group });
1313
+ }
1314
+ return /* @__PURE__ */ jsx8(
1315
+ FieldSetBase,
1316
+ {
1317
+ description: props.section.description,
1318
+ empty: fields.length === 0,
1319
+ id: props.section.id?.toString(),
1320
+ title: props.section.title,
1321
+ children: group
1322
+ }
1323
+ );
1202
1324
  }
1203
1325
 
1204
- // src/component/field/field.tsx
1205
- import { twMerge as twMerge4 } from "tailwind-merge";
1206
- import { jsx as jsx6, jsxs } from "react/jsx-runtime";
1207
- function Field(props) {
1208
- const cols2 = props.field.advanced?.cols;
1209
- const errors = props.field.name ? props.errors?.[props.field.name] : void 0;
1210
- const { orientation } = mergeStyle(props.style, {
1211
- orientation: buildOrientation(props.field)
1212
- });
1213
- const disabled = buildDisabled(props.field, props.disabled);
1214
- return /* @__PURE__ */ jsxs("div", { className: twMerge4("flex flex-col gap-3", getSpan(cols2)), children: [
1215
- /* @__PURE__ */ jsx6(
1216
- FieldGroup,
1217
- {
1218
- disabled,
1219
- errors,
1220
- field: props.field,
1221
- orientation,
1222
- children: /* @__PURE__ */ jsx6(
1223
- InputBase,
1224
- {
1225
- disabled,
1226
- errors,
1227
- field: props.field,
1228
- orientation,
1229
- children: props.children
1230
- }
1231
- )
1232
- }
1233
- ),
1234
- /* @__PURE__ */ jsx6(FieldError, { errors, name: props.field.name })
1235
- ] });
1236
- }
1326
+ // src/client/component/guard/visibility-guard.tsx
1327
+ import { useAtomValue } from "jotai";
1237
1328
 
1238
1329
  // src/client/lib/store-helper.ts
1239
1330
  import { atom } from "jotai";
@@ -1379,179 +1470,80 @@ function createAtomStore(initialValue = {}) {
1379
1470
  };
1380
1471
  }
1381
1472
 
1382
- // src/client/lib/error-store.ts
1473
+ // src/client/lib/state-store.ts
1383
1474
  var store = createAtomStore();
1384
- var clearInputErrorAtom = store.clear;
1385
- var reportErrorAtom = store.bulkReport;
1386
- var reportInputErrorAtom = store.report;
1475
+ var fieldStateAtom = store.atom;
1476
+ var reportFieldStateAtom = store.report;
1387
1477
 
1388
- // src/client/component/wrapper/with-error.tsx
1389
- import { useAtomValue } from "jotai";
1390
- import { jsx as jsx7 } from "react/jsx-runtime";
1391
- function withErrors(Component) {
1392
- const WithErrors = (props) => {
1393
- const errors = useAtomValue(reportInputErrorAtom(props.field.name));
1394
- return /* @__PURE__ */ jsx7(
1395
- Component,
1396
- {
1397
- ...props,
1398
- errors: errors ? { [props.field.name]: errors } : void 0
1399
- }
1400
- );
1401
- };
1402
- return WithErrors;
1478
+ // src/client/component/guard/visibility-guard.tsx
1479
+ function isColumnHidden(column, states) {
1480
+ return column.fields.every((field) => isFieldHidden(field, states));
1403
1481
  }
1404
-
1405
- // src/client/lib/state-store.ts
1406
- var store2 = createAtomStore();
1407
- var fieldStateAtom = store2.atom;
1408
- var reportFieldStateAtom = store2.report;
1409
-
1410
- // src/client/component/wrapper/with-field-state.tsx
1411
- import { useAtomValue as useAtomValue2 } from "jotai";
1412
- import { jsx as jsx8 } from "react/jsx-runtime";
1413
- function withFieldState(Component) {
1414
- const WithFieldState = (props) => {
1415
- const fieldState = useAtomValue2(reportFieldStateAtom(props.field.name));
1416
- const hidden = fieldState?.hidden ?? props.field.hidden ?? false;
1417
- if (hidden) {
1418
- return null;
1419
- }
1420
- const disabled = fieldState?.disabled ?? props.disabled;
1421
- return /* @__PURE__ */ jsx8(Component, { ...props, disabled });
1422
- };
1423
- return WithFieldState;
1482
+ function isFieldHidden(field, states) {
1483
+ return states[field.name]?.hidden ?? field.hidden ?? false;
1424
1484
  }
1425
-
1426
- // src/client/component/wrapper/index.tsx
1427
- var Field2 = withFieldState(withErrors(Field));
1428
-
1429
- // src/component/control.tsx
1430
- import { jsx as jsx9 } from "react/jsx-runtime";
1431
- function Control(props) {
1432
- const content = typeof props.children === "function" ? props.children({ isPending: props.isPending }) : props.children;
1433
- return /* @__PURE__ */ jsx9("div", { "data-slot": "field-control", className: "w-full", children: content });
1485
+ function isEntryHidden(entry, states) {
1486
+ return isColumn(entry) ? isColumnHidden(entry, states) : isFieldHidden(entry, states);
1434
1487
  }
1435
-
1436
- // src/component/group.tsx
1437
- import { jsx as jsx10 } from "react/jsx-runtime";
1438
- function Group(props) {
1439
- return /* @__PURE__ */ jsx10(
1440
- "div",
1441
- {
1442
- "data-slot": "field-group",
1443
- "data-compact": props.compact,
1444
- className: "flex w-full flex-col gap-8 data-[compact=true]:gap-3",
1445
- children: props.children
1446
- }
1447
- );
1448
- }
1449
-
1450
- // src/lib/string.tsx
1451
- import { jsx as jsx11 } from "react/jsx-runtime";
1452
- function formatMarkdown2(text) {
1453
- return formatMarkdown(
1454
- text,
1455
- (index, url, text2) => {
1456
- return /* @__PURE__ */ jsx11(
1457
- "a",
1458
- {
1459
- className: "underline",
1460
- href: url,
1461
- rel: "noopener noreferrer",
1462
- target: "_blank",
1463
- children: text2
1464
- },
1465
- `${url}-${index}`
1466
- );
1488
+ function VisibilityGuard(props) {
1489
+ const states = useAtomValue(fieldStateAtom);
1490
+ if (props.container) {
1491
+ const hidden = states[props.container.name]?.hidden ?? props.container.hidden ?? false;
1492
+ if (hidden) {
1493
+ return null;
1467
1494
  }
1468
- );
1469
- }
1470
-
1471
- // src/component/legend.tsx
1472
- import { Fragment, jsx as jsx12, jsxs as jsxs2 } from "react/jsx-runtime";
1473
- function Legend(props) {
1474
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
1475
- props.title && /* @__PURE__ */ jsx12("legend", { className: "mb-3 font-medium text-slate-800 dark:text-slate-200", children: formatMarkdown2(props.title) }),
1476
- props.description && /* @__PURE__ */ jsx12("p", { className: "-mt-2 text-sm leading-normal font-normal text-slate-600 dark:text-slate-400", children: formatMarkdown2(props.description) })
1477
- ] });
1478
- }
1479
-
1480
- // src/component/field/field-set.tsx
1481
- import { jsx as jsx13, jsxs as jsxs3 } from "react/jsx-runtime";
1482
- function FieldSet(props) {
1483
- const fields = props.section.fields || [];
1484
- const { compact } = mergeStyle(props.style, {
1485
- compact: props.section.compact
1486
- });
1487
- if (!props.section.title && !props.section.description) {
1488
- return /* @__PURE__ */ jsx13(Group, { compact, children: props.children });
1489
1495
  }
1490
- return /* @__PURE__ */ jsxs3(
1491
- "fieldset",
1492
- {
1493
- "data-slot": "field-set",
1494
- "data-empty": fields.length === 0,
1495
- className: "flex flex-col data-[empty=false]:gap-6",
1496
- id: props.section.id?.toString(),
1497
- children: [
1498
- /* @__PURE__ */ jsx13(
1499
- Legend,
1500
- {
1501
- description: props.section.description,
1502
- title: props.section.title
1503
- }
1504
- ),
1505
- /* @__PURE__ */ jsx13(Group, { compact, children: props.children })
1506
- ]
1507
- }
1508
- );
1496
+ if (props.fields.length === 0) {
1497
+ return null;
1498
+ }
1499
+ const allHidden = props.fields.every((entry) => isEntryHidden(entry, states));
1500
+ if (allHidden) {
1501
+ return null;
1502
+ }
1503
+ return props.children;
1509
1504
  }
1510
1505
 
1511
- // src/component/form.tsx
1512
- import { Fragment as Fragment2 } from "react";
1513
-
1514
1506
  // src/component/separator.tsx
1515
- import { jsx as jsx14 } from "react/jsx-runtime";
1507
+ import { jsx as jsx9 } from "react/jsx-runtime";
1516
1508
  function Separator() {
1517
- return /* @__PURE__ */ jsx14("div", { "data-slot": "field-separator", className: "relative -my-2 h-5 text-sm", children: /* @__PURE__ */ jsx14("div", { className: "absolute inset-0 top-1/2 h-px w-full bg-slate-200 dark:bg-slate-800" }) });
1509
+ return /* @__PURE__ */ jsx9("div", { "data-slot": "field-separator", className: "relative -my-2 h-5 text-sm", children: /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 top-1/2 h-px w-full bg-slate-200 dark:bg-slate-800" }) });
1518
1510
  }
1519
1511
 
1520
1512
  // src/component/form.tsx
1521
- import { jsx as jsx15, jsxs as jsxs4 } from "react/jsx-runtime";
1513
+ import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
1522
1514
  function Form(props) {
1523
1515
  const sections = prepare(props.sections, props.definition);
1524
- return /* @__PURE__ */ jsx15("div", { className: "h-full w-full", children: /* @__PURE__ */ jsx15("form", { noValidate: props.noValidate, action: props.action, children: /* @__PURE__ */ jsxs4(Group, { children: [
1525
- sections.map((section, index) => /* @__PURE__ */ jsxs4(Fragment2, { children: [
1526
- /* @__PURE__ */ jsx15(FieldSet, { section, style: props.config.style, children: props.children({
1516
+ return /* @__PURE__ */ jsx10("div", { className: "h-full w-full", children: /* @__PURE__ */ jsx10("form", { noValidate: props.noValidate, action: props.action, children: /* @__PURE__ */ jsxs4(Group, { children: [
1517
+ sections.map((section, index) => /* @__PURE__ */ jsxs4(VisibilityGuard, { fields: section.fields ?? [], children: [
1518
+ /* @__PURE__ */ jsx10(FieldSet, { section, style: props.config.style, children: props.children({
1527
1519
  disabled: props.readOnly,
1528
1520
  fields: section.fields
1529
1521
  }) }),
1530
- section.separator && /* @__PURE__ */ jsx15(Separator, {})
1522
+ section.separator && /* @__PURE__ */ jsx10(Separator, {})
1531
1523
  ] }, index)),
1532
- props.control && /* @__PURE__ */ jsx15(Control, { isPending: props.isPending, children: props.control })
1524
+ props.control && /* @__PURE__ */ jsx10(Control, { isPending: props.isPending, children: props.control })
1533
1525
  ] }) }) });
1534
1526
  }
1535
1527
 
1536
1528
  // src/component/description.tsx
1537
- import { jsx as jsx16 } from "react/jsx-runtime";
1529
+ import { jsx as jsx11 } from "react/jsx-runtime";
1538
1530
  function Description(props) {
1539
- return /* @__PURE__ */ jsx16("p", { className: "-mt-2 text-xs leading-normal font-normal text-slate-600 dark:text-slate-400", children: props.children });
1531
+ return /* @__PURE__ */ jsx11("p", { className: "-mt-2 text-xs leading-normal font-normal text-slate-600 dark:text-slate-400", children: props.children });
1540
1532
  }
1541
1533
 
1542
1534
  // src/component/formatted-description.tsx
1543
- import { jsx as jsx17 } from "react/jsx-runtime";
1535
+ import { jsx as jsx12 } from "react/jsx-runtime";
1544
1536
  function FormattedDescription(props) {
1545
1537
  const content = formatMarkdown2(props.text);
1546
1538
  if (content) {
1547
- return /* @__PURE__ */ jsx17(Description, { children: content });
1539
+ return /* @__PURE__ */ jsx12(Description, { children: content });
1548
1540
  }
1549
1541
  return null;
1550
1542
  }
1551
1543
 
1552
1544
  // src/component/label.tsx
1553
- import { twMerge as twMerge5 } from "tailwind-merge";
1554
- import { jsx as jsx18, jsxs as jsxs5 } from "react/jsx-runtime";
1545
+ import { twMerge } from "tailwind-merge";
1546
+ import { jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
1555
1547
  function Label(props) {
1556
1548
  const showOptionalLabel = props.style?.showOptionalLabel ?? true;
1557
1549
  const normal = isRadio(props.field) || isCheckbox(props.field);
@@ -1560,7 +1552,7 @@ function Label(props) {
1560
1552
  {
1561
1553
  "data-slot": "field-label",
1562
1554
  "data-normal": normal,
1563
- className: twMerge5(
1555
+ className: twMerge(
1564
1556
  "flex w-fit items-center gap-2 text-sm leading-snug font-medium select-none",
1565
1557
  "data-[normal=true]:font-normal",
1566
1558
  "group-data-[readonly=true]:cursor-not-allowed group-data-[readonly=true]:opacity-50"
@@ -1568,30 +1560,31 @@ function Label(props) {
1568
1560
  htmlFor: props.field.name,
1569
1561
  children: [
1570
1562
  props.children,
1571
- showOptionalLabel && !props.field.required && /* @__PURE__ */ jsx18("span", { className: "text-sm text-slate-600 dark:text-slate-400", children: translate("(Optional)", props.translations) })
1563
+ showOptionalLabel && !props.field.required && /* @__PURE__ */ jsx13("span", { className: "text-sm text-slate-600 dark:text-slate-400", children: translate("(Optional)", props.translations) })
1572
1564
  ]
1573
1565
  }
1574
1566
  );
1575
1567
  }
1576
1568
 
1577
1569
  // src/component/input-label.tsx
1578
- import { jsx as jsx19, jsxs as jsxs6 } from "react/jsx-runtime";
1570
+ import { jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
1579
1571
  function InputLabel(props) {
1580
- const label = isInterpolated(props.field.label) ? interpolate(props.field.label, {
1572
+ const interpolateOpts = {
1581
1573
  context: props.context,
1582
1574
  env: props.config?.env
1583
- }) : props.field.label;
1584
- const description = isInterpolated(props.field.description) ? interpolate(props.field.description, {
1585
- context: props.context,
1586
- env: props.config?.env
1587
- }) : props.field.description;
1575
+ };
1576
+ const label = interpolateIfNeeded(props.field.label, interpolateOpts);
1577
+ const description = interpolateIfNeeded(
1578
+ props.field.description,
1579
+ interpolateOpts
1580
+ );
1588
1581
  return /* @__PURE__ */ jsxs6(
1589
1582
  "div",
1590
1583
  {
1591
1584
  "data-slot": "field-content",
1592
1585
  className: "flex w-full flex-1 flex-col gap-1.5 leading-snug",
1593
1586
  children: [
1594
- /* @__PURE__ */ jsx19(
1587
+ /* @__PURE__ */ jsx14(
1595
1588
  Label,
1596
1589
  {
1597
1590
  field: props.field,
@@ -1600,7 +1593,7 @@ function InputLabel(props) {
1600
1593
  children: translate(label, props.translations)
1601
1594
  }
1602
1595
  ),
1603
- props.orientation === HORIZONTAL && /* @__PURE__ */ jsx19(
1596
+ props.orientation === HORIZONTAL && /* @__PURE__ */ jsx14(
1604
1597
  FormattedDescription,
1605
1598
  {
1606
1599
  text: translate(description, props.translations)
@@ -1612,10 +1605,10 @@ function InputLabel(props) {
1612
1605
  }
1613
1606
 
1614
1607
  // src/component/input-group.tsx
1615
- import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs7 } from "react/jsx-runtime";
1608
+ import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
1616
1609
  function InputGroup(props) {
1617
- return /* @__PURE__ */ jsxs7(Fragment3, { children: [
1618
- props.field.name && props.field.label && /* @__PURE__ */ jsx20(
1610
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
1611
+ props.field.name && props.field.label && /* @__PURE__ */ jsx15(
1619
1612
  InputLabel,
1620
1613
  {
1621
1614
  config: props.config,
@@ -1626,7 +1619,7 @@ function InputGroup(props) {
1626
1619
  }
1627
1620
  ),
1628
1621
  props.children,
1629
- props.orientation === VERTICAL && props.field.description && /* @__PURE__ */ jsx20(
1622
+ props.orientation === VERTICAL && props.field.description && /* @__PURE__ */ jsx15(
1630
1623
  FormattedDescription,
1631
1624
  {
1632
1625
  text: translate(props.field.description, props.translations)
@@ -1643,8 +1636,14 @@ function renderIfExists(value, render) {
1643
1636
  return render(value);
1644
1637
  }
1645
1638
 
1639
+ // src/client/lib/error-store.ts
1640
+ var store2 = createAtomStore();
1641
+ var clearInputErrorAtom = store2.clear;
1642
+ var reportErrorAtom = store2.bulkReport;
1643
+ var reportInputErrorAtom = store2.report;
1644
+
1646
1645
  // src/client/component/input.tsx
1647
- import { useCallback as useCallback3, useRef as useRef3, useTransition } from "react";
1646
+ import { useCallback as useCallback4, useRef as useRef3, useTransition } from "react";
1648
1647
 
1649
1648
  // src/client/lib/source-store.ts
1650
1649
  import { atom as atom2 } from "jotai";
@@ -1750,7 +1749,7 @@ function useInput(field, onMount, onUnmount, translations) {
1750
1749
  import { useSetAtom, useStore } from "jotai";
1751
1750
 
1752
1751
  // src/client/hook/use-timeout.ts
1753
- import { useCallback, useEffect as useEffect2, useRef } from "react";
1752
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef } from "react";
1754
1753
  function useTimeout() {
1755
1754
  const timeoutRef = useRef(null);
1756
1755
  useEffect2(() => {
@@ -1760,14 +1759,11 @@ function useTimeout() {
1760
1759
  }
1761
1760
  };
1762
1761
  }, []);
1763
- function clearTimeoutRef() {
1762
+ const setTimeoutRef = useCallback2((callback, delay) => {
1764
1763
  if (timeoutRef.current) {
1765
1764
  clearTimeout(timeoutRef.current);
1766
1765
  timeoutRef.current = null;
1767
1766
  }
1768
- }
1769
- const setTimeoutRef = useCallback((callback, delay) => {
1770
- clearTimeoutRef();
1771
1767
  timeoutRef.current = setTimeout(callback, delay);
1772
1768
  }, []);
1773
1769
  return setTimeoutRef;
@@ -1782,26 +1778,27 @@ var reportValueAtom = store3.report;
1782
1778
 
1783
1779
  // src/client/hook/use-value.ts
1784
1780
  import { useAtom as useAtom2 } from "jotai";
1785
- import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef2 } from "react";
1781
+ import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef2, useEffectEvent as useEffectEvent2 } from "react";
1786
1782
  function useValue(field, currentValue) {
1787
1783
  const { name } = field;
1788
1784
  const skipNextOnChangeRef = useRef2(false);
1789
1785
  const [value, setValue] = useAtom2(reportValueAtom(name));
1786
+ const onCurrentValueChange = useEffectEvent2(
1787
+ (currentValue2) => {
1788
+ const newValue = resolveValue(name, currentValue2);
1789
+ if (isValidValue(newValue)) {
1790
+ skipNextOnChangeRef.current = true;
1791
+ setValue(newValue);
1792
+ }
1793
+ }
1794
+ );
1790
1795
  useEffect3(() => {
1791
- if (!currentValue || !(name in currentValue)) {
1796
+ if (!currentValue) {
1792
1797
  return;
1793
1798
  }
1794
- const newValue = currentValue[name];
1795
- if (isValidValue(newValue)) {
1796
- logger.info("useValue: setting skipNextOnChange to true", {
1797
- fieldName: name,
1798
- newValue
1799
- });
1800
- skipNextOnChangeRef.current = true;
1801
- setValue(newValue);
1802
- }
1803
- }, [name, currentValue, setValue]);
1804
- const shouldSkipOnChange = useCallback2(() => {
1799
+ onCurrentValueChange(currentValue);
1800
+ }, [currentValue]);
1801
+ const shouldSkipOnChange = useCallback3(() => {
1805
1802
  if (skipNextOnChangeRef.current) {
1806
1803
  skipNextOnChangeRef.current = false;
1807
1804
  return true;
@@ -1814,14 +1811,38 @@ function useValue(field, currentValue) {
1814
1811
  value
1815
1812
  };
1816
1813
  }
1814
+ function resolveValue(name, currentValue) {
1815
+ if (name in currentValue) {
1816
+ return currentValue[name];
1817
+ }
1818
+ if (!name.includes(".")) {
1819
+ return void 0;
1820
+ }
1821
+ const keys = name.split(".");
1822
+ let result = currentValue;
1823
+ for (const key of keys) {
1824
+ if (result === null || result === void 0) {
1825
+ return void 0;
1826
+ }
1827
+ if (Array.isArray(result)) {
1828
+ const index = Number(key);
1829
+ result = Number.isInteger(index) ? result[index] : void 0;
1830
+ } else if (typeof result === "object") {
1831
+ result = result[key];
1832
+ } else {
1833
+ return void 0;
1834
+ }
1835
+ }
1836
+ return result;
1837
+ }
1817
1838
 
1818
1839
  // src/client/component/input.tsx
1819
- import { jsx as jsx21 } from "react/jsx-runtime";
1840
+ import { jsx as jsx16 } from "react/jsx-runtime";
1820
1841
  function Input(props) {
1821
1842
  const entity = props.field.advanced?.entity;
1822
1843
  const store4 = useStore();
1823
1844
  const setTimeoutRef = useTimeout();
1824
- const [, startTransition3] = useTransition();
1845
+ const [, startTransition2] = useTransition();
1825
1846
  const { setValue, shouldSkipOnChange, value } = useValue(
1826
1847
  props.field,
1827
1848
  props.value
@@ -1864,7 +1885,7 @@ function Input(props) {
1864
1885
  }
1865
1886
  };
1866
1887
  const inputProps = prepareInputValue(props.field, defaultValue);
1867
- const validated = useCallback3(
1888
+ const validated = useCallback4(
1868
1889
  (value2) => {
1869
1890
  const results = schema.safeParse(value2);
1870
1891
  const errors = results.error?.issues.map((issue) => issue.message) ?? [];
@@ -1879,7 +1900,7 @@ function Input(props) {
1879
1900
  },
1880
1901
  [props.field.validation?.custom, schema, setErrors, store4]
1881
1902
  );
1882
- const handleTriggerEvent = useCallback3(
1903
+ const handleTriggerEvent = useCallback4(
1883
1904
  (value2, callback) => {
1884
1905
  if (hasTextable) {
1885
1906
  setTimeoutRef(() => {
@@ -1887,37 +1908,18 @@ function Input(props) {
1887
1908
  }, 500);
1888
1909
  return;
1889
1910
  }
1890
- const currentValue = getEntity(value2, data, entity);
1891
- callback(currentValue);
1911
+ callback(getEntity(value2, data, entity));
1892
1912
  },
1893
1913
  [data, entity, hasTextable, setTimeoutRef]
1894
1914
  );
1895
- const onChange = useCallback3(
1915
+ const onChange = useCallback4(
1896
1916
  (event) => {
1897
1917
  const inputValue = event.target.value;
1898
- logger.info("onChange fired", {
1899
- fieldName: props.field.name,
1900
- fieldType: props.field.type,
1901
- inputValue,
1902
- currentValue: valueRef.current,
1903
- hasTextable,
1904
- hasClickable
1905
- });
1906
1918
  if (!hasClickable && shouldSkipOnChange()) {
1907
- logger.info("shouldSkipOnChange returned true", {
1908
- fieldName: props.field.name,
1909
- willSkip: !hasTextable || inputValue === valueRef.current,
1910
- reason: !hasTextable ? "not textable (checkbox/radio/select)" : "value unchanged"
1911
- });
1912
1919
  if (!hasTextable || inputValue === valueRef.current) {
1913
- logger.info("SKIPPING onChange", { fieldName: props.field.name });
1914
1920
  return;
1915
1921
  }
1916
1922
  }
1917
- logger.info("onChange processing", {
1918
- fieldName: props.field.name,
1919
- inputValue
1920
- });
1921
1923
  onValueChangeRef.current?.(inputValue);
1922
1924
  if (props.config.validation.change) {
1923
1925
  validated(inputValue);
@@ -1926,19 +1928,32 @@ function Input(props) {
1926
1928
  if (events) {
1927
1929
  handleTriggerEvent(inputValue, (selected) => {
1928
1930
  handleProxyEvent(events, ({ sources, states, values }) => {
1929
- startTransition3(() => {
1931
+ startTransition2(() => {
1930
1932
  handleSourceEvent(
1931
1933
  selected,
1932
1934
  sources,
1933
1935
  (target, source) => setSource(target, source)
1934
1936
  );
1935
- handleStateEvent(selected, states, (target, state) => {
1937
+ handleStateEvent(selected, states, (targets, state) => {
1938
+ logger.info(
1939
+ `Setting field states for targets: ${targets.join(
1940
+ ", "
1941
+ )} with state: ${JSON.stringify(state)}`
1942
+ );
1936
1943
  setFieldStates((prev) => {
1937
1944
  if (state) {
1938
- return { ...prev, [target]: state };
1945
+ return targets.reduce(
1946
+ (acc, target) => ({
1947
+ ...acc,
1948
+ [target]: state
1949
+ }),
1950
+ prev
1951
+ );
1939
1952
  }
1940
- const { [target]: _removed, ...rest } = prev;
1941
- return rest;
1953
+ return targets.reduce((acc, target) => {
1954
+ const { [target]: _removed, ...rest } = acc;
1955
+ return rest;
1956
+ }, prev);
1942
1957
  });
1943
1958
  });
1944
1959
  handleValueEvent(selected, values, (target, value2) => {
@@ -1958,8 +1973,6 @@ function Input(props) {
1958
1973
  hasTextable,
1959
1974
  props.config.validation.change,
1960
1975
  props.field.event?.change,
1961
- props.field.name,
1962
- props.field.type,
1963
1976
  setFieldStates,
1964
1977
  setSource,
1965
1978
  setValues,
@@ -1967,7 +1980,7 @@ function Input(props) {
1967
1980
  validated
1968
1981
  ]
1969
1982
  );
1970
- const onBlur = useCallback3(
1983
+ const onBlur = useCallback4(
1971
1984
  (event) => {
1972
1985
  if (!hasClickable) {
1973
1986
  const value2 = event.target.value;
@@ -1978,7 +1991,7 @@ function Input(props) {
1978
1991
  },
1979
1992
  [hasClickable, props.config.validation.blur, validated]
1980
1993
  );
1981
- return renderIfExists(props.config.inputs[props.field.type], (Component) => /* @__PURE__ */ jsx21(
1994
+ return renderIfExists(props.config.inputs[props.field.type], (Component) => /* @__PURE__ */ jsx16(
1982
1995
  InputGroup,
1983
1996
  {
1984
1997
  config: props.config,
@@ -1986,7 +1999,7 @@ function Input(props) {
1986
1999
  field: props.field,
1987
2000
  orientation: props.orientation,
1988
2001
  translations: props.translations,
1989
- children: /* @__PURE__ */ jsx21(
2002
+ children: /* @__PURE__ */ jsx16(
1990
2003
  Component,
1991
2004
  {
1992
2005
  ...commonPropsWithOptions,
@@ -2001,39 +2014,493 @@ function Input(props) {
2001
2014
  ));
2002
2015
  }
2003
2016
 
2004
- // src/component/column.tsx
2017
+ // src/component/field/field-error.tsx
2018
+ import { jsx as jsx17 } from "react/jsx-runtime";
2019
+ function FieldError(props) {
2020
+ if (!props.errors || props.errors.length === 0) {
2021
+ return null;
2022
+ }
2023
+ return /* @__PURE__ */ jsx17(
2024
+ "ul",
2025
+ {
2026
+ className: "text-sm text-red-600 dark:text-red-500",
2027
+ id: props.name ? `${props.name}-error` : void 0,
2028
+ children: props.errors?.map((error, index) => /* @__PURE__ */ jsx17("li", { children: error }, props.name ? `${props.name}-error-${index}` : index))
2029
+ }
2030
+ );
2031
+ }
2032
+
2033
+ // src/component/field/field-base.tsx
2034
+ import { twMerge as twMerge2 } from "tailwind-merge";
2035
+ import { jsx as jsx18 } from "react/jsx-runtime";
2036
+ function FieldBase(props) {
2037
+ const errors = props.errors && props.errors.length > 0;
2038
+ return /* @__PURE__ */ jsx18(
2039
+ "div",
2040
+ {
2041
+ "data-slot": "field",
2042
+ "data-clickable": props.isClickable ? "true" : "false",
2043
+ ...errors && { [DATA_INVALID]: "true" },
2044
+ ...props.disabled && { [DATA_READONLY]: "true" },
2045
+ "data-orientation": props.orientation,
2046
+ className: twMerge2(
2047
+ "group flex w-full flex-col items-center data-[invalid=true]:text-red-600 data-[invalid=true]:dark:text-red-500",
2048
+ "data-[clickable=true]:items-start",
2049
+ props.isCheckbox && (props.isReversed ? "flex-row-reverse!" : "flex-row!"),
2050
+ "data-[clickable=true]:has-[>[data-slot=field-content]]:[&>:first-child]:mt-px",
2051
+ props.className
2052
+ ),
2053
+ children: props.children
2054
+ }
2055
+ );
2056
+ }
2057
+
2058
+ // src/component/field/field-vertical.tsx
2059
+ import { twMerge as twMerge3 } from "tailwind-merge";
2060
+ import { jsx as jsx19 } from "react/jsx-runtime";
2061
+ function FieldVertical(props) {
2062
+ return /* @__PURE__ */ jsx19(
2063
+ FieldBase,
2064
+ {
2065
+ ...props,
2066
+ orientation: VERTICAL,
2067
+ className: twMerge3(
2068
+ "gap-3 has-[>[data-slot=field-content]]:items-start",
2069
+ !props.isClickable && "[&>*]:w-full"
2070
+ ),
2071
+ children: props.children
2072
+ }
2073
+ );
2074
+ }
2075
+
2076
+ // src/component/field/field-horizontal.tsx
2077
+ import { twMerge as twMerge4 } from "tailwind-merge";
2078
+ import { jsx as jsx20 } from "react/jsx-runtime";
2079
+ function FieldHorizontal(props) {
2080
+ return /* @__PURE__ */ jsx20(
2081
+ FieldBase,
2082
+ {
2083
+ ...props,
2084
+ orientation: HORIZONTAL,
2085
+ className: twMerge4(
2086
+ "gap-2 md:flex-row md:gap-4",
2087
+ "[&>[data-slot=field-content]]:min-w-0 [&>[data-slot=field-content]]:flex-grow [&>[data-slot=field-content]]:self-start",
2088
+ "[&_[role=checkbox]]:mt-[1.5px]",
2089
+ props.isClickable && "md:flex-col",
2090
+ !props.isClickable && [
2091
+ "md:justify-between",
2092
+ "[&>*:not([data-slot=field-content])]:w-full",
2093
+ "[&>*:not([data-slot=field-content])]:md:w-1/2",
2094
+ "[&>*:not([data-slot=field-content])]:xl:w-2/5"
2095
+ ]
2096
+ ),
2097
+ children: props.children
2098
+ }
2099
+ );
2100
+ }
2101
+
2102
+ // src/component/field/field-group.tsx
2103
+ import { jsx as jsx21 } from "react/jsx-runtime";
2104
+ function FieldGroup(props) {
2105
+ const clickable = isClickable(props.field);
2106
+ const checkbox = isCheckbox(props.field);
2107
+ const reversed = buildReverse(props.field);
2108
+ if (props.orientation === VERTICAL) {
2109
+ return /* @__PURE__ */ jsx21(
2110
+ FieldVertical,
2111
+ {
2112
+ disabled: props.disabled,
2113
+ errors: props.errors,
2114
+ isCheckbox: checkbox,
2115
+ isReversed: reversed,
2116
+ isClickable: clickable,
2117
+ children: props.children
2118
+ }
2119
+ );
2120
+ }
2121
+ return /* @__PURE__ */ jsx21(
2122
+ FieldHorizontal,
2123
+ {
2124
+ disabled: props.disabled,
2125
+ errors: props.errors,
2126
+ isCheckbox: checkbox,
2127
+ isReversed: reversed,
2128
+ isClickable: clickable,
2129
+ children: props.children
2130
+ }
2131
+ );
2132
+ }
2133
+
2134
+ // src/component/input/input-base.tsx
2135
+ function InputBase(props) {
2136
+ if (!props.field.type) {
2137
+ return null;
2138
+ }
2139
+ const commonProps = buildCommon(props.field, props.disabled);
2140
+ const dataAttributes = buildDataAttributes(props.field);
2141
+ const ariaAttributes = buildAriaAttributes(props.field, props.errors);
2142
+ const field = {
2143
+ ...props.field,
2144
+ disabled: commonProps.disabled
2145
+ };
2146
+ return props.children({
2147
+ ariaAttributes,
2148
+ commonProps,
2149
+ dataAttributes,
2150
+ field,
2151
+ orientation: props.orientation
2152
+ });
2153
+ }
2154
+
2155
+ // src/component/field/field.tsx
2156
+ import { twMerge as twMerge5 } from "tailwind-merge";
2157
+ import { jsx as jsx22, jsxs as jsxs8 } from "react/jsx-runtime";
2158
+ function Field(props) {
2159
+ const cols2 = props.field.advanced?.cols;
2160
+ const errors = props.field.name ? props.errors?.[props.field.name] : void 0;
2161
+ const { orientation } = mergeStyle(props.style, {
2162
+ orientation: buildOrientation(props.field)
2163
+ });
2164
+ const disabled = buildDisabled(props.field, props.disabled);
2165
+ return /* @__PURE__ */ jsxs8("div", { className: twMerge5("flex flex-col gap-3", getSpan(cols2)), children: [
2166
+ /* @__PURE__ */ jsx22(
2167
+ FieldGroup,
2168
+ {
2169
+ disabled,
2170
+ errors,
2171
+ field: props.field,
2172
+ orientation,
2173
+ children: /* @__PURE__ */ jsx22(
2174
+ InputBase,
2175
+ {
2176
+ disabled,
2177
+ errors,
2178
+ field: props.field,
2179
+ orientation,
2180
+ children: props.children
2181
+ }
2182
+ )
2183
+ }
2184
+ ),
2185
+ /* @__PURE__ */ jsx22(FieldError, { errors, name: props.field.name })
2186
+ ] });
2187
+ }
2188
+
2189
+ // src/client/component/field/field-with-error.tsx
2190
+ import { useAtomValue as useAtomValue2 } from "jotai";
2191
+ import { jsx as jsx23 } from "react/jsx-runtime";
2192
+ function withError(Component) {
2193
+ const WithError = (props) => {
2194
+ const errors = useAtomValue2(reportInputErrorAtom(props.field.name));
2195
+ return /* @__PURE__ */ jsx23(
2196
+ Component,
2197
+ {
2198
+ ...props,
2199
+ errors: errors ? { [props.field.name]: errors } : void 0
2200
+ }
2201
+ );
2202
+ };
2203
+ return WithError;
2204
+ }
2205
+
2206
+ // src/client/component/field/field-with-state.tsx
2207
+ import { useAtomValue as useAtomValue3 } from "jotai";
2208
+ import { jsx as jsx24 } from "react/jsx-runtime";
2209
+ function withState(Component) {
2210
+ const WithFieldState = (props) => {
2211
+ const fieldState = useAtomValue3(reportFieldStateAtom(props.field.name));
2212
+ const hidden = fieldState?.hidden ?? props.field.hidden ?? false;
2213
+ if (hidden) {
2214
+ return null;
2215
+ }
2216
+ return /* @__PURE__ */ jsx24(Component, { ...props, disabled: fieldState?.disabled ?? props.disabled });
2217
+ };
2218
+ return WithFieldState;
2219
+ }
2220
+
2221
+ // src/client/component/field/field.tsx
2222
+ var Field2 = withState(withError(Field));
2223
+
2224
+ // src/component/field/field-list-item.tsx
2005
2225
  import { twMerge as twMerge6 } from "tailwind-merge";
2006
- import { jsx as jsx22 } from "react/jsx-runtime";
2226
+ import { jsx as jsx25, jsxs as jsxs9 } from "react/jsx-runtime";
2227
+ function FieldListItem(props) {
2228
+ function handleRemove() {
2229
+ if (props.canRemove && props.onRemove) {
2230
+ props.onRemove(props.index);
2231
+ }
2232
+ }
2233
+ const removeButton = props.canRemove && props.onRemove != null && /* @__PURE__ */ jsx25(
2234
+ "button",
2235
+ {
2236
+ "aria-label": `Remove ${props.label} item ${props.index + 1}`,
2237
+ className: twMerge6(
2238
+ "rounded p-1 text-xl text-slate-400",
2239
+ "transition-colors duration-150",
2240
+ "hover:text-red-500",
2241
+ "focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:outline-none",
2242
+ "dark:text-slate-500 dark:hover:text-red-400"
2243
+ ),
2244
+ onClick: handleRemove,
2245
+ type: "button",
2246
+ children: /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", children: "\xD7" })
2247
+ }
2248
+ );
2249
+ if (props.isMultiField) {
2250
+ return /* @__PURE__ */ jsxs9("div", { className: "rounded-lg border border-slate-100 p-4 dark:border-slate-900", children: [
2251
+ /* @__PURE__ */ jsxs9("div", { className: "mb-3 flex items-center justify-between", children: [
2252
+ /* @__PURE__ */ jsxs9("span", { className: "text-sm font-medium text-slate-400 dark:text-slate-500", children: [
2253
+ props.label,
2254
+ " ",
2255
+ props.index + 1
2256
+ ] }),
2257
+ removeButton
2258
+ ] }),
2259
+ /* @__PURE__ */ jsx25(Group, { children: props.children })
2260
+ ] });
2261
+ }
2262
+ return /* @__PURE__ */ jsxs9("div", { className: "flex items-start gap-2", children: [
2263
+ /* @__PURE__ */ jsx25(Group, { children: props.children }),
2264
+ removeButton && /* @__PURE__ */ jsx25("div", { className: "shrink-0", children: removeButton })
2265
+ ] });
2266
+ }
2267
+
2268
+ // src/client/component/field/field-list.tsx
2269
+ import { twMerge as twMerge7 } from "tailwind-merge";
2270
+
2271
+ // src/client/hook/use-field-list.ts
2272
+ import { useSetAtom as useSetAtom2 } from "jotai";
2273
+ import { useCallback as useCallback5, useRef as useRef4, useState as useState2 } from "react";
2274
+ function useFieldList(field, value) {
2275
+ const min2 = field.advanced?.length?.min ?? 1;
2276
+ const max2 = field.advanced?.length?.max ?? Infinity;
2277
+ const [items, setItems] = useState2(
2278
+ () => getInitialList(field, value)
2279
+ );
2280
+ const nextId = useRef4(items.length);
2281
+ const itemsRef = useRef4(items);
2282
+ itemsRef.current = items;
2283
+ const setValues = useSetAtom2(valueAtom);
2284
+ const addItem = useCallback5(() => {
2285
+ setItems((previous) => {
2286
+ if (previous.length >= max2) {
2287
+ return previous;
2288
+ }
2289
+ const id = nextId.current++;
2290
+ return [...previous, id];
2291
+ });
2292
+ }, [max2]);
2293
+ const handleRemove = useCallback5(
2294
+ (index) => {
2295
+ if (items.length <= min2) {
2296
+ return;
2297
+ }
2298
+ const stableId = items[index];
2299
+ setItems((previous) => {
2300
+ if (previous.length <= min2) {
2301
+ return previous;
2302
+ }
2303
+ return previous.filter((_, i) => i !== index);
2304
+ });
2305
+ setValues((current) => {
2306
+ const next = { ...current };
2307
+ const prefix = `${field.name}.`;
2308
+ const leafNames = [];
2309
+ for (const row of field.fields) {
2310
+ if (isColumn(row)) {
2311
+ for (const columnField of row.fields) {
2312
+ leafNames.push(columnField.name);
2313
+ }
2314
+ } else {
2315
+ leafNames.push(row.name);
2316
+ }
2317
+ }
2318
+ for (const name of leafNames) {
2319
+ delete next[`${prefix}${stableId}.${name}`];
2320
+ }
2321
+ return next;
2322
+ });
2323
+ },
2324
+ [field.name, field.fields, items, min2, setValues]
2325
+ );
2326
+ const canAdd = items.length < max2;
2327
+ const canRemove = items.length > min2;
2328
+ return [items, addItem, handleRemove, canAdd, canRemove, max2];
2329
+ }
2330
+
2331
+ // src/client/component/field/field-list.tsx
2332
+ import { Fragment as Fragment3, jsx as jsx26, jsxs as jsxs10 } from "react/jsx-runtime";
2333
+ function FieldList(props) {
2334
+ const [items, addItem, handleRemove, canAdd, canRemove, max2] = useFieldList(
2335
+ props.field,
2336
+ props.value
2337
+ );
2338
+ const label = getLabel(props.field);
2339
+ const action = props.field.advanced?.action ?? "Add item";
2340
+ const hasLimit = max2 !== Infinity;
2341
+ const isMultiField = isMultiFieldList(props.field);
2342
+ return /* @__PURE__ */ jsxs10(Fragment3, { children: [
2343
+ items.map((key, index) => /* @__PURE__ */ jsx26(
2344
+ FieldListItem,
2345
+ {
2346
+ canRemove,
2347
+ index,
2348
+ isMultiField,
2349
+ label,
2350
+ onRemove: handleRemove,
2351
+ children: props.children(key)
2352
+ },
2353
+ key
2354
+ )),
2355
+ /* @__PURE__ */ jsxs10(
2356
+ "button",
2357
+ {
2358
+ "aria-disabled": !canAdd,
2359
+ "aria-label": hasLimit ? `${action}, ${items.length} of ${max2}` : action,
2360
+ className: twMerge7(
2361
+ "flex w-full items-center gap-1.5 rounded py-1",
2362
+ "text-sm font-medium text-slate-500",
2363
+ "transition-colors duration-150",
2364
+ "hover:text-slate-800",
2365
+ "focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 focus-visible:outline-none",
2366
+ "dark:text-slate-400 dark:hover:text-slate-200",
2367
+ !canAdd && "cursor-not-allowed opacity-50 hover:text-slate-500 dark:hover:text-slate-400"
2368
+ ),
2369
+ disabled: !canAdd,
2370
+ onClick: addItem,
2371
+ type: "button",
2372
+ children: [
2373
+ /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: "+" }),
2374
+ action,
2375
+ hasLimit && /* @__PURE__ */ jsxs10(
2376
+ "span",
2377
+ {
2378
+ "aria-hidden": "true",
2379
+ className: "ml-auto text-slate-400 tabular-nums dark:text-slate-500",
2380
+ children: [
2381
+ items.length,
2382
+ " / ",
2383
+ max2
2384
+ ]
2385
+ }
2386
+ )
2387
+ ]
2388
+ }
2389
+ )
2390
+ ] });
2391
+ }
2392
+
2393
+ // src/component/list.tsx
2394
+ import { jsx as jsx27 } from "react/jsx-runtime";
2395
+ function List(props) {
2396
+ const empty = Array.isArray(props.field.fields) && props.field.fields.length === 0;
2397
+ return /* @__PURE__ */ jsx27(
2398
+ FieldSetBase,
2399
+ {
2400
+ description: props.field.description,
2401
+ empty,
2402
+ id: props.field.name,
2403
+ title: props.field.label,
2404
+ children: props.children
2405
+ }
2406
+ );
2407
+ }
2408
+
2409
+ // src/client/component/guard/list-guard.tsx
2410
+ import { jsx as jsx28 } from "react/jsx-runtime";
2411
+ function ListGuard({ children, field, value }) {
2412
+ return /* @__PURE__ */ jsx28(VisibilityGuard, { container: field, fields: field.fields, children: /* @__PURE__ */ jsx28(List, { field, children: /* @__PURE__ */ jsx28(FieldList, { field, value, children }) }) });
2413
+ }
2414
+
2415
+ // src/component/column.tsx
2416
+ import { twMerge as twMerge8 } from "tailwind-merge";
2417
+ import { jsx as jsx29 } from "react/jsx-runtime";
2007
2418
  function Column(props) {
2008
2419
  const cols2 = getColumn(props.column?.advanced?.cols);
2009
- return /* @__PURE__ */ jsx22("div", { className: "flex w-full flex-col gap-4", children: /* @__PURE__ */ jsx22("div", { className: twMerge6("grid grid-cols-1 gap-3 sm:gap-4", cols2), children: props.children }) });
2420
+ return /* @__PURE__ */ jsx29("div", { className: "flex w-full flex-col gap-4", children: /* @__PURE__ */ jsx29("div", { className: twMerge8("grid grid-cols-1 gap-3 sm:gap-4", cols2), children: props.children }) });
2010
2421
  }
2011
2422
 
2012
2423
  // src/component/slot/slot-base.tsx
2013
2424
  import { Fragment as Fragment4 } from "react";
2014
- import { jsx as jsx23, jsxs as jsxs8 } from "react/jsx-runtime";
2425
+
2426
+ // src/component/slot/slot-list.tsx
2427
+ import { jsx as jsx30 } from "react/jsx-runtime";
2428
+ function SlotList(props) {
2429
+ const fields = Array.isArray(props.field.fields) ? props.field.fields.map((field) => {
2430
+ if (isField(field)) {
2431
+ return {
2432
+ ...field,
2433
+ name: `${props.field.name}.${props.index}.${field.name}`
2434
+ };
2435
+ }
2436
+ if (isColumn(field)) {
2437
+ return {
2438
+ ...field,
2439
+ fields: field.fields.map((columnField) => ({
2440
+ ...columnField,
2441
+ name: `${props.field.name}.${props.index}.${columnField.name}`
2442
+ }))
2443
+ };
2444
+ }
2445
+ return field;
2446
+ }) : [];
2447
+ return /* @__PURE__ */ jsx30(
2448
+ SlotBase,
2449
+ {
2450
+ children: props.children,
2451
+ components: props.components,
2452
+ disabled: props.disabled,
2453
+ fields,
2454
+ style: props.style,
2455
+ value: props.value
2456
+ }
2457
+ );
2458
+ }
2459
+
2460
+ // src/component/slot/slot-base.tsx
2461
+ import { jsx as jsx31, jsxs as jsxs11 } from "react/jsx-runtime";
2015
2462
  function SlotBase(props) {
2016
- const { column: Column2, field: Field3 } = props.components;
2017
- return prepare(props.fields).map((field, index) => /* @__PURE__ */ jsxs8(Fragment4, { children: [
2018
- isColumn(field) && /* @__PURE__ */ jsx23(Column2, { column: field, children: /* @__PURE__ */ jsx23(SlotBase, { ...props, fields: field.fields }) }),
2019
- isField(field) && /* @__PURE__ */ jsx23(Field3, { disabled: props.disabled, field, style: props.style, children: props.children })
2463
+ const { field: Field3, list: List2 } = props.components;
2464
+ return prepare(props.fields).map((field, index) => /* @__PURE__ */ jsxs11(Fragment4, { children: [
2465
+ isColumn(field) && /* @__PURE__ */ jsx31(Column, { column: field, children: /* @__PURE__ */ jsx31(SlotBase, { ...props, fields: field.fields }) }),
2466
+ isField(field) && /* @__PURE__ */ jsx31(Field3, { disabled: props.disabled, field, style: props.style, children: props.children }),
2467
+ isList(field) && /* @__PURE__ */ jsx31(List2, { field, value: props.value, children: (index2) => /* @__PURE__ */ jsx31(
2468
+ SlotList,
2469
+ {
2470
+ children: props.children,
2471
+ components: props.components,
2472
+ disabled: props.disabled,
2473
+ field,
2474
+ index: index2,
2475
+ style: props.style,
2476
+ value: props.value
2477
+ }
2478
+ ) })
2020
2479
  ] }, index));
2021
2480
  }
2022
2481
 
2023
2482
  // src/component/slot/slot-create.tsx
2024
- import { jsx as jsx24 } from "react/jsx-runtime";
2025
- function createSlot(Field3) {
2026
- const CreateSlot = (props) => /* @__PURE__ */ jsx24(SlotBase, { ...props, components: { column: Column, field: Field3 } });
2483
+ import { jsx as jsx32 } from "react/jsx-runtime";
2484
+ function createSlot(components) {
2485
+ const CreateSlot = (props) => /* @__PURE__ */ jsx32(SlotBase, { ...props, components });
2027
2486
  return CreateSlot;
2028
2487
  }
2029
2488
 
2489
+ // src/client/component/slot/slot.tsx
2490
+ var Slot = createSlot({ field: Field2, list: ListGuard });
2491
+
2030
2492
  // src/client/hook/use-form-action.ts
2031
- import { useSetAtom as useSetAtom2 } from "jotai";
2493
+ import { useSetAtom as useSetAtom3 } from "jotai";
2032
2494
  import { startTransition, useActionState } from "react";
2033
2495
  function useFormState(getSchema2, action, options) {
2034
- const { onSuccess, preserveValues = false, validation = true } = options ?? {};
2035
- const setError = useSetAtom2(reportErrorAtom);
2036
- const clearValues = useSetAtom2(clearAllValueAtom);
2496
+ const {
2497
+ onSuccess,
2498
+ preserveValues = false,
2499
+ validation = true,
2500
+ translations
2501
+ } = options ?? {};
2502
+ const setError = useSetAtom3(reportErrorAtom);
2503
+ const clearValues = useSetAtom3(clearAllValueAtom);
2037
2504
  const initialState = {
2038
2505
  data: null,
2039
2506
  error: null,
@@ -2042,7 +2509,7 @@ function useFormState(getSchema2, action, options) {
2042
2509
  const [state, formAction, isPending] = useActionState(
2043
2510
  async (prevState, formData) => {
2044
2511
  const [schemas, fields] = getSchema2();
2045
- const schema = buildSchema(schemas, fields, options?.translations);
2512
+ const schema = buildSchema(schemas, fields, translations);
2046
2513
  if (validation === false) {
2047
2514
  if (action) {
2048
2515
  return await action(formData, schema);
@@ -2057,16 +2524,23 @@ function useFormState(getSchema2, action, options) {
2057
2524
  setError(errors);
2058
2525
  });
2059
2526
  return failure(form, {
2060
- description: "Please correct the errors and try again.",
2527
+ description: translate(
2528
+ "Please correct the errors and try again.",
2529
+ translations
2530
+ ),
2061
2531
  details: [],
2062
- title: "There were validation errors submitting the form."
2532
+ title: translate(
2533
+ "There were validation errors submitting the form.",
2534
+ translations
2535
+ )
2063
2536
  });
2064
2537
  }
2538
+ const unflattened = unflatten(form);
2065
2539
  if (action) {
2066
2540
  try {
2067
- const result = await action(form, schema);
2541
+ const result = await action(unflattened, schema);
2068
2542
  if (!result.success) {
2069
- return failure(form, result.error);
2543
+ return failure(unflattened, result.error);
2070
2544
  }
2071
2545
  onSuccess?.(result.data);
2072
2546
  if (!preserveValues) {
@@ -2074,12 +2548,15 @@ function useFormState(getSchema2, action, options) {
2074
2548
  clearValues();
2075
2549
  });
2076
2550
  }
2077
- return success(form, preserveValues);
2551
+ return success(unflattened, preserveValues);
2078
2552
  } catch (error) {
2079
2553
  logger.error("Error executing form action:", error);
2080
- return failure(form, {
2081
- title: "An unexpected error occurred submitting the form.",
2082
- details: buildError(error)
2554
+ return failure(unflattened, {
2555
+ title: translate(
2556
+ "An unexpected error occurred submitting the form.",
2557
+ translations
2558
+ ),
2559
+ details: buildError(error, translations)
2083
2560
  });
2084
2561
  }
2085
2562
  }
@@ -2089,9 +2566,11 @@ function useFormState(getSchema2, action, options) {
2089
2566
  );
2090
2567
  return [formAction, state, isPending];
2091
2568
  }
2092
- function buildError(error) {
2093
- const detail = error instanceof Error ? error.message : ["Unknown error"];
2094
- return Array.isArray(detail) ? detail : [detail];
2569
+ function buildError(error, translations) {
2570
+ if (error instanceof Error) {
2571
+ return [error.message];
2572
+ }
2573
+ return [translate("Unknown error", translations)];
2095
2574
  }
2096
2575
  function success(value, preserveValues = false) {
2097
2576
  const data = preserveValues ? value : null;
@@ -2110,16 +2589,16 @@ function failure(value, error) {
2110
2589
  }
2111
2590
 
2112
2591
  // src/client/hook/use-schema.ts
2113
- import { startTransition as startTransition2, useCallback as useCallback5, useRef as useRef4 } from "react";
2592
+ import { useCallback as useCallback7, useRef as useRef5 } from "react";
2114
2593
 
2115
2594
  // src/client/hook/use-store.ts
2116
- import { useCallback as useCallback4 } from "react";
2117
- import { useSetAtom as useSetAtom3 } from "jotai";
2595
+ import { useCallback as useCallback6 } from "react";
2596
+ import { useSetAtom as useSetAtom4 } from "jotai";
2118
2597
  function useStore2() {
2119
- const clearErrors = useSetAtom3(clearInputErrorAtom);
2120
- const clearSources = useSetAtom3(clearInputSourceAtom);
2121
- const clearValues = useSetAtom3(clearInputValueAtom);
2122
- return useCallback4(
2598
+ const clearErrors = useSetAtom4(clearInputErrorAtom);
2599
+ const clearValues = useSetAtom4(clearInputValueAtom);
2600
+ const clearSources = useSetAtom4(clearInputSourceAtom);
2601
+ return useCallback6(
2123
2602
  (names) => {
2124
2603
  const target = Array.isArray(names) ? names : [names];
2125
2604
  clearErrors(target);
@@ -2133,42 +2612,34 @@ function useStore2() {
2133
2612
  // src/client/hook/use-schema.ts
2134
2613
  function useSchema() {
2135
2614
  const clear = useStore2();
2136
- const schemaRef = useRef4({});
2137
- const fieldsRef = useRef4([]);
2138
- const pendingUnmounts = useRef4(/* @__PURE__ */ new Set());
2139
- const onMount = useCallback5((name, schema, field) => {
2615
+ const schemaRef = useRef5({});
2616
+ const fieldsRef = useRef5([]);
2617
+ const onMount = useCallback7((name, schema, field) => {
2140
2618
  if (!(name in schemaRef.current)) {
2141
2619
  schemaRef.current[name] = schema;
2142
2620
  fieldsRef.current.push(field);
2143
2621
  }
2144
2622
  }, []);
2145
- const onUnmount = useCallback5(
2623
+ const onUnmount = useCallback7(
2146
2624
  (name) => {
2147
2625
  if (schemaRef.current[name]) {
2148
2626
  delete schemaRef.current[name];
2149
2627
  fieldsRef.current = fieldsRef.current.filter((field) => {
2150
2628
  return field.name !== name;
2151
2629
  });
2152
- pendingUnmounts.current.add(name);
2153
- startTransition2(() => {
2154
- if (pendingUnmounts.current.size > 0) {
2155
- clear(Array.from(pendingUnmounts.current));
2156
- pendingUnmounts.current.clear();
2157
- }
2158
- });
2630
+ clear([name]);
2159
2631
  }
2160
2632
  },
2161
2633
  [clear]
2162
2634
  );
2163
- function getSchema2() {
2635
+ const getSchema2 = useCallback7(() => {
2164
2636
  return [schemaRef.current, fieldsRef.current];
2165
- }
2637
+ }, []);
2166
2638
  return [getSchema2, onMount, onUnmount];
2167
2639
  }
2168
2640
 
2169
2641
  // src/client/component/form-content.tsx
2170
- import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs9 } from "react/jsx-runtime";
2171
- var Slot = createSlot(Field2);
2642
+ import { Fragment as Fragment5, jsx as jsx33, jsxs as jsxs12 } from "react/jsx-runtime";
2172
2643
  function FormContent(props) {
2173
2644
  const translations = props.translations?.[props.lang ?? ""];
2174
2645
  const [schema, onMount, onUnmount] = useSchema();
@@ -2179,8 +2650,8 @@ function FormContent(props) {
2179
2650
  });
2180
2651
  const isShowingError = props.config.validation.showError && !state.success && state.error;
2181
2652
  const value = state.data ?? props.value;
2182
- return /* @__PURE__ */ jsxs9(Fragment5, { children: [
2183
- isShowingError && renderIfExists(props.config.alert, (Alert) => /* @__PURE__ */ jsx25("div", { className: "mb-4 w-full", children: /* @__PURE__ */ jsx25(
2653
+ return /* @__PURE__ */ jsxs12(Fragment5, { children: [
2654
+ isShowingError && renderIfExists(props.config.alert, (Alert) => /* @__PURE__ */ jsx33("div", { className: "mb-4 w-full", children: /* @__PURE__ */ jsx33(
2184
2655
  Alert,
2185
2656
  {
2186
2657
  description: state.error?.description,
@@ -2188,7 +2659,7 @@ function FormContent(props) {
2188
2659
  title: state.error.title
2189
2660
  }
2190
2661
  ) })),
2191
- /* @__PURE__ */ jsx25(
2662
+ /* @__PURE__ */ jsx33(
2192
2663
  Form,
2193
2664
  {
2194
2665
  action,
@@ -2199,19 +2670,28 @@ function FormContent(props) {
2199
2670
  noValidate: true,
2200
2671
  readOnly: props.readOnly,
2201
2672
  sections: props.sections,
2202
- children: ({ disabled, fields }) => /* @__PURE__ */ jsx25(Slot, { disabled, fields, style: props.config.style, children: (internal) => /* @__PURE__ */ jsx25(
2203
- Input,
2673
+ children: ({ disabled, fields }) => /* @__PURE__ */ jsx33(
2674
+ Slot,
2204
2675
  {
2205
- ...internal,
2206
- config: props.config,
2207
- context: props.context,
2208
- onMount,
2209
- onUnmount,
2210
- onValueChange: props.onValueChange,
2211
- translations,
2212
- value
2676
+ disabled,
2677
+ fields,
2678
+ style: props.config.style,
2679
+ value,
2680
+ children: (internal) => /* @__PURE__ */ jsx33(
2681
+ Input,
2682
+ {
2683
+ ...internal,
2684
+ config: props.config,
2685
+ context: props.context,
2686
+ onMount,
2687
+ onUnmount,
2688
+ onValueChange: props.onValueChange,
2689
+ translations,
2690
+ value
2691
+ }
2692
+ )
2213
2693
  }
2214
- ) })
2694
+ )
2215
2695
  }
2216
2696
  )
2217
2697
  ] });
@@ -2219,9 +2699,9 @@ function FormContent(props) {
2219
2699
 
2220
2700
  // src/client/component/form.tsx
2221
2701
  import { Provider } from "jotai";
2222
- import { jsx as jsx26 } from "react/jsx-runtime";
2702
+ import { jsx as jsx34 } from "react/jsx-runtime";
2223
2703
  function Form2(props) {
2224
- return /* @__PURE__ */ jsx26(Provider, { children: /* @__PURE__ */ jsx26(FormContent, { ...props }) });
2704
+ return /* @__PURE__ */ jsx34(Provider, { children: /* @__PURE__ */ jsx34(FormContent, { ...props }) });
2225
2705
  }
2226
2706
  export {
2227
2707
  Form2 as Form