@form-engine-ts/mui 2.9.3 → 2.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ DEFAULT_MUI_SECTION_ORDER: () => DEFAULT_MUI_SECTION_ORDER,
23
24
  MuiButtonAdapter: () => MuiButtonAdapter,
24
25
  MuiCheckboxAdapter: () => MuiCheckboxAdapter,
25
26
  MuiErrorMessageAdapter: () => MuiErrorMessageAdapter,
@@ -61,13 +62,33 @@ module.exports = __toCommonJS(index_exports);
61
62
  var import_material = require("@mui/material");
62
63
 
63
64
  // src/types.ts
65
+ var DEFAULT_MUI_SECTION_ORDER = [
66
+ "basicSettings",
67
+ "questions",
68
+ "addQuestion",
69
+ "localization"
70
+ ];
64
71
  function resolveMuiAdapterOptions(options = {}) {
72
+ const buttonVariant = options.buttonVariant ?? "contained";
65
73
  return {
66
74
  size: options.size ?? "medium",
67
75
  variant: options.variant ?? "outlined",
68
- buttonVariant: options.buttonVariant ?? "contained",
76
+ buttonVariant,
77
+ buttonVariants: {
78
+ primary: options.buttonVariants?.primary ?? buttonVariant,
79
+ secondary: options.buttonVariants?.secondary ?? buttonVariant,
80
+ danger: options.buttonVariants?.danger ?? buttonVariant
81
+ },
69
82
  fullWidth: options.fullWidth ?? true,
70
- dense: options.dense ?? false
83
+ dense: options.dense ?? false,
84
+ getLocaleLabel: options.getLocaleLabel ?? ((locale) => locale),
85
+ ...options.getActionLabel === void 0 ? {} : { getActionLabel: options.getActionLabel },
86
+ layoutOptions: options.layoutOptions ?? {},
87
+ localizationOptions: {
88
+ collapsible: options.localizationOptions?.collapsible ?? false,
89
+ defaultExpanded: options.localizationOptions?.defaultExpanded ?? false
90
+ },
91
+ muiSlotProps: options.muiSlotProps ?? {}
71
92
  };
72
93
  }
73
94
 
@@ -106,7 +127,7 @@ function createMuiButtonAdapter(options) {
106
127
  "data-target-id": targetId,
107
128
  onClick,
108
129
  color: variant === "danger" ? "error" : "primary",
109
- variant: resolved.buttonVariant,
130
+ variant: resolved.buttonVariants?.[variant ?? "primary"] ?? resolved.buttonVariant,
110
131
  fullWidth: resolved.fullWidth,
111
132
  children
112
133
  }
@@ -232,16 +253,16 @@ var MuiFieldsetAdapter = createMuiFieldsetAdapter();
232
253
  // src/adapters/IconButton.tsx
233
254
  var import_material5 = require("@mui/material");
234
255
  var import_jsx_runtime5 = require("react/jsx-runtime");
