@form-engine-ts/react 2.1.0 → 2.1.1

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/dist/index.cjs CHANGED
@@ -23,6 +23,7 @@ __export(index_exports, {
23
23
  FormBuilder: () => FormBuilder,
24
24
  FormProvider: () => FormProvider,
25
25
  FormRenderer: () => FormRenderer,
26
+ resolveInitialFieldType: () => resolveInitialFieldType,
26
27
  useField: () => useField,
27
28
  useForm: () => useForm,
28
29
  useFormBuilder: () => useFormBuilder
@@ -774,6 +775,14 @@ function ConditionValueEditor({
774
775
  }
775
776
  );
776
777
  }
778
+ function resolveInitialFieldType(defaultType, allowedTypes) {
779
+ if (defaultType !== void 0 && (allowedTypes === void 0 || allowedTypes.includes(defaultType))) {
780
+ return defaultType;
781
+ }
782
+ if (allowedTypes !== void 0 && allowedTypes.length > 0) return allowedTypes[0] ?? null;
783
+ if (allowedTypes === void 0 || allowedTypes.includes("text")) return "text";
784
+ return null;
785
+ }
777
786
  function FormBuilder({
778
787
  schema,
779
788
  onChange,
@@ -784,7 +793,11 @@ function FormBuilder({
784
793
  onTranslationReport,
785
794
  policy,
786
795
  idFactory,
787
- factories
796
+ factories,
797
+ className = "",
798
+ defaultFieldType,
799
+ onActionError,
800
+ createManualTranslationMetadata
788
801
  }) {
789
802
  const headless = useFormBuilder({
790
803
  schema,
@@ -802,17 +815,40 @@ function FormBuilder({
802
815
  const translated = translator?.translate(key, locale, params);
803
816
  return translated === void 0 ? interpolate(BUILDER_DEFAULTS[key] ?? key, params) : translated;
804
817
  };
805
- const updateField = headless.updateField;
806
- const changeType = headless.changeFieldType;
807
- const removeField = headless.removeField;
818
+ const executeAction = (result, context) => {
819
+ if (!result.success) onActionError?.(result.error, context);
820
+ return result;
821
+ };
822
+ const updateField = (fieldId, updater, params) => executeAction(headless.updateField(fieldId, updater), {
823
+ action: "updateField",
824
+ targetId: fieldId,
825
+ ...params === void 0 ? {} : { params }
826
+ });
827
+ const changeType = (fieldId, type) => executeAction(headless.changeFieldType(fieldId, type), {
828
+ action: "changeFieldType",
829
+ targetId: fieldId,
830
+ params: { type }
831
+ });
832
+ const removeField = (fieldId) => executeAction(headless.removeField(fieldId), { action: "removeField", targetId: fieldId });
833
+ const initialFieldType = resolveInitialFieldType(defaultFieldType, policy?.allowedFieldTypes);
834
+ const maxFieldsReached = policy?.maxFields !== void 0 && schema.fields.length >= policy.maxFields;
808
835
  const moveField = (index, offset) => {
809
836
  const target = index + offset;
810
837
  const field = schema.fields[index];
811
- if (field !== void 0) headless.moveField(field.id, target);
838
+ if (field !== void 0) {
839
+ executeAction(headless.moveField(field.id, target), {
840
+ action: "moveField",
841
+ targetId: field.id,
842
+ params: { targetIndex: target }
843
+ });
844
+ }
845
+ };
846
+ const addField = () => {
847
+ if (initialFieldType === null) return;
848
+ executeAction(headless.addField(initialFieldType), { action: "addField" });
812
849
  };
813
- const addField = () => headless.addField("text");
814
850
  const enablePages = () => {
815
- if (schema.pages === void 0) headless.addPage();
851
+ if (schema.pages === void 0) executeAction(headless.addPage(), { action: "addPage" });
816
852
  };
817
853
  const pageForField = (fieldId) => schema.pages?.find((page) => page.questionIds.includes(fieldId));
818
854
  const movablePageQuestions = schema.pages?.flatMap((page) => page.questionIds.length > 1 ? page.questionIds : []) ?? [];
@@ -823,23 +859,37 @@ function FormBuilder({
823
859
  }
824
860
  const questionId = movablePageQuestions.includes(newPageQuestionId) ? newPageQuestionId : movablePageQuestions[0];
825
861
  if (questionId === void 0) return;
826
- headless.addPage(questionId);
862
+ executeAction(headless.addPage(questionId), {
863
+ action: "addPage",
864
+ targetId: questionId,
865
+ params: { questionId }
866
+ });
827
867
  setNewPageQuestionId("");
828
868
  };
829
869
  const removePage = (pageIndex) => {
830
870
  const page = schema.pages?.[pageIndex];
831
- if (page !== void 0) headless.removePage(page.id);
871
+ if (page !== void 0) executeAction(headless.removePage(page.id), { action: "removePage", targetId: page.id });
832
872
  };
833
873
  const movePage = (pageIndex, offset) => {
834
874
  const target = pageIndex + offset;
835
875
  const page = schema.pages?.[pageIndex];
836
- if (page !== void 0) headless.movePage(page.id, target);
876
+ if (page !== void 0) {
877
+ executeAction(headless.movePage(page.id, target), {
878
+ action: "movePage",
879
+ targetId: page.id,
880
+ params: { targetIndex: target }
881
+ });
882
+ }
837
883
  };
838
884
  const updatePage = (pageId, update) => {
839
885
  headless.updatePage(pageId, update);
840
886
  };
841
887
  const assignFieldToPage = (fieldId, pageId) => {
842
- headless.assignFieldToPage(fieldId, pageId);
888
+ executeAction(headless.assignFieldToPage(fieldId, pageId), {
889
+ action: "assignFieldToPage",
890
+ targetId: fieldId,
891
+ params: { pageId }
892
+ });
843
893
  };
844
894
  const addLocale = () => {
845
895
  const normalized = newLocale.trim();
@@ -853,12 +903,10 @@ function FormBuilder({
853
903
  setIsTranslating(true);
854
904
  setTranslationError(null);
855
905
  try {
856
- const populated = await (0, import_core2.populateSchemaTranslations)(
857
- schema,
858
- [editingLocale],
859
- translationAdapter,
860
- translationOptions ?? { overwrite: "all" }
861
- );
906
+ const populated = await (0, import_core2.populateSchemaTranslations)(schema, [editingLocale], translationAdapter, {
907
+ overwrite: "missing-only",
908
+ ...translationOptions
909
+ });
862
910
  onChange(populated.schema);
863
911
  onTranslationReport?.(populated.report);
864
912
  } catch (cause) {
@@ -867,28 +915,366 @@ function FormBuilder({
867
915
  setIsTranslating(false);
868
916
  }
869
917
  };
870
- const updateFormTranslation = (key, value) => {
918
+ const updateManualTranslation = (context) => {
919
+ const metadata = createManualTranslationMetadata?.(context);
920
+ const target = context.kind === "form" ? { kind: "form" } : { kind: context.kind, id: context.nodeId };
921
+ executeAction(
922
+ headless.setLocaleTranslation(
923
+ context.locale,
924
+ target,
925
+ context.property,
926
+ context.translatedText,
927
+ metadata === void 0 ? void 0 : { metadata }
928
+ ),
929
+ {
930
+ action: "setLocaleTranslation",
931
+ targetId: context.nodeId,
932
+ params: { locale: context.locale, kind: context.kind, property: context.property }
933
+ }
934
+ );
935
+ };
936
+ const updateFormTranslation = (property, translatedText) => {
871
937
  if (editingLocale.length === 0) return;
872
- headless.setLocaleTranslation(editingLocale, { kind: "form" }, key, value);
938
+ updateManualTranslation({
939
+ locale: editingLocale,
940
+ kind: "form",
941
+ nodeId: schema.id,
942
+ property,
943
+ sourceText: schema[property] ?? "",
944
+ translatedText,
945
+ ...schema.translationMetadata?.[editingLocale]?.[property] === void 0 ? {} : { existingTranslationMetadata: schema.translationMetadata[editingLocale]?.[property] }
946
+ });
873
947
  };
874
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "form-engine-builder", "aria-label": translate("builder.formBuilder"), children: [
875
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "form-engine-builder__pages", "aria-labelledby": "builder-pages-heading", children: [
876
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { id: "builder-pages-heading", children: translate("builder.pages") }),
877
- schema.pages === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: enablePages, children: translate("builder.enablePages") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
878
- schema.pages.map((page, pageIndex) => {
879
- const priorQuestionIds = new Set(schema.pages?.slice(0, pageIndex).flatMap((item) => item.questionIds));
880
- const availableSources = schema.fields.filter((field) => priorQuestionIds.has(field.id));
881
- const source = schema.fields.find((field) => field.id === page.displayCondition?.questionId);
882
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { className: "form-engine-builder__page", children: [
883
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("legend", { children: page.title ?? `${translate("builder.newPage")} ${pageIndex + 1}` }),
948
+ const setSourceText = (target, property, text) => executeAction(headless.setSourceText(target, property, text), {
949
+ action: "setSourceText",
950
+ ...target.id === void 0 ? {} : { targetId: target.id },
951
+ params: { kind: target.kind, property }
952
+ });
953
+ const updateOption = (fieldId, optionId, label) => executeAction(
954
+ headless.updateOption(fieldId, optionId, (option) => ({ ...option, label })),
955
+ { action: "updateOption", targetId: optionId, params: { fieldId } }
956
+ );
957
+ const addOption = (fieldId) => executeAction(headless.addOption(fieldId), { action: "addOption", targetId: fieldId });
958
+ const removeOption = (fieldId, optionId) => executeAction(headless.removeOption(fieldId, optionId), {
959
+ action: "removeOption",
960
+ targetId: optionId,
961
+ params: { fieldId }
962
+ });
963
+ const moveOption = (fieldId, optionId, targetIndex) => executeAction(headless.moveOption(fieldId, optionId, targetIndex), {
964
+ action: "moveOption",
965
+ targetId: optionId,
966
+ params: { fieldId, targetIndex }
967
+ });
968
+ const setDisplayCondition = (fieldId, condition) => executeAction(headless.setDisplayCondition(fieldId, condition), {
969
+ action: "setDisplayCondition",
970
+ targetId: fieldId,
971
+ ...condition === void 0 ? {} : { params: { condition } }
972
+ });
973
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
974
+ "section",
975
+ {
976
+ className: `form-engine-builder ${className}`.trim(),
977
+ "aria-label": translate("builder.formBuilder"),
978
+ onClickCapture: (event) => {
979
+ if (!(event.target instanceof HTMLElement)) return;
980
+ const actionTarget = event.target.closest("[data-builder-action]");
981
+ if (actionTarget?.dataset.builderAction === "addField" && maxFieldsReached && initialFieldType !== null)
982
+ addField();
983
+ if (actionTarget?.dataset.builderAction !== "addOption") return;
984
+ const fieldId = actionTarget.dataset.targetId;
985
+ const field = schema.fields.find((candidate) => candidate.id === fieldId);
986
+ if (fieldId !== void 0 && field !== void 0 && "options" in field && policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField) {
987
+ addOption(fieldId);
988
+ }
989
+ },
990
+ children: [
991
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "form-engine-builder__pages", "aria-labelledby": "builder-pages-heading", children: [
992
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { id: "builder-pages-heading", children: translate("builder.pages") }),
993
+ schema.pages === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: enablePages, children: translate("builder.enablePages") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
994
+ schema.pages.map((page, pageIndex) => {
995
+ const priorQuestionIds = new Set(schema.pages?.slice(0, pageIndex).flatMap((item) => item.questionIds));
996
+ const availableSources = schema.fields.filter((field) => priorQuestionIds.has(field.id));
997
+ const source = schema.fields.find((field) => field.id === page.displayCondition?.questionId);
998
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { className: "form-engine-builder__page", children: [
999
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("legend", { children: page.title ?? `${translate("builder.newPage")} ${pageIndex + 1}` }),
1000
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__toolbar", children: [
1001
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1002
+ "button",
1003
+ {
1004
+ type: "button",
1005
+ disabled: pageIndex === 0,
1006
+ "aria-label": translate("builder.moveUp", { title: page.title ?? page.id }),
1007
+ onClick: () => movePage(pageIndex, -1),
1008
+ children: "\u2191"
1009
+ }
1010
+ ),
1011
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1012
+ "button",
1013
+ {
1014
+ type: "button",
1015
+ disabled: pageIndex === (schema.pages?.length ?? 0) - 1,
1016
+ "aria-label": translate("builder.moveDown", { title: page.title ?? page.id }),
1017
+ onClick: () => movePage(pageIndex, 1),
1018
+ children: "\u2193"
1019
+ }
1020
+ ),
1021
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => removePage(pageIndex), children: translate("builder.deleteAction") })
1022
+ ] }),
1023
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1024
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1025
+ translate("builder.pageTitle"),
1026
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1027
+ "input",
1028
+ {
1029
+ value: page.title ?? "",
1030
+ onChange: (event) => {
1031
+ const value = event.currentTarget.value;
1032
+ updatePage(page.id, (current) => {
1033
+ if (value.length > 0) return { ...current, title: value };
1034
+ const { title: _title, ...withoutTitle } = current;
1035
+ return withoutTitle;
1036
+ });
1037
+ }
1038
+ }
1039
+ )
1040
+ ] }),
1041
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1042
+ translate("builder.pageDescription"),
1043
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1044
+ "input",
1045
+ {
1046
+ value: page.description ?? "",
1047
+ onChange: (event) => {
1048
+ const value = event.currentTarget.value;
1049
+ updatePage(page.id, (current) => {
1050
+ if (value.length > 0) return { ...current, description: value };
1051
+ const { description: _description, ...withoutDescription } = current;
1052
+ return withoutDescription;
1053
+ });
1054
+ }
1055
+ }
1056
+ )
1057
+ ] })
1058
+ ] }),
1059
+ editingLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__translation-editor", children: [
1060
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: editingLocale }),
1061
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1062
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1063
+ translate("builder.pageTitle"),
1064
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1065
+ "input",
1066
+ {
1067
+ value: page.translations?.[editingLocale]?.title ?? "",
1068
+ onChange: (event) => updateManualTranslation({
1069
+ locale: editingLocale,
1070
+ kind: "page",
1071
+ nodeId: page.id,
1072
+ property: "title",
1073
+ sourceText: page.title ?? "",
1074
+ translatedText: event.currentTarget.value,
1075
+ ...page.translationMetadata?.[editingLocale]?.title === void 0 ? {} : {
1076
+ existingTranslationMetadata: page.translationMetadata[editingLocale]?.title
1077
+ }
1078
+ })
1079
+ }
1080
+ )
1081
+ ] }),
1082
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1083
+ translate("builder.pageDescription"),
1084
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1085
+ "input",
1086
+ {
1087
+ value: page.translations?.[editingLocale]?.description ?? "",
1088
+ onChange: (event) => updateManualTranslation({
1089
+ locale: editingLocale,
1090
+ kind: "page",
1091
+ nodeId: page.id,
1092
+ property: "description",
1093
+ sourceText: page.description ?? "",
1094
+ translatedText: event.currentTarget.value,
1095
+ ...page.translationMetadata?.[editingLocale]?.description === void 0 ? {} : {
1096
+ existingTranslationMetadata: page.translationMetadata[editingLocale]?.description
1097
+ }
1098
+ })
1099
+ }
1100
+ )
1101
+ ] })
1102
+ ] })
1103
+ ] }),
1104
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__condition", children: [
1105
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1106
+ translate("builder.pageCondition"),
1107
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1108
+ "select",
1109
+ {
1110
+ value: page.displayCondition?.questionId ?? "",
1111
+ onChange: (event) => {
1112
+ const selected = schema.fields.find((field) => field.id === event.currentTarget.value);
1113
+ updatePage(page.id, (current) => {
1114
+ if (selected === void 0) {
1115
+ const { displayCondition: _condition, ...withoutCondition } = current;
1116
+ return withoutCondition;
1117
+ }
1118
+ return {
1119
+ ...current,
1120
+ displayCondition: conditionWithValue(
1121
+ selected.id,
1122
+ conditionOperators(selected)[0] ?? "not_empty",
1123
+ defaultConditionValue(selected)
1124
+ )
1125
+ };
1126
+ });
1127
+ },
1128
+ children: [
1129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: translate("builder.alwaysVisible") }),
1130
+ availableSources.map((field) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: field.id, children: field.title }, field.id))
1131
+ ]
1132
+ }
1133
+ )
1134
+ ] }),
1135
+ page.displayCondition !== void 0 && source !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1136
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1137
+ "select",
1138
+ {
1139
+ "aria-label": translate("builder.conditionOperator"),
1140
+ value: page.displayCondition.operator,
1141
+ onChange: (event) => {
1142
+ const operator = event.currentTarget.value;
1143
+ updatePage(page.id, (current) => ({
1144
+ ...current,
1145
+ displayCondition: conditionWithValue(source.id, operator, defaultConditionValue(source))
1146
+ }));
1147
+ },
1148
+ children: conditionOperators(source).map((operator) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: operator, children: translate(operatorKey(operator)) }, operator))
1149
+ }
1150
+ ),
1151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1152
+ ConditionValueEditor,
1153
+ {
1154
+ source,
1155
+ condition: page.displayCondition,
1156
+ onChange: (condition) => updatePage(page.id, (current) => ({ ...current, displayCondition: condition })),
1157
+ translate
1158
+ }
1159
+ )
1160
+ ] }) : null
1161
+ ] })
1162
+ ] }, page.id);
1163
+ }),
1164
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__page-add", children: [
1165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1166
+ translate("builder.pageQuestion"),
1167
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1168
+ "select",
1169
+ {
1170
+ value: newPageQuestionId,
1171
+ disabled: movablePageQuestions.length === 0,
1172
+ onChange: (event) => setNewPageQuestionId(event.currentTarget.value),
1173
+ children: [
1174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u2014" }),
1175
+ schema.fields.filter((field) => movablePageQuestions.includes(field.id)).map((field) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: field.id, children: field.title }, field.id))
1176
+ ]
1177
+ }
1178
+ )
1179
+ ] }),
1180
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: movablePageQuestions.length === 0, onClick: addPage, children: translate("builder.addPage") })
1181
+ ] })
1182
+ ] })
1183
+ ] }),
1184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "form-engine-builder__localization", "aria-labelledby": "builder-localization-heading", children: [
1185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { id: "builder-localization-heading", children: translate("builder.localization") }),
1186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1187
+ translate("builder.completionMessage"),
1188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1189
+ "input",
1190
+ {
1191
+ value: schema.completionMessage ?? "",
1192
+ onChange: (event) => setSourceText({ kind: "form" }, "completionMessage", event.currentTarget.value)
1193
+ }
1194
+ )
1195
+ ] }),
1196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1197
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1198
+ translate("builder.defaultLocale"),
1199
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1200
+ "input",
1201
+ {
1202
+ value: schema.defaultLocale ?? "",
1203
+ onChange: (event) => headless.setDefaultLocale(event.currentTarget.value)
1204
+ }
1205
+ )
1206
+ ] }),
1207
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1208
+ translate("builder.addLocale"),
1209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { value: newLocale, onChange: (event) => setNewLocale(event.currentTarget.value) })
1210
+ ] }),
1211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: addLocale, children: translate("builder.addLocale") }),
1212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1213
+ translate("builder.editLocale"),
1214
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: editingLocale, onChange: (event) => setEditingLocale(event.currentTarget.value), children: [
1215
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u2014" }),
1216
+ (schema.supportedLocales ?? []).filter((item) => item !== schema.defaultLocale).map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: item, children: item }, item))
1217
+ ] })
1218
+ ] }),
1219
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1220
+ "button",
1221
+ {
1222
+ type: "button",
1223
+ disabled: translationAdapter === void 0 || editingLocale.length === 0 || isTranslating,
1224
+ onClick: () => void translateAll(),
1225
+ children: translate("builder.autoTranslate")
1226
+ }
1227
+ )
1228
+ ] }),
1229
+ translationAdapter === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: translate("builder.translationUnavailable") }) : null,
1230
+ translationError === null ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "form-engine-builder__error", children: translationError }),
1231
+ editingLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1232
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1233
+ translate("builder.questionTitle"),
1234
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1235
+ "input",
1236
+ {
1237
+ value: schema.translations?.[editingLocale]?.title ?? "",
1238
+ onChange: (event) => updateFormTranslation("title", event.currentTarget.value)
1239
+ }
1240
+ )
1241
+ ] }),
1242
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1243
+ translate("builder.pageDescription"),
1244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1245
+ "input",
1246
+ {
1247
+ value: schema.translations?.[editingLocale]?.description ?? "",
1248
+ onChange: (event) => updateFormTranslation("description", event.currentTarget.value)
1249
+ }
1250
+ )
1251
+ ] }),
1252
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1253
+ translate("builder.completionMessage"),
1254
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1255
+ "input",
1256
+ {
1257
+ value: schema.translations?.[editingLocale]?.completionMessage ?? "",
1258
+ onChange: (event) => updateFormTranslation("completionMessage", event.currentTarget.value)
1259
+ }
1260
+ )
1261
+ ] })
1262
+ ] })
1263
+ ] }),
1264
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "form-engine-builder__list", children: schema.fields.map((field, index) => {
1265
+ const condition = field.displayCondition;
1266
+ const source = condition === void 0 ? void 0 : schema.fields.find((item) => item.id === condition.questionId);
1267
+ const availableSources = schema.fields.slice(0, index);
1268
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { className: "form-engine-builder__question", children: [
1269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("legend", { children: field.title }),
884
1270
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__toolbar", children: [
885
1271
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
886
1272
  "button",
887
1273
  {
888
1274
  type: "button",
889
- disabled: pageIndex === 0,
890
- "aria-label": translate("builder.moveUp", { title: page.title ?? page.id }),
891
- onClick: () => movePage(pageIndex, -1),
1275
+ disabled: index === 0,
1276
+ onClick: () => moveField(index, -1),
1277
+ "aria-label": translate("builder.moveUp", { title: field.title }),
892
1278
  children: "\u2191"
893
1279
  }
894
1280
  ),
@@ -896,65 +1282,94 @@ function FormBuilder({
896
1282
  "button",
897
1283
  {
898
1284
  type: "button",
899
- disabled: pageIndex === (schema.pages?.length ?? 0) - 1,
900
- "aria-label": translate("builder.moveDown", { title: page.title ?? page.id }),
901
- onClick: () => movePage(pageIndex, 1),
1285
+ disabled: index === schema.fields.length - 1,
1286
+ onClick: () => moveField(index, 1),
1287
+ "aria-label": translate("builder.moveDown", { title: field.title }),
902
1288
  children: "\u2193"
903
1289
  }
904
1290
  ),
905
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => removePage(pageIndex), children: translate("builder.deleteAction") })
1291
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1292
+ "button",
1293
+ {
1294
+ type: "button",
1295
+ disabled: schema.fields.length === 1,
1296
+ onClick: () => removeField(field.id),
1297
+ "aria-label": translate("builder.delete", { title: field.title }),
1298
+ children: translate("builder.deleteAction")
1299
+ }
1300
+ )
906
1301
  ] }),
