@form-engine-ts/mui 2.9.6 → 3.1.0

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
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  DEFAULT_MUI_SECTION_ORDER: () => DEFAULT_MUI_SECTION_ORDER,
24
+ MUI_LOCALIZATION_SECTION_ORDERS: () => MUI_LOCALIZATION_SECTION_ORDERS,
24
25
  MuiButtonAdapter: () => MuiButtonAdapter,
25
26
  MuiCheckboxAdapter: () => MuiCheckboxAdapter,
26
27
  MuiErrorMessageAdapter: () => MuiErrorMessageAdapter,
@@ -55,6 +56,7 @@ __export(index_exports, {
55
56
  mergeMuiAdapterOptions: () => mergeMuiAdapterOptions,
56
57
  muiBuilderComponents: () => muiBuilderComponents,
57
58
  muiBuilderSlots: () => muiBuilderSlots,
59
+ muiDefaultFieldTypeIcon: () => muiDefaultFieldTypeIcon,
58
60
  muiDefaultIconResolver: () => muiDefaultIconResolver,
59
61
  resolveMuiAdapterOptions: () => resolveMuiAdapterOptions,
60
62
  useResolvedMuiAdapterOptions: () => useResolvedMuiAdapterOptions
@@ -72,8 +74,15 @@ var DEFAULT_MUI_SECTION_ORDER = [
72
74
  "basicSettings",
73
75
  "questions",
74
76
  "addQuestion",
75
- "localization"
77
+ "localization",
78
+ "completionMessage"
76
79
  ];
80
+ var MUI_LOCALIZATION_SECTION_ORDERS = {
81
+ top: ["localization", "basicSettings", "completionMessage", "questions", "addQuestion"],
82
+ beforeQuestions: ["basicSettings", "localization", "completionMessage", "questions", "addQuestion"],
83
+ afterQuestions: ["basicSettings", "questions", "addQuestion", "localization", "completionMessage"],
84
+ bottom: ["basicSettings", "completionMessage", "questions", "addQuestion", "localization"]
85
+ };
77
86
  function resolveMuiAdapterOptions(options = {}) {
78
87
  const buttonVariant = options.buttonVariant ?? "contained";
79
88
  return {
@@ -99,8 +108,13 @@ function resolveMuiAdapterOptions(options = {}) {
99
108
  collapsible: options.localizationOptions?.collapsible ?? false,
100
109
  defaultExpanded: options.localizationOptions?.defaultExpanded ?? false,
101
110
  showSummary: options.localizationOptions?.showSummary ?? false,
111
+ ...options.localizationOptions?.availableLocales === void 0 ? {} : { availableLocales: options.localizationOptions.availableLocales },
112
+ ...options.localizationOptions?.placement === void 0 ? {} : { placement: options.localizationOptions.placement },
113
+ ...options.localizationOptions?.renderSummary === void 0 ? {} : { renderSummary: options.localizationOptions.renderSummary },
102
114
  ...options.localizationOptions?.emptyStateMessage === void 0 ? {} : { emptyStateMessage: options.localizationOptions.emptyStateMessage },
103
- defaultLocaleControl: options.localizationOptions?.defaultLocaleControl ?? "editable"
115
+ defaultLocaleControl: options.localizationOptions?.defaultLocaleControl ?? "editable",
116
+ noWrapActions: options.localizationOptions?.noWrapActions ?? true,
117
+ autoFocusNewTab: options.localizationOptions?.autoFocusNewTab ?? true
104
118
  },
105
119
  muiSlotProps: options.muiSlotProps ?? {}
106
120
  };
@@ -138,6 +152,7 @@ function createMuiButtonAdapter(options) {
138
152
  "aria-describedby": ariaDescribedBy,
139
153
  "aria-labelledby": ariaLabelledBy,
140
154
  onClick,
155
+ noWrap,
141
156
  children,
142
157
  title,
143
158
  variant,
@@ -145,9 +160,11 @@ function createMuiButtonAdapter(options) {
145
160
  targetId
146
161
  }) {
147
162
  const resolved = useResolvedMuiAdapterOptions(options);
163
+ const buttonSlotProps = resolved.muiSlotProps?.button;
148
164
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
149
165
  import_material.Button,
150
166
  {
167
+ ...buttonSlotProps,
151
168
  id,
152
169
  className,
153
170
  type: "button",
@@ -161,9 +178,9 @@ function createMuiButtonAdapter(options) {
161
178
  "data-target-id": targetId,
162
179
  onClick,
163
180
  color: variant === "danger" ? "error" : "primary",
164
- variant: resolved.buttonVariants?.[variant ?? "primary"] ?? resolved.buttonVariant,
181
+ variant: buttonSlotProps?.variant ?? resolved.buttonVariants?.[variant ?? "primary"] ?? resolved.buttonVariant,
165
182
  fullWidth: resolved.buttonFullWidth ?? false,
166
- sx: { whiteSpace: "nowrap" },
183
+ sx: { whiteSpace: noWrap === false ? "normal" : "nowrap" },
167
184
  children
168
185
  }
169
186
  );
@@ -192,6 +209,7 @@ function createMuiCheckboxAdapter(options) {
192
209
  label
193
210
  }) {
194
211
  const resolved = useResolvedMuiAdapterOptions(options);
212
+ const checkboxSlotProps = resolved.muiSlotProps?.checkbox;
195
213
  const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
196
214
  const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
197
215
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_material2.FormControl, { className, error: Boolean(error), required, disabled: disabled || readOnly, children: [
@@ -202,9 +220,10 @@ function createMuiCheckboxAdapter(options) {
202
220
  control: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
203
221
  import_material2.Checkbox,
204
222
  {
223
+ ...checkboxSlotProps,
205
224
  id,
206
225
  name,
207
- size: resolved.size,
226
+ size: checkboxSlotProps?.size ?? resolved.size,
208
227
  checked,
209
228
  required,
210
229
  disabled: disabled || readOnly,
@@ -316,16 +335,18 @@ function createMuiIconButtonAdapter(options) {
316
335
  title
317
336
  }) {
318
337
  const resolved = useResolvedMuiAdapterOptions(options);
338
+ const iconButtonSlotProps = resolved.muiSlotProps?.iconButton;
319
339
  const actionLabel = actionType === void 0 ? "Action" : resolved.getActionLabel?.(actionType) ?? FALLBACK_ACTION_LABELS[actionType];
320
340
  const tooltip = title ?? actionLabel;
321
341
  const color = actionType === "delete" ? "error" : actionType === "add" ? "primary" : "default";
322
342
  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)(
323
343
  import_material5.IconButton,
324
344
  {
345
+ ...iconButtonSlotProps,
325
346
  id,
326
347
  className,
327
348
  type: "button",
328
- size: resolved.size,
349
+ size: iconButtonSlotProps?.size ?? resolved.size,
329
350
  color,
330
351
  disabled: disabled || readOnly,
331
352
  "aria-label": ariaLabel ?? tooltip,
@@ -391,6 +412,9 @@ var MuiSectionAdapter = createMuiSectionAdapter();
391
412
  // src/adapters/Select.tsx
392
413
  var import_material7 = require("@mui/material");
393
414
  var import_jsx_runtime7 = require("react/jsx-runtime");
415
+ function normalizeOptions(options) {
416
+ return options.map((option) => typeof option === "string" ? { value: option, label: option } : option);
417
+ }
394
418
  function createMuiSelectAdapter(options) {
395
419
  return function MuiSelect({
396
420
  id,
@@ -407,13 +431,19 @@ function createMuiSelectAdapter(options) {
407
431
  helperText,
408
432
  value,
409
433
  onChange,
410
- options: selectOptions
434
+ onKeyDown,
435
+ options: selectOptions,
436
+ renderOption,
437
+ renderValue
411
438
  }) {
412
439
  const resolved = useResolvedMuiAdapterOptions(options);
440
+ const normalizedOptions = normalizeOptions(selectOptions);
441
+ const selectSlotProps = resolved.muiSlotProps?.select;
442
+ const menuProps = { ...selectSlotProps?.MenuProps, ...resolved.muiSlotProps?.selectMenu };
413
443
  const labelId = id === void 0 || label === void 0 ? void 0 : `${id}-label`;
414
444
  const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
415
445
  const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
416
- const handleChange = (event) => onChange(event.target.value);
446
+ const handleChange = (event) => onChange(String(event.target.value));
417
447
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
418
448
  import_material7.FormControl,
419
449
  {
@@ -429,20 +459,39 @@ function createMuiSelectAdapter(options) {
429
459
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
430
460
  import_material7.Select,
431
461
  {
462
+ ...selectSlotProps,
432
463
  id,
433
464
  labelId,
434
465
  name,
435
466
  label,
436
467
  value,
468
+ disabled,
437
469
  onChange: handleChange,
470
+ onKeyDown,
438
471
  required,
439
472
  readOnly,
473
+ variant: selectSlotProps?.variant ?? resolved.variant,
474
+ ...Object.keys(menuProps).length === 0 ? {} : { MenuProps: menuProps },
475
+ renderValue: (selected) => {
476
+ const selectedValue = typeof selected === "string" ? selected : String(selected ?? "");
477
+ const option = normalizedOptions.find((candidate) => candidate.value === selectedValue);
478
+ if (renderValue !== void 0) return renderValue(option);
479
+ if (option === void 0) return selectedValue;
480
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_material7.Box, { display: "flex", alignItems: "center", gap: 0.75, children: [
481
+ option.icon,
482
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: option.label })
483
+ ] });
484
+ },
440
485
  inputProps: {
486
+ ...selectSlotProps?.inputProps,
441
487
  "aria-label": ariaLabel,
442
488
  "aria-describedby": describedBy,
443
489
  "aria-labelledby": ariaLabelledBy
444
490
  },
445
- children: selectOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.MenuItem, { value: option.value, disabled: option.disabled, children: option.label }, option.value))
491
+ children: normalizedOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.MenuItem, { value: option.value, disabled: option.disabled, children: renderOption === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
492
+ option.icon === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.ListItemIcon, { sx: { minWidth: 32 }, children: option.icon }),
493
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.ListItemText, { primary: option.label, secondary: option.description })
494
+ ] }) : renderOption(option) }, option.value))
446
495
  }
447
496
  ),
448
497
  helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.FormHelperText, { id: helperId, children: helperText })
