@form-engine-ts/mui 2.9.5 → 3.0.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/README.md CHANGED
@@ -25,8 +25,15 @@ import { MuiFormBuilder } from "@form-engine-ts/mui";
25
25
  sectionOrder: ["basicSettings", "completionMessage", "questions", "addQuestion", "localization"]
26
26
  }}
27
27
  localizationOptions={{
28
+ availableLocales: [
29
+ { value: "ja", label: "日本語" },
30
+ { value: "en", label: "English" }
31
+ ],
32
+ placement: "afterQuestions",
28
33
  collapsible: true,
29
34
  defaultExpanded: "when-configured",
35
+ showSummary: true,
36
+ emptyStateMessage: "No translation locales have been added yet.",
30
37
  defaultLocaleControl: "readOnly"
31
38
  }}
32
39
  muiSlotProps={{ card: { sx: { p: 2 } }, accordion: { elevation: 0 } }}
@@ -54,6 +61,12 @@ slots as it does for the standard builder.
54
61
  width options. `fieldEditorOptions.description` and `localizationOptions.defaultLocaleControl` independently make those
55
62
  controls editable, read-only, or hidden. Set `dense` to reduce section, editor, option, and toolbar spacing.
56
63
  `buttonVariants` can override the MUI variant for `primary`, `secondary`, and `danger` actions independently.
64
+ `localizationOptions.availableLocales` supplies display-ready locale candidates independently from the policy; when
65
+ both are present, only candidates allowed by `allowedLocales` are shown. `placement` supports `top`,
66
+ `beforeQuestions`, `afterQuestions`, and `bottom` section presets. `showSummary` renders a status alert, while
67
+ `renderSummary` can provide a custom summary. `emptyStateMessage` customizes the guidance shown before a translation
68
+ locale is added. `defaultExpanded` also accepts `"always"`; `autoFocusNewTab` focuses a newly added locale tab by
69
+ default.
57
70
  `layoutOptions`, `localizationOptions`, and `muiSlotProps` are also accepted in `muiOptions` for low-level factories such
58
71
  as `createMuiBuilderSlots`; the dedicated `MuiFormBuilder` props take precedence.
59
72
 
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,
@@ -72,8 +73,15 @@ var DEFAULT_MUI_SECTION_ORDER = [
72
73
  "basicSettings",
73
74
  "questions",
74
75
  "addQuestion",
75
- "localization"
76
+ "localization",
77
+ "completionMessage"
76
78
  ];
79
+ var MUI_LOCALIZATION_SECTION_ORDERS = {
80
+ top: ["localization", "basicSettings", "completionMessage", "questions", "addQuestion"],
81
+ beforeQuestions: ["basicSettings", "localization", "completionMessage", "questions", "addQuestion"],
82
+ afterQuestions: ["basicSettings", "questions", "addQuestion", "localization", "completionMessage"],
83
+ bottom: ["basicSettings", "completionMessage", "questions", "addQuestion", "localization"]
84
+ };
77
85
  function resolveMuiAdapterOptions(options = {}) {
78
86
  const buttonVariant = options.buttonVariant ?? "contained";
79
87
  return {
@@ -98,7 +106,14 @@ function resolveMuiAdapterOptions(options = {}) {
98
106
  localizationOptions: {
99
107
  collapsible: options.localizationOptions?.collapsible ?? false,
100
108
  defaultExpanded: options.localizationOptions?.defaultExpanded ?? false,
101
- defaultLocaleControl: options.localizationOptions?.defaultLocaleControl ?? "editable"
109
+ showSummary: options.localizationOptions?.showSummary ?? false,
110
+ ...options.localizationOptions?.availableLocales === void 0 ? {} : { availableLocales: options.localizationOptions.availableLocales },
111
+ ...options.localizationOptions?.placement === void 0 ? {} : { placement: options.localizationOptions.placement },
112
+ ...options.localizationOptions?.renderSummary === void 0 ? {} : { renderSummary: options.localizationOptions.renderSummary },
113
+ ...options.localizationOptions?.emptyStateMessage === void 0 ? {} : { emptyStateMessage: options.localizationOptions.emptyStateMessage },
114
+ defaultLocaleControl: options.localizationOptions?.defaultLocaleControl ?? "editable",
115
+ noWrapActions: options.localizationOptions?.noWrapActions ?? true,
116
+ autoFocusNewTab: options.localizationOptions?.autoFocusNewTab ?? true
102
117
  },
103
118
  muiSlotProps: options.muiSlotProps ?? {}
104
119
  };
@@ -136,6 +151,7 @@ function createMuiButtonAdapter(options) {
136
151
  "aria-describedby": ariaDescribedBy,
137
152
  "aria-labelledby": ariaLabelledBy,
138
153
  onClick,
154
+ noWrap,
139
155
  children,
140
156
  title,
141
157
  variant,
@@ -161,6 +177,7 @@ function createMuiButtonAdapter(options) {
161
177
  color: variant === "danger" ? "error" : "primary",
162
178
  variant: resolved.buttonVariants?.[variant ?? "primary"] ?? resolved.buttonVariant,
163
179
  fullWidth: resolved.buttonFullWidth ?? false,
180
+ sx: { whiteSpace: noWrap === false ? "normal" : "nowrap" },
164
181
  children
165
182
  }
166
183
  );
@@ -189,6 +206,8 @@ function createMuiCheckboxAdapter(options) {
189
206
  label
190
207
  }) {
191
208
  const resolved = useResolvedMuiAdapterOptions(options);
209
+ const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
210
+ const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
192
211
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_material2.FormControl, { className, error: Boolean(error), required, disabled: disabled || readOnly, children: [
193
212
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
194
213
  import_material2.FormControlLabel,
@@ -205,7 +224,7 @@ function createMuiCheckboxAdapter(options) {
205
224
  disabled: disabled || readOnly,
206
225
  inputProps: {
207
226
  "aria-label": ariaLabel,
208
- "aria-describedby": ariaDescribedBy,
227
+ "aria-describedby": describedBy,
209
228
  "aria-labelledby": ariaLabelledBy
210
229
  },
211
230
  onChange: (event) => onChange(event.target.checked)
@@ -213,7 +232,7 @@ function createMuiCheckboxAdapter(options) {
213
232
  )
214
233
  }
215
234
  ),
216
- helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_material2.FormHelperText, { id: ariaDescribedBy, children: helperText })
235
+ helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_material2.FormHelperText, { id: helperId, children: helperText })
217
236
  ] });
218
237
  };
219
238
  }
@@ -402,10 +421,13 @@ function createMuiSelectAdapter(options) {
402
421
  helperText,
403
422
  value,
404
423
  onChange,
424
+ onKeyDown,
405
425
  options: selectOptions
406
426
  }) {
407
427
  const resolved = useResolvedMuiAdapterOptions(options);
408
428
  const labelId = id === void 0 || label === void 0 ? void 0 : `${id}-label`;
429
+ const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
430
+ const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
409
431
  const handleChange = (event) => onChange(event.target.value);
410
432
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
411
433
  import_material7.FormControl,
@@ -427,18 +449,20 @@ function createMuiSelectAdapter(options) {
427
449
  name,
428
450
  label,
429
451
  value,
452
+ disabled,
430
453
  onChange: handleChange,
454
+ onKeyDown,
431
455
  required,
432
456
  readOnly,
433
457
  inputProps: {
434
458
  "aria-label": ariaLabel,
435
- "aria-describedby": ariaDescribedBy,
459
+ "aria-describedby": describedBy,
436
460
  "aria-labelledby": ariaLabelledBy
437
461
  },
438
462
  children: selectOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.MenuItem, { value: option.value, disabled: option.disabled, children: option.label }, option.value))
439
463
  }
440
464
  ),
441
- helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.FormHelperText, { id: ariaDescribedBy, children: helperText })
465
+ helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.FormHelperText, { id: helperId, children: helperText })
442
466
  ]
443
467
  }
444
468
  );
@@ -525,6 +549,7 @@ function createMuiTextInputAdapter(options) {
525
549
  helperText,
526
550
  value,
527
551
  onChange,
552
+ onKeyDown,
528
553
  placeholder,
529
554
  maxLength,
530
555
  inputMode,
@@ -566,7 +591,8 @@ function createMuiTextInputAdapter(options) {
566
591
  fullWidth: resolved.inputFullWidth ?? resolved.fullWidth,
567
592
  size: resolved.size,
568
593
  variant: resolved.variant,
569
- onChange: (event) => onChange(event.currentTarget.value)
594
+ onChange: (event) => onChange(event.currentTarget.value),
595
+ onKeyDown
570
596
  }
571
597
  );
572
598
  };
