@form-engine-ts/react 2.9.2 → 2.9.4

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.js CHANGED
@@ -101,7 +101,7 @@ function createLocalStorageSubmissionAttemptStore(options = {}) {
101
101
  import {
102
102
  populateSchemaTranslations
103
103
  } from "@form-engine-ts/core";
104
- import { createContext, useContext, useState } from "react";
104
+ import { Children, createContext, isValidElement, useContext, useState } from "react";
105
105
 
106
106
  // src/hooks/useFormBuilder.ts
107
107
  import {
@@ -954,24 +954,39 @@ function DefaultCheckbox({
954
954
  id,
955
955
  className,
956
956
  disabled,
957
+ readOnly,
957
958
  "aria-label": ariaLabel,
959
+ "aria-describedby": ariaDescribedBy,
960
+ "aria-labelledby": ariaLabelledBy,
961
+ name,
962
+ required,
963
+ error,
964
+ helperText,
958
965
  checked,
959
966
  onChange,
960
967
  label
961
968
  }) {
962
- return /* @__PURE__ */ jsxs("label", { className, children: [
963
- /* @__PURE__ */ jsx(
964
- "input",
965
- {
966
- id,
967
- type: "checkbox",
968
- disabled,
969
- "aria-label": ariaLabel,
970
- checked,
971
- onChange: (event) => onChange(event.currentTarget.checked)
972
- }
973
- ),
974
- label
969
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
970
+ /* @__PURE__ */ jsxs("label", { className, children: [
971
+ /* @__PURE__ */ jsx(
972
+ "input",
973
+ {
974
+ id,
975
+ name,
976
+ type: "checkbox",
977
+ disabled: disabled || readOnly,
978
+ required,
979
+ "aria-label": ariaLabel,
980
+ "aria-describedby": ariaDescribedBy,
981
+ "aria-labelledby": ariaLabelledBy,
982
+ "aria-invalid": error === true ? true : void 0,
983
+ checked,
984
+ onChange: (event) => onChange(event.currentTarget.checked)
985
+ }
986
+ ),
987
+ label
988
+ ] }),
989
+ helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ jsx("small", { id: ariaDescribedBy, children: helperText })
975
990
  ] });
976
991
  }
977
992
  function DefaultSection({
@@ -1154,6 +1169,7 @@ var BUILDER_DEFAULTS = {
1154
1169
  "builder.basicSettings": "Basic settings",
1155
1170
  "builder.formTitle": "Form title",
1156
1171
  "builder.formDescription": "Form description",
1172
+ "builder.description": "Description",
1157
1173
  "builder.moveUp": "Move {{title}} up",
1158
1174
  "builder.moveDown": "Move {{title}} down",
1159
1175
  "builder.delete": "Delete {{title}}",
@@ -1188,12 +1204,31 @@ var BUILDER_DEFAULTS = {
1188
1204
  "builder.pageQuestion": "Question to move to the new page",
1189
1205
  "builder.questionPage": "Page",
1190
1206
  "builder.pageCondition": "Page display condition",
1207
+ "builder.unassigned": "Unassigned",
1191
1208
  "builder.localization": "Localization",
1192
1209
  "builder.defaultLocale": "Default locale",
1193
1210
  "builder.supportedLocales": "Supported locales",
1194
1211
  "builder.addLocale": "Add locale",
1195
1212
  "builder.editLocale": "Edit locale",
1196
1213
  "builder.autoTranslate": "Translate all text",
1214
+ "builder.translating": "Translating\u2026",
1215
+ "builder.translationLocale": "Translation locale",
1216
+ "builder.selectLocale": "Select a locale to edit translations.",
1217
+ "builder.translation": "{{locale}} translation",
1218
+ "builder.translatedFormTitle": "Translated form title",
1219
+ "builder.translatedFormDescription": "Translated form description",
1220
+ "builder.translatedCompletionMessage": "Translated completion message",
1221
+ "builder.translatedQuestionTitle": "Translated question title",
1222
+ "builder.translatedDescription": "Translated description",
1223
+ "builder.actions.moveUp": "Move up",
1224
+ "builder.actions.moveDown": "Move down",
1225
+ "builder.actions.delete": "Delete",
1226
+ "builder.actions.add": "Add",
1227
+ "builder.actions.edit": "Edit",
1228
+ "builder.actions.settings": "Settings",
1229
+ "builder.actions.translate": "Translate",
1230
+ "builder.actions.close": "Close",
1231
+ "builder.actions.dragHandle": "Reorder",
1197
1232
  "builder.translationUnavailable": "Provide an async translation adapter to enable automatic translation.",
1198
1233
  "builder.fieldType.text": "Text",
1199
1234
  "builder.fieldType.textarea": "Textarea",
@@ -1214,6 +1249,22 @@ function interpolate(template, params) {
1214
1249
  (token, name) => Object.hasOwn(params, name) ? String(params[name]) : token
1215
1250
  );
1216
1251
  }
1252
+ function BuilderSectionGroup({ children }) {
1253
+ return children;
1254
+ }
1255
+ function sectionGroupName(node) {
1256
+ if (!isValidElement(node) || node.type !== BuilderSectionGroup) return void 0;
1257
+ return node.props.name;
1258
+ }
1259
+ function OrderedBuilderSections({
1260
+ order,
1261
+ children
1262
+ }) {
1263
+ if (order === void 0) return children;
1264
+ const ranks = new Map(order.map((name, index) => [name, index]));
1265
+ const groups = Children.toArray(children);
1266
+ return groups.map((group, index) => ({ group, index, rank: ranks.get(sectionGroupName(group) ?? "questions") ?? order.length })).sort((left, right) => left.rank - right.rank || left.index - right.index).map(({ group }) => group);
1267
+ }
1217
1268
  function fieldTypeKey(type) {
1218
1269
  return `builder.fieldType.${type}`;
1219
1270
  }
@@ -1316,6 +1367,7 @@ function FormBuilder({
1316
1367
  features,
1317
1368
  components: componentOverrides,
1318
1369
  slots,
1370
+ sectionOrder,
1319
1371
  disableDefaultStyles = false,
1320
1372
  unstyled = false
1321
1373
  }) {
@@ -1333,6 +1385,7 @@ function FormBuilder({
1333
1385
  const PagesSlot = slots?.pages;
1334
1386
  const LocalizationSlot = slots?.localization;
1335
1387
  const TranslationActionsSlot = slots?.translationActions;
1388
+ const resolvedSectionOrder = sectionOrder ?? slots?.sectionOrder;
1336
1389
  const headless = useFormBuilder({
1337
1390
  schema,
1338
1391
  onChange,
@@ -1347,7 +1400,11 @@ function FormBuilder({
1347
1400
  const [translationError, setTranslationError] = useState(null);
1348
1401
  const [translationReport, setTranslationReport] = useState();
1349
1402
  const translate = (key, params = {}) => {
1350
- const translated = translator?.translate(key, locale, params);
1403
+ const translatorParams = {};
1404
+ for (const [name, value] of Object.entries(params)) {
1405
+ if (typeof value === "string" || typeof value === "number") translatorParams[name] = value;
1406
+ }
1407
+ const translated = translator?.translate(key, locale, translatorParams);
1351
1408
  return translated === void 0 ? interpolate(BUILDER_DEFAULTS[key] ?? key, params) : translated;
1352
1409
  };
1353
1410
  const pagesEnabled = features?.pages ?? true;
@@ -1632,8 +1689,8 @@ function FormBuilder({
1632
1689
  addOption(fieldId);
1633
1690
  }
1634
1691
  },
1635
- children: /* @__PURE__ */ jsxs(Fieldset, { className: builderClass("form-engine-builder__controls"), disabled: readOnly, children: [
1636
- /* @__PURE__ */ jsx(
1692
+ children: /* @__PURE__ */ jsx(Fieldset, { className: builderClass("form-engine-builder__controls"), disabled: readOnly, children: /* @__PURE__ */ jsxs(OrderedBuilderSections, { ...resolvedSectionOrder === void 0 ? {} : { order: resolvedSectionOrder }, children: [
1693
+ /* @__PURE__ */ jsx(BuilderSectionGroup, { name: "basicSettings", children: /* @__PURE__ */ jsx(
1637
1694
  Section,
1638
1695
  {
1639
1696
  className: builderClass("form-engine-builder__basic-settings"),
@@ -1667,8 +1724,17 @@ function FormBuilder({
1667
1724
  ) })
1668
1725
  ] })
1669
1726
  }
1670
- ),
1671
- pagesEnabled ? PagesSlot === void 0 ? /* @__PURE__ */ jsx(
1727
+ ) }),
1728
+ resolvedSectionOrder === void 0 ? null : /* @__PURE__ */ jsx(BuilderSectionGroup, { name: "completionMessage", children: /* @__PURE__ */ jsx(Section, { headingId: "builder-completion-message-heading", title: translate("builder.completionMessage"), children: /* @__PURE__ */ jsx(
1729
+ TextInput,
1730
+ {
1731
+ id: "builder-completion-message",
1732
+ label: translate("builder.completionMessage"),
1733
+ value: schema.completionMessage ?? "",
1734
+ onChange: (value) => setSourceText({ kind: "form" }, "completionMessage", value)
1735
+ }
1736
+ ) }) }),
1737
+ /* @__PURE__ */ jsx(BuilderSectionGroup, { name: "questions", children: pagesEnabled ? PagesSlot === void 0 ? /* @__PURE__ */ jsx(
1672
1738
  Section,
1673
1739
  {
1674
1740
  className: builderClass("form-engine-builder__pages"),
@@ -1718,6 +1784,7 @@ function FormBuilder({
1718
1784
  ToolbarSlot,
1719
1785
  {
1720
1786
  schema,
1787
+ translate,
1721
1788
  kind: "page",
1722
1789
  targetId: page.id,
1723
1790
  index: pageIndex,
@@ -1864,7 +1931,10 @@ function FormBuilder({
1864
1931
  {
1865
1932
  source,
1866
1933
  condition: page.displayCondition,
1867
- onChange: (condition) => updatePage(page.id, (current) => ({ ...current, displayCondition: condition })),
1934
+ onChange: (condition) => updatePage(page.id, (current) => ({
1935
+ ...current,
1936
+ displayCondition: condition
1937
+ })),
1868
1938
  translate,
1869
1939
  components
1870
1940
  }
@@ -1899,20 +1969,22 @@ function FormBuilder({
1899
1969
  PagesSlot,
1900
1970
  {
1901
1971
  schema,
1972
+ translate,
1902
1973
  currentLocale: editingLocale,
1974
+ ...features === void 0 ? {} : { features },
1903
1975
  readOnly,
1904
1976
  actions,
1905
1977
  components
1906
1978
  }
1907
- ) : null,
1908
- localizationEnabled ? LocalizationSlot === void 0 ? /* @__PURE__ */ jsxs(
1979
+ ) : null }),
1980
+ /* @__PURE__ */ jsx(BuilderSectionGroup, { name: "localization", children: localizationEnabled ? LocalizationSlot === void 0 ? /* @__PURE__ */ jsxs(
1909
1981
  Section,
1910
1982
  {
1911
1983
  className: builderClass("form-engine-builder__localization"),
1912
1984
  headingId: "builder-localization-heading",
1913
1985
  title: translate("builder.localization"),
1914
1986
  children: [
1915
- /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
1987
+ resolvedSectionOrder === void 0 ? /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
1916
1988
  TextInput,
1917
1989
  {
1918
1990
  id: "builder-completion-message",
@@ -1920,7 +1992,7 @@ function FormBuilder({
1920
1992
  value: schema.completionMessage ?? "",
1921
1993
  onChange: (value) => setSourceText({ kind: "form" }, "completionMessage", value)
1922
1994
  }
1923
- ) }),
1995
+ ) }) : null,
1924
1996
  /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__grid"), children: [
1925
1997
  /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
1926
1998
  TextInput,
@@ -1985,12 +2057,14 @@ function FormBuilder({
1985
2057
  TranslationActionsSlot,
1986
2058
  {
1987
2059
  schema,
2060
+ translate,
1988
2061
  currentLocale: editingLocale,
1989
2062
  onAutoTranslate: () => void translateAll(),
1990
2063
  isTranslating,
1991
2064
  ...translationError === null ? {} : { translationError },
1992
2065
  ...translationReport === void 0 ? {} : { translationReport },
1993
2066
  onClearTranslationError: () => setTranslationError(null),
2067
+ translationAdapterAvailable: translationAdapter !== void 0,
1994
2068
  readOnly,
1995
2069
  actions,
1996
2070
  components
@@ -2034,17 +2108,20 @@ function FormBuilder({
2034
2108
  LocalizationSlot,
2035
2109
  {
2036
2110
  schema,
2111
+ translate,
2037
2112
  currentLocale: editingLocale,
2038
2113
  onCurrentLocaleChange: setEditingLocale,
2039
2114
  onAutoTranslate: () => void translateAll(),
2040
2115
  isTranslating,
2041
2116
  ...translationError === null ? {} : { translationError },
2117
+ ...policy === void 0 ? {} : { policy },
2118
+ translationAdapterAvailable: translationAdapter !== void 0,
2042
2119
  readOnly,
2043
2120
  actions,
2044
2121
  components
2045
2122
  }
2046
- ) : null,
2047
- /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__list"), children: schema.fields.map((field, index) => {
2123
+ ) : null }),
2124
+ /* @__PURE__ */ jsx(BuilderSectionGroup, { name: "questions", children: /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__list"), children: schema.fields.map((field, index) => {
2048
2125
  const condition = field.displayCondition;
2049
2126
  const source = condition === void 0 ? void 0 : schema.fields.find((item) => item.id === condition.questionId);
2050
2127
  const availableSources = schema.fields.slice(0, index);
@@ -2056,7 +2133,9 @@ function FormBuilder({
2056
2133
  field,
2057
2134
  index,
2058
2135
  currentLocale: editingLocale,
2136
+ translate,
2059
2137
  ...policy === void 0 ? {} : { policy },
2138
+ ...features === void 0 ? {} : { features },
2060
2139
  readOnly,
2061
2140
  actions,
2062
2141
  components
@@ -2064,348 +2143,362 @@ function FormBuilder({
2064
2143
  field.id
2065
2144
  );
2066
2145
  }
2067
- return /* @__PURE__ */ jsxs(Fieldset, { className: builderClass("form-engine-builder__question"), legend: field.title, children: [
2068
- /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__toolbar"), children: ToolbarSlot === void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
2069
- /* @__PURE__ */ jsx(
2070
- IconButton,
2071
- {
2072
- actionType: "moveUp",
2073
- title: translate("builder.moveUp", { title: field.title }),
2074
- disabled: index === 0,
2075
- onClick: () => moveField(index, -1)
2076
- }
2077
- ),
2078
- /* @__PURE__ */ jsx(
2079
- IconButton,
2080
- {
2081
- actionType: "moveDown",
2082
- title: translate("builder.moveDown", { title: field.title }),
2083
- disabled: index === schema.fields.length - 1,
2084
- onClick: () => moveField(index, 1)
2085
- }
2086
- ),
2087
- /* @__PURE__ */ jsx(
2088
- IconButton,
2089
- {
2090
- actionType: "delete",
2091
- disabled: schema.fields.length === 1,
2092
- onClick: () => removeField(field.id),
2093
- "aria-label": translate("builder.delete", { title: field.title }),
2094
- title: translate("builder.delete", { title: field.title })
2095
- }
2096
- )
2097
- ] }) : /* @__PURE__ */ jsx(
2098
- ToolbarSlot,
2099
- {
2100
- schema,
2101
- kind: "field",
2102
- targetId: field.id,
2103
- index,
2104
- total: schema.fields.length,
2105
- title: field.title,
2106
- onMoveUp: () => moveField(index, -1),
2107
- onMoveDown: () => moveField(index, 1),
2108
- onRemove: () => removeField(field.id),
2109
- readOnly,
2110
- actions,
2111
- components
2112
- }
2113
- ) }),
2114
- /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__grid"), children: [
2115
- /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2116
- TextInput,
2117
- {
2118
- id: `builder-field-${field.id}-title`,
2119
- name: `fields.${field.id}.title`,
2120
- label: translate("builder.questionTitle"),
2121
- required: true,
2122
- error: field.title.trim().length === 0,
2123
- helperText: field.title.trim().length === 0 ? translate("builder.required") : "",
2124
- "aria-describedby": field.title.trim().length === 0 ? `builder-field-${field.id}-title-error` : void 0,
2125
- value: field.title,
2126
- placeholder: translate("builder.questionTitlePlaceholder"),
2127
- onChange: (value) => updateField(field.id, (current) => ({
2128
- ...current,
2129
- title: value.trim().length === 0 ? current.title : value
2130
- }))
2131
- }
2132
- ) }),
2133
- /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2134
- Select,
2135
- {
2136
- id: `builder-field-${field.id}-type`,
2137
- label: translate("builder.type"),
2138
- value: field.type,
2139
- onChange: (value) => changeType(field.id, value),
2140
- options: FIELD_TYPES.filter(
2141
- (type) => policy?.allowedFieldTypes === void 0 || policy.allowedFieldTypes.includes(type)
2142
- ).map((type) => ({ value: type, label: translate(fieldTypeKey(type)) }))
2143
- }
2144
- ) }),
2145
- /* @__PURE__ */ jsx(
2146
- Checkbox,
2147
- {
2148
- className: builderClass("form-engine-builder__check"),
2149
- checked: field.required === true,
2150
- onChange: (checked) => updateField(field.id, (current) => ({ ...current, required: checked })),
2151
- label: translate("builder.required")
2152
- }
2153
- )
2154
- ] }),
2155
- !pagesEnabled || schema.pages === void 0 ? null : /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2156
- Select,
2157
- {
2158
- id: `builder-field-${field.id}-page`,
2159
- label: translate("builder.questionPage"),
2160
- value: pageForField(field.id)?.id ?? "",
2161
- onChange: (value) => assignFieldToPage(field.id, value),
2162
- options: schema.pages.map((page, pageIndex) => ({
2163
- value: page.id,
2164
- label: page.title ?? `${translate("builder.newPage")} ${pageIndex + 1}`
2165
- }))
2166
- }
2167
- ) }),
2168
- !localizationEnabled || editingLocale.length === 0 ? null : /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__translation-editor"), children: [
2169
- /* @__PURE__ */ jsx("strong", { children: editingLocale }),
2170
- /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__grid"), children: [
2171
- /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2172
- TextInput,
2146
+ return /* @__PURE__ */ jsxs(
2147
+ Fieldset,
2148
+ {
2149
+ className: builderClass("form-engine-builder__question"),
2150
+ legend: field.title,
2151
+ children: [
2152
+ /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__toolbar"), children: ToolbarSlot === void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
2153
+ /* @__PURE__ */ jsx(
2154
+ IconButton,
2155
+ {
2156
+ actionType: "moveUp",
2157
+ title: translate("builder.moveUp", { title: field.title }),
2158
+ disabled: index === 0,
2159
+ onClick: () => moveField(index, -1)
2160
+ }
2161
+ ),
2162
+ /* @__PURE__ */ jsx(
2163
+ IconButton,
2164
+ {
2165
+ actionType: "moveDown",
2166
+ title: translate("builder.moveDown", { title: field.title }),
2167
+ disabled: index === schema.fields.length - 1,
2168
+ onClick: () => moveField(index, 1)
2169
+ }
2170
+ ),
2171
+ /* @__PURE__ */ jsx(
2172
+ IconButton,
2173
+ {
2174
+ actionType: "delete",
2175
+ disabled: schema.fields.length === 1,
2176
+ onClick: () => removeField(field.id),
2177
+ "aria-label": translate("builder.delete", { title: field.title }),
2178
+ title: translate("builder.delete", { title: field.title })
2179
+ }
2180
+ )
2181
+ ] }) : /* @__PURE__ */ jsx(
2182
+ ToolbarSlot,
2173
2183
  {
2174
- id: `builder-field-${field.id}-${editingLocale}-title`,
2175
- label: translate("builder.questionTitle"),
2176
- value: field.translations?.[editingLocale]?.title ?? "",
2177
- onChange: (value) => updateManualTranslation({
2178
- locale: editingLocale,
2179
- kind: "field",
2180
- nodeId: field.id,
2181
- property: "title",
2182
- sourceText: field.title,
2183
- translatedText: value,
2184
- ...field.translationMetadata?.[editingLocale]?.title === void 0 ? {} : {
2185
- existingTranslationMetadata: field.translationMetadata[editingLocale]?.title
2186
- }
2187
- })
2184
+ schema,
2185
+ translate,
2186
+ kind: "field",
2187
+ targetId: field.id,
2188
+ index,
2189
+ total: schema.fields.length,
2190
+ title: field.title,
2191
+ onMoveUp: () => moveField(index, -1),
2192
+ onMoveDown: () => moveField(index, 1),
2193
+ onRemove: () => removeField(field.id),
2194
+ readOnly,
2195
+ actions,
2196
+ components
2188
2197
  }
2189
2198
  ) }),
2190
- /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2191
- TextInput,
2199
+ /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__grid"), children: [
2200
+ /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2201
+ TextInput,
2202
+ {
2203
+ id: `builder-field-${field.id}-title`,
2204
+ name: `fields.${field.id}.title`,
2205
+ label: translate("builder.questionTitle"),
2206
+ required: true,
2207
+ error: field.title.trim().length === 0,
2208
+ helperText: field.title.trim().length === 0 ? translate("builder.required") : "",
2209
+ "aria-describedby": field.title.trim().length === 0 ? `builder-field-${field.id}-title-error` : void 0,
2210
+ value: field.title,
2211
+ placeholder: translate("builder.questionTitlePlaceholder"),
2212
+ onChange: (value) => updateField(field.id, (current) => ({
2213
+ ...current,
2214
+ title: value.trim().length === 0 ? current.title : value
2215
+ }))
2216
+ }
2217
+ ) }),
2218
+ /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2219
+ Select,
2220
+ {
2221
+ id: `builder-field-${field.id}-type`,
2222
+ label: translate("builder.type"),
2223
+ value: field.type,
2224
+ onChange: (value) => changeType(field.id, value),
2225
+ options: FIELD_TYPES.filter(
2226
+ (type) => policy?.allowedFieldTypes === void 0 || policy.allowedFieldTypes.includes(type)
2227
+ ).map((type) => ({ value: type, label: translate(fieldTypeKey(type)) }))
2228
+ }
2229
+ ) }),
2230
+ /* @__PURE__ */ jsx(
2231
+ Checkbox,
2232
+ {
2233
+ className: builderClass("form-engine-builder__check"),
2234
+ checked: field.required === true,
2235
+ onChange: (checked) => updateField(field.id, (current) => ({ ...current, required: checked })),
2236
+ label: translate("builder.required")
2237
+ }
2238
+ )
2239
+ ] }),
2240
+ !pagesEnabled || schema.pages === void 0 ? null : /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2241
+ Select,
2192
2242
  {
2193
- id: `builder-field-${field.id}-${editingLocale}-description`,
2194
- label: translate("builder.pageDescription"),
2195
- value: field.translations?.[editingLocale]?.description ?? "",
2196
- onChange: (value) => updateManualTranslation({
2197
- locale: editingLocale,
2198
- kind: "field",
2199
- nodeId: field.id,
2200
- property: "description",
2201
- sourceText: field.description ?? "",
2202
- translatedText: value,
2203
- ...field.translationMetadata?.[editingLocale]?.description === void 0 ? {} : {
2204
- existingTranslationMetadata: field.translationMetadata[editingLocale]?.description
2205
- }
2206
- })
2243
+ id: `builder-field-${field.id}-page`,
2244
+ label: translate("builder.questionPage"),
2245
+ value: pageForField(field.id)?.id ?? "",
2246
+ onChange: (value) => assignFieldToPage(field.id, value),
2247
+ options: schema.pages.map((page, pageIndex) => ({
2248
+ value: page.id,
2249
+ label: page.title ?? `${translate("builder.newPage")} ${pageIndex + 1}`
2250
+ }))
2207
2251
  }
2208
- ) })
2209
- ] }),
2210
- "options" in field ? field.options.map((option, optionIndex) => /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2211
- TextInput,
2212
- {
2213
- id: `builder-option-${option.id}-${editingLocale}`,
2214
- label: `${translate("builder.optionLabel", { index: optionIndex + 1 })} (${editingLocale})`,
2215
- value: option.translations?.[editingLocale] ?? "",
2216
- onChange: (value) => updateManualTranslation({
2217
- locale: editingLocale,
2218
- kind: "option",
2219
- nodeId: option.id,
2220
- property: "label",
2221
- sourceText: option.label,
2222
- translatedText: value,
2223
- ...option.translationMetadata?.[editingLocale]?.label === void 0 ? {} : {
2224
- existingTranslationMetadata: option.translationMetadata[editingLocale]?.label
2252
+ ) }),
2253
+ !localizationEnabled || editingLocale.length === 0 ? null : /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__translation-editor"), children: [
2254
+ /* @__PURE__ */ jsx("strong", { children: editingLocale }),
2255
+ /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__grid"), children: [
2256
+ /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2257
+ TextInput,
2258
+ {
2259
+ id: `builder-field-${field.id}-${editingLocale}-title`,
2260
+ label: translate("builder.questionTitle"),
2261
+ value: field.translations?.[editingLocale]?.title ?? "",
2262
+ onChange: (value) => updateManualTranslation({
2263
+ locale: editingLocale,
2264
+ kind: "field",
2265
+ nodeId: field.id,
2266
+ property: "title",
2267
+ sourceText: field.title,
2268
+ translatedText: value,
2269
+ ...field.translationMetadata?.[editingLocale]?.title === void 0 ? {} : {
2270
+ existingTranslationMetadata: field.translationMetadata[editingLocale]?.title
2271
+ }
2272
+ })
2273
+ }
2274
+ ) }),
2275
+ /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2276
+ TextInput,
2277
+ {
2278
+ id: `builder-field-${field.id}-${editingLocale}-description`,
2279
+ label: translate("builder.pageDescription"),
2280
+ value: field.translations?.[editingLocale]?.description ?? "",
2281
+ onChange: (value) => updateManualTranslation({
2282
+ locale: editingLocale,
2283
+ kind: "field",
2284
+ nodeId: field.id,
2285
+ property: "description",
2286
+ sourceText: field.description ?? "",
2287
+ translatedText: value,
2288
+ ...field.translationMetadata?.[editingLocale]?.description === void 0 ? {} : {
2289
+ existingTranslationMetadata: field.translationMetadata[editingLocale]?.description
2290
+ }
2291
+ })
2292
+ }
2293
+ ) })
2294
+ ] }),
2295
+ "options" in field ? field.options.map((option, optionIndex) => /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2296
+ TextInput,
2297
+ {
2298
+ id: `builder-option-${option.id}-${editingLocale}`,
2299
+ label: `${translate("builder.optionLabel", { index: optionIndex + 1 })} (${editingLocale})`,
2300
+ value: option.translations?.[editingLocale] ?? "",
2301
+ onChange: (value) => updateManualTranslation({
2302
+ locale: editingLocale,
2303
+ kind: "option",
2304
+ nodeId: option.id,
2305
+ property: "label",
2306
+ sourceText: option.label,
2307
+ translatedText: value,
2308
+ ...option.translationMetadata?.[editingLocale]?.label === void 0 ? {} : {
2309
+ existingTranslationMetadata: option.translationMetadata[editingLocale]?.label
2310
+ }
2311
+ })
2225
2312
  }
2226
- })
2227
- }
2228
- ) }, option.id)) : null
2229
- ] }),
2230
- field.type === "rating" ? /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__grid"), children: [
2231
- /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2232
- TextInput,
2233
- {
2234
- id: `builder-field-${field.id}-minimum`,
2235
- label: translate("builder.minimum"),
2236
- type: "number",
2237
- value: String(field.min ?? 1),
2238
- onChange: (value) => {
2239
- const min = Number(value);
2240
- if (!Number.isInteger(min)) return;
2241
- updateField(
2242
- field.id,
2243
- (current) => current.type === "rating" ? { ...current, min, max: Math.max(min, current.max ?? 5) } : current
2244
- );
2245
- }
2246
- }
2247
- ) }),
2248
- /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2249
- TextInput,
2250
- {
2251
- id: `builder-field-${field.id}-maximum`,
2252
- label: translate("builder.maximum"),
2253
- type: "number",
2254
- value: String(field.max ?? 5),
2255
- onChange: (value) => {
2256
- const max = Number(value);
2257
- if (!Number.isInteger(max)) return;
2258
- updateField(
2259
- field.id,
2260
- (current) => current.type === "rating" ? { ...current, min: Math.min(current.min ?? 1, max), max } : current
2261
- );
2262
- }
2263
- }
2264
- ) })
2265
- ] }) : null,
2266
- "options" in field ? /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__options"), children: [
2267
- /* @__PURE__ */ jsx("strong", { children: translate("builder.options") }),
2268
- field.options.map(
2269
- (option, optionIndex) => OptionEditorSlot === void 0 ? /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__option"), children: [
2270
- /* @__PURE__ */ jsx(
2313
+ ) }, option.id)) : null
2314
+ ] }),
2315
+ field.type === "rating" ? /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__grid"), children: [
2316
+ /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2271
2317
  TextInput,