907
1302
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
908
1303
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
909
- translate("builder.pageTitle"),
1304
+ translate("builder.questionTitle"),
910
1305
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
911
1306
  "input",
912
1307
  {
913
- value: page.title ?? "",
914
- onChange: (event) => {
915
- const value = event.currentTarget.value;
916
- updatePage(page.id, (current) => {
917
- if (value.length > 0) return { ...current, title: value };
918
- const { title: _title, ...withoutTitle } = current;
919
- return withoutTitle;
920
- });
921
- }
1308
+ value: field.title,
1309
+ placeholder: translate("builder.questionTitlePlaceholder"),
1310
+ onChange: (event) => updateField(field.id, (current) => ({
1311
+ ...current,
1312
+ title: event.currentTarget.value.trim().length === 0 ? current.title : event.currentTarget.value
1313
+ }))
922
1314
  }
923
1315
  )
924
1316
  ] }),
925
1317
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
926
- translate("builder.pageDescription"),
1318
+ translate("builder.type"),
927
1319
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
928
- "input",
1320
+ "select",
929
1321
  {
930
- value: page.description ?? "",
931
- onChange: (event) => {
932
- const value = event.currentTarget.value;
933
- updatePage(page.id, (current) => {
934
- if (value.length > 0) return { ...current, description: value };
935
- const { description: _description, ...withoutDescription } = current;
936
- return withoutDescription;
937
- });
938
- }
1322
+ value: field.type,
1323
+ onChange: (event) => changeType(field.id, event.currentTarget.value),
1324
+ children: FIELD_TYPES.filter(
1325
+ (type) => policy?.allowedFieldTypes === void 0 || policy.allowedFieldTypes.includes(type)
1326
+ ).map((type) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: type, children: translate(fieldTypeKey(type)) }, type))
939
1327
  }
