@form-engine-ts/react 4.2.0 → 4.3.1

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 CHANGED
@@ -168,8 +168,15 @@ state. Text controls forward schema `minLength`, `maxLength`, and `pattern` cons
168
168
  `renderCharacterCount` can replace the default count. Guard evaluation, confirmation, receipt persistence, and provider
169
169
  submission share an in-flight lock so rapid clicks cannot submit twice.
170
170
 
171
+ Set `submissionConfirmation={{ enabled: true, renderMode: "replace" }}` to show a standard answer review before
172
+ submission even when no submission guard is configured. The default `inline` mode keeps the form visible; `replace` and
173
+ `dialog` provide alternate presentations. The standard review lists visible answers with their resolved labels and
174
+ formatted display values. `renderSubmissionConfirmation` receives these as `visibleItems`; guard confirmations continue
175
+ to receive their findings, while generic confirmations provide an empty findings array.
176
+
171
177
  The Builder basic-settings section edits source `title` and `description` through the same policy-aware action pipeline.
172
- Submission confirmation slots receive the effective message, localized schema, and visible answers. An `onSubmit` result
178
+ Submission confirmation slots receive the effective message, localized schema, visible answers, and formatted
179
+ `visibleItems`. An `onSubmit` result
173
180
  may provide `submissionId` and `submittedAt`, which Renderer copies into its receipt. Receipt stores support `getBatch`,
174
181
  and `useSubmissionReceipts` loads multiple form/version receipts for list and dashboard surfaces.
175
182
 