2272
2318
  {
2273
- id: `builder-option-${option.id}`,
2274
- label: translate("builder.optionLabel", { index: optionIndex + 1 }),
2275
- value: option.label,
2276
- placeholder: translate("builder.optionLabelPlaceholder"),
2277
- onChange: (value) => value.trim().length === 0 ? void 0 : updateOption(field.id, option.id, value)
2319
+ id: `builder-field-${field.id}-minimum`,
2320
+ label: translate("builder.minimum"),
2321
+ type: "number",
2322
+ value: String(field.min ?? 1),
2323
+ onChange: (value) => {
2324
+ const min = Number(value);
2325
+ if (!Number.isInteger(min)) return;
2326
+ updateField(
2327
+ field.id,
2328
+ (current) => current.type === "rating" ? { ...current, min, max: Math.max(min, current.max ?? 5) } : current
2329
+ );
2330
+ }
2278
2331
  }
2279
- ),
2280
- ToolbarSlot === void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
2281
- /* @__PURE__ */ jsx(
2282
- IconButton,
2283
- {
2284
- actionType: "moveUp",
2285
- title: translate("builder.moveUp", { title: option.label }),
2286
- disabled: optionIndex === 0,
2287
- onClick: () => moveOption(field.id, option.id, optionIndex - 1)
2332
+ ) }),
2333
+ /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2334
+ TextInput,
2335
+ {
2336
+ id: `builder-field-${field.id}-maximum`,
2337
+ label: translate("builder.maximum"),
2338
+ type: "number",
2339
+ value: String(field.max ?? 5),
2340
+ onChange: (value) => {
2341
+ const max = Number(value);
2342
+ if (!Number.isInteger(max)) return;
2343
+ updateField(
2344
+ field.id,
2345
+ (current) => current.type === "rating" ? { ...current, min: Math.min(current.min ?? 1, max), max } : current
2346
+ );
2288
2347
  }
2289
- ),
2348
+ }
2349
+ ) })
2350
+ ] }) : null,
2351
+ "options" in field ? /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__options"), children: [
2352
+ /* @__PURE__ */ jsx("strong", { children: translate("builder.options") }),
2353
+ field.options.map(
2354
+ (option, optionIndex) => OptionEditorSlot === void 0 ? /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__option"), children: [
2355
+ /* @__PURE__ */ jsx(
2356
+ TextInput,
2357
+ {
2358
+ id: `builder-option-${option.id}`,
2359
+ label: translate("builder.optionLabel", { index: optionIndex + 1 }),
2360
+ value: option.label,
2361
+ placeholder: translate("builder.optionLabelPlaceholder"),
2362
+ onChange: (value) => value.trim().length === 0 ? void 0 : updateOption(field.id, option.id, value)
2363
+ }
2364
+ ),
2365
+ ToolbarSlot === void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
2366
+ /* @__PURE__ */ jsx(
2367
+ IconButton,
2368
+ {
2369
+ actionType: "moveUp",
2370
+ title: translate("builder.moveUp", { title: option.label }),
2371
+ disabled: optionIndex === 0,
2372
+ onClick: () => moveOption(field.id, option.id, optionIndex - 1)
2373
+ }
2374
+ ),
2375
+ /* @__PURE__ */ jsx(
2376
+ IconButton,
2377
+ {
2378
+ actionType: "moveDown",
2379
+ title: translate("builder.moveDown", { title: option.label }),
2380
+ disabled: optionIndex === field.options.length - 1,
2381
+ onClick: () => moveOption(field.id, option.id, optionIndex + 1)
2382
+ }
2383
+ ),
2384
+ /* @__PURE__ */ jsx(
2385
+ IconButton,
2386
+ {
2387
+ actionType: "delete",
2388
+ disabled: field.options.length === 1,
2389
+ onClick: () => removeOption(field.id, option.id),
2390
+ title: translate("builder.remove")
2391
+ }
2392
+ )
2393
+ ] }) : /* @__PURE__ */ jsx(
2394
+ ToolbarSlot,
2395
+ {
2396
+ schema,
2397
+ translate,
2398
+ kind: "option",
2399
+ targetId: option.id,
2400
+ index: optionIndex,
2401
+ total: field.options.length,
2402
+ title: option.label,
2403
+ onMoveUp: () => moveOption(field.id, option.id, optionIndex - 1),
2404
+ onMoveDown: () => moveOption(field.id, option.id, optionIndex + 1),
2405
+ onRemove: () => removeOption(field.id, option.id),
2406
+ readOnly,
2407
+ actions,
2408
+ components
2409
+ }
2410
+ )
2411
+ ] }, option.id) : /* @__PURE__ */ jsx(
2412
+ OptionEditorSlot,
2413
+ {
2414
+ schema,
2415
+ field,
2416
+ option,
2417
+ index: optionIndex,
2418
+ currentLocale: editingLocale,
2419
+ translate,
2420
+ readOnly,
2421
+ actions,
2422
+ components
2423
+ },
2424
+ option.id
2425
+ )
2426
+ ),
2427
+ /* @__PURE__ */ jsx(
2428
+ Button,
2429
+ {
2430
+ action: "addOption",
2431
+ targetId: field.id,
2432
+ disabled: policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
2433
+ onClick: () => addOption(field.id),
2434
+ children: translate("builder.addOption")
2435
+ }
2436
+ )
2437
+ ] }) : null,
2438
+ conditionsEnabled ? /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__condition"), children: [
2439
+ /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2440
+ Select,
2441
+ {
2442
+ id: `builder-field-${field.id}-condition`,
2443
+ label: translate("builder.displayCondition"),
2444
+ value: condition?.questionId ?? "",
2445
+ onChange: (value) => {
2446
+ const selected = schema.fields.find((item) => item.id === value);
2447
+ setDisplayCondition(
2448
+ field.id,
2449
+ selected === void 0 ? void 0 : conditionWithValue(
2450
+ selected.id,
2451
+ conditionOperators(selected)[0] ?? "not_empty",
2452
+ defaultConditionValue(selected)
2453
+ )
2454
+ );
2455
+ },
2456
+ options: [
2457
+ { value: "", label: translate("builder.alwaysVisible") },
2458
+ ...availableSources.map((candidate) => ({
2459
+ value: candidate.id,
2460
+ label: candidate.title
2461
+ }))
2462
+ ]
2463
+ }
2464
+ ) }),
2465
+ condition !== void 0 && source !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
2290
2466
  /* @__PURE__ */ jsx(
2291
- IconButton,
2467
+ Select,
2292
2468
  {
2293
- actionType: "moveDown",
2294
- title: translate("builder.moveDown", { title: option.label }),
2295
- disabled: optionIndex === field.options.length - 1,
2296
- onClick: () => moveOption(field.id, option.id, optionIndex + 1)
2469
+ "aria-label": translate("builder.conditionOperator"),
2470
+ value: condition.operator,
2471
+ onChange: (value) => {
2472
+ const operator = value;
2473
+ setDisplayCondition(
2474
+ field.id,
2475
+ conditionWithValue(source.id, operator, defaultConditionValue(source))
2476
+ );
2477
+ },
2478
+ options: conditionOperators(source).map((operator) => ({
2479
+ value: operator,
2480
+ label: translate(operatorKey(operator))
2481
+ }))
2297
2482
  }
2298
2483
  ),
