@form-engine-ts/react 2.1.1 → 2.3.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/dist/index.cjs CHANGED
@@ -162,10 +162,16 @@ function useFormBuilder({
162
162
  if (pages === void 0) return { success: false, error: { type: "node_not_found", kind: "page", id: pageId } };
163
163
  const updated = updater(page);
164
164
  if (updated.id !== pageId) return { success: false, error: { type: "invalid_id", kind: "page", id: updated.id } };
165
+ for (const text of [updated.title, updated.description]) {
166
+ if (text !== void 0) {
167
+ const error = textPolicyError(text);
168
+ if (error !== void 0) return error;
169
+ }
170
+ }
165
171
  onChange({ ...schema, pages: pages.map((candidate) => candidate.id === pageId ? updated : candidate) });
166
172
  return { success: true };
167
173
  },
168
- [onChange, schema]
174
+ [onChange, schema, textPolicyError]
169
175
  );
170
176
  const addField = (0, import_react.useCallback)(
171
177
  (type, pageId) => {
@@ -509,6 +515,13 @@ function useFormBuilder({
509
515
  const normalized = locale.trim();
510
516
  if (normalized.length === 0)
511
517
  return { success: false, error: { type: "invalid_operation", message: "Locale must not be empty." } };
518
+ if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.includes(normalized)) {
519
+ return { success: false, error: { type: "disallowed_locale", locale: normalized } };
520
+ }
521
+ const allUniqueLocales = (0, import_core.collectSchemaLocales)(schema).allUniqueLocales;
522
+ if (!allUniqueLocales.has(normalized) && policy?.maxLocales !== void 0 && allUniqueLocales.size >= policy.maxLocales) {
523
+ return { success: false, error: { type: "max_locales_exceeded", max: policy.maxLocales } };
524
+ }
512
525
  const error = textPolicyError(text);
513
526
  if (error !== void 0) return error;
514
527
  const supportedLocales = [.../* @__PURE__ */ new Set([...schema.supportedLocales ?? [], normalized])];
@@ -577,13 +590,22 @@ function useFormBuilder({
577
590
  onChange({ ...schema, supportedLocales, fields, ...pages === void 0 ? {} : { pages } });
578
591
  return { success: true };
579
592
  },
580
- [onChange, schema, textPolicyError]
593
+ [onChange, policy?.allowedLocales, policy?.maxLocales, schema, textPolicyError]
581
594
  );
582
595
  const addLocale = (0, import_react.useCallback)(
583
596
  (locale) => {
584
597
  const normalized = locale.trim();
585
598
  if (normalized.length === 0)
586
599
  return { success: false, error: { type: "invalid_operation", message: "Locale must not be empty." } };
600
+ if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.includes(normalized)) {
601
+ return { success: false, error: { type: "disallowed_locale", locale: normalized } };
602
+ }
603
+ const allUniqueLocales = (0, import_core.collectSchemaLocales)(schema).allUniqueLocales;
604
+ if ((schema.supportedLocales ?? []).includes(normalized) || schema.defaultLocale === normalized)
605
+ return { success: true };
606
+ if (policy?.maxLocales !== void 0 && !allUniqueLocales.has(normalized) && allUniqueLocales.size >= policy.maxLocales) {
607
+ return { success: false, error: { type: "max_locales_exceeded", max: policy.maxLocales } };
608
+ }
587
609
  onChange({
588
610
  ...schema,
589
611
  supportedLocales: [
@@ -596,13 +618,20 @@ function useFormBuilder({
596
618
  });
597
619
  return { success: true };
598
620
  },
599
- [onChange, schema]
621
+ [onChange, policy?.allowedLocales, policy?.maxLocales, schema]
600
622
  );
601
623
  const setDefaultLocale = (0, import_react.useCallback)(
602
624
  (locale) => {
603
625
  const normalized = locale.trim();
604
626
  if (normalized.length === 0)
605
627
  return { success: false, error: { type: "invalid_operation", message: "Locale must not be empty." } };
628
+ if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.includes(normalized)) {
629
+ return { success: false, error: { type: "disallowed_locale", locale: normalized } };
630
+ }
631
+ const allUniqueLocales = (0, import_core.collectSchemaLocales)(schema).allUniqueLocales;
632
+ if (!allUniqueLocales.has(normalized) && policy?.maxLocales !== void 0 && allUniqueLocales.size >= policy.maxLocales) {
633
+ return { success: false, error: { type: "max_locales_exceeded", max: policy.maxLocales } };
634
+ }
606
635
  onChange({
607
636
  ...schema,
608
637
  defaultLocale: normalized,
@@ -610,7 +639,7 @@ function useFormBuilder({
610
639
  });
611
640
  return { success: true };
612
641
  },
613
- [onChange, schema]
642
+ [onChange, policy?.allowedLocales, policy?.maxLocales, schema]
614
643
  );
615
644
  const validationIssues = (0, import_react.useMemo)(() => {
616
645
  const result = (0, import_core.validateFormSchema)(schema, policy === void 0 ? {} : { policy });
@@ -643,6 +672,260 @@ function useFormBuilder({
643
672
 
644
673
  // src/builder.tsx
645
674
  var import_jsx_runtime = require("react/jsx-runtime");
675
+ function DefaultButton({
676
+ id,
677
+ className,
678
+ disabled,
679
+ "aria-label": ariaLabel,
680
+ onClick,
681
+ children,
682
+ title,
683
+ action,
684
+ targetId
685
+ }) {
686
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
687
+ "button",
688
+ {
689
+ id,
690
+ className,
691
+ type: "button",
692
+ disabled,
693
+ "aria-label": ariaLabel,
694
+ title,
695
+ "data-builder-action": action,
696
+ "data-target-id": targetId,
697
+ onClick,
698
+ children
699
+ }
700
+ );
701
+ }
702
+ function DefaultIconButton(props) {
703
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
704
+ DefaultButton,
705
+ {
706
+ ...props.id === void 0 ? {} : { id: props.id },
707
+ ...props.className === void 0 ? {} : { className: props.className },
708
+ ...props.disabled === void 0 ? {} : { disabled: props.disabled },
709
+ "aria-label": props["aria-label"] ?? props.title,
710
+ ...props.onClick === void 0 ? {} : { onClick: props.onClick },
711
+ children: props.icon
712
+ }
713
+ );
714
+ }
715
+ function DefaultTextInput({
716
+ id,
717
+ className,
718
+ disabled,
719
+ readOnly,
720
+ "aria-label": ariaLabel,
721
+ value,
722
+ onChange,
723
+ placeholder,
724
+ maxLength,
725
+ inputMode,
726
+ type = "text",
727
+ min,
728
+ max,
729
+ step
730
+ }) {
731
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
732
+ "input",
733
+ {
734
+ id,
735
+ className,
736
+ disabled,
737
+ readOnly,
738
+ "aria-label": ariaLabel,
739
+ type: type === "text" ? void 0 : type,
740
+ inputMode,
741
+ min,
742
+ max,
743
+ step,
744
+ value,
745
+ placeholder,
746
+ maxLength,
747
+ onChange: (event) => onChange(event.currentTarget.value)
748
+ }
749
+ );
750
+ }
751
+ function DefaultTextArea({
752
+ id,
753
+ className,
754
+ disabled,
755
+ readOnly,
756
+ "aria-label": ariaLabel,
757
+ value,
758
+ onChange,
759
+ rows,
760
+ placeholder,
761
+ maxLength
762
+ }) {
763
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
764
+ "textarea",
765
+ {
766
+ id,
767
+ className,
768
+ disabled,
769
+ readOnly,
770
+ "aria-label": ariaLabel,
771
+ value,
772
+ rows,
773
+ placeholder,
774
+ maxLength,
775
+ onChange: (event) => onChange(event.currentTarget.value)
776
+ }
777
+ );
778
+ }
779
+ function DefaultSelect({
780
+ id,
781
+ className,
782
+ disabled,
783
+ "aria-label": ariaLabel,
784
+ value,
785
+ onChange,
786
+ options
787
+ }) {
788
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
789
+ "select",
790
+ {
791
+ id,
792
+ className,
793
+ disabled,
794
+ "aria-label": ariaLabel,
795
+ value,
796
+ onChange: (event) => onChange(event.currentTarget.value),
797
+ children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value))
798
+ }
799
+ );
800
+ }
801
+ function DefaultCheckbox({
802
+ id,
803
+ className,
804
+ disabled,
805
+ "aria-label": ariaLabel,
806
+ checked,
807
+ onChange,
808
+ label
809
+ }) {
810
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className, children: [
811
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
812
+ "input",
813
+ {
814
+ id,
815
+ type: "checkbox",
816
+ disabled,
817
+ "aria-label": ariaLabel,
818
+ checked,
819
+ onChange: (event) => onChange(event.currentTarget.checked)
820
+ }
821
+ ),
822
+ label
823
+ ] });
824
+ }
825
+ function DefaultSection({
826
+ id,
827
+ className,
828
+ title,
829
+ description,
830
+ headingId,
831
+ "aria-label": ariaLabel,
832
+ onClickCapture,
833
+ children
834
+ }) {
835
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
836
+ "section",
837
+ {
838
+ id,
839
+ className,
840
+ "aria-label": ariaLabel,
841
+ "aria-labelledby": title === void 0 ? void 0 : headingId,
842
+ onClickCapture,
843
+ children: [
844
+ title === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { id: headingId, children: title }),
845
+ description === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: description }),
846
+ children
847
+ ]
848
+ }
849
+ );
850
+ }
851
+ function DefaultFieldset({ className, legend, disabled, children }) {
852
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { className, disabled, children: [
853
+ legend === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("legend", { children: legend }),
854
+ children
855
+ ] });
856
+ }
857
+ function DefaultErrorMessage({ message }) {
858
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "form-engine-builder__error", children: message });
859
+ }
860
+ var DEFAULT_COMPONENTS = {
861
+ Button: DefaultButton,
862
+ IconButton: DefaultIconButton,
863
+ TextInput: DefaultTextInput,
864
+ TextArea: DefaultTextArea,
865
+ Select: DefaultSelect,
866
+ Checkbox: DefaultCheckbox,
867
+ Section: DefaultSection,
868
+ Fieldset: DefaultFieldset,
869
+ ErrorMessage: DefaultErrorMessage
870
+ };
871
+ var BuilderPrimitiveContext = (0, import_react2.createContext)({
872
+ components: DEFAULT_COMPONENTS,
873
+ readOnly: false
874
+ });
875
+ function BuilderButton(props) {
876
+ const context = (0, import_react2.useContext)(BuilderPrimitiveContext);
877
+ const Component = context.components.Button;
878
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props, disabled: context.readOnly || props.disabled === true, readOnly: context.readOnly });
879
+ }
880
+ function BuilderIconButton(props) {
881
+ const context = (0, import_react2.useContext)(BuilderPrimitiveContext);
882
+ const Component = context.components.IconButton;
883
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props, disabled: context.readOnly || props.disabled === true, readOnly: context.readOnly });
884
+ }
885
+ function BuilderTextInput(props) {
886
+ const context = (0, import_react2.useContext)(BuilderPrimitiveContext);
887
+ const Component = context.components.TextInput;
888
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props, disabled: context.readOnly || props.disabled === true, readOnly: context.readOnly });
889
+ }
890
+ function BuilderTextArea(props) {
891
+ const context = (0, import_react2.useContext)(BuilderPrimitiveContext);
892
+ const Component = context.components.TextArea;
893
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props, disabled: context.readOnly || props.disabled === true, readOnly: context.readOnly });
894
+ }
895
+ function BuilderSelect(props) {
896
+ const context = (0, import_react2.useContext)(BuilderPrimitiveContext);
897
+ const Component = context.components.Select;
898
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props, disabled: context.readOnly || props.disabled === true, readOnly: context.readOnly });
899
+ }
900
+ function BuilderCheckbox(props) {
901
+ const context = (0, import_react2.useContext)(BuilderPrimitiveContext);
902
+ const Component = context.components.Checkbox;
903
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props, disabled: context.readOnly || props.disabled === true, readOnly: context.readOnly });
904
+ }
905
+ function BuilderSection(props) {
906
+ const Component = (0, import_react2.useContext)(BuilderPrimitiveContext).components.Section;
907
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props });
908
+ }
909
+ function BuilderFieldset(props) {
910
+ const context = (0, import_react2.useContext)(BuilderPrimitiveContext);
911
+ const Component = context.components.Fieldset;
912
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props, disabled: context.readOnly || props.disabled === true });
913
+ }
914
+ function BuilderErrorMessage(props) {
915
+ const Component = (0, import_react2.useContext)(BuilderPrimitiveContext).components.ErrorMessage;
916
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { ...props });
917
+ }
918
+ var GUARDED_COMPONENTS = {
919
+ Button: BuilderButton,
920
+ IconButton: BuilderIconButton,
921
+ TextInput: BuilderTextInput,
922
+ TextArea: BuilderTextArea,
923
+ Select: BuilderSelect,
924
+ Checkbox: BuilderCheckbox,
925
+ Section: BuilderSection,
926
+ Fieldset: BuilderFieldset,
927
+ ErrorMessage: BuilderErrorMessage
928
+ };
646
929
  var FIELD_TYPES = [
647
930
  "text",
648
931
  "textarea",
@@ -741,37 +1024,52 @@ function ConditionValueEditor({
741
1024
  source,
742
1025
  condition,
743
1026
  onChange,
744
- translate
1027
+ translate,
1028
+ components
745
1029
  }) {
1030
+ const { Select, TextInput } = components;
746
1031
  if (condition.operator === "not_empty") return null;
747
1032
  const update = (value) => onChange({ ...condition, value });
748
1033
  if (source.type === "checkbox") {
749
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: String(condition.value), onChange: (event) => update(event.currentTarget.value === "true"), children: [
750
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "true", children: translate("builder.conditionTrue") }),
751
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "false", children: translate("builder.conditionFalse") })
752
- ] });
1034
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1035
+ Select,
1036
+ {
1037
+ value: String(condition.value),
1038
+ onChange: (value) => update(value === "true"),
1039
+ options: [
1040
+ { value: "true", label: translate("builder.conditionTrue") },
1041
+ { value: "false", label: translate("builder.conditionFalse") }
1042
+ ]
1043
+ }
1044
+ );
753
1045
  }