235
- var ACTION_LABELS = {
236
- moveUp: "\u4E0A\u3078\u79FB\u52D5",
237
- moveDown: "\u4E0B\u3078\u79FB\u52D5",
238
- delete: "\u524A\u9664",
239
- add: "\u8FFD\u52A0",
240
- edit: "\u7DE8\u96C6",
241
- settings: "\u8A2D\u5B9A",
242
- translate: "\u7FFB\u8A33",
243
- close: "\u9589\u3058\u308B",
244
- dragHandle: "\u4E26\u3079\u66FF\u3048"
256
+ var FALLBACK_ACTION_LABELS = {
257
+ moveUp: "Move up",
258
+ moveDown: "Move down",
259
+ delete: "Delete",
260
+ add: "Add",
261
+ edit: "Edit",
262
+ settings: "Settings",
263
+ translate: "Translate",
264
+ close: "Close",
265
+ dragHandle: "Reorder"
245
266
  };
246
267
  function createMuiIconButtonAdapter(options) {
247
268
  const resolved = resolveMuiAdapterOptions(options);
@@ -258,7 +279,7 @@ function createMuiIconButtonAdapter(options) {
258
279
  actionType,
259
280
  title
260
281
  }) {
261
- const actionLabel = actionType === void 0 ? "\u64CD\u4F5C" : ACTION_LABELS[actionType];
282
+ const actionLabel = actionType === void 0 ? "Action" : resolved.getActionLabel?.(actionType) ?? FALLBACK_ACTION_LABELS[actionType];
262
283
  const tooltip = title ?? actionLabel;
263
284
  const color = actionType === "delete" ? "error" : actionType === "add" ? "primary" : "default";
264
285
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material5.Tooltip, { title: tooltip, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -299,13 +320,14 @@ function createMuiSectionAdapter(options) {
299
320
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
300
321
  import_material6.Paper,
301
322
  {
323
+ ...resolved.muiSlotProps?.paper,
302
324
  id,
303
325
  className,
304
326
  elevation: 0,
305
327
  "aria-label": ariaLabel,
306
328
  "aria-labelledby": title === void 0 ? void 0 : headingId,
307
329
  onClickCapture,
308
- sx: {
330
+ sx: resolved.muiSlotProps?.paper?.sx ?? {
309
331
  p: resolved.dense ? 1.5 : 2,
310
332
  mb: resolved.dense ? 1 : 2,
311
333
  border: 1,
@@ -583,21 +605,22 @@ function createMuiOptionEditorSlot(options) {
583
605
  currentLocale,
584
606
  readOnly,
585
607
  actions,
586
- components
608
+ components,
609
+ translate
587
610
  }) {
588
611
  const { IconButton: IconButton2, TextInput } = components;
589
612
  const describedBy = option.label.trim().length === 0 ? `mui-option-${option.id}-error` : void 0;
590
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material10.Stack, { "data-mui-slot": "option-editor", spacing: resolved.dense ? 0.75 : 1, children: [
613
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material10.Stack, { ...resolved.muiSlotProps?.stack, "data-mui-slot": "option-editor", spacing: resolved.dense ? 0.75 : 1, children: [
591
614
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_material10.Stack, { direction: { xs: "column", sm: "row" }, spacing: 1, alignItems: { sm: "flex-start" }, children: [
592
615
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
593
616
  TextInput,
594
617
  {
595
618
  id: `mui-option-${option.id}`,
596
- label: `Option ${index + 1}`,
619
+ label: translate("builder.optionLabel", { index: index + 1 }),
597
620
  value: option.label,
598
621
  required: true,
599
622
  error: option.label.trim().length === 0,
600
- helperText: option.label.trim().length === 0 ? "Required" : "",
623
+ helperText: option.label.trim().length === 0 ? translate("builder.required") : "",
601
624
  "aria-describedby": describedBy,
602
625
  disabled: readOnly,
603
626
  onChange: (value) => actions.updateOption(field.id, option.id, (current) => ({ ...current, label: value }))
@@ -608,7 +631,7 @@ function createMuiOptionEditorSlot(options) {
608
631
  IconButton2,
609
632
  {
610
633
  actionType: "moveUp",
611
- title: `Move ${option.label} up`,
634
+ title: translate("builder.moveUp", { title: option.label }),
612
635
  disabled: readOnly || index === 0,
613
636
  onClick: () => actions.moveOption(field.id, option.id, index - 1)
614
637
  }
@@ -617,7 +640,7 @@ function createMuiOptionEditorSlot(options) {
617
640
  IconButton2,
618
641
  {
619
642
  actionType: "moveDown",
620
- title: `Move ${option.label} down`,
643
+ title: translate("builder.moveDown", { title: option.label }),
621
644
  disabled: readOnly || index === field.options.length - 1,
622
645
  onClick: () => actions.moveOption(field.id, option.id, index + 1)
623
646
  }
@@ -626,7 +649,7 @@ function createMuiOptionEditorSlot(options) {
626
649
  IconButton2,
627
650
  {
628
651
  actionType: "delete",
629
- title: `Delete ${option.label}`,
652
+ title: translate("builder.delete", { title: option.label }),
630
653
  disabled: readOnly || field.options.length <= 1,
631
654
  onClick: () => actions.removeOption(field.id, option.id)
632
655
  }
@@ -637,7 +660,9 @@ function createMuiOptionEditorSlot(options) {
637
660
  TextInput,
638
661
  {
639
662
  id: `mui-option-${option.id}-${currentLocale}`,
640
- label: `${currentLocale} translation`,
663
+ label: translate("builder.translation", {
664
+ locale: resolved.getLocaleLabel?.(currentLocale) ?? currentLocale
665
+ }),
641
666
  value: option.translations?.[currentLocale] ?? "",
642
667
  disabled: readOnly,
643
668
  onChange: (value) => actions.setLocaleTranslation(currentLocale, { kind: "option", id: option.id }, "label", value)
@@ -662,23 +687,25 @@ function createMuiToolbarSlot(options) {
662
687
  onMoveDown,
663
688
  onRemove,
664
689
  readOnly,
665
- components
690
+ components,
691
+ translate
666
692
  }) {
667
693
  const { IconButton: IconButton2 } = components;
668
694
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
669
695
  import_material11.Stack,
670
696
  {
697
+ ...resolved.muiSlotProps?.stack,
671
698
  "data-mui-slot": "toolbar",
672
699
  direction: "row",
673
700
  spacing: resolved.dense ? 0.5 : 1,
674
701
  alignItems: "center",
675
- sx: { mb: resolved.dense ? 1 : 2 },
702
+ sx: resolved.muiSlotProps?.stack?.sx ?? { mb: resolved.dense ? 1 : 2 },
676
703
  children: [
677
704
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
678
705
  IconButton2,
679
706
  {
680
707
  actionType: "moveUp",
681
- title: `Move ${title} up`,
708
+ title: translate("builder.moveUp", { title }),
682
709
  disabled: readOnly || index === 0,
683
710
  onClick: onMoveUp
684
711
  }
@@ -687,7 +714,7 @@ function createMuiToolbarSlot(options) {
687
714
  IconButton2,
688
715
  {
689
716
  actionType: "moveDown",
690
- title: `Move ${title} down`,
717
+ title: translate("builder.moveDown", { title }),
691
718
  disabled: readOnly || index === total - 1,
692
719
  onClick: onMoveDown
693
720
  }
@@ -696,7 +723,7 @@ function createMuiToolbarSlot(options) {
696
723
  IconButton2,
697
724
  {
698
725
  actionType: "delete",
699
- title: `Delete ${title}`,
726
+ title: translate("builder.delete", { title }),
700
727
  disabled: readOnly || kind !== "page" && total <= 1,
701
728
  onClick: onRemove
702
729
  }
@@ -723,6 +750,16 @@ var FIELD_TYPES = [
723
750
  function isFieldType(value) {
724
751
  return FIELD_TYPES.some((type) => type === value);
725
752
  }
753
+ function conditionOperators(field) {
754
+ if (field.type === "multi-select") return ["contains", "not_empty"];
755
+ if (field.type === "text" || field.type === "textarea") {
756
+ return ["equals", "not_equals", "contains", "not_empty"];
757
+ }
758
+ return ["equals", "not_equals", "not_empty"];
759
+ }
760
+ function isConditionOperator(value) {
761
+ return ["equals", "not_equals", "contains", "not_empty"].some((operator) => operator === value);
762
+ }
726
763
  function numericValue(value) {
727
764
  if (value.trim().length === 0) return void 0;
728
765
  const parsed = Number(value);
@@ -745,27 +782,32 @@ function createMuiFieldEditorSlot(options) {
745
782
  index,
746
783
  currentLocale,
747
784
  policy,
785
+ features,
748
786
  readOnly,
749
787
  actions,
750
- components
788
+ components,
789
+ translate
751
790
  }) {
752
791
  const { Button: Button2, Checkbox: Checkbox2, Select: Select2, TextArea, TextInput } = components;
753
792
  const allowedTypes = policy?.allowedFieldTypes ?? FIELD_TYPES;
754
793
  const pageId = schema.pages?.find((page) => page.questionIds.includes(field.id))?.id ?? "";
755
794
  const condition = field.displayCondition;
756
795
  const conditionSources = schema.fields.slice(0, index);
796
+ const conditionSource = conditionSources.find((source) => source.id === condition?.questionId);
757
797
  const titleErrorId = field.title.trim().length === 0 ? `mui-field-${field.id}-title-error` : void 0;
758
798
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
759
799
  import_material12.Card,
760
800
  {
801
+ ...resolved.muiSlotProps?.card,
761
802
  "data-mui-slot": "field-editor",
762
803
  variant: "outlined",
763
- sx: { mb: resolved.dense ? 1 : 2, p: resolved.dense ? 1.5 : 2 },
804
+ sx: resolved.muiSlotProps?.card?.sx ?? { mb: resolved.dense ? 1 : 2, p: resolved.dense ? 1.5 : 2 },
764
805
  children: [
765
806
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
766
807
  Toolbar,
767
808
  {
768
809
  schema,
810
+ translate,
769
811
  kind: "field",
770
812
  targetId: field.id,
771
813
  index,
@@ -779,7 +821,7 @@ function createMuiFieldEditorSlot(options) {
779
821
  components
780
822
  }
781
823
  ),
782
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { spacing: resolved.dense ? 1 : 2, children: [
824
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { ...resolved.muiSlotProps?.stack, spacing: resolved.dense ? 1 : 2, children: [
783
825
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material12.Typography, { variant: "subtitle1", fontWeight: "bold", children: field.title }),
784
826
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { direction: { xs: "column", md: "row" }, spacing: resolved.dense ? 1 : 2, children: [
785
827
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
@@ -787,11 +829,11 @@ function createMuiFieldEditorSlot(options) {
787
829
  {
788
830
  id: `mui-field-${field.id}-title`,
789
831
  name: `fields.${field.id}.title`,
790
- label: "Question title",
832
+ label: translate("builder.questionTitle"),
791
833
  value: field.title,
792
834
  required: true,
793
835
  error: field.title.trim().length === 0,
794
- helperText: field.title.trim().length === 0 ? "Required" : "",
836
+ helperText: field.title.trim().length === 0 ? translate("builder.required") : "",
795
837
  "aria-describedby": titleErrorId,
796
838
  disabled: readOnly,
797
839
  onChange: (value) => actions.setSourceText({ kind: "field", id: field.id }, "title", value)
@@ -802,12 +844,11 @@ function createMuiFieldEditorSlot(options) {
802
844
  {
803
845
  id: `mui-field-${field.id}-type`,
804
846
  name: `fields.${field.id}.type`,
805
- label: "Type",
806
- value: field.type,
807
- options: FIELD_TYPES.map((type) => ({
847
+ label: translate("builder.type"),
848
+ value: allowedTypes.includes(field.type) ? field.type : "",
849
+ options: FIELD_TYPES.filter((type) => allowedTypes.includes(type)).map((type) => ({
808
850
  value: type,
809
- label: type,
810
- disabled: !allowedTypes.includes(type)
851
+ label: translate(`builder.fieldType.${type}`)
811
852
  })),
812
853
  disabled: readOnly,
813
854
  onChange: (value) => {
@@ -821,7 +862,7 @@ function createMuiFieldEditorSlot(options) {
821
862
  {
822
863
  id: `mui-field-${field.id}-description`,
823
864
  name: `fields.${field.id}.description`,
824
- label: "Description",
865
+ label: translate("builder.description"),
825
866
  value: field.description ?? "",
826
867
  rows: resolved.dense ? 2 : 3,
827
868
  disabled: readOnly,
@@ -833,20 +874,20 @@ function createMuiFieldEditorSlot(options) {
833
874
  {
834
875
  id: `mui-field-${field.id}-required`,
835
876
  name: `fields.${field.id}.required`,
836
- label: "Required",
877
+ label: translate("builder.required"),
837
878
  checked: field.required,
838
879
  disabled: readOnly,
839
880
  onChange: (checked) => actions.updateField(field.id, (current) => ({ ...current, required: checked }))
840
881
  }
841
882
  ),
842
- schema.pages === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
883
+ features?.pages === false || schema.pages === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
843
884
  Select2,
844
885
  {
845
886
  id: `mui-field-${field.id}-page`,
846
- label: "Page",
887
+ label: translate("builder.questionPage"),
847
888
  value: pageId,
848
889
  options: [
849
- { value: "", label: "Unassigned" },
890
+ { value: "", label: translate("builder.unassigned") },
850
891
  ...schema.pages.map((page) => ({ value: page.id, label: page.title ?? page.id }))
851
892
  ],
852
893
  disabled: readOnly,
@@ -858,7 +899,7 @@ function createMuiFieldEditorSlot(options) {
858
899
  TextInput,
859
900
  {
860
901
  id: `mui-field-${field.id}-minimum`,
861
- label: "Minimum",
902
+ label: translate("builder.minimum"),
862
903
  type: "number",
863
904
  value: field.min === void 0 ? "" : String(field.min),
864
905
  disabled: readOnly,
@@ -869,7 +910,7 @@ function createMuiFieldEditorSlot(options) {
869
910
  TextInput,
870
911
  {
871
912
  id: `mui-field-${field.id}-maximum`,
872
- label: "Maximum",
913
+ label: translate("builder.maximum"),
873
914
  type: "number",
874
915
  value: field.max === void 0 ? "" : String(field.max),
875
916
  disabled: readOnly,
@@ -877,15 +918,15 @@ function createMuiFieldEditorSlot(options) {
877
918
  }
878
919
  )
879
920
  ] }) : null,
880
- conditionSources.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { direction: { xs: "column", md: "row" }, spacing: resolved.dense ? 1 : 2, children: [
921
+ features?.conditions === false || conditionSources.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { direction: { xs: "column", md: "row" }, spacing: resolved.dense ? 1 : 2, children: [
881
922
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
882
923
  Select2,
883
924
  {
884
925
  id: `mui-field-${field.id}-condition-source`,
885
- label: "Display condition",
926
+ label: translate("builder.displayCondition"),
886
927
  value: condition?.questionId ?? "",
887
928
  options: [
888
- { value: "", label: "Always visible" },
929
+ { value: "", label: translate("builder.alwaysVisible") },
889
930
  ...conditionSources.map((source) => ({ value: source.id, label: source.title }))
890
931
  ],
891
932
  disabled: readOnly,
@@ -895,27 +936,47 @@ function createMuiFieldEditorSlot(options) {
895
936
  )
896
937
  }
897
938
  ),
898
- condition === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
899
- TextInput,
900
- {
901
- id: `mui-field-${field.id}-condition-value`,
902
- label: "Condition value",
903
- value: condition.value === void 0 ? "" : String(condition.value),
904
- disabled: readOnly,
905
- onChange: (value) => actions.setDisplayCondition(field.id, { ...condition, value })
906
- }
907
- )
939
+ condition === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
940
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
941
+ Select2,
942
+ {
943
+ id: `mui-field-${field.id}-condition-operator`,
944
+ label: translate("builder.conditionOperator"),
945
+ value: condition.operator,
946
+ options: (conditionSource === void 0 ? [] : conditionOperators(conditionSource)).map(
947
+ (operator) => ({ value: operator, label: translate(`builder.operator.${operator}`) })
948
+ ),
949
+ disabled: readOnly,
950
+ onChange: (value) => {
951
+ if (!isConditionOperator(value)) return;
952
+ actions.setDisplayCondition(
953
+ field.id,
954
+ value === "not_empty" ? { questionId: condition.questionId, operator: value } : { ...condition, operator: value, value: condition.value ?? "" }
955
+ );
956
+ }
957
+ }
958
+ ),
959
+ condition.operator === "not_empty" ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
960
+ TextInput,
961
+ {
962
+ id: `mui-field-${field.id}-condition-value`,
963
+ label: translate("builder.conditionValue"),
964
+ value: condition.value === void 0 ? "" : String(condition.value),
965
+ disabled: readOnly,
966
+ onChange: (value) => actions.setDisplayCondition(field.id, { ...condition, value })
967
+ }
968
+ )
969
+ ] })
908
970
  ] }),
909
971
  currentLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { spacing: resolved.dense ? 1 : 2, children: [
910
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Typography, { variant: "subtitle2", children: [
911
- currentLocale,
912
- " translation"
913
- ] }),
972
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material12.Typography, { variant: "subtitle2", children: translate("builder.translation", {
973
+ locale: resolved.getLocaleLabel?.(currentLocale) ?? currentLocale
974
+ }) }),
914
975
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
915
976
  TextInput,
916
977
  {
917
978
  id: `mui-field-${field.id}-${currentLocale}-title`,
918
- label: "Translated question title",
979
+ label: translate("builder.translatedQuestionTitle"),
919
980
  value: field.translations?.[currentLocale]?.title ?? "",
920
981
  disabled: readOnly,
921
982
  onChange: (value) => actions.setLocaleTranslation(currentLocale, { kind: "field", id: field.id }, "title", value)
@@ -925,7 +986,7 @@ function createMuiFieldEditorSlot(options) {
925
986
  TextArea,
926
987
  {
927
988
  id: `mui-field-${field.id}-${currentLocale}-description`,
928
- label: "Translated description",
989
+ label: translate("builder.translatedDescription"),
929
990
  value: field.translations?.[currentLocale]?.description ?? "",
930
991
  disabled: readOnly,
931
992
  onChange: (value) => actions.setLocaleTranslation(currentLocale, { kind: "field", id: field.id }, "description", value)
@@ -933,7 +994,7 @@ function createMuiFieldEditorSlot(options) {
933
994
  )
934
995
  ] }),
935
996
  "options" in field ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { "data-mui-slot": "options", spacing: resolved.dense ? 1 : 2, children: [
936
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material12.Typography, { variant: "subtitle2", children: "Options" }),
997
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material12.Typography, { variant: "subtitle2", children: translate("builder.options") }),
937
998
  field.options.map((option, optionIndex) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
938
999
  OptionEditor,
939
1000
  {
@@ -942,6 +1003,7 @@ function createMuiFieldEditorSlot(options) {
942
1003
  option,
943
1004
  index: optionIndex,
944
1005
  currentLocale,
1006
+ translate,
945
1007
  readOnly,
946
1008
  actions,
947
1009
  components
@@ -955,7 +1017,7 @@ function createMuiFieldEditorSlot(options) {
955
1017
  targetId: field.id,
956
1018
  disabled: readOnly || policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
957
1019
  onClick: () => actions.addOption(field.id),
958
- children: "Add option"
1020
+ children: translate("builder.addOption")
959
1021
  }
960
1022
  )
961
1023
  ] }) : null
@@ -968,6 +1030,7 @@ function createMuiFieldEditorSlot(options) {
968
1030
  var MuiFieldEditorSlot = createMuiFieldEditorSlot();
969
1031
 
970
1032
  // src/slots/Localization.tsx
1033
+ var import_icons_material2 = require("@mui/icons-material");
971
1034
  var import_material13 = require("@mui/material");
972
1035
  var import_react = require("react");
973
1036
  var import_jsx_runtime14 = require("react/jsx-runtime");
@@ -980,27 +1043,152 @@ function createMuiLocalizationSlot(options) {
980
1043
  onAutoTranslate,
981
1044
  isTranslating,
982
1045
  translationError,
1046
+ policy,
1047
+ translationAdapterAvailable,
983
1048
  readOnly,
984
1049
  actions,
985
- components
1050
+ components,
1051
+ translate
986
1052
  }) {
987
- const { Button: Button2, ErrorMessage, TextArea, TextInput } = components;
1053
+ const { Button: Button2, ErrorMessage, Select: Select2, TextArea, TextInput } = components;
988
1054
  const [newLocale, setNewLocale] = (0, import_react.useState)("");
989
1055
  const locales = Array.from(
990
- /* @__PURE__ */ new Set([
991
- ...schema.supportedLocales ?? [],
992
- ...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale]
993
- ])
1056
+ new Set((schema.supportedLocales ?? []).filter((locale) => locale !== schema.defaultLocale))
994
1057
  );
1058
+ const editingLocaleConfigured = locales.includes(currentLocale);
1059
+ const registeredLocales = /* @__PURE__ */ new Set([
1060
+ ...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
1061
+ ...schema.supportedLocales ?? []
1062
+ ]);
1063
+ const availableAllowedLocales = policy?.allowedLocales?.filter((locale) => !registeredLocales.has(locale));
1064
+ const localeLimitReached = policy?.maxLocales !== void 0 && registeredLocales.size >= policy.maxLocales;
1065
+ const normalizedLocale = newLocale.trim();
1066
+ const localeAllowed = policy?.allowedLocales === void 0 || policy.allowedLocales.includes(normalizedLocale);
1067
+ const canAddLocale = !readOnly && normalizedLocale.length > 0 && localeAllowed && !registeredLocales.has(normalizedLocale) && !localeLimitReached;
995
1068
  const handleTabChange = (_event, value) => onCurrentLocaleChange(value);
996
1069
  const addLocale = () => {
997
- const normalized = newLocale.trim();
998
- if (normalized.length === 0) return;
999
- const result = actions.addLocale(normalized);
1070
+ if (!canAddLocale) return;
1071
+ const result = actions.addLocale(normalizedLocale);
1000
1072
  if (!result.success) return;
1001
- onCurrentLocaleChange(normalized);
1073
+ onCurrentLocaleChange(normalizedLocale);
1002
1074
  setNewLocale("");
1003
1075
  };
1076
+ const stackProps = resolved.muiSlotProps?.stack;
1077
+ const collapsible = resolved.localizationOptions?.collapsible ?? false;
1078
+ const content = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { ...stackProps, spacing: resolved.dense ? 1 : 2, children: [
1079
+ !collapsible ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "subtitle1", fontWeight: "bold", children: translate("builder.localization") }) : null,
1080
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { ...stackProps, direction: { xs: "column", sm: "row" }, spacing: resolved.dense ? 1 : 2, children: [
1081
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1082
+ TextInput,
1083
+ {
1084
+ id: "mui-builder-default-locale",
1085
+ label: translate("builder.defaultLocale"),
1086
+ value: schema.defaultLocale ?? "",
1087
+ disabled: readOnly,
1088
+ onChange: (value) => {
1089
+ const result = actions.setDefaultLocale(value);
1090
+ if (result.success && value === currentLocale) onCurrentLocaleChange("");
1091
+ }
1092
+ }
1093
+ ),
1094
+ availableAllowedLocales === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1095
+ TextInput,
1096
+ {
1097
+ id: "mui-builder-new-locale",
1098
+ label: translate("builder.addLocale"),
1099
+ value: newLocale,
1100
+ disabled: readOnly || localeLimitReached,
1101
+ onChange: setNewLocale
1102
+ }
1103
+ ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1104
+ Select2,
1105
+ {
1106
+ id: "mui-builder-new-locale",
1107
+ label: translate("builder.addLocale"),
1108
+ value: newLocale,
1109
+ options: [
1110
+ { value: "", label: "\u2014" },
1111
+ ...availableAllowedLocales.map((locale) => ({
1112
+ value: locale,
1113
+ label: resolved.getLocaleLabel?.(locale) ?? locale
1114
+ }))
1115
+ ],
1116
+ disabled: readOnly || localeLimitReached || availableAllowedLocales.length === 0,
1117
+ onChange: setNewLocale
1118
+ }
1119
+ ),
1120
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Button2, { variant: "primary", action: "addLocale", disabled: !canAddLocale, onClick: addLocale, children: translate("builder.addLocale") })
1121
+ ] }),
1122
+ locales.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1123
+ import_material13.Tabs,
1124
+ {
1125
+ value: editingLocaleConfigured ? currentLocale : false,
1126
+ onChange: handleTabChange,
1127
+ variant: "scrollable",
1128
+ scrollButtons: "auto",
1129
+ "aria-label": translate("builder.translationLocale"),
1130
+ children: locales.map((locale) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1131
+ import_material13.Tab,
1132
+ {
1133
+ value: locale,
1134
+ label: resolved.getLocaleLabel?.(locale) ?? locale,
1135
+ disabled: readOnly && locale !== currentLocale
1136
+ },
1137
+ locale
1138
+ ))
1139
+ }
1140
+ ),
1141
+ !translationAdapterAvailable ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1142
+ Button2,
1143
+ {
1144
+ variant: "secondary",
1145
+ disabled: readOnly || !editingLocaleConfigured || isTranslating,
1146
+ onClick: onAutoTranslate,
1147
+ children: isTranslating ? translate("builder.translating") : translate("builder.autoTranslate")
1148
+ }
1149
+ ),
1150
+ translationError === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ErrorMessage, { message: translationError }),
1151
+ !editingLocaleConfigured ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "body2", color: "text.secondary", children: translate("builder.selectLocale") }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { ...stackProps, spacing: resolved.dense ? 1 : 2, children: [
1152
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1153
+ TextInput,
1154
+ {
1155
+ id: `mui-builder-${currentLocale}-form-title`,
1156
+ label: translate("builder.translatedFormTitle"),
1157
+ value: schema.translations?.[currentLocale]?.title ?? "",
1158
+ disabled: readOnly,
1159
+ onChange: (value) => actions.setLocaleTranslation(currentLocale, { kind: "form" }, "title", value)
1160
+ }
1161
+ ),
1162
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1163
+ TextArea,
1164
+ {
1165
+ id: `mui-builder-${currentLocale}-form-description`,
1166
+ label: translate("builder.translatedFormDescription"),
1167
+ value: schema.translations?.[currentLocale]?.description ?? "",
1168
+ disabled: readOnly,
1169
+ onChange: (value) => actions.setLocaleTranslation(currentLocale, { kind: "form" }, "description", value)
1170
+ }
1171
+ ),
1172
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1173
+ TextInput,
1174
+ {
1175
+ id: `mui-builder-${currentLocale}-completion-message`,
1176
+ label: translate("builder.translatedCompletionMessage"),
1177
+ value: schema.translations?.[currentLocale]?.completionMessage ?? "",
1178
+ disabled: readOnly,
1179
+ onChange: (value) => actions.setLocaleTranslation(currentLocale, { kind: "form" }, "completionMessage", value)
1180
+ }
1181
+ )
1182
+ ] })
1183
+ ] });
1184
+ const configured = locales.length > 0;
1185
+ const defaultExpanded = resolved.localizationOptions?.defaultExpanded === "when-configured" ? configured : resolved.localizationOptions?.defaultExpanded ?? false;
1186
+ if (collapsible) {
1187
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Accordion, { ...resolved.muiSlotProps?.accordion, "data-mui-slot": "localization", defaultExpanded, children: [
1188
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.AccordionSummary, { expandIcon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_icons_material2.ExpandMore, {}), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "subtitle1", fontWeight: "bold", children: translate("builder.localization") }) }),
1189
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.AccordionDetails, { children: content })
1190
+ ] });
1191
+ }
1004
1192
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1005
1193
  import_material13.Box,
1006
1194
  {
@@ -1012,95 +1200,7 @@ function createMuiLocalizationSlot(options) {
1012
1200
  borderColor: "divider",
1013
1201
  borderRadius: 1
1014
1202
  },
1015
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { spacing: resolved.dense ? 1 : 2, children: [
1016
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "subtitle1", fontWeight: "bold", children: "Localization" }),
1017
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1018
- TextInput,
1019
- {
1020
- id: "mui-builder-completion-message",
1021
- label: "Completion message",
1022
- value: schema.completionMessage ?? "",
1023
- disabled: readOnly,
1024
- onChange: (value) => actions.setSourceText({ kind: "form" }, "completionMessage", value)
1025
- }
1026
- ),
1027
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { direction: { xs: "column", sm: "row" }, spacing: resolved.dense ? 1 : 2, children: [
1028
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1029
- TextInput,
1030
- {
1031
- id: "mui-builder-default-locale",
1032
- label: "Default locale",
1033
- value: schema.defaultLocale ?? "",
1034
- disabled: readOnly,
1035
- onChange: (value) => actions.setDefaultLocale(value)
1036
- }
1037
- ),
1038
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1039
- TextInput,
1040
- {
1041
- id: "mui-builder-new-locale",
1042
- label: "Add locale",
1043
- value: newLocale,
1044
- disabled: readOnly,
1045
- onChange: setNewLocale
1046
- }
1047
- ),
1048
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Button2, { disabled: readOnly || newLocale.trim().length === 0, onClick: addLocale, children: "Add locale" })
1049
- ] }),
1050
- locales.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1051
- import_material13.Tabs,
1052
- {
1053
- value: locales.includes(currentLocale) ? currentLocale : false,
1054
- onChange: handleTabChange,
1055
- variant: "scrollable",
1056
- scrollButtons: "auto",
1057
- "aria-label": "Translation locale",
1058
- children: locales.map((locale) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Tab, { value: locale, label: locale, disabled: readOnly && locale !== currentLocale }, locale))
1059
- }
1060
- ),
1061
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1062
- Button2,
1063
- {
1064
- variant: "secondary",
1065
- disabled: readOnly || currentLocale.length === 0 || isTranslating,
1066
- onClick: onAutoTranslate,
1067
- children: isTranslating ? "Translating\u2026" : "Translate all text"
1068
- }
1069
- ),
1070
- translationError === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ErrorMessage, { message: translationError }),
1071
- currentLocale.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "body2", color: "text.secondary", children: "Select a locale to edit translations." }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { spacing: resolved.dense ? 1 : 2, children: [
1072
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1073
- TextInput,
1074
- {
1075
- id: `mui-builder-${currentLocale}-form-title`,
1076
- label: "Translated form title",
1077
- value: schema.translations?.[currentLocale]?.title ?? "",
1078
- disabled: readOnly,
1079
- onChange: (value) => actions.setLocaleTranslation(currentLocale, { kind: "form" }, "title", value)
1080
- }
1081
- ),
1082
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1083
- TextArea,
1084
- {
1085
- id: `mui-builder-${currentLocale}-form-description`,
1086
- label: "Translated form description",
1087
- value: schema.translations?.[currentLocale]?.description ?? "",
1088
- disabled: readOnly,
1089
- onChange: (value) => actions.setLocaleTranslation(currentLocale, { kind: "form" }, "description", value)
1090
- }
1091
- ),
1092
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1093
- TextInput,
1094
- {
1095
- id: `mui-builder-${currentLocale}-completion-message`,
1096
- label: "Translated completion message",
1097
- value: schema.translations?.[currentLocale]?.completionMessage ?? "",
1098
- disabled: readOnly,
1099
- onChange: (value) => actions.setLocaleTranslation(currentLocale, { kind: "form" }, "completionMessage", value)
1100
- }
1101
- )
1102
- ] })
1103
- ] })
1203
+ children: content
1104
1204
  }