940
1328
  )
1329
+ ] }),
1330
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "form-engine-builder__check", children: [
1331
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1332
+ "input",
1333
+ {
1334
+ type: "checkbox",
1335
+ checked: field.required === true,
1336
+ onChange: (event) => updateField(field.id, (current) => ({ ...current, required: event.currentTarget.checked }))
1337
+ }
1338
+ ),
1339
+ translate("builder.required")
941
1340
  ] })
942
1341
  ] }),
1342
+ schema.pages === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1343
+ translate("builder.questionPage"),
1344
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1345
+ "select",
1346
+ {
1347
+ value: pageForField(field.id)?.id ?? "",
1348
+ onChange: (event) => assignFieldToPage(field.id, event.currentTarget.value),
1349
+ children: schema.pages.map((page, pageIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: page.id, children: page.title ?? `${translate("builder.newPage")} ${pageIndex + 1}` }, page.id))
1350
+ }
1351
+ )
1352
+ ] }),
943
1353
  editingLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__translation-editor", children: [
944
1354
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: editingLocale }),
945
1355
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
946
1356
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
947
- translate("builder.pageTitle"),
1357
+ translate("builder.questionTitle"),
948
1358
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
949
1359
  "input",
950
1360
  {
951
- value: page.translations?.[editingLocale]?.title ?? "",
952
- onChange: (event) => headless.setLocaleTranslation(
953
- editingLocale,
954
- { kind: "page", id: page.id },
955
- "title",
956
- event.currentTarget.value
957
- )
1361
+ value: field.translations?.[editingLocale]?.title ?? "",
1362
+ onChange: (event) => updateManualTranslation({
1363
+ locale: editingLocale,
1364
+ kind: "field",
1365
+ nodeId: field.id,
1366
+ property: "title",
1367
+ sourceText: field.title,
1368
+ translatedText: event.currentTarget.value,
1369
+ ...field.translationMetadata?.[editingLocale]?.title === void 0 ? {} : {
1370
+ existingTranslationMetadata: field.translationMetadata[editingLocale]?.title
1371
+ }
1372
+ })
958
1373
  }