754
1046
  if (source.type === "number" || source.type === "rating") {
755
1047
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
756
- "input",
1048
+ TextInput,
757
1049
  {
758
1050
  "aria-label": translate("builder.conditionValue"),
759
1051
  type: "number",
760
- value: typeof condition.value === "number" ? condition.value : "",
761
- onChange: (event) => update(event.currentTarget.value === "" ? 0 : event.currentTarget.valueAsNumber)
1052
+ value: typeof condition.value === "number" ? String(condition.value) : "",
1053
+ onChange: (value) => update(value === "" ? 0 : Number(value))
762
1054
  }
763
1055
  );
764
1056
  }
765
1057
  if ("options" in source) {
766
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { value: String(condition.value ?? ""), onChange: (event) => update(event.currentTarget.value), children: source.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.id, children: option.label }, option.id)) });
1058
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1059
+ Select,
1060
+ {
1061
+ value: String(condition.value ?? ""),
1062
+ onChange: update,
1063
+ options: source.options.map((option) => ({ value: option.id, label: option.label }))
1064
+ }
1065
+ );
767
1066
  }
768
1067
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
769
- "input",
1068
+ TextInput,
770
1069
  {
771
1070
  "aria-label": translate("builder.conditionValue"),
772
- type: "text",
773
1071
  value: typeof condition.value === "string" ? condition.value : "",
774
- onChange: (event) => update(event.currentTarget.value)
1072
+ onChange: update
775
1073
  }
