@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 +8 -1
- package/dist/index.cjs +43 -22
- package/dist/index.d.cts +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +43 -22
- package/package.json +3 -3
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,
|
|
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
|
-
|
|
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 (
|
|
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,
|
|
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
|
|
3778
|
+
if (validation.valid && !guardsConfirmed) {
|
|
3772
3779
|
rendererSubmissionInFlight.current = true;
|
|
3773
|
-
setGuardsPending(
|
|
3780
|
+
setGuardsPending(submissionGuards.length > 0);
|
|
3774
3781
|
try {
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
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 (
|
|
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:
|
|
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 &&
|
|
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 &&
|
|
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 &&
|
|
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 &&
|
|
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
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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,
|
|
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
|
|
3761
|
+
if (validation.valid && !guardsConfirmed) {
|
|
3755
3762
|
rendererSubmissionInFlight.current = true;
|
|
3756
|
-
setGuardsPending(
|
|
3763
|
+
setGuardsPending(submissionGuards.length > 0);
|
|
3757
3764
|
try {
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
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 (
|
|
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:
|
|
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 &&
|
|
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 &&
|
|
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 &&
|
|
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 &&
|
|
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.
|
|
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.
|
|
46
|
-
"@form-engine-ts/privacy": "4.
|
|
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",
|