@form-engine-ts/mui 2.9.5 → 2.9.6

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
@@ -27,6 +27,8 @@ import { MuiFormBuilder } from "@form-engine-ts/mui";
27
27
  localizationOptions={{
28
28
  collapsible: true,
29
29
  defaultExpanded: "when-configured",
30
+ showSummary: true,
31
+ emptyStateMessage: "No translation locales have been added yet.",
30
32
  defaultLocaleControl: "readOnly"
31
33
  }}
32
34
  muiSlotProps={{ card: { sx: { p: 2 } }, accordion: { elevation: 0 } }}
@@ -54,6 +56,9 @@ slots as it does for the standard builder.
54
56
  width options. `fieldEditorOptions.description` and `localizationOptions.defaultLocaleControl` independently make those
55
57
  controls editable, read-only, or hidden. Set `dense` to reduce section, editor, option, and toolbar spacing.
56
58
  `buttonVariants` can override the MUI variant for `primary`, `secondary`, and `danger` actions independently.
59
+ `localizationOptions.showSummary` adds a locale summary badge to the localization heading, and
60
+ `localizationOptions.emptyStateMessage` customizes the guidance shown before a translation locale is added. The
61
+ `defaultExpanded` option also accepts `"always"`.
57
62
  `layoutOptions`, `localizationOptions`, and `muiSlotProps` are also accepted in `muiOptions` for low-level factories such
58
63
  as `createMuiBuilderSlots`; the dedicated `MuiFormBuilder` props take precedence.
59
64
 
package/dist/index.cjs CHANGED
@@ -98,6 +98,8 @@ function resolveMuiAdapterOptions(options = {}) {
98
98
  localizationOptions: {
99
99
  collapsible: options.localizationOptions?.collapsible ?? false,
100
100
  defaultExpanded: options.localizationOptions?.defaultExpanded ?? false,
101
+ showSummary: options.localizationOptions?.showSummary ?? false,
102
+ ...options.localizationOptions?.emptyStateMessage === void 0 ? {} : { emptyStateMessage: options.localizationOptions.emptyStateMessage },
101
103
  defaultLocaleControl: options.localizationOptions?.defaultLocaleControl ?? "editable"
102
104
  },
103
105
  muiSlotProps: options.muiSlotProps ?? {}
@@ -161,6 +163,7 @@ function createMuiButtonAdapter(options) {
161
163
  color: variant === "danger" ? "error" : "primary",
162
164
  variant: resolved.buttonVariants?.[variant ?? "primary"] ?? resolved.buttonVariant,
163
165
  fullWidth: resolved.buttonFullWidth ?? false,
166
+ sx: { whiteSpace: "nowrap" },
164
167
  children
165
168
  }
166
169
  );
@@ -189,6 +192,8 @@ function createMuiCheckboxAdapter(options) {
189
192
  label
190
193
  }) {
191
194
  const resolved = useResolvedMuiAdapterOptions(options);
195
+ const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
196
+ const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
192
197
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_material2.FormControl, { className, error: Boolean(error), required, disabled: disabled || readOnly, children: [
193
198
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
194
199
  import_material2.FormControlLabel,
@@ -205,7 +210,7 @@ function createMuiCheckboxAdapter(options) {
205
210
  disabled: disabled || readOnly,
206
211
  inputProps: {
207
212
  "aria-label": ariaLabel,
208
- "aria-describedby": ariaDescribedBy,
213
+ "aria-describedby": describedBy,
209
214
  "aria-labelledby": ariaLabelledBy
210
215
  },
211
216
  onChange: (event) => onChange(event.target.checked)
@@ -213,7 +218,7 @@ function createMuiCheckboxAdapter(options) {
213
218
  )
214
219
  }
215
220
  ),
216
- helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_material2.FormHelperText, { id: ariaDescribedBy, children: helperText })
221
+ helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_material2.FormHelperText, { id: helperId, children: helperText })
217
222
  ] });
218
223
  };
219
224
  }
