@form-engine-ts/react 2.9.5 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -4
- package/dist/index.cjs +458 -145
- package/dist/index.d.cts +74 -5
- package/dist/index.d.ts +74 -5
- package/dist/index.js +458 -145
- package/dist/styles.css +51 -0
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,7 @@ __export(index_exports, {
|
|
|
23
23
|
FormBuilder: () => FormBuilder,
|
|
24
24
|
FormProvider: () => FormProvider,
|
|
25
25
|
FormRenderer: () => FormRenderer,
|
|
26
|
+
FormSubmissionError: () => FormSubmissionError,
|
|
26
27
|
createLocalStorageSubmissionAttemptStore: () => createLocalStorageSubmissionAttemptStore,
|
|
27
28
|
createLocalStorageSubmissionReceiptStore: () => createLocalStorageSubmissionReceiptStore,
|
|
28
29
|
resolveInitialFieldType: () => resolveInitialFieldType,
|
|
@@ -1244,6 +1245,11 @@ var BUILDER_DEFAULTS = {
|
|
|
1244
1245
|
"builder.translating": "Translating\u2026",
|
|
1245
1246
|
"builder.translationLocale": "Translation locale",
|
|
1246
1247
|
"builder.selectLocale": "Select a locale to edit translations.",
|
|
1248
|
+
"builder.localization.selectLocaleToAdd": "Select a locale to add",
|
|
1249
|
+
"builder.localization.noLocalesConfigured": "Translations not configured",
|
|
1250
|
+
"builder.localization.localesConfiguredSummary": "{{count}} locales configured",
|
|
1251
|
+
"builder.localization.allLocalesAdded": "\u3059\u3079\u3066\u306E\u5019\u88DC\u8A00\u8A9E\u304C\u8FFD\u52A0\u6E08\u307F\u3067\u3059",
|
|
1252
|
+
"builder.localization.maxLocalesReached": "\u767B\u9332\u53EF\u80FD\u306A\u6700\u5927\u8A00\u8A9E\u6570\uFF08{{max}}\uFF09\u306B\u9054\u3057\u307E\u3057\u305F",
|
|
1247
1253
|
"builder.translation": "{{locale}} translation",
|
|
1248
1254
|
"builder.translatedFormTitle": "Translated form title",
|
|
1249
1255
|
"builder.translatedFormDescription": "Translated form description",
|
|
@@ -2607,6 +2613,18 @@ function FormBuilder({
|
|
|
2607
2613
|
// src/context.tsx
|
|
2608
2614
|
var import_core3 = require("@form-engine-ts/core");
|
|
2609
2615
|
var import_react3 = require("react");
|
|
2616
|
+
|
|
2617
|
+
// src/types.ts
|
|
2618
|
+
var FormSubmissionError = class extends Error {
|
|
2619
|
+
payload;
|
|
2620
|
+
constructor(message, payload) {
|
|
2621
|
+
super(message);
|
|
2622
|
+
this.name = "FormSubmissionError";
|
|
2623
|
+
this.payload = payload ?? { formError: message };
|
|
2624
|
+
}
|
|
2625
|
+
};
|
|
2626
|
+
|
|
2627
|
+
// src/context.tsx
|
|
2610
2628
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
2611
2629
|
var FormContext = (0, import_react3.createContext)(null);
|
|
2612
2630
|
function issuesByField(issues) {
|
|
@@ -2614,6 +2632,18 @@ function issuesByField(issues) {
|
|
|
2614
2632
|
for (const issue of issues) result[issue.fieldId] ??= issue;
|
|
2615
2633
|
return result;
|
|
2616
2634
|
}
|
|
2635
|
+
function defaultAttemptId2() {
|
|
2636
|
+
const randomUuid = globalThis.crypto?.randomUUID;
|
|
2637
|
+
if (typeof randomUuid === "function") return randomUuid.call(globalThis.crypto);
|
|
2638
|
+
return `attempt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
2639
|
+
}
|
|
2640
|
+
function isServerErrorPayload(value) {
|
|
2641
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
2642
|
+
const record = value;
|
|
2643
|
+
const fieldErrors = record.fieldErrors;
|
|
2644
|
+
const formError = record.formError;
|
|
2645
|
+
return (Object.hasOwn(record, "fieldErrors") || Object.hasOwn(record, "formError")) && (fieldErrors === void 0 || typeof fieldErrors === "object" && fieldErrors !== null && !Array.isArray(fieldErrors) && Object.values(fieldErrors).every((message) => typeof message === "string")) && (formError === void 0 || typeof formError === "string");
|
|
2646
|
+
}
|
|
2617
2647
|
function FormProvider({
|
|
2618
2648
|
schema,
|
|
2619
2649
|
locale,
|
|
@@ -2657,6 +2687,17 @@ function FormProvider({
|
|
|
2657
2687
|
},
|
|
2658
2688
|
[validSchema, validationPageIndex]
|
|
2659
2689
|
);
|
|
2690
|
+
const setServerErrors = (0, import_react3.useCallback)((fieldErrors) => {
|
|
2691
|
+
setErrors(
|
|
2692
|
+
Object.fromEntries(
|
|
2693
|
+
Object.entries(fieldErrors).map(([fieldId, message]) => [
|
|
2694
|
+
fieldId,
|
|
2695
|
+
{ fieldId, code: "invalid_type", messageKey: message, params: {} }
|
|
2696
|
+
])
|
|
2697
|
+
)
|
|
2698
|
+
);
|
|
2699
|
+
setValidationPageIndex(null);
|
|
2700
|
+
}, []);
|
|
2660
2701
|
const restoreValues = (0, import_react3.useCallback)(
|
|
2661
2702
|
(restoredValues) => {
|
|
2662
2703
|
const fieldIds = new Set(validSchema.fields.map((field) => field.id));
|
|
@@ -2687,7 +2728,7 @@ function FormProvider({
|
|
|
2687
2728
|
setSubmitError(null);
|
|
2688
2729
|
}, [initialValues]);
|
|
2689
2730
|
const submit = (0, import_react3.useCallback)(
|
|
2690
|
-
async (beforeSubmit,
|
|
2731
|
+
async (beforeSubmit, submitContext) => {
|
|
2691
2732
|
if (submissionInFlight.current) return { status: "cancelled" };
|
|
2692
2733
|
const validation = (0, import_core3.validateAnswers)(validSchema, values);
|
|
2693
2734
|
if (!validation.valid) {
|
|
@@ -2708,13 +2749,22 @@ function FormProvider({
|
|
|
2708
2749
|
setSubmitStatus("idle");
|
|
2709
2750
|
return { status: "cancelled" };
|
|
2710
2751
|
}
|
|
2711
|
-
const
|
|
2712
|
-
|
|
2752
|
+
const context = submitContext ?? {
|
|
2753
|
+
attemptId: defaultAttemptId2(),
|
|
2754
|
+
formId: validSchema.id,
|
|
2755
|
+
formVersion: validSchema.version,
|
|
2756
|
+
locale,
|
|
2757
|
+
submittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2758
|
+
};
|
|
2759
|
+
const response = await onSubmit({ ...visibleValues }, context);
|
|
2713
2760
|
if (resetOnSuccess) setValues({ ...initialValues });
|
|
2714
2761
|
setSubmitStatus("success");
|
|
2715
2762
|
return response === void 0 ? { status: "success" } : { status: "success", response };
|
|
2716
2763
|
} catch (cause) {
|
|
2717
|
-
const error = cause
|
|
2764
|
+
const error = isServerErrorPayload(cause) ? new FormSubmissionError(
|
|
2765
|
+
cause.formError ?? (cause instanceof Error ? cause.message : "Form submission failed."),
|
|
2766
|
+
cause
|
|
2767
|
+
) : cause instanceof Error ? cause : new Error(String(cause));
|
|
2718
2768
|
setSubmitError(error);
|
|
2719
2769
|
setSubmitStatus("error");
|
|
2720
2770
|
return { status: "error", error };
|
|
@@ -2722,7 +2772,7 @@ function FormProvider({
|
|
|
2722
2772
|
submissionInFlight.current = false;
|
|
2723
2773
|
}
|
|
2724
2774
|
},
|
|
2725
|
-
[initialValues, onSubmit, resetOnSuccess, validSchema, values]
|
|
2775
|
+
[initialValues, locale, onSubmit, resetOnSuccess, validSchema, values]
|
|
2726
2776
|
);
|
|
2727
2777
|
const translate = (0, import_react3.useCallback)(
|
|
2728
2778
|
(key, params) => translator.translate(key, locale, params),
|
|
@@ -2741,6 +2791,7 @@ function FormProvider({
|
|
|
2741
2791
|
submitError,
|
|
2742
2792
|
isSubmitting: submitStatus === "submitting",
|
|
2743
2793
|
setValue,
|
|
2794
|
+
setServerErrors,
|
|
2744
2795
|
restoreValues,
|
|
2745
2796
|
validatePage,
|
|
2746
2797
|
reset,
|
|
@@ -2754,6 +2805,7 @@ function FormProvider({
|
|
|
2754
2805
|
reset,
|
|
2755
2806
|
restoreValues,
|
|
2756
2807
|
setValue,
|
|
2808
|
+
setServerErrors,
|
|
2757
2809
|
submit,
|
|
2758
2810
|
submitError,
|
|
2759
2811
|
submitStatus,
|
|
@@ -3086,7 +3138,11 @@ function DefaultField(props) {
|
|
|
3086
3138
|
fieldId: field.id,
|
|
3087
3139
|
current: typeof value === "string" ? value.length : 0,
|
|
3088
3140
|
max: field.maxLength
|
|
3089
|
-
}) :
|
|
3141
|
+
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-character-count", "aria-live": "polite", children: [
|
|
3142
|
+
typeof value === "string" ? value.length : 0,
|
|
3143
|
+
" / ",
|
|
3144
|
+
field.maxLength
|
|
3145
|
+
] }) : null,
|
|
3090
3146
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FieldMessage, { props })
|
|
3091
3147
|
] });
|
|
3092
3148
|
}
|
|
@@ -3096,6 +3152,30 @@ function isRecord(value) {
|
|
|
3096
3152
|
function isFormValue(value) {
|
|
3097
3153
|
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");
|
|
3098
3154
|
}
|
|
3155
|
+
function displaySubmittedValue(field, value, translate) {
|
|
3156
|
+
if (value === void 0 || value === null) return "";
|
|
3157
|
+
if (field.type === "checkbox") return value === true ? translate("form.yes") : translate("form.no");
|
|
3158
|
+
if (field.type === "multi-select" && Array.isArray(value)) {
|
|
3159
|
+
const labels = new Map(field.options.map((option) => [option.id, option.label]));
|
|
3160
|
+
return value.map((item) => labels.get(item) ?? item).join(", ");
|
|
3161
|
+
}
|
|
3162
|
+
if ((field.type === "radio" || field.type === "select") && typeof value === "string") {
|
|
3163
|
+
return field.options.find((option) => option.id === value)?.label ?? value;
|
|
3164
|
+
}
|
|
3165
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
3166
|
+
return String(value);
|
|
3167
|
+
}
|
|
3168
|
+
function buildSubmittedItems(schema, answers, visibility, translate, showHiddenFields) {
|
|
3169
|
+
return schema.fields.filter((field) => showHiddenFields || visibility[field.id] === true).map((field) => ({
|
|
3170
|
+
fieldId: field.id,
|
|
3171
|
+
title: field.title,
|
|
3172
|
+
type: field.type,
|
|
3173
|
+
rawValue: answers[field.id],
|
|
3174
|
+
displayValue: displaySubmittedValue(field, answers[field.id], translate),
|
|
3175
|
+
visible: visibility[field.id] === true,
|
|
3176
|
+
...field.metadata === void 0 ? {} : { metadata: field.metadata }
|
|
3177
|
+
}));
|
|
3178
|
+
}
|
|
3099
3179
|
function parseDraft(serialized) {
|
|
3100
3180
|
try {
|
|
3101
3181
|
const value = JSON.parse(serialized);
|
|
@@ -3114,15 +3194,65 @@ function parseDraft(serialized) {
|
|
|
3114
3194
|
return null;
|
|
3115
3195
|
}
|
|
3116
3196
|
}
|
|
3197
|
+
var DEFAULT_RENDERER_MESSAGES = {
|
|
3198
|
+
en: {
|
|
3199
|
+
submitButton: "Submit",
|
|
3200
|
+
submittingButton: "Submitting...",
|
|
3201
|
+
retryButton: "Retry",
|
|
3202
|
+
requiredField: "This field is required.",
|
|
3203
|
+
alreadySubmittedTitle: "Already Submitted",
|
|
3204
|
+
alreadySubmittedMessage: "Already submitted.",
|
|
3205
|
+
serverErrorSummary: "Submission failed. Please check your answers and try again.",
|
|
3206
|
+
confirmSensitiveDataTitle: "Sensitive data may be included",
|
|
3207
|
+
confirmSensitiveDataMessage: "The following answers may contain personal information. Continue submitting?",
|
|
3208
|
+
confirmButton: "Proceed",
|
|
3209
|
+
cancelButton: "Cancel"
|
|
3210
|
+
},
|
|
3211
|
+
ja: {
|
|
3212
|
+
submitButton: "\u9001\u4FE1\u3059\u308B",
|
|
3213
|
+
submittingButton: "\u9001\u4FE1\u4E2D...",
|
|
3214
|
+
retryButton: "\u518D\u9001\u4FE1\u3059\u308B",
|
|
3215
|
+
requiredField: "\u3053\u306E\u9805\u76EE\u306F\u5FC5\u9808\u3067\u3059",
|
|
3216
|
+
alreadySubmittedTitle: "\u56DE\u7B54\u6E08\u307F\u3067\u3059",
|
|
3217
|
+
alreadySubmittedMessage: "\u3053\u306E\u30A2\u30F3\u30B1\u30FC\u30C8\u306B\u306F\u3059\u3067\u306B\u56DE\u7B54\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
3218
|
+
serverErrorSummary: "\u9001\u4FE1\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002\u5185\u5BB9\u3092\u3054\u78BA\u8A8D\u306E\u4E0A\u3001\u518D\u5EA6\u304A\u8A66\u3057\u304F\u3060\u3055\u3044\u3002",
|
|
3219
|
+
confirmSensitiveDataTitle: "\u500B\u4EBA\u60C5\u5831\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059",
|
|
3220
|
+
confirmSensitiveDataMessage: "\u4EE5\u4E0B\u306E\u9805\u76EE\u306B\u500B\u4EBA\u60C5\u5831\u3068\u307F\u3089\u308C\u308B\u8A18\u8FF0\u304C\u3042\u308A\u307E\u3059\u3002\u3053\u306E\u307E\u307E\u9001\u4FE1\u3057\u3066\u3082\u3088\u308D\u3057\u3044\u3067\u3059\u304B\uFF1F",
|
|
3221
|
+
confirmButton: "\u3053\u306E\u307E\u307E\u9001\u4FE1",
|
|
3222
|
+
cancelButton: "\u4FEE\u6B63\u3059\u308B"
|
|
3223
|
+
}
|
|
3224
|
+
};
|
|
3225
|
+
function createRendererAttemptId() {
|
|
3226
|
+
const randomUuid = globalThis.crypto?.randomUUID;
|
|
3227
|
+
if (typeof randomUuid === "function") return randomUuid.call(globalThis.crypto);
|
|
3228
|
+
return `attempt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
3229
|
+
}
|
|
3230
|
+
function maskSensitiveValue(finding) {
|
|
3231
|
+
if (finding.maskedText !== void 0) return finding.maskedText;
|
|
3232
|
+
const value = finding.matchedText;
|
|
3233
|
+
if (value === void 0) return void 0;
|
|
3234
|
+
if (finding.type === "email") {
|
|
3235
|
+
const separator = value.indexOf("@");
|
|
3236
|
+
if (separator > 0) return `${value.slice(0, Math.min(2, separator))}***${value.slice(separator)}`;
|
|
3237
|
+
}
|
|
3238
|
+
if (finding.type === "phone" || finding.type === "postal_code") return "***";
|
|
3239
|
+
return value.length <= 2 ? "***" : `${value.slice(0, 2)}***`;
|
|
3240
|
+
}
|
|
3117
3241
|
function ContextFormRenderer({
|
|
3118
3242
|
components = {},
|
|
3119
3243
|
className = "",
|
|
3120
3244
|
successMessageKey,
|
|
3121
3245
|
errorMessageKey,
|
|
3246
|
+
attemptIdFactory,
|
|
3247
|
+
messages = {},
|
|
3248
|
+
messageResolver,
|
|
3122
3249
|
autoSaveKey,
|
|
3123
3250
|
beforeSubmit,
|
|
3124
3251
|
onDraftSave,
|
|
3125
3252
|
successRenderMode = "append",
|
|
3253
|
+
submissionConfirmationRenderMode = "inline",
|
|
3254
|
+
showHiddenFieldsInSummary = false,
|
|
3255
|
+
fieldsClassName,
|
|
3126
3256
|
hideFormOnSuccess = false,
|
|
3127
3257
|
submissionGuards = [],
|
|
3128
3258
|
receiptStore,
|
|
@@ -3141,9 +3271,12 @@ function ContextFormRenderer({
|
|
|
3141
3271
|
const [guardMessage, setGuardMessage] = (0, import_react5.useState)(null);
|
|
3142
3272
|
const [guardsPending, setGuardsPending] = (0, import_react5.useState)(false);
|
|
3143
3273
|
const [receipt, setReceipt] = (0, import_react5.useState)(null);
|
|
3274
|
+
const [completionData, setCompletionData] = (0, import_react5.useState)(null);
|
|
3144
3275
|
const [receiptLoaded, setReceiptLoaded] = (0, import_react5.useState)(receiptStore === void 0);
|
|
3145
3276
|
const rendererSubmissionInFlight = (0, import_react5.useRef)(false);
|
|
3277
|
+
const fallbackAttemptId = (0, import_react5.useRef)(null);
|
|
3146
3278
|
const completionRef = (0, import_react5.useRef)(null);
|
|
3279
|
+
const confirmationRef = (0, import_react5.useRef)(null);
|
|
3147
3280
|
const pages = form.schema.pages;
|
|
3148
3281
|
const visiblePageIndexes = (0, import_react5.useMemo)(
|
|
3149
3282
|
() => pages === void 0 ? [] : pages.flatMap((page, index) => form.pageVisibility[page.id] === true ? [index] : []),
|
|
@@ -3156,6 +3289,22 @@ function ContextFormRenderer({
|
|
|
3156
3289
|
const submitState = confirmation === null && !guardsPending ? form.submitStatus : "confirming";
|
|
3157
3290
|
const interactionLocked = submitState === "confirming" || submitState === "submitting";
|
|
3158
3291
|
const isReplaceMode = successRenderMode === "replace" || hideFormOnSuccess;
|
|
3292
|
+
const resolveMessage = (0, import_react5.useCallback)(
|
|
3293
|
+
(key, fallback) => {
|
|
3294
|
+
const defaultText = fallback ?? DEFAULT_RENDERER_MESSAGES[form.locale.toLowerCase().startsWith("ja") ? "ja" : "en"][key] ?? key;
|
|
3295
|
+
const configured = messages[key];
|
|
3296
|
+
return messageResolver?.(key, configured ?? defaultText) ?? configured ?? defaultText;
|
|
3297
|
+
},
|
|
3298
|
+
[form.locale, messageResolver, messages]
|
|
3299
|
+
);
|
|
3300
|
+
const fieldTranslate = (0, import_react5.useCallback)(
|
|
3301
|
+
(key, params) => key === "validation.required" && (messages.requiredField !== void 0 || messageResolver !== void 0) ? resolveMessage("requiredField") : form.translate(key, params),
|
|
3302
|
+
[form.translate, messageResolver, messages.requiredField, resolveMessage]
|
|
3303
|
+
);
|
|
3304
|
+
const focusSubmitButton = (0, import_react5.useCallback)(() => {
|
|
3305
|
+
const button = formRef.current?.querySelector(".fe-submit, button[type='submit'], button");
|
|
3306
|
+
button?.focus();
|
|
3307
|
+
}, []);
|
|
3159
3308
|
(0, import_react5.useEffect)(() => {
|
|
3160
3309
|
let active = true;
|
|
3161
3310
|
if (receiptStore === void 0) {
|
|
@@ -3191,6 +3340,7 @@ function ContextFormRenderer({
|
|
|
3191
3340
|
);
|
|
3192
3341
|
const control = fieldContainer?.querySelector("input, select, textarea");
|
|
3193
3342
|
if (control !== void 0 && control !== null) {
|
|
3343
|
+
control.scrollIntoView?.({ behavior: "smooth", block: "center" });
|
|
3194
3344
|
control.focus();
|
|
3195
3345
|
setFocusFieldId(null);
|
|
3196
3346
|
}
|
|
@@ -3199,6 +3349,38 @@ function ContextFormRenderer({
|
|
|
3199
3349
|
if (!isReplaceMode || form.submitStatus !== "success") return;
|
|
3200
3350
|
completionRef.current?.focus();
|
|
3201
3351
|
}, [form.submitStatus, isReplaceMode]);
|
|
3352
|
+
(0, import_react5.useEffect)(() => {
|
|
3353
|
+
if (confirmation === null) return;
|
|
3354
|
+
const confirmButton = confirmationRef.current?.querySelector("[data-fe-confirm], button");
|
|
3355
|
+
confirmButton?.focus();
|
|
3356
|
+
if (submissionConfirmationRenderMode !== "dialog") return;
|
|
3357
|
+
const onKeyDown = (event) => {
|
|
3358
|
+
if (event.key === "Escape") {
|
|
3359
|
+
event.preventDefault();
|
|
3360
|
+
setConfirmation(null);
|
|
3361
|
+
globalThis.setTimeout(focusSubmitButton, 0);
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3364
|
+
if (event.key !== "Tab") return;
|
|
3365
|
+
const focusable = [
|
|
3366
|
+
...confirmationRef.current?.querySelectorAll(
|
|
3367
|
+
"button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])"
|
|
3368
|
+
) ?? []
|
|
3369
|
+
].filter((element) => !element.hasAttribute("disabled"));
|
|
3370
|
+
if (focusable.length === 0) return;
|
|
3371
|
+
const first = focusable[0];
|
|
3372
|
+
const last = focusable[focusable.length - 1];
|
|
3373
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
3374
|
+
event.preventDefault();
|
|
3375
|
+
last?.focus();
|
|
3376
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
3377
|
+
event.preventDefault();
|
|
3378
|
+
first?.focus();
|
|
3379
|
+
}
|
|
3380
|
+
};
|
|
3381
|
+
globalThis.addEventListener("keydown", onKeyDown);
|
|
3382
|
+
return () => globalThis.removeEventListener("keydown", onKeyDown);
|
|
3383
|
+
}, [confirmation, focusSubmitButton, submissionConfirmationRenderMode]);
|
|
3202
3384
|
(0, import_react5.useEffect)(() => {
|
|
3203
3385
|
if (autoSaveKey === void 0 || typeof globalThis.localStorage === "undefined") return;
|
|
3204
3386
|
const loadIdentity = `${autoSaveKey}:${form.schema.id}:${form.schema.version}`;
|
|
@@ -3295,17 +3477,24 @@ function ContextFormRenderer({
|
|
|
3295
3477
|
rendererSubmissionInFlight.current = true;
|
|
3296
3478
|
try {
|
|
3297
3479
|
let submissionAttempt;
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3480
|
+
let attemptId = fallbackAttemptId.current;
|
|
3481
|
+
if (attemptStore !== void 0) {
|
|
3482
|
+
submissionAttempt = await attemptStore.getOrCreate(form.schema.id, form.schema.version, attemptIdFactory);
|
|
3483
|
+
attemptId = submissionAttempt.attemptId;
|
|
3484
|
+
} else if (attemptId === null) {
|
|
3485
|
+
attemptId = attemptIdFactory?.() ?? createRendererAttemptId();
|
|
3486
|
+
fallbackAttemptId.current = attemptId;
|
|
3487
|
+
}
|
|
3488
|
+
if (attemptId === null) throw new Error("Unable to create a submission attempt id.");
|
|
3489
|
+
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3490
|
+
const submitContext = {
|
|
3491
|
+
attemptId,
|
|
3492
|
+
formId: form.schema.id,
|
|
3493
|
+
formVersion: form.schema.version,
|
|
3494
|
+
locale: form.locale,
|
|
3495
|
+
submittedAt
|
|
3496
|
+
};
|
|
3497
|
+
const result = await form.submit(beforeSubmit, submitContext);
|
|
3309
3498
|
if (result.status === "invalid") {
|
|
3310
3499
|
const invalidPageIndex = pages?.findIndex(
|
|
3311
3500
|
(page) => firstInvalidFieldId === void 0 ? false : page.questionIds.includes(firstInvalidFieldId)
|
|
@@ -3314,14 +3503,40 @@ function ContextFormRenderer({
|
|
|
3314
3503
|
focusFirstIssue(firstInvalidFieldId);
|
|
3315
3504
|
return result;
|
|
3316
3505
|
}
|
|
3506
|
+
if (result.status === "error") {
|
|
3507
|
+
if (result.error instanceof FormSubmissionError) {
|
|
3508
|
+
const fieldErrors = result.error.payload.fieldErrors ?? {};
|
|
3509
|
+
form.setServerErrors?.(fieldErrors);
|
|
3510
|
+
const firstServerFieldId = form.schema.fields.find((field) => Object.hasOwn(fieldErrors, field.id))?.id ?? Object.keys(fieldErrors)[0];
|
|
3511
|
+
if (firstServerFieldId !== void 0) {
|
|
3512
|
+
const invalidPageIndex = pages?.findIndex((page) => page.questionIds.includes(firstServerFieldId));
|
|
3513
|
+
if (invalidPageIndex !== void 0 && invalidPageIndex >= 0) setCurrentPageIndex(invalidPageIndex);
|
|
3514
|
+
focusFirstIssue(firstServerFieldId);
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
return result;
|
|
3518
|
+
}
|
|
3317
3519
|
if (result.status !== "success") return result;
|
|
3520
|
+
const submittedAnswers = { ...form.values };
|
|
3521
|
+
const submittedItems = buildSubmittedItems(
|
|
3522
|
+
form.schema,
|
|
3523
|
+
submittedAnswers,
|
|
3524
|
+
form.visibility,
|
|
3525
|
+
(key) => form.translate(key),
|
|
3526
|
+
showHiddenFieldsInSummary
|
|
3527
|
+
);
|
|
3528
|
+
setCompletionData({
|
|
3529
|
+
answers: submittedAnswers,
|
|
3530
|
+
submittedItems,
|
|
3531
|
+
...result.response === void 0 ? {} : { response: result.response }
|
|
3532
|
+
});
|
|
3318
3533
|
if (receiptStore !== void 0) {
|
|
3319
3534
|
const response = result.response;
|
|
3320
|
-
const submissionId = response?.submissionId ?? submissionAttempt?.attemptId;
|
|
3535
|
+
const submissionId = response?.submissionId ?? submissionAttempt?.attemptId ?? attemptId;
|
|
3321
3536
|
const storedReceipt = {
|
|
3322
3537
|
formId: form.schema.id,
|
|
3323
3538
|
formVersion: form.schema.version,
|
|
3324
|
-
submittedAt: response?.submittedAt ??
|
|
3539
|
+
submittedAt: response?.submittedAt ?? submittedAt,
|
|
3325
3540
|
...submissionId === void 0 ? {} : { submissionId }
|
|
3326
3541
|
};
|
|
3327
3542
|
try {
|
|
@@ -3340,6 +3555,7 @@ function ContextFormRenderer({
|
|
|
3340
3555
|
} catch {
|
|
3341
3556
|
}
|
|
3342
3557
|
}
|
|
3558
|
+
if (attemptStore === void 0) fallbackAttemptId.current = null;
|
|
3343
3559
|
if (autoSaveKey !== void 0 && typeof globalThis.localStorage !== "undefined") {
|
|
3344
3560
|
globalThis.localStorage.removeItem(autoSaveKey);
|
|
3345
3561
|
setDraftRestored(false);
|
|
@@ -3361,6 +3577,7 @@ function ContextFormRenderer({
|
|
|
3361
3577
|
};
|
|
3362
3578
|
const cancelSubmission = () => {
|
|
3363
3579
|
setConfirmation(null);
|
|
3580
|
+
globalThis.setTimeout(focusSubmitButton, 0);
|
|
3364
3581
|
};
|
|
3365
3582
|
const resetReceipt = async () => {
|
|
3366
3583
|
if (receiptStore === void 0) return;
|
|
@@ -3378,144 +3595,220 @@ function ContextFormRenderer({
|
|
|
3378
3595
|
disabled: interactionLocked || submitState === "success",
|
|
3379
3596
|
onSubmit: () => void submitValues()
|
|
3380
3597
|
};
|
|
3381
|
-
return slots.renderSubmitButton?.(submitButtonProps) ?? /* @__PURE__ */ (0, import_jsx_runtime3.
|
|
3598
|
+
return slots.renderSubmitButton?.(submitButtonProps) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { className: "fe-submit", type: "submit", disabled: submitButtonProps.disabled, children: [
|
|
3599
|
+
submitState === "submitting" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fe-spinner", "aria-hidden": "true" }) : null,
|
|
3600
|
+
submitState === "submitting" ? resolveMessage("submittingButton") : resolveMessage("submitButton", form.translate(form.schema.submitLabelKey ?? "form.submit"))
|
|
3601
|
+
] });
|
|
3382
3602
|
};
|
|
3383
3603
|
const completionMessage = form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey));
|
|
3384
|
-
const
|
|
3604
|
+
const activeCompletionData = completionData ?? {
|
|
3605
|
+
answers: { ...form.values },
|
|
3606
|
+
submittedItems: buildSubmittedItems(
|
|
3607
|
+
form.schema,
|
|
3608
|
+
form.values,
|
|
3609
|
+
form.visibility,
|
|
3610
|
+
(key) => form.translate(key),
|
|
3611
|
+
showHiddenFieldsInSummary
|
|
3612
|
+
)
|
|
3613
|
+
};
|
|
3614
|
+
const completionProps = {
|
|
3615
|
+
message: completionMessage,
|
|
3616
|
+
schema: form.schema,
|
|
3617
|
+
answers: activeCompletionData.answers,
|
|
3618
|
+
submittedItems: activeCompletionData.submittedItems,
|
|
3619
|
+
...activeCompletionData.response === void 0 ? {} : { response: activeCompletionData.response },
|
|
3620
|
+
onReset: form.reset
|
|
3621
|
+
};
|
|
3622
|
+
const completionRegion = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: completionRef, className: "fe-completion", role: "status", "aria-live": "polite", tabIndex: -1, children: [
|
|
3623
|
+
slots.renderCompletion?.(completionProps) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: completionMessage }),
|
|
3624
|
+
slots.renderSubmittedValues?.({ items: activeCompletionData.submittedItems, schema: form.schema })
|
|
3625
|
+
] });
|
|
3626
|
+
const confirmationContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3627
|
+
"div",
|
|
3628
|
+
{
|
|
3629
|
+
ref: confirmationRef,
|
|
3630
|
+
className: "fe-submission-confirmation",
|
|
3631
|
+
role: submissionConfirmationRenderMode === "dialog" ? void 0 : "dialog",
|
|
3632
|
+
children: slots.renderSubmissionConfirmation?.({
|
|
3633
|
+
findings: confirmation?.findings ?? [],
|
|
3634
|
+
message: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")),
|
|
3635
|
+
schema: form.schema,
|
|
3636
|
+
visibleValues,
|
|
3637
|
+
onConfirm: confirmSubmission,
|
|
3638
|
+
onCancel: cancelSubmission
|
|
3639
|
+
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
3640
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: resolveMessage("confirmSensitiveDataTitle") }),
|
|
3641
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")) }),
|
|
3642
|
+
(confirmation?.findings ?? []).length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { children: (confirmation?.findings ?? []).map((finding, index) => {
|
|
3643
|
+
const field = form.schema.fields.find((candidate) => candidate.id === finding.fieldId);
|
|
3644
|
+
const typeLabels = form.locale.toLowerCase().startsWith("ja") ? { email: "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9", phone: "\u96FB\u8A71\u756A\u53F7", url: "URL", postal_code: "\u90F5\u4FBF\u756A\u53F7" } : { email: "Email address", phone: "Phone number", url: "URL", postal_code: "Postal code" };
|
|
3645
|
+
const typeLabel = finding.typeLabel ?? typeLabels[finding.type] ?? finding.type;
|
|
3646
|
+
const value = maskSensitiveValue(finding);
|
|
3647
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
|
|
3648
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: finding.fieldTitle ?? field?.title ?? finding.fieldId }),
|
|
3649
|
+
" ",
|
|
3650
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fe-sensitive-type", children: typeLabel }),
|
|
3651
|
+
value === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "fe-sensitive-value", children: [
|
|
3652
|
+
" ",
|
|
3653
|
+
value
|
|
3654
|
+
] })
|
|
3655
|
+
] }, `${finding.fieldId}-${finding.type}-${finding.start ?? index}`);
|
|
3656
|
+
}) }),
|
|
3657
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "data-fe-confirm": "true", onClick: confirmSubmission, children: resolveMessage("confirmButton", form.translate("form.confirmSubmission")) }),
|
|
3658
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: cancelSubmission, children: resolveMessage("cancelButton", form.translate("form.cancelSubmission")) })
|
|
3659
|
+
] })
|
|
3660
|
+
}
|
|
3661
|
+
);
|
|
3385
3662
|
if (!receiptLoaded) return null;
|
|
3386
3663
|
if (receipt !== null) {
|
|
3387
3664
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `fe-form fe-already-submitted ${className}`.trim(), children: slots.renderAlreadySubmitted?.({
|
|
3388
3665
|
receipt,
|
|
3389
3666
|
...receiptStore === void 0 ? {} : { onReset: () => void resetReceipt() }
|
|
3390
3667
|
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "status", children: [
|
|
3391
|
-
|
|
3392
|
-
|
|
3668
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: resolveMessage("alreadySubmittedTitle") }),
|
|
3669
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: resolveMessage("alreadySubmittedMessage", form.translate("form.alreadySubmitted")) }),
|
|
3670
|
+
receiptStore === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => void resetReceipt(), children: resolveMessage("submitButton", form.translate("form.submitAnother")) })
|
|
3393
3671
|
] }) });
|
|
3394
3672
|
}
|
|
3395
3673
|
if (form.submitStatus === "success" && isReplaceMode) {
|
|
3396
3674
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `fe-form ${className}`.trim(), children: completionRegion });
|
|
3397
3675
|
}
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3676
|
+
if (confirmation !== null && submissionConfirmationRenderMode === "replace") {
|
|
3677
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `fe-form ${className}`.trim(), children: confirmationContent });
|
|
3678
|
+
}
|
|
3679
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
3680
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3681
|
+
"form",
|
|
3682
|
+
{
|
|
3683
|
+
ref: formRef,
|
|
3684
|
+
className: `fe-form ${className}`.trim(),
|
|
3685
|
+
noValidate: true,
|
|
3686
|
+
onSubmit: handleSubmit,
|
|
3687
|
+
"aria-hidden": confirmation !== null && submissionConfirmationRenderMode === "dialog" ? true : void 0,
|
|
3688
|
+
children: [
|
|
3689
|
+
slots.renderHeader?.({
|
|
3690
|
+
title: form.schema.title,
|
|
3691
|
+
...form.schema.description === void 0 ? {} : { description: form.schema.description }
|
|
3692
|
+
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "fe-header", children: [
|
|
3693
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { children: form.schema.title }),
|
|
3694
|
+
form.schema.description === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: form.schema.description }),
|
|
3695
|
+
pages === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-progress", children: [
|
|
3696
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3697
|
+
"div",
|
|
3698
|
+
{
|
|
3699
|
+
className: "form-progress-bar",
|
|
3700
|
+
role: "progressbar",
|
|
3701
|
+
"aria-valuemin": 1,
|
|
3702
|
+
"aria-valuemax": visiblePageIndexes.length,
|
|
3703
|
+
"aria-valuenow": activeVisibleIndex + 1,
|
|
3704
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3705
|
+
"div",
|
|
3706
|
+
{
|
|
3707
|
+
className: "form-progress-fill",
|
|
3708
|
+
style: { width: `${(activeVisibleIndex + 1) / visiblePageIndexes.length * 100}%` }
|
|
3709
|
+
}
|
|
3710
|
+
)
|
|
3711
|
+
}
|
|
3712
|
+
),
|
|
3713
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: form.translate("form.step", { current: activeVisibleIndex + 1, total: visiblePageIndexes.length }) })
|
|
3714
|
+
] }),
|
|
3715
|
+
draftRestored ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "form-draft-badge", children: form.translate("form.draftRestored") }) : null
|
|
3716
|
+
] }),
|
|
3717
|
+
activePage === void 0 ? null : slots.renderPageHeader?.({
|
|
3718
|
+
page: activePage,
|
|
3719
|
+
pageIndex: activeVisibleIndex,
|
|
3720
|
+
totalPages: visiblePageIndexes.length
|
|
3721
|
+
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-page-header", children: [
|
|
3722
|
+
activePage.title === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "fe-page-title", children: activePage.title }),
|
|
3723
|
+
activePage.description === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "fe-page-description", children: activePage.description })
|
|
3724
|
+
] }),
|
|
3725
|
+
(() => {
|
|
3726
|
+
const fieldChildren = form.schema.fields.filter((field) => form.visibility[field.id] === true && (fieldIds === void 0 || fieldIds.has(field.id))).map((field) => {
|
|
3727
|
+
const error = form.errors[field.id];
|
|
3728
|
+
const props = {
|
|
3729
|
+
field,
|
|
3730
|
+
value: form.values[field.id],
|
|
3731
|
+
error,
|
|
3732
|
+
setValue: (value) => form.setValue(field.id, value),
|
|
3733
|
+
translate: fieldTranslate,
|
|
3734
|
+
inputId: `${prefix}-${field.id}`,
|
|
3735
|
+
errorId: `${prefix}-${field.id}-error`,
|
|
3736
|
+
helpId: `${prefix}-${field.id}-help`,
|
|
3737
|
+
...slots.renderCharacterCount === void 0 ? {} : { renderCharacterCount: slots.renderCharacterCount }
|
|
3738
|
+
};
|
|
3739
|
+
if (slots.renderField !== void 0) {
|
|
3740
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react5.Fragment, { children: slots.renderField({
|
|
3741
|
+
question: field,
|
|
3742
|
+
value: form.values[field.id],
|
|
3743
|
+
onChange: (value) => {
|
|
3744
|
+
if (isFormValue(value)) form.setValue(field.id, value);
|
|
3745
|
+
},
|
|
3746
|
+
...error === void 0 ? {} : { error }
|
|
3747
|
+
}) }, field.id);
|
|
3419
3748
|
}
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3749
|
+
const Component = components[field.type];
|
|
3750
|
+
return Component === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DefaultField, { ...props }, field.id) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Component, { ...props }, field.id);
|
|
3751
|
+
});
|
|
3752
|
+
const fieldClassName = `fe-fields${fieldsClassName === void 0 ? "" : ` ${fieldsClassName}`}`;
|
|
3753
|
+
return slots.renderFields?.({ children: fieldChildren, className: fieldClassName }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: fieldClassName, children: fieldChildren });
|
|
3754
|
+
})(),
|
|
3755
|
+
guardMessage === null ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "alert", children: guardMessage }),
|
|
3756
|
+
confirmation !== null && submissionConfirmationRenderMode === "inline" ? confirmationContent : null,
|
|
3757
|
+
validationIssues.length === 0 ? null : slots.renderValidationSummary?.({ issues: validationIssues }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-validation-summary", role: "alert", children: [
|
|
3758
|
+
validationIssues.length,
|
|
3759
|
+
" validation error",
|
|
3760
|
+
validationIssues.length === 1 ? "" : "s",
|
|
3761
|
+
"."
|
|
3762
|
+
] }),
|
|
3763
|
+
pages === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
3764
|
+
slots.renderNavigation?.({
|
|
3765
|
+
currentPage: 0,
|
|
3766
|
+
totalPages: 1,
|
|
3767
|
+
canPrev: false,
|
|
3768
|
+
canNext: false,
|
|
3769
|
+
onPrev: () => void 0,
|
|
3770
|
+
onNext: () => void 0
|
|
3771
|
+
}),
|
|
3772
|
+
renderSubmitButton()
|
|
3773
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "form-step-navigation", children: [
|
|
3774
|
+
slots.renderNavigation?.({
|
|
3775
|
+
currentPage: activeVisibleIndex,
|
|
3776
|
+
totalPages: visiblePageIndexes.length,
|
|
3777
|
+
canPrev,
|
|
3778
|
+
canNext,
|
|
3779
|
+
onPrev: () => {
|
|
3780
|
+
if (!interactionLocked) setCurrentPageIndex(visiblePageIndexes[activeVisibleIndex - 1] ?? 0);
|
|
3781
|
+
},
|
|
3782
|
+
onNext: handleNext
|
|
3783
|
+
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
3784
|
+
canPrev ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3785
|
+
"button",
|
|
3786
|
+
{
|
|
3787
|
+
className: "btn-prev",
|
|
3788
|
+
type: "button",
|
|
3789
|
+
disabled: interactionLocked,
|
|
3790
|
+
onClick: () => setCurrentPageIndex(visiblePageIndexes[activeVisibleIndex - 1] ?? 0),
|
|
3791
|
+
children: form.translate("form.back")
|
|
3792
|
+
}
|
|
3793
|
+
) : null,
|
|
3794
|
+
canNext ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "btn-next", type: "button", disabled: interactionLocked, onClick: handleNext, children: form.translate("form.next") }) : null
|
|
3795
|
+
] }),
|
|
3796
|
+
canNext ? null : renderSubmitButton()
|
|
3797
|
+
] }),
|
|
3798
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-status", "aria-live": "polite", children: [
|
|
3799
|
+
form.submitStatus === "success" ? completionRegion : null,
|
|
3800
|
+
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__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", children: [
|
|
3801
|
+
form.submitError.payload.formError,
|
|
3802
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => void submitValues(), children: resolveMessage("retryButton") })
|
|
3803
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", children: [
|
|
3804
|
+
errorMessageKey === void 0 ? resolveMessage("serverErrorSummary") : form.translate(errorMessageKey),
|
|
3805
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => void submitValues(), children: resolveMessage("retryButton") })
|
|
3806
|
+
] })) : null
|
|
3807
|
+
] })
|
|
3808
|
+
]
|
|
3457
3809
|
}
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
}) }),
|
|
3461
|
-
guardMessage === null ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "alert", children: guardMessage }),
|
|
3462
|
-
confirmation === null ? null : slots.renderSubmissionConfirmation?.({
|
|
3463
|
-
findings: confirmation.findings,
|
|
3464
|
-
message: confirmation.message ?? form.translate("form.confirmSensitiveData"),
|
|
3465
|
-
schema: form.schema,
|
|
3466
|
-
visibleValues,
|
|
3467
|
-
onConfirm: confirmSubmission,
|
|
3468
|
-
onCancel: cancelSubmission
|
|
3469
|
-
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-submission-confirmation", role: "dialog", "aria-modal": "true", children: [
|
|
3470
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: confirmation.message ?? form.translate("form.confirmSensitiveData") }),
|
|
3471
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: confirmSubmission, children: form.translate("form.confirmSubmission") }),
|
|
3472
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: cancelSubmission, children: form.translate("form.cancelSubmission") })
|
|
3473
|
-
] }),
|
|
3474
|
-
validationIssues.length === 0 ? null : slots.renderValidationSummary?.({ issues: validationIssues }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-validation-summary", role: "alert", children: [
|
|
3475
|
-
validationIssues.length,
|
|
3476
|
-
" validation error",
|
|
3477
|
-
validationIssues.length === 1 ? "" : "s",
|
|
3478
|
-
"."
|
|
3479
|
-
] }),
|
|
3480
|
-
pages === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
3481
|
-
slots.renderNavigation?.({
|
|
3482
|
-
currentPage: 0,
|
|
3483
|
-
totalPages: 1,
|
|
3484
|
-
canPrev: false,
|
|
3485
|
-
canNext: false,
|
|
3486
|
-
onPrev: () => void 0,
|
|
3487
|
-
onNext: () => void 0
|
|
3488
|
-
}),
|
|
3489
|
-
renderSubmitButton()
|
|
3490
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "form-step-navigation", children: [
|
|
3491
|
-
slots.renderNavigation?.({
|
|
3492
|
-
currentPage: activeVisibleIndex,
|
|
3493
|
-
totalPages: visiblePageIndexes.length,
|
|
3494
|
-
canPrev,
|
|
3495
|
-
canNext,
|
|
3496
|
-
onPrev: () => {
|
|
3497
|
-
if (!interactionLocked) setCurrentPageIndex(visiblePageIndexes[activeVisibleIndex - 1] ?? 0);
|
|
3498
|
-
},
|
|
3499
|
-
onNext: handleNext
|
|
3500
|
-
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
3501
|
-
canPrev ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3502
|
-
"button",
|
|
3503
|
-
{
|
|
3504
|
-
className: "btn-prev",
|
|
3505
|
-
type: "button",
|
|
3506
|
-
disabled: interactionLocked,
|
|
3507
|
-
onClick: () => setCurrentPageIndex(visiblePageIndexes[activeVisibleIndex - 1] ?? 0),
|
|
3508
|
-
children: form.translate("form.back")
|
|
3509
|
-
}
|
|
3510
|
-
) : null,
|
|
3511
|
-
canNext ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "btn-next", type: "button", disabled: interactionLocked, onClick: handleNext, children: form.translate("form.next") }) : null
|
|
3512
|
-
] }),
|
|
3513
|
-
canNext ? null : renderSubmitButton()
|
|
3514
|
-
] }),
|
|
3515
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-status", "aria-live": "polite", children: [
|
|
3516
|
-
form.submitStatus === "success" ? completionRegion : null,
|
|
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
|
|
3518
|
-
] })
|
|
3810
|
+
),
|
|
3811
|
+
confirmation !== null && submissionConfirmationRenderMode === "dialog" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fe-confirmation-dialog-backdrop", role: "dialog", "aria-modal": "true", children: confirmationContent }) : null
|
|
3519
3812
|
] });
|
|
3520
3813
|
}
|
|
3521
3814
|
var RENDERER_MESSAGES = {
|
|
@@ -3526,15 +3819,34 @@ var RENDERER_MESSAGES = {
|
|
|
3526
3819
|
"form.draftRestored": "Draft restored",
|
|
3527
3820
|
"form.submissionBlocked": "Submission blocked because sensitive data was detected.",
|
|
3528
3821
|
"form.confirmSensitiveData": "Sensitive data may be included. Confirm before submitting.",
|
|
3529
|
-
"form.confirmSubmission": "
|
|
3822
|
+
"form.confirmSubmission": "Proceed",
|
|
3530
3823
|
"form.cancelSubmission": "Cancel",
|
|
3824
|
+
"form.yes": "Yes",
|
|
3825
|
+
"form.no": "No",
|
|
3531
3826
|
"form.alreadySubmitted": "Already submitted.",
|
|
3532
3827
|
"form.submitAnother": "Submit another response",
|
|
3533
3828
|
"validation.required": "This field is required."
|
|
3534
3829
|
};
|
|
3830
|
+
var RENDERER_MESSAGES_JA = {
|
|
3831
|
+
"form.submit": "\u9001\u4FE1\u3059\u308B",
|
|
3832
|
+
"form.back": "\u623B\u308B",
|
|
3833
|
+
"form.next": "\u6B21\u3078",
|
|
3834
|
+
"form.step": "{{current}} / {{total}}",
|
|
3835
|
+
"form.draftRestored": "\u4E0B\u66F8\u304D\u3092\u5FA9\u5143\u3057\u307E\u3057\u305F",
|
|
3836
|
+
"form.submissionBlocked": "\u500B\u4EBA\u60C5\u5831\u304C\u691C\u51FA\u3055\u308C\u305F\u305F\u3081\u9001\u4FE1\u3067\u304D\u307E\u305B\u3093\u3002",
|
|
3837
|
+
"form.confirmSensitiveData": "\u500B\u4EBA\u60C5\u5831\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u9001\u4FE1\u524D\u306B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
3838
|
+
"form.confirmSubmission": "\u3053\u306E\u307E\u307E\u9001\u4FE1",
|
|
3839
|
+
"form.cancelSubmission": "\u4FEE\u6B63\u3059\u308B",
|
|
3840
|
+
"form.yes": "\u306F\u3044",
|
|
3841
|
+
"form.no": "\u3044\u3044\u3048",
|
|
3842
|
+
"form.alreadySubmitted": "\u56DE\u7B54\u6E08\u307F\u3067\u3059",
|
|
3843
|
+
"form.submitAnother": "\u5225\u306E\u56DE\u7B54\u3092\u9001\u4FE1",
|
|
3844
|
+
"validation.required": "\u3053\u306E\u9805\u76EE\u306F\u5FC5\u9808\u3067\u3059"
|
|
3845
|
+
};
|
|
3535
3846
|
var defaultRendererTranslator = {
|
|
3536
|
-
translate(key,
|
|
3537
|
-
|
|
3847
|
+
translate(key, locale, params = {}) {
|
|
3848
|
+
const localizedMessages = locale.toLowerCase().startsWith("ja") ? RENDERER_MESSAGES_JA : RENDERER_MESSAGES;
|
|
3849
|
+
return (localizedMessages[key] ?? key).replace(
|
|
3538
3850
|
/\{\{(\w+)\}\}/g,
|
|
3539
3851
|
(token, name) => Object.hasOwn(params, name) ? String(params[name]) : token
|
|
3540
3852
|
);
|
|
@@ -3569,6 +3881,7 @@ function FormRenderer(props) {
|
|
|
3569
3881
|
FormBuilder,
|
|
3570
3882
|
FormProvider,
|
|
3571
3883
|
FormRenderer,
|
|
3884
|
+
FormSubmissionError,
|
|
3572
3885
|
createLocalStorageSubmissionAttemptStore,
|
|
3573
3886
|
createLocalStorageSubmissionReceiptStore,
|
|
3574
3887
|
resolveInitialFieldType,
|