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
@@ -5,16 +5,27 @@ function Control(props) {
5
5
  return /* @__PURE__ */ jsx("div", { "data-slot": "field-control", className: "w-full", children: content });
6
6
  }
7
7
 
8
- // src/component/group.tsx
8
+ // src/component/field/field-set-advanced.tsx
9
+ import { Activity, useCallback, useState } from "react";
10
+
11
+ // src/component/chevron-icon.tsx
9
12
  import { jsx as jsx2 } from "react/jsx-runtime";
10
- function Group(props) {
13
+ function ChevronIcon(props) {
11
14
  return /* @__PURE__ */ jsx2(
12
- "div",
15
+ "svg",
13
16
  {
14
- "data-slot": "field-group",
15
- "data-compact": props.compact,
16
- className: "flex w-full flex-col gap-8 data-[compact=true]:gap-3",
17
- children: props.children
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
+ )
18
29
  }
19
30
  );
20
31
  }
@@ -26,10 +37,12 @@ var INPUT_NUMBER = "input/number";
26
37
  var TEXTAREA = "textarea";
27
38
  var RADIO = "radio";
28
39
  var CHECKBOX = "checkbox";
40
+ var LIST = "list";
29
41
  var SELECT = "select";
30
42
  var SELECT_MONTH = "select/month";
31
43
  var SELECT_YEAR = "select/year";
32
44
  var COLUMN = "column";
45
+ var FIELDS = "fields";
33
46
  var LABEL = "label";
34
47
  var VALUE = "value";
35
48
  var OPTIONS = "options";
@@ -51,17 +64,21 @@ var MAX_LENGTH = "maxLength";
51
64
  var $REF = "$ref";
52
65
  var VERTICAL = "vertical";
53
66
  var HORIZONTAL = "horizontal";
67
+ var TYPE = "type";
54
68
 
55
69
  // ../luna-core/src/util/is-type.ts
56
70
  function isObject(value) {
57
71
  return value !== null && Object.prototype.toString.call(value) === "[object Object]";
58
72
  }
59
73
  function isValue(value) {
60
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
74
+ return isString(value) || typeof value === "number" || isBoolean(value);
61
75
  }
62
76
  function isString(value) {
63
77
  return typeof value === "string";
64
78
  }
79
+ function isBoolean(value) {
80
+ return typeof value === "boolean";
81
+ }
65
82
 
66
83
  // ../luna-core/src/util/extract.ts
67
84
  var REGEX_TYPE = /[^/]+$/;
@@ -151,6 +168,9 @@ function interpolate(template, values = {}) {
151
168
  }
152
169
  return template;
153
170
  }