@@ -406,6 +411,8 @@ function createMuiSelectAdapter(options) {
406
411
  }) {
407
412
  const resolved = useResolvedMuiAdapterOptions(options);
408
413
  const labelId = id === void 0 || label === void 0 ? void 0 : `${id}-label`;
414
+ const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
415
+ const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
409
416
  const handleChange = (event) => onChange(event.target.value);
410
417
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
411
418
  import_material7.FormControl,
@@ -432,13 +439,13 @@ function createMuiSelectAdapter(options) {
432
439
  readOnly,
433
440
  inputProps: {
434
441
  "aria-label": ariaLabel,
435
- "aria-describedby": ariaDescribedBy,
442
+ "aria-describedby": describedBy,
436
443
  "aria-labelledby": ariaLabelledBy
437
444
  },
438
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))
439
446
  }
440
447
  ),
441
- helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.FormHelperText, { id: ariaDescribedBy, children: helperText })
448
+ helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.FormHelperText, { id: helperId, children: helperText })
442
449
  ]
443
450
  }
444
451
  );
@@ -1082,6 +1089,7 @@ var import_icons_material2 = require("@mui/icons-material");
1082
1089
  var import_material13 = require("@mui/material");
1083
1090
  var import_react2 = require("react");
1084
1091
  var import_jsx_runtime14 = require("react/jsx-runtime");
