@form-engine-ts/react 2.9.3 → 2.9.5

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
@@ -1088,7 +1088,7 @@ function BuilderButton(props) {
1088
1088
  ...props,
1089
1089
  className: primitiveClassName(props.className, "feb-button", context.unstyled),
1090
1090
  disabled: context.readOnly || props.disabled === true,
1091
- readOnly: context.readOnly
1091
+ readOnly: context.readOnly || props.readOnly === true
1092
1092
  }
1093
1093
  );
1094
1094
  }
@@ -1103,7 +1103,7 @@ function BuilderIconButton(props) {
1103
1103
  icon,
1104
1104
  className: primitiveClassName(props.className, "feb-icon-button", context.unstyled),
1105
1105
  disabled: context.readOnly || props.disabled === true,
1106
- readOnly: context.readOnly
1106
+ readOnly: context.readOnly || props.readOnly === true
1107
1107
  }
1108
1108
  );
1109
1109
  }
@@ -1116,7 +1116,7 @@ function BuilderTextInput(props) {
1116
1116
  ...props,
1117
1117
  className: primitiveClassName(props.className, "feb-input", context.unstyled),
1118
1118
  disabled: context.readOnly || props.disabled === true,
1119
- readOnly: context.readOnly
1119
+ readOnly: context.readOnly || props.readOnly === true
1120
1120
  }
1121
1121
  );
1122
1122
  }
@@ -1129,7 +1129,7 @@ function BuilderTextArea(props) {
1129
1129
  ...props,
1130
1130
  className: primitiveClassName(props.className, "feb-textarea", context.unstyled),
1131
1131
  disabled: context.readOnly || props.disabled === true,
1132
- readOnly: context.readOnly
1132
+ readOnly: context.readOnly || props.readOnly === true
1133
1133
  }
1134
1134
  );
1135
1135
  }
@@ -1142,7 +1142,7 @@ function BuilderSelect(props) {
1142
1142
  ...props,
1143
1143
  className: primitiveClassName(props.className, "feb-select", context.unstyled),
1144
1144
  disabled: context.readOnly || props.disabled === true,
1145
- readOnly: context.readOnly
1145
+ readOnly: context.readOnly || props.readOnly === true
1146
1146
  }
1147
1147
  );
1148
1148
  }
@@ -1155,7 +1155,7 @@ function BuilderCheckbox(props) {
1155
1155
  ...props,
1156
1156
  className: primitiveClassName(props.className, "feb-checkbox", context.unstyled),
1157
1157
  disabled: context.readOnly || props.disabled === true,
1158
- readOnly: context.readOnly
1158
+ readOnly: context.readOnly || props.readOnly === true
1159
1159
  }
1160
1160
  );
1161
1161
  }
@@ -1199,6 +1199,7 @@ var BUILDER_DEFAULTS = {
1199
1199
  "builder.basicSettings": "Basic settings",
1200
1200
  "builder.formTitle": "Form title",
1201
1201
  "builder.formDescription": "Form description",
1202
+ "builder.description": "Description",
1202
1203
  "builder.moveUp": "Move {{title}} up",
1203
1204
  "builder.moveDown": "Move {{title}} down",
1204
1205
  "builder.delete": "Delete {{title}}",
@@ -1233,12 +1234,31 @@ var BUILDER_DEFAULTS = {
1233
1234
  "builder.pageQuestion": "Question to move to the new page",
1234
1235
  "builder.questionPage": "Page",
1235
1236
  "builder.pageCondition": "Page display condition",
1237
+ "builder.unassigned": "Unassigned",
1236
1238
  "builder.localization": "Localization",
1237
1239
  "builder.defaultLocale": "Default locale",
1238
1240
  "builder.supportedLocales": "Supported locales",
1239
1241
  "builder.addLocale": "Add locale",
1240
1242
  "builder.editLocale": "Edit locale",
1241
1243
  "builder.autoTranslate": "Translate all text",
1244
+ "builder.translating": "Translating\u2026",
1245
+ "builder.translationLocale": "Translation locale",
1246
+ "builder.selectLocale": "Select a locale to edit translations.",
1247
+ "builder.translation": "{{locale}} translation",
1248
+ "builder.translatedFormTitle": "Translated form title",
1249
+ "builder.translatedFormDescription": "Translated form description",
1250
+ "builder.translatedCompletionMessage": "Translated completion message",
1251
+ "builder.translatedQuestionTitle": "Translated question title",
1252
+ "builder.translatedDescription": "Translated description",
1253
+ "builder.actions.moveUp": "Move up",
1254
+ "builder.actions.moveDown": "Move down",
1255
+ "builder.actions.delete": "Delete",
1256
+ "builder.actions.add": "Add",
1257
+ "builder.actions.edit": "Edit",
1258
+ "builder.actions.settings": "Settings",
1259
+ "builder.actions.translate": "Translate",
1260
+ "builder.actions.close": "Close",
1261
+ "builder.actions.dragHandle": "Reorder",
1242
1262
  "builder.translationUnavailable": "Provide an async translation adapter to enable automatic translation.",
1243
1263
  "builder.fieldType.text": "Text",
1244
1264
  "builder.fieldType.textarea": "Textarea",
@@ -1259,12 +1279,81 @@ function interpolate(template, params) {
1259
1279
  (token, name) => Object.hasOwn(params, name) ? String(params[name]) : token
1260
1280
  );
1261
1281
  }
1282
+ function BuilderSectionGroup({ children }) {
1283
+ return children;
1284
+ }
1285
+ function sectionGroupName(node) {
1286
+ if (!(0, import_react2.isValidElement)(node) || node.type !== BuilderSectionGroup) return void 0;
1287
+ return node.props.name;
1288
+ }
1289
+ function OrderedBuilderSections({
1290
+ order,
1291
+ children
1292
+ }) {
1293
+ if (order === void 0) return children;
1294
+ const ranks = new Map(order.map((name, index) => [name, index]));
1295
+ const groups = import_react2.Children.toArray(children);
1296
+ 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);
1297
+ }
1262
1298
  function fieldTypeKey(type) {
1263
1299
  return `builder.fieldType.${type}`;
1264
1300
  }
1265
1301
  function operatorKey(operator) {
1266
1302
  return `builder.operator.${operator}`;
1267
1303
  }