@@ -477,9 +526,11 @@ function createMuiTextAreaAdapter(options) {
477
526
  rows
478
527
  }) {
479
528
  const resolved = useResolvedMuiAdapterOptions(options);
529
+ const textFieldSlotProps = resolved.muiSlotProps?.textField;
480
530
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
481
531
  import_material8.TextField,
482
532
  {
533
+ ...textFieldSlotProps,
483
534
  id,
484
535
  className,
485
536
  name,
@@ -505,7 +556,7 @@ function createMuiTextAreaAdapter(options) {
505
556
  placeholder,
506
557
  fullWidth: resolved.inputFullWidth ?? resolved.fullWidth,
507
558
  size: resolved.size,
508
- variant: resolved.variant,
559
+ variant: textFieldSlotProps?.variant ?? resolved.variant,
509
560
  onChange: (event) => onChange(event.currentTarget.value)
510
561
  }
511
562
  );
@@ -532,6 +583,7 @@ function createMuiTextInputAdapter(options) {
532
583
  helperText,
533
584
  value,
534
585
  onChange,
586
+ onKeyDown,
535
587
  placeholder,
536
588
  maxLength,
537
589
  inputMode,
@@ -541,9 +593,11 @@ function createMuiTextInputAdapter(options) {
541
593
  step
542
594
  }) {
543
595
  const resolved = useResolvedMuiAdapterOptions(options);
596
+ const textFieldSlotProps = resolved.muiSlotProps?.textField;
544
597
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
545
598
  import_material9.TextField,
546
599
  {
600
+ ...textFieldSlotProps,
547
601
  id,
548
602
  className,
549
603
  name,
@@ -572,8 +626,9 @@ function createMuiTextInputAdapter(options) {
572
626
  placeholder,
573
627
  fullWidth: resolved.inputFullWidth ?? resolved.fullWidth,
574
628
  size: resolved.size,
575
- variant: resolved.variant,
576
- onChange: (event) => onChange(event.currentTarget.value)
629
+ variant: textFieldSlotProps?.variant ?? resolved.variant,
630
+ onChange: (event) => onChange(event.currentTarget.value),
631
+ onKeyDown
577
632
  }
578
633
  );
579
634
  };
@@ -605,6 +660,26 @@ function muiDefaultIconResolver(actionType) {
605
660
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.DragHandle, { fontSize: "small" });
606
661
  }
