@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/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, prepareSubmission) => {
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 submissionValues = prepareSubmission === void 0 ? visibleValues : await prepareSubmission(visibleValues);
2712
- const response = await onSubmit(submissionValues);
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 instanceof Error ? cause : new Error(String(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
- }) : null,
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
- const result = await form.submit(
3299
- beforeSubmit,
3300
- attemptStore === void 0 ? void 0 : async (values) => {
3301
- submissionAttempt = await attemptStore.getOrCreate(form.schema.id, form.schema.version);
3302
- return {
3303
- ...values,
3304
- attemptId: submissionAttempt.attemptId,
3305
- submissionId: submissionAttempt.attemptId
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 ?? (/* @__PURE__ */ new Date()).toISOString(),
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.jsx)("button", { className: "fe-submit", type: "submit", disabled: submitButtonProps.disabled, children: form.translate(form.schema.submitLabelKey ?? "form.submit") });
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 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 }) });
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
- form.translate("form.alreadySubmitted"),
3392
- receiptStore === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => void resetReceipt(), children: form.translate("form.submitAnother") })
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
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { ref: formRef, className: `fe-form ${className}`.trim(), noValidate: true, onSubmit: handleSubmit, children: [
3399
- slots.renderHeader?.({
3400
- title: form.schema.title,
3401
- ...form.schema.description === void 0 ? {} : { description: form.schema.description }
3402
- }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "fe-header", children: [
3403
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { children: form.schema.title }),
3404
- form.schema.description === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: form.schema.description }),
3405
- pages === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-progress", children: [
3406
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3407
- "div",
3408
- {
3409
- className: "form-progress-bar",
3410
- role: "progressbar",
3411
- "aria-valuemin": 1,
3412
- "aria-valuemax": visiblePageIndexes.length,
3413
- "aria-valuenow": activeVisibleIndex + 1,
3414
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3415
- "div",
3416
- {
3417
- className: "form-progress-fill",
3418
- style: { width: `${(activeVisibleIndex + 1) / visiblePageIndexes.length * 100}%` }
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
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: form.translate("form.step", { current: activeVisibleIndex + 1, total: visiblePageIndexes.length }) })
3424
- ] }),
3425
- draftRestored ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "form-draft-badge", children: form.translate("form.draftRestored") }) : null
3426
- ] }),
3427
- activePage === void 0 ? null : slots.renderPageHeader?.({
3428
- page: activePage,
3429
- pageIndex: activeVisibleIndex,
3430
- totalPages: visiblePageIndexes.length
3431
- }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-page-header", children: [
3432
- activePage.title === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "fe-page-title", children: activePage.title }),
3433
- activePage.description === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "fe-page-description", children: activePage.description })
3434
- ] }),
3435
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fe-fields", children: form.schema.fields.filter((field) => form.visibility[field.id] === true && (fieldIds === void 0 || fieldIds.has(field.id))).map((field) => {
3436
- const error = form.errors[field.id];
3437
- const props = {
3438
- field,
3439
- value: form.values[field.id],
3440
- error,
3441
- setValue: (value) => form.setValue(field.id, value),
3442
- translate: form.translate,
3443
- inputId: `${prefix}-${field.id}`,
3444
- errorId: `${prefix}-${field.id}-error`,
3445
- helpId: `${prefix}-${field.id}-help`,
3446
- ...slots.renderCharacterCount === void 0 ? {} : { renderCharacterCount: slots.renderCharacterCount }
3447
- };
3448
- if (slots.renderField !== void 0) {
3449
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react5.Fragment, { children: slots.renderField({
3450
- question: field,
3451
- value: form.values[field.id],
3452
- onChange: (value) => {
3453
- if (isFormValue(value)) form.setValue(field.id, value);
3454
- },
3455
- ...error === void 0 ? {} : { error }
3456
- }) }, field.id);
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
- const Component = components[field.type];
3459
- return Component === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DefaultField, { ...props }, field.id) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Component, { ...props }, field.id);
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": "Confirm submission",
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, _locale, params = {}) {
3537
- return (RENDERER_MESSAGES[key] ?? key).replace(
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,