@form-engine-ts/react 2.9.4 → 2.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -3
- package/dist/index.cjs +377 -143
- package/dist/index.d.cts +67 -7
- package/dist/index.d.ts +67 -7
- package/dist/index.js +377 -143
- package/dist/styles.css +17 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1058,7 +1058,7 @@ function BuilderButton(props) {
|
|
|
1058
1058
|
...props,
|
|
1059
1059
|
className: primitiveClassName(props.className, "feb-button", context.unstyled),
|
|
1060
1060
|
disabled: context.readOnly || props.disabled === true,
|
|
1061
|
-
readOnly: context.readOnly
|
|
1061
|
+
readOnly: context.readOnly || props.readOnly === true
|
|
1062
1062
|
}
|
|
1063
1063
|
);
|
|
1064
1064
|
}
|
|
@@ -1073,7 +1073,7 @@ function BuilderIconButton(props) {
|
|
|
1073
1073
|
icon,
|
|
1074
1074
|
className: primitiveClassName(props.className, "feb-icon-button", context.unstyled),
|
|
1075
1075
|
disabled: context.readOnly || props.disabled === true,
|
|
1076
|
-
readOnly: context.readOnly
|
|
1076
|
+
readOnly: context.readOnly || props.readOnly === true
|
|
1077
1077
|
}
|
|
1078
1078
|
);
|
|
1079
1079
|
}
|
|
@@ -1086,7 +1086,7 @@ function BuilderTextInput(props) {
|
|
|
1086
1086
|
...props,
|
|
1087
1087
|
className: primitiveClassName(props.className, "feb-input", context.unstyled),
|
|
1088
1088
|
disabled: context.readOnly || props.disabled === true,
|
|
1089
|
-
readOnly: context.readOnly
|
|
1089
|
+
readOnly: context.readOnly || props.readOnly === true
|
|
1090
1090
|
}
|
|
1091
1091
|
);
|
|
1092
1092
|
}
|
|
@@ -1099,7 +1099,7 @@ function BuilderTextArea(props) {
|
|
|
1099
1099
|
...props,
|
|
1100
1100
|
className: primitiveClassName(props.className, "feb-textarea", context.unstyled),
|
|
1101
1101
|
disabled: context.readOnly || props.disabled === true,
|
|
1102
|
-
readOnly: context.readOnly
|
|
1102
|
+
readOnly: context.readOnly || props.readOnly === true
|
|
1103
1103
|
}
|
|
1104
1104
|
);
|
|
1105
1105
|
}
|
|
@@ -1112,7 +1112,7 @@ function BuilderSelect(props) {
|
|
|
1112
1112
|
...props,
|
|
1113
1113
|
className: primitiveClassName(props.className, "feb-select", context.unstyled),
|
|
1114
1114
|
disabled: context.readOnly || props.disabled === true,
|
|
1115
|
-
readOnly: context.readOnly
|
|
1115
|
+
readOnly: context.readOnly || props.readOnly === true
|
|
1116
1116
|
}
|
|
1117
1117
|
);
|
|
1118
1118
|
}
|
|
@@ -1125,7 +1125,7 @@ function BuilderCheckbox(props) {
|
|
|
1125
1125
|
...props,
|
|
1126
1126
|
className: primitiveClassName(props.className, "feb-checkbox", context.unstyled),
|
|
1127
1127
|
disabled: context.readOnly || props.disabled === true,
|
|
1128
|
-
readOnly: context.readOnly
|
|
1128
|
+
readOnly: context.readOnly || props.readOnly === true
|
|
1129
1129
|
}
|
|
1130
1130
|
);
|
|
1131
1131
|
}
|
|
@@ -1271,6 +1271,59 @@ function fieldTypeKey(type) {
|
|
|
1271
1271
|
function operatorKey(operator) {
|
|
1272
1272
|
return `builder.operator.${operator}`;
|
|
1273
1273
|
}
|
|
1274
|
+
function manualTranslationContext(schema, locale, target, property, translatedText) {
|
|
1275
|
+
if (target.kind === "form") {
|
|
1276
|
+
if (property !== "title" && property !== "description" && property !== "completionMessage") return void 0;
|
|
1277
|
+
return {
|
|
1278
|
+
locale,
|
|
1279
|
+
kind: "form",
|
|
1280
|
+
nodeId: schema.id,
|
|
1281
|
+
property,
|
|
1282
|
+
sourceText: schema[property] ?? "",
|
|
1283
|
+
translatedText,
|
|
1284
|
+
...schema.translationMetadata?.[locale]?.[property] === void 0 ? {} : { existingTranslationMetadata: schema.translationMetadata[locale]?.[property] }
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
if (target.id === void 0) return void 0;
|
|
1288
|
+
if (target.kind === "field") {
|
|
1289
|
+
const field = schema.fields.find((candidate) => candidate.id === target.id);
|
|
1290
|
+
if (field === void 0 || property !== "title" && property !== "description") return void 0;
|
|
1291
|
+
return {
|
|
1292
|
+
locale,
|
|
1293
|
+
kind: "field",
|
|
1294
|
+
nodeId: field.id,
|
|
1295
|
+
property,
|
|
1296
|
+
sourceText: field[property] ?? "",
|
|
1297
|
+
translatedText,
|
|
1298
|
+
...field.translationMetadata?.[locale]?.[property] === void 0 ? {} : { existingTranslationMetadata: field.translationMetadata[locale]?.[property] }
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
if (target.kind === "page") {
|
|
1302
|
+
const page = schema.pages?.find((candidate) => candidate.id === target.id);
|
|
1303
|
+
if (page === void 0 || property !== "title" && property !== "description") return void 0;
|
|
1304
|
+
return {
|
|
1305
|
+
locale,
|
|
1306
|
+
kind: "page",
|
|
1307
|
+
nodeId: page.id,
|
|
1308
|
+
property,
|
|
1309
|
+
sourceText: page[property] ?? "",
|
|
1310
|
+
translatedText,
|
|
1311
|
+
...page.translationMetadata?.[locale]?.[property] === void 0 ? {} : { existingTranslationMetadata: page.translationMetadata[locale]?.[property] }
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
if (property !== "label") return void 0;
|
|
1315
|
+
const option = schema.fields.filter((field) => "options" in field).flatMap((field) => field.options).find((candidate) => candidate.id === target.id);
|
|
1316
|
+
if (option === void 0) return void 0;
|
|
1317
|
+
return {
|
|
1318
|
+
locale,
|
|
1319
|
+
kind: "option",
|
|
1320
|
+
nodeId: option.id,
|
|
1321
|
+
property,
|
|
1322
|
+
sourceText: option.label,
|
|
1323
|
+
translatedText,
|
|
1324
|
+
...option.translationMetadata?.[locale]?.[property] === void 0 ? {} : { existingTranslationMetadata: option.translationMetadata[locale]?.[property] }
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1274
1327
|
function defaultConditionValue(field) {
|
|
1275
1328
|
if (field.type === "checkbox") return true;
|
|
1276
1329
|
if (field.type === "number" || field.type === "rating") return field.min ?? 1;
|
|
@@ -1529,25 +1582,30 @@ function FormBuilder({
|
|
|
1529
1582
|
setIsTranslating(false);
|
|
1530
1583
|
}
|
|
1531
1584
|
};
|
|
1532
|
-
const
|
|
1533
|
-
if (readOnly) return;
|
|
1534
|
-
const
|
|
1535
|
-
const
|
|
1536
|
-
|
|
1585
|
+
const setManualTranslation = (targetLocale, target, property, text) => {
|
|
1586
|
+
if (readOnly) return { success: true };
|
|
1587
|
+
const normalizedLocale = targetLocale.trim();
|
|
1588
|
+
const context = manualTranslationContext(schema, normalizedLocale, target, property, text);
|
|
1589
|
+
const metadata = context === void 0 ? void 0 : createManualTranslationMetadata?.(context);
|
|
1590
|
+
return executeAction(
|
|
1537
1591
|
() => headless.setLocaleTranslation(
|
|
1538
|
-
|
|
1592
|
+
normalizedLocale,
|
|
1539
1593
|
target,
|
|
1540
|
-
|
|
1541
|
-
|
|
1594
|
+
property,
|
|
1595
|
+
text,
|
|
1542
1596
|
metadata === void 0 ? void 0 : { metadata }
|
|
1543
1597
|
),
|
|
1544
1598
|
{
|
|
1545
|
-
action: "
|
|
1546
|
-
targetId:
|
|
1547
|
-
params: { locale:
|
|
1599
|
+
action: "setManualTranslation",
|
|
1600
|
+
...target.id === void 0 ? {} : { targetId: target.id },
|
|
1601
|
+
params: { locale: normalizedLocale, kind: target.kind, property }
|
|
1548
1602
|
}
|
|
1549
1603
|
);
|
|
1550
1604
|
};
|
|
1605
|
+
const updateManualTranslation = (context) => {
|
|
1606
|
+
const target = context.kind === "form" ? { kind: "form" } : { kind: context.kind, id: context.nodeId };
|
|
1607
|
+
setManualTranslation(context.locale, target, context.property, context.translatedText);
|
|
1608
|
+
};
|
|
1551
1609
|
const updateFormTranslation = (property, translatedText) => {
|
|
1552
1610
|
if (editingLocale.length === 0) return;
|
|
1553
1611
|
updateManualTranslation({
|
|
@@ -1656,6 +1714,7 @@ function FormBuilder({
|
|
|
1656
1714
|
...target.id === void 0 ? {} : { targetId: target.id },
|
|
1657
1715
|
params: { locale: targetLocale, property }
|
|
1658
1716
|
}),
|
|
1717
|
+
setManualTranslation,
|
|
1659
1718
|
addLocale: (targetLocale) => executeAction(() => headless.addLocale(targetLocale), {
|
|
1660
1719
|
action: "addLocale",
|
|
1661
1720
|
params: { locale: targetLocale }
|
|
@@ -2576,6 +2635,17 @@ function FormProvider({
|
|
|
2576
2635
|
},
|
|
2577
2636
|
[validSchema, validationPageIndex]
|
|
2578
2637
|
);
|
|
2638
|
+
const setServerErrors = useCallback2((fieldErrors) => {
|
|
2639
|
+
setErrors(
|
|
2640
|
+
Object.fromEntries(
|
|
2641
|
+
Object.entries(fieldErrors).map(([fieldId, message]) => [
|
|
2642
|
+
fieldId,
|
|
2643
|
+
{ fieldId, code: "invalid_type", messageKey: message, params: {} }
|
|
2644
|
+
])
|
|
2645
|
+
)
|
|
2646
|
+
);
|
|
2647
|
+
setValidationPageIndex(null);
|
|
2648
|
+
}, []);
|
|
2579
2649
|
const restoreValues = useCallback2(
|
|
2580
2650
|
(restoredValues) => {
|
|
2581
2651
|
const fieldIds = new Set(validSchema.fields.map((field) => field.id));
|
|
@@ -2660,6 +2730,7 @@ function FormProvider({
|
|
|
2660
2730
|
submitError,
|
|
2661
2731
|
isSubmitting: submitStatus === "submitting",
|
|
2662
2732
|
setValue,
|
|
2733
|
+
setServerErrors,
|
|
2663
2734
|
restoreValues,
|
|
2664
2735
|
validatePage,
|
|
2665
2736
|
reset,
|
|
@@ -2673,6 +2744,7 @@ function FormProvider({
|
|
|
2673
2744
|
reset,
|
|
2674
2745
|
restoreValues,
|
|
2675
2746
|
setValue,
|
|
2747
|
+
setServerErrors,
|
|
2676
2748
|
submit,
|
|
2677
2749
|
submitError,
|
|
2678
2750
|
submitStatus,
|
|
@@ -2823,12 +2895,25 @@ import {
|
|
|
2823
2895
|
} from "@form-engine-ts/core";
|
|
2824
2896
|
import {
|
|
2825
2897
|
Fragment as Fragment2,
|
|
2898
|
+
useCallback as useCallback3,
|
|
2826
2899
|
useEffect as useEffect3,
|
|
2827
2900
|
useId,
|
|
2828
2901
|
useMemo as useMemo4,
|
|
2829
2902
|
useRef as useRef2,
|
|
2830
2903
|
useState as useState4
|
|
2831
2904
|
} from "react";
|
|
2905
|
+
|
|
2906
|
+
// src/types.ts
|
|
2907
|
+
var FormSubmissionError = class extends Error {
|
|
2908
|
+
payload;
|
|
2909
|
+
constructor(message, payload) {
|
|
2910
|
+
super(message);
|
|
2911
|
+
this.name = "FormSubmissionError";
|
|
2912
|
+
this.payload = payload ?? { formError: message };
|
|
2913
|
+
}
|
|
2914
|
+
};
|
|
2915
|
+
|
|
2916
|
+
// src/renderer.tsx
|
|
2832
2917
|
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2833
2918
|
function describedBy(field, error, helpId, errorId) {
|
|
2834
2919
|
const ids = [field.description === void 0 ? void 0 : helpId, error === void 0 ? void 0 : errorId].filter(
|
|
@@ -3025,6 +3110,30 @@ function isRecord(value) {
|
|
|
3025
3110
|
function isFormValue(value) {
|
|
3026
3111
|
return value === void 0 || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value) || Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
3027
3112
|
}
|
|
3113
|
+
function displaySubmittedValue(field, value, translate) {
|
|
3114
|
+
if (value === void 0 || value === null) return "";
|
|
3115
|
+
if (field.type === "checkbox") return value === true ? translate("form.yes") : translate("form.no");
|
|
3116
|
+
if (field.type === "multi-select" && Array.isArray(value)) {
|
|
3117
|
+
const labels = new Map(field.options.map((option) => [option.id, option.label]));
|
|
3118
|
+
return value.map((item) => labels.get(item) ?? item).join(", ");
|
|
3119
|
+
}
|
|
3120
|
+
if ((field.type === "radio" || field.type === "select") && typeof value === "string") {
|
|
3121
|
+
return field.options.find((option) => option.id === value)?.label ?? value;
|
|
3122
|
+
}
|
|
3123
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
3124
|
+
return String(value);
|
|
3125
|
+
}
|
|
3126
|
+
function buildSubmittedItems(schema, answers, visibility, translate, showHiddenFields) {
|
|
3127
|
+
return schema.fields.filter((field) => showHiddenFields || visibility[field.id] === true).map((field) => ({
|
|
3128
|
+
fieldId: field.id,
|
|
3129
|
+
title: field.title,
|
|
3130
|
+
type: field.type,
|
|
3131
|
+
rawValue: answers[field.id],
|
|
3132
|
+
displayValue: displaySubmittedValue(field, answers[field.id], translate),
|
|
3133
|
+
visible: visibility[field.id] === true,
|
|
3134
|
+
...field.metadata === void 0 ? {} : { metadata: field.metadata }
|
|
3135
|
+
}));
|
|
3136
|
+
}
|
|
3028
3137
|
function parseDraft(serialized) {
|
|
3029
3138
|
try {
|
|
3030
3139
|
const value = JSON.parse(serialized);
|
|
@@ -3051,6 +3160,11 @@ function ContextFormRenderer({
|
|
|
3051
3160
|
autoSaveKey,
|
|
3052
3161
|
beforeSubmit,
|
|
3053
3162
|
onDraftSave,
|
|
3163
|
+
successRenderMode = "append",
|
|
3164
|
+
submissionConfirmationRenderMode = "inline",
|
|
3165
|
+
showHiddenFieldsInSummary = false,
|
|
3166
|
+
fieldsClassName,
|
|
3167
|
+
hideFormOnSuccess = false,
|
|
3054
3168
|
submissionGuards = [],
|
|
3055
3169
|
receiptStore,
|
|
3056
3170
|
attemptStore,
|
|
@@ -3066,9 +3180,13 @@ function ContextFormRenderer({
|
|
|
3066
3180
|
const [focusFieldId, setFocusFieldId] = useState4(null);
|
|
3067
3181
|
const [confirmation, setConfirmation] = useState4(null);
|
|
3068
3182
|
const [guardMessage, setGuardMessage] = useState4(null);
|
|
3183
|
+
const [guardsPending, setGuardsPending] = useState4(false);
|
|
3069
3184
|
const [receipt, setReceipt] = useState4(null);
|
|
3185
|
+
const [completionData, setCompletionData] = useState4(null);
|
|
3070
3186
|
const [receiptLoaded, setReceiptLoaded] = useState4(receiptStore === void 0);
|
|
3071
3187
|
const rendererSubmissionInFlight = useRef2(false);
|
|
3188
|
+
const completionRef = useRef2(null);
|
|
3189
|
+
const confirmationRef = useRef2(null);
|
|
3072
3190
|
const pages = form.schema.pages;
|
|
3073
3191
|
const visiblePageIndexes = useMemo4(
|
|
3074
3192
|
() => pages === void 0 ? [] : pages.flatMap((page, index) => form.pageVisibility[page.id] === true ? [index] : []),
|
|
@@ -3078,8 +3196,13 @@ function ContextFormRenderer({
|
|
|
3078
3196
|
const activeVisibleIndex = visiblePageIndexes.indexOf(currentPageIndex);
|
|
3079
3197
|
const fieldIds = activePage === void 0 ? void 0 : new Set(activePage.questionIds);
|
|
3080
3198
|
const visibleValues = useMemo4(() => selectVisibleAnswers2(form.schema, form.values), [form.schema, form.values]);
|
|
3081
|
-
const submitState = confirmation === null ? form.submitStatus : "confirming";
|
|
3199
|
+
const submitState = confirmation === null && !guardsPending ? form.submitStatus : "confirming";
|
|
3082
3200
|
const interactionLocked = submitState === "confirming" || submitState === "submitting";
|
|
3201
|
+
const isReplaceMode = successRenderMode === "replace" || hideFormOnSuccess;
|
|
3202
|
+
const focusSubmitButton = useCallback3(() => {
|
|
3203
|
+
const button = formRef.current?.querySelector(".fe-submit, button[type='submit'], button");
|
|
3204
|
+
button?.focus();
|
|
3205
|
+
}, []);
|
|
3083
3206
|
useEffect3(() => {
|
|
3084
3207
|
let active = true;
|
|
3085
3208
|
if (receiptStore === void 0) {
|
|
@@ -3119,6 +3242,25 @@ function ContextFormRenderer({
|
|
|
3119
3242
|
setFocusFieldId(null);
|
|
3120
3243
|
}
|
|
3121
3244
|
}, [focusFieldId]);
|
|
3245
|
+
useEffect3(() => {
|
|
3246
|
+
if (!isReplaceMode || form.submitStatus !== "success") return;
|
|
3247
|
+
completionRef.current?.focus();
|
|
3248
|
+
}, [form.submitStatus, isReplaceMode]);
|
|
3249
|
+
useEffect3(() => {
|
|
3250
|
+
if (confirmation === null) return;
|
|
3251
|
+
const confirmButton = confirmationRef.current?.querySelector("[data-fe-confirm], button");
|
|
3252
|
+
confirmButton?.focus();
|
|
3253
|
+
if (submissionConfirmationRenderMode !== "dialog") return;
|
|
3254
|
+
const onKeyDown = (event) => {
|
|
3255
|
+
if (event.key === "Escape") {
|
|
3256
|
+
event.preventDefault();
|
|
3257
|
+
setConfirmation(null);
|
|
3258
|
+
globalThis.setTimeout(focusSubmitButton, 0);
|
|
3259
|
+
}
|
|
3260
|
+
};
|
|
3261
|
+
globalThis.addEventListener("keydown", onKeyDown);
|
|
3262
|
+
return () => globalThis.removeEventListener("keydown", onKeyDown);
|
|
3263
|
+
}, [confirmation, focusSubmitButton, submissionConfirmationRenderMode]);
|
|
3122
3264
|
useEffect3(() => {
|
|
3123
3265
|
if (autoSaveKey === void 0 || typeof globalThis.localStorage === "undefined") return;
|
|
3124
3266
|
const loadIdentity = `${autoSaveKey}:${form.schema.id}:${form.schema.version}`;
|
|
@@ -3184,13 +3326,14 @@ function ContextFormRenderer({
|
|
|
3184
3326
|
};
|
|
3185
3327
|
};
|
|
3186
3328
|
const submitValues = async (guardsConfirmed = false) => {
|
|
3187
|
-
if (rendererSubmissionInFlight.current || confirmation !== null && !guardsConfirmed) {
|
|
3329
|
+
if (rendererSubmissionInFlight.current || submitState === "submitting" || submitState === "success" || confirmation !== null && !guardsConfirmed) {
|
|
3188
3330
|
return { status: "cancelled" };
|
|
3189
3331
|
}
|
|
3190
3332
|
const validation = validateAnswers2(form.schema, form.values);
|
|
3191
3333
|
const firstInvalidFieldId = validation.issues[0]?.fieldId;
|
|
3192
3334
|
if (validation.valid && !guardsConfirmed && submissionGuards.length > 0) {
|
|
3193
3335
|
rendererSubmissionInFlight.current = true;
|
|
3336
|
+
setGuardsPending(true);
|
|
3194
3337
|
try {
|
|
3195
3338
|
const guardResult = await runSubmissionGuards(submissionGuards);
|
|
3196
3339
|
if (guardResult.status === "block") {
|
|
@@ -3207,6 +3350,7 @@ function ContextFormRenderer({
|
|
|
3207
3350
|
}
|
|
3208
3351
|
} finally {
|
|
3209
3352
|
rendererSubmissionInFlight.current = false;
|
|
3353
|
+
setGuardsPending(false);
|
|
3210
3354
|
}
|
|
3211
3355
|
}
|
|
3212
3356
|
setGuardMessage(null);
|
|
@@ -3232,7 +3376,33 @@ function ContextFormRenderer({
|
|
|
3232
3376
|
focusFirstIssue(firstInvalidFieldId);
|
|
3233
3377
|
return result;
|
|
3234
3378
|
}
|
|
3379
|
+
if (result.status === "error") {
|
|
3380
|
+
if (result.error instanceof FormSubmissionError) {
|
|
3381
|
+
const fieldErrors = result.error.payload.fieldErrors ?? {};
|
|
3382
|
+
form.setServerErrors?.(fieldErrors);
|
|
3383
|
+
const firstServerFieldId = Object.keys(fieldErrors)[0];
|
|
3384
|
+
if (firstServerFieldId !== void 0) {
|
|
3385
|
+
const invalidPageIndex = pages?.findIndex((page) => page.questionIds.includes(firstServerFieldId));
|
|
3386
|
+
if (invalidPageIndex !== void 0 && invalidPageIndex >= 0) setCurrentPageIndex(invalidPageIndex);
|
|
3387
|
+
focusFirstIssue(firstServerFieldId);
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
return result;
|
|
3391
|
+
}
|
|
3235
3392
|
if (result.status !== "success") return result;
|
|
3393
|
+
const submittedAnswers = { ...form.values };
|
|
3394
|
+
const submittedItems = buildSubmittedItems(
|
|
3395
|
+
form.schema,
|
|
3396
|
+
submittedAnswers,
|
|
3397
|
+
form.visibility,
|
|
3398
|
+
(key) => form.translate(key),
|
|
3399
|
+
showHiddenFieldsInSummary
|
|
3400
|
+
);
|
|
3401
|
+
setCompletionData({
|
|
3402
|
+
answers: submittedAnswers,
|
|
3403
|
+
submittedItems,
|
|
3404
|
+
...result.response === void 0 ? {} : { response: result.response }
|
|
3405
|
+
});
|
|
3236
3406
|
if (receiptStore !== void 0) {
|
|
3237
3407
|
const response = result.response;
|
|
3238
3408
|
const submissionId = response?.submissionId ?? submissionAttempt?.attemptId;
|
|
@@ -3244,7 +3414,6 @@ function ContextFormRenderer({
|
|
|
3244
3414
|
};
|
|
3245
3415
|
try {
|
|
3246
3416
|
await receiptStore.save(storedReceipt);
|
|
3247
|
-
setReceipt(storedReceipt);
|
|
3248
3417
|
} catch (cause) {
|
|
3249
3418
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
3250
3419
|
try {
|
|
@@ -3271,7 +3440,7 @@ function ContextFormRenderer({
|
|
|
3271
3440
|
};
|
|
3272
3441
|
const handleSubmit = (event) => {
|
|
3273
3442
|
event.preventDefault();
|
|
3274
|
-
if (
|
|
3443
|
+
if (submitState === "submitting" || submitState === "confirming" || submitState === "success") return;
|
|
3275
3444
|
void submitValues();
|
|
3276
3445
|
};
|
|
3277
3446
|
const confirmSubmission = () => {
|
|
@@ -3280,6 +3449,7 @@ function ContextFormRenderer({
|
|
|
3280
3449
|
};
|
|
3281
3450
|
const cancelSubmission = () => {
|
|
3282
3451
|
setConfirmation(null);
|
|
3452
|
+
globalThis.setTimeout(focusSubmitButton, 0);
|
|
3283
3453
|
};
|
|
3284
3454
|
const resetReceipt = async () => {
|
|
3285
3455
|
if (receiptStore === void 0) return;
|
|
@@ -3290,7 +3460,58 @@ function ContextFormRenderer({
|
|
|
3290
3460
|
const validationIssues = Object.values(form.errors).filter((issue) => issue !== void 0);
|
|
3291
3461
|
const canPrev = pages !== void 0 && activeVisibleIndex > 0;
|
|
3292
3462
|
const canNext = pages !== void 0 && activeVisibleIndex < visiblePageIndexes.length - 1;
|
|
3293
|
-
const renderSubmitButton = () =>
|
|
3463
|
+
const renderSubmitButton = () => {
|
|
3464
|
+
const submitButtonProps = {
|
|
3465
|
+
isSubmitting: submitState === "submitting",
|
|
3466
|
+
submitStatus: submitState,
|
|
3467
|
+
disabled: interactionLocked || submitState === "success",
|
|
3468
|
+
onSubmit: () => void submitValues()
|
|
3469
|
+
};
|
|
3470
|
+
return slots.renderSubmitButton?.(submitButtonProps) ?? /* @__PURE__ */ jsx3("button", { className: "fe-submit", type: "submit", disabled: submitButtonProps.disabled, children: form.translate(form.schema.submitLabelKey ?? "form.submit") });
|
|
3471
|
+
};
|
|
3472
|
+
const completionMessage = form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey));
|
|
3473
|
+
const activeCompletionData = completionData ?? {
|
|
3474
|
+
answers: { ...form.values },
|
|
3475
|
+
submittedItems: buildSubmittedItems(
|
|
3476
|
+
form.schema,
|
|
3477
|
+
form.values,
|
|
3478
|
+
form.visibility,
|
|
3479
|
+
(key) => form.translate(key),
|
|
3480
|
+
showHiddenFieldsInSummary
|
|
3481
|
+
)
|
|
3482
|
+
};
|
|
3483
|
+
const completionProps = {
|
|
3484
|
+
message: completionMessage,
|
|
3485
|
+
schema: form.schema,
|
|
3486
|
+
answers: activeCompletionData.answers,
|
|
3487
|
+
submittedItems: activeCompletionData.submittedItems,
|
|
3488
|
+
...activeCompletionData.response === void 0 ? {} : { response: activeCompletionData.response },
|
|
3489
|
+
onReset: form.reset
|
|
3490
|
+
};
|
|
3491
|
+
const completionRegion = /* @__PURE__ */ jsxs2("div", { ref: completionRef, className: "fe-completion", role: "status", "aria-live": "polite", tabIndex: -1, children: [
|
|
3492
|
+
slots.renderCompletion?.(completionProps) ?? /* @__PURE__ */ jsx3("div", { children: completionMessage }),
|
|
3493
|
+
slots.renderSubmittedValues?.({ items: activeCompletionData.submittedItems, schema: form.schema })
|
|
3494
|
+
] });
|
|
3495
|
+
const confirmationContent = /* @__PURE__ */ jsx3(
|
|
3496
|
+
"div",
|
|
3497
|
+
{
|
|
3498
|
+
ref: confirmationRef,
|
|
3499
|
+
className: "fe-submission-confirmation",
|
|
3500
|
+
role: submissionConfirmationRenderMode === "dialog" ? void 0 : "dialog",
|
|
3501
|
+
children: slots.renderSubmissionConfirmation?.({
|
|
3502
|
+
findings: confirmation?.findings ?? [],
|
|
3503
|
+
message: confirmation?.message ?? form.translate("form.confirmSensitiveData"),
|
|
3504
|
+
schema: form.schema,
|
|
3505
|
+
visibleValues,
|
|
3506
|
+
onConfirm: confirmSubmission,
|
|
3507
|
+
onCancel: cancelSubmission
|
|
3508
|
+
}) ?? /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
3509
|
+
/* @__PURE__ */ jsx3("p", { children: confirmation?.message ?? form.translate("form.confirmSensitiveData") }),
|
|
3510
|
+
/* @__PURE__ */ jsx3("button", { type: "button", "data-fe-confirm": "true", onClick: confirmSubmission, children: form.translate("form.confirmSubmission") }),
|
|
3511
|
+
/* @__PURE__ */ jsx3("button", { type: "button", onClick: cancelSubmission, children: form.translate("form.cancelSubmission") })
|
|
3512
|
+
] })
|
|
3513
|
+
}
|
|
3514
|
+
);
|
|
3294
3515
|
if (!receiptLoaded) return null;
|
|
3295
3516
|
if (receipt !== null) {
|
|
3296
3517
|
return /* @__PURE__ */ jsx3("div", { className: `fe-form fe-already-submitted ${className}`.trim(), children: slots.renderAlreadySubmitted?.({
|
|
@@ -3301,129 +3522,139 @@ function ContextFormRenderer({
|
|
|
3301
3522
|
receiptStore === void 0 ? null : /* @__PURE__ */ jsx3("button", { type: "button", onClick: () => void resetReceipt(), children: form.translate("form.submitAnother") })
|
|
3302
3523
|
] }) });
|
|
3303
3524
|
}
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3525
|
+
if (form.submitStatus === "success" && isReplaceMode) {
|
|
3526
|
+
return /* @__PURE__ */ jsx3("div", { className: `fe-form ${className}`.trim(), children: completionRegion });
|
|
3527
|
+
}
|
|
3528
|
+
if (confirmation !== null && submissionConfirmationRenderMode === "replace") {
|
|
3529
|
+
return /* @__PURE__ */ jsx3("div", { className: `fe-form ${className}`.trim(), children: confirmationContent });
|
|
3530
|
+
}
|
|
3531
|
+
return /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
3532
|
+
/* @__PURE__ */ jsxs2(
|
|
3533
|
+
"form",
|
|
3534
|
+
{
|
|
3535
|
+
ref: formRef,
|
|
3536
|
+
className: `fe-form ${className}`.trim(),
|
|
3537
|
+
noValidate: true,
|
|
3538
|
+
onSubmit: handleSubmit,
|
|
3539
|
+
"aria-hidden": confirmation !== null && submissionConfirmationRenderMode === "dialog" ? true : void 0,
|
|
3540
|
+
children: [
|
|
3541
|
+
slots.renderHeader?.({
|
|
3542
|
+
title: form.schema.title,
|
|
3543
|
+
...form.schema.description === void 0 ? {} : { description: form.schema.description }
|
|
3544
|
+
}) ?? /* @__PURE__ */ jsxs2("header", { className: "fe-header", children: [
|
|
3545
|
+
/* @__PURE__ */ jsx3("h1", { children: form.schema.title }),
|
|
3546
|
+
form.schema.description === void 0 ? null : /* @__PURE__ */ jsx3("p", { children: form.schema.description }),
|
|
3547
|
+
pages === void 0 ? null : /* @__PURE__ */ jsxs2("div", { className: "fe-progress", children: [
|
|
3548
|
+
/* @__PURE__ */ jsx3(
|
|
3549
|
+
"div",
|
|
3550
|
+
{
|
|
3551
|
+
className: "form-progress-bar",
|
|
3552
|
+
role: "progressbar",
|
|
3553
|
+
"aria-valuemin": 1,
|
|
3554
|
+
"aria-valuemax": visiblePageIndexes.length,
|
|
3555
|
+
"aria-valuenow": activeVisibleIndex + 1,
|
|
3556
|
+
children: /* @__PURE__ */ jsx3(
|
|
3557
|
+
"div",
|
|
3558
|
+
{
|
|
3559
|
+
className: "form-progress-fill",
|
|
3560
|
+
style: { width: `${(activeVisibleIndex + 1) / visiblePageIndexes.length * 100}%` }
|
|
3561
|
+
}
|
|
3562
|
+
)
|
|
3563
|
+
}
|
|
3564
|
+
),
|
|
3565
|
+
/* @__PURE__ */ jsx3("span", { children: form.translate("form.step", { current: activeVisibleIndex + 1, total: visiblePageIndexes.length }) })
|
|
3566
|
+
] }),
|
|
3567
|
+
draftRestored ? /* @__PURE__ */ jsx3("span", { className: "form-draft-badge", children: form.translate("form.draftRestored") }) : null
|
|
3568
|
+
] }),
|
|
3569
|
+
activePage === void 0 ? null : slots.renderPageHeader?.({
|
|
3570
|
+
page: activePage,
|
|
3571
|
+
pageIndex: activeVisibleIndex,
|
|
3572
|
+
totalPages: visiblePageIndexes.length
|
|
3573
|
+
}) ?? /* @__PURE__ */ jsxs2("div", { className: "fe-page-header", children: [
|
|
3574
|
+
activePage.title === void 0 ? null : /* @__PURE__ */ jsx3("h2", { className: "fe-page-title", children: activePage.title }),
|
|
3575
|
+
activePage.description === void 0 ? null : /* @__PURE__ */ jsx3("p", { className: "fe-page-description", children: activePage.description })
|
|
3576
|
+
] }),
|
|
3577
|
+
(() => {
|
|
3578
|
+
const fieldChildren = form.schema.fields.filter((field) => form.visibility[field.id] === true && (fieldIds === void 0 || fieldIds.has(field.id))).map((field) => {
|
|
3579
|
+
const error = form.errors[field.id];
|
|
3580
|
+
const props = {
|
|
3581
|
+
field,
|
|
3582
|
+
value: form.values[field.id],
|
|
3583
|
+
error,
|
|
3584
|
+
setValue: (value) => form.setValue(field.id, value),
|
|
3585
|
+
translate: form.translate,
|
|
3586
|
+
inputId: `${prefix}-${field.id}`,
|
|
3587
|
+
errorId: `${prefix}-${field.id}-error`,
|
|
3588
|
+
helpId: `${prefix}-${field.id}-help`,
|
|
3589
|
+
...slots.renderCharacterCount === void 0 ? {} : { renderCharacterCount: slots.renderCharacterCount }
|
|
3590
|
+
};
|
|
3591
|
+
if (slots.renderField !== void 0) {
|
|
3592
|
+
return /* @__PURE__ */ jsx3(Fragment2, { children: slots.renderField({
|
|
3593
|
+
question: field,
|
|
3594
|
+
value: form.values[field.id],
|
|
3595
|
+
onChange: (value) => {
|
|
3596
|
+
if (isFormValue(value)) form.setValue(field.id, value);
|
|
3597
|
+
},
|
|
3598
|
+
...error === void 0 ? {} : { error }
|
|
3599
|
+
}) }, field.id);
|
|
3325
3600
|
}
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3601
|
+
const Component = components[field.type];
|
|
3602
|
+
return Component === void 0 ? /* @__PURE__ */ jsx3(DefaultField, { ...props }, field.id) : /* @__PURE__ */ jsx3(Component, { ...props }, field.id);
|
|
3603
|
+
});
|
|
3604
|
+
const fieldClassName = `fe-fields${fieldsClassName === void 0 ? "" : ` ${fieldsClassName}`}`;
|
|
3605
|
+
return slots.renderFields?.({ children: fieldChildren, className: fieldClassName }) ?? /* @__PURE__ */ jsx3("div", { className: fieldClassName, children: fieldChildren });
|
|
3606
|
+
})(),
|
|
3607
|
+
guardMessage === null ? null : /* @__PURE__ */ jsx3("div", { role: "alert", children: guardMessage }),
|
|
3608
|
+
confirmation !== null && submissionConfirmationRenderMode === "inline" ? confirmationContent : null,
|
|
3609
|
+
validationIssues.length === 0 ? null : slots.renderValidationSummary?.({ issues: validationIssues }) ?? /* @__PURE__ */ jsxs2("div", { className: "fe-validation-summary", role: "alert", children: [
|
|
3610
|
+
validationIssues.length,
|
|
3611
|
+
" validation error",
|
|
3612
|
+
validationIssues.length === 1 ? "" : "s",
|
|
3613
|
+
"."
|
|
3614
|
+
] }),
|
|
3615
|
+
pages === void 0 ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
3616
|
+
slots.renderNavigation?.({
|
|
3617
|
+
currentPage: 0,
|
|
3618
|
+
totalPages: 1,
|
|
3619
|
+
canPrev: false,
|
|
3620
|
+
canNext: false,
|
|
3621
|
+
onPrev: () => void 0,
|
|
3622
|
+
onNext: () => void 0
|
|
3623
|
+
}),
|
|
3624
|
+
renderSubmitButton()
|
|
3625
|
+
] }) : /* @__PURE__ */ jsxs2("div", { className: "form-step-navigation", children: [
|
|
3626
|
+
slots.renderNavigation?.({
|
|
3627
|
+
currentPage: activeVisibleIndex,
|
|
3628
|
+
totalPages: visiblePageIndexes.length,
|
|
3629
|
+
canPrev,
|
|
3630
|
+
canNext,
|
|
3631
|
+
onPrev: () => {
|
|
3632
|
+
if (!interactionLocked) setCurrentPageIndex(visiblePageIndexes[activeVisibleIndex - 1] ?? 0);
|
|
3633
|
+
},
|
|
3634
|
+
onNext: handleNext
|
|
3635
|
+
}) ?? /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
3636
|
+
canPrev ? /* @__PURE__ */ jsx3(
|
|
3637
|
+
"button",
|
|
3638
|
+
{
|
|
3639
|
+
className: "btn-prev",
|
|
3640
|
+
type: "button",
|
|
3641
|
+
disabled: interactionLocked,
|
|
3642
|
+
onClick: () => setCurrentPageIndex(visiblePageIndexes[activeVisibleIndex - 1] ?? 0),
|
|
3643
|
+
children: form.translate("form.back")
|
|
3644
|
+
}
|
|
3645
|
+
) : null,
|
|
3646
|
+
canNext ? /* @__PURE__ */ jsx3("button", { className: "btn-next", type: "button", disabled: interactionLocked, onClick: handleNext, children: form.translate("form.next") }) : null
|
|
3647
|
+
] }),
|
|
3648
|
+
canNext ? null : renderSubmitButton()
|
|
3649
|
+
] }),
|
|
3650
|
+
/* @__PURE__ */ jsxs2("div", { className: "fe-status", "aria-live": "polite", children: [
|
|
3651
|
+
form.submitStatus === "success" ? completionRegion : null,
|
|
3652
|
+
form.submitStatus === "error" && form.submitError !== null ? slots.renderSubmitError?.({ error: form.submitError, onRetry: () => void submitValues() }) ?? (form.submitError instanceof FormSubmissionError && form.submitError.payload.formError !== void 0 ? /* @__PURE__ */ jsx3("div", { role: "alert", children: form.submitError.payload.formError }) : errorMessageKey === void 0 ? null : /* @__PURE__ */ jsx3("div", { role: "alert", children: form.translate(errorMessageKey) })) : null
|
|
3653
|
+
] })
|
|
3654
|
+
]
|
|
3363
3655
|
}
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
}) }),
|
|
3367
|
-
guardMessage === null ? null : /* @__PURE__ */ jsx3("div", { role: "alert", children: guardMessage }),
|
|
3368
|
-
confirmation === null ? null : slots.renderSubmissionConfirmation?.({
|
|
3369
|
-
findings: confirmation.findings,
|
|
3370
|
-
message: confirmation.message ?? form.translate("form.confirmSensitiveData"),
|
|
3371
|
-
schema: form.schema,
|
|
3372
|
-
visibleValues,
|
|
3373
|
-
onConfirm: confirmSubmission,
|
|
3374
|
-
onCancel: cancelSubmission
|
|
3375
|
-
}) ?? /* @__PURE__ */ jsxs2("div", { className: "fe-submission-confirmation", role: "dialog", "aria-modal": "true", children: [
|
|
3376
|
-
/* @__PURE__ */ jsx3("p", { children: confirmation.message ?? form.translate("form.confirmSensitiveData") }),
|
|
3377
|
-
/* @__PURE__ */ jsx3("button", { type: "button", onClick: confirmSubmission, children: form.translate("form.confirmSubmission") }),
|
|
3378
|
-
/* @__PURE__ */ jsx3("button", { type: "button", onClick: cancelSubmission, children: form.translate("form.cancelSubmission") })
|
|
3379
|
-
] }),
|
|
3380
|
-
validationIssues.length === 0 ? null : slots.renderValidationSummary?.({ issues: validationIssues }) ?? /* @__PURE__ */ jsxs2("div", { className: "fe-validation-summary", role: "alert", children: [
|
|
3381
|
-
validationIssues.length,
|
|
3382
|
-
" validation error",
|
|
3383
|
-
validationIssues.length === 1 ? "" : "s",
|
|
3384
|
-
"."
|
|
3385
|
-
] }),
|
|
3386
|
-
pages === void 0 ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
3387
|
-
slots.renderNavigation?.({
|
|
3388
|
-
currentPage: 0,
|
|
3389
|
-
totalPages: 1,
|
|
3390
|
-
canPrev: false,
|
|
3391
|
-
canNext: false,
|
|
3392
|
-
onPrev: () => void 0,
|
|
3393
|
-
onNext: () => void 0
|
|
3394
|
-
}),
|
|
3395
|
-
renderSubmitButton()
|
|
3396
|
-
] }) : /* @__PURE__ */ jsxs2("div", { className: "form-step-navigation", children: [
|
|
3397
|
-
slots.renderNavigation?.({
|
|
3398
|
-
currentPage: activeVisibleIndex,
|
|
3399
|
-
totalPages: visiblePageIndexes.length,
|
|
3400
|
-
canPrev,
|
|
3401
|
-
canNext,
|
|
3402
|
-
onPrev: () => {
|
|
3403
|
-
if (!interactionLocked) setCurrentPageIndex(visiblePageIndexes[activeVisibleIndex - 1] ?? 0);
|
|
3404
|
-
},
|
|
3405
|
-
onNext: handleNext
|
|
3406
|
-
}) ?? /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
3407
|
-
canPrev ? /* @__PURE__ */ jsx3(
|
|
3408
|
-
"button",
|
|
3409
|
-
{
|
|
3410
|
-
className: "btn-prev",
|
|
3411
|
-
type: "button",
|
|
3412
|
-
disabled: interactionLocked,
|
|
3413
|
-
onClick: () => setCurrentPageIndex(visiblePageIndexes[activeVisibleIndex - 1] ?? 0),
|
|
3414
|
-
children: form.translate("form.back")
|
|
3415
|
-
}
|
|
3416
|
-
) : null,
|
|
3417
|
-
canNext ? /* @__PURE__ */ jsx3("button", { className: "btn-next", type: "button", disabled: interactionLocked, onClick: handleNext, children: form.translate("form.next") }) : null
|
|
3418
|
-
] }),
|
|
3419
|
-
canNext ? null : renderSubmitButton()
|
|
3420
|
-
] }),
|
|
3421
|
-
/* @__PURE__ */ jsxs2("div", { className: "fe-status", "aria-live": "polite", children: [
|
|
3422
|
-
form.submitStatus === "success" ? slots.renderCompletion?.({
|
|
3423
|
-
message: form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey))
|
|
3424
|
-
}) ?? /* @__PURE__ */ jsx3("div", { role: "status", children: form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey)) }) : null,
|
|
3425
|
-
form.submitStatus === "error" && form.submitError !== null ? slots.renderSubmitError?.({ error: form.submitError, onRetry: () => void submitValues() }) ?? (errorMessageKey === void 0 ? null : /* @__PURE__ */ jsx3("div", { role: "alert", children: form.translate(errorMessageKey) })) : null
|
|
3426
|
-
] })
|
|
3656
|
+
),
|
|
3657
|
+
confirmation !== null && submissionConfirmationRenderMode === "dialog" ? /* @__PURE__ */ jsx3("div", { className: "fe-confirmation-dialog-backdrop", role: "dialog", "aria-modal": "true", children: confirmationContent }) : null
|
|
3427
3658
|
] });
|
|
3428
3659
|
}
|
|
3429
3660
|
var RENDERER_MESSAGES = {
|
|
@@ -3436,6 +3667,8 @@ var RENDERER_MESSAGES = {
|
|
|
3436
3667
|
"form.confirmSensitiveData": "Sensitive data may be included. Confirm before submitting.",
|
|
3437
3668
|
"form.confirmSubmission": "Confirm submission",
|
|
3438
3669
|
"form.cancelSubmission": "Cancel",
|
|
3670
|
+
"form.yes": "Yes",
|
|
3671
|
+
"form.no": "No",
|
|
3439
3672
|
"form.alreadySubmitted": "Already submitted.",
|
|
3440
3673
|
"form.submitAnother": "Submit another response",
|
|
3441
3674
|
"validation.required": "This field is required."
|
|
@@ -3476,6 +3709,7 @@ export {
|
|
|
3476
3709
|
FormBuilder,
|
|
3477
3710
|
FormProvider,
|
|
3478
3711
|
FormRenderer,
|
|
3712
|
+
FormSubmissionError,
|
|
3479
3713
|
createLocalStorageSubmissionAttemptStore,
|
|
3480
3714
|
createLocalStorageSubmissionReceiptStore,
|
|
3481
3715
|
resolveInitialFieldType,
|