607
662
  }
663
+ function muiDefaultFieldTypeIcon(type) {
664
+ switch (type) {
665
+ case "text":
666
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.TextFields, { fontSize: "small" });
667
+ case "textarea":
668
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Subject, { fontSize: "small" });
669
+ case "number":
670
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Numbers, { fontSize: "small" });
671
+ case "rating":
672
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Star, { fontSize: "small" });
673
+ case "select":
674
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.ArrowDropDown, { fontSize: "small" });
675
+ case "multi-select":
676
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.CheckBox, { fontSize: "small" });
677
+ case "checkbox":
678
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.CheckBox, { fontSize: "small" });
679
+ case "radio":
680
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.RadioButtonChecked, { fontSize: "small" });
681
+ }
682
+ }
608
683
 
609
684
  // src/components.ts
610
685
  function isAdapterOptions(value) {
@@ -621,7 +696,8 @@ function buildMuiBuilderComponents(options) {
621
696
  Section: createMuiSectionAdapter(options),
622
697
  Fieldset: createMuiFieldsetAdapter(options),
623
698
  ErrorMessage: createMuiErrorMessageAdapter(options),
624
- renderIcon: muiDefaultIconResolver
699
+ renderIcon: muiDefaultIconResolver,
700
+ renderFieldTypeIcon: muiDefaultFieldTypeIcon
625
701
  };
626
702
  }
