@form-engine-ts/mui 3.1.0 → 4.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
@@ -76,8 +76,9 @@ default.
76
76
  `layoutOptions`, `localizationOptions`, and `muiSlotProps` are also accepted in `muiOptions` for low-level factories such
77
77
  as `createMuiBuilderSlots`; the dedicated `MuiFormBuilder` props take precedence.
78
78
 
79
- Select options in the MUI adapter support icons, descriptions, custom metadata, and custom `renderOption`/`renderValue`
80
- callbacks. Without a custom renderer, icons appear beside labels in the menu and in the selected value. The standard
79
+ Select options in the MUI adapter support icons, descriptions, groups (`group`/`groupLabel` or `kind`), custom metadata,
80
+ and custom `renderOption`/`renderValue` callbacks. Without a custom renderer, icons appear beside labels in the menu and
81
+ in the selected value. The standard
81
82
  MUI field editor supplies icons for every field type and accepts `slots.fieldTypeSelect` and `slots.fieldEditorHeader`
82
83
  for focused customization. `muiSlotProps` also supports `textField`, `select`, `selectMenu`, `checkbox`, `button`, and
83
84
  `iconButton` MUI props in addition to the layout props.
package/dist/index.cjs CHANGED
@@ -210,6 +210,7 @@ function createMuiCheckboxAdapter(options) {
210
210
  }) {
211
211
  const resolved = useResolvedMuiAdapterOptions(options);
212
212
  const checkboxSlotProps = resolved.muiSlotProps?.checkbox;
213
+ const checkboxInputProps = checkboxSlotProps?.inputProps;
213
214
  const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
214
215
  const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
215
216
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_material2.FormControl, { className, error: Boolean(error), required, disabled: disabled || readOnly, children: [
@@ -228,9 +229,10 @@ function createMuiCheckboxAdapter(options) {
228
229
  required,
229
230
  disabled: disabled || readOnly,
230
231
  inputProps: {
231
- "aria-label": ariaLabel,
232
- "aria-describedby": describedBy,
233
- "aria-labelledby": ariaLabelledBy
232
+ ...checkboxInputProps,
233
+ ...ariaLabel === void 0 ? {} : { "aria-label": ariaLabel },
234
+ ...describedBy === void 0 ? {} : { "aria-describedby": describedBy },
235
+ ...ariaLabelledBy === void 0 ? {} : { "aria-labelledby": ariaLabelledBy }
234
236
  },
235
237
  onChange: (event) => onChange(event.target.checked)
236
238
  }
@@ -415,6 +417,40 @@ var import_jsx_runtime7 = require("react/jsx-runtime");
415
417
  function normalizeOptions(options) {
416
418
  return options.map((option) => typeof option === "string" ? { value: option, label: option } : option);
417
419
  }
420
+ function SelectGroupHeader({ children }) {
421
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.ListSubheader, { role: "presentation", children });
422
+ }
423
+ function groupSelectOptions(options) {
424
+ const groups = [];
425
+ const groupIndexes = /* @__PURE__ */ new Map();
426
+ for (const option of options) {
427
+ const groupKey = option.group ?? option.kind;
428
+ const groupLabel = option.groupLabel ?? groupKey;
429
+ if (groupKey === void 0 && groupLabel === void 0) {
430
+ groups.push({ items: [option] });
431
+ continue;
432
+ }
433
+ const key = groupKey ?? groupLabel;
434
+ if (key === void 0) {
435
+ groups.push({ items: [option] });
436
+ continue;
437
+ }
438
+ const existingIndex = groupIndexes.get(key);
439
+ if (existingIndex === void 0) {
440
+ groupIndexes.set(key, groups.length);
441
+ groups.push({
442
+ groupKey: key,
443
+ ...groupLabel === void 0 ? {} : { groupLabel },
444
+ items: [option]
445
+ });
446
+ continue;
447
+ }
448
+ const existing = groups[existingIndex];
449
+ if (existing === void 0) continue;
450
+ groups[existingIndex] = { ...existing, items: [...existing.items, option] };
451
+ }
452
+ return groups;
453
+ }
418
454
  function createMuiSelectAdapter(options) {
419
455
  return function MuiSelect({
420
456
  id,
@@ -438,12 +474,14 @@ function createMuiSelectAdapter(options) {
438
474
  }) {
439
475
  const resolved = useResolvedMuiAdapterOptions(options);
440
476
  const normalizedOptions = normalizeOptions(selectOptions);
477
+ const groupedOptions = groupSelectOptions(normalizedOptions);
441
478
  const selectSlotProps = resolved.muiSlotProps?.select;
442
479
  const menuProps = { ...selectSlotProps?.MenuProps, ...resolved.muiSlotProps?.selectMenu };
480
+ const slotInputProps = selectSlotProps?.inputProps;
443
481
  const labelId = id === void 0 || label === void 0 ? void 0 : `${id}-label`;
444
482
  const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
445
483
  const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
446
- const handleChange = (event) => onChange(String(event.target.value));
484
+ const handleChange = (event) => onChange(event.target.value);
447
485
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
448
486
  import_material7.FormControl,
449
487
  {
@@ -483,15 +521,27 @@ function createMuiSelectAdapter(options) {
483
521
  ] });
484
522
  },
485
523
  inputProps: {
486
- ...selectSlotProps?.inputProps,
487
- "aria-label": ariaLabel,
488
- "aria-describedby": describedBy,
489
- "aria-labelledby": ariaLabelledBy
524
+ ...slotInputProps,
525
+ ...ariaLabel === void 0 ? {} : { "aria-label": ariaLabel },
526
+ ...describedBy === void 0 ? {} : { "aria-describedby": describedBy },
527
+ ...ariaLabelledBy === void 0 ? {} : { "aria-labelledby": ariaLabelledBy }
490
528
  },
491
- children: normalizedOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.MenuItem, { value: option.value, disabled: option.disabled, children: renderOption === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
492
- option.icon === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.ListItemIcon, { sx: { minWidth: 32 }, children: option.icon }),
493
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.ListItemText, { primary: option.label, secondary: option.description })
494
- ] }) : renderOption(option) }, option.value))
529
+ children: groupedOptions.flatMap((group) => [
530
+ group.groupLabel === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectGroupHeader, { children: group.groupLabel }, `header-${group.groupKey ?? group.groupLabel}`),
531
+ ...group.items.map((option) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
532
+ import_material7.MenuItem,
533
+ {
534
+ value: option.value,
535
+ disabled: option.disabled,
536
+ ...option.group === void 0 && option.groupLabel === void 0 && option.kind === void 0 ? {} : { "aria-label": option.label },
537
+ children: renderOption === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
538
+ option.icon === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.ListItemIcon, { sx: { minWidth: 32 }, children: option.icon }),
539
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.ListItemText, { primary: option.label, secondary: option.description })
540
+ ] }) : renderOption(option)
541
+ },
542
+ option.value
543
+ ))
544
+ ])
495
545
  }