776
1074
  );
777
1075
  }
@@ -797,8 +1095,21 @@ function FormBuilder({
797
1095
  className = "",
798
1096
  defaultFieldType,
799
1097
  onActionError,
800
- createManualTranslationMetadata
1098
+ createManualTranslationMetadata,
1099
+ readOnly = false,
1100
+ features,
1101
+ components: componentOverrides,
1102
+ slots
801
1103
  }) {
1104
+ const resolvedComponents = { ...DEFAULT_COMPONENTS, ...componentOverrides };
1105
+ const components = GUARDED_COMPONENTS;
1106
+ const { Button, Checkbox, ErrorMessage, Fieldset, IconButton, Section, Select, TextInput } = components;
1107
+ const ToolbarSlot = slots?.toolbar;
1108
+ const FieldEditorSlot = slots?.fieldEditor;
1109
+ const OptionEditorSlot = slots?.optionEditor;
1110
+ const PagesSlot = slots?.pages;
1111
+ const LocalizationSlot = slots?.localization;
1112
+ const TranslationActionsSlot = slots?.translationActions;
802
1113
  const headless = useFormBuilder({
803
1114
  schema,
804
1115
  onChange,
@@ -815,28 +1126,33 @@ function FormBuilder({
815
1126
  const translated = translator?.translate(key, locale, params);
816
1127
  return translated === void 0 ? interpolate(BUILDER_DEFAULTS[key] ?? key, params) : translated;
817
1128
  };
818
- const executeAction = (result, context) => {
1129
+ const pagesEnabled = features?.pages ?? true;
1130
+ const localizationEnabled = features?.localization ?? true;
1131
+ const conditionsEnabled = features?.conditions ?? true;
1132
+ const executeAction = (run, context) => {
1133
+ if (readOnly) return { success: true };
1134
+ const result = run();
819
1135
  if (!result.success) onActionError?.(result.error, context);
820
1136
  return result;
821
1137
  };
822
- const updateField = (fieldId, updater, params) => executeAction(headless.updateField(fieldId, updater), {
1138
+ const updateField = (fieldId, updater, params) => executeAction(() => headless.updateField(fieldId, updater), {
823
1139
  action: "updateField",
824
1140
  targetId: fieldId,
825
1141
  ...params === void 0 ? {} : { params }
826
1142
  });
827
- const changeType = (fieldId, type) => executeAction(headless.changeFieldType(fieldId, type), {
1143
+ const changeType = (fieldId, type) => executeAction(() => headless.changeFieldType(fieldId, type), {
828
1144
  action: "changeFieldType",
829
1145
  targetId: fieldId,
830
1146
  params: { type }
831
1147
  });
832
- const removeField = (fieldId) => executeAction(headless.removeField(fieldId), { action: "removeField", targetId: fieldId });
1148
+ const removeField = (fieldId) => executeAction(() => headless.removeField(fieldId), { action: "removeField", targetId: fieldId });
833
1149
  const initialFieldType = resolveInitialFieldType(defaultFieldType, policy?.allowedFieldTypes);
834
1150
  const maxFieldsReached = policy?.maxFields !== void 0 && schema.fields.length >= policy.maxFields;
835
1151
  const moveField = (index, offset) => {
836
1152
  const target = index + offset;
837
1153
  const field = schema.fields[index];
838
1154
  if (field !== void 0) {
839
- executeAction(headless.moveField(field.id, target), {
1155
+ executeAction(() => headless.moveField(field.id, target), {
840
1156
  action: "moveField",
841
1157
  targetId: field.id,
842
1158
  params: { targetIndex: target }
@@ -845,10 +1161,10 @@ function FormBuilder({
845
1161
  };
846
1162
  const addField = () => {
847
1163
  if (initialFieldType === null) return;
848
- executeAction(headless.addField(initialFieldType), { action: "addField" });
1164
+ executeAction(() => headless.addField(initialFieldType), { action: "addField" });
849
1165
  };
850
1166
  const enablePages = () => {
851
- if (schema.pages === void 0) executeAction(headless.addPage(), { action: "addPage" });
1167
+ if (schema.pages === void 0) executeAction(() => headless.addPage(), { action: "addPage" });
852
1168
  };
853
1169
  const pageForField = (fieldId) => schema.pages?.find((page) => page.questionIds.includes(fieldId));
854
1170
  const movablePageQuestions = schema.pages?.flatMap((page) => page.questionIds.length > 1 ? page.questionIds : []) ?? [];
@@ -859,7 +1175,7 @@ function FormBuilder({
859
1175
  }
860
1176
  const questionId = movablePageQuestions.includes(newPageQuestionId) ? newPageQuestionId : movablePageQuestions[0];
861
1177
  if (questionId === void 0) return;
862
- executeAction(headless.addPage(questionId), {
1178
+ executeAction(() => headless.addPage(questionId), {
863
1179
  action: "addPage",
864
1180
  targetId: questionId,
865
1181
  params: { questionId }
@@ -868,13 +1184,14 @@ function FormBuilder({
868
1184
  };
869
1185
  const removePage = (pageIndex) => {
870
1186
  const page = schema.pages?.[pageIndex];
871
- if (page !== void 0) executeAction(headless.removePage(page.id), { action: "removePage", targetId: page.id });
1187
+ if (page !== void 0)
1188
+ executeAction(() => headless.removePage(page.id), { action: "removePage", targetId: page.id });
872
1189
  };
873
1190
  const movePage = (pageIndex, offset) => {
874
1191
  const target = pageIndex + offset;
875
1192
  const page = schema.pages?.[pageIndex];
876
1193
  if (page !== void 0) {
877
- executeAction(headless.movePage(page.id, target), {
1194
+ executeAction(() => headless.movePage(page.id, target), {
878
1195
  action: "movePage",
879
1196
  targetId: page.id,
880
1197
  params: { targetIndex: target }
@@ -882,30 +1199,44 @@ function FormBuilder({
882
1199
  }
883
1200
  };
884
1201
  const updatePage = (pageId, update) => {
885
- headless.updatePage(pageId, update);
1202
+ executeAction(() => headless.updatePage(pageId, update), {
1203
+ action: "updatePage",
1204
+ targetId: pageId
1205
+ });
886
1206
  };
887
1207
  const assignFieldToPage = (fieldId, pageId) => {
888
- executeAction(headless.assignFieldToPage(fieldId, pageId), {
1208
+ executeAction(() => headless.assignFieldToPage(fieldId, pageId), {
889
1209
  action: "assignFieldToPage",
890
1210
  targetId: fieldId,
891
1211
  params: { pageId }
892
1212
  });
893
1213
  };
894
1214
  const addLocale = () => {
1215
+ if (readOnly) return;
895
1216
  const normalized = newLocale.trim();
896
1217
  if (normalized.length === 0) return;
897
- headless.addLocale(normalized);
1218
+ const result = executeAction(() => headless.addLocale(normalized), {
1219
+ action: "addLocale",
1220
+ params: { locale: normalized }
1221
+ });
1222
+ if (!result.success) return;
898
1223
  setEditingLocale(normalized);
899
1224
  setNewLocale("");
900
1225
  };
1226
+ const setDefaultLocale = (locale2) => executeAction(() => headless.setDefaultLocale(locale2), {
1227
+ action: "setDefaultLocale",
1228
+ params: { locale: locale2 }
1229
+ });
901
1230
  const translateAll = async () => {
902
- if (translationAdapter === void 0 || editingLocale.length === 0) return;
1231
+ if (readOnly || translationAdapter === void 0 || editingLocale.length === 0) return;
903
1232
  setIsTranslating(true);
904
1233
  setTranslationError(null);
905
1234
  try {
1235
+ const translationPolicy = policy ?? translationOptions?.policy;
906
1236
  const populated = await (0, import_core2.populateSchemaTranslations)(schema, [editingLocale], translationAdapter, {
907
1237
  overwrite: "missing-only",
908
- ...translationOptions
1238
+ ...translationOptions,
1239
+ ...translationPolicy === void 0 ? {} : { policy: translationPolicy }
909
1240
  });
910
1241
  onChange(populated.schema);
911
1242
  onTranslationReport?.(populated.report);
@@ -916,10 +1247,11 @@ function FormBuilder({
916
1247
  }
917
1248
  };
918
1249
  const updateManualTranslation = (context) => {
1250
+ if (readOnly) return;
919
1251
  const metadata = createManualTranslationMetadata?.(context);
920
1252
  const target = context.kind === "form" ? { kind: "form" } : { kind: context.kind, id: context.nodeId };
921
1253
  executeAction(
922
- headless.setLocaleTranslation(
1254
+ () => headless.setLocaleTranslation(
923
1255
  context.locale,
924
1256
  target,
925
1257
  context.property,
@@ -945,41 +1277,124 @@ function FormBuilder({
945
1277
  ...schema.translationMetadata?.[editingLocale]?.[property] === void 0 ? {} : { existingTranslationMetadata: schema.translationMetadata[editingLocale]?.[property] }
946
1278
  });
947
1279
  };
948
- const setSourceText = (target, property, text) => executeAction(headless.setSourceText(target, property, text), {
1280
+ const setSourceText = (target, property, text) => executeAction(() => headless.setSourceText(target, property, text), {
949
1281
  action: "setSourceText",
950
1282
  ...target.id === void 0 ? {} : { targetId: target.id },
951
1283
  params: { kind: target.kind, property }
952
1284
  });
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), {
1285
+ const updateOption = (fieldId, optionId, label) => executeAction(() => headless.updateOption(fieldId, optionId, (option) => ({ ...option, label })), {
1286
+ action: "updateOption",
1287
+ targetId: optionId,
1288
+ params: { fieldId }
1289
+ });
1290
+ const addOption = (fieldId) => executeAction(() => headless.addOption(fieldId), { action: "addOption", targetId: fieldId });
1291
+ const removeOption = (fieldId, optionId) => executeAction(() => headless.removeOption(fieldId, optionId), {
959
1292
  action: "removeOption",
960
1293
  targetId: optionId,
961
1294
  params: { fieldId }
962
1295
  });
963
- const moveOption = (fieldId, optionId, targetIndex) => executeAction(headless.moveOption(fieldId, optionId, targetIndex), {
1296
+ const moveOption = (fieldId, optionId, targetIndex) => executeAction(() => headless.moveOption(fieldId, optionId, targetIndex), {
964
1297
  action: "moveOption",
965
1298
  targetId: optionId,
966
1299
  params: { fieldId, targetIndex }
967
1300
  });
968
- const setDisplayCondition = (fieldId, condition) => executeAction(headless.setDisplayCondition(fieldId, condition), {
1301
+ const setDisplayCondition = (fieldId, condition) => executeAction(() => headless.setDisplayCondition(fieldId, condition), {
969
1302
  action: "setDisplayCondition",
970
1303
  targetId: fieldId,
971
1304
  ...condition === void 0 ? {} : { params: { condition } }
972
1305
  });
973
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
974
- "section",
1306
+ const registeredLocales = /* @__PURE__ */ new Set([
1307
+ ...schema.defaultLocale === void 0 ? [] : [schema.defaultLocale],
1308
+ ...schema.supportedLocales ?? []
1309
+ ]);
1310
+ const availableAllowedLocales = policy?.allowedLocales?.filter((candidate) => !registeredLocales.has(candidate));
1311
+ const localeLimitReached = policy?.maxLocales !== void 0 && registeredLocales.size >= policy.maxLocales;
1312
+ const actions = {
1313
+ addField: (type, pageId) => executeAction(() => headless.addField(type, pageId), {
1314
+ action: "addField",
1315
+ ...pageId === void 0 ? { params: { type } } : { targetId: pageId, params: { type, pageId } }
1316
+ }),
1317
+ removeField: (fieldId) => executeAction(() => headless.removeField(fieldId), { action: "removeField", targetId: fieldId }),
1318
+ moveField: (fieldId, targetIndex) => executeAction(() => headless.moveField(fieldId, targetIndex), {
1319
+ action: "moveField",
1320
+ targetId: fieldId,
1321
+ params: { targetIndex }
1322
+ }),
1323
+ updateField: (fieldId, updater) => executeAction(() => headless.updateField(fieldId, updater), { action: "updateField", targetId: fieldId }),
1324
+ changeFieldType: (fieldId, type) => executeAction(() => headless.changeFieldType(fieldId, type), {
1325
+ action: "changeFieldType",
1326
+ targetId: fieldId,
1327
+ params: { type }
1328
+ }),
1329
+ addOption: (fieldId) => executeAction(() => headless.addOption(fieldId), { action: "addOption", targetId: fieldId }),
1330
+ updateOption: (fieldId, optionId, updater) => executeAction(() => headless.updateOption(fieldId, optionId, updater), {
1331
+ action: "updateOption",
1332
+ targetId: optionId,
1333
+ params: { fieldId }
1334
+ }),
1335
+ removeOption: (fieldId, optionId) => executeAction(() => headless.removeOption(fieldId, optionId), {
1336
+ action: "removeOption",
1337
+ targetId: optionId,
1338
+ params: { fieldId }
1339
+ }),
1340
+ moveOption: (fieldId, optionId, targetIndex) => executeAction(() => headless.moveOption(fieldId, optionId, targetIndex), {
1341
+ action: "moveOption",
1342
+ targetId: optionId,
1343
+ params: { fieldId, targetIndex }
1344
+ }),
1345
+ addPage: (questionId) => executeAction(() => headless.addPage(questionId), {
1346
+ action: "addPage",
1347
+ ...questionId === void 0 ? {} : { targetId: questionId, params: { questionId } }
1348
+ }),
1349
+ updatePage: (pageId, updater) => executeAction(() => headless.updatePage(pageId, updater), { action: "updatePage", targetId: pageId }),
1350
+ removePage: (pageId) => executeAction(() => headless.removePage(pageId), { action: "removePage", targetId: pageId }),
1351
+ movePage: (pageId, targetIndex) => executeAction(() => headless.movePage(pageId, targetIndex), {
1352
+ action: "movePage",
1353
+ targetId: pageId,
1354
+ params: { targetIndex }
1355
+ }),
1356
+ assignFieldToPage: (fieldId, pageId) => executeAction(() => headless.assignFieldToPage(fieldId, pageId), {
1357
+ action: "assignFieldToPage",
1358
+ targetId: fieldId,
1359
+ params: { pageId }
1360
+ }),
1361
+ setDisplayCondition: (fieldId, condition) => executeAction(() => headless.setDisplayCondition(fieldId, condition), {
1362
+ action: "setDisplayCondition",
1363
+ targetId: fieldId,
1364
+ ...condition === void 0 ? {} : { params: { condition } }
1365
+ }),
1366
+ setSourceText: (target, property, text) => executeAction(() => headless.setSourceText(target, property, text), {
1367
+ action: "setSourceText",
1368
+ ...target.id === void 0 ? {} : { targetId: target.id },
1369
+ params: { property, text }
1370
+ }),
1371
+ setLocaleTranslation: (targetLocale, target, property, text, options) => executeAction(() => headless.setLocaleTranslation(targetLocale, target, property, text, options), {
1372
+ action: "setLocaleTranslation",
1373
+ ...target.id === void 0 ? {} : { targetId: target.id },
1374
+ params: { locale: targetLocale, property }
1375
+ }),
1376
+ addLocale: (targetLocale) => executeAction(() => headless.addLocale(targetLocale), {
1377
+ action: "addLocale",
1378
+ params: { locale: targetLocale }
1379
+ }),
1380
+ setDefaultLocale: (targetLocale) => executeAction(() => headless.setDefaultLocale(targetLocale), {
1381
+ action: "setDefaultLocale",
1382
+ params: { locale: targetLocale }
1383
+ })
1384
+ };
1385
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BuilderPrimitiveContext.Provider, { value: { components: resolvedComponents, readOnly }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1386
+ Section,
975
1387
  {
976
1388
  className: `form-engine-builder ${className}`.trim(),
977
1389
  "aria-label": translate("builder.formBuilder"),
978
1390
  onClickCapture: (event) => {
1391
+ if (readOnly) return;
979
1392
  if (!(event.target instanceof HTMLElement)) return;
980
1393
  const actionTarget = event.target.closest("[data-builder-action]");
981
1394
  if (actionTarget?.dataset.builderAction === "addField" && maxFieldsReached && initialFieldType !== null)
982
1395
  addField();
1396
+ if (actionTarget?.dataset.builderAction === "addLocale" && localeLimitReached && newLocale.trim().length > 0)
1397
+ addLocale();
983
1398
  if (actionTarget?.dataset.builderAction !== "addOption") return;
984
1399
  const fieldId = actionTarget.dataset.targetId;
985
1400
  const field = schema.fields.find((candidate) => candidate.id === fieldId);
@@ -987,329 +1402,451 @@ function FormBuilder({
987
1402
  addOption(fieldId);
988
1403
  }
989
1404
  },
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: [
1405
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Fieldset, { className: "form-engine-builder__controls", disabled: readOnly, children: [
1406
+ pagesEnabled ? PagesSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1407
+ Section,
1408
+ {
1409
+ className: "form-engine-builder__pages",
1410
+ headingId: "builder-pages-heading",
1411
+ title: translate("builder.pages"),
1412
+ children: schema.pages === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { onClick: enablePages, children: translate("builder.enablePages") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1413
+ schema.pages.map((page, pageIndex) => {
1414
+ const priorQuestionIds = new Set(
1415
+ schema.pages?.slice(0, pageIndex).flatMap((item) => item.questionIds)
1416
+ );
1417
+ const availableSources = schema.fields.filter((field) => priorQuestionIds.has(field.id));
1418
+ const source = schema.fields.find((field) => field.id === page.displayCondition?.questionId);
1419
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1420
+ Fieldset,
1421
+ {
1422
+ className: "form-engine-builder__page",
1423
+ legend: page.title ?? `${translate("builder.newPage")} ${pageIndex + 1}`,
1424
+ children: [
1425
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "form-engine-builder__toolbar", children: ToolbarSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1426
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1427
+ IconButton,
1428
+ {
1429
+ icon: "\u2191",
1430
+ title: translate("builder.moveUp", { title: page.title ?? page.id }),
1431
+ disabled: pageIndex === 0,
1432
+ onClick: () => movePage(pageIndex, -1)
1433
+ }
1434
+ ),
1435
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1436
+ IconButton,
1437
+ {
1438
+ icon: "\u2193",
1439
+ title: translate("builder.moveDown", { title: page.title ?? page.id }),
1440
+ disabled: pageIndex === (schema.pages?.length ?? 0) - 1,
1441
+ onClick: () => movePage(pageIndex, 1)
1442
+ }
1443
+ ),
1444
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { onClick: () => removePage(pageIndex), variant: "danger", children: translate("builder.deleteAction") })
1445
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1446
+ ToolbarSlot,
1447
+ {
1448
+ schema,
1449
+ kind: "page",
1450
+ targetId: page.id,
1451
+ index: pageIndex,
1452
+ total: schema.pages?.length ?? 0,
1453
+ title: page.title ?? page.id,
1454
+ onMoveUp: () => movePage(pageIndex, -1),
1455
+ onMoveDown: () => movePage(pageIndex, 1),
1456
+ onRemove: () => removePage(pageIndex),
1457
+ readOnly,
1458
+ actions,
1459
+ components
1460
+ }
1461
+ ) }),
1462
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1463
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1464
+ translate("builder.pageTitle"),
1465
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1466
+ TextInput,
1467
+ {
1468
+ value: page.title ?? "",
1469
+ onChange: (value) => {
1470
+ updatePage(page.id, (current) => {
1471
+ if (value.length > 0) return { ...current, title: value };
1472
+ const { title: _title, ...withoutTitle } = current;
1473
+ return withoutTitle;
1474
+ });
1475
+ }
1476
+ }
1477
+ )
1478
+ ] }),
1479
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1480
+ translate("builder.pageDescription"),
1481
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1482
+ TextInput,
1483
+ {
1484
+ value: page.description ?? "",
1485
+ onChange: (value) => {
1486
+ updatePage(page.id, (current) => {
1487
+ if (value.length > 0) return { ...current, description: value };
1488
+ const { description: _description, ...withoutDescription } = current;
1489
+ return withoutDescription;
1490
+ });
1491
+ }
1492
+ }
1493
+ )
1494
+ ] })
1495
+ ] }),
1496
+ !localizationEnabled || editingLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__translation-editor", children: [
1497
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: editingLocale }),
1498
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1499
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1500
+ translate("builder.pageTitle"),
1501
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1502
+ TextInput,
1503
+ {
1504
+ value: page.translations?.[editingLocale]?.title ?? "",
1505
+ onChange: (value) => updateManualTranslation({
1506
+ locale: editingLocale,
1507
+ kind: "page",
1508
+ nodeId: page.id,
1509
+ property: "title",
1510
+ sourceText: page.title ?? "",
1511
+ translatedText: value,
1512
+ ...page.translationMetadata?.[editingLocale]?.title === void 0 ? {} : {
1513
+ existingTranslationMetadata: page.translationMetadata[editingLocale]?.title
1514
+ }
1515
+ })
1516
+ }
1517
+ )
1518
+ ] }),
1519
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1520
+ translate("builder.pageDescription"),
1521
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1522
+ TextInput,
1523
+ {
1524
+ value: page.translations?.[editingLocale]?.description ?? "",
1525
+ onChange: (value) => updateManualTranslation({
1526
+ locale: editingLocale,
1527
+ kind: "page",
1528
+ nodeId: page.id,
1529
+ property: "description",
1530
+ sourceText: page.description ?? "",
1531
+ translatedText: value,
1532
+ ...page.translationMetadata?.[editingLocale]?.description === void 0 ? {} : {
1533
+ existingTranslationMetadata: page.translationMetadata[editingLocale]?.description
1534
+ }
1535
+ })
1536
+ }
1537
+ )
1538
+ ] })
1539
+ ] })
1540
+ ] }),
1541
+ conditionsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__condition", children: [
1542
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1543
+ translate("builder.pageCondition"),
1544
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1545
+ Select,
1546
+ {
1547
+ value: page.displayCondition?.questionId ?? "",
1548
+ onChange: (value) => {
1549
+ const selected = schema.fields.find((field) => field.id === value);
1550
+ updatePage(page.id, (current) => {
1551
+ if (selected === void 0) {
1552
+ const { displayCondition: _condition, ...withoutCondition } = current;
1553
+ return withoutCondition;
1554
+ }
1555
+ return {
1556
+ ...current,
1557
+ displayCondition: conditionWithValue(
1558
+ selected.id,
1559
+ conditionOperators(selected)[0] ?? "not_empty",
1560
+ defaultConditionValue(selected)
1561
+ )
1562
+ };
1563
+ });
1564
+ },
1565
+ options: [
1566
+ { value: "", label: translate("builder.alwaysVisible") },
1567
+ ...availableSources.map((field) => ({ value: field.id, label: field.title }))
1568
+ ]
1569
+ }
1570
+ )
1571
+ ] }),
1572
+ page.displayCondition !== void 0 && source !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1573
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1574
+ Select,
1575
+ {
1576
+ "aria-label": translate("builder.conditionOperator"),
1577
+ value: page.displayCondition.operator,
1578
+ onChange: (value) => {
1579
+ const operator = value;
1580
+ updatePage(page.id, (current) => ({
1581
+ ...current,
1582
+ displayCondition: conditionWithValue(
1583
+ source.id,
1584
+ operator,
1585
+ defaultConditionValue(source)
1586
+ )
1587
+ }));
1588
+ },
1589
+ options: conditionOperators(source).map((operator) => ({
1590
+ value: operator,
1591
+ label: translate(operatorKey(operator))
1592
+ }))
1593
+ }
1594
+ ),
1595
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1596
+ ConditionValueEditor,
1597
+ {
1598
+ source,
1599
+ condition: page.displayCondition,
1600
+ onChange: (condition) => updatePage(page.id, (current) => ({ ...current, displayCondition: condition })),
1601
+ translate,
1602
+ components
1603
+ }
1604
+ )
1605
+ ] }) : null
1606
+ ] }) : null
1607
+ ]
1608
+ },
1609
+ page.id
1610
+ );
1611
+ }),
1612
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__page-add", children: [
1613
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1614
+ translate("builder.pageQuestion"),
1001
1615
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1002
- "button",
1616
+ Select,
1003
1617
  {
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"
1618
+ value: newPageQuestionId,
1619
+ disabled: movablePageQuestions.length === 0,
1620
+ onChange: setNewPageQuestionId,
1621
+ options: [
1622
+ { value: "", label: "\u2014" },
1623
+ ...schema.fields.filter((field) => movablePageQuestions.includes(field.id)).map((field) => ({ value: field.id, label: field.title }))
1624
+ ]
1009
1625
  }
1010
- ),
1011
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1012
- "button",
1626
+ )
1627
+ ] }),
1628
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { disabled: movablePageQuestions.length === 0, onClick: addPage, children: translate("builder.addPage") })
1629
+ ] })
1630
+ ] })
1631
+ }
1632
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1633
+ PagesSlot,
1634
+ {
1635
+ schema,
1636
+ currentLocale: editingLocale,
1637
+ readOnly,
1638
+ actions,
1639
+ components
1640
+ }
1641
+ ) : null,
1642
+ localizationEnabled ? LocalizationSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1643
+ Section,
1644
+ {
1645
+ className: "form-engine-builder__localization",
1646
+ headingId: "builder-localization-heading",
1647
+ title: translate("builder.localization"),
1648
+ children: [
1649
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1650
+ translate("builder.completionMessage"),
1651
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1652
+ TextInput,
1653
+ {
1654
+ value: schema.completionMessage ?? "",
1655
+ onChange: (value) => setSourceText({ kind: "form" }, "completionMessage", value)
1656
+ }
1657
+ )
1658
+ ] }),
1659
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1660
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1661
+ translate("builder.defaultLocale"),
1662
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextInput, { value: schema.defaultLocale ?? "", onChange: setDefaultLocale })
1663
+ ] }),
1664
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor: "builder-new-locale", children: [
1665
+ translate("builder.addLocale"),
1666
+ availableAllowedLocales === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextInput, { id: "builder-new-locale", value: newLocale, onChange: setNewLocale }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1667
+ Select,
1013
1668
  {
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"
1669
+ id: "builder-new-locale",
1670
+ value: newLocale,
1671
+ onChange: setNewLocale,
1672
+ options: [
1673
+ { value: "", label: "\u2014" },
1674
+ ...availableAllowedLocales.map((candidate) => ({ value: candidate, label: candidate }))
1675
+ ]
1019
1676
  }
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
- ] })
1677
+ )
1058
1678
  ] }),
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
- ] })
1679
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1680
+ Button,
1681
+ {
1682
+ action: "addLocale",
1683
+ disabled: newLocale.trim().length === 0 || localeLimitReached,
1684
+ onClick: addLocale,
1685
+ children: translate("builder.addLocale")
1686
+ }
1687
+ ),
1688
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1689
+ translate("builder.editLocale"),
1690
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1691
+ Select,
1692
+ {
1693
+ value: editingLocale,
1694
+ onChange: setEditingLocale,
1695
+ options: [
1696
+ { value: "", label: "\u2014" },
1697
+ ...(schema.supportedLocales ?? []).filter((item) => item !== schema.defaultLocale).map((item) => ({ value: item, label: item }))
1698
+ ]
1699
+ }
1700
+ )
1103
1701
  ] }),
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",
1702
+ TranslationActionsSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1703
+ Button,
1169
1704
  {
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
- ]
1705
+ disabled: translationAdapter === void 0 || editingLocale.length === 0 || isTranslating,
1706
+ onClick: () => void translateAll(),
1707
+ children: translate("builder.autoTranslate")
1708
+ }
1709
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1710
+ TranslationActionsSlot,
1711
+ {
1712
+ schema,
1713
+ currentLocale: editingLocale,
1714
+ onAutoTranslate: () => void translateAll(),
1715
+ isTranslating,
1716
+ readOnly,
1717
+ actions,
1718
+ components
1177
1719
  }