959
1374
  )
960
1375
  ] }),
@@ -963,61 +1378,175 @@ function FormBuilder({
963
1378
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
964
1379
  "input",
965
1380
  {
966
- value: page.translations?.[editingLocale]?.description ?? "",
967
- onChange: (event) => headless.setLocaleTranslation(
968
- editingLocale,
969
- { kind: "page", id: page.id },
970
- "description",
971
- event.currentTarget.value
972
- )
1381
+ value: field.translations?.[editingLocale]?.description ?? "",
1382
+ onChange: (event) => updateManualTranslation({
1383
+ locale: editingLocale,
1384
+ kind: "field",
1385
+ nodeId: field.id,
1386
+ property: "description",
1387
+ sourceText: field.description ?? "",
1388
+ translatedText: event.currentTarget.value,
1389
+ ...field.translationMetadata?.[editingLocale]?.description === void 0 ? {} : {
1390
+ existingTranslationMetadata: field.translationMetadata[editingLocale]?.description
1391
+ }
1392
+ })
973
1393
  }
974
1394
  )
975
1395
  ] })
976
- ] })
1396
+ ] }),
1397
+ "options" in field ? field.options.map((option, optionIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1398
+ translate("builder.optionLabel", { index: optionIndex + 1 }),
1399
+ " (",
1400
+ editingLocale,
1401
+ ")",
1402
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1403
+ "input",
1404
+ {
1405
+ value: option.translations?.[editingLocale] ?? "",
1406
+ onChange: (event) => updateManualTranslation({
1407
+ locale: editingLocale,
1408
+ kind: "option",
1409
+ nodeId: option.id,
1410
+ property: "label",
1411
+ sourceText: option.label,
1412
+ translatedText: event.currentTarget.value,
1413
+ ...option.translationMetadata?.[editingLocale]?.label === void 0 ? {} : {
1414
+ existingTranslationMetadata: option.translationMetadata[editingLocale]?.label
1415
+ }
1416
+ })
1417
+ }
1418
+ )
1419
+ ] }, option.id)) : null
977
1420
  ] }),