@@ -1082,6 +1108,18 @@ var import_icons_material2 = require("@mui/icons-material");
1082
1108
  var import_material13 = require("@mui/material");
1083
1109
  var import_react2 = require("react");
1084
1110
  var import_jsx_runtime14 = require("react/jsx-runtime");
1111
+ var DEFAULT_EMPTY_STATE_MESSAGE = "No translation locales have been added yet. Select a language from the dropdown above to add one.";
1112
+ function normalizeLocaleOptions(options, getLocaleLabel) {
1113
+ const seen = /* @__PURE__ */ new Set();
1114
+ const normalized = [];
1115
+ for (const item of options) {
1116
+ const value = typeof item === "string" ? item : item.value;
1117
+ if (value.length === 0 || seen.has(value)) continue;
1118
+ seen.add(value);
1119
+ normalized.push({ value, label: typeof item === "string" ? getLocaleLabel?.(value) ?? value : item.label });
1120
+ }
1121
+ return normalized;
1122
+ }
1085
1123
  function createMuiLocalizationSlot(options) {
1086
1124
  return function MuiLocalization({
1087
1125
  schema,
@@ -1100,6 +1138,7 @@ function createMuiLocalizationSlot(options) {
1100
1138
  const resolved = useResolvedMuiAdapterOptions(options);
1101
1139
  const { Button: Button2, ErrorMessage, Select: Select2, TextArea, TextInput } = components;
1102
1140
  const [newLocale, setNewLocale] = (0, import_react2.useState)("");
1141
+ const [pendingFocusLocale, setPendingFocusLocale] = (0, import_react2.useState)(null);
1103
1142
  const locales = Array.from(
1104
1143
  new Set((schema.supportedLocales ?? []).filter((locale) => locale !== schema.defaultLocale))
1105
1144
  );
@@ -1108,75 +1147,157 @@ function createMuiLocalizationSlot(options) {
1108
1147
  ...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
1109
1148
  ...schema.supportedLocales ?? []
1110
1149
  ]);
1111
- const availableAllowedLocales = policy?.allowedLocales?.filter((locale) => !registeredLocales.has(locale));
1150
+ const localizationOptions = resolved.localizationOptions ?? {};
1151
+ const availableLocaleSource = localizationOptions.availableLocales;
1152
+ const availableLocaleOptions = availableLocaleSource === void 0 ? policy?.allowedLocales?.map((value) => ({
1153
+ value,
1154
+ label: resolved.getLocaleLabel?.(value) ?? value
1155
+ })) : normalizeLocaleOptions(availableLocaleSource, resolved.getLocaleLabel);
1156
+ const filteredAvailableLocales = availableLocaleOptions?.filter(
1157
+ (locale) => !registeredLocales.has(locale.value) && (policy?.allowedLocales === void 0 || policy.allowedLocales.includes(locale.value))
1158
+ );
1159
+ const hasLocaleSelector = filteredAvailableLocales !== void 0;
1112
1160
  const localeLimitReached = policy?.maxLocales !== void 0 && registeredLocales.size >= policy.maxLocales;
1113
1161
  const normalizedLocale = newLocale.trim();
1162
+ const selectedLocaleAvailable = !hasLocaleSelector || filteredAvailableLocales.some((locale) => locale.value === normalizedLocale);
1114
1163
  const localeAllowed = policy?.allowedLocales === void 0 || policy.allowedLocales.includes(normalizedLocale);
1115
- const canAddLocale = !readOnly && normalizedLocale.length > 0 && localeAllowed && !registeredLocales.has(normalizedLocale) && !localeLimitReached;
1164
+ const canAddLocale = !readOnly && normalizedLocale.length > 0 && selectedLocaleAvailable && localeAllowed && !registeredLocales.has(normalizedLocale) && !localeLimitReached;
1116
1165
  const handleTabChange = (_event, value) => onCurrentLocaleChange(value);
1117
1166
  const addLocale = () => {
1118
1167
  if (!canAddLocale) return;
1119
1168
  const result = actions.addLocale(normalizedLocale);
1120
1169
  if (!result.success) return;
1121
1170
  onCurrentLocaleChange(normalizedLocale);
1171
+ if (localizationOptions.autoFocusNewTab ?? true) setPendingFocusLocale(normalizedLocale);
1122
1172
  setNewLocale("");
1123
1173
  };
1174
+ const handleAddKeyDown = (event) => {
1175
+ if (event.key !== "Enter" || !canAddLocale) return;
1176
+ event.preventDefault();
1177
+ event.stopPropagation();
1178
+ addLocale();
1179
+ };
1180
+ (0, import_react2.useEffect)(() => {
1181
+ if (pendingFocusLocale === null || currentLocale !== pendingFocusLocale) return;
1182
+ const tab = document.getElementById(`mui-builder-locale-tab-${pendingFocusLocale}`);
1183
+ if (tab === null) return;
1184
+ tab.focus();
1185
+ setPendingFocusLocale(null);
1186
+ }, [currentLocale, pendingFocusLocale]);
1124
1187
  const stackProps = resolved.muiSlotProps?.stack;
1125
- const collapsible = resolved.localizationOptions?.collapsible ?? false;
1126
- const defaultLocaleControl = resolved.localizationOptions?.defaultLocaleControl ?? "editable";
1188
+ const collapsible = localizationOptions.collapsible ?? false;
1189
+ const defaultLocaleControl = localizationOptions.defaultLocaleControl ?? "editable";
1190
+ const configuredTranslationLocales = locales.filter((locale) => locale !== schema.defaultLocale);
1191
+ const configuredLocales = [
1192
+ ...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
1193
+ ...configuredTranslationLocales
1194
+ ];
1195
+ const legacySummaryLabel = configuredTranslationLocales.length === 0 ? "Translations not configured" : `${configuredLocales.length} ${configuredLocales.length === 1 ? "language" : "languages"} configured: ${configuredLocales.map(
1196
+ (locale) => locale === schema.defaultLocale ? `${resolved.getLocaleLabel?.(locale) ?? locale} (default)` : resolved.getLocaleLabel?.(locale) ?? locale
1197
+ ).join(", ")}`;
1198
+ const emptyStateMessage = localizationOptions.emptyStateMessage === void 0 ? DEFAULT_EMPTY_STATE_MESSAGE : translate(localizationOptions.emptyStateMessage);
1199
+ const summaryContext = {
1200
+ defaultLocale: schema.defaultLocale ?? "",
1201
+ supportedLocales: schema.supportedLocales ?? [],
1202
+ totalLocales: registeredLocales.size
1203
+ };
1204
+ 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", {
1205
+ count: schema.supportedLocales?.length ?? 0
1206
+ }) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Alert, { severity: "info", sx: { mb: 2 }, children: translate("builder.localization.noLocalesConfigured") }) : null : localizationOptions.renderSummary(summaryContext);
1207
+ const title = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { alignItems: "center", direction: "row", spacing: 1, children: [
1208
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "subtitle1", fontWeight: "bold", children: translate("builder.localization") }),
1209
+ localizationOptions.renderSummary === void 0 && localizationOptions.showSummary && configuredTranslationLocales.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1210
+ import_material13.Chip,
1211
+ {
1212
+ label: legacySummaryLabel,
1213
+ size: "small",
1214
+ color: configuredTranslationLocales.length > 0 ? "primary" : "default",
1215
+ variant: "outlined"
1216
+ }
1217
+ ) : null
1218
+ ] });
1219
+ const noCandidateLocales = hasLocaleSelector && filteredAvailableLocales.length === 0;
1220
+ const candidateHelperText = localeLimitReached ? void 0 : noCandidateLocales ? translate("builder.localization.allLocalesAdded") : void 0;
1127
1221
  const content = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { ...stackProps, spacing: resolved.dense ? 1 : 2, children: [
1128
- !collapsible ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "subtitle1", fontWeight: "bold", children: translate("builder.localization") }) : null,
1129
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { ...stackProps, direction: { xs: "column", sm: "row" }, spacing: resolved.dense ? 1 : 2, children: [
1130
- defaultLocaleControl === "hidden" ? null : defaultLocaleControl === "readOnly" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { spacing: 0.5, children: [
1131
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "caption", color: "text.secondary", children: translate("builder.defaultLocale") }),
1132
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1133
- import_material13.Chip,
1134
- {
1135
- label: resolved.getLocaleLabel?.(schema.defaultLocale ?? "") ?? schema.defaultLocale ?? "",
1136
- size: resolved.size
1137
- }
1138
- )
1139
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1140
- TextInput,
1141
- {
1142
- id: "mui-builder-default-locale",
1143
- label: translate("builder.defaultLocale"),
1144
- value: schema.defaultLocale ?? "",
1145
- disabled: readOnly,
1146
- onChange: (value) => {
1147
- const result = actions.setDefaultLocale(value);
1148
- if (result.success && value === currentLocale) onCurrentLocaleChange("");
1149
- }
1150
- }
1151
- ),
1152
- availableAllowedLocales === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1153
- TextInput,
1154
- {
1155
- id: "mui-builder-new-locale",
1156
- label: translate("builder.addLocale"),
1157
- value: newLocale,
1158
- disabled: readOnly || localeLimitReached,
1159
- onChange: setNewLocale
1160
- }
1161
- ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1162
- Select2,
1163
- {
1164
- id: "mui-builder-new-locale",
1165
- label: translate("builder.addLocale"),
1166
- value: newLocale,
1167
- options: [
1168
- { value: "", label: "\u2014" },
1169
- ...availableAllowedLocales.map((locale) => ({
1170
- value: locale,
1171
- label: resolved.getLocaleLabel?.(locale) ?? locale
1172
- }))
1173
- ],
1174
- disabled: readOnly || localeLimitReached || availableAllowedLocales.length === 0,
1175
- onChange: setNewLocale
1176
- }
1177
- ),
1178
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Button2, { variant: "primary", action: "addLocale", disabled: !canAddLocale, onClick: addLocale, children: translate("builder.addLocale") })
1179
- ] }),
1222
+ !collapsible ? title : null,
1223
+ summary,
1224
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1225
+ import_material13.Stack,
1226
+ {
1227
+ ...stackProps,
1228
+ direction: { xs: "column", sm: "row" },
1229
+ alignItems: { xs: "stretch", sm: "center" },
1230
+ spacing: resolved.dense ? 1 : 2,
1231
+ sx: { mt: 1 },
1232
+ children: [
1233
+ defaultLocaleControl === "hidden" ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Box, { sx: { flexGrow: 1, minWidth: 0, width: { xs: "100%", sm: "auto" } }, children: defaultLocaleControl === "readOnly" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { spacing: 0.5, children: [
1234
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "caption", color: "text.secondary", children: translate("builder.defaultLocale") }),
1235
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1236
+ import_material13.Chip,
1237
+ {
1238
+ label: resolved.getLocaleLabel?.(schema.defaultLocale ?? "") ?? schema.defaultLocale ?? "",
1239
+ size: resolved.size
1240
+ }
1241
+ )
1242
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1243
+ TextInput,
1244
+ {
1245
+ id: "mui-builder-default-locale",
1246
+ label: translate("builder.defaultLocale"),
1247
+ value: schema.defaultLocale ?? "",
1248
+ disabled: readOnly,
1249
+ onChange: (value) => {
1250
+ const result = actions.setDefaultLocale(value);
1251
+ if (result.success && value === currentLocale) onCurrentLocaleChange("");
1252
+ }
1253
+ }
1254
+ ) }),
1255
+ /* @__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)(
1256
+ Select2,
1257
+ {
1258
+ id: "mui-builder-new-locale",
1259
+ label: translate("builder.localization.selectLocaleToAdd"),
1260
+ value: newLocale,
1261
+ options: filteredAvailableLocales.map((locale) => ({ value: locale.value, label: locale.label })),
1262
+ ...candidateHelperText === void 0 ? {} : { helperText: candidateHelperText },
1263
+ disabled: readOnly || localeLimitReached || noCandidateLocales,
1264
+ onChange: setNewLocale
1265
+ }
1266
+ ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1267
+ TextInput,
1268
+ {
1269
+ id: "mui-builder-new-locale",
1270
+ label: translate("builder.addLocale"),
1271
+ value: newLocale,
1272
+ disabled: readOnly || localeLimitReached,
1273
+ onChange: setNewLocale
1274
+ }
1275
+ ) }),
1276
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1277
+ import_material13.Box,
1278
+ {
1279
+ sx: {
1280
+ flexShrink: 0,
1281
+ minWidth: "max-content",
1282
+ ...localizationOptions.noWrapActions ?? true ? { whiteSpace: "nowrap" } : {}
1283
+ },
1284
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1285
+ Button2,
1286
+ {
1287
+ variant: "primary",
1288
+ action: "addLocale",
1289
+ noWrap: localizationOptions.noWrapActions ?? true,
1290
+ disabled: !canAddLocale,
1291
+ onClick: addLocale,
1292
+ children: translate("builder.addLocale")
1293
+ }
1294
+ )
1295
+ }
1296
+ )
1297
+ ]
1298
+ }
1299
+ ),
1300
+ 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,
1180
1301
  locales.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1181
1302
  import_material13.Tabs,
1182
1303
  {
@@ -1188,25 +1309,28 @@ function createMuiLocalizationSlot(options) {
1188
1309
  children: locales.map((locale) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1189
1310
  import_material13.Tab,
1190
1311
  {
1312
+ id: `mui-builder-locale-tab-${locale}`,
1191
1313
  value: locale,
1192
1314
  label: resolved.getLocaleLabel?.(locale) ?? locale,
1193
- disabled: readOnly && locale !== currentLocale
1315
+ disabled: readOnly && locale !== currentLocale,
1316
+ sx: { whiteSpace: "nowrap" }
1194
1317
  },
1195
1318
  locale
1196
1319
  ))
1197
1320
  }
1198
1321
  ),
1199
- !translationAdapterAvailable ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1322
+ !translationAdapterAvailable ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Box, { sx: { minWidth: "max-content", whiteSpace: "nowrap" }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1200
1323
  Button2,
1201
1324
  {
1202
1325
  variant: "secondary",
1326
+ noWrap: localizationOptions.noWrapActions ?? true,
1203
1327
  disabled: readOnly || !editingLocaleConfigured || isTranslating,
1204
1328
  onClick: onAutoTranslate,
1205
1329
  children: isTranslating ? translate("builder.translating") : translate("builder.autoTranslate")
1206
1330
  }
1207
- ),
1331
+ ) }),
1208
1332
  translationError === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ErrorMessage, { message: translationError }),
1209
- !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: [
1333
+ !editingLocaleConfigured ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "body2", color: "text.secondary", children: locales.length === 0 ? emptyStateMessage : translate("builder.selectLocale") }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { ...stackProps, spacing: resolved.dense ? 1 : 2, children: [
1210
1334
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1211
1335
  TextInput,
1212
1336
  {
@@ -1240,10 +1364,10 @@ function createMuiLocalizationSlot(options) {
1240
1364
  ] })
1241
1365
  ] });
1242
1366
  const configured = locales.length > 0;
1243
- const defaultExpanded = resolved.localizationOptions?.defaultExpanded === "when-configured" ? configured : resolved.localizationOptions?.defaultExpanded ?? false;
1367
+ const defaultExpanded = localizationOptions.defaultExpanded === "when-configured" ? configured : localizationOptions.defaultExpanded === "always" ? true : localizationOptions.defaultExpanded ?? false;
1244
1368
  if (collapsible) {
1245
1369
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Accordion, { ...resolved.muiSlotProps?.accordion, "data-mui-slot": "localization", defaultExpanded, children: [
1246
- /* @__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") }) }),
1370
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.AccordionSummary, { expandIcon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_icons_material2.ExpandMore, {}), children: title }),
1247
1371
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.AccordionDetails, { children: content })
1248
1372
  ] });
1249
1373
  }
@@ -1318,7 +1442,8 @@ function MuiFormBuilder({
1318
1442
  const contextValue = (0, import_react4.useMemo)(() => ({ options: contextOptions }), [contextOptions]);
1319
1443
  const components = (0, import_react4.useMemo)(() => ({ ...muiBuilderComponents, ...customComponents }), [customComponents]);
1320
1444
  const slots = (0, import_react4.useMemo)(() => ({ ...muiBuilderSlots, ...customSlots }), [customSlots]);
1321
- const resolvedSectionOrder = sectionOrder ?? contextOptions.layoutOptions?.sectionOrder;
1445
+ const placement = contextOptions.localizationOptions?.placement;
1446
+ const resolvedSectionOrder = sectionOrder ?? (placement === void 0 ? contextOptions.layoutOptions?.sectionOrder : MUI_LOCALIZATION_SECTION_ORDERS[placement]);
1322
1447
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MuiFormBuilderContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1323
1448
  import_react3.FormBuilder,
1324
1449
  {
@@ -1333,6 +1458,7 @@ function MuiFormBuilder({
1333
1458
  // Annotate the CommonJS export names for ESM import in node:
1334
1459
  0 && (module.exports = {
1335
1460
  DEFAULT_MUI_SECTION_ORDER,
1461
+ MUI_LOCALIZATION_SECTION_ORDERS,
1336
1462
  MuiButtonAdapter,
1337
1463
  MuiCheckboxAdapter,
1338
1464
  MuiErrorMessageAdapter,
package/dist/index.d.cts CHANGED
@@ -1,17 +1,29 @@
1
- import { BuilderActionIconType, BuilderButtonProps, BuilderCheckboxProps, BuilderErrorMessageProps, BuilderFieldsetProps, BuilderIconButtonProps, BuilderSectionProps, BuilderSelectProps, BuilderTextAreaProps, BuilderTextInputProps, FormBuilderComponents, FormBuilderSlots, FormBuilderProps, BuilderFieldEditorSlotProps, BuilderLocalizationSlotProps, BuilderOptionEditorSlotProps, BuilderToolbarSlotProps } from '@form-engine-ts/react';
1
+ import { BuilderActionIconType, LocalizationSummaryContext, BuilderButtonProps, BuilderCheckboxProps, BuilderErrorMessageProps, BuilderFieldsetProps, BuilderIconButtonProps, BuilderSectionProps, BuilderSelectProps, BuilderTextAreaProps, BuilderTextInputProps, FormBuilderComponents, FormBuilderSlots, FormBuilderProps, BuilderFieldEditorSlotProps, BuilderLocalizationSlotProps, BuilderOptionEditorSlotProps, BuilderToolbarSlotProps } from '@form-engine-ts/react';
2
2
  import * as react from 'react';
3
- import { ComponentType, ReactNode } from 'react';
3
+ import { ReactNode, ComponentType } from 'react';
4
4
  import { CardProps, PaperProps, StackProps, AccordionProps } from '@mui/material';
5
5
 
6
6
  type BuilderSectionName = "basicSettings" | "completionMessage" | "questions" | "addQuestion" | "localization";
7
7
  type MuiButtonVariant = "contained" | "outlined" | "text";
8
+ interface LocaleOptionItem {
9
+ readonly value: string;
10
+ readonly label: string;
11
+ }
12
+ type LocalizationSectionPlacement = "top" | "beforeQuestions" | "afterQuestions" | "bottom";
8
13
  interface MuiLayoutOptions {
9
14
  readonly sectionOrder?: readonly BuilderSectionName[];
10
15
  }
11
16
  interface MuiLocalizationOptions {
17
+ readonly availableLocales?: readonly (LocaleOptionItem | string)[];
18
+ readonly placement?: LocalizationSectionPlacement;
12
19
  readonly collapsible?: boolean;
13
- readonly defaultExpanded?: boolean | "when-configured";
20
+ readonly defaultExpanded?: boolean | "when-configured" | "always";
21
+ readonly showSummary?: boolean;
22
+ readonly renderSummary?: (context: LocalizationSummaryContext) => ReactNode;
23
+ readonly emptyStateMessage?: string;
14
24
  readonly defaultLocaleControl?: "editable" | "readOnly" | "hidden";
25
+ readonly noWrapActions?: boolean;
26
+ readonly autoFocusNewTab?: boolean;
15
27
  }
16
28
  interface MuiFieldEditorOptions {
17
29
  readonly description?: "editable" | "readOnly" | "hidden";
@@ -63,6 +75,7 @@ interface ResolvedMuiAdapterOptions {
63
75
  readonly muiSlotProps?: MuiBuilderSlotProps;
64
76
  }
65
77
  declare const DEFAULT_MUI_SECTION_ORDER: readonly BuilderSectionName[];
78
+ declare const MUI_LOCALIZATION_SECTION_ORDERS: Readonly<Record<LocalizationSectionPlacement, readonly BuilderSectionName[]>>;
66
79
  declare function resolveMuiAdapterOptions(options?: MuiAdapterOptions): ResolvedMuiAdapterOptions;
67
80
 
68
81
  declare function createMuiButtonAdapter(options?: MuiAdapterOptions): ComponentType<BuilderButtonProps>;
@@ -136,4 +149,4 @@ declare const MuiToolbarSlot: NonNullable<FormBuilderSlots["toolbar"]>;
136
149
  declare const muiBuilderSlots: FormBuilderSlots;
137
150
  declare function createMuiBuilderSlots(options?: MuiAdapterOptions, customOverrides?: Partial<FormBuilderSlots>): FormBuilderSlots;
138
151
 
139
- export { type BuilderSectionName, DEFAULT_MUI_SECTION_ORDER, type MuiAdapterOptions, type MuiBuilderOverrides, type MuiBuilderSlotProps, MuiButtonAdapter, type MuiButtonVariant, MuiCheckboxAdapter, MuiErrorMessageAdapter, type MuiFieldEditorOptions, MuiFieldEditorSlot, MuiFieldsetAdapter, MuiFormBuilder, MuiFormBuilderContext, type MuiFormBuilderContextValue, type MuiFormBuilderProps, MuiIconButtonAdapter, type MuiLayoutOptions, type MuiLocalizationOptions, MuiLocalizationSlot, MuiOptionEditorSlot, MuiSectionAdapter, MuiSelectAdapter, MuiTextAreaAdapter, MuiTextInputAdapter, MuiToolbarSlot, type ResolvedMuiAdapterOptions, createMuiBuilderComponents, createMuiBuilderProps, createMuiBuilderSlots, createMuiButtonAdapter, createMuiCheckboxAdapter, createMuiErrorMessageAdapter, createMuiFieldEditorSlot, createMuiFieldsetAdapter, createMuiIconButtonAdapter, createMuiLocalizationSlot, createMuiOptionEditorSlot, createMuiSectionAdapter, createMuiSelectAdapter, createMuiTextAreaAdapter, createMuiTextInputAdapter, createMuiToolbarSlot, mergeMuiAdapterOptions, muiBuilderComponents, muiBuilderSlots, muiDefaultIconResolver, resolveMuiAdapterOptions, useResolvedMuiAdapterOptions };
152
+ export { type BuilderSectionName, DEFAULT_MUI_SECTION_ORDER, type LocaleOptionItem, type LocalizationSectionPlacement, MUI_LOCALIZATION_SECTION_ORDERS, type MuiAdapterOptions, type MuiBuilderOverrides, type MuiBuilderSlotProps, MuiButtonAdapter, type MuiButtonVariant, MuiCheckboxAdapter, MuiErrorMessageAdapter, type MuiFieldEditorOptions, MuiFieldEditorSlot, MuiFieldsetAdapter, MuiFormBuilder, MuiFormBuilderContext, type MuiFormBuilderContextValue, type MuiFormBuilderProps, MuiIconButtonAdapter, type MuiLayoutOptions, type MuiLocalizationOptions, MuiLocalizationSlot, MuiOptionEditorSlot, MuiSectionAdapter, MuiSelectAdapter, MuiTextAreaAdapter, MuiTextInputAdapter, MuiToolbarSlot, type ResolvedMuiAdapterOptions, createMuiBuilderComponents, createMuiBuilderProps, createMuiBuilderSlots, createMuiButtonAdapter, createMuiCheckboxAdapter, createMuiErrorMessageAdapter, createMuiFieldEditorSlot, createMuiFieldsetAdapter, createMuiIconButtonAdapter, createMuiLocalizationSlot, createMuiOptionEditorSlot, createMuiSectionAdapter, createMuiSelectAdapter, createMuiTextAreaAdapter, createMuiTextInputAdapter, createMuiToolbarSlot, mergeMuiAdapterOptions, muiBuilderComponents, muiBuilderSlots, muiDefaultIconResolver, resolveMuiAdapterOptions, useResolvedMuiAdapterOptions };
package/dist/index.d.ts CHANGED
@@ -1,17 +1,29 @@
1
- import { BuilderActionIconType, BuilderButtonProps, BuilderCheckboxProps, BuilderErrorMessageProps, BuilderFieldsetProps, BuilderIconButtonProps, BuilderSectionProps, BuilderSelectProps, BuilderTextAreaProps, BuilderTextInputProps, FormBuilderComponents, FormBuilderSlots, FormBuilderProps, BuilderFieldEditorSlotProps, BuilderLocalizationSlotProps, BuilderOptionEditorSlotProps, BuilderToolbarSlotProps } from '@form-engine-ts/react';
1
+ import { BuilderActionIconType, LocalizationSummaryContext, BuilderButtonProps, BuilderCheckboxProps, BuilderErrorMessageProps, BuilderFieldsetProps, BuilderIconButtonProps, BuilderSectionProps, BuilderSelectProps, BuilderTextAreaProps, BuilderTextInputProps, FormBuilderComponents, FormBuilderSlots, FormBuilderProps, BuilderFieldEditorSlotProps, BuilderLocalizationSlotProps, BuilderOptionEditorSlotProps, BuilderToolbarSlotProps } from '@form-engine-ts/react';
2
2
  import * as react from 'react';
3
- import { ComponentType, ReactNode } from 'react';
3
+ import { ReactNode, ComponentType } from 'react';
4
4
  import { CardProps, PaperProps, StackProps, AccordionProps } from '@mui/material';
5
5
 
6
6
  type BuilderSectionName = "basicSettings" | "completionMessage" | "questions" | "addQuestion" | "localization";
7
7
  type MuiButtonVariant = "contained" | "outlined" | "text";
8
+ interface LocaleOptionItem {
9
+ readonly value: string;
10
+ readonly label: string;
11
+ }
12
+ type LocalizationSectionPlacement = "top" | "beforeQuestions" | "afterQuestions" | "bottom";
8
13
  interface MuiLayoutOptions {
9
14
  readonly sectionOrder?: readonly BuilderSectionName[];
10
15
  }
11
16
  interface MuiLocalizationOptions {
17
+ readonly availableLocales?: readonly (LocaleOptionItem | string)[];
18
+ readonly placement?: LocalizationSectionPlacement;
12
19
  readonly collapsible?: boolean;
13
- readonly defaultExpanded?: boolean | "when-configured";
20
+ readonly defaultExpanded?: boolean | "when-configured" | "always";
21
+ readonly showSummary?: boolean;
22
+ readonly renderSummary?: (context: LocalizationSummaryContext) => ReactNode;
23
+ readonly emptyStateMessage?: string;
14
24
  readonly defaultLocaleControl?: "editable" | "readOnly" | "hidden";
25
+ readonly noWrapActions?: boolean;
26
+ readonly autoFocusNewTab?: boolean;
15
27
  }
16
28
  interface MuiFieldEditorOptions {
17
29
  readonly description?: "editable" | "readOnly" | "hidden";
@@ -63,6 +75,7 @@ interface ResolvedMuiAdapterOptions {
63
75
  readonly muiSlotProps?: MuiBuilderSlotProps;
64
76
  }
65
77
  declare const DEFAULT_MUI_SECTION_ORDER: readonly BuilderSectionName[];
78
+ declare const MUI_LOCALIZATION_SECTION_ORDERS: Readonly<Record<LocalizationSectionPlacement, readonly BuilderSectionName[]>>;
66
79
  declare function resolveMuiAdapterOptions(options?: MuiAdapterOptions): ResolvedMuiAdapterOptions;
67
80
 
68
81
  declare function createMuiButtonAdapter(options?: MuiAdapterOptions): ComponentType<BuilderButtonProps>;
@@ -136,4 +149,4 @@ declare const MuiToolbarSlot: NonNullable<FormBuilderSlots["toolbar"]>;
136
149
  declare const muiBuilderSlots: FormBuilderSlots;
137
150
  declare function createMuiBuilderSlots(options?: MuiAdapterOptions, customOverrides?: Partial<FormBuilderSlots>): FormBuilderSlots;
138
151
 
139
- export { type BuilderSectionName, DEFAULT_MUI_SECTION_ORDER, type MuiAdapterOptions, type MuiBuilderOverrides, type MuiBuilderSlotProps, MuiButtonAdapter, type MuiButtonVariant, MuiCheckboxAdapter, MuiErrorMessageAdapter, type MuiFieldEditorOptions, MuiFieldEditorSlot, MuiFieldsetAdapter, MuiFormBuilder, MuiFormBuilderContext, type MuiFormBuilderContextValue, type MuiFormBuilderProps, MuiIconButtonAdapter, type MuiLayoutOptions, type MuiLocalizationOptions, MuiLocalizationSlot, MuiOptionEditorSlot, MuiSectionAdapter, MuiSelectAdapter, MuiTextAreaAdapter, MuiTextInputAdapter, MuiToolbarSlot, type ResolvedMuiAdapterOptions, createMuiBuilderComponents, createMuiBuilderProps, createMuiBuilderSlots, createMuiButtonAdapter, createMuiCheckboxAdapter, createMuiErrorMessageAdapter, createMuiFieldEditorSlot, createMuiFieldsetAdapter, createMuiIconButtonAdapter, createMuiLocalizationSlot, createMuiOptionEditorSlot, createMuiSectionAdapter, createMuiSelectAdapter, createMuiTextAreaAdapter, createMuiTextInputAdapter, createMuiToolbarSlot, mergeMuiAdapterOptions, muiBuilderComponents, muiBuilderSlots, muiDefaultIconResolver, resolveMuiAdapterOptions, useResolvedMuiAdapterOptions };
152
+ export { type BuilderSectionName, DEFAULT_MUI_SECTION_ORDER, type LocaleOptionItem, type LocalizationSectionPlacement, MUI_LOCALIZATION_SECTION_ORDERS, type MuiAdapterOptions, type MuiBuilderOverrides, type MuiBuilderSlotProps, MuiButtonAdapter, type MuiButtonVariant, MuiCheckboxAdapter, MuiErrorMessageAdapter, type MuiFieldEditorOptions, MuiFieldEditorSlot, MuiFieldsetAdapter, MuiFormBuilder, MuiFormBuilderContext, type MuiFormBuilderContextValue, type MuiFormBuilderProps, MuiIconButtonAdapter, type MuiLayoutOptions, type MuiLocalizationOptions, MuiLocalizationSlot, MuiOptionEditorSlot, MuiSectionAdapter, MuiSelectAdapter, MuiTextAreaAdapter, MuiTextInputAdapter, MuiToolbarSlot, type ResolvedMuiAdapterOptions, createMuiBuilderComponents, createMuiBuilderProps, createMuiBuilderSlots, createMuiButtonAdapter, createMuiCheckboxAdapter, createMuiErrorMessageAdapter, createMuiFieldEditorSlot, createMuiFieldsetAdapter, createMuiIconButtonAdapter, createMuiLocalizationSlot, createMuiOptionEditorSlot, createMuiSectionAdapter, createMuiSelectAdapter, createMuiTextAreaAdapter, createMuiTextInputAdapter, createMuiToolbarSlot, mergeMuiAdapterOptions, muiBuilderComponents, muiBuilderSlots, muiDefaultIconResolver, resolveMuiAdapterOptions, useResolvedMuiAdapterOptions };
package/dist/index.js CHANGED
@@ -9,8 +9,15 @@ var DEFAULT_MUI_SECTION_ORDER = [
9
9
  "basicSettings",
10
10
  "questions",
11
11
  "addQuestion",
12
- "localization"
12
+ "localization",
13
+ "completionMessage"
13
14
  ];
15
+ var MUI_LOCALIZATION_SECTION_ORDERS = {
16
+ top: ["localization", "basicSettings", "completionMessage", "questions", "addQuestion"],
17
+ beforeQuestions: ["basicSettings", "localization", "completionMessage", "questions", "addQuestion"],
18
+ afterQuestions: ["basicSettings", "questions", "addQuestion", "localization", "completionMessage"],
19
+ bottom: ["basicSettings", "completionMessage", "questions", "addQuestion", "localization"]
20
+ };
14
21
  function resolveMuiAdapterOptions(options = {}) {
15
22
  const buttonVariant = options.buttonVariant ?? "contained";
16
23
  return {
@@ -35,7 +42,14 @@ function resolveMuiAdapterOptions(options = {}) {
35
42
  localizationOptions: {
36
43
  collapsible: options.localizationOptions?.collapsible ?? false,
37
44
  defaultExpanded: options.localizationOptions?.defaultExpanded ?? false,
38
- defaultLocaleControl: options.localizationOptions?.defaultLocaleControl ?? "editable"
45
+ showSummary: options.localizationOptions?.showSummary ?? false,
46
+ ...options.localizationOptions?.availableLocales === void 0 ? {} : { availableLocales: options.localizationOptions.availableLocales },
47
+ ...options.localizationOptions?.placement === void 0 ? {} : { placement: options.localizationOptions.placement },
48
+ ...options.localizationOptions?.renderSummary === void 0 ? {} : { renderSummary: options.localizationOptions.renderSummary },
49
+ ...options.localizationOptions?.emptyStateMessage === void 0 ? {} : { emptyStateMessage: options.localizationOptions.emptyStateMessage },
50
+ defaultLocaleControl: options.localizationOptions?.defaultLocaleControl ?? "editable",
51
+ noWrapActions: options.localizationOptions?.noWrapActions ?? true,
52
+ autoFocusNewTab: options.localizationOptions?.autoFocusNewTab ?? true
39
53
  },
40
54
  muiSlotProps: options.muiSlotProps ?? {}
41
55
  };
@@ -73,6 +87,7 @@ function createMuiButtonAdapter(options) {
73
87
  "aria-describedby": ariaDescribedBy,
74
88
  "aria-labelledby": ariaLabelledBy,
75
89
  onClick,
90
+ noWrap,
76
91
  children,
77
92
  title,
78
93
  variant,
@@ -98,6 +113,7 @@ function createMuiButtonAdapter(options) {
98
113
  color: variant === "danger" ? "error" : "primary",
99
114
  variant: resolved.buttonVariants?.[variant ?? "primary"] ?? resolved.buttonVariant,
100
115
  fullWidth: resolved.buttonFullWidth ?? false,
116
+ sx: { whiteSpace: noWrap === false ? "normal" : "nowrap" },
101
117
  children
102
118
  }
103
119
  );
@@ -126,6 +142,8 @@ function createMuiCheckboxAdapter(options) {
126
142
  label
127
143
  }) {
128
144
  const resolved = useResolvedMuiAdapterOptions(options);
145
+ const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
146
+ const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
129
147
  return /* @__PURE__ */ jsxs(FormControl, { className, error: Boolean(error), required, disabled: disabled || readOnly, children: [
130
148
  /* @__PURE__ */ jsx2(
131
149
  FormControlLabel,
@@ -142,7 +160,7 @@ function createMuiCheckboxAdapter(options) {
142
160
  disabled: disabled || readOnly,
143
161
  inputProps: {
144
162
  "aria-label": ariaLabel,
145
- "aria-describedby": ariaDescribedBy,
163
+ "aria-describedby": describedBy,
146
164
  "aria-labelledby": ariaLabelledBy
147
165
  },
148
166
  onChange: (event) => onChange(event.target.checked)
@@ -150,7 +168,7 @@ function createMuiCheckboxAdapter(options) {
150
168
  )
151
169
  }
152
170
  ),
153
- helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx2(FormHelperText, { id: ariaDescribedBy, children: helperText })
171
+ helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx2(FormHelperText, { id: helperId, children: helperText })
154
172
  ] });