1304
+ function manualTranslationContext(schema, locale, target, property, translatedText) {
1305
+ if (target.kind === "form") {
1306
+ if (property !== "title" && property !== "description" && property !== "completionMessage") return void 0;
1307
+ return {
1308
+ locale,
1309
+ kind: "form",
1310
+ nodeId: schema.id,
1311
+ property,
1312
+ sourceText: schema[property] ?? "",
1313
+ translatedText,
1314
+ ...schema.translationMetadata?.[locale]?.[property] === void 0 ? {} : { existingTranslationMetadata: schema.translationMetadata[locale]?.[property] }
1315
+ };
1316
+ }
1317
+ if (target.id === void 0) return void 0;
1318
+ if (target.kind === "field") {
1319
+ const field = schema.fields.find((candidate) => candidate.id === target.id);
1320
+ if (field === void 0 || property !== "title" && property !== "description") return void 0;
1321
+ return {
1322
+ locale,
1323
+ kind: "field",
1324
+ nodeId: field.id,
1325
+ property,
1326
+ sourceText: field[property] ?? "",
1327
+ translatedText,
1328
+ ...field.translationMetadata?.[locale]?.[property] === void 0 ? {} : { existingTranslationMetadata: field.translationMetadata[locale]?.[property] }
1329
+ };
1330
+ }
1331
+ if (target.kind === "page") {
1332
+ const page = schema.pages?.find((candidate) => candidate.id === target.id);
1333
+ if (page === void 0 || property !== "title" && property !== "description") return void 0;
1334
+ return {
1335
+ locale,
1336
+ kind: "page",
1337
+ nodeId: page.id,
1338
+ property,
1339
+ sourceText: page[property] ?? "",
1340
+ translatedText,
1341
+ ...page.translationMetadata?.[locale]?.[property] === void 0 ? {} : { existingTranslationMetadata: page.translationMetadata[locale]?.[property] }
1342
+ };
1343
+ }
1344
+ if (property !== "label") return void 0;
1345
+ const option = schema.fields.filter((field) => "options" in field).flatMap((field) => field.options).find((candidate) => candidate.id === target.id);
1346
+ if (option === void 0) return void 0;
1347
+ return {
1348
+ locale,
1349
+ kind: "option",
1350
+ nodeId: option.id,
1351
+ property,
1352
+ sourceText: option.label,
1353
+ translatedText,
1354
+ ...option.translationMetadata?.[locale]?.[property] === void 0 ? {} : { existingTranslationMetadata: option.translationMetadata[locale]?.[property] }
1355
+ };
1356
+ }
1268
1357
  function defaultConditionValue(field) {
1269
1358
  if (field.type === "checkbox") return true;
1270
1359
  if (field.type === "number" || field.type === "rating") return field.min ?? 1;
@@ -1361,6 +1450,7 @@ function FormBuilder({
1361
1450
  features,
1362
1451
  components: componentOverrides,
1363
1452
  slots,
1453
+ sectionOrder,
1364
1454
  disableDefaultStyles = false,
1365
1455
  unstyled = false
1366
1456
  }) {
@@ -1378,6 +1468,7 @@ function FormBuilder({
1378
1468
  const PagesSlot = slots?.pages;
1379
1469
  const LocalizationSlot = slots?.localization;
1380
1470
  const TranslationActionsSlot = slots?.translationActions;
1471
+ const resolvedSectionOrder = sectionOrder ?? slots?.sectionOrder;
1381
1472
  const headless = useFormBuilder({
1382
1473
  schema,
1383
1474
  onChange,
@@ -1392,7 +1483,11 @@ function FormBuilder({
1392
1483
  const [translationError, setTranslationError] = (0, import_react2.useState)(null);
1393
1484
  const [translationReport, setTranslationReport] = (0, import_react2.useState)();
1394
1485
  const translate = (key, params = {}) => {
1395
- const translated = translator?.translate(key, locale, params);
1486
+ const translatorParams = {};
1487
+ for (const [name, value] of Object.entries(params)) {
1488
+ if (typeof value === "string" || typeof value === "number") translatorParams[name] = value;
1489
+ }
1490
+ const translated = translator?.translate(key, locale, translatorParams);
1396
1491
  return translated === void 0 ? interpolate(BUILDER_DEFAULTS[key] ?? key, params) : translated;
1397
1492
  };
1398
1493
  const pagesEnabled = features?.pages ?? true;
@@ -1517,25 +1612,30 @@ function FormBuilder({
1517
1612
  setIsTranslating(false);
1518
1613
  }
1519
1614
  };
1520
- const updateManualTranslation = (context) => {
1521
- if (readOnly) return;
1522
- const metadata = createManualTranslationMetadata?.(context);
1523
- const target = context.kind === "form" ? { kind: "form" } : { kind: context.kind, id: context.nodeId };
1524
- executeAction(
1615
+ const setManualTranslation = (targetLocale, target, property, text) => {
1616
+ if (readOnly) return { success: true };
1617
+ const normalizedLocale = targetLocale.trim();
1618
+ const context = manualTranslationContext(schema, normalizedLocale, target, property, text);
1619
+ const metadata = context === void 0 ? void 0 : createManualTranslationMetadata?.(context);
1620
+ return executeAction(
1525
1621
  () => headless.setLocaleTranslation(
1526
- context.locale,
1622
+ normalizedLocale,
1527
1623
  target,
1528
- context.property,
1529
- context.translatedText,
1624
+ property,
1625
+ text,
1530
1626
  metadata === void 0 ? void 0 : { metadata }
1531
1627
  ),
1532
1628
  {
1533
- action: "setLocaleTranslation",
1534
- targetId: context.nodeId,
1535
- params: { locale: context.locale, kind: context.kind, property: context.property }
1629
+ action: "setManualTranslation",
1630
+ ...target.id === void 0 ? {} : { targetId: target.id },
1631
+ params: { locale: normalizedLocale, kind: target.kind, property }
1536
1632
  }
1537
1633
  );
1538
1634
  };
1635
+ const updateManualTranslation = (context) => {
1636
+ const target = context.kind === "form" ? { kind: "form" } : { kind: context.kind, id: context.nodeId };
1637
+ setManualTranslation(context.locale, target, context.property, context.translatedText);
1638
+ };
1539
1639
  const updateFormTranslation = (property, translatedText) => {
1540
1640
  if (editingLocale.length === 0) return;
1541
1641
  updateManualTranslation({
@@ -1644,6 +1744,7 @@ function FormBuilder({
1644
1744
  ...target.id === void 0 ? {} : { targetId: target.id },
1645
1745
  params: { locale: targetLocale, property }
1646
1746
  }),
1747
+ setManualTranslation,
1647
1748
  addLocale: (targetLocale) => executeAction(() => headless.addLocale(targetLocale), {
1648
1749
  action: "addLocale",
1649
1750
  params: { locale: targetLocale }
@@ -1677,8 +1778,8 @@ function FormBuilder({
1677
1778
  addOption(fieldId);
1678
1779
  }
1679
1780
  },
1680
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Fieldset, { className: builderClass("form-engine-builder__controls"), disabled: readOnly, children: [
1681
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1781
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Fieldset, { className: builderClass("form-engine-builder__controls"), disabled: readOnly, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(OrderedBuilderSections, { ...resolvedSectionOrder === void 0 ? {} : { order: resolvedSectionOrder }, children: [
1782
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BuilderSectionGroup, { name: "basicSettings", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1682
1783
  Section,
1683
1784
  {
1684
1785
  className: builderClass("form-engine-builder__basic-settings"),
@@ -1712,8 +1813,17 @@ function FormBuilder({
1712
1813
  ) })
1713
1814
  ] })
1714
1815
  }
1715
- ),
1716
- pagesEnabled ? PagesSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1816
+ ) }),
1817
+ resolvedSectionOrder === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BuilderSectionGroup, { name: "completionMessage", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Section, { headingId: "builder-completion-message-heading", title: translate("builder.completionMessage"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1818
+ TextInput,
1819
+ {
1820
+ id: "builder-completion-message",
1821
+ label: translate("builder.completionMessage"),
1822
+ value: schema.completionMessage ?? "",
1823
+ onChange: (value) => setSourceText({ kind: "form" }, "completionMessage", value)
1824
+ }
1825
+ ) }) }),
1826
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BuilderSectionGroup, { name: "questions", children: pagesEnabled ? PagesSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1717
1827
  Section,
1718
1828
  {
1719
1829
  className: builderClass("form-engine-builder__pages"),
@@ -1763,6 +1873,7 @@ function FormBuilder({
1763
1873
  ToolbarSlot,
1764
1874
  {
1765
1875
  schema,
1876
+ translate,
1766
1877
  kind: "page",
1767
1878
  targetId: page.id,
1768
1879
  index: pageIndex,
@@ -1909,7 +2020,10 @@ function FormBuilder({
1909
2020
  {
1910
2021
  source,
1911
2022
  condition: page.displayCondition,
1912
- onChange: (condition) => updatePage(page.id, (current) => ({ ...current, displayCondition: condition })),
2023
+ onChange: (condition) => updatePage(page.id, (current) => ({
2024
+ ...current,
2025
+ displayCondition: condition
2026
+ })),
1913
2027
  translate,
1914
2028
  components
1915
2029
  }
@@ -1944,20 +2058,22 @@ function FormBuilder({
1944
2058
  PagesSlot,
1945
2059
  {
1946
2060
  schema,
2061
+ translate,
1947
2062
  currentLocale: editingLocale,
2063
+ ...features === void 0 ? {} : { features },
1948
2064
  readOnly,
1949
2065
  actions,
1950
2066
  components
1951
2067
  }
1952
- ) : null,
1953
- localizationEnabled ? LocalizationSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2068
+ ) : null }),
2069
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BuilderSectionGroup, { name: "localization", children: localizationEnabled ? LocalizationSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1954
2070
  Section,
1955
2071
  {
1956
2072
  className: builderClass("form-engine-builder__localization"),
1957
2073
  headingId: "builder-localization-heading",
1958
2074
  title: translate("builder.localization"),
1959
2075
  children: [
1960
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2076
+ resolvedSectionOrder === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1961
2077
  TextInput,
1962
2078
  {
1963
2079
  id: "builder-completion-message",
@@ -1965,7 +2081,7 @@ function FormBuilder({
1965
2081
  value: schema.completionMessage ?? "",
1966
2082
  onChange: (value) => setSourceText({ kind: "form" }, "completionMessage", value)
1967
2083
  }
1968
- ) }),
2084
+ ) }) : null,
1969
2085
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__grid"), children: [
1970
2086
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1971
2087
  TextInput,
@@ -2030,12 +2146,14 @@ function FormBuilder({
2030
2146
  TranslationActionsSlot,
2031
2147
  {
2032
2148
  schema,
2149
+ translate,
2033
2150
  currentLocale: editingLocale,
2034
2151
  onAutoTranslate: () => void translateAll(),
2035
2152
  isTranslating,
2036
2153
  ...translationError === null ? {} : { translationError },
2037
2154
  ...translationReport === void 0 ? {} : { translationReport },
2038
2155
  onClearTranslationError: () => setTranslationError(null),
2156
+ translationAdapterAvailable: translationAdapter !== void 0,
2039
2157
  readOnly,
2040
2158
  actions,
2041
2159
  components
@@ -2079,17 +2197,20 @@ function FormBuilder({
2079
2197
  LocalizationSlot,
2080
2198
  {
2081
2199
  schema,
2200
+ translate,
2082
2201
  currentLocale: editingLocale,
2083
2202
  onCurrentLocaleChange: setEditingLocale,
2084
2203
  onAutoTranslate: () => void translateAll(),
2085
2204
  isTranslating,
2086
2205
  ...translationError === null ? {} : { translationError },
2206
+ ...policy === void 0 ? {} : { policy },
2207
+ translationAdapterAvailable: translationAdapter !== void 0,
2087
2208
  readOnly,
2088
2209
  actions,
2089
2210
  components
2090
2211
  }
2091
- ) : null,
2092
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__list"), children: schema.fields.map((field, index) => {
2212
+ ) : null }),
2213
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BuilderSectionGroup, { name: "questions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__list"), children: schema.fields.map((field, index) => {
2093
2214
  const condition = field.displayCondition;
2094
2215
  const source = condition === void 0 ? void 0 : schema.fields.find((item) => item.id === condition.questionId);
2095
2216
  const availableSources = schema.fields.slice(0, index);
@@ -2101,7 +2222,9 @@ function FormBuilder({
2101
2222
  field,
2102
2223
  index,
2103
2224
  currentLocale: editingLocale,
2225
+ translate,
2104
2226
  ...policy === void 0 ? {} : { policy },
2227
+ ...features === void 0 ? {} : { features },
2105
2228
  readOnly,
2106
2229
  actions,
2107
2230
  components
@@ -2109,348 +2232,362 @@ function FormBuilder({
2109
2232
  field.id
2110
2233
  );
2111
2234
  }
2112
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Fieldset, { className: builderClass("form-engine-builder__question"), legend: field.title, children: [
2113
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__toolbar"), children: ToolbarSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2114
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2115
- IconButton,
2116
- {
2117
- actionType: "moveUp",
2118
- title: translate("builder.moveUp", { title: field.title }),
2119
- disabled: index === 0,
2120
- onClick: () => moveField(index, -1)
2121
- }
2122
- ),
2123
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2124
- IconButton,
2125
- {
2126
- actionType: "moveDown",
2127
- title: translate("builder.moveDown", { title: field.title }),
2128
- disabled: index === schema.fields.length - 1,
2129
- onClick: () => moveField(index, 1)
2130
- }
2131
- ),
2132
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2133
- IconButton,
2134
- {
2135
- actionType: "delete",
2136
- disabled: schema.fields.length === 1,
2137
- onClick: () => removeField(field.id),
2138
- "aria-label": translate("builder.delete", { title: field.title }),
2139
- title: translate("builder.delete", { title: field.title })
2140
- }
2141
- )
2142
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2143
- ToolbarSlot,
2144
- {
2145
- schema,
2146
- kind: "field",
2147
- targetId: field.id,
2148
- index,
2149
- total: schema.fields.length,
2150
- title: field.title,
2151
- onMoveUp: () => moveField(index, -1),
2152
- onMoveDown: () => moveField(index, 1),
2153
- onRemove: () => removeField(field.id),
2154
- readOnly,
2155
- actions,
2156
- components
2157
- }
2158
- ) }),
2159
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__grid"), children: [
2160
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2161
- TextInput,
2162
- {
2163
- id: `builder-field-${field.id}-title`,
2164
- name: `fields.${field.id}.title`,
2165
- label: translate("builder.questionTitle"),
2166
- required: true,
2167
- error: field.title.trim().length === 0,
2168
- helperText: field.title.trim().length === 0 ? translate("builder.required") : "",
2169
- "aria-describedby": field.title.trim().length === 0 ? `builder-field-${field.id}-title-error` : void 0,
2170
- value: field.title,
2171
- placeholder: translate("builder.questionTitlePlaceholder"),
2172
- onChange: (value) => updateField(field.id, (current) => ({
2173
- ...current,
2174
- title: value.trim().length === 0 ? current.title : value
2175
- }))
2176
- }
2177
- ) }),
2178
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2179
- Select,
2180
- {
2181
- id: `builder-field-${field.id}-type`,
2182
- label: translate("builder.type"),
2183
- value: field.type,
2184
- onChange: (value) => changeType(field.id, value),
2185
- options: FIELD_TYPES.filter(
2186
- (type) => policy?.allowedFieldTypes === void 0 || policy.allowedFieldTypes.includes(type)
2187
- ).map((type) => ({ value: type, label: translate(fieldTypeKey(type)) }))
2188
- }
2189
- ) }),
2190
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2191
- Checkbox,
2192
- {
2193
- className: builderClass("form-engine-builder__check"),
2194
- checked: field.required === true,
2195
- onChange: (checked) => updateField(field.id, (current) => ({ ...current, required: checked })),
2196
- label: translate("builder.required")
2197
- }
2198
- )
2199
- ] }),
2200
- !pagesEnabled || schema.pages === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2201
- Select,
2202
- {
2203
- id: `builder-field-${field.id}-page`,
2204
- label: translate("builder.questionPage"),
2205
- value: pageForField(field.id)?.id ?? "",
2206
- onChange: (value) => assignFieldToPage(field.id, value),
2207
- options: schema.pages.map((page, pageIndex) => ({
2208
- value: page.id,
2209
- label: page.title ?? `${translate("builder.newPage")} ${pageIndex + 1}`
2210
- }))
2211
- }
2212
- ) }),
2213
- !localizationEnabled || editingLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__translation-editor"), children: [
2214
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: editingLocale }),
2215
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__grid"), children: [
2216
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2217
- TextInput,
2235
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2236
+ Fieldset,
2237
+ {
2238
+ className: builderClass("form-engine-builder__question"),
2239
+ legend: field.title,
2240
+ children: [
2241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__toolbar"), children: ToolbarSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2242
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2243
+ IconButton,
2244
+ {
2245
+ actionType: "moveUp",
2246
+ title: translate("builder.moveUp", { title: field.title }),
2247
+ disabled: index === 0,
2248
+ onClick: () => moveField(index, -1)
2249
+ }
2250
+ ),
2251
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2252
+ IconButton,
2253
+ {
2254
+ actionType: "moveDown",
2255
+ title: translate("builder.moveDown", { title: field.title }),
2256
+ disabled: index === schema.fields.length - 1,
2257
+ onClick: () => moveField(index, 1)
2258
+ }
2259
+ ),
2260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2261
+ IconButton,
2262
+ {
2263
+ actionType: "delete",
2264
+ disabled: schema.fields.length === 1,
2265
+ onClick: () => removeField(field.id),
2266
+ "aria-label": translate("builder.delete", { title: field.title }),
2267
+ title: translate("builder.delete", { title: field.title })
2268
+ }
2269
+ )
2270
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2271
+ ToolbarSlot,
2218
2272
  {
2219
- id: `builder-field-${field.id}-${editingLocale}-title`,
2220
- label: translate("builder.questionTitle"),
2221
- value: field.translations?.[editingLocale]?.title ?? "",
2222
- onChange: (value) => updateManualTranslation({
2223
- locale: editingLocale,
2224
- kind: "field",
2225
- nodeId: field.id,
2226
- property: "title",
2227
- sourceText: field.title,
2228
- translatedText: value,
2229
- ...field.translationMetadata?.[editingLocale]?.title === void 0 ? {} : {
2230
- existingTranslationMetadata: field.translationMetadata[editingLocale]?.title
2231
- }
2232
- })
2273
+ schema,
2274
+ translate,
2275
+ kind: "field",
2276
+ targetId: field.id,
2277
+ index,
2278
+ total: schema.fields.length,
2279
+ title: field.title,
2280
+ onMoveUp: () => moveField(index, -1),
2281
+ onMoveDown: () => moveField(index, 1),
2282
+ onRemove: () => removeField(field.id),
2283
+ readOnly,
2284
+ actions,
2285
+ components
2233
2286
  }
2234
2287
  ) }),