1105
1205
  );
1106
1206
  };
@@ -1109,6 +1209,7 @@ var MuiLocalizationSlot = createMuiLocalizationSlot();
1109
1209
 
1110
1210
  // src/slots/index.ts
1111
1211
  var muiBuilderSlots = {
1212
+ sectionOrder: DEFAULT_MUI_SECTION_ORDER,
1112
1213
  toolbar: createMuiToolbarSlot(),
1113
1214
  fieldEditor: createMuiFieldEditorSlot(),
1114
1215
  optionEditor: createMuiOptionEditorSlot(),
@@ -1116,6 +1217,7 @@ var muiBuilderSlots = {
1116
1217
  };
1117
1218
  function createMuiBuilderSlots(options, customOverrides = {}) {
1118
1219
  return {
1220
+ sectionOrder: options?.layoutOptions?.sectionOrder ?? DEFAULT_MUI_SECTION_ORDER,
1119
1221
  toolbar: createMuiToolbarSlot(options),
1120
1222
  fieldEditor: createMuiFieldEditorSlot(options),
1121
1223
  optionEditor: createMuiOptionEditorSlot(options),
@@ -1139,19 +1241,35 @@ var import_react3 = require("react");
1139
1241
  var import_jsx_runtime15 = require("react/jsx-runtime");
1140
1242
  function MuiFormBuilder({
1141
1243
  muiOptions,
1244
+ layoutOptions,
1245
+ localizationOptions,
1246
+ muiSlotProps,
1142
1247
  components: customComponents,
1143
1248
  slots: customSlots,
1144
1249
  ...props
1145
1250
  }) {
1251
+ const resolvedMuiOptions = (0, import_react3.useMemo)(
1252
+ () => ({
1253
+ ...muiOptions,
1254
+ ...layoutOptions === void 0 ? {} : { layoutOptions },
1255
+ ...localizationOptions === void 0 ? {} : { localizationOptions },
1256
+ ...muiSlotProps === void 0 ? {} : { muiSlotProps }
1257
+ }),
1258
+ [layoutOptions, localizationOptions, muiOptions, muiSlotProps]
1259
+ );
1146
1260
  const components = (0, import_react3.useMemo)(
1147
- () => createMuiBuilderComponents(muiOptions, customComponents),
1148
- [muiOptions, customComponents]
1261
+ () => createMuiBuilderComponents(resolvedMuiOptions, customComponents),
1262
+ [resolvedMuiOptions, customComponents]
1263
+ );
1264
+ const slots = (0, import_react3.useMemo)(
1265
+ () => createMuiBuilderSlots(resolvedMuiOptions, customSlots),
1266
+ [resolvedMuiOptions, customSlots]
1149
1267
  );
1150
- const slots = (0, import_react3.useMemo)(() => createMuiBuilderSlots(muiOptions, customSlots), [muiOptions, customSlots]);
1151
1268
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react2.FormBuilder, { ...props, components, disableDefaultStyles: true, slots });
1152
1269
  }
1153
1270
  // Annotate the CommonJS export names for ESM import in node:
1154
1271
  0 && (module.exports = {
1272
+ DEFAULT_MUI_SECTION_ORDER,
1155
1273
  MuiButtonAdapter,
1156
1274
  MuiCheckboxAdapter,
1157
1275
  MuiErrorMessageAdapter,