155
173
  };
156
174
  }
@@ -339,10 +357,13 @@ function createMuiSelectAdapter(options) {
339
357
  helperText,
340
358
  value,
341
359
  onChange,
360
+ onKeyDown,
342
361
  options: selectOptions
343
362
  }) {
344
363
  const resolved = useResolvedMuiAdapterOptions(options);
345
364
  const labelId = id === void 0 || label === void 0 ? void 0 : `${id}-label`;
365
+ const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
366
+ const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
346
367
  const handleChange = (event) => onChange(event.target.value);
347
368
  return /* @__PURE__ */ jsxs4(
348
369
  FormControl2,
@@ -364,18 +385,20 @@ function createMuiSelectAdapter(options) {
364
385
  name,
365
386
  label,
366
387
  value,
388
+ disabled,
367
389
  onChange: handleChange,
390
+ onKeyDown,
368
391
  required,
369
392
  readOnly,
370
393
  inputProps: {
371
394
  "aria-label": ariaLabel,
372
- "aria-describedby": ariaDescribedBy,
395
+ "aria-describedby": describedBy,
373
396
  "aria-labelledby": ariaLabelledBy
374
397
  },
375
398
  children: selectOptions.map((option) => /* @__PURE__ */ jsx7(MenuItem, { value: option.value, disabled: option.disabled, children: option.label }, option.value))
376
399
  }
377
400
  ),
378
- helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx7(FormHelperText2, { id: ariaDescribedBy, children: helperText })
401
+ helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx7(FormHelperText2, { id: helperId, children: helperText })
379
402
  ]