2235
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2236
- TextInput,
2288
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__grid"), children: [
2289
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2290
+ TextInput,
2291
+ {
2292
+ id: `builder-field-${field.id}-title`,
2293
+ name: `fields.${field.id}.title`,
2294
+ label: translate("builder.questionTitle"),
2295
+ required: true,
2296
+ error: field.title.trim().length === 0,
2297
+ helperText: field.title.trim().length === 0 ? translate("builder.required") : "",
2298
+ "aria-describedby": field.title.trim().length === 0 ? `builder-field-${field.id}-title-error` : void 0,
2299
+ value: field.title,
2300
+ placeholder: translate("builder.questionTitlePlaceholder"),
2301
+ onChange: (value) => updateField(field.id, (current) => ({
2302
+ ...current,
2303
+ title: value.trim().length === 0 ? current.title : value
2304
+ }))
2305
+ }
2306
+ ) }),
2307
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2308
+ Select,
2309
+ {
2310
+ id: `builder-field-${field.id}-type`,
2311
+ label: translate("builder.type"),
2312
+ value: field.type,
2313
+ onChange: (value) => changeType(field.id, value),
2314
+ options: FIELD_TYPES.filter(
2315
+ (type) => policy?.allowedFieldTypes === void 0 || policy.allowedFieldTypes.includes(type)
2316
+ ).map((type) => ({ value: type, label: translate(fieldTypeKey(type)) }))
2317
+ }
2318
+ ) }),
2319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2320
+ Checkbox,
2321
+ {
2322
+ className: builderClass("form-engine-builder__check"),
2323
+ checked: field.required === true,
2324
+ onChange: (checked) => updateField(field.id, (current) => ({ ...current, required: checked })),
2325
+ label: translate("builder.required")
2326
+ }
2327
+ )
2328
+ ] }),
2329
+ !pagesEnabled || schema.pages === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2330
+ Select,
2237
2331
  {
2238
- id: `builder-field-${field.id}-${editingLocale}-description`,
2239
- label: translate("builder.pageDescription"),
2240
- value: field.translations?.[editingLocale]?.description ?? "",
2241
- onChange: (value) => updateManualTranslation({
2242
- locale: editingLocale,
2243
- kind: "field",
2244
- nodeId: field.id,
2245
- property: "description",
2246
- sourceText: field.description ?? "",
2247
- translatedText: value,
2248
- ...field.translationMetadata?.[editingLocale]?.description === void 0 ? {} : {
2249
- existingTranslationMetadata: field.translationMetadata[editingLocale]?.description
2250
- }
2251
- })
2332
+ id: `builder-field-${field.id}-page`,
2333
+ label: translate("builder.questionPage"),
2334
+ value: pageForField(field.id)?.id ?? "",
2335
+ onChange: (value) => assignFieldToPage(field.id, value),
2336
+ options: schema.pages.map((page, pageIndex) => ({
2337
+ value: page.id,
2338
+ label: page.title ?? `${translate("builder.newPage")} ${pageIndex + 1}`
2339
+ }))
2252
2340
  }
