@form-engine-ts/mui 3.2.0 → 4.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 +13 -5
- package/dist/index.cjs +218 -58
- package/dist/index.d.cts +19 -4
- package/dist/index.d.ts +19 -4
- package/dist/index.js +208 -47
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -17,7 +17,10 @@ import { MuiFormBuilder } from "@form-engine-ts/mui";
|
|
|
17
17
|
dense: true,
|
|
18
18
|
inputFullWidth: true,
|
|
19
19
|
buttonFullWidth: false,
|
|
20
|
-
fieldEditorOptions: {
|
|
20
|
+
fieldEditorOptions: {
|
|
21
|
+
description: "hidden",
|
|
22
|
+
byType: { rating: { ratingBounds: "readOnly" } }
|
|
23
|
+
},
|
|
21
24
|
getLocaleLabel: (locale) => localeNames[locale] ?? locale,
|
|
22
25
|
buttonVariants: { primary: "contained", secondary: "outlined", danger: "outlined" }
|
|
23
26
|
}}
|
|
@@ -64,8 +67,10 @@ slots as it does for the standard builder.
|
|
|
64
67
|
|
|
65
68
|
`MuiAdapterOptions` defaults to `size: "medium"`, `variant: "outlined"`, `buttonVariant: "contained"`,
|
|
66
69
|
`inputFullWidth: true`, and `buttonFullWidth: false`. The legacy `fullWidth` option remains the fallback for both new
|
|
67
|
-
width options. `fieldEditorOptions
|
|
68
|
-
|
|
70
|
+
width options. `fieldEditorOptions` controls title, description, required, type selection, options, display conditions,
|
|
71
|
+
text limits, rating bounds, and number limits. Each control can be `editable`, `readOnly`, or `hidden`, and `byType`
|
|
72
|
+
can override controls for individual question types. `localizationOptions.defaultLocaleControl` independently controls
|
|
73
|
+
the default locale input. Set `dense` to reduce section, editor, option, and toolbar spacing.
|
|
69
74
|
`buttonVariants` can override the MUI variant for `primary`, `secondary`, and `danger` actions independently.
|
|
70
75
|
`localizationOptions.availableLocales` supplies display-ready locale candidates independently from the policy; when
|
|
71
76
|
both are present, only candidates allowed by `allowedLocales` are shown. `placement` supports `top`,
|
|
@@ -76,11 +81,14 @@ default.
|
|
|
76
81
|
`layoutOptions`, `localizationOptions`, and `muiSlotProps` are also accepted in `muiOptions` for low-level factories such
|
|
77
82
|
as `createMuiBuilderSlots`; the dedicated `MuiFormBuilder` props take precedence.
|
|
78
83
|
|
|
79
|
-
Select options in the MUI adapter support icons, descriptions,
|
|
80
|
-
callbacks. Without a custom renderer, icons appear beside labels in the menu and
|
|
84
|
+
Select options in the MUI adapter support icons, descriptions, groups (`group`/`groupLabel` or `kind`), custom metadata,
|
|
85
|
+
and custom `renderOption`/`renderValue` callbacks. Without a custom renderer, icons appear beside labels in the menu and
|
|
86
|
+
in the selected value. The standard
|
|
81
87
|
MUI field editor supplies icons for every field type and accepts `slots.fieldTypeSelect` and `slots.fieldEditorHeader`
|
|
82
88
|
for focused customization. `muiSlotProps` also supports `textField`, `select`, `selectMenu`, `checkbox`, `button`, and
|
|
83
89
|
`iconButton` MUI props in addition to the layout props.
|
|
90
|
+
`fieldEditorOptions.fieldTypeOptions` can explicitly order, sort, or transform the generated type choices without
|
|
91
|
+
mutating the defaults.
|
|
84
92
|
|
|
85
93
|
`MuiFormBuilder` supplies options through `MuiFormBuilderContext` to module-stable adapter and slot component types.
|
|
86
94
|
Controlled schema updates therefore preserve input focus and uncontrolled MUI state such as an open localization
|
package/dist/index.cjs
CHANGED
|
@@ -102,7 +102,17 @@ function resolveMuiAdapterOptions(options = {}) {
|
|
|
102
102
|
...options.getActionLabel === void 0 ? {} : { getActionLabel: options.getActionLabel },
|
|
103
103
|
layoutOptions: options.layoutOptions ?? {},
|
|
104
104
|
fieldEditorOptions: {
|
|
105
|
-
|
|
105
|
+
title: options.fieldEditorOptions?.title ?? "editable",
|
|
106
|
+
description: options.fieldEditorOptions?.description ?? "editable",
|
|
107
|
+
required: options.fieldEditorOptions?.required ?? "editable",
|
|
108
|
+
typeSelect: options.fieldEditorOptions?.typeSelect ?? "editable",
|
|
109
|
+
options: options.fieldEditorOptions?.options ?? "editable",
|
|
110
|
+
displayConditions: options.fieldEditorOptions?.displayConditions ?? "editable",
|
|
111
|
+
textLimits: options.fieldEditorOptions?.textLimits ?? "editable",
|
|
112
|
+
ratingBounds: options.fieldEditorOptions?.ratingBounds ?? "editable",
|
|
113
|
+
numberLimits: options.fieldEditorOptions?.numberLimits ?? "editable",
|
|
114
|
+
...options.fieldEditorOptions?.byType === void 0 ? {} : { byType: options.fieldEditorOptions.byType },
|
|
115
|
+
...options.fieldEditorOptions?.fieldTypeOptions === void 0 ? {} : { fieldTypeOptions: options.fieldEditorOptions.fieldTypeOptions }
|
|
106
116
|
},
|
|
107
117
|
localizationOptions: {
|
|
108
118
|
collapsible: options.localizationOptions?.collapsible ?? false,
|
|
@@ -128,7 +138,13 @@ function mergeMuiAdapterOptions(base = {}, overrides = {}) {
|
|
|
128
138
|
...overrides,
|
|
129
139
|
...base.buttonVariants === void 0 && overrides.buttonVariants === void 0 ? {} : { buttonVariants: { ...base.buttonVariants, ...overrides.buttonVariants } },
|
|
130
140
|
...base.layoutOptions === void 0 && overrides.layoutOptions === void 0 ? {} : { layoutOptions: { ...base.layoutOptions, ...overrides.layoutOptions } },
|
|
131
|
-
...base.fieldEditorOptions === void 0 && overrides.fieldEditorOptions === void 0 ? {} : {
|
|
141
|
+
...base.fieldEditorOptions === void 0 && overrides.fieldEditorOptions === void 0 ? {} : {
|
|
142
|
+
fieldEditorOptions: {
|
|
143
|
+
...base.fieldEditorOptions,
|
|
144
|
+
...overrides.fieldEditorOptions,
|
|
145
|
+
...base.fieldEditorOptions?.byType === void 0 && overrides.fieldEditorOptions?.byType === void 0 ? {} : { byType: { ...base.fieldEditorOptions?.byType, ...overrides.fieldEditorOptions?.byType } }
|
|
146
|
+
}
|
|
147
|
+
},
|
|
132
148
|
...base.localizationOptions === void 0 && overrides.localizationOptions === void 0 ? {} : { localizationOptions: { ...base.localizationOptions, ...overrides.localizationOptions } },
|
|
133
149
|
...base.muiSlotProps === void 0 && overrides.muiSlotProps === void 0 ? {} : { muiSlotProps: { ...base.muiSlotProps, ...overrides.muiSlotProps } }
|
|
134
150
|
};
|
|
@@ -417,6 +433,40 @@ var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
|
417
433
|
function normalizeOptions(options) {
|
|
418
434
|
return options.map((option) => typeof option === "string" ? { value: option, label: option } : option);
|
|
419
435
|
}
|
|
436
|
+
function SelectGroupHeader({ children }) {
|
|
437
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.ListSubheader, { role: "presentation", children });
|
|
438
|
+
}
|
|
439
|
+
function groupSelectOptions(options) {
|
|
440
|
+
const groups = [];
|
|
441
|
+
const groupIndexes = /* @__PURE__ */ new Map();
|
|
442
|
+
for (const option of options) {
|
|
443
|
+
const groupKey = option.group ?? option.kind;
|
|
444
|
+
const groupLabel = option.groupLabel ?? groupKey;
|
|
445
|
+
if (groupKey === void 0 && groupLabel === void 0) {
|
|
446
|
+
groups.push({ items: [option] });
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
const key = groupKey ?? groupLabel;
|
|
450
|
+
if (key === void 0) {
|
|
451
|
+
groups.push({ items: [option] });
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
const existingIndex = groupIndexes.get(key);
|
|
455
|
+
if (existingIndex === void 0) {
|
|
456
|
+
groupIndexes.set(key, groups.length);
|
|
457
|
+
groups.push({
|
|
458
|
+
groupKey: key,
|
|
459
|
+
...groupLabel === void 0 ? {} : { groupLabel },
|
|
460
|
+
items: [option]
|
|
461
|
+
});
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
const existing = groups[existingIndex];
|
|
465
|
+
if (existing === void 0) continue;
|
|
466
|
+
groups[existingIndex] = { ...existing, items: [...existing.items, option] };
|
|
467
|
+
}
|
|
468
|
+
return groups;
|
|
469
|
+
}
|
|
420
470
|
function createMuiSelectAdapter(options) {
|
|
421
471
|
return function MuiSelect({
|
|
422
472
|
id,
|
|
@@ -440,13 +490,14 @@ function createMuiSelectAdapter(options) {
|
|
|
440
490
|
}) {
|
|
441
491
|
const resolved = useResolvedMuiAdapterOptions(options);
|
|
442
492
|
const normalizedOptions = normalizeOptions(selectOptions);
|
|
493
|
+
const groupedOptions = groupSelectOptions(normalizedOptions);
|
|
443
494
|
const selectSlotProps = resolved.muiSlotProps?.select;
|
|
444
495
|
const menuProps = { ...selectSlotProps?.MenuProps, ...resolved.muiSlotProps?.selectMenu };
|
|
445
496
|
const slotInputProps = selectSlotProps?.inputProps;
|
|
446
497
|
const labelId = id === void 0 || label === void 0 ? void 0 : `${id}-label`;
|
|
447
498
|
const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
|
|
448
499
|
const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
|
|
449
|
-
const handleChange = (event) => onChange(
|
|
500
|
+
const handleChange = (event) => onChange(event.target.value);
|
|
450
501
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
451
502
|
import_material7.FormControl,
|
|
452
503
|
{
|
|
@@ -491,10 +542,22 @@ function createMuiSelectAdapter(options) {
|
|
|
491
542
|
...describedBy === void 0 ? {} : { "aria-describedby": describedBy },
|
|
492
543
|
...ariaLabelledBy === void 0 ? {} : { "aria-labelledby": ariaLabelledBy }
|
|
493
544
|
},
|
|
494
|
-
children:
|
|
495
|
-
|
|
496
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
497
|
-
|
|
545
|
+
children: groupedOptions.flatMap((group) => [
|
|
546
|
+
group.groupLabel === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectGroupHeader, { children: group.groupLabel }, `header-${group.groupKey ?? group.groupLabel}`),
|
|
547
|
+
...group.items.map((option) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
548
|
+
import_material7.MenuItem,
|
|
549
|
+
{
|
|
550
|
+
value: option.value,
|
|
551
|
+
disabled: option.disabled,
|
|
552
|
+
...option.group === void 0 && option.groupLabel === void 0 && option.kind === void 0 ? {} : { "aria-label": option.label },
|
|
553
|
+
children: renderOption === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
554
|
+
option.icon === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.ListItemIcon, { sx: { minWidth: 32 }, children: option.icon }),
|
|
555
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.ListItemText, { primary: option.label, secondary: option.description })
|
|
556
|
+
] }) : renderOption(option)
|
|
557
|
+
},
|
|
558
|
+
option.value
|
|
559
|
+
))
|
|
560
|
+
])
|
|
498
561
|
}
|
|
499
562
|
),
|
|
500
563
|
helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_material7.FormHelperText, { id: helperId, children: helperText })
|
|
@@ -503,7 +566,10 @@ function createMuiSelectAdapter(options) {
|
|
|
503
566
|
);
|
|
504
567
|
};
|
|
505
568
|
}
|
|
506
|
-
|
|
569
|
+
function MuiSelectAdapter(props) {
|
|
570
|
+
const Adapter = createMuiSelectAdapter();
|
|
571
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Adapter, { ...props });
|
|
572
|
+
}
|
|
507
573
|
|
|
508
574
|
// src/adapters/TextArea.tsx
|
|
509
575
|
var import_material8 = require("@mui/material");
|
|
@@ -724,6 +790,7 @@ function createMuiBuilderComponents(optionsOrOverrides = {}, customOverrides) {
|
|
|
724
790
|
|
|
725
791
|
// src/slots/FieldEditor.tsx
|
|
726
792
|
var import_core = require("@form-engine-ts/core");
|
|
793
|
+
var import_react2 = require("@form-engine-ts/react");
|
|
727
794
|
var import_material12 = require("@mui/material");
|
|
728
795
|
|
|
729
796
|
// src/slots/OptionEditor.tsx
|
|
@@ -896,6 +963,13 @@ function updateBound(field, property, value) {
|
|
|
896
963
|
const { [property]: _removed, ...remaining } = field;
|
|
897
964
|
return remaining;
|
|
898
965
|
}
|
|
966
|
+
function updateNumberProperty(field, property, value) {
|
|
967
|
+
if (field.type !== "number") return field;
|
|
968
|
+
const nextValue = numericValue(value);
|
|
969
|
+
if (nextValue !== void 0) return { ...field, [property]: nextValue };
|
|
970
|
+
const { [property]: _removed, ...remaining } = field;
|
|
971
|
+
return remaining;
|
|
972
|
+
}
|
|
899
973
|
function createMuiFieldEditorSlot(options) {
|
|
900
974
|
const Toolbar = createMuiToolbarSlot(options);
|
|
901
975
|
const OptionEditor = createMuiOptionEditorSlot(options);
|
|
@@ -910,7 +984,9 @@ function createMuiFieldEditorSlot(options) {
|
|
|
910
984
|
actions,
|
|
911
985
|
components,
|
|
912
986
|
translate,
|
|
913
|
-
slots
|
|
987
|
+
slots,
|
|
988
|
+
fieldEditorControls,
|
|
989
|
+
fieldTypeOptions: fieldTypeOptionsConfig
|
|
914
990
|
}) {
|
|
915
991
|
const resolved = useResolvedMuiAdapterOptions(options);
|
|
916
992
|
const { Button: Button2, Checkbox: Checkbox2, Select: Select2, TextArea, TextInput } = components;
|
|
@@ -919,10 +995,41 @@ function createMuiFieldEditorSlot(options) {
|
|
|
919
995
|
const condition = field.displayCondition;
|
|
920
996
|
const conditionSources = schema.fields.slice(0, index);
|
|
921
997
|
const conditionSource = conditionSources.find((source) => source.id === condition?.questionId);
|
|
922
|
-
const
|
|
998
|
+
const fieldEditorOptions = resolved.fieldEditorOptions ?? {};
|
|
999
|
+
const controls = (0, import_react2.resolveFieldEditorControls)({
|
|
1000
|
+
...fieldEditorControls ?? {},
|
|
1001
|
+
...fieldEditorOptions,
|
|
1002
|
+
...fieldEditorOptions.byType?.[field.type] ?? {}
|
|
1003
|
+
});
|
|
923
1004
|
const titleErrorId = field.title.trim().length === 0 ? `mui-field-${field.id}-title-error` : void 0;
|
|
924
1005
|
const FieldTypeSelect = slots?.fieldTypeSelect;
|
|
925
1006
|
const FieldEditorHeader = slots?.fieldEditorHeader;
|
|
1007
|
+
const fieldTypeSelectId = `mui-field-${field.id}-type`;
|
|
1008
|
+
const fieldTypeSelectLabel = translate("builder.type");
|
|
1009
|
+
const generatedFieldTypeOptions = FIELD_TYPES.filter(
|
|
1010
|
+
(type) => allowedTypes.includes(type)
|
|
1011
|
+
).map((type) => {
|
|
1012
|
+
const definition = import_core.DEFAULT_FIELD_TYPE_DEFINITIONS.find((candidate) => candidate.type === type);
|
|
1013
|
+
const group = definition?.category;
|
|
1014
|
+
return {
|
|
1015
|
+
value: type,
|
|
1016
|
+
label: translate(definition?.labelKey ?? `builder.fieldType.${type}`),
|
|
1017
|
+
description: translate(`builder.fieldTypeDescription.${type}`),
|
|
1018
|
+
icon: components.renderFieldTypeIcon(type),
|
|
1019
|
+
...group === void 0 ? {} : {
|
|
1020
|
+
group,
|
|
1021
|
+
groupLabel: translate(`builder.fieldCategory.${group}`)
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
});
|
|
1025
|
+
const fieldTypeOptions = (0, import_react2.resolveFieldTypeSelectOptions)(
|
|
1026
|
+
generatedFieldTypeOptions,
|
|
1027
|
+
fieldTypeOptionsConfig ?? fieldEditorOptions.fieldTypeOptions,
|
|
1028
|
+
{
|
|
1029
|
+
currentType: field.type,
|
|
1030
|
+
allowedTypes
|
|
1031
|
+
}
|
|
1032
|
+
);
|
|
926
1033
|
const changeFieldType = (nextType) => {
|
|
927
1034
|
if (allowedTypes.includes(nextType)) actions.changeFieldType(field.id, nextType);
|
|
928
1035
|
};
|
|
@@ -957,7 +1064,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
957
1064
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FieldEditorHeader, { field, index, totalFields: schema.fields.length, actions }),
|
|
958
1065
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { ...resolved.muiSlotProps?.stack, spacing: resolved.dense ? 1 : 2, children: [
|
|
959
1066
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { direction: { xs: "column", md: "row" }, spacing: resolved.dense ? 1 : 2, children: [
|
|
960
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1067
|
+
controls.title === "hidden" ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
961
1068
|
TextInput,
|
|
962
1069
|
{
|
|
963
1070
|
id: `mui-field-${field.id}-title`,
|
|
@@ -968,27 +1075,19 @@ function createMuiFieldEditorSlot(options) {
|
|
|
968
1075
|
error: field.title.trim().length === 0,
|
|
969
1076
|
helperText: field.title.trim().length === 0 ? translate("builder.required") : "",
|
|
970
1077
|
"aria-describedby": titleErrorId,
|
|
971
|
-
disabled: readOnly,
|
|
1078
|
+
disabled: readOnly || controls.title === "readOnly",
|
|
972
1079
|
onChange: (value) => actions.setSourceText({ kind: "field", id: field.id }, "title", value)
|
|
973
1080
|
}
|
|
974
1081
|
),
|
|
975
|
-
FieldTypeSelect === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1082
|
+
controls.typeSelect === "hidden" ? null : FieldTypeSelect === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
976
1083
|
Select2,
|
|
977
1084
|
{
|
|
978
|
-
id:
|
|
1085
|
+
id: fieldTypeSelectId,
|
|
979
1086
|
name: `fields.${field.id}.type`,
|
|
980
|
-
label:
|
|
1087
|
+
label: fieldTypeSelectLabel,
|
|
981
1088
|
value: allowedTypes.includes(field.type) ? field.type : "",
|
|
982
|
-
options:
|
|
983
|
-
|
|
984
|
-
return {
|
|
985
|
-
value: type,
|
|
986
|
-
label: translate(definition?.labelKey ?? `builder.fieldType.${type}`),
|
|
987
|
-
icon: components.renderFieldTypeIcon(type),
|
|
988
|
-
...definition?.category === void 0 ? {} : { kind: definition.category }
|
|
989
|
-
};
|
|
990
|
-
}),
|
|
991
|
-
disabled: readOnly,
|
|
1089
|
+
options: fieldTypeOptions,
|
|
1090
|
+
disabled: readOnly || controls.typeSelect === "readOnly",
|
|
992
1091
|
onChange: (value) => {
|
|
993
1092
|
if (isFieldType(value)) changeFieldType(value);
|
|
994
1093
|
}
|
|
@@ -996,16 +1095,21 @@ function createMuiFieldEditorSlot(options) {
|
|
|
996
1095
|
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
997
1096
|
FieldTypeSelect,
|
|
998
1097
|
{
|
|
1098
|
+
id: fieldTypeSelectId,
|
|
1099
|
+
name: `fields.${field.id}.type`,
|
|
1100
|
+
label: fieldTypeSelectLabel,
|
|
999
1101
|
currentType: field.type,
|
|
1000
1102
|
allowedTypes,
|
|
1103
|
+
options: fieldTypeOptions,
|
|
1001
1104
|
onChangeType: changeFieldType,
|
|
1002
|
-
disabled: readOnly,
|
|
1003
|
-
readOnly,
|
|
1105
|
+
disabled: readOnly || controls.typeSelect === "readOnly",
|
|
1106
|
+
readOnly: readOnly || controls.typeSelect === "readOnly",
|
|
1107
|
+
"aria-labelledby": `${fieldTypeSelectId}-label`,
|
|
1004
1108
|
renderIcon: components.renderFieldTypeIcon
|
|
1005
1109
|
}
|
|
1006
1110
|
)
|
|
1007
1111
|
] }),
|
|
1008
|
-
|
|
1112
|
+
controls.description === "hidden" ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1009
1113
|
TextArea,
|
|
1010
1114
|
{
|
|
1011
1115
|
id: `mui-field-${field.id}-description`,
|
|
@@ -1013,19 +1117,19 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1013
1117
|
label: translate("builder.description"),
|
|
1014
1118
|
value: field.description ?? "",
|
|
1015
1119
|
rows: resolved.dense ? 2 : 3,
|
|
1016
|
-
disabled: readOnly,
|
|
1017
|
-
readOnly:
|
|
1120
|
+
disabled: readOnly || controls.description === "readOnly",
|
|
1121
|
+
readOnly: readOnly || controls.description === "readOnly",
|
|
1018
1122
|
onChange: (value) => actions.setSourceText({ kind: "field", id: field.id }, "description", value)
|
|
1019
1123
|
}
|
|
1020
1124
|
),
|
|
1021
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1125
|
+
controls.required === "hidden" ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1022
1126
|
Checkbox2,
|
|
1023
1127
|
{
|
|
1024
1128
|
id: `mui-field-${field.id}-required`,
|
|
1025
1129
|
name: `fields.${field.id}.required`,
|
|
1026
1130
|
label: translate("builder.required"),
|
|
1027
1131
|
checked: field.required,
|
|
1028
|
-
disabled: readOnly,
|
|
1132
|
+
disabled: readOnly || controls.required === "readOnly",
|
|
1029
1133
|
onChange: (checked) => actions.updateField(field.id, (current) => ({ ...current, required: checked }))
|
|
1030
1134
|
}
|
|
1031
1135
|
),
|
|
@@ -1043,7 +1147,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1043
1147
|
onChange: (value) => actions.assignFieldToPage(field.id, value.length === 0 ? null : value)
|
|
1044
1148
|
}
|
|
1045
1149
|
),
|
|
1046
|
-
field.type === "number" || field.type === "rating" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { direction: { xs: "column", sm: "row" }, spacing: resolved.dense ? 1 : 2, children: [
|
|
1150
|
+
field.type === "number" || field.type === "rating" ? (field.type === "number" ? controls.numberLimits : controls.ratingBounds) === "hidden" ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { direction: { xs: "column", sm: "row" }, spacing: resolved.dense ? 1 : 2, children: [
|
|
1047
1151
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1048
1152
|
TextInput,
|
|
1049
1153
|
{
|
|
@@ -1051,7 +1155,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1051
1155
|
label: translate("builder.minimum"),
|
|
1052
1156
|
type: "number",
|
|
1053
1157
|
value: field.min === void 0 ? "" : String(field.min),
|
|
1054
|
-
disabled: readOnly,
|
|
1158
|
+
disabled: readOnly || (field.type === "number" ? controls.numberLimits : controls.ratingBounds) === "readOnly",
|
|
1055
1159
|
onChange: (value) => actions.updateField(field.id, (current) => updateBound(current, "min", value))
|
|
1056
1160
|
}
|
|
1057
1161
|
),
|
|
@@ -1062,12 +1166,68 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1062
1166
|
label: translate("builder.maximum"),
|
|
1063
1167
|
type: "number",
|
|
1064
1168
|
value: field.max === void 0 ? "" : String(field.max),
|
|
1065
|
-
disabled: readOnly,
|
|
1169
|
+
disabled: readOnly || (field.type === "number" ? controls.numberLimits : controls.ratingBounds) === "readOnly",
|
|
1066
1170
|
onChange: (value) => actions.updateField(field.id, (current) => updateBound(current, "max", value))
|
|
1067
1171
|
}
|
|
1068
1172
|
)
|
|
1069
1173
|
] }) : null,
|
|
1070
|
-
|
|
1174
|
+
field.type === "number" && controls.numberLimits !== "hidden" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1175
|
+
TextInput,
|
|
1176
|
+
{
|
|
1177
|
+
id: `mui-field-${field.id}-step`,
|
|
1178
|
+
label: translate("builder.step"),
|
|
1179
|
+
type: "number",
|
|
1180
|
+
value: field.step === void 0 ? "" : String(field.step),
|
|
1181
|
+
disabled: readOnly || controls.numberLimits === "readOnly",
|
|
1182
|
+
onChange: (value) => actions.updateField(field.id, (current) => updateNumberProperty(current, "step", value))
|
|
1183
|
+
}
|
|
1184
|
+
) : null,
|
|
1185
|
+
(field.type === "text" || field.type === "textarea") && controls.textLimits !== "hidden" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { direction: { xs: "column", sm: "row" }, spacing: resolved.dense ? 1 : 2, children: [
|
|
1186
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1187
|
+
TextInput,
|
|
1188
|
+
{
|
|
1189
|
+
id: `mui-field-${field.id}-min-length`,
|
|
1190
|
+
label: translate("builder.minimumLength"),
|
|
1191
|
+
type: "number",
|
|
1192
|
+
value: field.minLength === void 0 ? "" : String(field.minLength),
|
|
1193
|
+
disabled: readOnly || controls.textLimits === "readOnly",
|
|
1194
|
+
onChange: (value) => actions.updateField(field.id, (current) => {
|
|
1195
|
+
if (current.type !== "text" && current.type !== "textarea") return current;
|
|
1196
|
+
const parsed = numericValue(value);
|
|
1197
|
+
return parsed === void 0 ? current : { ...current, minLength: Math.max(0, Math.floor(parsed)) };
|
|
1198
|
+
})
|
|
1199
|
+
}
|
|
1200
|
+
),
|
|
1201
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1202
|
+
TextInput,
|
|
1203
|
+
{
|
|
1204
|
+
id: `mui-field-${field.id}-max-length`,
|
|
1205
|
+
label: translate("builder.maximumLength"),
|
|
1206
|
+
type: "number",
|
|
1207
|
+
value: field.maxLength === void 0 ? "" : String(field.maxLength),
|
|
1208
|
+
disabled: readOnly || controls.textLimits === "readOnly",
|
|
1209
|
+
onChange: (value) => actions.updateField(field.id, (current) => {
|
|
1210
|
+
if (current.type !== "text" && current.type !== "textarea") return current;
|
|
1211
|
+
const parsed = numericValue(value);
|
|
1212
|
+
return parsed === void 0 ? current : { ...current, maxLength: Math.max(0, Math.floor(parsed)) };
|
|
1213
|
+
})
|
|
1214
|
+
}
|
|
1215
|
+
),
|
|
1216
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1217
|
+
TextInput,
|
|
1218
|
+
{
|
|
1219
|
+
id: `mui-field-${field.id}-pattern`,
|
|
1220
|
+
label: translate("builder.pattern"),
|
|
1221
|
+
value: field.pattern ?? "",
|
|
1222
|
+
disabled: readOnly || controls.textLimits === "readOnly",
|
|
1223
|
+
onChange: (value) => actions.updateField(
|
|
1224
|
+
field.id,
|
|
1225
|
+
(current) => current.type === "text" || current.type === "textarea" ? value.length === 0 ? current : { ...current, pattern: value } : current
|
|
1226
|
+
)
|
|
1227
|
+
}
|
|
1228
|
+
)
|
|
1229
|
+
] }) : null,
|
|
1230
|
+
features?.conditions === false || conditionSources.length === 0 || controls.displayConditions === "hidden" ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { direction: { xs: "column", md: "row" }, spacing: resolved.dense ? 1 : 2, children: [
|
|
1071
1231
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1072
1232
|
Select2,
|
|
1073
1233
|
{
|
|
@@ -1078,7 +1238,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1078
1238
|
{ value: "", label: translate("builder.alwaysVisible") },
|
|
1079
1239
|
...conditionSources.map((source) => ({ value: source.id, label: source.title }))
|
|
1080
1240
|
],
|
|
1081
|
-
disabled: readOnly,
|
|
1241
|
+
disabled: readOnly || controls.displayConditions === "readOnly",
|
|
1082
1242
|
onChange: (value) => actions.setDisplayCondition(
|
|
1083
1243
|
field.id,
|
|
1084
1244
|
value.length === 0 ? void 0 : { questionId: value, operator: "equals", value: "" }
|
|
@@ -1095,7 +1255,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1095
1255
|
options: (conditionSource === void 0 ? [] : conditionOperators(conditionSource)).map(
|
|
1096
1256
|
(operator) => ({ value: operator, label: translate(`builder.operator.${operator}`) })
|
|
1097
1257
|
),
|
|
1098
|
-
disabled: readOnly,
|
|
1258
|
+
disabled: readOnly || controls.displayConditions === "readOnly",
|
|
1099
1259
|
onChange: (value) => {
|
|
1100
1260
|
if (!isConditionOperator(value)) return;
|
|
1101
1261
|
actions.setDisplayCondition(
|
|
@@ -1111,7 +1271,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1111
1271
|
id: `mui-field-${field.id}-condition-value`,
|
|
1112
1272
|
label: translate("builder.conditionValue"),
|
|
1113
1273
|
value: condition.value === void 0 ? "" : String(condition.value),
|
|
1114
|
-
disabled: readOnly,
|
|
1274
|
+
disabled: readOnly || controls.displayConditions === "readOnly",
|
|
1115
1275
|
onChange: (value) => actions.setDisplayCondition(field.id, { ...condition, value })
|
|
1116
1276
|
}
|
|
1117
1277
|
)
|
|
@@ -1121,29 +1281,29 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1121
1281
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material12.Typography, { variant: "subtitle2", children: translate("builder.translation", {
|
|
1122
1282
|
locale: resolved.getLocaleLabel?.(currentLocale) ?? currentLocale
|
|
1123
1283
|
}) }),
|
|
1124
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1284
|
+
controls.title === "hidden" ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1125
1285
|
TextInput,
|
|
1126
1286
|
{
|
|
1127
1287
|
id: `mui-field-${field.id}-${currentLocale}-title`,
|
|
1128
1288
|
label: translate("builder.translatedQuestionTitle"),
|
|
1129
1289
|
value: field.translations?.[currentLocale]?.title ?? "",
|
|
1130
|
-
disabled: readOnly,
|
|
1290
|
+
disabled: readOnly || controls.title === "readOnly",
|
|
1131
1291
|
onChange: (value) => actions.setManualTranslation(currentLocale, { kind: "field", id: field.id }, "title", value)
|
|
1132
1292
|
}
|
|
1133
1293
|
),
|
|
1134
|
-
|
|
1294
|
+
controls.description === "hidden" ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1135
1295
|
TextArea,
|
|
1136
1296
|
{
|
|
1137
1297
|
id: `mui-field-${field.id}-${currentLocale}-description`,
|
|
1138
1298
|
label: translate("builder.translatedDescription"),
|
|
1139
1299
|
value: field.translations?.[currentLocale]?.description ?? "",
|
|
1140
|
-
disabled: readOnly,
|
|
1141
|
-
readOnly:
|
|
1300
|
+
disabled: readOnly || controls.description === "readOnly",
|
|
1301
|
+
readOnly: readOnly || controls.description === "readOnly",
|
|
1142
1302
|
onChange: (value) => actions.setManualTranslation(currentLocale, { kind: "field", id: field.id }, "description", value)
|
|
1143
1303
|
}
|
|
1144
1304
|
)
|
|
1145
1305
|
] }),
|
|
1146
|
-
"options" in field ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { "data-mui-slot": "options", spacing: resolved.dense ? 1 : 2, children: [
|
|
1306
|
+
"options" in field && controls.options !== "hidden" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_material12.Stack, { "data-mui-slot": "options", spacing: resolved.dense ? 1 : 2, children: [
|
|
1147
1307
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_material12.Typography, { variant: "subtitle2", children: translate("builder.options") }),
|
|
1148
1308
|
field.options.map((option, optionIndex) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1149
1309
|
OptionEditor,
|
|
@@ -1154,7 +1314,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1154
1314
|
index: optionIndex,
|
|
1155
1315
|
currentLocale,
|
|
1156
1316
|
translate,
|
|
1157
|
-
readOnly,
|
|
1317
|
+
readOnly: readOnly || controls.options === "readOnly",
|
|
1158
1318
|
actions,
|
|
1159
1319
|
components
|
|
1160
1320
|
},
|
|
@@ -1165,7 +1325,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1165
1325
|
{
|
|
1166
1326
|
action: "addOption",
|
|
1167
1327
|
targetId: field.id,
|
|
1168
|
-
disabled: readOnly || policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
|
|
1328
|
+
disabled: readOnly || controls.options === "readOnly" || policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
|
|
1169
1329
|
onClick: () => actions.addOption(field.id),
|
|
1170
1330
|
children: translate("builder.addOption")
|
|
1171
1331
|
}
|
|
@@ -1182,7 +1342,7 @@ var MuiFieldEditorSlot = createMuiFieldEditorSlot();
|
|
|
1182
1342
|
// src/slots/Localization.tsx
|
|
1183
1343
|
var import_icons_material2 = require("@mui/icons-material");
|
|
1184
1344
|
var import_material13 = require("@mui/material");
|
|
1185
|
-
var
|
|
1345
|
+
var import_react3 = require("react");
|
|
1186
1346
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1187
1347
|
var DEFAULT_EMPTY_STATE_MESSAGE = "No translation locales have been added yet. Select a language from the dropdown above to add one.";
|
|
1188
1348
|
function normalizeLocaleOptions(options, getLocaleLabel) {
|
|
@@ -1213,8 +1373,8 @@ function createMuiLocalizationSlot(options) {
|
|
|
1213
1373
|
}) {
|
|
1214
1374
|
const resolved = useResolvedMuiAdapterOptions(options);
|
|
1215
1375
|
const { Button: Button2, ErrorMessage, Select: Select2, TextArea, TextInput } = components;
|
|
1216
|
-
const [newLocale, setNewLocale] = (0,
|
|
1217
|
-
const [pendingFocusLocale, setPendingFocusLocale] = (0,
|
|
1376
|
+
const [newLocale, setNewLocale] = (0, import_react3.useState)("");
|
|
1377
|
+
const [pendingFocusLocale, setPendingFocusLocale] = (0, import_react3.useState)(null);
|
|
1218
1378
|
const locales = Array.from(
|
|
1219
1379
|
new Set((schema.supportedLocales ?? []).filter((locale) => locale !== schema.defaultLocale))
|
|
1220
1380
|
);
|
|
@@ -1253,7 +1413,7 @@ function createMuiLocalizationSlot(options) {
|
|
|
1253
1413
|
event.stopPropagation();
|
|
1254
1414
|
addLocale();
|
|
1255
1415
|
};
|
|
1256
|
-
(0,
|
|
1416
|
+
(0, import_react3.useEffect)(() => {
|
|
1257
1417
|
if (pendingFocusLocale === null || currentLocale !== pendingFocusLocale) return;
|
|
1258
1418
|
const tab = document.getElementById(`mui-builder-locale-tab-${pendingFocusLocale}`);
|
|
1259
1419
|
if (tab === null) return;
|
|
@@ -1494,8 +1654,8 @@ function createMuiBuilderProps(options, overrides = {}) {
|
|
|
1494
1654
|
}
|
|
1495
1655
|
|
|
1496
1656
|
// src/MuiFormBuilder.tsx
|
|
1497
|
-
var
|
|
1498
|
-
var
|
|
1657
|
+
var import_react4 = require("@form-engine-ts/react");
|
|
1658
|
+
var import_react5 = require("react");
|
|
1499
1659
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1500
1660
|
function MuiFormBuilder({
|
|
1501
1661
|
muiOptions,
|
|
@@ -1507,7 +1667,7 @@ function MuiFormBuilder({
|
|
|
1507
1667
|
sectionOrder,
|
|
1508
1668
|
...props
|
|
1509
1669
|
}) {
|
|
1510
|
-
const contextOptions = (0,
|
|
1670
|
+
const contextOptions = (0, import_react5.useMemo)(
|
|
1511
1671
|
() => mergeMuiAdapterOptions(muiOptions, {
|
|
1512
1672
|
...layoutOptions === void 0 ? {} : { layoutOptions },
|
|
1513
1673
|
...localizationOptions === void 0 ? {} : { localizationOptions },
|
|
@@ -1515,13 +1675,13 @@ function MuiFormBuilder({
|
|
|
1515
1675
|
}),
|
|
1516
1676
|
[layoutOptions, localizationOptions, muiOptions, muiSlotProps]
|
|
1517
1677
|
);
|
|
1518
|
-
const contextValue = (0,
|
|
1519
|
-
const components = (0,
|
|
1520
|
-
const slots = (0,
|
|
1678
|
+
const contextValue = (0, import_react5.useMemo)(() => ({ options: contextOptions }), [contextOptions]);
|
|
1679
|
+
const components = (0, import_react5.useMemo)(() => ({ ...muiBuilderComponents, ...customComponents }), [customComponents]);
|
|
1680
|
+
const slots = (0, import_react5.useMemo)(() => ({ ...muiBuilderSlots, ...customSlots }), [customSlots]);
|
|
1521
1681
|
const placement = contextOptions.localizationOptions?.placement;
|
|
1522
1682
|
const resolvedSectionOrder = sectionOrder ?? (placement === void 0 ? contextOptions.layoutOptions?.sectionOrder : MUI_LOCALIZATION_SECTION_ORDERS[placement]);
|
|
1523
1683
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MuiFormBuilderContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1524
|
-
|
|
1684
|
+
import_react4.FormBuilder,
|
|
1525
1685
|
{
|
|
1526
1686
|
...props,
|
|
1527
1687
|
components,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { BuilderActionIconType, LocalizationSummaryContext, BuilderButtonProps, BuilderCheckboxProps, BuilderErrorMessageProps, BuilderFieldsetProps, BuilderIconButtonProps, BuilderSectionProps, BuilderSelectProps, BuilderTextAreaProps, BuilderTextInputProps, FormBuilderComponents, FormBuilderSlots, FormBuilderProps,
|
|
1
|
+
import { BuilderActionIconType, FieldPropertyControlMode, QuestionType, FieldEditorControlsConfig, FieldTypeSelectOptionsConfig, LocalizationSummaryContext, BuilderButtonProps, BuilderCheckboxProps, BuilderErrorMessageProps, BuilderFieldsetProps, BuilderIconButtonProps, BuilderSectionProps, BuilderSelectProps, BuilderTextAreaProps, BuilderTextInputProps, FormBuilderComponents, FormBuilderSlots, FormBuilderProps, BuilderFieldEditorSlotProps, BuilderLocalizationSlotProps, BuilderOptionEditorSlotProps, BuilderToolbarSlotProps } from '@form-engine-ts/react';
|
|
2
2
|
import * as react from 'react';
|
|
3
|
-
import { 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";
|
|
@@ -28,8 +28,23 @@ interface MuiLocalizationOptions {
|
|
|
28
28
|
readonly noWrapActions?: boolean;
|
|
29
29
|
readonly autoFocusNewTab?: boolean;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* MUI's field editor controls mirror `FieldEditorControlsConfig` while keeping
|
|
33
|
+
* the original interface members directly declared for semver/reporting compatibility.
|
|
34
|
+
*/
|
|
31
35
|
interface MuiFieldEditorOptions {
|
|
36
|
+
readonly title?: FieldPropertyControlMode;
|
|
32
37
|
readonly description?: "editable" | "readOnly" | "hidden";
|
|
38
|
+
readonly required?: FieldPropertyControlMode;
|
|
39
|
+
readonly typeSelect?: FieldPropertyControlMode;
|
|
40
|
+
readonly options?: FieldPropertyControlMode;
|
|
41
|
+
readonly displayConditions?: FieldPropertyControlMode;
|
|
42
|
+
readonly textLimits?: FieldPropertyControlMode;
|
|
43
|
+
readonly ratingBounds?: FieldPropertyControlMode;
|
|
44
|
+
readonly numberLimits?: FieldPropertyControlMode;
|
|
45
|
+
/** Per-question-type overrides take precedence over the base controls. */
|
|
46
|
+
readonly byType?: Partial<Record<QuestionType, Partial<FieldEditorControlsConfig>>>;
|
|
47
|
+
readonly fieldTypeOptions?: FieldTypeSelectOptionsConfig;
|
|
33
48
|
}
|
|
34
49
|
interface MuiSlotProps {
|
|
35
50
|
readonly card?: Partial<CardProps>;
|
|
@@ -117,8 +132,8 @@ declare const MuiIconButtonAdapter: ComponentType<BuilderIconButtonProps>;
|
|
|
117
132
|
declare function createMuiSectionAdapter(options?: MuiAdapterOptions): ComponentType<BuilderSectionProps>;
|
|
118
133
|
declare const MuiSectionAdapter: ComponentType<BuilderSectionProps>;
|
|
119
134
|
|
|
120
|
-
declare function createMuiSelectAdapter(options?: MuiAdapterOptions): ComponentType<BuilderSelectProps
|
|
121
|
-
declare
|
|
135
|
+
declare function createMuiSelectAdapter<T extends string = string>(options?: MuiAdapterOptions): ComponentType<BuilderSelectProps<T>>;
|
|
136
|
+
declare function MuiSelectAdapter<T extends string = string>(props: BuilderSelectProps<T>): ReactElement;
|
|
122
137
|
|
|
123
138
|
declare function createMuiTextAreaAdapter(options?: MuiAdapterOptions): ComponentType<BuilderTextAreaProps>;
|
|
124
139
|
declare const MuiTextAreaAdapter: ComponentType<BuilderTextAreaProps>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { BuilderActionIconType, LocalizationSummaryContext, BuilderButtonProps, BuilderCheckboxProps, BuilderErrorMessageProps, BuilderFieldsetProps, BuilderIconButtonProps, BuilderSectionProps, BuilderSelectProps, BuilderTextAreaProps, BuilderTextInputProps, FormBuilderComponents, FormBuilderSlots, FormBuilderProps,
|
|
1
|
+
import { BuilderActionIconType, FieldPropertyControlMode, QuestionType, FieldEditorControlsConfig, FieldTypeSelectOptionsConfig, LocalizationSummaryContext, BuilderButtonProps, BuilderCheckboxProps, BuilderErrorMessageProps, BuilderFieldsetProps, BuilderIconButtonProps, BuilderSectionProps, BuilderSelectProps, BuilderTextAreaProps, BuilderTextInputProps, FormBuilderComponents, FormBuilderSlots, FormBuilderProps, BuilderFieldEditorSlotProps, BuilderLocalizationSlotProps, BuilderOptionEditorSlotProps, BuilderToolbarSlotProps } from '@form-engine-ts/react';
|
|
2
2
|
import * as react from 'react';
|
|
3
|
-
import { 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";
|
|
@@ -28,8 +28,23 @@ interface MuiLocalizationOptions {
|
|
|
28
28
|
readonly noWrapActions?: boolean;
|
|
29
29
|
readonly autoFocusNewTab?: boolean;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* MUI's field editor controls mirror `FieldEditorControlsConfig` while keeping
|
|
33
|
+
* the original interface members directly declared for semver/reporting compatibility.
|
|
34
|
+
*/
|
|
31
35
|
interface MuiFieldEditorOptions {
|
|
36
|
+
readonly title?: FieldPropertyControlMode;
|
|
32
37
|
readonly description?: "editable" | "readOnly" | "hidden";
|
|
38
|
+
readonly required?: FieldPropertyControlMode;
|
|
39
|
+
readonly typeSelect?: FieldPropertyControlMode;
|
|
40
|
+
readonly options?: FieldPropertyControlMode;
|
|
41
|
+
readonly displayConditions?: FieldPropertyControlMode;
|
|
42
|
+
readonly textLimits?: FieldPropertyControlMode;
|
|
43
|
+
readonly ratingBounds?: FieldPropertyControlMode;
|
|
44
|
+
readonly numberLimits?: FieldPropertyControlMode;
|
|
45
|
+
/** Per-question-type overrides take precedence over the base controls. */
|
|
46
|
+
readonly byType?: Partial<Record<QuestionType, Partial<FieldEditorControlsConfig>>>;
|
|
47
|
+
readonly fieldTypeOptions?: FieldTypeSelectOptionsConfig;
|
|
33
48
|
}
|
|
34
49
|
interface MuiSlotProps {
|
|
35
50
|
readonly card?: Partial<CardProps>;
|
|
@@ -117,8 +132,8 @@ declare const MuiIconButtonAdapter: ComponentType<BuilderIconButtonProps>;
|
|
|
117
132
|
declare function createMuiSectionAdapter(options?: MuiAdapterOptions): ComponentType<BuilderSectionProps>;
|
|
118
133
|
declare const MuiSectionAdapter: ComponentType<BuilderSectionProps>;
|
|
119
134
|
|
|
120
|
-
declare function createMuiSelectAdapter(options?: MuiAdapterOptions): ComponentType<BuilderSelectProps
|
|
121
|
-
declare
|
|
135
|
+
declare function createMuiSelectAdapter<T extends string = string>(options?: MuiAdapterOptions): ComponentType<BuilderSelectProps<T>>;
|
|
136
|
+
declare function MuiSelectAdapter<T extends string = string>(props: BuilderSelectProps<T>): ReactElement;
|
|
122
137
|
|
|
123
138
|
declare function createMuiTextAreaAdapter(options?: MuiAdapterOptions): ComponentType<BuilderTextAreaProps>;
|
|
124
139
|
declare const MuiTextAreaAdapter: ComponentType<BuilderTextAreaProps>;
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,17 @@ function resolveMuiAdapterOptions(options = {}) {
|
|
|
37
37
|
...options.getActionLabel === void 0 ? {} : { getActionLabel: options.getActionLabel },
|
|
38
38
|
layoutOptions: options.layoutOptions ?? {},
|
|
39
39
|
fieldEditorOptions: {
|
|
40
|
-
|
|
40
|
+
title: options.fieldEditorOptions?.title ?? "editable",
|
|
41
|
+
description: options.fieldEditorOptions?.description ?? "editable",
|
|
42
|
+
required: options.fieldEditorOptions?.required ?? "editable",
|
|
43
|
+
typeSelect: options.fieldEditorOptions?.typeSelect ?? "editable",
|
|
44
|
+
options: options.fieldEditorOptions?.options ?? "editable",
|
|
45
|
+
displayConditions: options.fieldEditorOptions?.displayConditions ?? "editable",
|
|
46
|
+
textLimits: options.fieldEditorOptions?.textLimits ?? "editable",
|
|
47
|
+
ratingBounds: options.fieldEditorOptions?.ratingBounds ?? "editable",
|
|
48
|
+
numberLimits: options.fieldEditorOptions?.numberLimits ?? "editable",
|
|
49
|
+
...options.fieldEditorOptions?.byType === void 0 ? {} : { byType: options.fieldEditorOptions.byType },
|
|
50
|
+
...options.fieldEditorOptions?.fieldTypeOptions === void 0 ? {} : { fieldTypeOptions: options.fieldEditorOptions.fieldTypeOptions }
|
|
41
51
|
},
|
|
42
52
|
localizationOptions: {
|
|
43
53
|
collapsible: options.localizationOptions?.collapsible ?? false,
|
|
@@ -63,7 +73,13 @@ function mergeMuiAdapterOptions(base = {}, overrides = {}) {
|
|
|
63
73
|
...overrides,
|
|
64
74
|
...base.buttonVariants === void 0 && overrides.buttonVariants === void 0 ? {} : { buttonVariants: { ...base.buttonVariants, ...overrides.buttonVariants } },
|
|
65
75
|
...base.layoutOptions === void 0 && overrides.layoutOptions === void 0 ? {} : { layoutOptions: { ...base.layoutOptions, ...overrides.layoutOptions } },
|
|
66
|
-
...base.fieldEditorOptions === void 0 && overrides.fieldEditorOptions === void 0 ? {} : {
|
|
76
|
+
...base.fieldEditorOptions === void 0 && overrides.fieldEditorOptions === void 0 ? {} : {
|
|
77
|
+
fieldEditorOptions: {
|
|
78
|
+
...base.fieldEditorOptions,
|
|
79
|
+
...overrides.fieldEditorOptions,
|
|
80
|
+
...base.fieldEditorOptions?.byType === void 0 && overrides.fieldEditorOptions?.byType === void 0 ? {} : { byType: { ...base.fieldEditorOptions?.byType, ...overrides.fieldEditorOptions?.byType } }
|
|
81
|
+
}
|
|
82
|
+
},
|
|
67
83
|
...base.localizationOptions === void 0 && overrides.localizationOptions === void 0 ? {} : { localizationOptions: { ...base.localizationOptions, ...overrides.localizationOptions } },
|
|
68
84
|
...base.muiSlotProps === void 0 && overrides.muiSlotProps === void 0 ? {} : { muiSlotProps: { ...base.muiSlotProps, ...overrides.muiSlotProps } }
|
|
69
85
|
};
|
|
@@ -354,6 +370,7 @@ import {
|
|
|
354
370
|
InputLabel,
|
|
355
371
|
ListItemIcon,
|
|
356
372
|
ListItemText,
|
|
373
|
+
ListSubheader,
|
|
357
374
|
MenuItem,
|
|
358
375
|
Select
|
|
359
376
|
} from "@mui/material";
|
|
@@ -361,6 +378,40 @@ import { Fragment, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
|
361
378
|
function normalizeOptions(options) {
|
|
362
379
|
return options.map((option) => typeof option === "string" ? { value: option, label: option } : option);
|
|
363
380
|
}
|
|
381
|
+
function SelectGroupHeader({ children }) {
|
|
382
|
+
return /* @__PURE__ */ jsx7(ListSubheader, { role: "presentation", children });
|
|
383
|
+
}
|
|
384
|
+
function groupSelectOptions(options) {
|
|
385
|
+
const groups = [];
|
|
386
|
+
const groupIndexes = /* @__PURE__ */ new Map();
|
|
387
|
+
for (const option of options) {
|
|
388
|
+
const groupKey = option.group ?? option.kind;
|
|
389
|
+
const groupLabel = option.groupLabel ?? groupKey;
|
|
390
|
+
if (groupKey === void 0 && groupLabel === void 0) {
|
|
391
|
+
groups.push({ items: [option] });
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
const key = groupKey ?? groupLabel;
|
|
395
|
+
if (key === void 0) {
|
|
396
|
+
groups.push({ items: [option] });
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const existingIndex = groupIndexes.get(key);
|
|
400
|
+
if (existingIndex === void 0) {
|
|
401
|
+
groupIndexes.set(key, groups.length);
|
|
402
|
+
groups.push({
|
|
403
|
+
groupKey: key,
|
|
404
|
+
...groupLabel === void 0 ? {} : { groupLabel },
|
|
405
|
+
items: [option]
|
|
406
|
+
});
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
const existing = groups[existingIndex];
|
|
410
|
+
if (existing === void 0) continue;
|
|
411
|
+
groups[existingIndex] = { ...existing, items: [...existing.items, option] };
|
|
412
|
+
}
|
|
413
|
+
return groups;
|
|
414
|
+
}
|
|
364
415
|
function createMuiSelectAdapter(options) {
|
|
365
416
|
return function MuiSelect({
|
|
366
417
|
id,
|
|
@@ -384,13 +435,14 @@ function createMuiSelectAdapter(options) {
|
|
|
384
435
|
}) {
|
|
385
436
|
const resolved = useResolvedMuiAdapterOptions(options);
|
|
386
437
|
const normalizedOptions = normalizeOptions(selectOptions);
|
|
438
|
+
const groupedOptions = groupSelectOptions(normalizedOptions);
|
|
387
439
|
const selectSlotProps = resolved.muiSlotProps?.select;
|
|
388
440
|
const menuProps = { ...selectSlotProps?.MenuProps, ...resolved.muiSlotProps?.selectMenu };
|
|
389
441
|
const slotInputProps = selectSlotProps?.inputProps;
|
|
390
442
|
const labelId = id === void 0 || label === void 0 ? void 0 : `${id}-label`;
|
|
391
443
|
const helperId = id === void 0 ? void 0 : ariaDescribedBy?.split(/\s+/u)[0] ?? `${id}-helper-text`;
|
|
392
444
|
const describedBy = ariaDescribedBy ?? (helperText === void 0 || helperText.length === 0 ? void 0 : helperId);
|
|
393
|
-
const handleChange = (event) => onChange(
|
|
445
|
+
const handleChange = (event) => onChange(event.target.value);
|
|
394
446
|
return /* @__PURE__ */ jsxs4(
|
|
395
447
|
FormControl2,
|
|
396
448
|
{
|
|
@@ -435,10 +487,22 @@ function createMuiSelectAdapter(options) {
|
|
|
435
487
|
...describedBy === void 0 ? {} : { "aria-describedby": describedBy },
|
|
436
488
|
...ariaLabelledBy === void 0 ? {} : { "aria-labelledby": ariaLabelledBy }
|
|
437
489
|
},
|
|
438
|
-
children:
|
|
439
|
-
|
|
440
|
-
/* @__PURE__ */ jsx7(
|
|
441
|
-
|
|
490
|
+
children: groupedOptions.flatMap((group) => [
|
|
491
|
+
group.groupLabel === void 0 ? null : /* @__PURE__ */ jsx7(SelectGroupHeader, { children: group.groupLabel }, `header-${group.groupKey ?? group.groupLabel}`),
|
|
492
|
+
...group.items.map((option) => /* @__PURE__ */ jsx7(
|
|
493
|
+
MenuItem,
|
|
494
|
+
{
|
|
495
|
+
value: option.value,
|
|
496
|
+
disabled: option.disabled,
|
|
497
|
+
...option.group === void 0 && option.groupLabel === void 0 && option.kind === void 0 ? {} : { "aria-label": option.label },
|
|
498
|
+
children: renderOption === void 0 ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
499
|
+
option.icon === void 0 ? null : /* @__PURE__ */ jsx7(ListItemIcon, { sx: { minWidth: 32 }, children: option.icon }),
|
|
500
|
+
/* @__PURE__ */ jsx7(ListItemText, { primary: option.label, secondary: option.description })
|
|
501
|
+
] }) : renderOption(option)
|
|
502
|
+
},
|
|
503
|
+
option.value
|
|
504
|
+
))
|
|
505
|
+
])
|
|
442
506
|
}
|
|
443
507
|
),
|
|
444
508
|
helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx7(FormHelperText2, { id: helperId, children: helperText })
|
|
@@ -447,7 +511,10 @@ function createMuiSelectAdapter(options) {
|
|
|
447
511
|
);
|
|
448
512
|
};
|
|
449
513
|
}
|
|
450
|
-
|
|
514
|
+
function MuiSelectAdapter(props) {
|
|
515
|
+
const Adapter = createMuiSelectAdapter();
|
|
516
|
+
return /* @__PURE__ */ jsx7(Adapter, { ...props });
|
|
517
|
+
}
|
|
451
518
|
|
|
452
519
|
// src/adapters/TextArea.tsx
|
|
453
520
|
import { TextField } from "@mui/material";
|
|
@@ -685,6 +752,7 @@ function createMuiBuilderComponents(optionsOrOverrides = {}, customOverrides) {
|
|
|
685
752
|
|
|
686
753
|
// src/slots/FieldEditor.tsx
|
|
687
754
|
import { DEFAULT_FIELD_TYPE_DEFINITIONS } from "@form-engine-ts/core";
|
|
755
|
+
import { resolveFieldEditorControls, resolveFieldTypeSelectOptions } from "@form-engine-ts/react";
|
|
688
756
|
import { Card, Stack as Stack3, Typography as Typography3 } from "@mui/material";
|
|
689
757
|
|
|
690
758
|
// src/slots/OptionEditor.tsx
|
|
@@ -857,6 +925,13 @@ function updateBound(field, property, value) {
|
|
|
857
925
|
const { [property]: _removed, ...remaining } = field;
|
|
858
926
|
return remaining;
|
|
859
927
|
}
|
|
928
|
+
function updateNumberProperty(field, property, value) {
|
|
929
|
+
if (field.type !== "number") return field;
|
|
930
|
+
const nextValue = numericValue(value);
|
|
931
|
+
if (nextValue !== void 0) return { ...field, [property]: nextValue };
|
|
932
|
+
const { [property]: _removed, ...remaining } = field;
|
|
933
|
+
return remaining;
|
|
934
|
+
}
|
|
860
935
|
function createMuiFieldEditorSlot(options) {
|
|
861
936
|
const Toolbar = createMuiToolbarSlot(options);
|
|
862
937
|
const OptionEditor = createMuiOptionEditorSlot(options);
|
|
@@ -871,7 +946,9 @@ function createMuiFieldEditorSlot(options) {
|
|
|
871
946
|
actions,
|
|
872
947
|
components,
|
|
873
948
|
translate,
|
|
874
|
-
slots
|
|
949
|
+
slots,
|
|
950
|
+
fieldEditorControls,
|
|
951
|
+
fieldTypeOptions: fieldTypeOptionsConfig
|
|
875
952
|
}) {
|
|
876
953
|
const resolved = useResolvedMuiAdapterOptions(options);
|
|
877
954
|
const { Button: Button2, Checkbox: Checkbox2, Select: Select2, TextArea, TextInput } = components;
|
|
@@ -880,10 +957,41 @@ function createMuiFieldEditorSlot(options) {
|
|
|
880
957
|
const condition = field.displayCondition;
|
|
881
958
|
const conditionSources = schema.fields.slice(0, index);
|
|
882
959
|
const conditionSource = conditionSources.find((source) => source.id === condition?.questionId);
|
|
883
|
-
const
|
|
960
|
+
const fieldEditorOptions = resolved.fieldEditorOptions ?? {};
|
|
961
|
+
const controls = resolveFieldEditorControls({
|
|
962
|
+
...fieldEditorControls ?? {},
|
|
963
|
+
...fieldEditorOptions,
|
|
964
|
+
...fieldEditorOptions.byType?.[field.type] ?? {}
|
|
965
|
+
});
|
|
884
966
|
const titleErrorId = field.title.trim().length === 0 ? `mui-field-${field.id}-title-error` : void 0;
|
|
885
967
|
const FieldTypeSelect = slots?.fieldTypeSelect;
|
|
886
968
|
const FieldEditorHeader = slots?.fieldEditorHeader;
|
|
969
|
+
const fieldTypeSelectId = `mui-field-${field.id}-type`;
|
|
970
|
+
const fieldTypeSelectLabel = translate("builder.type");
|
|
971
|
+
const generatedFieldTypeOptions = FIELD_TYPES.filter(
|
|
972
|
+
(type) => allowedTypes.includes(type)
|
|
973
|
+
).map((type) => {
|
|
974
|
+
const definition = DEFAULT_FIELD_TYPE_DEFINITIONS.find((candidate) => candidate.type === type);
|
|
975
|
+
const group = definition?.category;
|
|
976
|
+
return {
|
|
977
|
+
value: type,
|
|
978
|
+
label: translate(definition?.labelKey ?? `builder.fieldType.${type}`),
|
|
979
|
+
description: translate(`builder.fieldTypeDescription.${type}`),
|
|
980
|
+
icon: components.renderFieldTypeIcon(type),
|
|
981
|
+
...group === void 0 ? {} : {
|
|
982
|
+
group,
|
|
983
|
+
groupLabel: translate(`builder.fieldCategory.${group}`)
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
});
|
|
987
|
+
const fieldTypeOptions = resolveFieldTypeSelectOptions(
|
|
988
|
+
generatedFieldTypeOptions,
|
|
989
|
+
fieldTypeOptionsConfig ?? fieldEditorOptions.fieldTypeOptions,
|
|
990
|
+
{
|
|
991
|
+
currentType: field.type,
|
|
992
|
+
allowedTypes
|
|
993
|
+
}
|
|
994
|
+
);
|
|
887
995
|
const changeFieldType = (nextType) => {
|
|
888
996
|
if (allowedTypes.includes(nextType)) actions.changeFieldType(field.id, nextType);
|
|
889
997
|
};
|
|
@@ -918,7 +1026,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
918
1026
|
] }) : /* @__PURE__ */ jsx13(FieldEditorHeader, { field, index, totalFields: schema.fields.length, actions }),
|
|
919
1027
|
/* @__PURE__ */ jsxs7(Stack3, { ...resolved.muiSlotProps?.stack, spacing: resolved.dense ? 1 : 2, children: [
|
|
920
1028
|
/* @__PURE__ */ jsxs7(Stack3, { direction: { xs: "column", md: "row" }, spacing: resolved.dense ? 1 : 2, children: [
|
|
921
|
-
/* @__PURE__ */ jsx13(
|
|
1029
|
+
controls.title === "hidden" ? null : /* @__PURE__ */ jsx13(
|
|
922
1030
|
TextInput,
|
|
923
1031
|
{
|
|
924
1032
|
id: `mui-field-${field.id}-title`,
|
|
@@ -929,27 +1037,19 @@ function createMuiFieldEditorSlot(options) {
|
|
|
929
1037
|
error: field.title.trim().length === 0,
|
|
930
1038
|
helperText: field.title.trim().length === 0 ? translate("builder.required") : "",
|
|
931
1039
|
"aria-describedby": titleErrorId,
|
|
932
|
-
disabled: readOnly,
|
|
1040
|
+
disabled: readOnly || controls.title === "readOnly",
|
|
933
1041
|
onChange: (value) => actions.setSourceText({ kind: "field", id: field.id }, "title", value)
|
|
934
1042
|
}
|
|
935
1043
|
),
|
|
936
|
-
FieldTypeSelect === void 0 ? /* @__PURE__ */ jsx13(
|
|
1044
|
+
controls.typeSelect === "hidden" ? null : FieldTypeSelect === void 0 ? /* @__PURE__ */ jsx13(
|
|
937
1045
|
Select2,
|
|
938
1046
|
{
|
|
939
|
-
id:
|
|
1047
|
+
id: fieldTypeSelectId,
|
|
940
1048
|
name: `fields.${field.id}.type`,
|
|
941
|
-
label:
|
|
1049
|
+
label: fieldTypeSelectLabel,
|
|
942
1050
|
value: allowedTypes.includes(field.type) ? field.type : "",
|
|
943
|
-
options:
|
|
944
|
-
|
|
945
|
-
return {
|
|
946
|
-
value: type,
|
|
947
|
-
label: translate(definition?.labelKey ?? `builder.fieldType.${type}`),
|
|
948
|
-
icon: components.renderFieldTypeIcon(type),
|
|
949
|
-
...definition?.category === void 0 ? {} : { kind: definition.category }
|
|
950
|
-
};
|
|
951
|
-
}),
|
|
952
|
-
disabled: readOnly,
|
|
1051
|
+
options: fieldTypeOptions,
|
|
1052
|
+
disabled: readOnly || controls.typeSelect === "readOnly",
|
|
953
1053
|
onChange: (value) => {
|
|
954
1054
|
if (isFieldType(value)) changeFieldType(value);
|
|
955
1055
|
}
|
|
@@ -957,16 +1057,21 @@ function createMuiFieldEditorSlot(options) {
|
|
|
957
1057
|
) : /* @__PURE__ */ jsx13(
|
|
958
1058
|
FieldTypeSelect,
|
|
959
1059
|
{
|
|
1060
|
+
id: fieldTypeSelectId,
|
|
1061
|
+
name: `fields.${field.id}.type`,
|
|
1062
|
+
label: fieldTypeSelectLabel,
|
|
960
1063
|
currentType: field.type,
|
|
961
1064
|
allowedTypes,
|
|
1065
|
+
options: fieldTypeOptions,
|
|
962
1066
|
onChangeType: changeFieldType,
|
|
963
|
-
disabled: readOnly,
|
|
964
|
-
readOnly,
|
|
1067
|
+
disabled: readOnly || controls.typeSelect === "readOnly",
|
|
1068
|
+
readOnly: readOnly || controls.typeSelect === "readOnly",
|
|
1069
|
+
"aria-labelledby": `${fieldTypeSelectId}-label`,
|
|
965
1070
|
renderIcon: components.renderFieldTypeIcon
|
|
966
1071
|
}
|
|
967
1072
|
)
|
|
968
1073
|
] }),
|
|
969
|
-
|
|
1074
|
+
controls.description === "hidden" ? null : /* @__PURE__ */ jsx13(
|
|
970
1075
|
TextArea,
|
|
971
1076
|
{
|
|
972
1077
|
id: `mui-field-${field.id}-description`,
|
|
@@ -974,19 +1079,19 @@ function createMuiFieldEditorSlot(options) {
|
|
|
974
1079
|
label: translate("builder.description"),
|
|
975
1080
|
value: field.description ?? "",
|
|
976
1081
|
rows: resolved.dense ? 2 : 3,
|
|
977
|
-
disabled: readOnly,
|
|
978
|
-
readOnly:
|
|
1082
|
+
disabled: readOnly || controls.description === "readOnly",
|
|
1083
|
+
readOnly: readOnly || controls.description === "readOnly",
|
|
979
1084
|
onChange: (value) => actions.setSourceText({ kind: "field", id: field.id }, "description", value)
|
|
980
1085
|
}
|
|
981
1086
|
),
|
|
982
|
-
/* @__PURE__ */ jsx13(
|
|
1087
|
+
controls.required === "hidden" ? null : /* @__PURE__ */ jsx13(
|
|
983
1088
|
Checkbox2,
|
|
984
1089
|
{
|
|
985
1090
|
id: `mui-field-${field.id}-required`,
|
|
986
1091
|
name: `fields.${field.id}.required`,
|
|
987
1092
|
label: translate("builder.required"),
|
|
988
1093
|
checked: field.required,
|
|
989
|
-
disabled: readOnly,
|
|
1094
|
+
disabled: readOnly || controls.required === "readOnly",
|
|
990
1095
|
onChange: (checked) => actions.updateField(field.id, (current) => ({ ...current, required: checked }))
|
|
991
1096
|
}
|
|
992
1097
|
),
|
|
@@ -1004,7 +1109,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1004
1109
|
onChange: (value) => actions.assignFieldToPage(field.id, value.length === 0 ? null : value)
|
|
1005
1110
|
}
|
|
1006
1111
|
),
|
|
1007
|
-
field.type === "number" || field.type === "rating" ? /* @__PURE__ */ jsxs7(Stack3, { direction: { xs: "column", sm: "row" }, spacing: resolved.dense ? 1 : 2, children: [
|
|
1112
|
+
field.type === "number" || field.type === "rating" ? (field.type === "number" ? controls.numberLimits : controls.ratingBounds) === "hidden" ? null : /* @__PURE__ */ jsxs7(Stack3, { direction: { xs: "column", sm: "row" }, spacing: resolved.dense ? 1 : 2, children: [
|
|
1008
1113
|
/* @__PURE__ */ jsx13(
|
|
1009
1114
|
TextInput,
|
|
1010
1115
|
{
|
|
@@ -1012,7 +1117,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1012
1117
|
label: translate("builder.minimum"),
|
|
1013
1118
|
type: "number",
|
|
1014
1119
|
value: field.min === void 0 ? "" : String(field.min),
|
|
1015
|
-
disabled: readOnly,
|
|
1120
|
+
disabled: readOnly || (field.type === "number" ? controls.numberLimits : controls.ratingBounds) === "readOnly",
|
|
1016
1121
|
onChange: (value) => actions.updateField(field.id, (current) => updateBound(current, "min", value))
|
|
1017
1122
|
}
|
|
1018
1123
|
),
|
|
@@ -1023,12 +1128,68 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1023
1128
|
label: translate("builder.maximum"),
|
|
1024
1129
|
type: "number",
|
|
1025
1130
|
value: field.max === void 0 ? "" : String(field.max),
|
|
1026
|
-
disabled: readOnly,
|
|
1131
|
+
disabled: readOnly || (field.type === "number" ? controls.numberLimits : controls.ratingBounds) === "readOnly",
|
|
1027
1132
|
onChange: (value) => actions.updateField(field.id, (current) => updateBound(current, "max", value))
|
|
1028
1133
|
}
|
|
1029
1134
|
)
|
|
1030
1135
|
] }) : null,
|
|
1031
|
-
|
|
1136
|
+
field.type === "number" && controls.numberLimits !== "hidden" ? /* @__PURE__ */ jsx13(
|
|
1137
|
+
TextInput,
|
|
1138
|
+
{
|
|
1139
|
+
id: `mui-field-${field.id}-step`,
|
|
1140
|
+
label: translate("builder.step"),
|
|
1141
|
+
type: "number",
|
|
1142
|
+
value: field.step === void 0 ? "" : String(field.step),
|
|
1143
|
+
disabled: readOnly || controls.numberLimits === "readOnly",
|
|
1144
|
+
onChange: (value) => actions.updateField(field.id, (current) => updateNumberProperty(current, "step", value))
|
|
1145
|
+
}
|
|
1146
|
+
) : null,
|
|
1147
|
+
(field.type === "text" || field.type === "textarea") && controls.textLimits !== "hidden" ? /* @__PURE__ */ jsxs7(Stack3, { direction: { xs: "column", sm: "row" }, spacing: resolved.dense ? 1 : 2, children: [
|
|
1148
|
+
/* @__PURE__ */ jsx13(
|
|
1149
|
+
TextInput,
|
|
1150
|
+
{
|
|
1151
|
+
id: `mui-field-${field.id}-min-length`,
|
|
1152
|
+
label: translate("builder.minimumLength"),
|
|
1153
|
+
type: "number",
|
|
1154
|
+
value: field.minLength === void 0 ? "" : String(field.minLength),
|
|
1155
|
+
disabled: readOnly || controls.textLimits === "readOnly",
|
|
1156
|
+
onChange: (value) => actions.updateField(field.id, (current) => {
|
|
1157
|
+
if (current.type !== "text" && current.type !== "textarea") return current;
|
|
1158
|
+
const parsed = numericValue(value);
|
|
1159
|
+
return parsed === void 0 ? current : { ...current, minLength: Math.max(0, Math.floor(parsed)) };
|
|
1160
|
+
})
|
|
1161
|
+
}
|
|
1162
|
+
),
|
|
1163
|
+
/* @__PURE__ */ jsx13(
|
|
1164
|
+
TextInput,
|
|
1165
|
+
{
|
|
1166
|
+
id: `mui-field-${field.id}-max-length`,
|
|
1167
|
+
label: translate("builder.maximumLength"),
|
|
1168
|
+
type: "number",
|
|
1169
|
+
value: field.maxLength === void 0 ? "" : String(field.maxLength),
|
|
1170
|
+
disabled: readOnly || controls.textLimits === "readOnly",
|
|
1171
|
+
onChange: (value) => actions.updateField(field.id, (current) => {
|
|
1172
|
+
if (current.type !== "text" && current.type !== "textarea") return current;
|
|
1173
|
+
const parsed = numericValue(value);
|
|
1174
|
+
return parsed === void 0 ? current : { ...current, maxLength: Math.max(0, Math.floor(parsed)) };
|
|
1175
|
+
})
|
|
1176
|
+
}
|
|
1177
|
+
),
|
|
1178
|
+
/* @__PURE__ */ jsx13(
|
|
1179
|
+
TextInput,
|
|
1180
|
+
{
|
|
1181
|
+
id: `mui-field-${field.id}-pattern`,
|
|
1182
|
+
label: translate("builder.pattern"),
|
|
1183
|
+
value: field.pattern ?? "",
|
|
1184
|
+
disabled: readOnly || controls.textLimits === "readOnly",
|
|
1185
|
+
onChange: (value) => actions.updateField(
|
|
1186
|
+
field.id,
|
|
1187
|
+
(current) => current.type === "text" || current.type === "textarea" ? value.length === 0 ? current : { ...current, pattern: value } : current
|
|
1188
|
+
)
|
|
1189
|
+
}
|
|
1190
|
+
)
|
|
1191
|
+
] }) : null,
|
|
1192
|
+
features?.conditions === false || conditionSources.length === 0 || controls.displayConditions === "hidden" ? null : /* @__PURE__ */ jsxs7(Stack3, { direction: { xs: "column", md: "row" }, spacing: resolved.dense ? 1 : 2, children: [
|
|
1032
1193
|
/* @__PURE__ */ jsx13(
|
|
1033
1194
|
Select2,
|
|
1034
1195
|
{
|
|
@@ -1039,7 +1200,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1039
1200
|
{ value: "", label: translate("builder.alwaysVisible") },
|
|
1040
1201
|
...conditionSources.map((source) => ({ value: source.id, label: source.title }))
|
|
1041
1202
|
],
|
|
1042
|
-
disabled: readOnly,
|
|
1203
|
+
disabled: readOnly || controls.displayConditions === "readOnly",
|
|
1043
1204
|
onChange: (value) => actions.setDisplayCondition(
|
|
1044
1205
|
field.id,
|
|
1045
1206
|
value.length === 0 ? void 0 : { questionId: value, operator: "equals", value: "" }
|
|
@@ -1056,7 +1217,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1056
1217
|
options: (conditionSource === void 0 ? [] : conditionOperators(conditionSource)).map(
|
|
1057
1218
|
(operator) => ({ value: operator, label: translate(`builder.operator.${operator}`) })
|
|
1058
1219
|
),
|
|
1059
|
-
disabled: readOnly,
|
|
1220
|
+
disabled: readOnly || controls.displayConditions === "readOnly",
|
|
1060
1221
|
onChange: (value) => {
|
|
1061
1222
|
if (!isConditionOperator(value)) return;
|
|
1062
1223
|
actions.setDisplayCondition(
|
|
@@ -1072,7 +1233,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1072
1233
|
id: `mui-field-${field.id}-condition-value`,
|
|
1073
1234
|
label: translate("builder.conditionValue"),
|
|
1074
1235
|
value: condition.value === void 0 ? "" : String(condition.value),
|
|
1075
|
-
disabled: readOnly,
|
|
1236
|
+
disabled: readOnly || controls.displayConditions === "readOnly",
|
|
1076
1237
|
onChange: (value) => actions.setDisplayCondition(field.id, { ...condition, value })
|
|
1077
1238
|
}
|
|
1078
1239
|
)
|
|
@@ -1082,29 +1243,29 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1082
1243
|
/* @__PURE__ */ jsx13(Typography3, { variant: "subtitle2", children: translate("builder.translation", {
|
|
1083
1244
|
locale: resolved.getLocaleLabel?.(currentLocale) ?? currentLocale
|
|
1084
1245
|
}) }),
|
|
1085
|
-
/* @__PURE__ */ jsx13(
|
|
1246
|
+
controls.title === "hidden" ? null : /* @__PURE__ */ jsx13(
|
|
1086
1247
|
TextInput,
|
|
1087
1248
|
{
|
|
1088
1249
|
id: `mui-field-${field.id}-${currentLocale}-title`,
|
|
1089
1250
|
label: translate("builder.translatedQuestionTitle"),
|
|
1090
1251
|
value: field.translations?.[currentLocale]?.title ?? "",
|
|
1091
|
-
disabled: readOnly,
|
|
1252
|
+
disabled: readOnly || controls.title === "readOnly",
|
|
1092
1253
|
onChange: (value) => actions.setManualTranslation(currentLocale, { kind: "field", id: field.id }, "title", value)
|
|
1093
1254
|
}
|
|
1094
1255
|
),
|
|
1095
|
-
|
|
1256
|
+
controls.description === "hidden" ? null : /* @__PURE__ */ jsx13(
|
|
1096
1257
|
TextArea,
|
|
1097
1258
|
{
|
|
1098
1259
|
id: `mui-field-${field.id}-${currentLocale}-description`,
|
|
1099
1260
|
label: translate("builder.translatedDescription"),
|
|
1100
1261
|
value: field.translations?.[currentLocale]?.description ?? "",
|
|
1101
|
-
disabled: readOnly,
|
|
1102
|
-
readOnly:
|
|
1262
|
+
disabled: readOnly || controls.description === "readOnly",
|
|
1263
|
+
readOnly: readOnly || controls.description === "readOnly",
|
|
1103
1264
|
onChange: (value) => actions.setManualTranslation(currentLocale, { kind: "field", id: field.id }, "description", value)
|
|
1104
1265
|
}
|
|
1105
1266
|
)
|
|
1106
1267
|
] }),
|
|
1107
|
-
"options" in field ? /* @__PURE__ */ jsxs7(Stack3, { "data-mui-slot": "options", spacing: resolved.dense ? 1 : 2, children: [
|
|
1268
|
+
"options" in field && controls.options !== "hidden" ? /* @__PURE__ */ jsxs7(Stack3, { "data-mui-slot": "options", spacing: resolved.dense ? 1 : 2, children: [
|
|
1108
1269
|
/* @__PURE__ */ jsx13(Typography3, { variant: "subtitle2", children: translate("builder.options") }),
|
|
1109
1270
|
field.options.map((option, optionIndex) => /* @__PURE__ */ jsx13(
|
|
1110
1271
|
OptionEditor,
|
|
@@ -1115,7 +1276,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1115
1276
|
index: optionIndex,
|
|
1116
1277
|
currentLocale,
|
|
1117
1278
|
translate,
|
|
1118
|
-
readOnly,
|
|
1279
|
+
readOnly: readOnly || controls.options === "readOnly",
|
|
1119
1280
|
actions,
|
|
1120
1281
|
components
|
|
1121
1282
|
},
|
|
@@ -1126,7 +1287,7 @@ function createMuiFieldEditorSlot(options) {
|
|
|
1126
1287
|
{
|
|
1127
1288
|
action: "addOption",
|
|
1128
1289
|
targetId: field.id,
|
|
1129
|
-
disabled: readOnly || policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
|
|
1290
|
+
disabled: readOnly || controls.options === "readOnly" || policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
|
|
1130
1291
|
onClick: () => actions.addOption(field.id),
|
|
1131
1292
|
children: translate("builder.addOption")
|
|
1132
1293
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@form-engine-ts/mui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.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": "
|
|
50
|
-
"@form-engine-ts/react": "
|
|
49
|
+
"@form-engine-ts/core": "4.1.0",
|
|
50
|
+
"@form-engine-ts/react": "4.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": "
|
|
62
|
-
"@form-engine-ts/react": "
|
|
61
|
+
"@form-engine-ts/core": "4.1.0",
|
|
62
|
+
"@form-engine-ts/react": "4.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",
|