2299
2484
  /* @__PURE__ */ jsx(
2300
- IconButton,
2485
+ ConditionValueEditor,
2301
2486
  {
2302
- actionType: "delete",
2303
- disabled: field.options.length === 1,
2304
- onClick: () => removeOption(field.id, option.id),
2305
- title: translate("builder.remove")
2487
+ source,
2488
+ condition,
2489
+ onChange: (next) => setDisplayCondition(field.id, next),
2490
+ translate,
2491
+ components
2306
2492
  }
2307
2493
  )
2308
- ] }) : /* @__PURE__ */ jsx(
2309
- ToolbarSlot,
2310
- {
2311
- schema,
2312
- kind: "option",
2313
- targetId: option.id,
2314
- index: optionIndex,
2315
- total: field.options.length,
2316
- title: option.label,
2317
- onMoveUp: () => moveOption(field.id, option.id, optionIndex - 1),
2318
- onMoveDown: () => moveOption(field.id, option.id, optionIndex + 1),
2319
- onRemove: () => removeOption(field.id, option.id),
2320
- readOnly,
2321
- actions,
2322
- components
2323
- }
2324
- )
2325
- ] }, option.id) : /* @__PURE__ */ jsx(
2326
- OptionEditorSlot,
2327
- {
2328
- schema,
2329
- field,
2330
- option,
2331
- index: optionIndex,
2332
- currentLocale: editingLocale,
2333
- readOnly,
2334
- actions,
2335
- components
2336
- },
2337
- option.id
2338
- )
2339
- ),
2340
- /* @__PURE__ */ jsx(
2341
- Button,
2342
- {
2343
- action: "addOption",
2344
- targetId: field.id,
2345
- disabled: policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
2346
- onClick: () => addOption(field.id),
2347
- children: translate("builder.addOption")
2348
- }
2349
- )
2350
- ] }) : null,
2351
- conditionsEnabled ? /* @__PURE__ */ jsxs("div", { className: builderClass("form-engine-builder__condition"), children: [
2352
- /* @__PURE__ */ jsx("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ jsx(
2353
- Select,
2354
- {
2355
- id: `builder-field-${field.id}-condition`,
2356
- label: translate("builder.displayCondition"),
2357
- value: condition?.questionId ?? "",
2358
- onChange: (value) => {
2359
- const selected = schema.fields.find((item) => item.id === value);
2360
- setDisplayCondition(
2361
- field.id,
2362
- selected === void 0 ? void 0 : conditionWithValue(
2363
- selected.id,
2364
- conditionOperators(selected)[0] ?? "not_empty",
2365
- defaultConditionValue(selected)
2366
- )
2367
- );
2368
- },
2369
- options: [
2370
- { value: "", label: translate("builder.alwaysVisible") },
2371
- ...availableSources.map((candidate) => ({ value: candidate.id, label: candidate.title }))
2372
- ]
2373
- }
2374
- ) }),
2375
- condition !== void 0 && source !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
2376
- /* @__PURE__ */ jsx(
2377
- Select,
2378
- {
2379
- "aria-label": translate("builder.conditionOperator"),
2380
- value: condition.operator,
2381
- onChange: (value) => {
2382
- const operator = value;
2383
- setDisplayCondition(
2384
- field.id,
2385
- conditionWithValue(source.id, operator, defaultConditionValue(source))
2386
- );
2387
- },
2388
- options: conditionOperators(source).map((operator) => ({
2389
- value: operator,
2390
- label: translate(operatorKey(operator))
2391
- }))
2392
- }
2393
- ),
2394
- /* @__PURE__ */ jsx(
2395
- ConditionValueEditor,
2396
- {
2397
- source,
2398
- condition,
2399
- onChange: (next) => setDisplayCondition(field.id, next),
2400
- translate,
2401
- components
2402
- }
2403
- )
2404
- ] }) : null
2405
- ] }) : null
2406
- ] }, field.id);
2407
- }) }),
2408
- /* @__PURE__ */ jsx(
2494
+ ] }) : null
2495
+ ] }) : null
2496
+ ]
2497
+ },
2498
+ field.id
2499
+ );
2500
+ }) }) }),
2501
+ /* @__PURE__ */ jsx(BuilderSectionGroup, { name: "addQuestion", children: /* @__PURE__ */ jsx(
2409
2502
  Button,
2410
2503
  {
2411
2504
  className: builderClass("form-engine-builder__add"),
@@ -2414,8 +2507,8 @@ function FormBuilder({
2414
2507
  onClick: addField,
2415
2508
  children: translate("builder.addQuestion")
2416
2509
  }
2417
- )
2418
- ] })
2510
+ ) })
2511
+ ] }) })
2419
2512
  }
2420
2513
  )
2421
2514
  }