2253
- ) })
2254
- ] }),
2255
- "options" in field ? field.options.map((option, optionIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2256
- TextInput,
2257
- {
2258
- id: `builder-option-${option.id}-${editingLocale}`,
2259
- label: `${translate("builder.optionLabel", { index: optionIndex + 1 })} (${editingLocale})`,
2260
- value: option.translations?.[editingLocale] ?? "",
2261
- onChange: (value) => updateManualTranslation({
2262
- locale: editingLocale,
2263
- kind: "option",
2264
- nodeId: option.id,
2265
- property: "label",
2266
- sourceText: option.label,
2267
- translatedText: value,
2268
- ...option.translationMetadata?.[editingLocale]?.label === void 0 ? {} : {
2269
- existingTranslationMetadata: option.translationMetadata[editingLocale]?.label
2341
+ ) }),
2342
+ !localizationEnabled || editingLocale.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__translation-editor"), children: [
2343
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: editingLocale }),
2344
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__grid"), children: [
2345
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2346
+ TextInput,
2347
+ {
2348
+ id: `builder-field-${field.id}-${editingLocale}-title`,
2349
+ label: translate("builder.questionTitle"),
2350
+ value: field.translations?.[editingLocale]?.title ?? "",
2351
+ onChange: (value) => updateManualTranslation({
2352
+ locale: editingLocale,
2353
+ kind: "field",
2354
+ nodeId: field.id,
2355
+ property: "title",
2356
+ sourceText: field.title,
2357
+ translatedText: value,
2358
+ ...field.translationMetadata?.[editingLocale]?.title === void 0 ? {} : {
2359
+ existingTranslationMetadata: field.translationMetadata[editingLocale]?.title
2360
+ }
2361
+ })
2362
+ }
2363
+ ) }),
2364
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2365
+ TextInput,
2366
+ {
2367
+ id: `builder-field-${field.id}-${editingLocale}-description`,
2368
+ label: translate("builder.pageDescription"),
2369
+ value: field.translations?.[editingLocale]?.description ?? "",
2370
+ onChange: (value) => updateManualTranslation({
2371
+ locale: editingLocale,
2372
+ kind: "field",
2373
+ nodeId: field.id,
2374
+ property: "description",
2375
+ sourceText: field.description ?? "",
2376
+ translatedText: value,
2377
+ ...field.translationMetadata?.[editingLocale]?.description === void 0 ? {} : {
2378
+ existingTranslationMetadata: field.translationMetadata[editingLocale]?.description
2379
+ }
2380
+ })
2381
+ }
2382
+ ) })
2383
+ ] }),
2384
+ "options" in field ? field.options.map((option, optionIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2385
+ TextInput,
2386
+ {
2387
+ id: `builder-option-${option.id}-${editingLocale}`,
2388
+ label: `${translate("builder.optionLabel", { index: optionIndex + 1 })} (${editingLocale})`,
2389
+ value: option.translations?.[editingLocale] ?? "",
2390
+ onChange: (value) => updateManualTranslation({
2391
+ locale: editingLocale,
2392
+ kind: "option",
2393
+ nodeId: option.id,
2394
+ property: "label",
2395
+ sourceText: option.label,
2396
+ translatedText: value,
2397
+ ...option.translationMetadata?.[editingLocale]?.label === void 0 ? {} : {
2398
+ existingTranslationMetadata: option.translationMetadata[editingLocale]?.label
2399
+ }
2400
+ })
2270
2401
  }
2271
- })
2272
- }
2273
- ) }, option.id)) : null
2274
- ] }),
2275
- field.type === "rating" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__grid"), children: [
2276
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2277
- TextInput,
2278
- {
2279
- id: `builder-field-${field.id}-minimum`,
2280
- label: translate("builder.minimum"),
2281
- type: "number",
2282
- value: String(field.min ?? 1),
2283
- onChange: (value) => {
2284
- const min = Number(value);
2285
- if (!Number.isInteger(min)) return;
2286
- updateField(
2287
- field.id,
2288
- (current) => current.type === "rating" ? { ...current, min, max: Math.max(min, current.max ?? 5) } : current
2289
- );
2290
- }
2291
- }
2292
- ) }),
2293
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2294
- TextInput,
2295
- {
2296
- id: `builder-field-${field.id}-maximum`,
2297
- label: translate("builder.maximum"),
2298
- type: "number",
2299
- value: String(field.max ?? 5),
2300
- onChange: (value) => {
2301
- const max = Number(value);
2302
- if (!Number.isInteger(max)) return;
2303
- updateField(
2304
- field.id,
2305
- (current) => current.type === "rating" ? { ...current, min: Math.min(current.min ?? 1, max), max } : current
2306
- );
2307
- }
2308
- }
2309
- ) })
2310
- ] }) : null,
2311
- "options" in field ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__options"), children: [
2312
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: translate("builder.options") }),
2313
- field.options.map(
2314
- (option, optionIndex) => OptionEditorSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__option"), children: [
2315
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2402
+ ) }, option.id)) : null
2403
+ ] }),
2404
+ field.type === "rating" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__grid"), children: [
2405
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2316
2406
  TextInput,