1092
+ var DEFAULT_EMPTY_STATE_MESSAGE = "No translation locales have been added yet. Select a language from the dropdown above to add one.";
1085
1093
  function createMuiLocalizationSlot(options) {
1086
1094
  return function MuiLocalization({
1087
1095
  schema,
@@ -1122,61 +1130,93 @@ function createMuiLocalizationSlot(options) {
1122
1130
  setNewLocale("");
1123
1131
  };
1124
1132
  const stackProps = resolved.muiSlotProps?.stack;
1125
- const collapsible = resolved.localizationOptions?.collapsible ?? false;
1126
- const defaultLocaleControl = resolved.localizationOptions?.defaultLocaleControl ?? "editable";
1133
+ const localizationOptions = resolved.localizationOptions ?? {};
1134
+ const collapsible = localizationOptions.collapsible ?? false;
1135
+ const defaultLocaleControl = localizationOptions.defaultLocaleControl ?? "editable";
1136
+ const configuredTranslationLocales = locales.filter((locale) => locale !== schema.defaultLocale);
1137
+ const configuredLocales = [
1138
+ ...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
1139
+ ...configuredTranslationLocales
1140
+ ];
1141
+ const summaryLabel = configuredTranslationLocales.length === 0 ? "Translations not configured" : `${configuredLocales.length} ${configuredLocales.length === 1 ? "language" : "languages"} configured: ${configuredLocales.map(
1142
+ (locale) => locale === schema.defaultLocale ? `${resolved.getLocaleLabel?.(locale) ?? locale} (default)` : resolved.getLocaleLabel?.(locale) ?? locale
1143
+ ).join(", ")}`;
1144
+ const emptyStateMessage = localizationOptions.emptyStateMessage === void 0 ? DEFAULT_EMPTY_STATE_MESSAGE : translate(localizationOptions.emptyStateMessage);
1145
+ const title = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_material13.Stack, { alignItems: "center", direction: "row", spacing: 1, children: [
1146
+ /* @__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)(
1148
+ import_material13.Chip,
1149
+ {
1150
+ label: summaryLabel,
1151
+ size: "small",
1152
+ color: configuredTranslationLocales.length > 0 ? "primary" : "default",
1153
+ variant: "outlined"
1154
+ }
1155
+ ) : null
1156
+ ] });
1127
1157
  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
- ] }),
1158
+ !collapsible ? title : null,
1159
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1160
+ import_material13.Stack,
1161
+ {
1162
+ ...stackProps,
1163
+ direction: { xs: "column", sm: "row" },
1164
+ alignItems: { xs: "stretch", sm: "center" },
1165
+ spacing: resolved.dense ? 1 : 2,
1166
+ sx: { mt: 1 },
1167
+ children: [
1168
+ 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: [
1169
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.Typography, { variant: "caption", color: "text.secondary", children: translate("builder.defaultLocale") }),
1170
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1171
+ import_material13.Chip,
1172
+ {
1173
+ label: resolved.getLocaleLabel?.(schema.defaultLocale ?? "") ?? schema.defaultLocale ?? "",
1174
+ size: resolved.size
1175
+ }
1176
+ )
1177
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1178
+ TextInput,
1179
+ {
1180
+ id: "mui-builder-default-locale",
1181
+ label: translate("builder.defaultLocale"),
1182
+ value: schema.defaultLocale ?? "",
1183
+ disabled: readOnly,
1184
+ onChange: (value) => {
1185
+ const result = actions.setDefaultLocale(value);
1186
+ if (result.success && value === currentLocale) onCurrentLocaleChange("");
1187
+ }
1188
+ }
1189
+ ) }),
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,
1192
+ {
1193
+ id: "mui-builder-new-locale",
1194
+ label: translate("builder.addLocale"),
1195
+ value: newLocale,
1196
+ disabled: readOnly || localeLimitReached,
1197
+ onChange: setNewLocale
1198
+ }
1199
+ ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1200
+ Select2,
1201
+ {
1202
+ id: "mui-builder-new-locale",
1203
+ label: translate("builder.addLocale"),
1204
+ 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,
1213
+ onChange: setNewLocale
1214
+ }
1215
+ ) }),
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") }) })
1217
+ ]
1218
+ }
1219
+ ),
1180
1220
  locales.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1181
1221
  import_material13.Tabs,
1182
1222
  {
@@ -1190,13 +1230,14 @@ function createMuiLocalizationSlot(options) {
1190
1230
  {
1191
1231
  value: locale,
1192
1232
  label: resolved.getLocaleLabel?.(locale) ?? locale,
1193
- disabled: readOnly && locale !== currentLocale
1233
+ disabled: readOnly && locale !== currentLocale,
1234
+ sx: { whiteSpace: "nowrap" }
1194
1235
  },
1195
1236
  locale
1196
1237
  ))
1197
1238
  }
1198
1239
  ),
1199
- !translationAdapterAvailable ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1240
+ !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
1241
  Button2,
1201
1242
  {
1202
1243
  variant: "secondary",
@@ -1204,9 +1245,9 @@ function createMuiLocalizationSlot(options) {
1204
1245
  onClick: onAutoTranslate,
1205
1246
  children: isTranslating ? translate("builder.translating") : translate("builder.autoTranslate")
1206
1247
  }
1207
- ),
1248
+ ) }),
1208
1249
  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: [
1250
+ !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
1251
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1211
1252
  TextInput,
1212
1253
  {
@@ -1240,10 +1281,10 @@ function createMuiLocalizationSlot(options) {
1240
1281
  ] })
1241
1282
  ] });
1242
1283
  const configured = locales.length > 0;
1243
- const defaultExpanded = resolved.localizationOptions?.defaultExpanded === "when-configured" ? configured : resolved.localizationOptions?.defaultExpanded ?? false;
1284
+ const defaultExpanded = localizationOptions.defaultExpanded === "when-configured" ? configured : localizationOptions.defaultExpanded === "always" ? true : localizationOptions.defaultExpanded ?? false;
1244
1285
  if (collapsible) {
1245
1286
  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") }) }),
1287
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.AccordionSummary, { expandIcon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_icons_material2.ExpandMore, {}), children: title }),
1247
1288
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_material13.AccordionDetails, { children: content })
1248
1289
  ] });
1249
1290
  }
package/dist/index.d.cts CHANGED
@@ -10,7 +10,9 @@ interface MuiLayoutOptions {
10
10
  }
11
11
  interface MuiLocalizationOptions {
12
12
  readonly collapsible?: boolean;
13
- readonly defaultExpanded?: boolean | "when-configured";
13
+ readonly defaultExpanded?: boolean | "when-configured" | "always";
14
+ readonly showSummary?: boolean;
15
+ readonly emptyStateMessage?: string;
14
16
  readonly defaultLocaleControl?: "editable" | "readOnly" | "hidden";
15
17
  }
16
18
  interface MuiFieldEditorOptions {
package/dist/index.d.ts CHANGED
@@ -10,7 +10,9 @@ interface MuiLayoutOptions {
10
10
  }
11
11
  interface MuiLocalizationOptions {
12
12
  readonly collapsible?: boolean;
13
- readonly defaultExpanded?: boolean | "when-configured";
13
+ readonly defaultExpanded?: boolean | "when-configured" | "always";
14
+ readonly showSummary?: boolean;
15
+ readonly emptyStateMessage?: string;
14
16
  readonly defaultLocaleControl?: "editable" | "readOnly" | "hidden";
15
17
  }
16
18
  interface MuiFieldEditorOptions {
package/dist/index.js CHANGED
@@ -35,6 +35,8 @@ function resolveMuiAdapterOptions(options = {}) {
35
35
  localizationOptions: {
36
36
  collapsible: options.localizationOptions?.collapsible ?? false,
37
37
  defaultExpanded: options.localizationOptions?.defaultExpanded ?? false,
38
+ showSummary: options.localizationOptions?.showSummary ?? false,
39
+ ...options.localizationOptions?.emptyStateMessage === void 0 ? {} : { emptyStateMessage: options.localizationOptions.emptyStateMessage },
38
40
  defaultLocaleControl: options.localizationOptions?.defaultLocaleControl ?? "editable"
39
41
  },
40
42
  muiSlotProps: options.muiSlotProps ?? {}
@@ -98,6 +100,7 @@ function createMuiButtonAdapter(options) {
98
100
  color: variant === "danger" ? "error" : "primary",
99
101
  variant: resolved.buttonVariants?.[variant ?? "primary"] ?? resolved.buttonVariant,
100
102
  fullWidth: resolved.buttonFullWidth ?? false,
103
+ sx: { whiteSpace: "nowrap" },
101
104
  children
102
105
  }
103
106
  );
@@ -126,6 +129,8 @@ function createMuiCheckboxAdapter(options) {
126
129
  label
127
130
  }) {
128
131
  const resolved = useResolvedMuiAdapterOptions(options);
132
+ const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
133
+ const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
129
134
  return /* @__PURE__ */ jsxs(FormControl, { className, error: Boolean(error), required, disabled: disabled || readOnly, children: [
130
135
  /* @__PURE__ */ jsx2(
131
136
  FormControlLabel,
@@ -142,7 +147,7 @@ function createMuiCheckboxAdapter(options) {
142
147
  disabled: disabled || readOnly,
143
148
  inputProps: {
144
149
  "aria-label": ariaLabel,
145
- "aria-describedby": ariaDescribedBy,
150
+ "aria-describedby": describedBy,
146
151
  "aria-labelledby": ariaLabelledBy
147
152
  },
148
153
  onChange: (event) => onChange(event.target.checked)
@@ -150,7 +155,7 @@ function createMuiCheckboxAdapter(options) {
150
155
  )
151
156
  }
152
157
  ),
153
- helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx2(FormHelperText, { id: ariaDescribedBy, children: helperText })
158
+ helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx2(FormHelperText, { id: helperId, children: helperText })
154
159
  ] });
155
160
  };
156
161
  }
@@ -343,6 +348,8 @@ function createMuiSelectAdapter(options) {
343
348
  }) {
344
349
  const resolved = useResolvedMuiAdapterOptions(options);
345
350
  const labelId = id === void 0 || label === void 0 ? void 0 : `${id}-label`;
351
+ const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
352
+ const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
346
353
  const handleChange = (event) => onChange(event.target.value);
347
354
  return /* @__PURE__ */ jsxs4(
348
355
  FormControl2,
@@ -369,13 +376,13 @@ function createMuiSelectAdapter(options) {
369
376
  readOnly,
370
377
  inputProps: {
371
378
  "aria-label": ariaLabel,
372
- "aria-describedby": ariaDescribedBy,
379
+ "aria-describedby": describedBy,
373
380
  "aria-labelledby": ariaLabelledBy
374
381
  },
375
382
  children: selectOptions.map((option) => /* @__PURE__ */ jsx7(MenuItem, { value: option.value, disabled: option.disabled, children: option.label }, option.value))
376
383
  }
377
384
  ),
378
- helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx7(FormHelperText2, { id: ariaDescribedBy, children: helperText })
385
+ helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx7(FormHelperText2, { id: helperId, children: helperText })
379
386
  ]
380
387
  }
381
388
  );