380
403
  }
381
404
  );
@@ -462,6 +485,7 @@ function createMuiTextInputAdapter(options) {
462
485
  helperText,
463
486
  value,
464
487
  onChange,
488
+ onKeyDown,
465
489
  placeholder,
466
490
  maxLength,
467
491
  inputMode,
@@ -503,7 +527,8 @@ function createMuiTextInputAdapter(options) {
503
527
  fullWidth: resolved.inputFullWidth ?? resolved.fullWidth,
504
528
  size: resolved.size,
505
529
  variant: resolved.variant,
506
- onChange: (event) => onChange(event.currentTarget.value)
530
+ onChange: (event) => onChange(event.currentTarget.value),
531
+ onKeyDown
507
532
  }
508
533
  );
509
534
  };
@@ -1026,9 +1051,32 @@ var MuiFieldEditorSlot = createMuiFieldEditorSlot();
1026
1051
 
1027
1052
  // src/slots/Localization.tsx
1028
1053
  import { ExpandMore } from "@mui/icons-material";
1029
- import { Accordion, AccordionDetails, AccordionSummary, Box as Box2, Chip, Stack as Stack4, Tab, Tabs, Typography as Typography4 } from "@mui/material";
1030
- import { useState } from "react";
1054
+ import {
1055
+ Accordion,
1056
+ AccordionDetails,
1057
+ AccordionSummary,
1058
+ Alert as Alert2,
1059
+ Box as Box2,
1060
+ Chip,
1061
+ Stack as Stack4,
1062
+ Tab,
1063
+ Tabs,
1064
+ Typography as Typography4
1065
+ } from "@mui/material";
1066
+ import { useEffect, useState } from "react";
1031
1067
  import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