627
703
  var muiBuilderComponents = {
@@ -634,7 +710,8 @@ var muiBuilderComponents = {
634
710
  Section: MuiSectionAdapter,
635
711
  Fieldset: MuiFieldsetAdapter,
636
712
  ErrorMessage: MuiErrorMessageAdapter,
637
- renderIcon: muiDefaultIconResolver
713
+ renderIcon: muiDefaultIconResolver,
714
+ renderFieldTypeIcon: muiDefaultFieldTypeIcon
638
715
  };
639
716
  function createMuiBuilderComponents(optionsOrOverrides = {}, customOverrides) {
640
717
  const options = isAdapterOptions(optionsOrOverrides) ? optionsOrOverrides : void 0;
@@ -643,6 +720,7 @@ function createMuiBuilderComponents(optionsOrOverrides = {}, customOverrides) {
643
720
  }
644
721
 
645
722
  // src/slots/FieldEditor.tsx
723
+ var import_core = require("@form-engine-ts/core");
646
724
  var import_material12 = require("@mui/material");
647
725
 
648
726
  // src/slots/OptionEditor.tsx
@@ -789,16 +867,7 @@ var MuiToolbarSlot = createMuiToolbarSlot();
789
867
 
790
868
  // src/slots/FieldEditor.tsx
791
869
  var import_jsx_runtime13 = require("react/jsx-runtime");
792
- var FIELD_TYPES = [
793
- "text",
794
- "textarea",
795
- "number",
796
- "rating",
797
- "select",
798
- "multi-select",
799
- "checkbox",
800
- "radio"
801
- ];
870
+ var FIELD_TYPES = import_core.DEFAULT_FIELD_TYPE_DEFINITIONS.map((definition) => definition.type);
802
871
  function isFieldType(value) {
803
872
  return FIELD_TYPES.some((type) => type === value);
804
873
  }
@@ -837,7 +906,8 @@ function createMuiFieldEditorSlot(options) {
837
906
  readOnly,
838
907
  actions,
839
908
  components,
840
- translate
909
+ translate,
910
+ slots
841
911
  }) {
842
912
  const resolved = useResolvedMuiAdapterOptions(options);
843
913
  const { Button: Button2, Checkbox: Checkbox2, Select: Select2, TextArea, TextInput } = components;
@@ -848,6 +918,11 @@ function createMuiFieldEditorSlot(options) {
848
918
  const conditionSource = conditionSources.find((source) => source.id === condition?.questionId);
849
919
  const descriptionMode = resolved.fieldEditorOptions?.description ?? "editable";
850
920
  const titleErrorId = field.title.trim().length === 0 ? `mui-field-${field.id}-title-error` : void 0;
921
+ const FieldTypeSelect = slots?.fieldTypeSelect;
922
+ const FieldEditorHeader = slots?.fieldEditorHeader;
923
+ const changeFieldType = (nextType) => {
924
+ if (allowedTypes.includes(nextType)) actions.changeFieldType(field.id, nextType);
925
+ };
851
926
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
852
927
  import_material12.Card,
853
928
  {
@@ -856,26 +931,28 @@ function createMuiFieldEditorSlot(options) {
856
931
  variant: "outlined",
857
932
  sx: resolved.muiSlotProps?.card?.sx ?? { mb: resolved.dense ? 1 : 2, p: resolved.dense ? 1.5 : 2 },
858
933
  children: [
859
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
860
- Toolbar,
861
- {
862
- schema,
863
- translate,
864
- kind: "field",
865
- targetId: field.id,
866
- index,
867
- total: schema.fields.length,
868
- title: field.title,
869
- onMoveUp: () => actions.moveField(field.id, index - 1),
870
- onMoveDown: () => actions.moveField(field.id, index + 1),
871
- onRemove: () => actions.removeField(field.id),
872
- readOnly,
873
- actions,
874
- components
875
- }
876
- ),
934
+ FieldEditorHeader === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
935
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
936
+ Toolbar,
937
+ {
938
+ schema,
939
+ translate,
940
+ kind: "field",
941
+ targetId: field.id,
942
+ index,
943
+ total: schema.fields.length,
944
+ title: field.title,
945
+ onMoveUp: () => actions.moveField(field.id, index - 1),
946
+ onMoveDown: () => actions.moveField(field.id, index + 1),
947
+ onRemove: () => actions.removeField(field.id),
948
+ readOnly,
949
+ actions,
950
+ components
951
+ }
952
+ ),
953
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material12.Typography, { variant: "subtitle1", fontWeight: "bold", children: field.title })
954
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FieldEditorHeader, { field, index, totalFields: schema.fields.length, actions }),
877
955
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { ...resolved.muiSlotProps?.stack, spacing: resolved.dense ? 1 : 2, children: [
878
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material12.Typography, { variant: "subtitle1", fontWeight: "bold", children: field.title }),
879
956
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { direction: { xs: "column", md: "row" }, spacing: resolved.dense ? 1 : 2, children: [
880
957
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
881
958
  TextInput,
@@ -892,22 +969,37 @@ function createMuiFieldEditorSlot(options) {
892
969
  onChange: (value) => actions.setSourceText({ kind: "field", id: field.id }, "title", value)
893
970
  }
894
971
  ),
895
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
972
+ FieldTypeSelect === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
896
973
  Select2,
897
974
  {
898
975
  id: `mui-field-${field.id}-type`,
899
976
  name: `fields.${field.id}.type`,
900
977
  label: translate("builder.type"),
901
978
  value: allowedTypes.includes(field.type) ? field.type : "",
902
- options: FIELD_TYPES.filter((type) => allowedTypes.includes(type)).map((type) => ({
903
- value: type,
904
- label: translate(`builder.fieldType.${type}`)
905
- })),
979
+ options: FIELD_TYPES.filter((type) => allowedTypes.includes(type)).map((type) => {
980
+ const definition = import_core.DEFAULT_FIELD_TYPE_DEFINITIONS.find((candidate) => candidate.type === type);
981
+ return {
982
+ value: type,
983
+ label: translate(definition?.labelKey ?? `builder.fieldType.${type}`),
984
+ icon: components.renderFieldTypeIcon(type),
985
+ ...definition?.category === void 0 ? {} : { kind: definition.category }
986
+ };
987
+ }),
906
988
  disabled: readOnly,
907
989
  onChange: (value) => {
908
- if (isFieldType(value)) actions.changeFieldType(field.id, value);
990
+ if (isFieldType(value)) changeFieldType(value);
909
991
  }
910
992
  }
993
+ ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
994
+ FieldTypeSelect,
995
+ {
996
+ currentType: field.type,
997
+ allowedTypes,
998
+ onChangeType: changeFieldType,
999
+ disabled: readOnly,
1000
+ readOnly,
1001
+ renderIcon: components.renderFieldTypeIcon
1002
+ }
911
1003
  )
912
1004
  ] }),