@@ -1029,6 +1036,7 @@ import { ExpandMore } from "@mui/icons-material";
1029
1036
  import { Accordion, AccordionDetails, AccordionSummary, Box as Box2, Chip, Stack as Stack4, Tab, Tabs, Typography as Typography4 } from "@mui/material";
1030
1037
  import { useState } from "react";
1031
1038
  import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
1039
+ var DEFAULT_EMPTY_STATE_MESSAGE = "No translation locales have been added yet. Select a language from the dropdown above to add one.";
1032
1040
  function createMuiLocalizationSlot(options) {
1033
1041
  return function MuiLocalization({
1034
1042
  schema,
@@ -1069,61 +1077,93 @@ function createMuiLocalizationSlot(options) {
1069
1077
  setNewLocale("");
1070
1078
  };
1071
1079
  const stackProps = resolved.muiSlotProps?.stack;
1072
- const collapsible = resolved.localizationOptions?.collapsible ?? false;
1073
- const defaultLocaleControl = resolved.localizationOptions?.defaultLocaleControl ?? "editable";
1080
+ const localizationOptions = resolved.localizationOptions ?? {};
1081
+ const collapsible = localizationOptions.collapsible ?? false;
1082
+ const defaultLocaleControl = localizationOptions.defaultLocaleControl ?? "editable";
1083
+ const configuredTranslationLocales = locales.filter((locale) => locale !== schema.defaultLocale);
1084
+ const configuredLocales = [
1085
+ ...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
1086
+ ...configuredTranslationLocales
1087
+ ];
1088
+ const summaryLabel = configuredTranslationLocales.length === 0 ? "Translations not configured" : `${configuredLocales.length} ${configuredLocales.length === 1 ? "language" : "languages"} configured: ${configuredLocales.map(
1089
+ (locale) => locale === schema.defaultLocale ? `${resolved.getLocaleLabel?.(locale) ?? locale} (default)` : resolved.getLocaleLabel?.(locale) ?? locale
1090
+ ).join(", ")}`;
1091
+ const emptyStateMessage = localizationOptions.emptyStateMessage === void 0 ? DEFAULT_EMPTY_STATE_MESSAGE : translate(localizationOptions.emptyStateMessage);
1092
+ const title = /* @__PURE__ */ jsxs8(Stack4, { alignItems: "center", direction: "row", spacing: 1, children: [
1093
+ /* @__PURE__ */ jsx14(Typography4, { variant: "subtitle1", fontWeight: "bold", children: translate("builder.localization") }),
1094
+ localizationOptions.showSummary ? /* @__PURE__ */ jsx14(
1095
+ Chip,
1096
+ {
1097
+ label: summaryLabel,
1098
+ size: "small",
1099
+ color: configuredTranslationLocales.length > 0 ? "primary" : "default",
1100
+ variant: "outlined"
1101
+ }
1102
+ ) : null
1103
+ ] });
1074
1104
  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
- ] }),
1105
+ !collapsible ? title : null,
1106
+ /* @__PURE__ */ jsxs8(
1107
+ Stack4,
1108
+ {
1109
+ ...stackProps,
1110
+ direction: { xs: "column", sm: "row" },
1111
+ alignItems: { xs: "stretch", sm: "center" },
1112
+ spacing: resolved.dense ? 1 : 2,
1113
+ sx: { mt: 1 },
1114
+ children: [
1115
+ 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: [
1116
+ /* @__PURE__ */ jsx14(Typography4, { variant: "caption", color: "text.secondary", children: translate("builder.defaultLocale") }),
1117
+ /* @__PURE__ */ jsx14(
1118
+ Chip,
1119
+ {
1120
+ label: resolved.getLocaleLabel?.(schema.defaultLocale ?? "") ?? schema.defaultLocale ?? "",
1121
+ size: resolved.size
1122
+ }
1123
+ )
1124
+ ] }) : /* @__PURE__ */ jsx14(
1125
+ TextInput,
1126
+ {
1127
+ id: "mui-builder-default-locale",
1128
+ label: translate("builder.defaultLocale"),
1129
+ value: schema.defaultLocale ?? "",
1130
+ disabled: readOnly,
1131
+ onChange: (value) => {
1132
+ const result = actions.setDefaultLocale(value);
1133
+ if (result.success && value === currentLocale) onCurrentLocaleChange("");
1134
+ }
1135
+ }
1136
+ ) }),
1137
+ /* @__PURE__ */ jsx14(Box2, { sx: { flexGrow: 1, minWidth: 0, width: { xs: "100%", sm: "auto" } }, children: availableAllowedLocales === void 0 ? /* @__PURE__ */ jsx14(
1138
+ TextInput,
1139
+ {
1140
+ id: "mui-builder-new-locale",
1141
+ label: translate("builder.addLocale"),
1142
+ value: newLocale,
1143
+ disabled: readOnly || localeLimitReached,
1144
+ onChange: setNewLocale
1145
+ }
1146
+ ) : /* @__PURE__ */ jsx14(
1147
+ Select2,
1148
+ {
1149
+ id: "mui-builder-new-locale",
1150
+ label: translate("builder.addLocale"),
1151
+ value: newLocale,
1152
+ options: [
1153
+ { value: "", label: "\u2014" },
1154
+ ...availableAllowedLocales.map((locale) => ({
1155
+ value: locale,
1156
+ label: resolved.getLocaleLabel?.(locale) ?? locale
1157
+ }))
1158
+ ],
1159
+ disabled: readOnly || localeLimitReached || availableAllowedLocales.length === 0,
1160
+ onChange: setNewLocale
1161
+ }
1162
+ ) }),
1163
+ /* @__PURE__ */ jsx14(Box2, { sx: { flexShrink: 0, minWidth: "max-content", whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsx14(Button2, { variant: "primary", action: "addLocale", disabled: !canAddLocale, onClick: addLocale, children: translate("builder.addLocale") }) })
1164
+ ]
1165
+ }
1166
+ ),
1127
1167
  locales.length === 0 ? null : /* @__PURE__ */ jsx14(
1128
1168
  Tabs,
1129
1169
  {
@@ -1137,13 +1177,14 @@ function createMuiLocalizationSlot(options) {
1137
1177
  {
1138
1178
  value: locale,
1139
1179
  label: resolved.getLocaleLabel?.(locale) ?? locale,
1140
- disabled: readOnly && locale !== currentLocale
1180
+ disabled: readOnly && locale !== currentLocale,
1181
+ sx: { whiteSpace: "nowrap" }
1141
1182
  },
1142
1183
  locale
1143
1184
  ))
1144
1185
  }