1421
+ field.type === "rating" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1422
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1423
+ translate("builder.minimum"),
1424
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1425
+ "input",
1426
+ {
1427
+ type: "number",
1428
+ value: field.min ?? 1,
1429
+ onChange: (event) => {
1430
+ const min = event.currentTarget.valueAsNumber;
1431
+ if (!Number.isInteger(min)) return;
1432
+ updateField(
1433
+ field.id,
1434
+ (current) => current.type === "rating" ? { ...current, min, max: Math.max(min, current.max ?? 5) } : current
1435
+ );
1436
+ }
1437
+ }
1438
+ )
1439
+ ] }),
1440
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1441
+ translate("builder.maximum"),
1442
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1443
+ "input",
1444
+ {
1445
+ type: "number",
1446
+ value: field.max ?? 5,
1447
+ onChange: (event) => {
1448
+ const max = event.currentTarget.valueAsNumber;
1449
+ if (!Number.isInteger(max)) return;
1450
+ updateField(
1451
+ field.id,
1452
+ (current) => current.type === "rating" ? { ...current, min: Math.min(current.min ?? 1, max), max } : current
1453
+ );
1454
+ }
1455
+ }
1456
+ )
1457
+ ] })
1458
+ ] }) : null,
1459
+ "options" in field ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__options", children: [
1460
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: translate("builder.options") }),
1461
+ field.options.map((option, optionIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__option", children: [
1462
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1463
+ "input",
1464
+ {
1465
+ "aria-label": translate("builder.optionLabel", { index: optionIndex + 1 }),
1466
+ value: option.label,
1467
+ placeholder: translate("builder.optionLabelPlaceholder"),
1468
+ onChange: (event) => event.currentTarget.value.trim().length === 0 ? void 0 : updateOption(field.id, option.id, event.currentTarget.value)
1469
+ }
1470
+ ),
1471
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1472
+ "button",
1473
+ {
1474
+ type: "button",
1475
+ disabled: optionIndex === 0,
1476
+ "aria-label": translate("builder.moveUp", { title: option.label }),
1477
+ onClick: () => moveOption(field.id, option.id, optionIndex - 1),
1478
+ children: "\u2191"
1479
+ }
1480
+ ),
1481
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1482
+ "button",
1483
+ {
1484
+ type: "button",
1485
+ disabled: optionIndex === field.options.length - 1,
1486
+ "aria-label": translate("builder.moveDown", { title: option.label }),
1487
+ onClick: () => moveOption(field.id, option.id, optionIndex + 1),
1488
+ children: "\u2193"
1489
+ }
1490
+ ),
1491
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1492
+ "button",
1493
+ {
1494
+ type: "button",
1495
+ disabled: field.options.length === 1,
1496
+ onClick: () => removeOption(field.id, option.id),
1497
+ children: translate("builder.remove")
1498
+ }
1499
+ )
1500
+ ] }, option.id)),
1501
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1502
+ "button",
1503
+ {
1504
+ type: "button",
1505
+ "data-builder-action": "addOption",
1506
+ "data-target-id": field.id,
1507
+ disabled: policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
1508
+ onClick: () => addOption(field.id),
1509
+ children: translate("builder.addOption")
1510
+ }
1511
+ )
1512
+ ] }) : null,
978
1513
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__condition", children: [
979
1514
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
980
- translate("builder.pageCondition"),
1515
+ translate("builder.displayCondition"),
981
1516
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
982
1517
  "select",