1178
1720
  )
1179
1721
  ] }),
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))
1722
+ translationAdapter === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: translate("builder.translationUnavailable") }) : null,
1723
+ translationError === null ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorMessage, { message: translationError }),
1724
+ editingLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1725
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1726
+ translate("builder.questionTitle"),
1727
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1728
+ TextInput,
1729
+ {
1730
+ value: schema.translations?.[editingLocale]?.title ?? "",
1731
+ onChange: (value) => updateFormTranslation("title", value)
1732
+ }
1733
+ )
1734
+ ] }),
1735
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1736
+ translate("builder.pageDescription"),
1737
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1738
+ TextInput,
1739
+ {
1740
+ value: schema.translations?.[editingLocale]?.description ?? "",
1741
+ onChange: (value) => updateFormTranslation("description", value)
1742
+ }
1743
+ )
1744
+ ] }),
1745
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1746
+ translate("builder.completionMessage"),
1747
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1748
+ TextInput,
1749
+ {
1750
+ value: schema.translations?.[editingLocale]?.completionMessage ?? "",
1751
+ onChange: (value) => updateFormTranslation("completionMessage", value)
1752
+ }
1753
+ )
1754
+ ] })
1217
1755
  ] })
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
- ] }),
1756
+ ]
1757
+ }
1758
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1759
+ LocalizationSlot,
1760
+ {
1761
+ schema,
1762
+ currentLocale: editingLocale,
1763
+ onCurrentLocaleChange: setEditingLocale,
1764
+ onAutoTranslate: () => void translateAll(),
1765
+ isTranslating,
1766
+ ...translationError === null ? {} : { translationError },
1767
+ readOnly,
1768
+ actions,
1769
+ components
1770
+ }
1771
+ ) : null,
1264
1772
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "form-engine-builder__list", children: schema.fields.map((field, index) => {
1265
1773
  const condition = field.displayCondition;
1266
1774
  const source = condition === void 0 ? void 0 : schema.fields.find((item) => item.id === condition.questionId);
1267
1775
  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 }),