1145
1186
  ),
1146
- !translationAdapterAvailable ? null : /* @__PURE__ */ jsx14(
1187
+ !translationAdapterAvailable ? null : /* @__PURE__ */ jsx14(Box2, { sx: { minWidth: "max-content", whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsx14(
1147
1188
  Button2,
1148
1189
  {
1149
1190
  variant: "secondary",
@@ -1151,9 +1192,9 @@ function createMuiLocalizationSlot(options) {
1151
1192
  onClick: onAutoTranslate,
1152
1193
  children: isTranslating ? translate("builder.translating") : translate("builder.autoTranslate")
1153
1194
  }
1154
- ),
1195
+ ) }),
1155
1196
  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: [
1197
+ !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
1198
  /* @__PURE__ */ jsx14(
1158
1199
  TextInput,
1159
1200
  {
@@ -1187,10 +1228,10 @@ function createMuiLocalizationSlot(options) {
1187
1228
  ] })
1188
1229
  ] });
1189
1230
  const configured = locales.length > 0;
1190
- const defaultExpanded = resolved.localizationOptions?.defaultExpanded === "when-configured" ? configured : resolved.localizationOptions?.defaultExpanded ?? false;
1231
+ const defaultExpanded = localizationOptions.defaultExpanded === "when-configured" ? configured : localizationOptions.defaultExpanded === "always" ? true : localizationOptions.defaultExpanded ?? false;
1191
1232
  if (collapsible) {
1192
1233
  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") }) }),
1234
+ /* @__PURE__ */ jsx14(AccordionSummary, { expandIcon: /* @__PURE__ */ jsx14(ExpandMore, {}), children: title }),
1194
1235
  /* @__PURE__ */ jsx14(AccordionDetails, { children: content })
1195
1236
  ] });
1196
1237
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@form-engine-ts/mui",
3
- "version": "2.9.5",
3
+ "version": "2.9.6",
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": "2.9.6",
50
+ "@form-engine-ts/react": "2.9.6"
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": "2.9.6",
62
+ "@form-engine-ts/react": "2.9.6"
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",