496
546
  ),
497
547
  helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.FormHelperText, { id: helperId, children: helperText })
@@ -500,7 +550,10 @@ function createMuiSelectAdapter(options) {
500
550
  );
501
551
  };
502
552
  }
503
- var MuiSelectAdapter = createMuiSelectAdapter();
553
+ function MuiSelectAdapter(props) {
554
+ const Adapter = createMuiSelectAdapter();
555
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Adapter, { ...props });
556
+ }
504
557
 
505
558
  // src/adapters/TextArea.tsx
506
559
  var import_material8 = require("@mui/material");
@@ -920,6 +973,24 @@ function createMuiFieldEditorSlot(options) {
920
973
  const titleErrorId = field.title.trim().length === 0 ? `mui-field-${field.id}-title-error` : void 0;
921
974
  const FieldTypeSelect = slots?.fieldTypeSelect;
922
975
  const FieldEditorHeader = slots?.fieldEditorHeader;
976
+ const fieldTypeSelectId = `mui-field-${field.id}-type`;
977
+ const fieldTypeSelectLabel = translate("builder.type");
978
+ const fieldTypeOptions = FIELD_TYPES.filter(
979
+ (type) => allowedTypes.includes(type)
980
+ ).map((type) => {
981
+ const definition = import_core.DEFAULT_FIELD_TYPE_DEFINITIONS.find((candidate) => candidate.type === type);
982
+ const group = definition?.category;
983
+ return {
984
+ value: type,
985
+ label: translate(definition?.labelKey ?? `builder.fieldType.${type}`),
986
+ description: translate(`builder.fieldTypeDescription.${type}`),
987
+ icon: components.renderFieldTypeIcon(type),
988
+ ...group === void 0 ? {} : {
989
+ group,
990
+ groupLabel: translate(`builder.fieldCategory.${group}`)
991
+ }
992
+ };
993
+ });
923
994
  const changeFieldType = (nextType) => {
924
995
  if (allowedTypes.includes(nextType)) actions.changeFieldType(field.id, nextType);
925
996
  };
@@ -972,19 +1043,11 @@ function createMuiFieldEditorSlot(options) {
972
1043
  FieldTypeSelect === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
973
1044
  Select2,
974
1045
  {
975
- id: `mui-field-${field.id}-type`,
1046
+ id: fieldTypeSelectId,
976
1047
  name: `fields.${field.id}.type`,
977
- label: translate("builder.type"),
1048
+ label: fieldTypeSelectLabel,
978
1049
  value: allowedTypes.includes(field.type) ? field.type : "",
979
- options: FIELD_TYPES.filter((type) => allowedTypes.includes(type)).map((type) => {
980
- const definition = import_core.DEFAULT_FIELD_TYPE_DEFINITIONS.find((candidate) => candidate.type === type);
981
- return {
982
- value: type,
983
- label: translate(definition?.labelKey ?? `builder.fieldType.${type}`),
984
- icon: components.renderFieldTypeIcon(type),
985
- ...definition?.category === void 0 ? {} : { kind: definition.category }
986
- };
987
- }),
1050
+ options: fieldTypeOptions,
988
1051
  disabled: readOnly,
989
1052
  onChange: (value) => {
990
1053
  if (isFieldType(value)) changeFieldType(value);
@@ -993,11 +1056,16 @@ function createMuiFieldEditorSlot(options) {
993
1056
  ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
994
1057
  FieldTypeSelect,
995
1058
  {
1059
+ id: fieldTypeSelectId,
1060
+ name: `fields.${field.id}.type`,
1061
+ label: fieldTypeSelectLabel,
996
1062
  currentType: field.type,
997
1063
  allowedTypes,
1064
+ options: fieldTypeOptions,
998
1065
  onChangeType: changeFieldType,
999
1066
  disabled: readOnly,
1000
1067
  readOnly,
1068
+ "aria-labelledby": `${fieldTypeSelectId}-label`,
1001
1069
  renderIcon: components.renderFieldTypeIcon
1002
1070
  }
1003
1071
  )
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { BuilderActionIconType, LocalizationSummaryContext, BuilderButtonProps, BuilderCheckboxProps, BuilderErrorMessageProps, BuilderFieldsetProps, BuilderIconButtonProps, BuilderSectionProps, BuilderSelectProps, BuilderTextAreaProps, BuilderTextInputProps, FormBuilderComponents, FormBuilderSlots, FormBuilderProps, QuestionType, BuilderFieldEditorSlotProps, BuilderLocalizationSlotProps, BuilderOptionEditorSlotProps, BuilderToolbarSlotProps } from '@form-engine-ts/react';
2
2
  import * as react from 'react';
3
- import { ReactNode, ComponentType } from 'react';
3
+ import { ReactNode, ComponentType, ReactElement } from 'react';
4
4
  import { CardProps, PaperProps, AccordionProps, StackProps, TextFieldProps, SelectProps, MenuProps, CheckboxProps, ButtonProps, IconButtonProps } from '@mui/material';
5
5
 
6
6
  type BuilderSectionName = "basicSettings" | "completionMessage" | "questions" | "addQuestion" | "localization";
@@ -117,8 +117,8 @@ declare const MuiIconButtonAdapter: ComponentType<BuilderIconButtonProps>;
117
117
  declare function createMuiSectionAdapter(options?: MuiAdapterOptions): ComponentType<BuilderSectionProps>;
118
118
  declare const MuiSectionAdapter: ComponentType<BuilderSectionProps>;
119
119
 
120
- declare function createMuiSelectAdapter(options?: MuiAdapterOptions): ComponentType<BuilderSelectProps>;
121
- declare const MuiSelectAdapter: ComponentType<BuilderSelectProps>;
120
+ declare function createMuiSelectAdapter<T extends string = string>(options?: MuiAdapterOptions): ComponentType<BuilderSelectProps<T>>;
121
+ declare function MuiSelectAdapter<T extends string = string>(props: BuilderSelectProps<T>): ReactElement;
122
122
 
123
123
  declare function createMuiTextAreaAdapter(options?: MuiAdapterOptions): ComponentType<BuilderTextAreaProps>;
124
124
  declare const MuiTextAreaAdapter: ComponentType<BuilderTextAreaProps>;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { BuilderActionIconType, LocalizationSummaryContext, BuilderButtonProps, BuilderCheckboxProps, BuilderErrorMessageProps, BuilderFieldsetProps, BuilderIconButtonProps, BuilderSectionProps, BuilderSelectProps, BuilderTextAreaProps, BuilderTextInputProps, FormBuilderComponents, FormBuilderSlots, FormBuilderProps, QuestionType, BuilderFieldEditorSlotProps, BuilderLocalizationSlotProps, BuilderOptionEditorSlotProps, BuilderToolbarSlotProps } from '@form-engine-ts/react';
2
2
  import * as react from 'react';
3
- import { ReactNode, ComponentType } from 'react';
3
+ import { ReactNode, ComponentType, ReactElement } from 'react';
4
4
  import { CardProps, PaperProps, AccordionProps, StackProps, TextFieldProps, SelectProps, MenuProps, CheckboxProps, ButtonProps, IconButtonProps } from '@mui/material';
5
5
 
6
6
  type BuilderSectionName = "basicSettings" | "completionMessage" | "questions" | "addQuestion" | "localization";
@@ -117,8 +117,8 @@ declare const MuiIconButtonAdapter: ComponentType<BuilderIconButtonProps>;
117
117
  declare function createMuiSectionAdapter(options?: MuiAdapterOptions): ComponentType<BuilderSectionProps>;
118
118
  declare const MuiSectionAdapter: ComponentType<BuilderSectionProps>;
119
119
 
120
- declare function createMuiSelectAdapter(options?: MuiAdapterOptions): ComponentType<BuilderSelectProps>;
121
- declare const MuiSelectAdapter: ComponentType<BuilderSelectProps>;
120
+ declare function createMuiSelectAdapter<T extends string = string>(options?: MuiAdapterOptions): ComponentType<BuilderSelectProps<T>>;
121
+ declare function MuiSelectAdapter<T extends string = string>(props: BuilderSelectProps<T>): ReactElement;
122
122
 
123
123
  declare function createMuiTextAreaAdapter(options?: MuiAdapterOptions): ComponentType<BuilderTextAreaProps>;
124
124
  declare const MuiTextAreaAdapter: ComponentType<BuilderTextAreaProps>;
package/dist/index.js CHANGED
@@ -145,6 +145,7 @@ function createMuiCheckboxAdapter(options) {
145
145
  }) {
146
146
  const resolved = useResolvedMuiAdapterOptions(options);
147
147
  const checkboxSlotProps = resolved.muiSlotProps?.checkbox;
148
+ const checkboxInputProps = checkboxSlotProps?.inputProps;
148
149
  const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
149
150
  const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
150
151
  return /* @__PURE__ */ jsxs(FormControl, { className, error: Boolean(error), required, disabled: disabled || readOnly, children: [
@@ -163,9 +164,10 @@ function createMuiCheckboxAdapter(options) {
163
164
  required,
164
165
  disabled: disabled || readOnly,
165
166
  inputProps: {
166
- "aria-label": ariaLabel,
167
- "aria-describedby": describedBy,
168
- "aria-labelledby": ariaLabelledBy
167
+ ...checkboxInputProps,
168
+ ...ariaLabel === void 0 ? {} : { "aria-label": ariaLabel },
169
+ ...describedBy === void 0 ? {} : { "aria-describedby": describedBy },
170
+ ...ariaLabelledBy === void 0 ? {} : { "aria-labelledby": ariaLabelledBy }
169
171
  },
170
172
  onChange: (event) => onChange(event.target.checked)
171
173
  }
@@ -352,6 +354,7 @@ import {
352
354
  InputLabel,
353
355
  ListItemIcon,
354
356
  ListItemText,
357
+ ListSubheader,
355
358
  MenuItem,
356
359
  Select
357
360
  } from "@mui/material";
@@ -359,6 +362,40 @@ import { Fragment, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
359
362
  function normalizeOptions(options) {
360
363
  return options.map((option) => typeof option === "string" ? { value: option, label: option } : option);
361
364
  }
365
+ function SelectGroupHeader({ children }) {
366
+ return /* @__PURE__ */ jsx7(ListSubheader, { role: "presentation", children });
367
+ }
368
+ function groupSelectOptions(options) {
369
+ const groups = [];
370
+ const groupIndexes = /* @__PURE__ */ new Map();
371
+ for (const option of options) {
372
+ const groupKey = option.group ?? option.kind;
373
+ const groupLabel = option.groupLabel ?? groupKey;
374
+ if (groupKey === void 0 && groupLabel === void 0) {
375
+ groups.push({ items: [option] });
376
+ continue;
377
+ }
378
+ const key = groupKey ?? groupLabel;
379
+ if (key === void 0) {
380
+ groups.push({ items: [option] });
381
+ continue;
382
+ }
383
+ const existingIndex = groupIndexes.get(key);
384
+ if (existingIndex === void 0) {
385
+ groupIndexes.set(key, groups.length);
386
+ groups.push({
387
+ groupKey: key,
388
+ ...groupLabel === void 0 ? {} : { groupLabel },
389
+ items: [option]
390
+ });
391
+ continue;
392
+ }
393
+ const existing = groups[existingIndex];
394
+ if (existing === void 0) continue;
395
+ groups[existingIndex] = { ...existing, items: [...existing.items, option] };
396
+ }
397
+ return groups;
398
+ }
362
399
  function createMuiSelectAdapter(options) {
363
400
  return function MuiSelect({
364
401
  id,
@@ -382,12 +419,14 @@ function createMuiSelectAdapter(options) {
382
419
  }) {
383
420
  const resolved = useResolvedMuiAdapterOptions(options);
384
421
  const normalizedOptions = normalizeOptions(selectOptions);
422
+ const groupedOptions = groupSelectOptions(normalizedOptions);
385
423
  const selectSlotProps = resolved.muiSlotProps?.select;
386
424
  const menuProps = { ...selectSlotProps?.MenuProps, ...resolved.muiSlotProps?.selectMenu };
425
+ const slotInputProps = selectSlotProps?.inputProps;
387
426
  const labelId = id === void 0 || label === void 0 ? void 0 : `${id}-label`;
388
427
  const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
389
428
  const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
390
- const handleChange = (event) => onChange(String(event.target.value));
429
+ const handleChange = (event) => onChange(event.target.value);
391
430
  return /* @__PURE__ */ jsxs4(
392
431
  FormControl2,
393
432
  {
@@ -427,15 +466,27 @@ function createMuiSelectAdapter(options) {
427
466
  ] });
428
467
  },
429
468
  inputProps: {
430
- ...selectSlotProps?.inputProps,
431
- "aria-label": ariaLabel,
432
- "aria-describedby": describedBy,
433
- "aria-labelledby": ariaLabelledBy
469
+ ...slotInputProps,
470
+ ...ariaLabel === void 0 ? {} : { "aria-label": ariaLabel },
471
+ ...describedBy === void 0 ? {} : { "aria-describedby": describedBy },
472
+ ...ariaLabelledBy === void 0 ? {} : { "aria-labelledby": ariaLabelledBy }
434
473
  },
435
- children: normalizedOptions.map((option) => /* @__PURE__ */ jsx7(MenuItem, { value: option.value, disabled: option.disabled, children: renderOption === void 0 ? /* @__PURE__ */ jsxs4(Fragment, { children: [
436
- option.icon === void 0 ? null : /* @__PURE__ */ jsx7(ListItemIcon, { sx: { minWidth: 32 }, children: option.icon }),
437
- /* @__PURE__ */ jsx7(ListItemText, { primary: option.label, secondary: option.description })
438
- ] }) : renderOption(option) }, option.value))
474
+ children: groupedOptions.flatMap((group) => [
475
+ group.groupLabel === void 0 ? null : /* @__PURE__ */ jsx7(SelectGroupHeader, { children: group.groupLabel }, `header-${group.groupKey ?? group.groupLabel}`),
476
+ ...group.items.map((option) => /* @__PURE__ */ jsx7(
477
+ MenuItem,
478
+ {
479
+ value: option.value,
480
+ disabled: option.disabled,
481
+ ...option.group === void 0 && option.groupLabel === void 0 && option.kind === void 0 ? {} : { "aria-label": option.label },
482
+ children: renderOption === void 0 ? /* @__PURE__ */ jsxs4(Fragment, { children: [
483
+ option.icon === void 0 ? null : /* @__PURE__ */ jsx7(ListItemIcon, { sx: { minWidth: 32 }, children: option.icon }),
484
+ /* @__PURE__ */ jsx7(ListItemText, { primary: option.label, secondary: option.description })
485
+ ] }) : renderOption(option)
486
+ },
487
+ option.value
488
+ ))
489
+ ])
439
490
  }
440
491
  ),
441
492
  helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx7(FormHelperText2, { id: helperId, children: helperText })
@@ -444,7 +495,10 @@ function createMuiSelectAdapter(options) {
444
495
  );
445
496
  };
446
497
  }
447
- var MuiSelectAdapter = createMuiSelectAdapter();
498
+ function MuiSelectAdapter(props) {
499
+ const Adapter = createMuiSelectAdapter();
500
+ return /* @__PURE__ */ jsx7(Adapter, { ...props });
501
+ }
448
502
 
449
503
  // src/adapters/TextArea.tsx
450
504
  import { TextField } from "@mui/material";
@@ -881,6 +935,24 @@ function createMuiFieldEditorSlot(options) {
881
935
  const titleErrorId = field.title.trim().length === 0 ? `mui-field-${field.id}-title-error` : void 0;
882
936
  const FieldTypeSelect = slots?.fieldTypeSelect;
883
937
  const FieldEditorHeader = slots?.fieldEditorHeader;
938
+ const fieldTypeSelectId = `mui-field-${field.id}-type`;
939
+ const fieldTypeSelectLabel = translate("builder.type");
940
+ const fieldTypeOptions = FIELD_TYPES.filter(
941
+ (type) => allowedTypes.includes(type)
942
+ ).map((type) => {
943
+ const definition = DEFAULT_FIELD_TYPE_DEFINITIONS.find((candidate) => candidate.type === type);
944
+ const group = definition?.category;
945
+ return {
946
+ value: type,
947
+ label: translate(definition?.labelKey ?? `builder.fieldType.${type}`),
948
+ description: translate(`builder.fieldTypeDescription.${type}`),
949
+ icon: components.renderFieldTypeIcon(type),
950
+ ...group === void 0 ? {} : {
951
+ group,
952
+ groupLabel: translate(`builder.fieldCategory.${group}`)
953
+ }
954
+ };
955
+ });
884
956
  const changeFieldType = (nextType) => {
885
957
  if (allowedTypes.includes(nextType)) actions.changeFieldType(field.id, nextType);
886
958
  };
@@ -933,19 +1005,11 @@ function createMuiFieldEditorSlot(options) {
933
1005
  FieldTypeSelect === void 0 ? /* @__PURE__ */ jsx13(
934
1006
  Select2,
935
1007
  {
936
- id: `mui-field-${field.id}-type`,
1008
+ id: fieldTypeSelectId,
937
1009
  name: `fields.${field.id}.type`,
938
- label: translate("builder.type"),
1010
+ label: fieldTypeSelectLabel,
939
1011
  value: allowedTypes.includes(field.type) ? field.type : "",
940
- options: FIELD_TYPES.filter((type) => allowedTypes.includes(type)).map((type) => {
941
- const definition = DEFAULT_FIELD_TYPE_DEFINITIONS.find((candidate) => candidate.type === type);
942
- return {
943
- value: type,
944
- label: translate(definition?.labelKey ?? `builder.fieldType.${type}`),
945
- icon: components.renderFieldTypeIcon(type),
946
- ...definition?.category === void 0 ? {} : { kind: definition.category }
947
- };
948
- }),
1012
+ options: fieldTypeOptions,
949
1013
  disabled: readOnly,
950
1014
  onChange: (value) => {
951
1015
  if (isFieldType(value)) changeFieldType(value);
@@ -954,11 +1018,16 @@ function createMuiFieldEditorSlot(options) {
954
1018
  ) : /* @__PURE__ */ jsx13(
955
1019
  FieldTypeSelect,
956
1020
  {
1021
+ id: fieldTypeSelectId,
1022
+ name: `fields.${field.id}.type`,
1023
+ label: fieldTypeSelectLabel,
957
1024
  currentType: field.type,
958
1025
  allowedTypes,
1026
+ options: fieldTypeOptions,
959
1027
  onChangeType: changeFieldType,
960
1028
  disabled: readOnly,
961
1029
  readOnly,
1030
+ "aria-labelledby": `${fieldTypeSelectId}-label`,
962
1031
  renderIcon: components.renderFieldTypeIcon
963
1032
  }
964
1033
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@form-engine-ts/mui",
3
- "version": "3.1.0",
3
+ "version": "4.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": "3.1.0",
50
- "@form-engine-ts/react": "3.1.0"
49
+ "@form-engine-ts/core": "4.0.0",
50
+ "@form-engine-ts/react": "4.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/react": "3.1.0",
62
- "@form-engine-ts/core": "3.1.0"
61
+ "@form-engine-ts/react": "4.0.0",
62
+ "@form-engine-ts/core": "4.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",