1270
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__toolbar", children: [
1776
+ if (FieldEditorSlot !== void 0) {
1777
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1778
+ FieldEditorSlot,
1779
+ {
1780
+ schema,
1781
+ field,
1782
+ index,
1783
+ currentLocale: editingLocale,
1784
+ ...policy === void 0 ? {} : { policy },
1785
+ readOnly,
1786
+ actions,
1787
+ components
1788
+ },
1789
+ field.id
1790
+ );
1791
+ }
1792
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Fieldset, { className: "form-engine-builder__question", legend: field.title, children: [
1793
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "form-engine-builder__toolbar", children: ToolbarSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1271
1794
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1272
- "button",
1795
+ IconButton,
1273
1796
  {
1274
- type: "button",
1797
+ icon: "\u2191",
1798
+ title: translate("builder.moveUp", { title: field.title }),
1275
1799
  disabled: index === 0,
1276
- onClick: () => moveField(index, -1),
1277
- "aria-label": translate("builder.moveUp", { title: field.title }),
1278
- children: "\u2191"
1800
+ onClick: () => moveField(index, -1)
1279
1801
  }
1280
1802
  ),
1281
1803
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1282
- "button",
1804
+ IconButton,
1283
1805
  {
1284
- type: "button",
1806
+ icon: "\u2193",
1807
+ title: translate("builder.moveDown", { title: field.title }),
1285
1808
  disabled: index === schema.fields.length - 1,
1286
- onClick: () => moveField(index, 1),
1287
- "aria-label": translate("builder.moveDown", { title: field.title }),
1288
- children: "\u2193"
1809
+ onClick: () => moveField(index, 1)
1289
1810
  }
1290
1811
  ),