2317
2407
  {
2318
- id: `builder-option-${option.id}`,
2319
- label: translate("builder.optionLabel", { index: optionIndex + 1 }),
2320
- value: option.label,
2321
- placeholder: translate("builder.optionLabelPlaceholder"),
2322
- onChange: (value) => value.trim().length === 0 ? void 0 : updateOption(field.id, option.id, value)
2408
+ id: `builder-field-${field.id}-minimum`,
2409
+ label: translate("builder.minimum"),
2410
+ type: "number",
2411
+ value: String(field.min ?? 1),
2412
+ onChange: (value) => {
2413
+ const min = Number(value);
2414
+ if (!Number.isInteger(min)) return;
2415
+ updateField(
2416
+ field.id,
2417
+ (current) => current.type === "rating" ? { ...current, min, max: Math.max(min, current.max ?? 5) } : current
2418
+ );
2419
+ }
2323
2420
  }
2324
- ),
2325
- ToolbarSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2326
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2327
- IconButton,
2328
- {
2329
- actionType: "moveUp",
2330
- title: translate("builder.moveUp", { title: option.label }),
2331
- disabled: optionIndex === 0,
2332
- onClick: () => moveOption(field.id, option.id, optionIndex - 1)
2421
+ ) }),
2422
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2423
+ TextInput,
2424
+ {
2425
+ id: `builder-field-${field.id}-maximum`,
2426
+ label: translate("builder.maximum"),
2427
+ type: "number",
2428
+ value: String(field.max ?? 5),
2429
+ onChange: (value) => {
2430
+ const max = Number(value);
2431
+ if (!Number.isInteger(max)) return;
2432
+ updateField(
2433
+ field.id,
2434
+ (current) => current.type === "rating" ? { ...current, min: Math.min(current.min ?? 1, max), max } : current
2435
+ );
2333
2436
  }
2334
- ),
2437
+ }
2438
+ ) })
2439
+ ] }) : null,
2440
+ "options" in field ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__options"), children: [
2441
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: translate("builder.options") }),
2442
+ field.options.map(
2443
+ (option, optionIndex) => OptionEditorSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__option"), children: [
2444
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2445
+ TextInput,
2446
+ {
2447
+ id: `builder-option-${option.id}`,
2448
+ label: translate("builder.optionLabel", { index: optionIndex + 1 }),
2449
+ value: option.label,
2450
+ placeholder: translate("builder.optionLabelPlaceholder"),
2451
+ onChange: (value) => value.trim().length === 0 ? void 0 : updateOption(field.id, option.id, value)
2452
+ }
2453
+ ),
2454
+ ToolbarSlot === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2455
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2456
+ IconButton,
2457
+ {
2458
+ actionType: "moveUp",
2459
+ title: translate("builder.moveUp", { title: option.label }),
2460
+ disabled: optionIndex === 0,
2461
+ onClick: () => moveOption(field.id, option.id, optionIndex - 1)
2462
+ }
2463
+ ),
2464
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2465
+ IconButton,
2466
+ {
2467
+ actionType: "moveDown",
2468
+ title: translate("builder.moveDown", { title: option.label }),
2469
+ disabled: optionIndex === field.options.length - 1,
2470
+ onClick: () => moveOption(field.id, option.id, optionIndex + 1)
2471
+ }
2472
+ ),
2473
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2474
+ IconButton,
2475
+ {
2476
+ actionType: "delete",
2477
+ disabled: field.options.length === 1,
2478
+ onClick: () => removeOption(field.id, option.id),
2479
+ title: translate("builder.remove")
2480
+ }
2481
+ )
2482
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2483
+ ToolbarSlot,
2484
+ {
2485
+ schema,
2486
+ translate,
2487
+ kind: "option",
2488
+ targetId: option.id,
2489
+ index: optionIndex,
2490
+ total: field.options.length,
2491
+ title: option.label,
2492
+ onMoveUp: () => moveOption(field.id, option.id, optionIndex - 1),
2493
+ onMoveDown: () => moveOption(field.id, option.id, optionIndex + 1),
2494
+ onRemove: () => removeOption(field.id, option.id),
2495
+ readOnly,
2496
+ actions,
2497
+ components
2498
+ }
2499
+ )
2500
+ ] }, option.id) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2501
+ OptionEditorSlot,
2502
+ {
2503
+ schema,
2504
+ field,
2505
+ option,
2506
+ index: optionIndex,
2507
+ currentLocale: editingLocale,
2508
+ translate,
2509
+ readOnly,
2510
+ actions,
2511
+ components
2512
+ },
2513
+ option.id
2514
+ )
2515
+ ),
2516
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2517
+ Button,
2518
+ {
2519
+ action: "addOption",
2520
+ targetId: field.id,
2521
+ disabled: policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
2522
+ onClick: () => addOption(field.id),
2523
+ children: translate("builder.addOption")
2524
+ }
2525
+ )
2526
+ ] }) : null,
2527
+ conditionsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__condition"), children: [
2528
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2529
+ Select,
2530
+ {
2531
+ id: `builder-field-${field.id}-condition`,
2532
+ label: translate("builder.displayCondition"),
2533
+ value: condition?.questionId ?? "",
2534
+ onChange: (value) => {
2535
+ const selected = schema.fields.find((item) => item.id === value);
2536
+ setDisplayCondition(
2537
+ field.id,
2538
+ selected === void 0 ? void 0 : conditionWithValue(
2539
+ selected.id,
2540
+ conditionOperators(selected)[0] ?? "not_empty",
2541
+ defaultConditionValue(selected)
2542
+ )
2543
+ );
2544
+ },
2545
+ options: [
2546
+ { value: "", label: translate("builder.alwaysVisible") },
2547
+ ...availableSources.map((candidate) => ({
2548
+ value: candidate.id,
2549
+ label: candidate.title
2550
+ }))
2551
+ ]
2552
+ }
2553
+ ) }),
2554
+ condition !== void 0 && source !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2335
2555
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2336
- IconButton,
2556
+ Select,
2337
2557
  {
2338
- actionType: "moveDown",
2339
- title: translate("builder.moveDown", { title: option.label }),
2340
- disabled: optionIndex === field.options.length - 1,
2341
- onClick: () => moveOption(field.id, option.id, optionIndex + 1)
2558
+ "aria-label": translate("builder.conditionOperator"),
2559
+ value: condition.operator,
2560
+ onChange: (value) => {
2561
+ const operator = value;
2562
+ setDisplayCondition(
2563
+ field.id,
2564
+ conditionWithValue(source.id, operator, defaultConditionValue(source))
2565
+ );
2566
+ },
2567
+ options: conditionOperators(source).map((operator) => ({
2568
+ value: operator,
2569
+ label: translate(operatorKey(operator))
2570
+ }))
2342
2571
  }