983
1518
  {
984
- value: page.displayCondition?.questionId ?? "",
1519
+ value: condition?.questionId ?? "",
985
1520
  onChange: (event) => {
986
- const selected = schema.fields.find((field) => field.id === event.currentTarget.value);
987
- updatePage(page.id, (current) => {
988
- if (selected === void 0) {
989
- const { displayCondition: _condition, ...withoutCondition } = current;
990
- return withoutCondition;
991
- }
992
- return {
993
- ...current,
994
- displayCondition: conditionWithValue(
995
- selected.id,
996
- conditionOperators(selected)[0] ?? "not_empty",
997
- defaultConditionValue(selected)
998
- )
999
- };
1000
- });
1521
+ const selected = schema.fields.find((item) => item.id === event.currentTarget.value);
1522
+ setDisplayCondition(
1523
+ field.id,
1524
+ selected === void 0 ? void 0 : conditionWithValue(
1525
+ selected.id,
1526
+ conditionOperators(selected)[0] ?? "not_empty",
1527
+ defaultConditionValue(selected)
1528
+ )
1529
+ );
1001
1530
  },
1002
1531
  children: [
1003
1532
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: translate("builder.alwaysVisible") }),
1004
- availableSources.map((field) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: field.id, children: field.title }, field.id))
1533
+ availableSources.map((candidate) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: candidate.id, children: candidate.title }, candidate.id))
1005
1534
  ]
1006
1535
  }
1007
1536
  )
1008
1537
  ] }),
1009
- page.displayCondition !== void 0 && source !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1538
+ condition !== void 0 && source !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1010
1539
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1011
1540
  "select",
1012
1541
  {
1013
1542
  "aria-label": translate("builder.conditionOperator"),
1014
- value: page.displayCondition.operator,
1543
+ value: condition.operator,
1015
1544
  onChange: (event) => {
1016
1545
  const operator = event.currentTarget.value;
1017
- updatePage(page.id, (current) => ({
1018
- ...current,
1019
- displayCondition: conditionWithValue(source.id, operator, defaultConditionValue(source))
1020
- }));
1546
+ setDisplayCondition(
1547
+ field.id,
1548
+ conditionWithValue(source.id, operator, defaultConditionValue(source))
1549
+ );
1021
1550
  },
1022
1551
  children: conditionOperators(source).map((operator) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: operator, children: translate(operatorKey(operator)) }, operator))
1023
1552
  }
@@ -1026,395 +1555,29 @@ function FormBuilder({
1026
1555
  ConditionValueEditor,
1027
1556
  {
1028
1557
  source,
1029
- condition: page.displayCondition,
1030
- onChange: (condition) => updatePage(page.id, (current) => ({ ...current, displayCondition: condition })),
1558
+ condition,
1559
+ onChange: (next) => setDisplayCondition(field.id, next),
1031
1560
  translate
1032
1561
  }
1033
1562
  )
1034
1563
  ] }) : null
1035
1564
  ] })
1036
- ] }, page.id);
1037
- }),
1038
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__page-add", children: [
1039
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1040
- translate("builder.pageQuestion"),
1041
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1042
- "select",
1043
- {
1044
- value: newPageQuestionId,
1045
- disabled: movablePageQuestions.length === 0,
1046
- onChange: (event) => setNewPageQuestionId(event.currentTarget.value),
1047
- children: [
1048
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u2014" }),
1049
- schema.fields.filter((field) => movablePageQuestions.includes(field.id)).map((field) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: field.id, children: field.title }, field.id))
1050
- ]
1051
- }
1052
- )
1053
- ] }),
1054
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: movablePageQuestions.length === 0, onClick: addPage, children: translate("builder.addPage") })
1055
- ] })
1056
- ] })
1057
- ] }),
1058
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "form-engine-builder__localization", "aria-labelledby": "builder-localization-heading", children: [
1059
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { id: "builder-localization-heading", children: translate("builder.localization") }),
1060
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1061
- translate("builder.completionMessage"),
1062
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1063
- "input",
1064
- {
1065
- value: schema.completionMessage ?? "",
1066
- onChange: (event) => headless.setSourceText({ kind: "form" }, "completionMessage", event.currentTarget.value)
1067
- }
1068
- )
1069
- ] }),
1070
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1071
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1072
- translate("builder.defaultLocale"),
1073
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1074
- "input",
1075
- {
1076
- value: schema.defaultLocale ?? "",
1077
- onChange: (event) => headless.setDefaultLocale(event.currentTarget.value)
1078
- }
1079
- )
1080
- ] }),
1081
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1082
- translate("builder.addLocale"),
1083
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { value: newLocale, onChange: (event) => setNewLocale(event.currentTarget.value) })
1084
- ] }),
1085
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: addLocale, children: translate("builder.addLocale") }),
1086
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1087
- translate("builder.editLocale"),
1088
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: editingLocale, onChange: (event) => setEditingLocale(event.currentTarget.value), children: [
1089
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u2014" }),
1090
- (schema.supportedLocales ?? []).filter((item) => item !== schema.defaultLocale).map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: item, children: item }, item))
1091
- ] })
1092
- ] }),
1565
+ ] }, field.id);
1566
+ }) }),
1093
1567
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1094
1568
  "button",