913
1005
  descriptionMode === "hidden" ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
@@ -1090,6 +1182,17 @@ var import_material13 = require("@mui/material");
1090
1182
  var import_react2 = require("react");
1091
1183
  var import_jsx_runtime14 = require("react/jsx-runtime");
1092
1184
  var DEFAULT_EMPTY_STATE_MESSAGE = "No translation locales have been added yet. Select a language from the dropdown above to add one.";
1185
+ function normalizeLocaleOptions(options, getLocaleLabel) {
1186
+ const seen = /* @__PURE__ */ new Set();
1187
+ const normalized = [];
1188
+ for (const item of options) {
1189
+ const value = typeof item === "string" ? item : item.value;
1190
+ if (value.length === 0 || seen.has(value)) continue;
1191
+ seen.add(value);
1192
+ normalized.push({ value, label: typeof item === "string" ? getLocaleLabel?.(value) ?? value : item.label });
1193
+ }
1194
+ return normalized;
1195
+ }
1093
1196
  function createMuiLocalizationSlot(options) {
1094
1197
  return function MuiLocalization({
1095
1198
  schema,
@@ -1108,6 +1211,7 @@ function createMuiLocalizationSlot(options) {
1108
1211
  const resolved = useResolvedMuiAdapterOptions(options);
1109
1212
  const { Button: Button2, ErrorMessage, Select: Select2, TextArea, TextInput } = components;
1110
1213
  const [newLocale, setNewLocale] = (0, import_react2.useState)("");
1214
+ const [pendingFocusLocale, setPendingFocusLocale] = (0, import_react2.useState)(null);
1111
1215
  const locales = Array.from(
1112
1216
  new Set((schema.supportedLocales ?? []).filter((locale) => locale !== schema.defaultLocale))
1113
1217
  );
@@ -1116,21 +1220,44 @@ function createMuiLocalizationSlot(options) {
1116
1220
  ...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
1117
1221
  ...schema.supportedLocales ?? []
1118
1222
  ]);
1119
- const availableAllowedLocales = policy?.allowedLocales?.filter((locale) => !registeredLocales.has(locale));
1223
+ const localizationOptions = resolved.localizationOptions ?? {};
1224
+ const availableLocaleSource = localizationOptions.availableLocales;
1225
+ const availableLocaleOptions = availableLocaleSource === void 0 ? policy?.allowedLocales?.map((value) => ({
1226
+ value,
1227
+ label: resolved.getLocaleLabel?.(value) ?? value
1228
+ })) : normalizeLocaleOptions(availableLocaleSource, resolved.getLocaleLabel);
1229
+ const filteredAvailableLocales = availableLocaleOptions?.filter(
1230
+ (locale) => !registeredLocales.has(locale.value) && (policy?.allowedLocales === void 0 || policy.allowedLocales.includes(locale.value))
1231
+ );
1232
+ const hasLocaleSelector = filteredAvailableLocales !== void 0;
1120
1233
  const localeLimitReached = policy?.maxLocales !== void 0 && registeredLocales.size >= policy.maxLocales;
1121
1234
  const normalizedLocale = newLocale.trim();
1235
+ const selectedLocaleAvailable = !hasLocaleSelector || filteredAvailableLocales.some((locale) => locale.value === normalizedLocale);
1122
1236
  const localeAllowed = policy?.allowedLocales === void 0 || policy.allowedLocales.includes(normalizedLocale);
1123
- const canAddLocale = !readOnly && normalizedLocale.length > 0 && localeAllowed && !registeredLocales.has(normalizedLocale) && !localeLimitReached;
1237
+ const canAddLocale = !readOnly && normalizedLocale.length > 0 && selectedLocaleAvailable && localeAllowed && !registeredLocales.has(normalizedLocale) && !localeLimitReached;
1124
1238
  const handleTabChange = (_event, value) => onCurrentLocaleChange(value);
1125
1239
  const addLocale = () => {
1126
1240
  if (!canAddLocale) return;
1127
1241
  const result = actions.addLocale(normalizedLocale);
1128
1242
  if (!result.success) return;
1129
1243
  onCurrentLocaleChange(normalizedLocale);
1244
+ if (localizationOptions.autoFocusNewTab ?? true) setPendingFocusLocale(normalizedLocale);
1130
1245
  setNewLocale("");
1131
1246
  };
1247
+ const handleAddKeyDown = (event) => {
1248
+ if (event.key !== "Enter" || !canAddLocale) return;
1249
+ event.preventDefault();
1250
+ event.stopPropagation();
1251
+ addLocale();
1252
+ };
1253
+ (0, import_react2.useEffect)(() => {
1254
+ if (pendingFocusLocale === null || currentLocale !== pendingFocusLocale) return;
1255
+ const tab = document.getElementById(`mui-builder-locale-tab-${pendingFocusLocale}`);
1256
+ if (tab === null) return;
1257
+ tab.focus();
1258
+ setPendingFocusLocale(null);
1259
+ }, [currentLocale, pendingFocusLocale]);
1132
1260
  const stackProps = resolved.muiSlotProps?.stack;
1133
- const localizationOptions = resolved.localizationOptions ?? {};
1134
1261
  const collapsible = localizationOptions.collapsible ?? false;
1135
1262
  const defaultLocaleControl = localizationOptions.defaultLocaleControl ?? "editable";
1136
1263
  const configuredTranslationLocales = locales.filter((locale) => locale !== schema.defaultLocale);
@@ -1138,24 +1265,35 @@ function createMuiLocalizationSlot(options) {
1138
1265
  ...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
1139
1266
  ...configuredTranslationLocales
1140
1267
  ];
1141
- const summaryLabel = configuredTranslationLocales.length === 0 ? "Translations not configured" : `${configuredLocales.length} ${configuredLocales.length === 1 ? "language" : "languages"} configured: ${configuredLocales.map(
1268
+ const legacySummaryLabel = configuredTranslationLocales.length === 0 ? "Translations not configured" : `${configuredLocales.length} ${configuredLocales.length === 1 ? "language" : "languages"} configured: ${configuredLocales.map(
1142
1269
  (locale) => locale === schema.defaultLocale ? `${resolved.getLocaleLabel?.(locale) ?? locale} (default)` : resolved.getLocaleLabel?.(locale) ?? locale
1143
1270
  ).join(", ")}`;
1144
1271
  const emptyStateMessage = localizationOptions.emptyStateMessage === void 0 ? DEFAULT_EMPTY_STATE_MESSAGE : translate(localizationOptions.emptyStateMessage);
1272
+ const summaryContext = {
1273
+ defaultLocale: schema.defaultLocale ?? "",
1274
+ supportedLocales: schema.supportedLocales ?? [],
1275
+ totalLocales: registeredLocales.size
1276
+ };
1277
+ const summary = localizationOptions.renderSummary === void 0 ? localizationOptions.showSummary ? configuredTranslationLocales.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Alert, { severity: "success", sx: { mb: 2 }, children: translate("builder.localization.localesConfiguredSummary", {
1278
+ count: schema.supportedLocales?.length ?? 0
1279
+ }) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Alert, { severity: "info", sx: { mb: 2 }, children: translate("builder.localization.noLocalesConfigured") }) : null : localizationOptions.renderSummary(summaryContext);
1145
1280
  const title = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { alignItems: "center", direction: "row", spacing: 1, children: [
1146
1281
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "subtitle1", fontWeight: "bold", children: translate("builder.localization") }),
1147
- localizationOptions.showSummary ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1282
+ localizationOptions.renderSummary === void 0 && localizationOptions.showSummary && configuredTranslationLocales.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1148
1283
  import_material13.Chip,
1149
1284
  {
1150
- label: summaryLabel,
1285
+ label: legacySummaryLabel,
1151
1286
  size: "small",
1152
1287
  color: configuredTranslationLocales.length > 0 ? "primary" : "default",
1153
1288
  variant: "outlined"
1154
1289
  }
1155
1290
  ) : null
1156
1291
  ] });
1292
+ const noCandidateLocales = hasLocaleSelector && filteredAvailableLocales.length === 0;
1293
+ const candidateHelperText = localeLimitReached ? void 0 : noCandidateLocales ? translate("builder.localization.allLocalesAdded") : void 0;
1157
1294
  const content = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { ...stackProps, spacing: resolved.dense ? 1 : 2, children: [
1158
1295
  !collapsible ? title : null,
1296
+ summary,
1159
1297
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1160
1298
  import_material13.Stack,
1161
1299
  {
@@ -1187,36 +1325,52 @@ function createMuiLocalizationSlot(options) {
1187
1325
  }
1188
1326
  }
1189
1327
  ) }),
1190
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Box, { sx: { flexGrow: 1, minWidth: 0, width: { xs: "100%", sm: "auto" } }, children: availableAllowedLocales === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1191
- TextInput,
1328
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Box, { sx: { flexGrow: 1, minWidth: 0, width: { xs: "100%", sm: "auto" } }, onKeyDownCapture: handleAddKeyDown, children: hasLocaleSelector ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1329
+ Select2,
1192
1330
  {
1193
1331
  id: "mui-builder-new-locale",
1194
- label: translate("builder.addLocale"),
1332
+ label: translate("builder.localization.selectLocaleToAdd"),
1195
1333
  value: newLocale,
1196
- disabled: readOnly || localeLimitReached,
1334
+ options: filteredAvailableLocales.map((locale) => ({ value: locale.value, label: locale.label })),
1335
+ ...candidateHelperText === void 0 ? {} : { helperText: candidateHelperText },
1336
+ disabled: readOnly || localeLimitReached || noCandidateLocales,
1197
1337
  onChange: setNewLocale
1198
1338
  }
1199
1339
  ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1200
- Select2,
1340
+ TextInput,
1201
1341
  {
1202
1342
  id: "mui-builder-new-locale",
1203
1343
  label: translate("builder.addLocale"),
1204
1344
  value: newLocale,
1205
- options: [
1206
- { value: "", label: "\u2014" },
1207
- ...availableAllowedLocales.map((locale) => ({
1208
- value: locale,
1209
- label: resolved.getLocaleLabel?.(locale) ?? locale
1210
- }))
1211
- ],
1212
- disabled: readOnly || localeLimitReached || availableAllowedLocales.length === 0,
1345
+ disabled: readOnly || localeLimitReached,
1213
1346
  onChange: setNewLocale
1214
1347
  }
1215
1348
  ) }),
1216
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Box, { sx: { flexShrink: 0, minWidth: "max-content", whiteSpace: "nowrap" }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Button2, { variant: "primary", action: "addLocale", disabled: !canAddLocale, onClick: addLocale, children: translate("builder.addLocale") }) })
1349
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1350
+ import_material13.Box,
1351
+ {
1352
+ sx: {
1353
+ flexShrink: 0,
1354
+ minWidth: "max-content",
1355
+ ...localizationOptions.noWrapActions ?? true ? { whiteSpace: "nowrap" } : {}
1356
+ },
1357
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1358
+ Button2,
1359
+ {
1360
+ variant: "primary",
1361
+ action: "addLocale",
1362
+ noWrap: localizationOptions.noWrapActions ?? true,
1363
+ disabled: !canAddLocale,
1364
+ onClick: addLocale,
1365
+ children: translate("builder.addLocale")
1366
+ }
1367
+ )
1368
+ }
1369
+ )
1217
1370
  ]
1218
1371
  }
1219
1372
  ),
1373
+ localeLimitReached ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "body2", color: "text.secondary", children: translate("builder.localization.maxLocalesReached", { max: policy?.maxLocales ?? 0 }) }) : null,
1220
1374
  locales.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1221
1375
  import_material13.Tabs,
1222
1376
  {
@@ -1228,6 +1382,7 @@ function createMuiLocalizationSlot(options) {
1228
1382
  children: locales.map((locale) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1229
1383
  import_material13.Tab,
1230
1384
  {
1385
+ id: `mui-builder-locale-tab-${locale}`,
1231
1386
  value: locale,
1232
1387
  label: resolved.getLocaleLabel?.(locale) ?? locale,
1233
1388
  disabled: readOnly && locale !== currentLocale,
@@ -1241,6 +1396,7 @@ function createMuiLocalizationSlot(options) {
1241
1396
  Button2,
1242
1397
  {
1243
1398
  variant: "secondary",
1399
+ noWrap: localizationOptions.noWrapActions ?? true,
1244
1400
  disabled: readOnly || !editingLocaleConfigured || isTranslating,
1245
1401
  onClick: onAutoTranslate,
1246
1402
  children: isTranslating ? translate("builder.translating") : translate("builder.autoTranslate")
@@ -1359,7 +1515,8 @@ function MuiFormBuilder({
1359
1515
  const contextValue = (0, import_react4.useMemo)(() => ({ options: contextOptions }), [contextOptions]);
1360
1516
  const components = (0, import_react4.useMemo)(() => ({ ...muiBuilderComponents, ...customComponents }), [customComponents]);
1361
1517
  const slots = (0, import_react4.useMemo)(() => ({ ...muiBuilderSlots, ...customSlots }), [customSlots]);
1362
- const resolvedSectionOrder = sectionOrder ?? contextOptions.layoutOptions?.sectionOrder;
1518
+ const placement = contextOptions.localizationOptions?.placement;
1519
+ const resolvedSectionOrder = sectionOrder ?? (placement === void 0 ? contextOptions.layoutOptions?.sectionOrder : MUI_LOCALIZATION_SECTION_ORDERS[placement]);
1363
1520
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MuiFormBuilderContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1364
1521
  import_react3.FormBuilder,
1365
1522
  {
@@ -1374,6 +1531,7 @@ function MuiFormBuilder({
1374
1531
  // Annotate the CommonJS export names for ESM import in node:
1375
1532
  0 && (module.exports = {
1376
1533
  DEFAULT_MUI_SECTION_ORDER,
1534
+ MUI_LOCALIZATION_SECTION_ORDERS,
1377
1535
  MuiButtonAdapter,
1378
1536
  MuiCheckboxAdapter,
1379
1537
  MuiErrorMessageAdapter,
@@ -1408,6 +1566,7 @@ function MuiFormBuilder({
1408
1566
  mergeMuiAdapterOptions,
1409
1567
  muiBuilderComponents,
1410
1568
  muiBuilderSlots,
1569
+ muiDefaultFieldTypeIcon,
1411
1570
  muiDefaultIconResolver,
1412
1571
  resolveMuiAdapterOptions,
1413
1572
  useResolvedMuiAdapterOptions