@form-engine-ts/react 4.2.0 → 4.3.2
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 +19 -1
- package/dist/index.cjs +178 -26
- package/dist/index.d.cts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +178 -26
- package/dist/styles.css +35 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -39,6 +39,17 @@ export function ContactForm() {
|
|
|
39
39
|
|
|
40
40
|
Use any compatible `TranslationAdapter` in place of the mock translator.
|
|
41
41
|
|
|
42
|
+
## Choice field layout
|
|
43
|
+
|
|
44
|
+
Radio and checkbox questions use the flat layout by default. Set `appearance.choiceField` to `"grouped"` to render
|
|
45
|
+
them as bordered, accessible `<fieldset>` groups with `<legend>` titles:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
<FormRenderer appearance={{ choiceField: "grouped" }} />
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`groupedChoiceFields={true}` remains available as a deprecated compatibility alias.
|
|
52
|
+
|
|
42
53
|
Define `schema.pages` to enable Back/Next navigation, page validation, conditional page skipping, and an accessible
|
|
43
54
|
progress indicator. Pass `autoSaveKey` to persist a versioned draft in `localStorage` after a 500ms debounce and restore it
|
|
44
55
|
on the next mount:
|
|
@@ -168,8 +179,15 @@ state. Text controls forward schema `minLength`, `maxLength`, and `pattern` cons
|
|
|
168
179
|
`renderCharacterCount` can replace the default count. Guard evaluation, confirmation, receipt persistence, and provider
|
|
169
180
|
submission share an in-flight lock so rapid clicks cannot submit twice.
|
|
170
181
|
|
|
182
|
+
Set `submissionConfirmation={{ enabled: true, renderMode: "replace" }}` to show a standard answer review before
|
|
183
|
+
submission even when no submission guard is configured. The default `inline` mode keeps the form visible; `replace` and
|
|
184
|
+
`dialog` provide alternate presentations. The standard review lists visible answers with their resolved labels and
|
|
185
|
+
formatted display values. `renderSubmissionConfirmation` receives these as `visibleItems`; guard confirmations continue
|
|
186
|
+
to receive their findings, while generic confirmations provide an empty findings array.
|
|
187
|
+
|
|
171
188
|
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,
|
|
189
|
+
Submission confirmation slots receive the effective message, localized schema, visible answers, and formatted
|
|
190
|
+
`visibleItems`. An `onSubmit` result
|
|
173
191
|
may provide `submissionId` and `submittedAt`, which Renderer copies into its receipt. Receipt stores support `getBatch`,
|
|
174
192
|
and `useSubmissionReceipts` loads multiple form/version receipts for list and dashboard surfaces.
|
|
175
193
|
|
package/dist/index.cjs
CHANGED
|
@@ -3276,8 +3276,11 @@ function describedBy(field, error, helpId, errorId) {
|
|
|
3276
3276
|
);
|
|
3277
3277
|
return ids.length === 0 ? void 0 : ids.join(" ");
|
|
3278
3278
|
}
|
|
3279
|
-
function RequiredMark({
|
|
3280
|
-
|
|
3279
|
+
function RequiredMark({
|
|
3280
|
+
required,
|
|
3281
|
+
className = "fe-required"
|
|
3282
|
+
}) {
|
|
3283
|
+
return required ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className, "aria-hidden": "true", children: [
|
|
3281
3284
|
" ",
|
|
3282
3285
|
"*"
|
|
3283
3286
|
] }) : null;
|
|
@@ -3288,13 +3291,59 @@ function FieldMessage({ props }) {
|
|
|
3288
3291
|
props.error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { id: props.errorId, className: "fe-error", children: props.translate(props.error.messageKey, props.error.params) })
|
|
3289
3292
|
] });
|
|
3290
3293
|
}
|
|
3291
|
-
function
|
|
3294
|
+
function GroupedChoiceDescription({ props }) {
|
|
3295
|
+
return props.field.description === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { id: props.helpId, className: "fe-field-description", children: props.field.description });
|
|
3296
|
+
}
|
|
3297
|
+
function GroupedChoiceError({ props }) {
|
|
3298
|
+
return props.error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { id: props.errorId, className: "fe-field-error", role: "alert", children: props.translate(props.error.messageKey, props.error.params) });
|
|
3299
|
+
}
|
|
3300
|
+
function DefaultField({
|
|
3301
|
+
groupedChoiceFields,
|
|
3302
|
+
...props
|
|
3303
|
+
}) {
|
|
3292
3304
|
const { field, value, setValue, inputId, error, translate } = props;
|
|
3293
3305
|
const ariaProps = {
|
|
3294
3306
|
"aria-describedby": describedBy(field, error, props.helpId, props.errorId),
|
|
3295
3307
|
"aria-invalid": error === void 0 ? void 0 : true
|
|
3296
3308
|
};
|
|
3297
3309
|
if (field.type === "checkbox") {
|
|
3310
|
+
if (groupedChoiceFields) {
|
|
3311
|
+
return (
|
|
3312
|
+
// biome-ignore lint/a11y/useAriaPropsSupportedByRole: The grouped fieldset exposes the required state for the complete choice question.
|
|
3313
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3314
|
+
"fieldset",
|
|
3315
|
+
{
|
|
3316
|
+
className: "fe-choice-group fe-field--checkbox",
|
|
3317
|
+
"data-field-id": field.id,
|
|
3318
|
+
"aria-describedby": describedBy(field, error, props.helpId, props.errorId),
|
|
3319
|
+
"aria-invalid": Boolean(error),
|
|
3320
|
+
"aria-required": field.required,
|
|
3321
|
+
children: [
|
|
3322
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("legend", { className: "fe-choice-legend", children: [
|
|
3323
|
+
field.title,
|
|
3324
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RequiredMark, { required: field.required, className: "fe-required-badge" })
|
|
3325
|
+
] }),
|
|
3326
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GroupedChoiceDescription, { props }),
|
|
3327
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fe-choice-options", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "fe-choice-option", htmlFor: inputId, children: [
|
|
3328
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3329
|
+
"input",
|
|
3330
|
+
{
|
|
3331
|
+
id: inputId,
|
|
3332
|
+
name: field.id,
|
|
3333
|
+
type: "checkbox",
|
|
3334
|
+
checked: value === true,
|
|
3335
|
+
"aria-label": field.title,
|
|
3336
|
+
onChange: (event) => setValue(event.currentTarget.checked)
|
|
3337
|
+
}
|
|
3338
|
+
),
|
|
3339
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: field.title })
|
|
3340
|
+
] }) }),
|
|
3341
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GroupedChoiceError, { props })
|
|
3342
|
+
]
|
|
3343
|
+
}
|
|
3344
|
+
)
|
|
3345
|
+
);
|
|
3346
|
+
}
|
|
3298
3347
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-field fe-field--checkbox", "data-field-id": field.id, children: [
|
|
3299
3348
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "fe-check-label", htmlFor: inputId, children: [
|
|
3300
3349
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -3318,6 +3367,85 @@ function DefaultField(props) {
|
|
|
3318
3367
|
}
|
|
3319
3368
|
if (field.type === "radio" || field.type === "multi-select") {
|
|
3320
3369
|
const selected = Array.isArray(value) ? value : [];
|
|
3370
|
+
if (field.type === "radio" && groupedChoiceFields) {
|
|
3371
|
+
return (
|
|
3372
|
+
// biome-ignore lint/a11y/useAriaPropsSupportedByRole: The grouped fieldset exposes the required state for the complete choice question.
|
|
3373
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3374
|
+
"fieldset",
|
|
3375
|
+
{
|
|
3376
|
+
className: "fe-choice-group fe-field--radio",
|
|
3377
|
+
"data-field-id": field.id,
|
|
3378
|
+
"aria-describedby": describedBy(field, error, props.helpId, props.errorId),
|
|
3379
|
+
"aria-invalid": Boolean(error),
|
|
3380
|
+
"aria-required": field.required,
|
|
3381
|
+
children: [
|
|
3382
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("legend", { className: "fe-choice-legend", children: [
|
|
3383
|
+
field.title,
|
|
3384
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RequiredMark, { required: field.required, className: "fe-required-badge" })
|
|
3385
|
+
] }),
|
|
3386
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GroupedChoiceDescription, { props }),
|
|
3387
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fe-choice-options", children: field.options.map((option, index) => {
|
|
3388
|
+
const optionId = `${inputId}-${index}`;
|
|
3389
|
+
const checked = value === option.id;
|
|
3390
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "fe-choice-option", htmlFor: optionId, children: [
|
|
3391
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3392
|
+
"input",
|
|
3393
|
+
{
|
|
3394
|
+
id: optionId,
|
|
3395
|
+
name: field.id,
|
|
3396
|
+
type: "radio",
|
|
3397
|
+
value: option.id,
|
|
3398
|
+
checked,
|
|
3399
|
+
onKeyDown: (event) => {
|
|
3400
|
+
if (event.key !== "ArrowDown" && event.key !== "ArrowRight" && event.key !== "ArrowUp" && event.key !== "ArrowLeft")
|
|
3401
|
+
return;
|
|
3402
|
+
event.preventDefault();
|
|
3403
|
+
const offset = event.key === "ArrowDown" || event.key === "ArrowRight" ? 1 : -1;
|
|
3404
|
+
const nextIndex = (index + offset + field.options.length) % field.options.length;
|
|
3405
|
+
const nextOption = field.options[nextIndex];
|
|
3406
|
+
if (nextOption === void 0) return;
|
|
3407
|
+
setValue(nextOption.id);
|
|
3408
|
+
document.getElementById(`${inputId}-${nextIndex}`)?.focus();
|
|
3409
|
+
},
|
|
3410
|
+
onChange: () => setValue(option.id)
|
|
3411
|
+
}
|
|
3412
|
+
),
|
|
3413
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: option.label })
|
|
3414
|
+
] }, option.id);
|
|
3415
|
+
}) }),
|
|
3416
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GroupedChoiceError, { props })
|
|
3417
|
+
]
|
|
3418
|
+
}
|
|
3419
|
+
)
|
|
3420
|
+
);
|
|
3421
|
+
}
|
|
3422
|
+
if (field.type === "radio") {
|
|
3423
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-field fe-field--radio", "data-field-id": field.id, children: [
|
|
3424
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-label", children: [
|
|
3425
|
+
field.title,
|
|
3426
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RequiredMark, { required: field.required })
|
|
3427
|
+
] }),
|
|
3428
|
+
field.options.map((option, index) => {
|
|
3429
|
+
const optionId = `${inputId}-${index}`;
|
|
3430
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "fe-check-label", htmlFor: optionId, children: [
|
|
3431
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3432
|
+
"input",
|
|
3433
|
+
{
|
|
3434
|
+
...ariaProps,
|
|
3435
|
+
id: optionId,
|
|
3436
|
+
name: field.id,
|
|
3437
|
+
type: "radio",
|
|
3438
|
+
value: option.id,
|
|
3439
|
+
checked: value === option.id,
|
|
3440
|
+
onChange: () => setValue(option.id)
|
|
3441
|
+
}
|
|
3442
|
+
),
|
|
3443
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: option.label })
|
|
3444
|
+
] }, option.id);
|
|
3445
|
+
}),
|
|
3446
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FieldMessage, { props })
|
|
3447
|
+
] });
|
|
3448
|
+
}
|
|
3321
3449
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("fieldset", { className: `fe-field fe-field--${field.type}`, "data-field-id": field.id, ...ariaProps, children: [
|
|
3322
3450
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("legend", { className: "fe-label", children: [
|
|
3323
3451
|
field.title,
|
|
@@ -3567,7 +3695,10 @@ function ContextFormRenderer({
|
|
|
3567
3695
|
beforeSubmit,
|
|
3568
3696
|
onDraftSave,
|
|
3569
3697
|
successRenderMode = "append",
|
|
3570
|
-
|
|
3698
|
+
appearance,
|
|
3699
|
+
groupedChoiceFields = false,
|
|
3700
|
+
submissionConfirmation,
|
|
3701
|
+
submissionConfirmationRenderMode,
|
|
3571
3702
|
showHiddenFieldsInSummary = false,
|
|
3572
3703
|
fieldsClassName,
|
|
3573
3704
|
hideFormOnSuccess = false,
|
|
@@ -3578,6 +3709,7 @@ function ContextFormRenderer({
|
|
|
3578
3709
|
slots = {}
|
|
3579
3710
|
}) {
|
|
3580
3711
|
const form = useForm();
|
|
3712
|
+
const isGroupedMode = appearance?.choiceField === "grouped" || groupedChoiceFields;
|
|
3581
3713
|
const prefix = (0, import_react5.useId)().replace(/:/g, "");
|
|
3582
3714
|
const formRef = (0, import_react5.useRef)(null);
|
|
3583
3715
|
const loadedDraftKey = (0, import_react5.useRef)(null);
|
|
@@ -3603,6 +3735,12 @@ function ContextFormRenderer({
|
|
|
3603
3735
|
const activeVisibleIndex = visiblePageIndexes.indexOf(currentPageIndex);
|
|
3604
3736
|
const fieldIds = activePage === void 0 ? void 0 : new Set(activePage.questionIds);
|
|
3605
3737
|
const visibleValues = (0, import_react5.useMemo)(() => (0, import_core4.selectVisibleAnswers)(form.schema, form.values), [form.schema, form.values]);
|
|
3738
|
+
const visibleItems = (0, import_react5.useMemo)(
|
|
3739
|
+
() => buildSubmittedItems(form.schema, form.values, form.visibility, (key) => form.translate(key), false),
|
|
3740
|
+
[form.schema, form.translate, form.values, form.visibility]
|
|
3741
|
+
);
|
|
3742
|
+
const confirmationRenderMode = submissionConfirmation?.renderMode ?? submissionConfirmationRenderMode ?? "inline";
|
|
3743
|
+
const confirmationEnabled = submissionConfirmation?.enabled === true;
|
|
3606
3744
|
const submitState = confirmation === null && !guardsPending ? form.submitStatus : "confirming";
|
|
3607
3745
|
const interactionLocked = submitState === "confirming" || submitState === "submitting";
|
|
3608
3746
|
const isReplaceMode = successRenderMode === "replace" || hideFormOnSuccess;
|
|
@@ -3670,7 +3808,7 @@ function ContextFormRenderer({
|
|
|
3670
3808
|
if (confirmation === null) return;
|
|
3671
3809
|
const confirmButton = confirmationRef.current?.querySelector("[data-fe-confirm], button");
|
|
3672
3810
|
confirmButton?.focus();
|
|
3673
|
-
if (
|
|
3811
|
+
if (confirmationRenderMode !== "dialog") return;
|
|
3674
3812
|
const onKeyDown = (event) => {
|
|
3675
3813
|
if (event.key === "Escape") {
|
|
3676
3814
|
event.preventDefault();
|
|
@@ -3697,7 +3835,7 @@ function ContextFormRenderer({
|
|
|
3697
3835
|
};
|
|
3698
3836
|
globalThis.addEventListener("keydown", onKeyDown);
|
|
3699
3837
|
return () => globalThis.removeEventListener("keydown", onKeyDown);
|
|
3700
|
-
}, [confirmation,
|
|
3838
|
+
}, [confirmation, confirmationRenderMode, focusSubmitButton]);
|
|
3701
3839
|
(0, import_react5.useEffect)(() => {
|
|
3702
3840
|
if (autoSaveKey === void 0 || typeof globalThis.localStorage === "undefined") return;
|
|
3703
3841
|
const loadIdentity = `${autoSaveKey}:${form.schema.id}:${form.schema.version}`;
|
|
@@ -3768,21 +3906,29 @@ function ContextFormRenderer({
|
|
|
3768
3906
|
}
|
|
3769
3907
|
const validation = (0, import_core4.validateAnswers)(form.schema, form.values);
|
|
3770
3908
|
const firstInvalidFieldId = validation.issues[0]?.fieldId;
|
|
3771
|
-
if (validation.valid && !guardsConfirmed
|
|
3909
|
+
if (validation.valid && !guardsConfirmed) {
|
|
3772
3910
|
rendererSubmissionInFlight.current = true;
|
|
3773
|
-
setGuardsPending(
|
|
3911
|
+
setGuardsPending(submissionGuards.length > 0);
|
|
3774
3912
|
try {
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3913
|
+
if (submissionGuards.length > 0) {
|
|
3914
|
+
const guardResult = await runSubmissionGuards(submissionGuards);
|
|
3915
|
+
if (guardResult.status === "block") {
|
|
3916
|
+
setGuardMessage(guardResult.message ?? form.translate("form.submissionBlocked"));
|
|
3917
|
+
return { status: "cancelled" };
|
|
3918
|
+
}
|
|
3919
|
+
if (guardResult.status === "confirm") {
|
|
3920
|
+
setGuardMessage(null);
|
|
3921
|
+
setConfirmation({
|
|
3922
|
+
findings: guardResult.findings,
|
|
3923
|
+
generic: false,
|
|
3924
|
+
...guardResult.message === void 0 ? {} : { message: guardResult.message }
|
|
3925
|
+
});
|
|
3926
|
+
return { status: "cancelled" };
|
|
3927
|
+
}
|
|
3779
3928
|
}
|
|
3780
|
-
if (
|
|
3929
|
+
if (confirmationEnabled) {
|
|
3781
3930
|
setGuardMessage(null);
|
|
3782
|
-
setConfirmation({
|
|
3783
|
-
findings: guardResult.findings,
|
|
3784
|
-
...guardResult.message === void 0 ? {} : { message: guardResult.message }
|
|
3785
|
-
});
|
|
3931
|
+
setConfirmation({ findings: [], generic: true });
|
|
3786
3932
|
return { status: "cancelled" };
|
|
3787
3933
|
}
|
|
3788
3934
|
} finally {
|
|
@@ -3945,17 +4091,18 @@ function ContextFormRenderer({
|
|
|
3945
4091
|
{
|
|
3946
4092
|
ref: confirmationRef,
|
|
3947
4093
|
className: "fe-submission-confirmation",
|
|
3948
|
-
role:
|
|
4094
|
+
role: confirmationRenderMode === "dialog" ? void 0 : "dialog",
|
|
3949
4095
|
children: slots.renderSubmissionConfirmation?.({
|
|
3950
4096
|
findings: confirmation?.findings ?? [],
|
|
3951
|
-
message: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")),
|
|
4097
|
+
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
4098
|
schema: form.schema,
|
|
3953
4099
|
visibleValues,
|
|
4100
|
+
visibleItems,
|
|
3954
4101
|
onConfirm: confirmSubmission,
|
|
3955
4102
|
onCancel: cancelSubmission
|
|
3956
4103
|
}) ?? /* @__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")) }),
|
|
4104
|
+
/* @__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") }),
|
|
4105
|
+
/* @__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
4106
|
(confirmation?.findings ?? []).length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { children: (confirmation?.findings ?? []).map((finding, index) => {
|
|
3960
4107
|
const field = form.schema.fields.find((candidate) => candidate.id === finding.fieldId);
|
|
3961
4108
|
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 +4118,11 @@ function ContextFormRenderer({
|
|
|
3971
4118
|
] })
|
|
3972
4119
|
] }, `${finding.fieldId}-${finding.type}-${finding.start ?? index}`);
|
|
3973
4120
|
}) }),
|
|
4121
|
+
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: [
|
|
4122
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: item.title }),
|
|
4123
|
+
": ",
|
|
4124
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: item.displayValue })
|
|
4125
|
+
] }, item.fieldId)) }) : null,
|
|
3974
4126
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "data-fe-confirm": "true", onClick: confirmSubmission, children: resolveMessage("confirmButton", form.translate("form.confirmSubmission")) }),
|
|
3975
4127
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: cancelSubmission, children: resolveMessage("cancelButton", form.translate("form.cancelSubmission")) })
|
|
3976
4128
|
] })
|
|
@@ -3990,7 +4142,7 @@ function ContextFormRenderer({
|
|
|
3990
4142
|
if (form.submitStatus === "success" && isReplaceMode) {
|
|
3991
4143
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `fe-form ${className}`.trim(), children: completionRegion });
|
|
3992
4144
|
}
|
|
3993
|
-
if (confirmation !== null &&
|
|
4145
|
+
if (confirmation !== null && confirmationRenderMode === "replace") {
|
|
3994
4146
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `fe-form ${className}`.trim(), children: confirmationContent });
|
|
3995
4147
|
}
|
|
3996
4148
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
@@ -4001,7 +4153,7 @@ function ContextFormRenderer({
|
|
|
4001
4153
|
className: `fe-form ${className}`.trim(),
|
|
4002
4154
|
noValidate: true,
|
|
4003
4155
|
onSubmit: handleSubmit,
|
|
4004
|
-
"aria-hidden": confirmation !== null &&
|
|
4156
|
+
"aria-hidden": confirmation !== null && confirmationRenderMode === "dialog" ? true : void 0,
|
|
4005
4157
|
children: [
|
|
4006
4158
|
slots.renderHeader?.({
|
|
4007
4159
|
title: form.schema.title,
|
|
@@ -4064,13 +4216,13 @@ function ContextFormRenderer({
|
|
|
4064
4216
|
}) }, field.id);
|
|
4065
4217
|
}
|
|
4066
4218
|
const Component = components[field.type];
|
|
4067
|
-
return Component === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DefaultField, { ...props }, field.id) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Component, { ...props }, field.id);
|
|
4219
|
+
return Component === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DefaultField, { ...props, groupedChoiceFields: isGroupedMode }, field.id) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Component, { ...props }, field.id);
|
|
4068
4220
|
});
|
|
4069
4221
|
const fieldClassName = `fe-fields${fieldsClassName === void 0 ? "" : ` ${fieldsClassName}`}`;
|
|
4070
4222
|
return slots.renderFields?.({ children: fieldChildren, className: fieldClassName }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: fieldClassName, children: fieldChildren });
|
|
4071
4223
|
})(),
|
|
4072
4224
|
guardMessage === null ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "alert", children: guardMessage }),
|
|
4073
|
-
confirmation !== null &&
|
|
4225
|
+
confirmation !== null && confirmationRenderMode === "inline" ? confirmationContent : null,
|
|
4074
4226
|
validationIssues.length === 0 ? null : slots.renderValidationSummary?.({ issues: validationIssues }) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-validation-summary", role: "alert", children: [
|
|
4075
4227
|
validationIssues.length,
|
|
4076
4228
|
" validation error",
|
|
@@ -4125,7 +4277,7 @@ function ContextFormRenderer({
|
|
|
4125
4277
|
]
|
|
4126
4278
|
}
|
|
4127
4279
|
),
|
|
4128
|
-
confirmation !== null &&
|
|
4280
|
+
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
4281
|
] });
|
|
4130
4282
|
}
|
|
4131
4283
|
var RENDERER_MESSAGES = {
|
package/dist/index.d.cts
CHANGED
|
@@ -477,7 +477,20 @@ type SubmissionGuardResult = {
|
|
|
477
477
|
};
|
|
478
478
|
type SubmissionGuard = (schema: FormSchema, values: Record<string, unknown>) => SubmissionGuardResult | Promise<SubmissionGuardResult>;
|
|
479
479
|
type FormSuccessRenderMode = "append" | "replace";
|
|
480
|
+
type ChoiceFieldLayoutMode = "default" | "grouped";
|
|
481
|
+
interface FormRendererAppearance {
|
|
482
|
+
/**
|
|
483
|
+
* Layout preset for radio and checkbox questions.
|
|
484
|
+
* - "default": keep the flat question layout.
|
|
485
|
+
* - "grouped": render a bordered fieldset and legend.
|
|
486
|
+
*/
|
|
487
|
+
readonly choiceField?: ChoiceFieldLayoutMode;
|
|
488
|
+
}
|
|
480
489
|
type SubmissionConfirmationRenderMode = "inline" | "replace" | "dialog";
|
|
490
|
+
interface SubmissionConfirmationOptions {
|
|
491
|
+
readonly enabled?: boolean;
|
|
492
|
+
readonly renderMode?: SubmissionConfirmationRenderMode;
|
|
493
|
+
}
|
|
481
494
|
type FormSubmitStatus = "idle" | "submitting" | "confirming" | "success" | "error";
|
|
482
495
|
/** @deprecated Use FormSubmitStatus instead. */
|
|
483
496
|
type FormSubmitState = "idle" | "submitting" | "confirming" | "success" | "error";
|
|
@@ -488,10 +501,11 @@ interface RenderSubmitButtonProps {
|
|
|
488
501
|
readonly onSubmit: () => void;
|
|
489
502
|
}
|
|
490
503
|
interface SubmissionConfirmationSlotProps {
|
|
491
|
-
readonly findings
|
|
504
|
+
readonly findings?: readonly SensitiveDataFinding[];
|
|
492
505
|
readonly message?: string;
|
|
493
506
|
readonly schema: FormSchema;
|
|
494
507
|
readonly visibleValues: Record<string, unknown>;
|
|
508
|
+
readonly visibleItems?: readonly FormSubmittedAnswerItem[];
|
|
495
509
|
readonly onConfirm: () => void;
|
|
496
510
|
readonly onCancel: () => void;
|
|
497
511
|
}
|
|
@@ -666,11 +680,16 @@ type FieldComponents = Partial<Record<FieldType, ComponentType<FieldComponentPro
|
|
|
666
680
|
interface FormRendererPresentationProps extends SubmissionProtectionProps {
|
|
667
681
|
readonly components?: FieldComponents;
|
|
668
682
|
readonly className?: string;
|
|
683
|
+
readonly appearance?: FormRendererAppearance;
|
|
684
|
+
/** @deprecated Use appearance.choiceField="grouped" instead. */
|
|
685
|
+
readonly groupedChoiceFields?: boolean;
|
|
669
686
|
/**
|
|
670
687
|
* Controls where the completion message is rendered after a successful submission.
|
|
671
688
|
* Defaults to "append" for backwards compatibility.
|
|
672
689
|
*/
|
|
673
690
|
readonly successRenderMode?: FormSuccessRenderMode;
|
|
691
|
+
readonly submissionConfirmation?: SubmissionConfirmationOptions;
|
|
692
|
+
/** @deprecated Use submissionConfirmation.renderMode instead. */
|
|
674
693
|
readonly submissionConfirmationRenderMode?: SubmissionConfirmationRenderMode;
|
|
675
694
|
readonly showHiddenFieldsInSummary?: boolean;
|
|
676
695
|
readonly fieldsClassName?: string;
|
|
@@ -697,4 +716,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
|
|
|
697
716
|
type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
|
|
698
717
|
declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
|
|
699
718
|
|
|
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 };
|
|
719
|
+
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 ChoiceFieldLayoutMode, 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 FormRendererAppearance, 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
|
@@ -477,7 +477,20 @@ type SubmissionGuardResult = {
|
|
|
477
477
|
};
|
|
478
478
|
type SubmissionGuard = (schema: FormSchema, values: Record<string, unknown>) => SubmissionGuardResult | Promise<SubmissionGuardResult>;
|
|
479
479
|
type FormSuccessRenderMode = "append" | "replace";
|
|
480
|
+
type ChoiceFieldLayoutMode = "default" | "grouped";
|
|
481
|
+
interface FormRendererAppearance {
|
|
482
|
+
/**
|
|
483
|
+
* Layout preset for radio and checkbox questions.
|
|
484
|
+
* - "default": keep the flat question layout.
|
|
485
|
+
* - "grouped": render a bordered fieldset and legend.
|
|
486
|
+
*/
|
|
487
|
+
readonly choiceField?: ChoiceFieldLayoutMode;
|
|
488
|
+
}
|
|
480
489
|
type SubmissionConfirmationRenderMode = "inline" | "replace" | "dialog";
|
|
490
|
+
interface SubmissionConfirmationOptions {
|
|
491
|
+
readonly enabled?: boolean;
|
|
492
|
+
readonly renderMode?: SubmissionConfirmationRenderMode;
|
|
493
|
+
}
|
|
481
494
|
type FormSubmitStatus = "idle" | "submitting" | "confirming" | "success" | "error";
|
|
482
495
|
/** @deprecated Use FormSubmitStatus instead. */
|
|
483
496
|
type FormSubmitState = "idle" | "submitting" | "confirming" | "success" | "error";
|
|
@@ -488,10 +501,11 @@ interface RenderSubmitButtonProps {
|
|
|
488
501
|
readonly onSubmit: () => void;
|
|
489
502
|
}
|
|
490
503
|
interface SubmissionConfirmationSlotProps {
|
|
491
|
-
readonly findings
|
|
504
|
+
readonly findings?: readonly SensitiveDataFinding[];
|
|
492
505
|
readonly message?: string;
|
|
493
506
|
readonly schema: FormSchema;
|
|
494
507
|
readonly visibleValues: Record<string, unknown>;
|
|
508
|
+
readonly visibleItems?: readonly FormSubmittedAnswerItem[];
|
|
495
509
|
readonly onConfirm: () => void;
|
|
496
510
|
readonly onCancel: () => void;
|
|
497
511
|
}
|
|
@@ -666,11 +680,16 @@ type FieldComponents = Partial<Record<FieldType, ComponentType<FieldComponentPro
|
|
|
666
680
|
interface FormRendererPresentationProps extends SubmissionProtectionProps {
|
|
667
681
|
readonly components?: FieldComponents;
|
|
668
682
|
readonly className?: string;
|
|
683
|
+
readonly appearance?: FormRendererAppearance;
|
|
684
|
+
/** @deprecated Use appearance.choiceField="grouped" instead. */
|
|
685
|
+
readonly groupedChoiceFields?: boolean;
|
|
669
686
|
/**
|
|
670
687
|
* Controls where the completion message is rendered after a successful submission.
|
|
671
688
|
* Defaults to "append" for backwards compatibility.
|
|
672
689
|
*/
|
|
673
690
|
readonly successRenderMode?: FormSuccessRenderMode;
|
|
691
|
+
readonly submissionConfirmation?: SubmissionConfirmationOptions;
|
|
692
|
+
/** @deprecated Use submissionConfirmation.renderMode instead. */
|
|
674
693
|
readonly submissionConfirmationRenderMode?: SubmissionConfirmationRenderMode;
|
|
675
694
|
readonly showHiddenFieldsInSummary?: boolean;
|
|
676
695
|
readonly fieldsClassName?: string;
|
|
@@ -697,4 +716,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
|
|
|
697
716
|
type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
|
|
698
717
|
declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
|
|
699
718
|
|
|
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 };
|
|
719
|
+
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 ChoiceFieldLayoutMode, 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 FormRendererAppearance, 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
|
@@ -3259,8 +3259,11 @@ function describedBy(field, error, helpId, errorId) {
|
|
|
3259
3259
|
);
|
|
3260
3260
|
return ids.length === 0 ? void 0 : ids.join(" ");
|
|
3261
3261
|
}
|
|
3262
|
-
function RequiredMark({
|
|
3263
|
-
|
|
3262
|
+
function RequiredMark({
|
|
3263
|
+
required,
|
|
3264
|
+
className = "fe-required"
|
|
3265
|
+
}) {
|
|
3266
|
+
return required ? /* @__PURE__ */ jsxs2("span", { className, "aria-hidden": "true", children: [
|
|
3264
3267
|
" ",
|
|
3265
3268
|
"*"
|
|
3266
3269
|
] }) : null;
|
|
@@ -3271,13 +3274,59 @@ function FieldMessage({ props }) {
|
|
|
3271
3274
|
props.error === void 0 ? null : /* @__PURE__ */ jsx3("div", { id: props.errorId, className: "fe-error", children: props.translate(props.error.messageKey, props.error.params) })
|
|
3272
3275
|
] });
|
|
3273
3276
|
}
|
|
3274
|
-
function
|
|
3277
|
+
function GroupedChoiceDescription({ props }) {
|
|
3278
|
+
return props.field.description === void 0 ? null : /* @__PURE__ */ jsx3("div", { id: props.helpId, className: "fe-field-description", children: props.field.description });
|
|
3279
|
+
}
|
|
3280
|
+
function GroupedChoiceError({ props }) {
|
|
3281
|
+
return props.error === void 0 ? null : /* @__PURE__ */ jsx3("div", { id: props.errorId, className: "fe-field-error", role: "alert", children: props.translate(props.error.messageKey, props.error.params) });
|
|
3282
|
+
}
|
|
3283
|
+
function DefaultField({
|
|
3284
|
+
groupedChoiceFields,
|
|
3285
|
+
...props
|
|
3286
|
+
}) {
|
|
3275
3287
|
const { field, value, setValue, inputId, error, translate } = props;
|
|
3276
3288
|
const ariaProps = {
|
|
3277
3289
|
"aria-describedby": describedBy(field, error, props.helpId, props.errorId),
|
|
3278
3290
|
"aria-invalid": error === void 0 ? void 0 : true
|
|
3279
3291
|
};
|
|
3280
3292
|
if (field.type === "checkbox") {
|
|
3293
|
+
if (groupedChoiceFields) {
|
|
3294
|
+
return (
|
|
3295
|
+
// biome-ignore lint/a11y/useAriaPropsSupportedByRole: The grouped fieldset exposes the required state for the complete choice question.
|
|
3296
|
+
/* @__PURE__ */ jsxs2(
|
|
3297
|
+
"fieldset",
|
|
3298
|
+
{
|
|
3299
|
+
className: "fe-choice-group fe-field--checkbox",
|
|
3300
|
+
"data-field-id": field.id,
|
|
3301
|
+
"aria-describedby": describedBy(field, error, props.helpId, props.errorId),
|
|
3302
|
+
"aria-invalid": Boolean(error),
|
|
3303
|
+
"aria-required": field.required,
|
|
3304
|
+
children: [
|
|
3305
|
+
/* @__PURE__ */ jsxs2("legend", { className: "fe-choice-legend", children: [
|
|
3306
|
+
field.title,
|
|
3307
|
+
/* @__PURE__ */ jsx3(RequiredMark, { required: field.required, className: "fe-required-badge" })
|
|
3308
|
+
] }),
|
|
3309
|
+
/* @__PURE__ */ jsx3(GroupedChoiceDescription, { props }),
|
|
3310
|
+
/* @__PURE__ */ jsx3("div", { className: "fe-choice-options", children: /* @__PURE__ */ jsxs2("label", { className: "fe-choice-option", htmlFor: inputId, children: [
|
|
3311
|
+
/* @__PURE__ */ jsx3(
|
|
3312
|
+
"input",
|
|
3313
|
+
{
|
|
3314
|
+
id: inputId,
|
|
3315
|
+
name: field.id,
|
|
3316
|
+
type: "checkbox",
|
|
3317
|
+
checked: value === true,
|
|
3318
|
+
"aria-label": field.title,
|
|
3319
|
+
onChange: (event) => setValue(event.currentTarget.checked)
|
|
3320
|
+
}
|
|
3321
|
+
),
|
|
3322
|
+
/* @__PURE__ */ jsx3("span", { children: field.title })
|
|
3323
|
+
] }) }),
|
|
3324
|
+
/* @__PURE__ */ jsx3(GroupedChoiceError, { props })
|
|
3325
|
+
]
|
|
3326
|
+
}
|
|
3327
|
+
)
|
|
3328
|
+
);
|
|
3329
|
+
}
|
|
3281
3330
|
return /* @__PURE__ */ jsxs2("div", { className: "fe-field fe-field--checkbox", "data-field-id": field.id, children: [
|
|
3282
3331
|
/* @__PURE__ */ jsxs2("label", { className: "fe-check-label", htmlFor: inputId, children: [
|
|
3283
3332
|
/* @__PURE__ */ jsx3(
|
|
@@ -3301,6 +3350,85 @@ function DefaultField(props) {
|
|
|
3301
3350
|
}
|
|
3302
3351
|
if (field.type === "radio" || field.type === "multi-select") {
|
|
3303
3352
|
const selected = Array.isArray(value) ? value : [];
|
|
3353
|
+
if (field.type === "radio" && groupedChoiceFields) {
|
|
3354
|
+
return (
|
|
3355
|
+
// biome-ignore lint/a11y/useAriaPropsSupportedByRole: The grouped fieldset exposes the required state for the complete choice question.
|
|
3356
|
+
/* @__PURE__ */ jsxs2(
|
|
3357
|
+
"fieldset",
|
|
3358
|
+
{
|
|
3359
|
+
className: "fe-choice-group fe-field--radio",
|
|
3360
|
+
"data-field-id": field.id,
|
|
3361
|
+
"aria-describedby": describedBy(field, error, props.helpId, props.errorId),
|
|
3362
|
+
"aria-invalid": Boolean(error),
|
|
3363
|
+
"aria-required": field.required,
|
|
3364
|
+
children: [
|
|
3365
|
+
/* @__PURE__ */ jsxs2("legend", { className: "fe-choice-legend", children: [
|
|
3366
|
+
field.title,
|
|
3367
|
+
/* @__PURE__ */ jsx3(RequiredMark, { required: field.required, className: "fe-required-badge" })
|
|
3368
|
+
] }),
|
|
3369
|
+
/* @__PURE__ */ jsx3(GroupedChoiceDescription, { props }),
|
|
3370
|
+
/* @__PURE__ */ jsx3("div", { className: "fe-choice-options", children: field.options.map((option, index) => {
|
|
3371
|
+
const optionId = `${inputId}-${index}`;
|
|
3372
|
+
const checked = value === option.id;
|
|
3373
|
+
return /* @__PURE__ */ jsxs2("label", { className: "fe-choice-option", htmlFor: optionId, children: [
|
|
3374
|
+
/* @__PURE__ */ jsx3(
|
|
3375
|
+
"input",
|
|
3376
|
+
{
|
|
3377
|
+
id: optionId,
|
|
3378
|
+
name: field.id,
|
|
3379
|
+
type: "radio",
|
|
3380
|
+
value: option.id,
|
|
3381
|
+
checked,
|
|
3382
|
+
onKeyDown: (event) => {
|
|
3383
|
+
if (event.key !== "ArrowDown" && event.key !== "ArrowRight" && event.key !== "ArrowUp" && event.key !== "ArrowLeft")
|
|
3384
|
+
return;
|
|
3385
|
+
event.preventDefault();
|
|
3386
|
+
const offset = event.key === "ArrowDown" || event.key === "ArrowRight" ? 1 : -1;
|
|
3387
|
+
const nextIndex = (index + offset + field.options.length) % field.options.length;
|
|
3388
|
+
const nextOption = field.options[nextIndex];
|
|
3389
|
+
if (nextOption === void 0) return;
|
|
3390
|
+
setValue(nextOption.id);
|
|
3391
|
+
document.getElementById(`${inputId}-${nextIndex}`)?.focus();
|
|
3392
|
+
},
|
|
3393
|
+
onChange: () => setValue(option.id)
|
|
3394
|
+
}
|
|
3395
|
+
),
|
|
3396
|
+
/* @__PURE__ */ jsx3("span", { children: option.label })
|
|
3397
|
+
] }, option.id);
|
|
3398
|
+
}) }),
|
|
3399
|
+
/* @__PURE__ */ jsx3(GroupedChoiceError, { props })
|
|
3400
|
+
]
|
|
3401
|
+
}
|
|
3402
|
+
)
|
|
3403
|
+
);
|
|
3404
|
+
}
|
|
3405
|
+
if (field.type === "radio") {
|
|
3406
|
+
return /* @__PURE__ */ jsxs2("div", { className: "fe-field fe-field--radio", "data-field-id": field.id, children: [
|
|
3407
|
+
/* @__PURE__ */ jsxs2("div", { className: "fe-label", children: [
|
|
3408
|
+
field.title,
|
|
3409
|
+
/* @__PURE__ */ jsx3(RequiredMark, { required: field.required })
|
|
3410
|
+
] }),
|
|
3411
|
+
field.options.map((option, index) => {
|
|
3412
|
+
const optionId = `${inputId}-${index}`;
|
|
3413
|
+
return /* @__PURE__ */ jsxs2("label", { className: "fe-check-label", htmlFor: optionId, children: [
|
|
3414
|
+
/* @__PURE__ */ jsx3(
|
|
3415
|
+
"input",
|
|
3416
|
+
{
|
|
3417
|
+
...ariaProps,
|
|
3418
|
+
id: optionId,
|
|
3419
|
+
name: field.id,
|
|
3420
|
+
type: "radio",
|
|
3421
|
+
value: option.id,
|
|
3422
|
+
checked: value === option.id,
|
|
3423
|
+
onChange: () => setValue(option.id)
|
|
3424
|
+
}
|
|
3425
|
+
),
|
|
3426
|
+
/* @__PURE__ */ jsx3("span", { children: option.label })
|
|
3427
|
+
] }, option.id);
|
|
3428
|
+
}),
|
|
3429
|
+
/* @__PURE__ */ jsx3(FieldMessage, { props })
|
|
3430
|
+
] });
|
|
3431
|
+
}
|
|
3304
3432
|
return /* @__PURE__ */ jsxs2("fieldset", { className: `fe-field fe-field--${field.type}`, "data-field-id": field.id, ...ariaProps, children: [
|
|
3305
3433
|
/* @__PURE__ */ jsxs2("legend", { className: "fe-label", children: [
|
|
3306
3434
|
field.title,
|
|
@@ -3550,7 +3678,10 @@ function ContextFormRenderer({
|
|
|
3550
3678
|
beforeSubmit,
|
|
3551
3679
|
onDraftSave,
|
|
3552
3680
|
successRenderMode = "append",
|
|
3553
|
-
|
|
3681
|
+
appearance,
|
|
3682
|
+
groupedChoiceFields = false,
|
|
3683
|
+
submissionConfirmation,
|
|
3684
|
+
submissionConfirmationRenderMode,
|
|
3554
3685
|
showHiddenFieldsInSummary = false,
|
|
3555
3686
|
fieldsClassName,
|
|
3556
3687
|
hideFormOnSuccess = false,
|
|
@@ -3561,6 +3692,7 @@ function ContextFormRenderer({
|
|
|
3561
3692
|
slots = {}
|
|
3562
3693
|
}) {
|
|
3563
3694
|
const form = useForm();
|
|
3695
|
+
const isGroupedMode = appearance?.choiceField === "grouped" || groupedChoiceFields;
|
|
3564
3696
|
const prefix = useId().replace(/:/g, "");
|
|
3565
3697
|
const formRef = useRef2(null);
|
|
3566
3698
|
const loadedDraftKey = useRef2(null);
|
|
@@ -3586,6 +3718,12 @@ function ContextFormRenderer({
|
|
|
3586
3718
|
const activeVisibleIndex = visiblePageIndexes.indexOf(currentPageIndex);
|
|
3587
3719
|
const fieldIds = activePage === void 0 ? void 0 : new Set(activePage.questionIds);
|
|
3588
3720
|
const visibleValues = useMemo4(() => selectVisibleAnswers2(form.schema, form.values), [form.schema, form.values]);
|
|
3721
|
+
const visibleItems = useMemo4(
|
|
3722
|
+
() => buildSubmittedItems(form.schema, form.values, form.visibility, (key) => form.translate(key), false),
|
|
3723
|
+
[form.schema, form.translate, form.values, form.visibility]
|
|
3724
|
+
);
|
|
3725
|
+
const confirmationRenderMode = submissionConfirmation?.renderMode ?? submissionConfirmationRenderMode ?? "inline";
|
|
3726
|
+
const confirmationEnabled = submissionConfirmation?.enabled === true;
|
|
3589
3727
|
const submitState = confirmation === null && !guardsPending ? form.submitStatus : "confirming";
|
|
3590
3728
|
const interactionLocked = submitState === "confirming" || submitState === "submitting";
|
|
3591
3729
|
const isReplaceMode = successRenderMode === "replace" || hideFormOnSuccess;
|
|
@@ -3653,7 +3791,7 @@ function ContextFormRenderer({
|
|
|
3653
3791
|
if (confirmation === null) return;
|
|
3654
3792
|
const confirmButton = confirmationRef.current?.querySelector("[data-fe-confirm], button");
|
|
3655
3793
|
confirmButton?.focus();
|
|
3656
|
-
if (
|
|
3794
|
+
if (confirmationRenderMode !== "dialog") return;
|
|
3657
3795
|
const onKeyDown = (event) => {
|
|
3658
3796
|
if (event.key === "Escape") {
|
|
3659
3797
|
event.preventDefault();
|
|
@@ -3680,7 +3818,7 @@ function ContextFormRenderer({
|
|
|
3680
3818
|
};
|
|
3681
3819
|
globalThis.addEventListener("keydown", onKeyDown);
|
|
3682
3820
|
return () => globalThis.removeEventListener("keydown", onKeyDown);
|
|
3683
|
-
}, [confirmation,
|
|
3821
|
+
}, [confirmation, confirmationRenderMode, focusSubmitButton]);
|
|
3684
3822
|
useEffect3(() => {
|
|
3685
3823
|
if (autoSaveKey === void 0 || typeof globalThis.localStorage === "undefined") return;
|
|
3686
3824
|
const loadIdentity = `${autoSaveKey}:${form.schema.id}:${form.schema.version}`;
|
|
@@ -3751,21 +3889,29 @@ function ContextFormRenderer({
|
|
|
3751
3889
|
}
|
|
3752
3890
|
const validation = validateAnswers2(form.schema, form.values);
|
|
3753
3891
|
const firstInvalidFieldId = validation.issues[0]?.fieldId;
|
|
3754
|
-
if (validation.valid && !guardsConfirmed
|
|
3892
|
+
if (validation.valid && !guardsConfirmed) {
|
|
3755
3893
|
rendererSubmissionInFlight.current = true;
|
|
3756
|
-
setGuardsPending(
|
|
3894
|
+
setGuardsPending(submissionGuards.length > 0);
|
|
3757
3895
|
try {
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3896
|
+
if (submissionGuards.length > 0) {
|
|
3897
|
+
const guardResult = await runSubmissionGuards(submissionGuards);
|
|
3898
|
+
if (guardResult.status === "block") {
|
|
3899
|
+
setGuardMessage(guardResult.message ?? form.translate("form.submissionBlocked"));
|
|
3900
|
+
return { status: "cancelled" };
|
|
3901
|
+
}
|
|
3902
|
+
if (guardResult.status === "confirm") {
|
|
3903
|
+
setGuardMessage(null);
|
|
3904
|
+
setConfirmation({
|
|
3905
|
+
findings: guardResult.findings,
|
|
3906
|
+
generic: false,
|
|
3907
|
+
...guardResult.message === void 0 ? {} : { message: guardResult.message }
|
|
3908
|
+
});
|
|
3909
|
+
return { status: "cancelled" };
|
|
3910
|
+
}
|
|
3762
3911
|
}
|
|
3763
|
-
if (
|
|
3912
|
+
if (confirmationEnabled) {
|
|
3764
3913
|
setGuardMessage(null);
|
|
3765
|
-
setConfirmation({
|
|
3766
|
-
findings: guardResult.findings,
|
|
3767
|
-
...guardResult.message === void 0 ? {} : { message: guardResult.message }
|
|
3768
|
-
});
|
|
3914
|
+
setConfirmation({ findings: [], generic: true });
|
|
3769
3915
|
return { status: "cancelled" };
|
|
3770
3916
|
}
|
|
3771
3917
|
} finally {
|
|
@@ -3928,17 +4074,18 @@ function ContextFormRenderer({
|
|
|
3928
4074
|
{
|
|
3929
4075
|
ref: confirmationRef,
|
|
3930
4076
|
className: "fe-submission-confirmation",
|
|
3931
|
-
role:
|
|
4077
|
+
role: confirmationRenderMode === "dialog" ? void 0 : "dialog",
|
|
3932
4078
|
children: slots.renderSubmissionConfirmation?.({
|
|
3933
4079
|
findings: confirmation?.findings ?? [],
|
|
3934
|
-
message: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")),
|
|
4080
|
+
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
4081
|
schema: form.schema,
|
|
3936
4082
|
visibleValues,
|
|
4083
|
+
visibleItems,
|
|
3937
4084
|
onConfirm: confirmSubmission,
|
|
3938
4085
|
onCancel: cancelSubmission
|
|
3939
4086
|
}) ?? /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
3940
|
-
/* @__PURE__ */ jsx3("h2", { children: resolveMessage("confirmSensitiveDataTitle") }),
|
|
3941
|
-
/* @__PURE__ */ jsx3("p", { children: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")) }),
|
|
4087
|
+
/* @__PURE__ */ jsx3("h2", { children: confirmation?.generic === true ? form.locale.toLowerCase().startsWith("ja") ? "\u56DE\u7B54\u5185\u5BB9\u306E\u78BA\u8A8D" : "Review your answers" : resolveMessage("confirmSensitiveDataTitle") }),
|
|
4088
|
+
/* @__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
4089
|
(confirmation?.findings ?? []).length === 0 ? null : /* @__PURE__ */ jsx3("ul", { children: (confirmation?.findings ?? []).map((finding, index) => {
|
|
3943
4090
|
const field = form.schema.fields.find((candidate) => candidate.id === finding.fieldId);
|
|
3944
4091
|
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 +4101,11 @@ function ContextFormRenderer({
|
|
|
3954
4101
|
] })
|
|
3955
4102
|
] }, `${finding.fieldId}-${finding.type}-${finding.start ?? index}`);
|
|
3956
4103
|
}) }),
|
|
4104
|
+
confirmation?.generic === true ? /* @__PURE__ */ jsx3("ul", { className: "fe-submission-summary", children: visibleItems.map((item) => /* @__PURE__ */ jsxs2("li", { children: [
|
|
4105
|
+
/* @__PURE__ */ jsx3("span", { children: item.title }),
|
|
4106
|
+
": ",
|
|
4107
|
+
/* @__PURE__ */ jsx3("span", { children: item.displayValue })
|
|
4108
|
+
] }, item.fieldId)) }) : null,
|
|
3957
4109
|
/* @__PURE__ */ jsx3("button", { type: "button", "data-fe-confirm": "true", onClick: confirmSubmission, children: resolveMessage("confirmButton", form.translate("form.confirmSubmission")) }),
|
|
3958
4110
|
/* @__PURE__ */ jsx3("button", { type: "button", onClick: cancelSubmission, children: resolveMessage("cancelButton", form.translate("form.cancelSubmission")) })
|
|
3959
4111
|
] })
|
|
@@ -3973,7 +4125,7 @@ function ContextFormRenderer({
|
|
|
3973
4125
|
if (form.submitStatus === "success" && isReplaceMode) {
|
|
3974
4126
|
return /* @__PURE__ */ jsx3("div", { className: `fe-form ${className}`.trim(), children: completionRegion });
|
|
3975
4127
|
}
|
|
3976
|
-
if (confirmation !== null &&
|
|
4128
|
+
if (confirmation !== null && confirmationRenderMode === "replace") {
|
|
3977
4129
|
return /* @__PURE__ */ jsx3("div", { className: `fe-form ${className}`.trim(), children: confirmationContent });
|
|
3978
4130
|
}
|
|
3979
4131
|
return /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
@@ -3984,7 +4136,7 @@ function ContextFormRenderer({
|
|
|
3984
4136
|
className: `fe-form ${className}`.trim(),
|
|
3985
4137
|
noValidate: true,
|
|
3986
4138
|
onSubmit: handleSubmit,
|
|
3987
|
-
"aria-hidden": confirmation !== null &&
|
|
4139
|
+
"aria-hidden": confirmation !== null && confirmationRenderMode === "dialog" ? true : void 0,
|
|
3988
4140
|
children: [
|
|
3989
4141
|
slots.renderHeader?.({
|
|
3990
4142
|
title: form.schema.title,
|
|
@@ -4047,13 +4199,13 @@ function ContextFormRenderer({
|
|
|
4047
4199
|
}) }, field.id);
|
|
4048
4200
|
}
|
|
4049
4201
|
const Component = components[field.type];
|
|
4050
|
-
return Component === void 0 ? /* @__PURE__ */ jsx3(DefaultField, { ...props }, field.id) : /* @__PURE__ */ jsx3(Component, { ...props }, field.id);
|
|
4202
|
+
return Component === void 0 ? /* @__PURE__ */ jsx3(DefaultField, { ...props, groupedChoiceFields: isGroupedMode }, field.id) : /* @__PURE__ */ jsx3(Component, { ...props }, field.id);
|
|
4051
4203
|
});
|
|
4052
4204
|
const fieldClassName = `fe-fields${fieldsClassName === void 0 ? "" : ` ${fieldsClassName}`}`;
|
|
4053
4205
|
return slots.renderFields?.({ children: fieldChildren, className: fieldClassName }) ?? /* @__PURE__ */ jsx3("div", { className: fieldClassName, children: fieldChildren });
|
|
4054
4206
|
})(),
|
|
4055
4207
|
guardMessage === null ? null : /* @__PURE__ */ jsx3("div", { role: "alert", children: guardMessage }),
|
|
4056
|
-
confirmation !== null &&
|
|
4208
|
+
confirmation !== null && confirmationRenderMode === "inline" ? confirmationContent : null,
|
|
4057
4209
|
validationIssues.length === 0 ? null : slots.renderValidationSummary?.({ issues: validationIssues }) ?? /* @__PURE__ */ jsxs2("div", { className: "fe-validation-summary", role: "alert", children: [
|
|
4058
4210
|
validationIssues.length,
|
|
4059
4211
|
" validation error",
|
|
@@ -4108,7 +4260,7 @@ function ContextFormRenderer({
|
|
|
4108
4260
|
]
|
|
4109
4261
|
}
|
|
4110
4262
|
),
|
|
4111
|
-
confirmation !== null &&
|
|
4263
|
+
confirmation !== null && confirmationRenderMode === "dialog" ? /* @__PURE__ */ jsx3("div", { className: "fe-confirmation-dialog-backdrop", role: "dialog", "aria-modal": "true", children: confirmationContent }) : null
|
|
4112
4264
|
] });
|
|
4113
4265
|
}
|
|
4114
4266
|
var RENDERER_MESSAGES = {
|
package/dist/styles.css
CHANGED
|
@@ -123,6 +123,41 @@
|
|
|
123
123
|
.fe-check-label input {
|
|
124
124
|
margin-top: 0.22rem;
|
|
125
125
|
}
|
|
126
|
+
.fe-choice-group {
|
|
127
|
+
background-color: var(--fe-bg-color, #ffffff);
|
|
128
|
+
border: 1px solid var(--fe-border-color, #e2e8f0);
|
|
129
|
+
border-radius: var(--fe-radius, 8px);
|
|
130
|
+
box-sizing: border-box;
|
|
131
|
+
margin: 0 0 1.25rem;
|
|
132
|
+
padding: 1rem 1.25rem;
|
|
133
|
+
}
|
|
134
|
+
.fe-choice-legend {
|
|
135
|
+
color: var(--fe-text-color, #1a202c);
|
|
136
|
+
font-size: 0.95rem;
|
|
137
|
+
font-weight: 600;
|
|
138
|
+
padding: 0 0.5rem;
|
|
139
|
+
}
|
|
140
|
+
.fe-choice-options {
|
|
141
|
+
display: flex;
|
|
142
|
+
flex-direction: column;
|
|
143
|
+
gap: 0.625rem;
|
|
144
|
+
margin-top: 0.5rem;
|
|
145
|
+
}
|
|
146
|
+
.fe-choice-option {
|
|
147
|
+
align-items: center;
|
|
148
|
+
cursor: pointer;
|
|
149
|
+
display: flex;
|
|
150
|
+
gap: 0.5rem;
|
|
151
|
+
}
|
|
152
|
+
.fe-field-description {
|
|
153
|
+
color: #667085;
|
|
154
|
+
font-size: 0.875rem;
|
|
155
|
+
}
|
|
156
|
+
.fe-field-error {
|
|
157
|
+
color: #b42318;
|
|
158
|
+
font-size: 0.875rem;
|
|
159
|
+
font-weight: 600;
|
|
160
|
+
}
|
|
126
161
|
.fe-rating-options {
|
|
127
162
|
display: flex;
|
|
128
163
|
flex-wrap: wrap;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@form-engine-ts/react",
|
|
3
|
-
"version": "4.2
|
|
3
|
+
"version": "4.3.2",
|
|
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
|
|
46
|
-
"@form-engine-ts/privacy": "4.2
|
|
45
|
+
"@form-engine-ts/core": "4.3.2",
|
|
46
|
+
"@form-engine-ts/privacy": "4.3.2"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": ">=18.2 <20",
|