1291
1812
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1292
- "button",
1813
+ Button,
1293
1814
  {
1294
- type: "button",
1295
1815
  disabled: schema.fields.length === 1,
1296
1816
  onClick: () => removeField(field.id),
1297
1817
  "aria-label": translate("builder.delete", { title: field.title }),
1818
+ variant: "danger",
1298
1819
  children: translate("builder.deleteAction")
1299
1820
  }
1300
1821
  )
1301
- ] }),
1822
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1823
+ ToolbarSlot,
1824
+ {
1825
+ schema,
1826
+ kind: "field",
1827
+ targetId: field.id,
1828
+ index,
1829
+ total: schema.fields.length,
1830
+ title: field.title,
1831
+ onMoveUp: () => moveField(index, -1),
1832
+ onMoveDown: () => moveField(index, 1),
1833
+ onRemove: () => removeField(field.id),
1834
+ readOnly,
1835
+ actions,
1836
+ components
1837
+ }
1838
+ ) }),
1302
1839
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1303
1840
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1304
1841
  translate("builder.questionTitle"),
1305
1842
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1306
- "input",
1843
+ TextInput,
1307
1844
  {
1308
1845
  value: field.title,
1309
1846
  placeholder: translate("builder.questionTitlePlaceholder"),
1310
- onChange: (event) => updateField(field.id, (current) => ({
1847
+ onChange: (value) => updateField(field.id, (current) => ({
1311
1848
  ...current,
1312
- title: event.currentTarget.value.trim().length === 0 ? current.title : event.currentTarget.value
1849
+ title: value.trim().length === 0 ? current.title : value
1313
1850
  }))
1314
1851
  }
1315
1852
  )
@@ -1317,55 +1854,56 @@ function FormBuilder({
1317
1854
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1318
1855
  translate("builder.type"),
1319
1856
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1320
- "select",
1857
+ Select,
1321
1858
  {
1322
1859
  value: field.type,
1323
- onChange: (event) => changeType(field.id, event.currentTarget.value),
1324
- children: FIELD_TYPES.filter(
1860
+ onChange: (value) => changeType(field.id, value),
1861
+ options: FIELD_TYPES.filter(
1325
1862
  (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))
1863
+ ).map((type) => ({ value: type, label: translate(fieldTypeKey(type)) }))
1327
1864
  }
1328
1865
  )
1329
1866
  ] }),
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")
1340
- ] })
1867
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1868
+ Checkbox,
1869
+ {
1870
+ className: "form-engine-builder__check",
1871
+ checked: field.required === true,
1872
+ onChange: (checked) => updateField(field.id, (current) => ({ ...current, required: checked })),
1873
+ label: translate("builder.required")
1874
+ }
1875
+ )
1341
1876
  ] }),