package/dist/index.cjs CHANGED
@@ -3567,7 +3567,8 @@ function ContextFormRenderer({
3567
3567
  beforeSubmit,
3568
3568
  onDraftSave,
3569
3569
  successRenderMode = "append",
3570
- submissionConfirmationRenderMode = "inline",
3570
+ submissionConfirmation,
3571
+ submissionConfirmationRenderMode,
3571
3572
  showHiddenFieldsInSummary = false,
3572
3573
  fieldsClassName,
3573
3574
  hideFormOnSuccess = false,
@@ -3603,6 +3604,12 @@ function ContextFormRenderer({
3603
3604
  const activeVisibleIndex = visiblePageIndexes.indexOf(currentPageIndex);
3604
3605
  const fieldIds = activePage === void 0 ? void 0 : new Set(activePage.questionIds);
3605
3606
  const visibleValues = (0, import_react5.useMemo)(() => (0, import_core4.selectVisibleAnswers)(form.schema, form.values), [form.schema, form.values]);
3607
+ const visibleItems = (0, import_react5.useMemo)(
3608
+ () => buildSubmittedItems(form.schema, form.values, form.visibility, (key) => form.translate(key), false),
3609
+ [form.schema, form.translate, form.values, form.visibility]
3610
+ );
3611
+ const confirmationRenderMode = submissionConfirmation?.renderMode ?? submissionConfirmationRenderMode ?? "inline";
3612
+ const confirmationEnabled = submissionConfirmation?.enabled === true;
3606
3613
  const submitState = confirmation === null && !guardsPending ? form.submitStatus : "confirming";
3607
3614
  const interactionLocked = submitState === "confirming" || submitState === "submitting";
3608
3615
  const isReplaceMode = successRenderMode === "replace" || hideFormOnSuccess;
@@ -3670,7 +3677,7 @@ function ContextFormRenderer({
3670
3677
  if (confirmation === null) return;
3671
3678
  const confirmButton = confirmationRef.current?.querySelector("[data-fe-confirm], button");
3672
3679
  confirmButton?.focus();
3673
- if (submissionConfirmationRenderMode !== "dialog") return;
3680
+ if (confirmationRenderMode !== "dialog") return;
3674
3681
  const onKeyDown = (event) => {
3675
3682
  if (event.key === "Escape") {
3676
3683
  event.preventDefault();
@@ -3697,7 +3704,7 @@ function ContextFormRenderer({
3697
3704
  };
3698
3705
  globalThis.addEventListener("keydown", onKeyDown);
3699
3706
  return () => globalThis.removeEventListener("keydown", onKeyDown);
3700
- }, [confirmation, focusSubmitButton, submissionConfirmationRenderMode]);
3707
+ }, [confirmation, confirmationRenderMode, focusSubmitButton]);
3701
3708
  (0, import_react5.useEffect)(() => {
3702
3709
  if (autoSaveKey === void 0 || typeof globalThis.localStorage === "undefined") return;
3703
3710
  const loadIdentity = `${autoSaveKey}:${form.schema.id}:${form.schema.version}`;
@@ -3768,21 +3775,29 @@ function ContextFormRenderer({
3768
3775
  }
3769
3776
  const validation = (0, import_core4.validateAnswers)(form.schema, form.values);
3770
3777
  const firstInvalidFieldId = validation.issues[0]?.fieldId;
3771
- if (validation.valid && !guardsConfirmed && submissionGuards.length > 0) {
3778
+ if (validation.valid && !guardsConfirmed) {
3772
3779
  rendererSubmissionInFlight.current = true;
3773
- setGuardsPending(true);
3780
+ setGuardsPending(submissionGuards.length > 0);
3774
3781
  try {
3775
- const guardResult = await runSubmissionGuards(submissionGuards);
3776
- if (guardResult.status === "block") {
3777
- setGuardMessage(guardResult.message ?? form.translate("form.submissionBlocked"));
3778
- return { status: "cancelled" };
3782
+ if (submissionGuards.length > 0) {
3783
+ const guardResult = await runSubmissionGuards(submissionGuards);
3784
+ if (guardResult.status === "block") {
3785
+ setGuardMessage(guardResult.message ?? form.translate("form.submissionBlocked"));
3786
+ return { status: "cancelled" };
3787
+ }
3788
+ if (guardResult.status === "confirm") {
3789
+ setGuardMessage(null);
3790
+ setConfirmation({
3791
+ findings: guardResult.findings,
3792
+ generic: false,
3793
+ ...guardResult.message === void 0 ? {} : { message: guardResult.message }
3794
+ });
3795
+ return { status: "cancelled" };
3796
+ }
3779
3797
  }
3780
- if (guardResult.status === "confirm") {
3798
+ if (confirmationEnabled) {
3781
3799
  setGuardMessage(null);
3782
- setConfirmation({
3783
- findings: guardResult.findings,
3784
- ...guardResult.message === void 0 ? {} : { message: guardResult.message }
3785
- });
3800
+ setConfirmation({ findings: [], generic: true });
3786
3801
  return { status: "cancelled" };
3787
3802
  }
3788
3803
  } finally {
@@ -3945,17 +3960,18 @@ function ContextFormRenderer({
3945
3960
  {
3946
3961
  ref: confirmationRef,
3947
3962
  className: "fe-submission-confirmation",
3948
- role: submissionConfirmationRenderMode === "dialog" ? void 0 : "dialog",
3963
+ role: confirmationRenderMode === "dialog" ? void 0 : "dialog",
3949
3964
  children: slots.renderSubmissionConfirmation?.({
3950
3965
  findings: confirmation?.findings ?? [],
3951
- message: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")),
3966
+ message: confirmation?.message ?? (confirmation?.generic === true ? form.locale.toLowerCase().startsWith("ja") ? "\u56DE\u7B54\u5185\u5BB9\u3092\u3054\u78BA\u8A8D\u306E\u3046\u3048\u3001\u9001\u4FE1\u3057\u3066\u304F\u3060\u3055\u3044\u3002" : "Please review your answers before submitting." : resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData"))),
3952
3967
  schema: form.schema,
3953
3968
  visibleValues,
3969
+ visibleItems,
3954
3970
  onConfirm: confirmSubmission,
3955
3971
  onCancel: cancelSubmission
3956
3972
  }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
3957
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: resolveMessage("confirmSensitiveDataTitle") }),
3958
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")) }),
3973
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: confirmation?.generic === true ? form.locale.toLowerCase().startsWith("ja") ? "\u56DE\u7B54\u5185\u5BB9\u306E\u78BA\u8A8D" : "Review your answers" : resolveMessage("confirmSensitiveDataTitle") }),
3974
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: confirmation?.message ?? (confirmation?.generic === true ? form.locale.toLowerCase().startsWith("ja") ? "\u56DE\u7B54\u5185\u5BB9\u3092\u3054\u78BA\u8A8D\u306E\u3046\u3048\u3001\u9001\u4FE1\u3057\u3066\u304F\u3060\u3055\u3044\u3002" : "Please review your answers before submitting." : resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData"))) }),
3959
3975
  (confirmation?.findings ?? []).length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { children: (confirmation?.findings ?? []).map((finding, index) => {
3960
3976
  const field = form.schema.fields.find((candidate) => candidate.id === finding.fieldId);
3961
3977
  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" };
@@ -3971,6 +3987,11 @@ function ContextFormRenderer({
3971
3987
  ] })
3972
3988
  ] }, `${finding.fieldId}-${finding.type}-${finding.start ?? index}`);
3973
3989
  }) }),
3990
+ confirmation?.generic === true ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "fe-submission-summary", children: visibleItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
3991
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: item.title }),
3992
+ ": ",
3993
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: item.displayValue })
3994
+ ] }, item.fieldId)) }) : null,
3974
3995
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "data-fe-confirm": "true", onClick: confirmSubmission, children: resolveMessage("confirmButton", form.translate("form.confirmSubmission")) }),
3975
3996
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: cancelSubmission, children: resolveMessage("cancelButton", form.translate("form.cancelSubmission")) })
3976
3997
  ] })
