@form-engine-ts/mui 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,7 +36,13 @@ import { MuiFormBuilder } from "@form-engine-ts/mui";
36
36
  emptyStateMessage: "No translation locales have been added yet.",
37
37
  defaultLocaleControl: "readOnly"
38
38
  }}
39
- muiSlotProps={{ card: { sx: { p: 2 } }, accordion: { elevation: 0 } }}
39
+ muiSlotProps={{
40
+ card: { sx: { p: 2 } },
41
+ accordion: { elevation: 0 },
42
+ textField: { "data-testid": "builder-input" },
43
+ select: { variant: "filled" },
44
+ selectMenu: { PaperProps: { elevation: 4 } }
45
+ }}
40
46
  />;
41
47
  ```
42
48
 
@@ -70,6 +76,12 @@ default.
70
76
  `layoutOptions`, `localizationOptions`, and `muiSlotProps` are also accepted in `muiOptions` for low-level factories such
71
77
  as `createMuiBuilderSlots`; the dedicated `MuiFormBuilder` props take precedence.
72
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
81
+ MUI field editor supplies icons for every field type and accepts `slots.fieldTypeSelect` and `slots.fieldEditorHeader`
82
+ for focused customization. `muiSlotProps` also supports `textField`, `select`, `selectMenu`, `checkbox`, `button`, and
83
+ `iconButton` MUI props in addition to the layout props.
84
+
73
85
  `MuiFormBuilder` supplies options through `MuiFormBuilderContext` to module-stable adapter and slot component types.
74
86
  Controlled schema updates therefore preserve input focus and uncontrolled MUI state such as an open localization
75
87
  accordion even when the parent passes inline option objects.
package/dist/index.cjs CHANGED
@@ -56,6 +56,7 @@ __export(index_exports, {
56
56
  mergeMuiAdapterOptions: () => mergeMuiAdapterOptions,
57
57
  muiBuilderComponents: () => muiBuilderComponents,
58
58
  muiBuilderSlots: () => muiBuilderSlots,
59
+ muiDefaultFieldTypeIcon: () => muiDefaultFieldTypeIcon,
59
60
  muiDefaultIconResolver: () => muiDefaultIconResolver,
60
61
  resolveMuiAdapterOptions: () => resolveMuiAdapterOptions,
61
62
  useResolvedMuiAdapterOptions: () => useResolvedMuiAdapterOptions
@@ -159,9 +160,11 @@ function createMuiButtonAdapter(options) {
159
160
  targetId
160
161
  }) {
161
162
  const resolved = useResolvedMuiAdapterOptions(options);
163
+ const buttonSlotProps = resolved.muiSlotProps?.button;
162
164
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
163
165
  import_material.Button,
164
166
  {
167
+ ...buttonSlotProps,
165
168
  id,
166
169
  className,
167
170
  type: "button",
@@ -175,7 +178,7 @@ function createMuiButtonAdapter(options) {
175
178
  "data-target-id": targetId,
176
179
  onClick,
177
180
  color: variant === "danger" ? "error" : "primary",
178
- variant: resolved.buttonVariants?.[variant ?? "primary"] ?? resolved.buttonVariant,
181
+ variant: buttonSlotProps?.variant ?? resolved.buttonVariants?.[variant ?? "primary"] ?? resolved.buttonVariant,
179
182
  fullWidth: resolved.buttonFullWidth ?? false,
180
183
  sx: { whiteSpace: noWrap === false ? "normal" : "nowrap" },
181
184
  children
@@ -206,6 +209,7 @@ function createMuiCheckboxAdapter(options) {
206
209
  label
207
210
  }) {
208
211
  const resolved = useResolvedMuiAdapterOptions(options);
212
+ const checkboxSlotProps = resolved.muiSlotProps?.checkbox;
209
213
  const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
210
214
  const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
211
215
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_material2.FormControl, { className, error: Boolean(error), required, disabled: disabled || readOnly, children: [
@@ -216,9 +220,10 @@ function createMuiCheckboxAdapter(options) {
216
220
  control: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
217
221
  import_material2.Checkbox,
218
222
  {
223
+ ...checkboxSlotProps,
219
224
  id,
220
225
  name,
221
- size: resolved.size,
226
+ size: checkboxSlotProps?.size ?? resolved.size,
222
227
  checked,
223
228
  required,
224
229
  disabled: disabled || readOnly,
@@ -330,16 +335,18 @@ function createMuiIconButtonAdapter(options) {
330
335
  title
331
336
  }) {
332
337
  const resolved = useResolvedMuiAdapterOptions(options);
338
+ const iconButtonSlotProps = resolved.muiSlotProps?.iconButton;
333
339
  const actionLabel = actionType === void 0 ? "Action" : resolved.getActionLabel?.(actionType) ?? FALLBACK_ACTION_LABELS[actionType];
334
340
  const tooltip = title ?? actionLabel;
335
341
  const color = actionType === "delete" ? "error" : actionType === "add" ? "primary" : "default";
336
342
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_material5.Tooltip, { title: tooltip, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
337
343
  import_material5.IconButton,
338
344
  {
345
+ ...iconButtonSlotProps,
339
346
  id,
340
347
  className,
341
348
  type: "button",
342
- size: resolved.size,
349
+ size: iconButtonSlotProps?.size ?? resolved.size,
343
350
  color,
344
351
  disabled: disabled || readOnly,
345
352
  "aria-label": ariaLabel ?? tooltip,
@@ -405,6 +412,9 @@ var MuiSectionAdapter = createMuiSectionAdapter();
405
412
  // src/adapters/Select.tsx
406
413
  var import_material7 = require("@mui/material");
407
414
  var import_jsx_runtime7 = require("react/jsx-runtime");
415
+ function normalizeOptions(options) {
416
+ return options.map((option) => typeof option === "string" ? { value: option, label: option } : option);
417
+ }
408
418
  function createMuiSelectAdapter(options) {
409
419
  return function MuiSelect({
410
420
  id,
@@ -422,13 +432,18 @@ function createMuiSelectAdapter(options) {
422
432
  value,
423
433
  onChange,
424
434
  onKeyDown,
425
- options: selectOptions
435
+ options: selectOptions,
436
+ renderOption,
437
+ renderValue
426
438
  }) {
427
439
  const resolved = useResolvedMuiAdapterOptions(options);
440
+ const normalizedOptions = normalizeOptions(selectOptions);
441
+ const selectSlotProps = resolved.muiSlotProps?.select;
442
+ const menuProps = { ...selectSlotProps?.MenuProps, ...resolved.muiSlotProps?.selectMenu };
428
443
  const labelId = id === void 0 || label === void 0 ? void 0 : `${id}-label`;
429
444
  const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
430
445
  const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
431
- const handleChange = (event) => onChange(event.target.value);
446
+ const handleChange = (event) => onChange(String(event.target.value));
432
447
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
433
448
  import_material7.FormControl,
434
449
  {
@@ -444,6 +459,7 @@ function createMuiSelectAdapter(options) {
444
459
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
445
460
  import_material7.Select,
446
461
  {
462
+ ...selectSlotProps,
447
463
  id,
448
464
  labelId,
449
465
  name,
@@ -454,12 +470,28 @@ function createMuiSelectAdapter(options) {
454
470
  onKeyDown,
455
471
  required,
456
472
  readOnly,
473
+ variant: selectSlotProps?.variant ?? resolved.variant,
474
+ ...Object.keys(menuProps).length === 0 ? {} : { MenuProps: menuProps },
475
+ renderValue: (selected) => {
476
+ const selectedValue = typeof selected === "string" ? selected : String(selected ?? "");
477
+ const option = normalizedOptions.find((candidate) => candidate.value === selectedValue);
478
+ if (renderValue !== void 0) return renderValue(option);
479
+ if (option === void 0) return selectedValue;
480
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_material7.Box, { display: "flex", alignItems: "center", gap: 0.75, children: [
481
+ option.icon,
482
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: option.label })
483
+ ] });
484
+ },
457
485
  inputProps: {
486
+ ...selectSlotProps?.inputProps,
458
487
  "aria-label": ariaLabel,
459
488
  "aria-describedby": describedBy,
460
489
  "aria-labelledby": ariaLabelledBy
461
490
  },
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))
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))
463
495
  }
464
496
  ),
465
497
  helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.FormHelperText, { id: helperId, children: helperText })
@@ -494,9 +526,11 @@ function createMuiTextAreaAdapter(options) {
494
526
  rows
495
527
  }) {
496
528
  const resolved = useResolvedMuiAdapterOptions(options);
529
+ const textFieldSlotProps = resolved.muiSlotProps?.textField;
497
530
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
498
531
  import_material8.TextField,
499
532
  {
533
+ ...textFieldSlotProps,
500
534
  id,
501
535
  className,
502
536
  name,
@@ -522,7 +556,7 @@ function createMuiTextAreaAdapter(options) {
522
556
  placeholder,
523
557
  fullWidth: resolved.inputFullWidth ?? resolved.fullWidth,
524
558
  size: resolved.size,
525
- variant: resolved.variant,
559
+ variant: textFieldSlotProps?.variant ?? resolved.variant,
526
560
  onChange: (event) => onChange(event.currentTarget.value)
527
561
  }
528
562
  );
@@ -559,9 +593,11 @@ function createMuiTextInputAdapter(options) {
559
593
  step
560
594
  }) {
561
595
  const resolved = useResolvedMuiAdapterOptions(options);
596
+ const textFieldSlotProps = resolved.muiSlotProps?.textField;
562
597
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
563
598
  import_material9.TextField,
564
599
  {
600
+ ...textFieldSlotProps,
565
601
  id,
566
602
  className,
567
603
  name,
@@ -590,7 +626,7 @@ function createMuiTextInputAdapter(options) {
590
626
  placeholder,
591
627
  fullWidth: resolved.inputFullWidth ?? resolved.fullWidth,
592
628
  size: resolved.size,
593
- variant: resolved.variant,
629
+ variant: textFieldSlotProps?.variant ?? resolved.variant,
594
630
  onChange: (event) => onChange(event.currentTarget.value),
595
631
  onKeyDown
596
632
  }
@@ -624,6 +660,26 @@ function muiDefaultIconResolver(actionType) {
624
660
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.DragHandle, { fontSize: "small" });
625
661
  }
626
662
  }
663
+ function muiDefaultFieldTypeIcon(type) {
664
+ switch (type) {
665
+ case "text":
666
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.TextFields, { fontSize: "small" });
667
+ case "textarea":
668
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Subject, { fontSize: "small" });
669
+ case "number":
670
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Numbers, { fontSize: "small" });
671
+ case "rating":
672
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.Star, { fontSize: "small" });
673
+ case "select":
674
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.ArrowDropDown, { fontSize: "small" });
675
+ case "multi-select":
676
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.CheckBox, { fontSize: "small" });
677
+ case "checkbox":
678
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.CheckBox, { fontSize: "small" });
679
+ case "radio":
680
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons_material.RadioButtonChecked, { fontSize: "small" });
681
+ }
682
+ }
627
683
 
628
684
  // src/components.ts
629
685
  function isAdapterOptions(value) {
@@ -640,7 +696,8 @@ function buildMuiBuilderComponents(options) {
640
696
  Section: createMuiSectionAdapter(options),
641
697
  Fieldset: createMuiFieldsetAdapter(options),
642
698
  ErrorMessage: createMuiErrorMessageAdapter(options),
643
- renderIcon: muiDefaultIconResolver
699
+ renderIcon: muiDefaultIconResolver,
700
+ renderFieldTypeIcon: muiDefaultFieldTypeIcon
644
701
  };
645
702
  }
646
703
  var muiBuilderComponents = {
@@ -653,7 +710,8 @@ var muiBuilderComponents = {
653
710
  Section: MuiSectionAdapter,
654
711
  Fieldset: MuiFieldsetAdapter,
655
712
  ErrorMessage: MuiErrorMessageAdapter,
656
- renderIcon: muiDefaultIconResolver
713
+ renderIcon: muiDefaultIconResolver,
714
+ renderFieldTypeIcon: muiDefaultFieldTypeIcon
657
715
  };
658
716
  function createMuiBuilderComponents(optionsOrOverrides = {}, customOverrides) {
659
717
  const options = isAdapterOptions(optionsOrOverrides) ? optionsOrOverrides : void 0;
@@ -662,6 +720,7 @@ function createMuiBuilderComponents(optionsOrOverrides = {}, customOverrides) {
662
720
  }
663
721
 
664
722
  // src/slots/FieldEditor.tsx
723
+ var import_core = require("@form-engine-ts/core");
665
724
  var import_material12 = require("@mui/material");
666
725
 
667
726
  // src/slots/OptionEditor.tsx
@@ -808,16 +867,7 @@ var MuiToolbarSlot = createMuiToolbarSlot();
808
867
 
809
868
  // src/slots/FieldEditor.tsx
810
869
  var import_jsx_runtime13 = require("react/jsx-runtime");
811
- var FIELD_TYPES = [
812
- "text",
813
- "textarea",
814
- "number",
815
- "rating",
816
- "select",
817
- "multi-select",
818
- "checkbox",
819
- "radio"
820
- ];
870
+ var FIELD_TYPES = import_core.DEFAULT_FIELD_TYPE_DEFINITIONS.map((definition) => definition.type);
821
871
  function isFieldType(value) {
822
872
  return FIELD_TYPES.some((type) => type === value);
823
873
  }
@@ -856,7 +906,8 @@ function createMuiFieldEditorSlot(options) {
856
906
  readOnly,
857
907
  actions,
858
908
  components,
859
- translate
909
+ translate,
910
+ slots
860
911
  }) {
861
912
  const resolved = useResolvedMuiAdapterOptions(options);
862
913
  const { Button: Button2, Checkbox: Checkbox2, Select: Select2, TextArea, TextInput } = components;
@@ -867,6 +918,11 @@ function createMuiFieldEditorSlot(options) {
867
918
  const conditionSource = conditionSources.find((source) => source.id === condition?.questionId);
868
919
  const descriptionMode = resolved.fieldEditorOptions?.description ?? "editable";
869
920
  const titleErrorId = field.title.trim().length === 0 ? `mui-field-${field.id}-title-error` : void 0;
921
+ const FieldTypeSelect = slots?.fieldTypeSelect;
922
+ const FieldEditorHeader = slots?.fieldEditorHeader;
923
+ const changeFieldType = (nextType) => {
924
+ if (allowedTypes.includes(nextType)) actions.changeFieldType(field.id, nextType);
925
+ };
870
926
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
871
927
  import_material12.Card,
872
928
  {
@@ -875,26 +931,28 @@ function createMuiFieldEditorSlot(options) {
875
931
  variant: "outlined",
876
932
  sx: resolved.muiSlotProps?.card?.sx ?? { mb: resolved.dense ? 1 : 2, p: resolved.dense ? 1.5 : 2 },
877
933
  children: [
878
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
879
- Toolbar,
880
- {
881
- schema,
882
- translate,
883
- kind: "field",
884
- targetId: field.id,
885
- index,
886
- total: schema.fields.length,
887
- title: field.title,
888
- onMoveUp: () => actions.moveField(field.id, index - 1),
889
- onMoveDown: () => actions.moveField(field.id, index + 1),
890
- onRemove: () => actions.removeField(field.id),
891
- readOnly,
892
- actions,
893
- components
894
- }
895
- ),
934
+ FieldEditorHeader === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
935
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
936
+ Toolbar,
937
+ {
938
+ schema,
939
+ translate,
940
+ kind: "field",
941
+ targetId: field.id,
942
+ index,
943
+ total: schema.fields.length,
944
+ title: field.title,
945
+ onMoveUp: () => actions.moveField(field.id, index - 1),
946
+ onMoveDown: () => actions.moveField(field.id, index + 1),
947
+ onRemove: () => actions.removeField(field.id),
948
+ readOnly,
949
+ actions,
950
+ components
951
+ }
952
+ ),
953
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material12.Typography, { variant: "subtitle1", fontWeight: "bold", children: field.title })
954
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FieldEditorHeader, { field, index, totalFields: schema.fields.length, actions }),
896
955
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { ...resolved.muiSlotProps?.stack, spacing: resolved.dense ? 1 : 2, children: [
897
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material12.Typography, { variant: "subtitle1", fontWeight: "bold", children: field.title }),
898
956
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { direction: { xs: "column", md: "row" }, spacing: resolved.dense ? 1 : 2, children: [
899
957
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
900
958
  TextInput,
@@ -911,22 +969,37 @@ function createMuiFieldEditorSlot(options) {
911
969
  onChange: (value) => actions.setSourceText({ kind: "field", id: field.id }, "title", value)
912
970
  }
913
971
  ),
914
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
972
+ FieldTypeSelect === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
915
973
  Select2,
916
974
  {
917
975
  id: `mui-field-${field.id}-type`,
918
976
  name: `fields.${field.id}.type`,
919
977
  label: translate("builder.type"),
920
978
  value: allowedTypes.includes(field.type) ? field.type : "",
921
- options: FIELD_TYPES.filter((type) => allowedTypes.includes(type)).map((type) => ({
922
- value: type,
923
- label: translate(`builder.fieldType.${type}`)
924
- })),
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
+ }),
925
988
  disabled: readOnly,
926
989
  onChange: (value) => {
927
- if (isFieldType(value)) actions.changeFieldType(field.id, value);
990
+ if (isFieldType(value)) changeFieldType(value);
928
991
  }
929
992
  }
993
+ ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
994
+ FieldTypeSelect,
995
+ {
996
+ currentType: field.type,
997
+ allowedTypes,
998
+ onChangeType: changeFieldType,
999
+ disabled: readOnly,
1000
+ readOnly,
1001
+ renderIcon: components.renderFieldTypeIcon
1002
+ }
930
1003
  )
931
1004
  ] }),
932
1005
  descriptionMode === "hidden" ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
@@ -1493,6 +1566,7 @@ function MuiFormBuilder({
1493
1566
  mergeMuiAdapterOptions,
1494
1567
  muiBuilderComponents,
1495
1568
  muiBuilderSlots,
1569
+ muiDefaultFieldTypeIcon,
1496
1570
  muiDefaultIconResolver,
1497
1571
  resolveMuiAdapterOptions,
1498
1572
  useResolvedMuiAdapterOptions
package/dist/index.d.cts CHANGED
@@ -1,10 +1,13 @@
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';
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
3
  import { ReactNode, ComponentType } from 'react';
4
- import { CardProps, PaperProps, StackProps, AccordionProps } from '@mui/material';
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";
7
7
  type MuiButtonVariant = "contained" | "outlined" | "text";
8
+ type MuiComponentSlotProps<T> = Partial<T> & {
9
+ readonly [key: `data-${string}`]: string | number | boolean | undefined;
10
+ };
8
11
  interface LocaleOptionItem {
9
12
  readonly value: string;
10
13
  readonly label: string;
@@ -28,11 +31,29 @@ interface MuiLocalizationOptions {
28
31
  interface MuiFieldEditorOptions {
29
32
  readonly description?: "editable" | "readOnly" | "hidden";
30
33
  }
31
- interface MuiBuilderSlotProps {
34
+ interface MuiSlotProps {
32
35
  readonly card?: Partial<CardProps>;
33
36
  readonly paper?: Partial<PaperProps>;
37
+ readonly accordion?: Partial<AccordionProps>;
34
38
  readonly stack?: Partial<StackProps>;
39
+ readonly textField?: MuiComponentSlotProps<TextFieldProps>;
40
+ readonly select?: MuiComponentSlotProps<SelectProps>;
41
+ readonly selectMenu?: Partial<MenuProps>;
42
+ readonly checkbox?: MuiComponentSlotProps<CheckboxProps>;
43
+ readonly button?: MuiComponentSlotProps<ButtonProps>;
44
+ readonly iconButton?: MuiComponentSlotProps<IconButtonProps>;
45
+ }
46
+ interface MuiBuilderSlotProps {
47
+ readonly card?: Partial<CardProps>;
48
+ readonly paper?: Partial<PaperProps>;
35
49
  readonly accordion?: Partial<AccordionProps>;
50
+ readonly stack?: Partial<StackProps>;
51
+ readonly textField?: MuiComponentSlotProps<TextFieldProps>;
52
+ readonly select?: MuiComponentSlotProps<SelectProps>;
53
+ readonly selectMenu?: Partial<MenuProps>;
54
+ readonly checkbox?: MuiComponentSlotProps<CheckboxProps>;
55
+ readonly button?: MuiComponentSlotProps<ButtonProps>;
56
+ readonly iconButton?: MuiComponentSlotProps<IconButtonProps>;
36
57
  }
37
58
  interface MuiAdapterOptions {
38
59
  readonly size?: "small" | "medium";
@@ -123,6 +144,7 @@ declare function mergeMuiAdapterOptions(base?: MuiAdapterOptions, overrides?: Mu
123
144
  declare function useResolvedMuiAdapterOptions(overrides?: MuiAdapterOptions): ResolvedMuiAdapterOptions;
124
145
 
125
146
  declare function muiDefaultIconResolver(actionType: BuilderActionIconType): ReactNode;
147
+ declare function muiDefaultFieldTypeIcon(type: QuestionType): ReactNode;
126
148
 
127
149
  interface MuiFormBuilderProps extends Omit<FormBuilderProps, "components" | "disableDefaultStyles" | "slots" | "unstyled"> {
128
150
  readonly muiOptions?: MuiAdapterOptions;
@@ -149,4 +171,4 @@ declare const MuiToolbarSlot: NonNullable<FormBuilderSlots["toolbar"]>;
149
171
  declare const muiBuilderSlots: FormBuilderSlots;
150
172
  declare function createMuiBuilderSlots(options?: MuiAdapterOptions, customOverrides?: Partial<FormBuilderSlots>): FormBuilderSlots;
151
173
 
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 };
174
+ 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, type MuiSlotProps, MuiTextAreaAdapter, MuiTextInputAdapter, MuiToolbarSlot, type ResolvedMuiAdapterOptions, createMuiBuilderComponents, createMuiBuilderProps, createMuiBuilderSlots, createMuiButtonAdapter, createMuiCheckboxAdapter, createMuiErrorMessageAdapter, createMuiFieldEditorSlot, createMuiFieldsetAdapter, createMuiIconButtonAdapter, createMuiLocalizationSlot, createMuiOptionEditorSlot, createMuiSectionAdapter, createMuiSelectAdapter, createMuiTextAreaAdapter, createMuiTextInputAdapter, createMuiToolbarSlot, mergeMuiAdapterOptions, muiBuilderComponents, muiBuilderSlots, muiDefaultFieldTypeIcon, muiDefaultIconResolver, resolveMuiAdapterOptions, useResolvedMuiAdapterOptions };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,13 @@
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';
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
3
  import { ReactNode, ComponentType } from 'react';
4
- import { CardProps, PaperProps, StackProps, AccordionProps } from '@mui/material';
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";
7
7
  type MuiButtonVariant = "contained" | "outlined" | "text";
8
+ type MuiComponentSlotProps<T> = Partial<T> & {
9
+ readonly [key: `data-${string}`]: string | number | boolean | undefined;
10
+ };
8
11
  interface LocaleOptionItem {
9
12
  readonly value: string;
10
13
  readonly label: string;
@@ -28,11 +31,29 @@ interface MuiLocalizationOptions {
28
31
  interface MuiFieldEditorOptions {
29
32
  readonly description?: "editable" | "readOnly" | "hidden";
30
33
  }
31
- interface MuiBuilderSlotProps {
34
+ interface MuiSlotProps {
32
35
  readonly card?: Partial<CardProps>;
33
36
  readonly paper?: Partial<PaperProps>;
37
+ readonly accordion?: Partial<AccordionProps>;
34
38
  readonly stack?: Partial<StackProps>;
39
+ readonly textField?: MuiComponentSlotProps<TextFieldProps>;
40
+ readonly select?: MuiComponentSlotProps<SelectProps>;
41
+ readonly selectMenu?: Partial<MenuProps>;
42
+ readonly checkbox?: MuiComponentSlotProps<CheckboxProps>;
43
+ readonly button?: MuiComponentSlotProps<ButtonProps>;
44
+ readonly iconButton?: MuiComponentSlotProps<IconButtonProps>;
45
+ }
46
+ interface MuiBuilderSlotProps {
47
+ readonly card?: Partial<CardProps>;
48
+ readonly paper?: Partial<PaperProps>;
35
49
  readonly accordion?: Partial<AccordionProps>;
50
+ readonly stack?: Partial<StackProps>;
51
+ readonly textField?: MuiComponentSlotProps<TextFieldProps>;
52
+ readonly select?: MuiComponentSlotProps<SelectProps>;
53
+ readonly selectMenu?: Partial<MenuProps>;
54
+ readonly checkbox?: MuiComponentSlotProps<CheckboxProps>;
55
+ readonly button?: MuiComponentSlotProps<ButtonProps>;
56
+ readonly iconButton?: MuiComponentSlotProps<IconButtonProps>;
36
57
  }
37
58
  interface MuiAdapterOptions {
38
59
  readonly size?: "small" | "medium";
@@ -123,6 +144,7 @@ declare function mergeMuiAdapterOptions(base?: MuiAdapterOptions, overrides?: Mu
123
144
  declare function useResolvedMuiAdapterOptions(overrides?: MuiAdapterOptions): ResolvedMuiAdapterOptions;
124
145
 
125
146
  declare function muiDefaultIconResolver(actionType: BuilderActionIconType): ReactNode;
147
+ declare function muiDefaultFieldTypeIcon(type: QuestionType): ReactNode;
126
148
 
127
149
  interface MuiFormBuilderProps extends Omit<FormBuilderProps, "components" | "disableDefaultStyles" | "slots" | "unstyled"> {
128
150
  readonly muiOptions?: MuiAdapterOptions;
@@ -149,4 +171,4 @@ declare const MuiToolbarSlot: NonNullable<FormBuilderSlots["toolbar"]>;
149
171
  declare const muiBuilderSlots: FormBuilderSlots;
150
172
  declare function createMuiBuilderSlots(options?: MuiAdapterOptions, customOverrides?: Partial<FormBuilderSlots>): FormBuilderSlots;
151
173
 
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 };
174
+ 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, type MuiSlotProps, MuiTextAreaAdapter, MuiTextInputAdapter, MuiToolbarSlot, type ResolvedMuiAdapterOptions, createMuiBuilderComponents, createMuiBuilderProps, createMuiBuilderSlots, createMuiButtonAdapter, createMuiCheckboxAdapter, createMuiErrorMessageAdapter, createMuiFieldEditorSlot, createMuiFieldsetAdapter, createMuiIconButtonAdapter, createMuiLocalizationSlot, createMuiOptionEditorSlot, createMuiSectionAdapter, createMuiSelectAdapter, createMuiTextAreaAdapter, createMuiTextInputAdapter, createMuiToolbarSlot, mergeMuiAdapterOptions, muiBuilderComponents, muiBuilderSlots, muiDefaultFieldTypeIcon, muiDefaultIconResolver, resolveMuiAdapterOptions, useResolvedMuiAdapterOptions };
package/dist/index.js CHANGED
@@ -95,9 +95,11 @@ function createMuiButtonAdapter(options) {
95
95
  targetId
96
96
  }) {
97
97
  const resolved = useResolvedMuiAdapterOptions(options);
98
+ const buttonSlotProps = resolved.muiSlotProps?.button;
98
99
  return /* @__PURE__ */ jsx(
99
100
  Button,
100
101
  {
102
+ ...buttonSlotProps,
101
103
  id,
102
104
  className,
103
105
  type: "button",
@@ -111,7 +113,7 @@ function createMuiButtonAdapter(options) {
111
113
  "data-target-id": targetId,
112
114
  onClick,
113
115
  color: variant === "danger" ? "error" : "primary",
114
- variant: resolved.buttonVariants?.[variant ?? "primary"] ?? resolved.buttonVariant,
116
+ variant: buttonSlotProps?.variant ?? resolved.buttonVariants?.[variant ?? "primary"] ?? resolved.buttonVariant,
115
117
  fullWidth: resolved.buttonFullWidth ?? false,
116
118
  sx: { whiteSpace: noWrap === false ? "normal" : "nowrap" },
117
119
  children
@@ -142,6 +144,7 @@ function createMuiCheckboxAdapter(options) {
142
144
  label
143
145
  }) {
144
146
  const resolved = useResolvedMuiAdapterOptions(options);
147
+ const checkboxSlotProps = resolved.muiSlotProps?.checkbox;
145
148
  const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
146
149
  const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
147
150
  return /* @__PURE__ */ jsxs(FormControl, { className, error: Boolean(error), required, disabled: disabled || readOnly, children: [
@@ -152,9 +155,10 @@ function createMuiCheckboxAdapter(options) {
152
155
  control: /* @__PURE__ */ jsx2(
153
156
  Checkbox,
154
157
  {
158
+ ...checkboxSlotProps,
155
159
  id,
156
160
  name,
157
- size: resolved.size,
161
+ size: checkboxSlotProps?.size ?? resolved.size,
158
162
  checked,
159
163
  required,
160
164
  disabled: disabled || readOnly,
@@ -266,16 +270,18 @@ function createMuiIconButtonAdapter(options) {
266
270
  title
267
271
  }) {
268
272
  const resolved = useResolvedMuiAdapterOptions(options);
273
+ const iconButtonSlotProps = resolved.muiSlotProps?.iconButton;
269
274
  const actionLabel = actionType === void 0 ? "Action" : resolved.getActionLabel?.(actionType) ?? FALLBACK_ACTION_LABELS[actionType];
270
275
  const tooltip = title ?? actionLabel;
271
276
  const color = actionType === "delete" ? "error" : actionType === "add" ? "primary" : "default";
272
277
  return /* @__PURE__ */ jsx5(Tooltip, { title: tooltip, children: /* @__PURE__ */ jsx5("span", { children: /* @__PURE__ */ jsx5(
273
278
  IconButton,
274
279
  {
280
+ ...iconButtonSlotProps,
275
281
  id,
276
282
  className,
277
283
  type: "button",
278
- size: resolved.size,
284
+ size: iconButtonSlotProps?.size ?? resolved.size,
279
285
  color,
280
286
  disabled: disabled || readOnly,
281
287
  "aria-label": ariaLabel ?? tooltip,
@@ -339,8 +345,20 @@ function createMuiSectionAdapter(options) {
339
345
  var MuiSectionAdapter = createMuiSectionAdapter();
340
346
 
341
347
  // src/adapters/Select.tsx
342
- import { FormControl as FormControl2, FormHelperText as FormHelperText2, InputLabel, MenuItem, Select } from "@mui/material";
343
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
348
+ import {
349
+ Box as Box2,
350
+ FormControl as FormControl2,
351
+ FormHelperText as FormHelperText2,
352
+ InputLabel,
353
+ ListItemIcon,
354
+ ListItemText,
355
+ MenuItem,
356
+ Select
357
+ } from "@mui/material";
358
+ import { Fragment, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
359
+ function normalizeOptions(options) {
360
+ return options.map((option) => typeof option === "string" ? { value: option, label: option } : option);
361
+ }
344
362
  function createMuiSelectAdapter(options) {
345
363
  return function MuiSelect({
346
364
  id,
@@ -358,13 +376,18 @@ function createMuiSelectAdapter(options) {
358
376
  value,
359
377
  onChange,
360
378
  onKeyDown,
361
- options: selectOptions
379
+ options: selectOptions,
380
+ renderOption,
381
+ renderValue
362
382
  }) {
363
383
  const resolved = useResolvedMuiAdapterOptions(options);
384
+ const normalizedOptions = normalizeOptions(selectOptions);
385
+ const selectSlotProps = resolved.muiSlotProps?.select;
386
+ const menuProps = { ...selectSlotProps?.MenuProps, ...resolved.muiSlotProps?.selectMenu };
364
387
  const labelId = id === void 0 || label === void 0 ? void 0 : `${id}-label`;
365
388
  const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
366
389
  const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
367
- const handleChange = (event) => onChange(event.target.value);
390
+ const handleChange = (event) => onChange(String(event.target.value));
368
391
  return /* @__PURE__ */ jsxs4(
369
392
  FormControl2,
370
393
  {
@@ -380,6 +403,7 @@ function createMuiSelectAdapter(options) {
380
403
  /* @__PURE__ */ jsx7(
381
404
  Select,
382
405
  {
406
+ ...selectSlotProps,
383
407
  id,
384
408
  labelId,
385
409
  name,
@@ -390,12 +414,28 @@ function createMuiSelectAdapter(options) {
390
414
  onKeyDown,
391
415
  required,
392
416
  readOnly,
417
+ variant: selectSlotProps?.variant ?? resolved.variant,
418
+ ...Object.keys(menuProps).length === 0 ? {} : { MenuProps: menuProps },
419
+ renderValue: (selected) => {
420
+ const selectedValue = typeof selected === "string" ? selected : String(selected ?? "");
421
+ const option = normalizedOptions.find((candidate) => candidate.value === selectedValue);
422
+ if (renderValue !== void 0) return renderValue(option);
423
+ if (option === void 0) return selectedValue;
424
+ return /* @__PURE__ */ jsxs4(Box2, { display: "flex", alignItems: "center", gap: 0.75, children: [
425
+ option.icon,
426
+ /* @__PURE__ */ jsx7("span", { children: option.label })
427
+ ] });
428
+ },
393
429
  inputProps: {
430
+ ...selectSlotProps?.inputProps,
394
431
  "aria-label": ariaLabel,
395
432
  "aria-describedby": describedBy,
396
433
  "aria-labelledby": ariaLabelledBy
397
434
  },
398
- children: selectOptions.map((option) => /* @__PURE__ */ jsx7(MenuItem, { value: option.value, disabled: option.disabled, children: option.label }, option.value))
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))
399
439
  }
400
440
  ),
401
441
  helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx7(FormHelperText2, { id: helperId, children: helperText })
@@ -430,9 +470,11 @@ function createMuiTextAreaAdapter(options) {
430
470
  rows
431
471
  }) {
432
472
  const resolved = useResolvedMuiAdapterOptions(options);
473
+ const textFieldSlotProps = resolved.muiSlotProps?.textField;
433
474
  return /* @__PURE__ */ jsx8(
434
475
  TextField,
435
476
  {
477
+ ...textFieldSlotProps,
436
478
  id,
437
479
  className,
438
480
  name,
@@ -458,7 +500,7 @@ function createMuiTextAreaAdapter(options) {
458
500
  placeholder,
459
501
  fullWidth: resolved.inputFullWidth ?? resolved.fullWidth,
460
502
  size: resolved.size,
461
- variant: resolved.variant,
503
+ variant: textFieldSlotProps?.variant ?? resolved.variant,
462
504
  onChange: (event) => onChange(event.currentTarget.value)
463
505
  }
464
506
  );
@@ -495,9 +537,11 @@ function createMuiTextInputAdapter(options) {
495
537
  step
496
538
  }) {
497
539
  const resolved = useResolvedMuiAdapterOptions(options);
540
+ const textFieldSlotProps = resolved.muiSlotProps?.textField;
498
541
  return /* @__PURE__ */ jsx9(
499
542
  TextField2,
500
543
  {
544
+ ...textFieldSlotProps,
501
545
  id,
502
546
  className,
503
547
  name,
@@ -526,7 +570,7 @@ function createMuiTextInputAdapter(options) {
526
570
  placeholder,
527
571
  fullWidth: resolved.inputFullWidth ?? resolved.fullWidth,
528
572
  size: resolved.size,
529
- variant: resolved.variant,
573
+ variant: textFieldSlotProps?.variant ?? resolved.variant,
530
574
  onChange: (event) => onChange(event.currentTarget.value),
531
575
  onKeyDown
532
576
  }
@@ -539,12 +583,19 @@ var MuiTextInputAdapter = createMuiTextInputAdapter();
539
583
  import {
540
584
  Add,
541
585
  ArrowDownward,
586
+ ArrowDropDown,
542
587
  ArrowUpward,
588
+ CheckBox,
543
589
  Close,
544
590
  Delete,
545
591
  DragHandle,
546
592
  Edit,
593
+ Numbers,
594
+ RadioButtonChecked,
547
595
  Settings,
596
+ Star,
597
+ Subject,
598
+ TextFields,
548
599
  Translate
549
600
  } from "@mui/icons-material";
550
601
  import { jsx as jsx10 } from "react/jsx-runtime";
@@ -570,6 +621,26 @@ function muiDefaultIconResolver(actionType) {
570
621
  return /* @__PURE__ */ jsx10(DragHandle, { fontSize: "small" });
571
622
  }
572
623
  }
624
+ function muiDefaultFieldTypeIcon(type) {
625
+ switch (type) {
626
+ case "text":
627
+ return /* @__PURE__ */ jsx10(TextFields, { fontSize: "small" });
628
+ case "textarea":
629
+ return /* @__PURE__ */ jsx10(Subject, { fontSize: "small" });
630
+ case "number":
631
+ return /* @__PURE__ */ jsx10(Numbers, { fontSize: "small" });
632
+ case "rating":
633
+ return /* @__PURE__ */ jsx10(Star, { fontSize: "small" });
634
+ case "select":
635
+ return /* @__PURE__ */ jsx10(ArrowDropDown, { fontSize: "small" });
636
+ case "multi-select":
637
+ return /* @__PURE__ */ jsx10(CheckBox, { fontSize: "small" });
638
+ case "checkbox":
639
+ return /* @__PURE__ */ jsx10(CheckBox, { fontSize: "small" });
640
+ case "radio":
641
+ return /* @__PURE__ */ jsx10(RadioButtonChecked, { fontSize: "small" });
642
+ }
643
+ }
573
644
 
574
645
  // src/components.ts
575
646
  function isAdapterOptions(value) {
@@ -586,7 +657,8 @@ function buildMuiBuilderComponents(options) {
586
657
  Section: createMuiSectionAdapter(options),
587
658
  Fieldset: createMuiFieldsetAdapter(options),
588
659
  ErrorMessage: createMuiErrorMessageAdapter(options),
589
- renderIcon: muiDefaultIconResolver
660
+ renderIcon: muiDefaultIconResolver,
661
+ renderFieldTypeIcon: muiDefaultFieldTypeIcon
590
662
  };
591
663
  }
592
664
  var muiBuilderComponents = {
@@ -599,7 +671,8 @@ var muiBuilderComponents = {
599
671
  Section: MuiSectionAdapter,
600
672
  Fieldset: MuiFieldsetAdapter,
601
673
  ErrorMessage: MuiErrorMessageAdapter,
602
- renderIcon: muiDefaultIconResolver
674
+ renderIcon: muiDefaultIconResolver,
675
+ renderFieldTypeIcon: muiDefaultFieldTypeIcon
603
676
  };
604
677
  function createMuiBuilderComponents(optionsOrOverrides = {}, customOverrides) {
605
678
  const options = isAdapterOptions(optionsOrOverrides) ? optionsOrOverrides : void 0;
@@ -608,6 +681,7 @@ function createMuiBuilderComponents(optionsOrOverrides = {}, customOverrides) {
608
681
  }
609
682
 
610
683
  // src/slots/FieldEditor.tsx
684
+ import { DEFAULT_FIELD_TYPE_DEFINITIONS } from "@form-engine-ts/core";
611
685
  import { Card, Stack as Stack3, Typography as Typography3 } from "@mui/material";
612
686
 
613
687
  // src/slots/OptionEditor.tsx
@@ -753,17 +827,8 @@ function createMuiToolbarSlot(options) {
753
827
  var MuiToolbarSlot = createMuiToolbarSlot();
754
828
 
755
829
  // src/slots/FieldEditor.tsx
756
- import { Fragment, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
757
- var FIELD_TYPES = [
758
- "text",
759
- "textarea",
760
- "number",
761
- "rating",
762
- "select",
763
- "multi-select",
764
- "checkbox",
765
- "radio"
766
- ];
830
+ import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
831
+ var FIELD_TYPES = DEFAULT_FIELD_TYPE_DEFINITIONS.map((definition) => definition.type);
767
832
  function isFieldType(value) {
768
833
  return FIELD_TYPES.some((type) => type === value);
769
834
  }
@@ -802,7 +867,8 @@ function createMuiFieldEditorSlot(options) {
802
867
  readOnly,
803
868
  actions,
804
869
  components,
805
- translate
870
+ translate,
871
+ slots
806
872
  }) {
807
873
  const resolved = useResolvedMuiAdapterOptions(options);
808
874
  const { Button: Button2, Checkbox: Checkbox2, Select: Select2, TextArea, TextInput } = components;
@@ -813,6 +879,11 @@ function createMuiFieldEditorSlot(options) {
813
879
  const conditionSource = conditionSources.find((source) => source.id === condition?.questionId);
814
880
  const descriptionMode = resolved.fieldEditorOptions?.description ?? "editable";
815
881
  const titleErrorId = field.title.trim().length === 0 ? `mui-field-${field.id}-title-error` : void 0;
882
+ const FieldTypeSelect = slots?.fieldTypeSelect;
883
+ const FieldEditorHeader = slots?.fieldEditorHeader;
884
+ const changeFieldType = (nextType) => {
885
+ if (allowedTypes.includes(nextType)) actions.changeFieldType(field.id, nextType);
886
+ };
816
887
  return /* @__PURE__ */ jsxs7(
817
888
  Card,
818
889
  {
@@ -821,26 +892,28 @@ function createMuiFieldEditorSlot(options) {
821
892
  variant: "outlined",
822
893
  sx: resolved.muiSlotProps?.card?.sx ?? { mb: resolved.dense ? 1 : 2, p: resolved.dense ? 1.5 : 2 },
823
894
  children: [
824
- /* @__PURE__ */ jsx13(
825
- Toolbar,
826
- {
827
- schema,
828
- translate,
829
- kind: "field",
830
- targetId: field.id,
831
- index,
832
- total: schema.fields.length,
833
- title: field.title,
834
- onMoveUp: () => actions.moveField(field.id, index - 1),
835
- onMoveDown: () => actions.moveField(field.id, index + 1),
836
- onRemove: () => actions.removeField(field.id),
837
- readOnly,
838
- actions,
839
- components
840
- }
841
- ),
895
+ FieldEditorHeader === void 0 ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
896
+ /* @__PURE__ */ jsx13(
897
+ Toolbar,
898
+ {
899
+ schema,
900
+ translate,
901
+ kind: "field",
902
+ targetId: field.id,
903
+ index,
904
+ total: schema.fields.length,
905
+ title: field.title,
906
+ onMoveUp: () => actions.moveField(field.id, index - 1),
907
+ onMoveDown: () => actions.moveField(field.id, index + 1),
908
+ onRemove: () => actions.removeField(field.id),
909
+ readOnly,
910
+ actions,
911
+ components
912
+ }
913
+ ),
914
+ /* @__PURE__ */ jsx13(Typography3, { variant: "subtitle1", fontWeight: "bold", children: field.title })
915
+ ] }) : /* @__PURE__ */ jsx13(FieldEditorHeader, { field, index, totalFields: schema.fields.length, actions }),
842
916
  /* @__PURE__ */ jsxs7(Stack3, { ...resolved.muiSlotProps?.stack, spacing: resolved.dense ? 1 : 2, children: [
843
- /* @__PURE__ */ jsx13(Typography3, { variant: "subtitle1", fontWeight: "bold", children: field.title }),
844
917
  /* @__PURE__ */ jsxs7(Stack3, { direction: { xs: "column", md: "row" }, spacing: resolved.dense ? 1 : 2, children: [
845
918
  /* @__PURE__ */ jsx13(
846
919
  TextInput,
@@ -857,22 +930,37 @@ function createMuiFieldEditorSlot(options) {
857
930
  onChange: (value) => actions.setSourceText({ kind: "field", id: field.id }, "title", value)
858
931
  }
859
932
  ),
860
- /* @__PURE__ */ jsx13(
933
+ FieldTypeSelect === void 0 ? /* @__PURE__ */ jsx13(
861
934
  Select2,
862
935
  {
863
936
  id: `mui-field-${field.id}-type`,
864
937
  name: `fields.${field.id}.type`,
865
938
  label: translate("builder.type"),
866
939
  value: allowedTypes.includes(field.type) ? field.type : "",
867
- options: FIELD_TYPES.filter((type) => allowedTypes.includes(type)).map((type) => ({
868
- value: type,
869
- label: translate(`builder.fieldType.${type}`)
870
- })),
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
+ }),
871
949
  disabled: readOnly,
872
950
  onChange: (value) => {
873
- if (isFieldType(value)) actions.changeFieldType(field.id, value);
951
+ if (isFieldType(value)) changeFieldType(value);
874
952
  }
875
953
  }
954
+ ) : /* @__PURE__ */ jsx13(
955
+ FieldTypeSelect,
956
+ {
957
+ currentType: field.type,
958
+ allowedTypes,
959
+ onChangeType: changeFieldType,
960
+ disabled: readOnly,
961
+ readOnly,
962
+ renderIcon: components.renderFieldTypeIcon
963
+ }
876
964
  )
877
965
  ] }),
878
966
  descriptionMode === "hidden" ? null : /* @__PURE__ */ jsx13(
@@ -955,7 +1043,7 @@ function createMuiFieldEditorSlot(options) {
955
1043
  )
956
1044
  }
957
1045
  ),
958
- condition === void 0 ? null : /* @__PURE__ */ jsxs7(Fragment, { children: [
1046
+ condition === void 0 ? null : /* @__PURE__ */ jsxs7(Fragment2, { children: [
959
1047
  /* @__PURE__ */ jsx13(
960
1048
  Select2,
961
1049
  {
@@ -1056,7 +1144,7 @@ import {
1056
1144
  AccordionDetails,
1057
1145
  AccordionSummary,
1058
1146
  Alert as Alert2,
1059
- Box as Box2,
1147
+ Box as Box3,
1060
1148
  Chip,
1061
1149
  Stack as Stack4,
1062
1150
  Tab,
@@ -1187,7 +1275,7 @@ function createMuiLocalizationSlot(options) {
1187
1275
  spacing: resolved.dense ? 1 : 2,
1188
1276
  sx: { mt: 1 },
1189
1277
  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: [
1278
+ defaultLocaleControl === "hidden" ? null : /* @__PURE__ */ jsx14(Box3, { sx: { flexGrow: 1, minWidth: 0, width: { xs: "100%", sm: "auto" } }, children: defaultLocaleControl === "readOnly" ? /* @__PURE__ */ jsxs8(Stack4, { spacing: 0.5, children: [
1191
1279
  /* @__PURE__ */ jsx14(Typography4, { variant: "caption", color: "text.secondary", children: translate("builder.defaultLocale") }),
1192
1280
  /* @__PURE__ */ jsx14(
1193
1281
  Chip,
@@ -1209,7 +1297,7 @@ function createMuiLocalizationSlot(options) {
1209
1297
  }
1210
1298
  }
1211
1299
  ) }),
1212
- /* @__PURE__ */ jsx14(Box2, { sx: { flexGrow: 1, minWidth: 0, width: { xs: "100%", sm: "auto" } }, onKeyDownCapture: handleAddKeyDown, children: hasLocaleSelector ? /* @__PURE__ */ jsx14(
1300
+ /* @__PURE__ */ jsx14(Box3, { sx: { flexGrow: 1, minWidth: 0, width: { xs: "100%", sm: "auto" } }, onKeyDownCapture: handleAddKeyDown, children: hasLocaleSelector ? /* @__PURE__ */ jsx14(
1213
1301
  Select2,
1214
1302
  {
1215
1303
  id: "mui-builder-new-locale",
@@ -1231,7 +1319,7 @@ function createMuiLocalizationSlot(options) {
1231
1319
  }
1232
1320
  ) }),
1233
1321
  /* @__PURE__ */ jsx14(
1234
- Box2,
1322
+ Box3,
1235
1323
  {
1236
1324
  sx: {
1237
1325
  flexShrink: 0,
@@ -1276,7 +1364,7 @@ function createMuiLocalizationSlot(options) {
1276
1364
  ))
1277
1365
  }
1278
1366
  ),
1279
- !translationAdapterAvailable ? null : /* @__PURE__ */ jsx14(Box2, { sx: { minWidth: "max-content", whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsx14(
1367
+ !translationAdapterAvailable ? null : /* @__PURE__ */ jsx14(Box3, { sx: { minWidth: "max-content", whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsx14(
1280
1368
  Button2,
1281
1369
  {
1282
1370
  variant: "secondary",
@@ -1329,7 +1417,7 @@ function createMuiLocalizationSlot(options) {
1329
1417
  ] });
1330
1418
  }
1331
1419
  return /* @__PURE__ */ jsx14(
1332
- Box2,
1420
+ Box3,
1333
1421
  {
1334
1422
  "data-mui-slot": "localization",
1335
1423
  sx: {
@@ -1451,6 +1539,7 @@ export {
1451
1539
  mergeMuiAdapterOptions,
1452
1540
  muiBuilderComponents,
1453
1541
  muiBuilderSlots,
1542
+ muiDefaultFieldTypeIcon,
1454
1543
  muiDefaultIconResolver,
1455
1544
  resolveMuiAdapterOptions,
1456
1545
  useResolvedMuiAdapterOptions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@form-engine-ts/mui",
3
- "version": "3.0.0",
3
+ "version": "3.1.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.0.0",
50
- "@form-engine-ts/react": "3.0.0"
49
+ "@form-engine-ts/core": "3.1.0",
50
+ "@form-engine-ts/react": "3.1.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": "3.0.0",
62
- "@form-engine-ts/react": "3.0.0"
61
+ "@form-engine-ts/react": "3.1.0",
62
+ "@form-engine-ts/core": "3.1.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",