1342
- schema.pages === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1877
+ !pagesEnabled || schema.pages === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1343
1878
  translate("builder.questionPage"),
1344
1879
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1345
- "select",
1880
+ Select,
1346
1881
  {
1347
1882
  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))
1883
+ onChange: (value) => assignFieldToPage(field.id, value),
1884
+ options: schema.pages.map((page, pageIndex) => ({
1885
+ value: page.id,
1886
+ label: page.title ?? `${translate("builder.newPage")} ${pageIndex + 1}`
1887
+ }))
1350
1888
  }
1351
1889
  )
1352
1890
  ] }),
1353
- editingLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__translation-editor", children: [
1891
+ !localizationEnabled || editingLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__translation-editor", children: [
1354
1892
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: editingLocale }),
1355
1893
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__grid", children: [
1356
1894
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1357
1895
  translate("builder.questionTitle"),
1358
1896
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1359
- "input",
1897
+ TextInput,
1360
1898
  {
1361
1899
  value: field.translations?.[editingLocale]?.title ?? "",
1362
- onChange: (event) => updateManualTranslation({
1900
+ onChange: (value) => updateManualTranslation({
1363
1901
  locale: editingLocale,
1364
1902
  kind: "field",
1365
1903
  nodeId: field.id,
1366
1904
  property: "title",
1367
1905
  sourceText: field.title,
1368
- translatedText: event.currentTarget.value,
1906
+ translatedText: value,
1369
1907
  ...field.translationMetadata?.[editingLocale]?.title === void 0 ? {} : {
1370
1908
  existingTranslationMetadata: field.translationMetadata[editingLocale]?.title
1371
1909
  }
@@ -1376,16 +1914,16 @@ function FormBuilder({
1376
1914
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1377
1915
  translate("builder.pageDescription"),
1378
1916
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1379
- "input",
1917
+ TextInput,
1380
1918
  {
1381
1919
  value: field.translations?.[editingLocale]?.description ?? "",
1382
- onChange: (event) => updateManualTranslation({
1920
+ onChange: (value) => updateManualTranslation({
1383
1921
  locale: editingLocale,
1384
1922
  kind: "field",
1385
1923
  nodeId: field.id,
1386
1924
  property: "description",
1387
1925
  sourceText: field.description ?? "",
1388
- translatedText: event.currentTarget.value,
1926
+ translatedText: value,
1389
1927
  ...field.translationMetadata?.[editingLocale]?.description === void 0 ? {} : {
1390
1928
  existingTranslationMetadata: field.translationMetadata[editingLocale]?.description
1391
1929
  }
@@ -1400,16 +1938,16 @@ function FormBuilder({
1400
1938
  editingLocale,
1401
1939
  ")",
1402
1940
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1403
- "input",
1941
+ TextInput,
1404
1942
  {
1405
1943
  value: option.translations?.[editingLocale] ?? "",
1406
- onChange: (event) => updateManualTranslation({
1944
+ onChange: (value) => updateManualTranslation({
1407
1945
  locale: editingLocale,
1408
1946
  kind: "option",
1409
1947
  nodeId: option.id,
1410
1948
  property: "label",
1411
1949
  sourceText: option.label,
1412
- translatedText: event.currentTarget.value,
1950
+ translatedText: value,
1413
1951
  ...option.translationMetadata?.[editingLocale]?.label === void 0 ? {} : {
1414
1952
  existingTranslationMetadata: option.translationMetadata[editingLocale]?.label
1415
1953
  }
@@ -1422,12 +1960,12 @@ function FormBuilder({
1422
1960
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1423
1961
  translate("builder.minimum"),
1424
1962
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1425
- "input",
1963
+ TextInput,
1426
1964
  {
1427
1965
  type: "number",
1428
- value: field.min ?? 1,
1429
- onChange: (event) => {
1430
- const min = event.currentTarget.valueAsNumber;
1966
+ value: String(field.min ?? 1),
1967
+ onChange: (value) => {
1968
+ const min = Number(value);
1431
1969
  if (!Number.isInteger(min)) return;
1432
1970
  updateField(
1433
1971
  field.id,
@@ -1440,12 +1978,12 @@ function FormBuilder({
1440
1978
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1441
1979
  translate("builder.maximum"),
1442
1980
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1443
- "input",
1981
+ TextInput,
1444
1982
  {
1445
1983
  type: "number",
1446
- value: field.max ?? 5,
1447
- onChange: (event) => {
1448
- const max = event.currentTarget.valueAsNumber;
1984
+ value: String(field.max ?? 5),
1985
+ onChange: (value) => {
1986
+ const max = Number(value);
1449
1987
  if (!Number.isInteger(max)) return;
1450
1988
  updateField(
1451
1989
  field.id,
@@ -1458,67 +1996,97 @@ function FormBuilder({
1458
1996
  ] }) : null,
1459
1997
  "options" in field ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__options", children: [
1460
1998
  /* @__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",
1999
+ field.options.map(
2000
+ (option, optionIndex) => OptionEditorSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__option", children: [
2001
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2002
+ TextInput,
2003
+ {
2004
+ "aria-label": translate("builder.optionLabel", { index: optionIndex + 1 }),
2005
+ value: option.label,
2006
+ placeholder: translate("builder.optionLabelPlaceholder"),
2007
+ onChange: (value) => value.trim().length === 0 ? void 0 : updateOption(field.id, option.id, value)
2008
+ }
2009
+ ),
2010
+ ToolbarSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2011
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2012
+ IconButton,
2013
+ {
2014
+ icon: "\u2191",
2015
+ title: translate("builder.moveUp", { title: option.label }),
2016
+ disabled: optionIndex === 0,
2017
+ onClick: () => moveOption(field.id, option.id, optionIndex - 1)
2018
+ }
2019
+ ),
2020
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2021
+ IconButton,
2022
+ {
2023
+ icon: "\u2193",
2024
+ title: translate("builder.moveDown", { title: option.label }),
2025
+ disabled: optionIndex === field.options.length - 1,
2026
+ onClick: () => moveOption(field.id, option.id, optionIndex + 1)
2027
+ }
2028
+ ),
2029
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2030
+ Button,
2031
+ {
2032
+ disabled: field.options.length === 1,
2033
+ onClick: () => removeOption(field.id, option.id),
2034
+ variant: "danger",
2035
+ children: translate("builder.remove")
2036
+ }
2037
+ )
2038
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2039
+ ToolbarSlot,
2040
+ {
2041
+ schema,
2042
+ kind: "option",
2043
+ targetId: option.id,
2044
+ index: optionIndex,
2045
+ total: field.options.length,
2046
+ title: option.label,
2047
+ onMoveUp: () => moveOption(field.id, option.id, optionIndex - 1),
2048
+ onMoveDown: () => moveOption(field.id, option.id, optionIndex + 1),
2049
+ onRemove: () => removeOption(field.id, option.id),
2050
+ readOnly,
2051
+ actions,
2052
+ components
2053
+ }
2054
+ )
2055
+ ] }, option.id) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2056
+ OptionEditorSlot,
1493
2057
  {
1494
- type: "button",
1495
- disabled: field.options.length === 1,
1496
- onClick: () => removeOption(field.id, option.id),
1497
- children: translate("builder.remove")
1498
- }
2058
+ schema,
2059
+ field,
2060
+ option,
2061
+ index: optionIndex,
2062
+ currentLocale: editingLocale,
2063
+ readOnly,
2064
+ actions,
2065
+ components
2066
+ },
2067
+ option.id
1499
2068
  )
1500
- ] }, option.id)),
2069
+ ),
1501
2070
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1502
- "button",
2071
+ Button,
1503
2072
  {
1504
- type: "button",
1505
- "data-builder-action": "addOption",
1506
- "data-target-id": field.id,
2073
+ action: "addOption",
2074
+ targetId: field.id,
1507
2075
  disabled: policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
1508
2076
  onClick: () => addOption(field.id),
1509
2077
  children: translate("builder.addOption")
1510
2078
  }
1511
2079
  )
1512
2080
  ] }) : null,
1513
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__condition", children: [
2081
+ conditionsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "form-engine-builder__condition", children: [
1514
2082
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
1515
2083
  translate("builder.displayCondition"),
1516
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1517
- "select",
2084
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2085
+ Select,
1518
2086
  {
1519
2087
  value: condition?.questionId ?? "",
1520
- onChange: (event) => {
1521
- const selected = schema.fields.find((item) => item.id === event.currentTarget.value);
2088
+ onChange: (value) => {
2089
+ const selected = schema.fields.find((item) => item.id === value);
1522
2090
  setDisplayCondition(
1523
2091
  field.id,
1524
2092
  selected === void 0 ? void 0 : conditionWithValue(
@@ -1528,27 +2096,30 @@ function FormBuilder({
1528
2096
  )
1529
2097
  );
1530
2098
  },
1531
- children: [
1532
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: translate("builder.alwaysVisible") }),
1533
- availableSources.map((candidate) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: candidate.id, children: candidate.title }, candidate.id))
2099
+ options: [
2100
+ { value: "", label: translate("builder.alwaysVisible") },
2101
+ ...availableSources.map((candidate) => ({ value: candidate.id, label: candidate.title }))
1534
2102
  ]
1535
2103
  }
1536
2104
  )
1537
2105
  ] }),
1538
2106
  condition !== void 0 && source !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1539
2107
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1540
- "select",
2108
+ Select,
1541
2109
  {
1542
2110
  "aria-label": translate("builder.conditionOperator"),
1543
2111
  value: condition.operator,
1544
- onChange: (event) => {
1545
- const operator = event.currentTarget.value;
2112
+ onChange: (value) => {
2113
+ const operator = value;
1546
2114
  setDisplayCondition(
1547
2115
  field.id,
1548
2116
  conditionWithValue(source.id, operator, defaultConditionValue(source))
1549
2117
  );
1550
2118
  },
1551
- children: conditionOperators(source).map((operator) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: operator, children: translate(operatorKey(operator)) }, operator))
2119
+ options: conditionOperators(source).map((operator) => ({
2120
+ value: operator,
2121
+ label: translate(operatorKey(operator))
2122
+ }))
1552
2123
  }
1553
2124
  ),
1554
2125
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1557,27 +2128,27 @@ function FormBuilder({
1557
2128
  source,
1558
2129
  condition,
1559
2130
  onChange: (next) => setDisplayCondition(field.id, next),
1560
- translate
2131
+ translate,
2132
+ components
1561
2133
  }
1562
2134
  )
1563
2135
  ] }) : null
1564
- ] })
2136
+ ] }) : null
1565
2137
  ] }, field.id);
1566
2138
  }) }),
1567
2139
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1568
- "button",
2140
+ Button,
1569
2141
  {
1570
2142
  className: "form-engine-builder__add",
1571
- type: "button",
1572
- "data-builder-action": "addField",
2143
+ action: "addField",
1573
2144
  disabled: initialFieldType === null || maxFieldsReached,
1574
2145
  onClick: addField,
1575
2146
  children: translate("builder.addQuestion")
1576
2147
  }
1577
2148
  )
1578
- ]
2149
+ ] })
1579
2150
  }
1580
- );
2151
+ ) });
1581
2152
  }
1582
2153
 
1583
2154
  // src/context.tsx