2343
2572
  ),
2344
2573
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2345
- IconButton,
2574
+ ConditionValueEditor,
2346
2575
  {
2347
- actionType: "delete",
2348
- disabled: field.options.length === 1,
2349
- onClick: () => removeOption(field.id, option.id),
2350
- title: translate("builder.remove")
2576
+ source,
2577
+ condition,
2578
+ onChange: (next) => setDisplayCondition(field.id, next),
2579
+ translate,
2580
+ components
2351
2581
  }
2352
2582
  )
2353
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2354
- ToolbarSlot,
2355
- {
2356
- schema,
2357
- kind: "option",
2358
- targetId: option.id,
2359
- index: optionIndex,
2360
- total: field.options.length,
2361
- title: option.label,
2362
- onMoveUp: () => moveOption(field.id, option.id, optionIndex - 1),
2363
- onMoveDown: () => moveOption(field.id, option.id, optionIndex + 1),
2364
- onRemove: () => removeOption(field.id, option.id),
2365
- readOnly,
2366
- actions,
2367
- components
2368
- }
2369
- )
2370
- ] }, option.id) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2371
- OptionEditorSlot,
2372
- {
2373
- schema,
2374
- field,
2375
- option,
2376
- index: optionIndex,
2377
- currentLocale: editingLocale,
2378
- readOnly,
2379
- actions,
2380
- components
2381
- },
2382
- option.id
2383
- )
2384
- ),
2385
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2386
- Button,
2387
- {
2388
- action: "addOption",
2389
- targetId: field.id,
2390
- disabled: policy?.maxOptionsPerField !== void 0 && field.options.length >= policy.maxOptionsPerField,
2391
- onClick: () => addOption(field.id),
2392
- children: translate("builder.addOption")
2393
- }
2394
- )
2395
- ] }) : null,
2396
- conditionsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: builderClass("form-engine-builder__condition"), children: [
2397
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: builderClass("form-engine-builder__field"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2398
- Select,
2399
- {
2400
- id: `builder-field-${field.id}-condition`,
2401
- label: translate("builder.displayCondition"),
2402
- value: condition?.questionId ?? "",
2403
- onChange: (value) => {
2404
- const selected = schema.fields.find((item) => item.id === value);
2405
- setDisplayCondition(
2406
- field.id,
2407
- selected === void 0 ? void 0 : conditionWithValue(
2408
- selected.id,
2409
- conditionOperators(selected)[0] ?? "not_empty",
2410
- defaultConditionValue(selected)
2411
- )
2412
- );
2413
- },
2414
- options: [
2415
- { value: "", label: translate("builder.alwaysVisible") },
2416
- ...availableSources.map((candidate) => ({ value: candidate.id, label: candidate.title }))
2417
- ]
2418
- }
2419
- ) }),
2420
- condition !== void 0 && source !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2421
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2422
- Select,
2423
- {
2424
- "aria-label": translate("builder.conditionOperator"),
2425
- value: condition.operator,
2426
- onChange: (value) => {
2427
- const operator = value;
2428
- setDisplayCondition(
2429
- field.id,
2430
- conditionWithValue(source.id, operator, defaultConditionValue(source))
2431
- );
2432
- },
2433
- options: conditionOperators(source).map((operator) => ({
2434
- value: operator,
2435
- label: translate(operatorKey(operator))
2436
- }))
2437
- }
2438
- ),
2439
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2440
- ConditionValueEditor,
2441
- {
2442
- source,
2443
- condition,
2444
- onChange: (next) => setDisplayCondition(field.id, next),
2445
- translate,
2446
- components
2447
- }
2448
- )
2449
- ] }) : null
2450
- ] }) : null
2451
- ] }, field.id);
2452
- }) }),
2453
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2583
+ ] }) : null
2584
+ ] }) : null
2585
+ ]
2586
+ },
2587
+ field.id
2588
+ );
2589
+ }) }) }),
2590
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BuilderSectionGroup, { name: "addQuestion", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2454
2591
  Button,
2455
2592
  {
2456
2593
  className: builderClass("form-engine-builder__add"),
@@ -2459,8 +2596,8 @@ function FormBuilder({
2459
2596
  onClick: addField,
2460
2597
  children: translate("builder.addQuestion")
2461
2598
  }
2462
- )
2463
- ] })
2599
+ ) })
2600
+ ] }) })
2464
2601
  }