171
+ function interpolateIfNeeded(template, values = {}) {
172
+ return isInterpolated(template) ? interpolate(template, values) : template;
173
+ }
154
174
  function isInterpolated(template) {
155
175
  if (isString(template)) {
156
176
  return /{([^}]+)}/.test(template);
@@ -219,11 +239,14 @@ var isNumber = createTypeChecker(INPUT_NUMBER, TYPE_NUMBER);
219
239
  function isClickable(field) {
220
240
  return isRadio(field) || isCheckbox(field);
221
241
  }
242
+ function isList(slot) {
243
+ return slot.type === LIST;
244
+ }
222
245
  function isColumn(slot) {
223
246
  return slot.type === COLUMN;
224
247
  }
225
248
  function isField(slot) {
226
- return slot.type !== COLUMN;
249
+ return slot.type !== COLUMN && slot.type !== LIST;
227
250
  }
228
251
  function isOptions(field) {
229
252
  return isSelect(field) || isRadio(field);
@@ -485,7 +508,7 @@ function prepareDefaultValue(field, value) {
485
508
  var REGEX_REF = /^#\/definition\//;
486
509
  function prepare(base = [], definition) {
487
510
  const resolved = resolveRefs(base, definition);
488
- return Array.isArray(resolved) ? resolved.sort((a, b) => getOrder(a) - getOrder(b)) : [];
511
+ return Array.isArray(resolved) ? resolved.filter(filter).sort((a, b) => getOrder(a) - getOrder(b)) : [];
489
512
  }
490
513
  function resolveRefs(base, definition, cache = /* @__PURE__ */ new Map(), visited = /* @__PURE__ */ new WeakSet()) {
491
514
  if (!isDefinition(definition) || !base || typeof base !== "object") {
@@ -526,6 +549,15 @@ function getOrder(item) {
526
549
  function isDefinition(definition) {
527
550
  return definition !== void 0 && isObject(definition) && Object.keys(definition).length > 0;
528
551
  }
552
+ function filter(base) {
553
+ if (TYPE in base) {
554
+ return true;
555
+ }
556
+ if (Array.isArray(base[FIELDS])) {
557
+ return base[FIELDS].length > 0;
558
+ }
559
+ return true;
560
+ }
529
561
 
530
562
  // ../luna-core/src/util/attributes.ts
531
563
  function getPrefixedAttributes(prefix, record) {
@@ -573,6 +605,31 @@ function getSpan(value) {
573
605
  }
574
606
  }
575
607
 
608
+ // ../luna-core/src/util/list.ts
609
+ function getInitialCount(list, value) {
610
+ const min = list.advanced?.length?.min ?? 1;
611
+ if (value) {
612
+ const data = extract(value, list.name);
613
+ if (Array.isArray(data)) {
614
+ return Math.max(data.length, min);
615
+ }
616
+ }
617
+ return Math.max(min, 0);
618
+ }
619
+ function isMultiFieldList(list) {
620
+ if (!Array.isArray(list.fields) || list.fields.length === 0) {
621
+ return false;
622
+ }
623
+ return list.fields.length > 1 || list.fields[0].type === COLUMN;
624
+ }
625
+ function getInitialList(list, value) {
626
+ const count = getInitialCount(list, value);
627
+ return Array.from({ length: count }, (_, index) => index);
628
+ }
629
+ function getLabel(list) {
630
+ return list.label ?? list.name;
631
+ }
632
+
576
633
  // ../luna-core/src/util/translate.ts
577
634
  function translate(key, dictionary) {
578
635
  if (!key) {
@@ -610,94 +667,266 @@ function formatMarkdown2(text) {
610
667
  );
611
668
  }
612
669
 
670
+ // src/component/field/field-set-advanced.tsx
671
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
672
+ function FieldSetAdvanced(props) {
673
+ const { fields = [] } = props.section;
674
+ const [isOpen, setIsOpen] = useState(false);
675
+ const handleOpen = useCallback(() => setIsOpen((previous) => !previous), []);
676
+ return /* @__PURE__ */ jsxs(
677
+ "fieldset",
678
+ {
679
+ "data-slot": "field-set",
680
+ "data-advanced": "true",
681
+ "data-expanded": isOpen,
682
+ "data-empty": fields.length === 0,
683
+ className: "flex flex-col",
684
+ id: props.section.id?.toString(),
685
+ children: [
686
+ /* @__PURE__ */ jsx4("legend", { children: /* @__PURE__ */ jsxs(
687
+ "button",
688
+ {
689
+ className: "flex cursor-pointer items-center gap-2 text-base font-medium text-slate-600 dark:text-slate-400",
690
+ onClick: handleOpen,
691
+ type: "button",
692
+ children: [
693
+ /* @__PURE__ */ jsx4(ChevronIcon, { expanded: isOpen }),
694
+ /* @__PURE__ */ jsx4("span", { children: formatMarkdown2(props.section.title) })
695
+ ]
696
+ }
697
+ ) }),
698
+ /* @__PURE__ */ jsx4(Activity, { mode: isOpen ? "visible" : "hidden", children: /* @__PURE__ */ jsxs(
699
+ "div",
700
+ {
701
+ className: "mt-3 ml-1.5 flex flex-col gap-4 border-l-2 border-slate-300 pl-4 dark:border-slate-600",
702
+ "data-slot": "field-set-content",
703
+ children: [
704
+ 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) }),
705
+ props.group
706
+ ]
707
+ }
708
+ ) })
709
+ ]
710
+ }
711
+ );
712
+ }
713
+
613
714
  // src/component/legend.tsx
614
- import { Fragment, jsx as jsx4, jsxs } from "react/jsx-runtime";
715
+ import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
615
716
  function Legend(props) {
616
- return /* @__PURE__ */ jsxs(Fragment, { children: [
617
- props.title && /* @__PURE__ */ jsx4("legend", { className: "mb-3 font-medium text-slate-800 dark:text-slate-200", children: formatMarkdown2(props.title) }),
618
- props.description && /* @__PURE__ */ jsx4("p", { className: "-mt-2 text-sm leading-normal font-normal text-slate-600 dark:text-slate-400", children: formatMarkdown2(props.description) })
717
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
718
+ props.title && /* @__PURE__ */ jsx5("legend", { className: "mb-3 font-medium text-slate-800 dark:text-slate-200", children: formatMarkdown2(props.title) }),
719
+ 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) })
619
720
  ] });
620
721
  }
621
722
 
723
+ // src/component/field/field-set-base.tsx
724
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
725
+ function FieldSetBase(props) {
726
+ return /* @__PURE__ */ jsxs3(
727
+ "fieldset",
728
+ {
729
+ "data-slot": "field-set",
730
+ "data-empty": props.empty,
731
+ className: "flex flex-col data-[empty=false]:gap-6",
732
+ id: props.id,
733
+ children: [
734
+ /* @__PURE__ */ jsx6(Legend, { description: props.description, title: props.title }),
735
+ props.children
736
+ ]
737
+ }
738
+ );
739
+ }
740
+
741
+ // src/component/group.tsx
742
+ import { jsx as jsx7 } from "react/jsx-runtime";
743
+ function Group(props) {
744
+ return /* @__PURE__ */ jsx7(
745
+ "div",
746
+ {
747
+ "data-slot": "field-group",
748
+ "data-compact": props.compact,
749
+ className: "flex w-full flex-col gap-8 data-[compact=true]:gap-3",
750
+ children: props.children
751
+ }
752
+ );
753
+ }
754
+
622
755
  // src/component/field/field-set.tsx
623
- import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
756
+ import { jsx as jsx8 } from "react/jsx-runtime";
624
757
  function FieldSet(props) {
625
- const fields = props.section.fields || [];
758
+ const { fields = [] } = props.section;
626
759
  const { compact } = mergeStyle(props.style, {
627
760
  compact: props.section.compact
628
761
  });
762
+ const group = /* @__PURE__ */ jsx8(Group, { compact, children: props.children });
629
763
  if (!props.section.title && !props.section.description) {
630
- return /* @__PURE__ */ jsx5(Group, { compact, children: props.children });
764
+ return group;
631
765
  }
632
- return /* @__PURE__ */ jsxs2(
633
- "fieldset",
766
+ if (props.section.advanced) {
767
+ return /* @__PURE__ */ jsx8(FieldSetAdvanced, { section: props.section, group });
768
+ }
769
+ return /* @__PURE__ */ jsx8(
770
+ FieldSetBase,
634
771
  {
635
- "data-slot": "field-set",
636
- "data-empty": fields.length === 0,
637
- className: "flex flex-col data-[empty=false]:gap-6",
772
+ description: props.section.description,
773
+ empty: fields.length === 0,
638
774
  id: props.section.id?.toString(),
639
- children: [
640
- /* @__PURE__ */ jsx5(
641
- Legend,
642
- {
643
- description: props.section.description,
644
- title: props.section.title
645
- }
646
- ),
647
- /* @__PURE__ */ jsx5(Group, { compact, children: props.children })
648
- ]
775
+ title: props.section.title,
776
+ children: group
649
777
  }
650
778
  );
651
779
  }
652
780
 
653
- // src/component/form.tsx
654
- import { Fragment as Fragment2 } from "react";
781
+ // src/client/component/guard/visibility-guard.tsx
782
+ import { useAtomValue } from "jotai";
783
+
784
+ // src/client/lib/store-helper.ts
785
+ import { atom } from "jotai";
786
+ import { atomFamily } from "jotai-family";
787
+ import { deepEqual } from "fast-equals";
788
+ function createRecordAtomFamily(baseAtom) {
789
+ return atomFamily(
790
+ (name) => atom(
791
+ (get) => {
792
+ return get(baseAtom)[name] ?? void 0;
793
+ },
794
+ (get, set, newValue) => {
795
+ const current = get(baseAtom);
796
+ if (newValue !== void 0 && newValue !== null) {
797
+ const currentValue = current[name];
798
+ if (!currentValue || !deepEqual(currentValue, newValue)) {
799
+ set(baseAtom, { ...current, [name]: newValue });
800
+ }
801
+ } else if (current[name]) {
802
+ const { [name]: _unused, ...rest } = current;
803
+ set(baseAtom, rest);
804
+ }
805
+ }
806
+ )
807
+ );
808
+ }
809
+ function createClearAllAtom(baseAtom) {
810
+ return atom(null, (get, set) => {
811
+ const current = get(baseAtom);
812
+ if (current && Object.keys(current).length > 0) {
813
+ set(baseAtom, {});
814
+ }
815
+ });
816
+ }
817
+ function createClearAtom(baseAtom) {
818
+ return atom(null, (get, set, names) => {
819
+ const current = get(baseAtom);
820
+ const next = { ...current };
821
+ let hasChanges = false;
822
+ for (const name of names) {
823
+ if (next[name]) {
824
+ delete next[name];
825
+ hasChanges = true;
826
+ }
827
+ }
828
+ if (hasChanges) {
829
+ set(baseAtom, next);
830
+ }
831
+ });
832
+ }
833
+ function createBulkReportAtom(baseAtom) {
834
+ return atom(null, (get, set, newValue) => {
835
+ const current = get(baseAtom);
836
+ if (!deepEqual(current, newValue)) {
837
+ set(baseAtom, newValue);
838
+ }
839
+ });
840
+ }
841
+ function createAtomStore(initialValue = {}) {
842
+ const baseAtom = atom(initialValue);
843
+ return {
844
+ atom: baseAtom,
845
+ clearAll: createClearAllAtom(baseAtom),
846
+ clear: createClearAtom(baseAtom),
847
+ bulkReport: createBulkReportAtom(baseAtom),
848
+ report: createRecordAtomFamily(baseAtom)
849
+ };
850
+ }
851
+
852
+ // src/client/lib/state-store.ts
853
+ var store = createAtomStore();
854
+ var fieldStateAtom = store.atom;
855
+ var reportFieldStateAtom = store.report;
856
+
857
+ // src/client/component/guard/visibility-guard.tsx
858
+ function isColumnHidden(column, states) {
859
+ return column.fields.every((field) => isFieldHidden(field, states));
860
+ }
861
+ function isFieldHidden(field, states) {
862
+ return states[field.name]?.hidden ?? field.hidden ?? false;
863
+ }
864
+ function isEntryHidden(entry, states) {
865
+ return isColumn(entry) ? isColumnHidden(entry, states) : isFieldHidden(entry, states);
866
+ }
867
+ function VisibilityGuard(props) {
868
+ const states = useAtomValue(fieldStateAtom);
869
+ if (props.container) {
870
+ const hidden = states[props.container.name]?.hidden ?? props.container.hidden ?? false;
871
+ if (hidden) {
872
+ return null;
873
+ }
874
+ }
875
+ if (props.fields.length === 0) {
876
+ return null;
877
+ }
878
+ const allHidden = props.fields.every((entry) => isEntryHidden(entry, states));
879
+ if (allHidden) {
880
+ return null;
881
+ }
882
+ return props.children;
883
+ }
655
884
 
656
885
  // src/component/separator.tsx
657
- import { jsx as jsx6 } from "react/jsx-runtime";
886
+ import { jsx as jsx9 } from "react/jsx-runtime";
658
887
  function Separator() {
659
- return /* @__PURE__ */ jsx6("div", { "data-slot": "field-separator", className: "relative -my-2 h-5 text-sm", children: /* @__PURE__ */ jsx6("div", { className: "absolute inset-0 top-1/2 h-px w-full bg-slate-200 dark:bg-slate-800" }) });
888
+ 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" }) });
660
889
  }
661
890
 
662
891
  // src/component/form.tsx
663
- import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
892
+ import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
664
893
  function Form(props) {
665
894
  const sections = prepare(props.sections, props.definition);
666
- return /* @__PURE__ */ jsx7("div", { className: "h-full w-full", children: /* @__PURE__ */ jsx7("form", { noValidate: props.noValidate, action: props.action, children: /* @__PURE__ */ jsxs3(Group, { children: [
667
- sections.map((section, index) => /* @__PURE__ */ jsxs3(Fragment2, { children: [
668
- /* @__PURE__ */ jsx7(FieldSet, { section, style: props.config.style, children: props.children({
895
+ return /* @__PURE__ */ jsx10("div", { className: "h-full w-full", children: /* @__PURE__ */ jsx10("form", { noValidate: props.noValidate, action: props.action, children: /* @__PURE__ */ jsxs4(Group, { children: [
896
+ sections.map((section, index) => /* @__PURE__ */ jsxs4(VisibilityGuard, { fields: section.fields ?? [], children: [
897
+ /* @__PURE__ */ jsx10(FieldSet, { section, style: props.config.style, children: props.children({
669
898
  disabled: props.readOnly,
670
899
  fields: section.fields
671
900
  }) }),
672
- section.separator && /* @__PURE__ */ jsx7(Separator, {})
901
+ section.separator && /* @__PURE__ */ jsx10(Separator, {})
673
902
  ] }, index)),
674
- props.control && /* @__PURE__ */ jsx7(Control, { isPending: props.isPending, children: props.control })
903
+ props.control && /* @__PURE__ */ jsx10(Control, { isPending: props.isPending, children: props.control })
675
904
  ] }) }) });
676
905
  }
677
906
 
678
907
  // src/component/description.tsx
679
- import { jsx as jsx8 } from "react/jsx-runtime";
908
+ import { jsx as jsx11 } from "react/jsx-runtime";
680
909
  function Description(props) {
681
- return /* @__PURE__ */ jsx8("p", { className: "-mt-2 text-xs leading-normal font-normal text-slate-600 dark:text-slate-400", children: props.children });
910
+ return /* @__PURE__ */ jsx11("p", { className: "-mt-2 text-xs leading-normal font-normal text-slate-600 dark:text-slate-400", children: props.children });
682
911
  }
683
912
 
684
913
  // src/component/formatted-description.tsx
685
- import { jsx as jsx9 } from "react/jsx-runtime";
914
+ import { jsx as jsx12 } from "react/jsx-runtime";
686
915
  function FormattedDescription(props) {
687
916
  const content = formatMarkdown2(props.text);
688
917
  if (content) {
689
- return /* @__PURE__ */ jsx9(Description, { children: content });
918
+ return /* @__PURE__ */ jsx12(Description, { children: content });
690
919
  }
691
920
  return null;
692
921
  }
693
922
 
694
923
  // src/component/label.tsx
695
924
  import { twMerge } from "tailwind-merge";
696
- import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
925
+ import { jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
697
926
  function Label(props) {
698
927
  const showOptionalLabel = props.style?.showOptionalLabel ?? true;
699
928
  const normal = isRadio(props.field) || isCheckbox(props.field);
700
- return /* @__PURE__ */ jsxs4(
929
+ return /* @__PURE__ */ jsxs5(
701
930
  "label",
702
931
  {
703
932
  "data-slot": "field-label",
@@ -710,30 +939,31 @@ function Label(props) {
710
939
  htmlFor: props.field.name,
711
940
  children: [
712
941
  props.children,
713
- showOptionalLabel && !props.field.required && /* @__PURE__ */ jsx10("span", { className: "text-sm text-slate-600 dark:text-slate-400", children: translate("(Optional)", props.translations) })
942
+ showOptionalLabel && !props.field.required && /* @__PURE__ */ jsx13("span", { className: "text-sm text-slate-600 dark:text-slate-400", children: translate("(Optional)", props.translations) })
714
943
  ]
715
944
  }
716
945
  );
717
946
  }
718
947
 
719
948
  // src/component/input-label.tsx
720
- import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
949
+ import { jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
721
950
  function InputLabel(props) {
722
- const label = isInterpolated(props.field.label) ? interpolate(props.field.label, {
951
+ const interpolateOpts = {
723
952
  context: props.context,
724
953
  env: props.config?.env
725
- }) : props.field.label;
726
- const description = isInterpolated(props.field.description) ? interpolate(props.field.description, {
727
- context: props.context,
728
- env: props.config?.env
729
- }) : props.field.description;
730
- return /* @__PURE__ */ jsxs5(
954
+ };
955
+ const label = interpolateIfNeeded(props.field.label, interpolateOpts);
956
+ const description = interpolateIfNeeded(
957
+ props.field.description,
958
+ interpolateOpts
959
+ );
960
+ return /* @__PURE__ */ jsxs6(
731
961
  "div",
732
962
  {
733
963
  "data-slot": "field-content",
734
964
  className: "flex w-full flex-1 flex-col gap-1.5 leading-snug",
735
965
  children: [
736
- /* @__PURE__ */ jsx11(
966
+ /* @__PURE__ */ jsx14(
737
967
  Label,
738
968
  {
739
969
  field: props.field,
@@ -742,7 +972,7 @@ function InputLabel(props) {
742
972
  children: translate(label, props.translations)
743
973
  }
744
974
  ),
745
- props.orientation === HORIZONTAL && /* @__PURE__ */ jsx11(
975
+ props.orientation === HORIZONTAL && /* @__PURE__ */ jsx14(
746
976
  FormattedDescription,
747
977
  {
748
978
  text: translate(description, props.translations)
@@ -754,10 +984,10 @@ function InputLabel(props) {
754
984
  }
755
985
 
756
986
  // src/component/input-group.tsx
757
- import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
987
+ import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
758
988
  function InputGroup(props) {
759
- return /* @__PURE__ */ jsxs6(Fragment3, { children: [
760
- props.field.name && props.field.label && /* @__PURE__ */ jsx12(
989
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
990
+ props.field.name && props.field.label && /* @__PURE__ */ jsx15(
761
991
  InputLabel,
762
992
  {
763
993
  config: props.config,
@@ -768,7 +998,7 @@ function InputGroup(props) {
768
998
  }
769
999
  ),
770
1000
  props.children,
771
- props.orientation === VERTICAL && props.field.description && /* @__PURE__ */ jsx12(
1001
+ props.orientation === VERTICAL && props.field.description && /* @__PURE__ */ jsx15(
772
1002
  FormattedDescription,
773
1003
  {
774
1004
  text: translate(props.field.description, props.translations)
@@ -786,7 +1016,7 @@ function renderIfExists(value, render) {
786
1016
  }
787
1017
 
788
1018
  // src/server/component/input.tsx
789
- import { jsx as jsx13 } from "react/jsx-runtime";
1019
+ import { jsx as jsx16 } from "react/jsx-runtime";
790
1020
  function Input(props) {
791
1021
  const source = resolveSource(props.field, props.value);
792
1022
  const { commonPropsWithOptions, defaultValue } = prepareInputProps(
@@ -796,7 +1026,7 @@ function Input(props) {
796
1026
  props.value
797
1027
  );
798
1028
  const defaultProps = prepareDefaultValue(props.field, defaultValue);
799
- return renderIfExists(props.config.inputs[props.field.type], (Component) => /* @__PURE__ */ jsx13(
1029
+ return renderIfExists(props.config.inputs[props.field.type], (Component) => /* @__PURE__ */ jsx16(
800
1030
  InputGroup,
801
1031
  {
802
1032
  config: props.config,
@@ -804,7 +1034,7 @@ function Input(props) {
804
1034
  field: props.field,
805
1035
  orientation: props.orientation,
806
1036
  translations: props.translations,
807
- children: /* @__PURE__ */ jsx13(
1037
+ children: /* @__PURE__ */ jsx16(
808
1038
  Component,
809
1039
  {
810
1040
  ...props.ariaAttributes,
@@ -818,27 +1048,27 @@ function Input(props) {
818
1048
  }
819
1049
 
820
1050
  // src/component/field/field-error.tsx
821
- import { jsx as jsx14 } from "react/jsx-runtime";
1051
+ import { jsx as jsx17 } from "react/jsx-runtime";
822
1052
  function FieldError(props) {
823
1053
  if (!props.errors || props.errors.length === 0) {
824
1054
  return null;
825
1055
  }
826
- return /* @__PURE__ */ jsx14(
1056
+ return /* @__PURE__ */ jsx17(
827
1057
  "ul",
828
1058
  {
829
1059
  className: "text-sm text-red-600 dark:text-red-500",
830
1060
  id: props.name ? `${props.name}-error` : void 0,
831
- children: props.errors?.map((error, index) => /* @__PURE__ */ jsx14("li", { children: error }, props.name ? `${props.name}-error-${index}` : index))
1061
+ children: props.errors?.map((error, index) => /* @__PURE__ */ jsx17("li", { children: error }, props.name ? `${props.name}-error-${index}` : index))
832
1062
  }
833
1063
  );
834
1064
  }
835
1065
 
836
1066
  // src/component/field/field-base.tsx
837
1067
  import { twMerge as twMerge2 } from "tailwind-merge";
838
- import { jsx as jsx15 } from "react/jsx-runtime";
1068
+ import { jsx as jsx18 } from "react/jsx-runtime";
839
1069
  function FieldBase(props) {
840
1070
  const errors = props.errors && props.errors.length > 0;
841
- return /* @__PURE__ */ jsx15(
1071
+ return /* @__PURE__ */ jsx18(
842
1072
  "div",
843
1073
  {
844
1074
  "data-slot": "field",
@@ -860,9 +1090,9 @@ function FieldBase(props) {
860
1090
 
861
1091
  // src/component/field/field-vertical.tsx
862
1092
  import { twMerge as twMerge3 } from "tailwind-merge";
863
- import { jsx as jsx16 } from "react/jsx-runtime";
1093
+ import { jsx as jsx19 } from "react/jsx-runtime";
864
1094
  function FieldVertical(props) {
865
- return /* @__PURE__ */ jsx16(
1095
+ return /* @__PURE__ */ jsx19(
866
1096
  FieldBase,
867
1097
  {
868
1098
  ...props,
@@ -878,9 +1108,9 @@ function FieldVertical(props) {
878
1108
 
879
1109
  // src/component/field/field-horizontal.tsx
880
1110
  import { twMerge as twMerge4 } from "tailwind-merge";
881
- import { jsx as jsx17 } from "react/jsx-runtime";
1111
+ import { jsx as jsx20 } from "react/jsx-runtime";
882
1112
  function FieldHorizontal(props) {
883
- return /* @__PURE__ */ jsx17(
1113
+ return /* @__PURE__ */ jsx20(
884
1114
  FieldBase,
885
1115
  {
886
1116
  ...props,
@@ -903,13 +1133,13 @@ function FieldHorizontal(props) {
903
1133
  }
904
1134
 
905
1135
  // src/component/field/field-group.tsx
906
- import { jsx as jsx18 } from "react/jsx-runtime";
1136
+ import { jsx as jsx21 } from "react/jsx-runtime";
907
1137
  function FieldGroup(props) {
908
1138
  const clickable = isClickable(props.field);
909
1139
  const checkbox = isCheckbox(props.field);
910
1140
  const reversed = buildReverse(props.field);
911
1141
  if (props.orientation === VERTICAL) {
912
- return /* @__PURE__ */ jsx18(
1142
+ return /* @__PURE__ */ jsx21(
913
1143
  FieldVertical,
914
1144
  {
915
1145
  disabled: props.disabled,
@@ -921,7 +1151,7 @@ function FieldGroup(props) {
921
1151
  }
922
1152
  );
923
1153
  }
924
- return /* @__PURE__ */ jsx18(
1154
+ return /* @__PURE__ */ jsx21(
925
1155
  FieldHorizontal,
926
1156
  {
927
1157
  disabled: props.disabled,
@@ -957,7 +1187,7 @@ function InputBase(props) {
957
1187
 
958
1188
  // src/component/field/field.tsx
959
1189
  import { twMerge as twMerge5 } from "tailwind-merge";
960
- import { jsx as jsx19, jsxs as jsxs7 } from "react/jsx-runtime";
1190
+ import { jsx as jsx22, jsxs as jsxs8 } from "react/jsx-runtime";
961
1191
  function Field(props) {
962
1192
  const cols2 = props.field.advanced?.cols;
963
1193
  const errors = props.field.name ? props.errors?.[props.field.name] : void 0;
@@ -965,15 +1195,15 @@ function Field(props) {
965
1195
  orientation: buildOrientation(props.field)
966
1196
  });
967
1197
  const disabled = buildDisabled(props.field, props.disabled);
968
- return /* @__PURE__ */ jsxs7("div", { className: twMerge5("flex flex-col gap-3", getSpan(cols2)), children: [
969
- /* @__PURE__ */ jsx19(
1198
+ return /* @__PURE__ */ jsxs8("div", { className: twMerge5("flex flex-col gap-3", getSpan(cols2)), children: [
1199
+ /* @__PURE__ */ jsx22(
970
1200
  FieldGroup,
971
1201
  {
972
1202
  disabled,
973
1203
  errors,
974
1204
  field: props.field,
975
1205
  orientation,
976
- children: /* @__PURE__ */ jsx19(
1206
+ children: /* @__PURE__ */ jsx22(
977
1207
  InputBase,
978
1208
  {
979
1209
  disabled,
@@ -985,44 +1215,175 @@ function Field(props) {
985
1215
  )
986
1216
  }
987
1217
  ),
988
- /* @__PURE__ */ jsx19(FieldError, { errors, name: props.field.name })
1218
+ /* @__PURE__ */ jsx22(FieldError, { errors, name: props.field.name })
989
1219
  ] });
990
1220
  }
991
1221
 
992
- // src/component/column.tsx
1222
+ // src/component/field/field-list-item.tsx
993
1223
  import { twMerge as twMerge6 } from "tailwind-merge";
994
- import { jsx as jsx20 } from "react/jsx-runtime";
1224
+ import { jsx as jsx23, jsxs as jsxs9 } from "react/jsx-runtime";
1225
+ function FieldListItem(props) {
1226
+ function handleRemove() {
1227
+ if (props.canRemove && props.onRemove) {
1228
+ props.onRemove(props.index);
1229
+ }
1230
+ }
1231
+ const removeButton = props.canRemove && props.onRemove != null && /* @__PURE__ */ jsx23(
1232
+ "button",
1233
+ {
1234
+ "aria-label": `Remove ${props.label} item ${props.index + 1}`,
1235
+ className: twMerge6(
1236
+ "rounded p-1 text-xl text-slate-400",
1237
+ "transition-colors duration-150",
1238
+ "hover:text-red-500",
1239
+ "focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:outline-none",
1240
+ "dark:text-slate-500 dark:hover:text-red-400"
1241
+ ),
1242
+ onClick: handleRemove,
1243
+ type: "button",
1244
+ children: /* @__PURE__ */ jsx23("span", { "aria-hidden": "true", children: "\xD7" })
1245
+ }
1246
+ );
1247
+ if (props.isMultiField) {
1248
+ return /* @__PURE__ */ jsxs9("div", { className: "rounded-lg border border-slate-100 p-4 dark:border-slate-900", children: [
1249
+ /* @__PURE__ */ jsxs9("div", { className: "mb-3 flex items-center justify-between", children: [
1250
+ /* @__PURE__ */ jsxs9("span", { className: "text-sm font-medium text-slate-400 dark:text-slate-500", children: [
1251
+ props.label,
1252
+ " ",
1253
+ props.index + 1
1254
+ ] }),
1255
+ removeButton
1256
+ ] }),
1257
+ /* @__PURE__ */ jsx23(Group, { children: props.children })
1258
+ ] });
1259
+ }
1260
+ return /* @__PURE__ */ jsxs9("div", { className: "flex items-start gap-2", children: [
1261
+ /* @__PURE__ */ jsx23(Group, { children: props.children }),
1262
+ removeButton && /* @__PURE__ */ jsx23("div", { className: "shrink-0", children: removeButton })
1263
+ ] });
1264
+ }
1265
+
1266
+ // src/component/field/field-list.tsx
1267
+ import { jsx as jsx24 } from "react/jsx-runtime";
1268
+ function FieldList(props) {
1269
+ const label = getLabel(props.field);
1270
+ const isMultiField = isMultiFieldList(props.field);
1271
+ return getInitialList(props.field, props.value).map((index) => /* @__PURE__ */ jsx24(
1272
+ FieldListItem,
1273
+ {
1274
+ index,
1275
+ isMultiField,
1276
+ label,
1277
+ children: props.children(index)
1278
+ },
1279
+ index
1280
+ ));
1281
+ }
1282
+
1283
+ // src/component/list.tsx
1284
+ import { jsx as jsx25 } from "react/jsx-runtime";
1285
+ function List(props) {
1286
+ const empty = Array.isArray(props.field.fields) && props.field.fields.length === 0;
1287
+ return /* @__PURE__ */ jsx25(
1288
+ FieldSetBase,
1289
+ {
1290
+ description: props.field.description,
1291
+ empty,
1292
+ id: props.field.name,
1293
+ title: props.field.label,
1294
+ children: props.children
1295
+ }
1296
+ );
1297
+ }
1298
+
1299
+ // src/component/slot/list-slot.tsx
1300
+ import { jsx as jsx26 } from "react/jsx-runtime";
1301
+ function ListSlot({ children, field, value }) {
1302
+ return /* @__PURE__ */ jsx26(List, { field, children: /* @__PURE__ */ jsx26(FieldList, { field, value, children }) });
1303
+ }
1304
+
1305
+ // src/component/column.tsx
1306
+ import { twMerge as twMerge7 } from "tailwind-merge";
1307
+ import { jsx as jsx27 } from "react/jsx-runtime";
995
1308
  function Column(props) {
996
1309
  const cols2 = getColumn(props.column?.advanced?.cols);
997
- return /* @__PURE__ */ jsx20("div", { className: "flex w-full flex-col gap-4", children: /* @__PURE__ */ jsx20("div", { className: twMerge6("grid grid-cols-1 gap-3 sm:gap-4", cols2), children: props.children }) });
1310
+ return /* @__PURE__ */ jsx27("div", { className: "flex w-full flex-col gap-4", children: /* @__PURE__ */ jsx27("div", { className: twMerge7("grid grid-cols-1 gap-3 sm:gap-4", cols2), children: props.children }) });
1311
+ }
1312
+
1313
+ // src/component/slot/slot-base.tsx
1314
+ import { Fragment as Fragment3 } from "react";
1315
+
1316
+ // src/component/slot/slot-list.tsx
1317
+ import { jsx as jsx28 } from "react/jsx-runtime";
1318
+ function SlotList(props) {
1319
+ const fields = Array.isArray(props.field.fields) ? props.field.fields.map((field) => {
1320
+ if (isField(field)) {
1321
+ return {
1322
+ ...field,
1323
+ name: `${props.field.name}.${props.index}.${field.name}`
1324
+ };
1325
+ }
1326
+ if (isColumn(field)) {
1327
+ return {
1328
+ ...field,
1329
+ fields: field.fields.map((columnField) => ({
1330
+ ...columnField,
1331
+ name: `${props.field.name}.${props.index}.${columnField.name}`
1332
+ }))
1333
+ };
1334
+ }
1335
+ return field;
1336
+ }) : [];
1337
+ return /* @__PURE__ */ jsx28(
1338
+ SlotBase,
1339
+ {
1340
+ children: props.children,
1341
+ components: props.components,
1342
+ disabled: props.disabled,
1343
+ fields,
1344
+ style: props.style,
1345
+ value: props.value
1346
+ }
1347
+ );
998
1348
  }
999
1349
 
1000
1350
  // src/component/slot/slot-base.tsx
1001
- import { Fragment as Fragment4 } from "react";
1002
- import { jsx as jsx21, jsxs as jsxs8 } from "react/jsx-runtime";
1351
+ import { jsx as jsx29, jsxs as jsxs10 } from "react/jsx-runtime";
1003
1352
  function SlotBase(props) {
1004
- const { column: Column2, field: Field2 } = props.components;
1005
- return prepare(props.fields).map((field, index) => /* @__PURE__ */ jsxs8(Fragment4, { children: [
1006
- isColumn(field) && /* @__PURE__ */ jsx21(Column2, { column: field, children: /* @__PURE__ */ jsx21(SlotBase, { ...props, fields: field.fields }) }),
1007
- isField(field) && /* @__PURE__ */ jsx21(Field2, { disabled: props.disabled, field, style: props.style, children: props.children })
1353
+ const { field: Field2, list: List2 } = props.components;
1354
+ return prepare(props.fields).map((field, index) => /* @__PURE__ */ jsxs10(Fragment3, { children: [
1355
+ isColumn(field) && /* @__PURE__ */ jsx29(Column, { column: field, children: /* @__PURE__ */ jsx29(SlotBase, { ...props, fields: field.fields }) }),
1356
+ isField(field) && /* @__PURE__ */ jsx29(Field2, { disabled: props.disabled, field, style: props.style, children: props.children }),
1357
+ isList(field) && /* @__PURE__ */ jsx29(List2, { field, value: props.value, children: (index2) => /* @__PURE__ */ jsx29(
1358
+ SlotList,
1359
+ {
1360
+ children: props.children,
1361
+ components: props.components,
1362
+ disabled: props.disabled,
1363
+ field,
1364
+ index: index2,
1365
+ style: props.style,
1366
+ value: props.value
1367
+ }
1368
+ ) })
1008
1369
  ] }, index));
1009
1370
  }
1010
1371
 
1011
1372
  // src/component/slot/slot-create.tsx
1012
- import { jsx as jsx22 } from "react/jsx-runtime";
1013
- function createSlot(Field2) {
1014
- const CreateSlot = (props) => /* @__PURE__ */ jsx22(SlotBase, { ...props, components: { column: Column, field: Field2 } });
1373
+ import { jsx as jsx30 } from "react/jsx-runtime";
1374
+ function createSlot(components) {
1375
+ const CreateSlot = (props) => /* @__PURE__ */ jsx30(SlotBase, { ...props, components });
1015
1376
  return CreateSlot;
1016
1377
  }
1017
1378
 
1018
1379
  // src/component/slot/slot.tsx
1019
- var Slot = createSlot(Field);
1380
+ var Slot = createSlot({ field: Field, list: ListSlot });
1020
1381
 
1021
1382
  // src/server/component/form.tsx
1022
- import { jsx as jsx23 } from "react/jsx-runtime";
1383
+ import { jsx as jsx31 } from "react/jsx-runtime";
1023
1384
  function Form2(props) {
1024
1385
  const translations = props.translations?.[props.lang ?? ""];
1025
- return /* @__PURE__ */ jsx23(
1386
+ return /* @__PURE__ */ jsx31(
1026
1387
  Form,
1027
1388
  {
1028
1389
  config: props.config,
@@ -1030,7 +1391,7 @@ function Form2(props) {
1030
1391
  definition: props.definition,
1031
1392
  readOnly: props.readOnly,
1032
1393
  sections: props.sections,
1033
- children: ({ disabled, fields }) => /* @__PURE__ */ jsx23(Slot, { disabled, fields, style: props.config.style, children: (internal) => /* @__PURE__ */ jsx23(
1394
+ children: ({ disabled, fields }) => /* @__PURE__ */ jsx31(Slot, { disabled, fields, style: props.config.style, children: (internal) => /* @__PURE__ */ jsx31(
1034
1395
  Input,
1035
1396
  {
1036
1397
  ...internal,
@@ -1046,12 +1407,12 @@ function Form2(props) {
1046
1407
 
1047
1408
  // src/server/component/fallback.tsx
1048
1409
  import { Suspense } from "react";
1049
- import { jsx as jsx24 } from "react/jsx-runtime";
1410
+ import { jsx as jsx32 } from "react/jsx-runtime";
1050
1411
  function Fallback(props) {
1051
- return /* @__PURE__ */ jsx24(
1412
+ return /* @__PURE__ */ jsx32(
1052
1413
  Suspense,
1053
1414
  {
1054
- fallback: /* @__PURE__ */ jsx24(Form2, { config: props.config, sections: props.sections, readOnly: true }),
1415
+ fallback: /* @__PURE__ */ jsx32(Form2, { config: props.config, sections: props.sections, readOnly: true }),
1055
1416
  children: props.children
1056
1417
  }
1057
1418
  );