1095
1569
  {
1570
+ className: "form-engine-builder__add",
1096
1571
  type: "button",
1097
- disabled: translationAdapter === void 0 || editingLocale.length === 0 || isTranslating,
1098
- onClick: () => void translateAll(),
1099
- children: translate("builder.autoTranslate")
1572
+ "data-builder-action": "addField",
1573
+ disabled: initialFieldType === null || maxFieldsReached,
1574
+ onClick: addField,
1575
+ children: translate("builder.addQuestion")
1100
1576
  }
1101
1577
  )
1102
- ] }),
1103
- translationAdapter === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: translate("builder.translationUnavailable") }) : null,
1104
- translationError === null ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "form-engine-builder__error", children: translationError }),
1105
- editingLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1106
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1107
- translate("builder.questionTitle"),
1108
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1109
- "input",
1110
- {
1111
- value: schema.translations?.[editingLocale]?.title ?? "",
1112
- onChange: (event) => updateFormTranslation("title", event.currentTarget.value)
1113
- }
1114
- )
1115
- ] }),
1116
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1117
- translate("builder.pageDescription"),
1118
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1119
- "input",
1120
- {
1121
- value: schema.translations?.[editingLocale]?.description ?? "",
1122
- onChange: (event) => updateFormTranslation("description", event.currentTarget.value)
1123
- }
1124
- )
1125
- ] }),
1126
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1127
- translate("builder.completionMessage"),
1128
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1129
- "input",
1130
- {
1131
- value: schema.translations?.[editingLocale]?.completionMessage ?? "",
1132
- onChange: (event) => updateFormTranslation("completionMessage", event.currentTarget.value)
1133
- }
1134
- )
1135
- ] })
1136
- ] })
1137
- ] }),
1138
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "form-engine-builder__list", children: schema.fields.map((field, index) => {
1139
- const condition = field.displayCondition;
1140
- const source = condition === void 0 ? void 0 : schema.fields.find((item) => item.id === condition.questionId);
1141
- const availableSources = schema.fields.slice(0, index);
1142
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { className: "form-engine-builder__question", children: [
1143
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("legend", { children: field.title }),
1144
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__toolbar", children: [
1145
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1146
- "button",
1147
- {
1148
- type: "button",
1149
- disabled: index === 0,
1150
- onClick: () => moveField(index, -1),
1151
- "aria-label": translate("builder.moveUp", { title: field.title }),
1152
- children: "\u2191"
1153
- }
1154
- ),
1155
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1156
- "button",
1157
- {
1158
- type: "button",
1159
- disabled: index === schema.fields.length - 1,
1160
- onClick: () => moveField(index, 1),
1161
- "aria-label": translate("builder.moveDown", { title: field.title }),
1162
- children: "\u2193"
1163
- }
1164
- ),
1165
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1166
- "button",
1167
- {
1168
- type: "button",
1169
- disabled: schema.fields.length === 1,
1170
- onClick: () => removeField(field.id),
1171
- "aria-label": translate("builder.delete", { title: field.title }),
1172
- children: translate("builder.deleteAction")
1173
- }
1174
- )
1175
- ] }),
1176
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1177
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1178
- translate("builder.questionTitle"),
1179
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1180
- "input",
1181
- {
1182
- value: field.title,
1183
- placeholder: translate("builder.questionTitlePlaceholder"),
1184
- onChange: (event) => updateField(field.id, (current) => ({
1185
- ...current,
1186
- title: event.currentTarget.value.trim().length === 0 ? current.title : event.currentTarget.value
1187
- }))
1188
- }
1189
- )
1190
- ] }),
1191
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1192
- translate("builder.type"),
1193
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1194
- "select",
1195
- {
1196
- value: field.type,
1197
- onChange: (event) => changeType(field.id, event.currentTarget.value),
1198
- children: FIELD_TYPES.filter(
1199
- (type) => policy?.allowedFieldTypes === void 0 || policy.allowedFieldTypes.includes(type)
1200
- ).map((type) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: type, children: translate(fieldTypeKey(type)) }, type))
1201
- }
1202
- )
1203
- ] }),
1204
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "form-engine-builder__check", children: [
1205
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1206
- "input",
1207
- {
1208
- type: "checkbox",
1209
- checked: field.required === true,
1210
- onChange: (event) => updateField(field.id, (current) => ({ ...current, required: event.currentTarget.checked }))
1211
- }
1212
- ),
1213
- translate("builder.required")
1214
- ] })
1215
- ] }),
1216
- schema.pages === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1217
- translate("builder.questionPage"),
1218
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1219
- "select",
1220
- {
1221
- value: pageForField(field.id)?.id ?? "",
1222
- onChange: (event) => assignFieldToPage(field.id, event.currentTarget.value),
1223
- children: schema.pages.map((page, pageIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: page.id, children: page.title ?? `${translate("builder.newPage")} ${pageIndex + 1}` }, page.id))
1224
- }
1225
- )
1226
- ] }),
1227
- editingLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__translation-editor", children: [
1228
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: editingLocale }),
1229
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1230
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1231
- translate("builder.questionTitle"),
1232
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1233
- "input",
1234
- {
1235
- value: field.translations?.[editingLocale]?.title ?? "",
1236
- onChange: (event) => headless.setLocaleTranslation(
1237
- editingLocale,
1238
- { kind: "field", id: field.id },
1239
- "title",
1240
- event.currentTarget.value
1241
- )
1242
- }
1243
- )
1244
- ] }),
1245
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1246
- translate("builder.pageDescription"),
1247
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1248
- "input",
1249
- {
1250
- value: field.translations?.[editingLocale]?.description ?? "",
1251
- onChange: (event) => headless.setLocaleTranslation(
1252
- editingLocale,
1253
- { kind: "field", id: field.id },
1254
- "description",
1255
- event.currentTarget.value
1256
- )
1257
- }
1258
- )
1259
- ] })
1260
- ] }),
1261
- "options" in field ? field.options.map((option, optionIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1262
- translate("builder.optionLabel", { index: optionIndex + 1 }),
1263
- " (",
1264
- editingLocale,
1265
- ")",
1266
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1267
- "input",
1268
- {
1269
- value: option.translations?.[editingLocale] ?? "",
1270
- onChange: (event) => headless.setLocaleTranslation(
1271
- editingLocale,
1272
- { kind: "option", id: option.id },
1273
- "label",
1274
- event.currentTarget.value
1275
- )
1276
- }
1277
- )
1278
- ] }, option.id)) : null
1279
- ] }),
1280
- field.type === "rating" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1281
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1282
- translate("builder.minimum"),
1283
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1284
- "input",
1285
- {
1286
- type: "number",
1287
- value: field.min ?? 1,
1288
- onChange: (event) => {
1289
- const min = event.currentTarget.valueAsNumber;
1290
- if (!Number.isInteger(min)) return;
1291
- updateField(
1292
- field.id,
1293
- (current) => current.type === "rating" ? { ...current, min, max: Math.max(min, current.max ?? 5) } : current
1294
- );
1295
- }
1296
- }
1297
- )
1298
- ] }),
1299
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1300
- translate("builder.maximum"),
1301
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1302
- "input",
1303
- {
1304
- type: "number",
1305
- value: field.max ?? 5,
1306
- onChange: (event) => {
1307
- const max = event.currentTarget.valueAsNumber;
1308
- if (!Number.isInteger(max)) return;
1309
- updateField(
1310
- field.id,
1311
- (current) => current.type === "rating" ? { ...current, min: Math.min(current.min ?? 1, max), max } : current
1312
- );
1313
- }
1314
- }
1315
- )
1316
- ] })
1317
- ] }) : null,
1318
- "options" in field ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__options", children: [
1319
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: translate("builder.options") }),
1320
- field.options.map((option, optionIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__option", children: [
1321
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1322
- "input",
1323
- {
1324
- "aria-label": translate("builder.optionLabel", { index: optionIndex + 1 }),
1325
- value: option.label,
1326
- placeholder: translate("builder.optionLabelPlaceholder"),
1327
- onChange: (event) => event.currentTarget.value.trim().length === 0 ? void 0 : headless.updateOption(field.id, option.id, (item) => ({
1328
- ...item,
1329
- label: event.currentTarget.value
1330
- }))
1331
- }
1332
- ),
1333
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1334
- "button",
1335
- {
1336
- type: "button",
1337
- disabled: field.options.length === 1,
1338
- onClick: () => headless.removeOption(field.id, option.id),
1339
- children: translate("builder.remove")
1340
- }
1341
- )
1342
- ] }, option.id)),
1343
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1344
- "button",
1345
- {
1346
- type: "button",
1347
- disabled: policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
1348
- onClick: () => headless.addOption(field.id),
1349
- children: translate("builder.addOption")
1350
- }
1351
- )
1352
- ] }) : null,
1353
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__condition", children: [
1354
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1355
- translate("builder.displayCondition"),
1356
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1357
- "select",
1358
- {
1359
- value: condition?.questionId ?? "",
1360
- onChange: (event) => {
1361
- const selected = schema.fields.find((item) => item.id === event.currentTarget.value);
1362
- headless.setDisplayCondition(
1363
- field.id,
1364
- selected === void 0 ? void 0 : conditionWithValue(
1365
- selected.id,
1366
- conditionOperators(selected)[0] ?? "not_empty",
1367
- defaultConditionValue(selected)
1368
- )
1369
- );
1370
- },
1371
- children: [
1372
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: translate("builder.alwaysVisible") }),
1373
- availableSources.map((candidate) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: candidate.id, children: candidate.title }, candidate.id))
1374
- ]
1375
- }
1376
- )
1377
- ] }),
1378
- condition !== void 0 && source !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1379
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1380
- "select",
1381
- {
1382
- "aria-label": translate("builder.conditionOperator"),
1383
- value: condition.operator,
1384
- onChange: (event) => {
1385
- const operator = event.currentTarget.value;
1386
- headless.setDisplayCondition(
1387
- field.id,
1388
- conditionWithValue(source.id, operator, defaultConditionValue(source))
1389
- );
1390
- },
1391
- children: conditionOperators(source).map((operator) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: operator, children: translate(operatorKey(operator)) }, operator))
1392
- }
1393
- ),
1394
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1395
- ConditionValueEditor,
1396
- {
1397
- source,
1398
- condition,
1399
- onChange: (next) => headless.setDisplayCondition(field.id, next),
1400
- translate
1401
- }
1402
- )
1403
- ] }) : null
1404
- ] })
1405
- ] }, field.id);
1406
- }) }),
1407
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1408
- "button",
1409
- {
1410
- className: "form-engine-builder__add",
1411
- type: "button",
1412
- disabled: policy?.maxFields !== void 0 && schema.fields.length >= policy.maxFields,
1413
- onClick: addField,
1414
- children: translate("builder.addQuestion")
1415
- }
1416
- )
1417
- ] });
1578
+ ]
1579
+ }
1580
+ );
1418
1581
  }
1419
1582
 
1420
1583
  // src/context.tsx
@@ -2055,6 +2218,7 @@ function FormRenderer(props) {
2055
2218
  FormBuilder,
2056
2219
  FormProvider,
2057
2220
  FormRenderer,
2221
+ resolveInitialFieldType,
2058
2222
  useField,
2059
2223
  useForm,
2060
2224
  useFormBuilder