2465
2602
  )
2466
2603
  }
@@ -2985,6 +3122,8 @@ function ContextFormRenderer({
2985
3122
  autoSaveKey,
2986
3123
  beforeSubmit,
2987
3124
  onDraftSave,
3125
+ successRenderMode = "append",
3126
+ hideFormOnSuccess = false,
2988
3127
  submissionGuards = [],
2989
3128
  receiptStore,
2990
3129
  attemptStore,
@@ -3000,9 +3139,11 @@ function ContextFormRenderer({
3000
3139
  const [focusFieldId, setFocusFieldId] = (0, import_react5.useState)(null);
3001
3140
  const [confirmation, setConfirmation] = (0, import_react5.useState)(null);
3002
3141
  const [guardMessage, setGuardMessage] = (0, import_react5.useState)(null);
3142
+ const [guardsPending, setGuardsPending] = (0, import_react5.useState)(false);
3003
3143
  const [receipt, setReceipt] = (0, import_react5.useState)(null);
3004
3144
  const [receiptLoaded, setReceiptLoaded] = (0, import_react5.useState)(receiptStore === void 0);
3005
3145
  const rendererSubmissionInFlight = (0, import_react5.useRef)(false);
3146
+ const completionRef = (0, import_react5.useRef)(null);
3006
3147
  const pages = form.schema.pages;
3007
3148
  const visiblePageIndexes = (0, import_react5.useMemo)(
3008
3149
  () => pages === void 0 ? [] : pages.flatMap((page, index) => form.pageVisibility[page.id] === true ? [index] : []),
@@ -3012,8 +3153,9 @@ function ContextFormRenderer({
3012
3153
  const activeVisibleIndex = visiblePageIndexes.indexOf(currentPageIndex);
3013
3154
  const fieldIds = activePage === void 0 ? void 0 : new Set(activePage.questionIds);
3014
3155
  const visibleValues = (0, import_react5.useMemo)(() => (0, import_core4.selectVisibleAnswers)(form.schema, form.values), [form.schema, form.values]);
3015
- const submitState = confirmation === null ? form.submitStatus : "confirming";
3156
+ const submitState = confirmation === null && !guardsPending ? form.submitStatus : "confirming";
3016
3157
  const interactionLocked = submitState === "confirming" || submitState === "submitting";
3158
+ const isReplaceMode = successRenderMode === "replace" || hideFormOnSuccess;
3017
3159
  (0, import_react5.useEffect)(() => {
3018
3160
  let active = true;
3019
3161
  if (receiptStore === void 0) {
@@ -3053,6 +3195,10 @@ function ContextFormRenderer({
3053
3195
  setFocusFieldId(null);
3054
3196
  }
3055
3197
  }, [focusFieldId]);
3198
+ (0, import_react5.useEffect)(() => {
3199
+ if (!isReplaceMode || form.submitStatus !== "success") return;
3200
+ completionRef.current?.focus();
3201
+ }, [form.submitStatus, isReplaceMode]);
3056
3202
  (0, import_react5.useEffect)(() => {
3057
3203
  if (autoSaveKey === void 0 || typeof globalThis.localStorage === "undefined") return;
3058
3204
  const loadIdentity = `${autoSaveKey}:${form.schema.id}:${form.schema.version}`;
@@ -3118,13 +3264,14 @@ function ContextFormRenderer({
3118
3264
  };
3119
3265
  };
3120
3266
  const submitValues = async (guardsConfirmed = false) => {
3121
- if (rendererSubmissionInFlight.current || confirmation !== null && !guardsConfirmed) {
3267
+ if (rendererSubmissionInFlight.current || submitState === "submitting" || submitState === "success" || confirmation !== null && !guardsConfirmed) {
3122
3268
  return { status: "cancelled" };
3123
3269
  }
3124
3270
  const validation = (0, import_core4.validateAnswers)(form.schema, form.values);
3125
3271
  const firstInvalidFieldId = validation.issues[0]?.fieldId;
3126
3272
  if (validation.valid && !guardsConfirmed && submissionGuards.length > 0) {
3127
3273
  rendererSubmissionInFlight.current = true;
3274
+ setGuardsPending(true);
3128
3275
  try {
3129
3276
  const guardResult = await runSubmissionGuards(submissionGuards);
3130
3277
  if (guardResult.status === "block") {
@@ -3141,6 +3288,7 @@ function ContextFormRenderer({
3141
3288
  }
3142
3289
  } finally {
3143
3290
  rendererSubmissionInFlight.current = false;
3291
+ setGuardsPending(false);
3144
3292
  }
3145
3293
  }
3146
3294
  setGuardMessage(null);
@@ -3178,7 +3326,6 @@ function ContextFormRenderer({
3178
3326
  };
3179
3327
  try {
3180
3328
  await receiptStore.save(storedReceipt);
3181
- setReceipt(storedReceipt);
3182
3329
  } catch (cause) {
3183
3330
  const error = cause instanceof Error ? cause : new Error(String(cause));
3184
3331
  try {
@@ -3205,7 +3352,7 @@ function ContextFormRenderer({
3205
3352
  };
3206
3353
  const handleSubmit = (event) => {
3207
3354
  event.preventDefault();
3208
- if (interactionLocked) return;
3355
+ if (submitState === "submitting" || submitState === "confirming" || submitState === "success") return;
3209
3356
  void submitValues();
3210
3357
  };
3211
3358
  const confirmSubmission = () => {
@@ -3224,7 +3371,17 @@ function ContextFormRenderer({
3224
3371
  const validationIssues = Object.values(form.errors).filter((issue) => issue !== void 0);
3225
3372
  const canPrev = pages !== void 0 && activeVisibleIndex > 0;
3226
3373
  const canNext = pages !== void 0 && activeVisibleIndex < visiblePageIndexes.length - 1;
3227
- const renderSubmitButton = () => slots.renderSubmitButton?.({ isSubmitting: interactionLocked, onSubmit: () => void submitValues() }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "fe-submit", type: "submit", disabled: interactionLocked, children: form.translate(form.schema.submitLabelKey ?? "form.submit") });
3374
+ const renderSubmitButton = () => {
3375
+ const submitButtonProps = {
3376
+ isSubmitting: submitState === "submitting",
3377
+ submitStatus: submitState,
3378
+ disabled: interactionLocked || submitState === "success",
3379
+ onSubmit: () => void submitValues()
3380
+ };
3381
+ return slots.renderSubmitButton?.(submitButtonProps) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "fe-submit", type: "submit", disabled: submitButtonProps.disabled, children: form.translate(form.schema.submitLabelKey ?? "form.submit") });
3382
+ };
3383
+ const completionMessage = form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey));
3384
+ const completionRegion = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: completionRef, className: "fe-completion", role: "status", "aria-live": "polite", tabIndex: -1, children: slots.renderCompletion?.({ message: completionMessage }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: completionMessage }) });
3228
3385
  if (!receiptLoaded) return null;
3229
3386
  if (receipt !== null) {
3230
3387
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `fe-form fe-already-submitted ${className}`.trim(), children: slots.renderAlreadySubmitted?.({
@@ -3235,6 +3392,9 @@ function ContextFormRenderer({
3235
3392
  receiptStore === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => void resetReceipt(), children: form.translate("form.submitAnother") })
3236
3393
  ] }) });
3237
3394
  }
3395
+ if (form.submitStatus === "success" && isReplaceMode) {
3396
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `fe-form ${className}`.trim(), children: completionRegion });
3397
+ }
3238
3398
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { ref: formRef, className: `fe-form ${className}`.trim(), noValidate: true, onSubmit: handleSubmit, children: [
3239
3399
  slots.renderHeader?.({
3240
3400
  title: form.schema.title,
@@ -3353,9 +3513,7 @@ function ContextFormRenderer({
3353
3513
  canNext ? null : renderSubmitButton()
3354
3514
  ] }),
3355
3515
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-status", "aria-live": "polite", children: [
3356
- form.submitStatus === "success" ? slots.renderCompletion?.({
3357
- message: form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey))
3358
- }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "status", children: form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey)) }) : null,
3516
+ form.submitStatus === "success" ? completionRegion : null,
3359
3517
  form.submitStatus === "error" && form.submitError !== null ? slots.renderSubmitError?.({ error: form.submitError, onRetry: () => void submitValues() }) ?? (errorMessageKey === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "alert", children: form.translate(errorMessageKey) })) : null
3360
3518
  ] })
3361
3519
  ] });