@@ -3990,7 +4011,7 @@ function ContextFormRenderer({
3990
4011
  if (form.submitStatus === "success" && isReplaceMode) {
3991
4012
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `fe-form ${className}`.trim(), children: completionRegion });
3992
4013
  }
3993
- if (confirmation !== null && submissionConfirmationRenderMode === "replace") {
4014
+ if (confirmation !== null && confirmationRenderMode === "replace") {
3994
4015
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `fe-form ${className}`.trim(), children: confirmationContent });
3995
4016
  }
3996
4017
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
@@ -4001,7 +4022,7 @@ function ContextFormRenderer({
4001
4022
  className: `fe-form ${className}`.trim(),
4002
4023
  noValidate: true,
4003
4024
  onSubmit: handleSubmit,
4004
- "aria-hidden": confirmation !== null && submissionConfirmationRenderMode === "dialog" ? true : void 0,
4025
+ "aria-hidden": confirmation !== null && confirmationRenderMode === "dialog" ? true : void 0,
4005
4026
  children: [
4006
4027
  slots.renderHeader?.({
4007
4028
  title: form.schema.title,
@@ -4070,7 +4091,7 @@ function ContextFormRenderer({
4070
4091
  return slots.renderFields?.({ children: fieldChildren, className: fieldClassName }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: fieldClassName, children: fieldChildren });
4071
4092
  })(),
4072
4093
  guardMessage === null ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "alert", children: guardMessage }),
4073
- confirmation !== null && submissionConfirmationRenderMode === "inline" ? confirmationContent : null,
4094
+ confirmation !== null && confirmationRenderMode === "inline" ? confirmationContent : null,
4074
4095
  validationIssues.length === 0 ? null : slots.renderValidationSummary?.({ issues: validationIssues }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-validation-summary", role: "alert", children: [
4075
4096
  validationIssues.length,
4076
4097
  " validation error",
@@ -4125,7 +4146,7 @@ function ContextFormRenderer({
4125
4146
  ]
4126
4147
  }
4127
4148
  ),
4128
- confirmation !== null && submissionConfirmationRenderMode === "dialog" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fe-confirmation-dialog-backdrop", role: "dialog", "aria-modal": "true", children: confirmationContent }) : null
4149
+ confirmation !== null && confirmationRenderMode === "dialog" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fe-confirmation-dialog-backdrop", role: "dialog", "aria-modal": "true", children: confirmationContent }) : null
4129
4150
  ] });
4130
4151
  }