1068
+ var DEFAULT_EMPTY_STATE_MESSAGE = "No translation locales have been added yet. Select a language from the dropdown above to add one.";
1069
+ function normalizeLocaleOptions(options, getLocaleLabel) {
1070
+ const seen = /* @__PURE__ */ new Set();
1071
+ const normalized = [];
1072
+ for (const item of options) {
1073
+ const value = typeof item === "string" ? item : item.value;
1074
+ if (value.length === 0 || seen.has(value)) continue;
1075
+ seen.add(value);
1076
+ normalized.push({ value, label: typeof item === "string" ? getLocaleLabel?.(value) ?? value : item.label });
1077
+ }
1078
+ return normalized;
1079
+ }
1032
1080
  function createMuiLocalizationSlot(options) {
1033
1081
  return function MuiLocalization({
1034
1082
  schema,
@@ -1047,6 +1095,7 @@ function createMuiLocalizationSlot(options) {
1047
1095
  const resolved = useResolvedMuiAdapterOptions(options);
1048
1096
  const { Button: Button2, ErrorMessage, Select: Select2, TextArea, TextInput } = components;
1049
1097
  const [newLocale, setNewLocale] = useState("");
1098
+ const [pendingFocusLocale, setPendingFocusLocale] = useState(null);
1050
1099
  const locales = Array.from(
1051
1100
  new Set((schema.supportedLocales ?? []).filter((locale) => locale !== schema.defaultLocale))
1052
1101
  );
@@ -1055,75 +1104,157 @@ function createMuiLocalizationSlot(options) {
1055
1104
  ...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
1056
1105
  ...schema.supportedLocales ?? []
1057
1106
  ]);
1058
- const availableAllowedLocales = policy?.allowedLocales?.filter((locale) => !registeredLocales.has(locale));
1107
+ const localizationOptions = resolved.localizationOptions ?? {};
1108
+ const availableLocaleSource = localizationOptions.availableLocales;
1109
+ const availableLocaleOptions = availableLocaleSource === void 0 ? policy?.allowedLocales?.map((value) => ({
1110
+ value,
1111
+ label: resolved.getLocaleLabel?.(value) ?? value
1112
+ })) : normalizeLocaleOptions(availableLocaleSource, resolved.getLocaleLabel);
1113
+ const filteredAvailableLocales = availableLocaleOptions?.filter(
1114
+ (locale) => !registeredLocales.has(locale.value) && (policy?.allowedLocales === void 0 || policy.allowedLocales.includes(locale.value))
1115
+ );
1116
+ const hasLocaleSelector = filteredAvailableLocales !== void 0;
1059
1117
  const localeLimitReached = policy?.maxLocales !== void 0 && registeredLocales.size >= policy.maxLocales;
1060
1118
  const normalizedLocale = newLocale.trim();
1119
+ const selectedLocaleAvailable = !hasLocaleSelector || filteredAvailableLocales.some((locale) => locale.value === normalizedLocale);
1061
1120
  const localeAllowed = policy?.allowedLocales === void 0 || policy.allowedLocales.includes(normalizedLocale);
1062
- const canAddLocale = !readOnly && normalizedLocale.length > 0 && localeAllowed && !registeredLocales.has(normalizedLocale) && !localeLimitReached;
1121
+ const canAddLocale = !readOnly && normalizedLocale.length > 0 && selectedLocaleAvailable && localeAllowed && !registeredLocales.has(normalizedLocale) && !localeLimitReached;
1063
1122
  const handleTabChange = (_event, value) => onCurrentLocaleChange(value);
1064
1123
  const addLocale = () => {
1065
1124
  if (!canAddLocale) return;
1066
1125
  const result = actions.addLocale(normalizedLocale);
1067
1126
  if (!result.success) return;
1068
1127
  onCurrentLocaleChange(normalizedLocale);
1128
+ if (localizationOptions.autoFocusNewTab ?? true) setPendingFocusLocale(normalizedLocale);
1069
1129
  setNewLocale("");
1070
1130
  };
1131
+ const handleAddKeyDown = (event) => {
1132
+ if (event.key !== "Enter" || !canAddLocale) return;
1133
+ event.preventDefault();
1134
+ event.stopPropagation();
1135
+ addLocale();
1136
+ };
1137
+ useEffect(() => {
1138
+ if (pendingFocusLocale === null || currentLocale !== pendingFocusLocale) return;
1139
+ const tab = document.getElementById(`mui-builder-locale-tab-${pendingFocusLocale}`);
1140
+ if (tab === null) return;
1141
+ tab.focus();
1142
+ setPendingFocusLocale(null);
1143
+ }, [currentLocale, pendingFocusLocale]);
1071
1144
  const stackProps = resolved.muiSlotProps?.stack;
1072
- const collapsible = resolved.localizationOptions?.collapsible ?? false;
1073
- const defaultLocaleControl = resolved.localizationOptions?.defaultLocaleControl ?? "editable";
1145
+ const collapsible = localizationOptions.collapsible ?? false;
1146
+ const defaultLocaleControl = localizationOptions.defaultLocaleControl ?? "editable";
1147
+ const configuredTranslationLocales = locales.filter((locale) => locale !== schema.defaultLocale);
1148
+ const configuredLocales = [
1149
+ ...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
1150
+ ...configuredTranslationLocales
1151
+ ];
1152
+ const legacySummaryLabel = configuredTranslationLocales.length === 0 ? "Translations not configured" : `${configuredLocales.length} ${configuredLocales.length === 1 ? "language" : "languages"} configured: ${configuredLocales.map(
1153
+ (locale) => locale === schema.defaultLocale ? `${resolved.getLocaleLabel?.(locale) ?? locale} (default)` : resolved.getLocaleLabel?.(locale) ?? locale
1154
+ ).join(", ")}`;
1155
+ const emptyStateMessage = localizationOptions.emptyStateMessage === void 0 ? DEFAULT_EMPTY_STATE_MESSAGE : translate(localizationOptions.emptyStateMessage);
1156
+ const summaryContext = {
1157
+ defaultLocale: schema.defaultLocale ?? "",
1158
+ supportedLocales: schema.supportedLocales ?? [],
1159
+ totalLocales: registeredLocales.size
1160
+ };
1161
+ const summary = localizationOptions.renderSummary === void 0 ? localizationOptions.showSummary ? configuredTranslationLocales.length > 0 ? /* @__PURE__ */ jsx14(Alert2, { severity: "success", sx: { mb: 2 }, children: translate("builder.localization.localesConfiguredSummary", {
1162
+ count: schema.supportedLocales?.length ?? 0
1163
+ }) }) : /* @__PURE__ */ jsx14(Alert2, { severity: "info", sx: { mb: 2 }, children: translate("builder.localization.noLocalesConfigured") }) : null : localizationOptions.renderSummary(summaryContext);
1164
+ const title = /* @__PURE__ */ jsxs8(Stack4, { alignItems: "center", direction: "row", spacing: 1, children: [
1165
+ /* @__PURE__ */ jsx14(Typography4, { variant: "subtitle1", fontWeight: "bold", children: translate("builder.localization") }),
1166
+ localizationOptions.renderSummary === void 0 && localizationOptions.showSummary && configuredTranslationLocales.length > 0 ? /* @__PURE__ */ jsx14(
1167
+ Chip,
1168
+ {
1169
+ label: legacySummaryLabel,
1170
+ size: "small",
1171
+ color: configuredTranslationLocales.length > 0 ? "primary" : "default",
1172
+ variant: "outlined"
1173
+ }
1174
+ ) : null
1175
+ ] });
1176
+ const noCandidateLocales = hasLocaleSelector && filteredAvailableLocales.length === 0;
1177
+ const candidateHelperText = localeLimitReached ? void 0 : noCandidateLocales ? translate("builder.localization.allLocalesAdded") : void 0;
1074
1178
  const content = /* @__PURE__ */ jsxs8(Stack4, { ...stackProps, spacing: resolved.dense ? 1 : 2, children: [
1075
- !collapsible ? /* @__PURE__ */ jsx14(Typography4, { variant: "subtitle1", fontWeight: "bold", children: translate("builder.localization") }) : null,
1076
- /* @__PURE__ */ jsxs8(Stack4, { ...stackProps, direction: { xs: "column", sm: "row" }, spacing: resolved.dense ? 1 : 2, children: [
1077
- defaultLocaleControl === "hidden" ? null : defaultLocaleControl === "readOnly" ? /* @__PURE__ */ jsxs8(Stack4, { spacing: 0.5, children: [
1078
- /* @__PURE__ */ jsx14(Typography4, { variant: "caption", color: "text.secondary", children: translate("builder.defaultLocale") }),
1079
- /* @__PURE__ */ jsx14(
1080
- Chip,
1081
- {
1082
- label: resolved.getLocaleLabel?.(schema.defaultLocale ?? "") ?? schema.defaultLocale ?? "",
1083
- size: resolved.size
1084
- }
1085
- )
1086
- ] }) : /* @__PURE__ */ jsx14(
1087
- TextInput,
1088
- {
1089
- id: "mui-builder-default-locale",
1090
- label: translate("builder.defaultLocale"),
1091
- value: schema.defaultLocale ?? "",
1092
- disabled: readOnly,
1093
- onChange: (value) => {
1094
- const result = actions.setDefaultLocale(value);
1095
- if (result.success && value === currentLocale) onCurrentLocaleChange("");
1096
- }
1097
- }
1098
- ),
1099
- availableAllowedLocales === void 0 ? /* @__PURE__ */ jsx14(
1100
- TextInput,
1101
- {
1102
- id: "mui-builder-new-locale",
1103
- label: translate("builder.addLocale"),
1104
- value: newLocale,
1105
- disabled: readOnly || localeLimitReached,
1106
- onChange: setNewLocale
1107
- }
1108
- ) : /* @__PURE__ */ jsx14(
1109
- Select2,
1110
- {
1111
- id: "mui-builder-new-locale",
1112
- label: translate("builder.addLocale"),
1113
- value: newLocale,
1114
- options: [
1115
- { value: "", label: "\u2014" },
1116
- ...availableAllowedLocales.map((locale) => ({
1117
- value: locale,
1118
- label: resolved.getLocaleLabel?.(locale) ?? locale
1119
- }))
1120
- ],
1121
- disabled: readOnly || localeLimitReached || availableAllowedLocales.length === 0,
1122
- onChange: setNewLocale
1123
- }
1124
- ),
1125
- /* @__PURE__ */ jsx14(Button2, { variant: "primary", action: "addLocale", disabled: !canAddLocale, onClick: addLocale, children: translate("builder.addLocale") })
1126
- ] }),
1179
+ !collapsible ? title : null,
1180
+ summary,
1181
+ /* @__PURE__ */ jsxs8(
1182
+ Stack4,
1183
+ {
1184
+ ...stackProps,
1185
+ direction: { xs: "column", sm: "row" },
1186
+ alignItems: { xs: "stretch", sm: "center" },
1187
+ spacing: resolved.dense ? 1 : 2,
1188
+ sx: { mt: 1 },
1189
+ children: [
1190
+ defaultLocaleControl === "hidden" ? null : /* @__PURE__ */ jsx14(Box2, { sx: { flexGrow: 1, minWidth: 0, width: { xs: "100%", sm: "auto" } }, children: defaultLocaleControl === "readOnly" ? /* @__PURE__ */ jsxs8(Stack4, { spacing: 0.5, children: [
1191
+ /* @__PURE__ */ jsx14(Typography4, { variant: "caption", color: "text.secondary", children: translate("builder.defaultLocale") }),
1192
+ /* @__PURE__ */ jsx14(
1193
+ Chip,
1194
+ {
1195
+ label: resolved.getLocaleLabel?.(schema.defaultLocale ?? "") ?? schema.defaultLocale ?? "",
1196
+ size: resolved.size
1197
+ }
1198
+ )
1199
+ ] }) : /* @__PURE__ */ jsx14(
1200
+ TextInput,
1201
+ {
1202
+ id: "mui-builder-default-locale",
1203
+ label: translate("builder.defaultLocale"),
1204
+ value: schema.defaultLocale ?? "",
1205
+ disabled: readOnly,
1206
+ onChange: (value) => {
1207
+ const result = actions.setDefaultLocale(value);
1208
+ if (result.success && value === currentLocale) onCurrentLocaleChange("");
1209
+ }
1210
+ }
1211
+ ) }),
1212
+ /* @__PURE__ */ jsx14(Box2, { sx: { flexGrow: 1, minWidth: 0, width: { xs: "100%", sm: "auto" } }, onKeyDownCapture: handleAddKeyDown, children: hasLocaleSelector ? /* @__PURE__ */ jsx14(
1213
+ Select2,
1214
+ {
1215
+ id: "mui-builder-new-locale",
1216
+ label: translate("builder.localization.selectLocaleToAdd"),
1217
+ value: newLocale,
1218
+ options: filteredAvailableLocales.map((locale) => ({ value: locale.value, label: locale.label })),
1219
+ ...candidateHelperText === void 0 ? {} : { helperText: candidateHelperText },
1220
+ disabled: readOnly || localeLimitReached || noCandidateLocales,
1221
+ onChange: setNewLocale
1222
+ }
1223
+ ) : /* @__PURE__ */ jsx14(
1224
+ TextInput,
1225
+ {
1226
+ id: "mui-builder-new-locale",
1227
+ label: translate("builder.addLocale"),
1228
+ value: newLocale,
1229
+ disabled: readOnly || localeLimitReached,
1230
+ onChange: setNewLocale
1231
+ }
1232
+ ) }),
1233
+ /* @__PURE__ */ jsx14(
1234
+ Box2,
1235
+ {
1236
+ sx: {
1237
+ flexShrink: 0,
1238
+ minWidth: "max-content",
1239
+ ...localizationOptions.noWrapActions ?? true ? { whiteSpace: "nowrap" } : {}
1240
+ },
1241
+ children: /* @__PURE__ */ jsx14(
1242
+ Button2,
1243
+ {
1244
+ variant: "primary",
1245
+ action: "addLocale",
1246
+ noWrap: localizationOptions.noWrapActions ?? true,
1247
+ disabled: !canAddLocale,
1248
+ onClick: addLocale,
1249
+ children: translate("builder.addLocale")
1250
+ }
1251
+ )
1252
+ }
1253
+ )
1254
+ ]
1255
+ }
1256
+ ),
1257
+ localeLimitReached ? /* @__PURE__ */ jsx14(Typography4, { variant: "body2", color: "text.secondary", children: translate("builder.localization.maxLocalesReached", { max: policy?.maxLocales ?? 0 }) }) : null,
1127
1258
  locales.length === 0 ? null : /* @__PURE__ */ jsx14(
1128
1259
  Tabs,
1129
1260
  {
@@ -1135,25 +1266,28 @@ function createMuiLocalizationSlot(options) {
1135
1266
  children: locales.map((locale) => /* @__PURE__ */ jsx14(
1136
1267
  Tab,
1137
1268
  {
1269
+ id: `mui-builder-locale-tab-${locale}`,
1138
1270
  value: locale,
1139
1271
  label: resolved.getLocaleLabel?.(locale) ?? locale,
1140
- disabled: readOnly && locale !== currentLocale
1272
+ disabled: readOnly && locale !== currentLocale,
1273
+ sx: { whiteSpace: "nowrap" }
1141
1274
  },
1142
1275
  locale
1143
1276
  ))
1144
1277
  }
1145
1278
  ),
1146
- !translationAdapterAvailable ? null : /* @__PURE__ */ jsx14(
1279
+ !translationAdapterAvailable ? null : /* @__PURE__ */ jsx14(Box2, { sx: { minWidth: "max-content", whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsx14(
1147
1280
  Button2,
1148
1281
  {
1149
1282
  variant: "secondary",
1283
+ noWrap: localizationOptions.noWrapActions ?? true,
1150
1284
  disabled: readOnly || !editingLocaleConfigured || isTranslating,
1151
1285
  onClick: onAutoTranslate,
1152
1286
  children: isTranslating ? translate("builder.translating") : translate("builder.autoTranslate")
1153
1287
  }
1154
- ),
1288
+ ) }),
1155
1289
  translationError === void 0 ? null : /* @__PURE__ */ jsx14(ErrorMessage, { message: translationError }),
1156
- !editingLocaleConfigured ? /* @__PURE__ */ jsx14(Typography4, { variant: "body2", color: "text.secondary", children: translate("builder.selectLocale") }) : /* @__PURE__ */ jsxs8(Stack4, { ...stackProps, spacing: resolved.dense ? 1 : 2, children: [
1290
+ !editingLocaleConfigured ? /* @__PURE__ */ jsx14(Typography4, { variant: "body2", color: "text.secondary", children: locales.length === 0 ? emptyStateMessage : translate("builder.selectLocale") }) : /* @__PURE__ */ jsxs8(Stack4, { ...stackProps, spacing: resolved.dense ? 1 : 2, children: [
1157
1291
  /* @__PURE__ */ jsx14(
1158
1292
  TextInput,
1159
1293
  {
@@ -1187,10 +1321,10 @@ function createMuiLocalizationSlot(options) {
1187
1321
  ] })
1188
1322
  ] });
1189
1323
  const configured = locales.length > 0;
1190
- const defaultExpanded = resolved.localizationOptions?.defaultExpanded === "when-configured" ? configured : resolved.localizationOptions?.defaultExpanded ?? false;
1324
+ const defaultExpanded = localizationOptions.defaultExpanded === "when-configured" ? configured : localizationOptions.defaultExpanded === "always" ? true : localizationOptions.defaultExpanded ?? false;
1191
1325
  if (collapsible) {
1192
1326
  return /* @__PURE__ */ jsxs8(Accordion, { ...resolved.muiSlotProps?.accordion, "data-mui-slot": "localization", defaultExpanded, children: [
1193
- /* @__PURE__ */ jsx14(AccordionSummary, { expandIcon: /* @__PURE__ */ jsx14(ExpandMore, {}), children: /* @__PURE__ */ jsx14(Typography4, { variant: "subtitle1", fontWeight: "bold", children: translate("builder.localization") }) }),
1327
+ /* @__PURE__ */ jsx14(AccordionSummary, { expandIcon: /* @__PURE__ */ jsx14(ExpandMore, {}), children: title }),
1194
1328
  /* @__PURE__ */ jsx14(AccordionDetails, { children: content })
1195
1329
  ] });
1196
1330
  }
@@ -1267,7 +1401,8 @@ function MuiFormBuilder({
1267
1401
  const contextValue = useMemo(() => ({ options: contextOptions }), [contextOptions]);
1268
1402
  const components = useMemo(() => ({ ...muiBuilderComponents, ...customComponents }), [customComponents]);
1269
1403
  const slots = useMemo(() => ({ ...muiBuilderSlots, ...customSlots }), [customSlots]);
1270
- const resolvedSectionOrder = sectionOrder ?? contextOptions.layoutOptions?.sectionOrder;
1404
+ const placement = contextOptions.localizationOptions?.placement;
1405
+ const resolvedSectionOrder = sectionOrder ?? (placement === void 0 ? contextOptions.layoutOptions?.sectionOrder : MUI_LOCALIZATION_SECTION_ORDERS[placement]);
1271
1406
  return /* @__PURE__ */ jsx15(MuiFormBuilderContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx15(
1272
1407
  FormBuilder,
1273
1408
  {
@@ -1281,6 +1416,7 @@ function MuiFormBuilder({
1281
1416
  }
1282
1417
  export {
1283
1418
  DEFAULT_MUI_SECTION_ORDER,
1419
+ MUI_LOCALIZATION_SECTION_ORDERS,
1284
1420
  MuiButtonAdapter,
1285
1421
  MuiCheckboxAdapter,
1286
1422
  MuiErrorMessageAdapter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@form-engine-ts/mui",
3
- "version": "2.9.5",
3
+ "version": "3.0.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -46,8 +46,8 @@
46
46
  "@mui/material": "^6.0.0 || ^7.0.0",
47
47
  "react": "^18.0.0 || ^19.0.0",
48
48
  "react-dom": "^18.0.0 || ^19.0.0",
49
- "@form-engine-ts/core": "2.9.5",
50
- "@form-engine-ts/react": "2.9.5"
49
+ "@form-engine-ts/core": "3.0.0",
50
+ "@form-engine-ts/react": "3.0.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@emotion/react": "^11.14.0",
@@ -58,8 +58,8 @@
58
58
  "@types/react-dom": "^19.2.4",
59
59
  "react": "^19.2.8",
60
60
  "react-dom": "^19.2.8",
61
- "@form-engine-ts/core": "2.9.5",
62
- "@form-engine-ts/react": "2.9.5"
61
+ "@form-engine-ts/core": "3.0.0",
62
+ "@form-engine-ts/react": "3.0.0"
63
63
  },
64
64
  "scripts": {
65
65
  "build": "tsup src/index.ts --format esm,cjs --dts --clean --external @emotion/react --external @emotion/styled --external @form-engine-ts/core --external @form-engine-ts/react --external @mui/icons-material --external @mui/material --external react --external react-dom",