4131
4152
  var RENDERER_MESSAGES = {
package/dist/index.d.cts CHANGED
@@ -478,6 +478,10 @@ type SubmissionGuardResult = {
478
478
  type SubmissionGuard = (schema: FormSchema, values: Record<string, unknown>) => SubmissionGuardResult | Promise<SubmissionGuardResult>;
479
479
  type FormSuccessRenderMode = "append" | "replace";
480
480
  type SubmissionConfirmationRenderMode = "inline" | "replace" | "dialog";
481
+ interface SubmissionConfirmationOptions {
482
+ readonly enabled?: boolean;
483
+ readonly renderMode?: SubmissionConfirmationRenderMode;
484
+ }
481
485
  type FormSubmitStatus = "idle" | "submitting" | "confirming" | "success" | "error";
482
486
  /** @deprecated Use FormSubmitStatus instead. */
483
487
  type FormSubmitState = "idle" | "submitting" | "confirming" | "success" | "error";
@@ -488,10 +492,11 @@ interface RenderSubmitButtonProps {
488
492
  readonly onSubmit: () => void;
489
493
  }
490
494
  interface SubmissionConfirmationSlotProps {
491
- readonly findings: readonly SensitiveDataFinding[];
495
+ readonly findings?: readonly SensitiveDataFinding[];
492
496
  readonly message?: string;
493
497
  readonly schema: FormSchema;
494
498
  readonly visibleValues: Record<string, unknown>;
499
+ readonly visibleItems?: readonly FormSubmittedAnswerItem[];
495
500
  readonly onConfirm: () => void;
496
501
  readonly onCancel: () => void;
497
502
  }
@@ -671,6 +676,8 @@ interface FormRendererPresentationProps extends SubmissionProtectionProps {
671
676
  * Defaults to "append" for backwards compatibility.
672
677
  */
673
678
  readonly successRenderMode?: FormSuccessRenderMode;
679
+ readonly submissionConfirmation?: SubmissionConfirmationOptions;
680
+ /** @deprecated Use submissionConfirmation.renderMode instead. */
674
681
  readonly submissionConfirmationRenderMode?: SubmissionConfirmationRenderMode;
675
682
  readonly showHiddenFieldsInSummary?: boolean;
676
683
  readonly fieldsClassName?: string;
@@ -697,4 +704,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
697
704
  type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
698
705
  declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
699
706
 
700
- export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type UseSubmissionReceiptsResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts };
707
+ export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationOptions, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type UseSubmissionReceiptsResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts };
package/dist/index.d.ts CHANGED
@@ -478,6 +478,10 @@ type SubmissionGuardResult = {
478
478
  type SubmissionGuard = (schema: FormSchema, values: Record<string, unknown>) => SubmissionGuardResult | Promise<SubmissionGuardResult>;
479
479
  type FormSuccessRenderMode = "append" | "replace";
480
480
  type SubmissionConfirmationRenderMode = "inline" | "replace" | "dialog";
481
+ interface SubmissionConfirmationOptions {
482
+ readonly enabled?: boolean;
483
+ readonly renderMode?: SubmissionConfirmationRenderMode;
484
+ }
481
485
  type FormSubmitStatus = "idle" | "submitting" | "confirming" | "success" | "error";
482
486
  /** @deprecated Use FormSubmitStatus instead. */
483
487
  type FormSubmitState = "idle" | "submitting" | "confirming" | "success" | "error";
@@ -488,10 +492,11 @@ interface RenderSubmitButtonProps {
488
492
  readonly onSubmit: () => void;
489
493
  }
490
494
  interface SubmissionConfirmationSlotProps {
491
- readonly findings: readonly SensitiveDataFinding[];
495
+ readonly findings?: readonly SensitiveDataFinding[];
492
496
  readonly message?: string;
493
497
  readonly schema: FormSchema;
494
498
  readonly visibleValues: Record<string, unknown>;
499
+ readonly visibleItems?: readonly FormSubmittedAnswerItem[];
495
500
  readonly onConfirm: () => void;
496
501
  readonly onCancel: () => void;
497
502
  }
@@ -671,6 +676,8 @@ interface FormRendererPresentationProps extends SubmissionProtectionProps {
671
676
  * Defaults to "append" for backwards compatibility.
672
677
  */
673
678
  readonly successRenderMode?: FormSuccessRenderMode;
679
+ readonly submissionConfirmation?: SubmissionConfirmationOptions;
680
+ /** @deprecated Use submissionConfirmation.renderMode instead. */
674
681
  readonly submissionConfirmationRenderMode?: SubmissionConfirmationRenderMode;
675
682
  readonly showHiddenFieldsInSummary?: boolean;
676
683
  readonly fieldsClassName?: string;
@@ -697,4 +704,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
697
704
  type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
698
705
  declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
699
706
 
700
- export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type UseSubmissionReceiptsResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts };
707
+ export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationOptions, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type UseSubmissionReceiptsResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts };
package/dist/index.js CHANGED
@@ -3550,7 +3550,8 @@ function ContextFormRenderer({
3550
3550
  beforeSubmit,
3551
3551
  onDraftSave,
3552
3552
  successRenderMode = "append",
3553
- submissionConfirmationRenderMode = "inline",
3553
+ submissionConfirmation,
3554
+ submissionConfirmationRenderMode,
3554
3555
  showHiddenFieldsInSummary = false,
3555
3556
  fieldsClassName,
3556
3557
  hideFormOnSuccess = false,
@@ -3586,6 +3587,12 @@ function ContextFormRenderer({
3586
3587
  const activeVisibleIndex = visiblePageIndexes.indexOf(currentPageIndex);
3587
3588
  const fieldIds = activePage === void 0 ? void 0 : new Set(activePage.questionIds);
3588
3589
  const visibleValues = useMemo4(() => selectVisibleAnswers2(form.schema, form.values), [form.schema, form.values]);
3590
+ const visibleItems = useMemo4(
3591
+ () => buildSubmittedItems(form.schema, form.values, form.visibility, (key) => form.translate(key), false),
3592
+ [form.schema, form.translate, form.values, form.visibility]
3593
+ );
3594
+ const confirmationRenderMode = submissionConfirmation?.renderMode ?? submissionConfirmationRenderMode ?? "inline";
3595
+ const confirmationEnabled = submissionConfirmation?.enabled === true;
3589
3596
  const submitState = confirmation === null && !guardsPending ? form.submitStatus : "confirming";
3590
3597
  const interactionLocked = submitState === "confirming" || submitState === "submitting";
3591
3598
  const isReplaceMode = successRenderMode === "replace" || hideFormOnSuccess;
@@ -3653,7 +3660,7 @@ function ContextFormRenderer({
3653
3660
  if (confirmation === null) return;
3654
3661
  const confirmButton = confirmationRef.current?.querySelector("[data-fe-confirm], button");
3655
3662
  confirmButton?.focus();
3656
- if (submissionConfirmationRenderMode !== "dialog") return;
3663
+ if (confirmationRenderMode !== "dialog") return;
3657
3664
  const onKeyDown = (event) => {
3658
3665
  if (event.key === "Escape") {
3659
3666
  event.preventDefault();
@@ -3680,7 +3687,7 @@ function ContextFormRenderer({
3680
3687
  };
3681
3688
  globalThis.addEventListener("keydown", onKeyDown);
3682
3689
  return () => globalThis.removeEventListener("keydown", onKeyDown);
3683
- }, [confirmation, focusSubmitButton, submissionConfirmationRenderMode]);
3690
+ }, [confirmation, confirmationRenderMode, focusSubmitButton]);
3684
3691
  useEffect3(() => {
3685
3692
  if (autoSaveKey === void 0 || typeof globalThis.localStorage === "undefined") return;
3686
3693
  const loadIdentity = `${autoSaveKey}:${form.schema.id}:${form.schema.version}`;
@@ -3751,21 +3758,29 @@ function ContextFormRenderer({
3751
3758
  }
3752
3759
  const validation = validateAnswers2(form.schema, form.values);
3753
3760
  const firstInvalidFieldId = validation.issues[0]?.fieldId;
3754
- if (validation.valid && !guardsConfirmed && submissionGuards.length > 0) {
3761
+ if (validation.valid && !guardsConfirmed) {
3755
3762
  rendererSubmissionInFlight.current = true;
3756
- setGuardsPending(true);
3763
+ setGuardsPending(submissionGuards.length > 0);
3757
3764
  try {
3758
- const guardResult = await runSubmissionGuards(submissionGuards);
3759
- if (guardResult.status === "block") {
3760
- setGuardMessage(guardResult.message ?? form.translate("form.submissionBlocked"));
3761
- return { status: "cancelled" };
3765
+ if (submissionGuards.length > 0) {
3766
+ const guardResult = await runSubmissionGuards(submissionGuards);
3767
+ if (guardResult.status === "block") {
3768
+ setGuardMessage(guardResult.message ?? form.translate("form.submissionBlocked"));
3769
+ return { status: "cancelled" };
3770
+ }
3771
+ if (guardResult.status === "confirm") {
3772
+ setGuardMessage(null);
3773
+ setConfirmation({
3774
+ findings: guardResult.findings,
3775
+ generic: false,
3776
+ ...guardResult.message === void 0 ? {} : { message: guardResult.message }
3777
+ });
3778
+ return { status: "cancelled" };
3779
+ }
3762
3780
  }
3763
- if (guardResult.status === "confirm") {
3781
+ if (confirmationEnabled) {
3764
3782
  setGuardMessage(null);
3765
- setConfirmation({
3766
- findings: guardResult.findings,
3767
- ...guardResult.message === void 0 ? {} : { message: guardResult.message }
3768
- });
3783
+ setConfirmation({ findings: [], generic: true });
3769
3784
  return { status: "cancelled" };
3770
3785
  }
3771
3786
  } finally {
@@ -3928,17 +3943,18 @@ function ContextFormRenderer({
3928
3943
  {
3929
3944
  ref: confirmationRef,
3930
3945
  className: "fe-submission-confirmation",
3931
- role: submissionConfirmationRenderMode === "dialog" ? void 0 : "dialog",
3946
+ role: confirmationRenderMode === "dialog" ? void 0 : "dialog",
3932
3947
  children: slots.renderSubmissionConfirmation?.({
3933
3948
  findings: confirmation?.findings ?? [],
3934
- message: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")),
3949
+ message: confirmation?.message ?? (confirmation?.generic === true ? form.locale.toLowerCase().startsWith("ja") ? "\u56DE\u7B54\u5185\u5BB9\u3092\u3054\u78BA\u8A8D\u306E\u3046\u3048\u3001\u9001\u4FE1\u3057\u3066\u304F\u3060\u3055\u3044\u3002" : "Please review your answers before submitting." : resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData"))),
3935
3950
  schema: form.schema,
3936
3951
  visibleValues,
3952
+ visibleItems,
3937
3953
  onConfirm: confirmSubmission,
3938
3954
  onCancel: cancelSubmission
3939
3955
  }) ?? /* @__PURE__ */ jsxs2(Fragment3, { children: [
3940
- /* @__PURE__ */ jsx3("h2", { children: resolveMessage("confirmSensitiveDataTitle") }),
3941
- /* @__PURE__ */ jsx3("p", { children: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")) }),
3956
+ /* @__PURE__ */ jsx3("h2", { children: confirmation?.generic === true ? form.locale.toLowerCase().startsWith("ja") ? "\u56DE\u7B54\u5185\u5BB9\u306E\u78BA\u8A8D" : "Review your answers" : resolveMessage("confirmSensitiveDataTitle") }),
3957
+ /* @__PURE__ */ jsx3("p", { children: confirmation?.message ?? (confirmation?.generic === true ? form.locale.toLowerCase().startsWith("ja") ? "\u56DE\u7B54\u5185\u5BB9\u3092\u3054\u78BA\u8A8D\u306E\u3046\u3048\u3001\u9001\u4FE1\u3057\u3066\u304F\u3060\u3055\u3044\u3002" : "Please review your answers before submitting." : resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData"))) }),
3942
3958
  (confirmation?.findings ?? []).length === 0 ? null : /* @__PURE__ */ jsx3("ul", { children: (confirmation?.findings ?? []).map((finding, index) => {
3943
3959
  const field = form.schema.fields.find((candidate) => candidate.id === finding.fieldId);
3944
3960
  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" };
@@ -3954,6 +3970,11 @@ function ContextFormRenderer({
3954
3970
  ] })
3955
3971
  ] }, `${finding.fieldId}-${finding.type}-${finding.start ?? index}`);
3956
3972
  }) }),
3973
+ confirmation?.generic === true ? /* @__PURE__ */ jsx3("ul", { className: "fe-submission-summary", children: visibleItems.map((item) => /* @__PURE__ */ jsxs2("li", { children: [
3974
+ /* @__PURE__ */ jsx3("span", { children: item.title }),
3975
+ ": ",
3976
+ /* @__PURE__ */ jsx3("span", { children: item.displayValue })
3977
+ ] }, item.fieldId)) }) : null,
3957
3978
  /* @__PURE__ */ jsx3("button", { type: "button", "data-fe-confirm": "true", onClick: confirmSubmission, children: resolveMessage("confirmButton", form.translate("form.confirmSubmission")) }),
3958
3979
  /* @__PURE__ */ jsx3("button", { type: "button", onClick: cancelSubmission, children: resolveMessage("cancelButton", form.translate("form.cancelSubmission")) })
3959
3980
  ] })
@@ -3973,7 +3994,7 @@ function ContextFormRenderer({
3973
3994
  if (form.submitStatus === "success" && isReplaceMode) {
3974
3995
  return /* @__PURE__ */ jsx3("div", { className: `fe-form ${className}`.trim(), children: completionRegion });
3975
3996
  }
3976
- if (confirmation !== null && submissionConfirmationRenderMode === "replace") {
3997
+ if (confirmation !== null && confirmationRenderMode === "replace") {
3977
3998
  return /* @__PURE__ */ jsx3("div", { className: `fe-form ${className}`.trim(), children: confirmationContent });
3978
3999
  }
3979
4000
  return /* @__PURE__ */ jsxs2(Fragment3, { children: [
@@ -3984,7 +4005,7 @@ function ContextFormRenderer({
3984
4005
  className: `fe-form ${className}`.trim(),
3985
4006
  noValidate: true,
3986
4007
  onSubmit: handleSubmit,
3987
- "aria-hidden": confirmation !== null && submissionConfirmationRenderMode === "dialog" ? true : void 0,
4008
+ "aria-hidden": confirmation !== null && confirmationRenderMode === "dialog" ? true : void 0,
3988
4009
  children: [
3989
4010
  slots.renderHeader?.({
3990
4011
  title: form.schema.title,
@@ -4053,7 +4074,7 @@ function ContextFormRenderer({
4053
4074
  return slots.renderFields?.({ children: fieldChildren, className: fieldClassName }) ?? /* @__PURE__ */ jsx3("div", { className: fieldClassName, children: fieldChildren });
4054
4075
  })(),
4055
4076
  guardMessage === null ? null : /* @__PURE__ */ jsx3("div", { role: "alert", children: guardMessage }),
4056
- confirmation !== null && submissionConfirmationRenderMode === "inline" ? confirmationContent : null,
4077
+ confirmation !== null && confirmationRenderMode === "inline" ? confirmationContent : null,
4057
4078
  validationIssues.length === 0 ? null : slots.renderValidationSummary?.({ issues: validationIssues }) ?? /* @__PURE__ */ jsxs2("div", { className: "fe-validation-summary", role: "alert", children: [
4058
4079
  validationIssues.length,
4059
4080
  " validation error",
@@ -4108,7 +4129,7 @@ function ContextFormRenderer({
4108
4129
  ]
4109
4130
  }
4110
4131
  ),
4111
- confirmation !== null && submissionConfirmationRenderMode === "dialog" ? /* @__PURE__ */ jsx3("div", { className: "fe-confirmation-dialog-backdrop", role: "dialog", "aria-modal": "true", children: confirmationContent }) : null
4132
+ confirmation !== null && confirmationRenderMode === "dialog" ? /* @__PURE__ */ jsx3("div", { className: "fe-confirmation-dialog-backdrop", role: "dialog", "aria-modal": "true", children: confirmationContent }) : null
4112
4133
  ] });
4113
4134
  }
4114
4135
  var RENDERER_MESSAGES = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@form-engine-ts/react",
3
- "version": "4.2.0",
3
+ "version": "4.3.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -42,8 +42,8 @@
42
42
  "typescript"
43
43
  ],
44
44
  "dependencies": {
45
- "@form-engine-ts/core": "4.2.0",
46
- "@form-engine-ts/privacy": "4.2.0"
45
+ "@form-engine-ts/core": "4.3.1",
46
+ "@form-engine-ts/privacy": "4.3.1"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "react": ">=18.2 <20",