@form-engine-ts/react 2.9.6 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -12
- package/dist/index.cjs +203 -44
- package/dist/index.d.cts +36 -7
- package/dist/index.d.ts +36 -7
- package/dist/index.js +203 -44
- package/dist/styles.css +34 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ export function ContactForm() {
|
|
|
29
29
|
schema={schema}
|
|
30
30
|
locale="en"
|
|
31
31
|
translator={mockTranslator}
|
|
32
|
-
onSubmit={async (values) => console.log(values)}
|
|
32
|
+
onSubmit={async (values, context) => console.log(values, context.attemptId)}
|
|
33
33
|
>
|
|
34
34
|
<FormRenderer />
|
|
35
35
|
</FormProvider>
|
|
@@ -149,14 +149,15 @@ and `useSubmissionReceipts` loads multiple form/version receipts for list and da
|
|
|
149
149
|
|
|
150
150
|
Receipt persistence is best-effort: `onReceiptError` observes storage failures while the successful completion screen is
|
|
151
151
|
preserved. Pass an SSR-safe `createLocalStorageSubmissionAttemptStore()` as `attemptStore` to reserve an ID immediately
|
|
152
|
-
before submission.
|
|
153
|
-
|
|
154
|
-
to concurrent `get` calls.
|
|
155
|
-
|
|
156
|
-
After success, completion rendering receives a snapshot of `answers`, `schema`, the
|
|
157
|
-
`submittedItems`. Each summary item includes the field title, raw value, formatted display value,
|
|
158
|
-
metadata. Use `renderSubmittedValues` for a typed summary slot; hidden fields are omitted by default
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
`
|
|
162
|
-
`
|
|
152
|
+
before submission. `onSubmit(answers, context)` keeps `attemptId`, `formId`, `formVersion`, `locale`, and `submittedAt`
|
|
153
|
+
outside the answers object, retains the same attempt after a failed request, promotes it to the receipt after success,
|
|
154
|
+
and then clears the attempt. Custom receipt stores may omit `getBatch`; the hook falls back to concurrent `get` calls.
|
|
155
|
+
|
|
156
|
+
After success, completion rendering receives a typed `FormCompletionSlotProps` snapshot of `answers`, `schema`, the
|
|
157
|
+
optional response, and `submittedItems`. Each summary item includes the field title, raw value, formatted display value,
|
|
158
|
+
visibility, and field metadata. Use `renderSubmittedValues` for a typed summary slot; hidden fields are omitted by default
|
|
159
|
+
and can be included with `showHiddenFieldsInSummary`. Supply `messages` or `messageResolver` to localize standard buttons,
|
|
160
|
+
validation, retry, already-submitted, server-error, and sensitive-data confirmation UI. Server validation can be returned
|
|
161
|
+
by throwing `FormSubmissionError` or a payload with `fieldErrors` and `formError`; field messages are mapped back to the
|
|
162
|
+
form, scrolled into view, and focused. Use `submissionConfirmationRenderMode="replace"` or `"dialog"` for alternate
|
|
163
|
+
confirmation presentations, and `fieldsClassName` or `renderFields` to control the fields wrapper.
|
package/dist/index.cjs
CHANGED
|
@@ -1245,6 +1245,11 @@ var BUILDER_DEFAULTS = {
|
|
|
1245
1245
|
"builder.translating": "Translating\u2026",
|
|
1246
1246
|
"builder.translationLocale": "Translation locale",
|
|
1247
1247
|
"builder.selectLocale": "Select a locale to edit translations.",
|
|
1248
|
+
"builder.localization.selectLocaleToAdd": "Select a locale to add",
|
|
1249
|
+
"builder.localization.noLocalesConfigured": "Translations not configured",
|
|
1250
|
+
"builder.localization.localesConfiguredSummary": "{{count}} locales configured",
|
|
1251
|
+
"builder.localization.allLocalesAdded": "\u3059\u3079\u3066\u306E\u5019\u88DC\u8A00\u8A9E\u304C\u8FFD\u52A0\u6E08\u307F\u3067\u3059",
|
|
1252
|
+
"builder.localization.maxLocalesReached": "\u767B\u9332\u53EF\u80FD\u306A\u6700\u5927\u8A00\u8A9E\u6570\uFF08{{max}}\uFF09\u306B\u9054\u3057\u307E\u3057\u305F",
|
|
1248
1253
|
"builder.translation": "{{locale}} translation",
|
|
1249
1254
|
"builder.translatedFormTitle": "Translated form title",
|
|
1250
1255
|
"builder.translatedFormDescription": "Translated form description",
|
|
@@ -2608,6 +2613,18 @@ function FormBuilder({
|
|
|
2608
2613
|
// src/context.tsx
|
|
2609
2614
|
var import_core3 = require("@form-engine-ts/core");
|
|
2610
2615
|
var import_react3 = require("react");
|
|
2616
|
+
|
|
2617
|
+
// src/types.ts
|
|
2618
|
+
var FormSubmissionError = class extends Error {
|
|
2619
|
+
payload;
|
|
2620
|
+
constructor(message, payload) {
|
|
2621
|
+
super(message);
|
|
2622
|
+
this.name = "FormSubmissionError";
|
|
2623
|
+
this.payload = payload ?? { formError: message };
|
|
2624
|
+
}
|
|
2625
|
+
};
|
|
2626
|
+
|
|
2627
|
+
// src/context.tsx
|
|
2611
2628
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
2612
2629
|
var FormContext = (0, import_react3.createContext)(null);
|
|
2613
2630
|
function issuesByField(issues) {
|
|
@@ -2615,6 +2632,18 @@ function issuesByField(issues) {
|
|
|
2615
2632
|
for (const issue of issues) result[issue.fieldId] ??= issue;
|
|
2616
2633
|
return result;
|
|
2617
2634
|
}
|
|
2635
|
+
function defaultAttemptId2() {
|
|
2636
|
+
const randomUuid = globalThis.crypto?.randomUUID;
|
|
2637
|
+
if (typeof randomUuid === "function") return randomUuid.call(globalThis.crypto);
|
|
2638
|
+
return `attempt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
2639
|
+
}
|
|
2640
|
+
function isServerErrorPayload(value) {
|
|
2641
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
2642
|
+
const record = value;
|
|
2643
|
+
const fieldErrors = record.fieldErrors;
|
|
2644
|
+
const formError = record.formError;
|
|
2645
|
+
return (Object.hasOwn(record, "fieldErrors") || Object.hasOwn(record, "formError")) && (fieldErrors === void 0 || typeof fieldErrors === "object" && fieldErrors !== null && !Array.isArray(fieldErrors) && Object.values(fieldErrors).every((message) => typeof message === "string")) && (formError === void 0 || typeof formError === "string");
|
|
2646
|
+
}
|
|
2618
2647
|
function FormProvider({
|
|
2619
2648
|
schema,
|
|
2620
2649
|
locale,
|
|
@@ -2699,7 +2728,7 @@ function FormProvider({
|
|
|
2699
2728
|
setSubmitError(null);
|
|
2700
2729
|
}, [initialValues]);
|
|
2701
2730
|
const submit = (0, import_react3.useCallback)(
|
|
2702
|
-
async (beforeSubmit,
|
|
2731
|
+
async (beforeSubmit, submitContext) => {
|
|
2703
2732
|
if (submissionInFlight.current) return { status: "cancelled" };
|
|
2704
2733
|
const validation = (0, import_core3.validateAnswers)(validSchema, values);
|
|
2705
2734
|
if (!validation.valid) {
|
|
@@ -2720,13 +2749,22 @@ function FormProvider({
|
|
|
2720
2749
|
setSubmitStatus("idle");
|
|
2721
2750
|
return { status: "cancelled" };
|
|
2722
2751
|
}
|
|
2723
|
-
const
|
|
2724
|
-
|
|
2752
|
+
const context = submitContext ?? {
|
|
2753
|
+
attemptId: defaultAttemptId2(),
|
|
2754
|
+
formId: validSchema.id,
|
|
2755
|
+
formVersion: validSchema.version,
|
|
2756
|
+
locale,
|
|
2757
|
+
submittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2758
|
+
};
|
|
2759
|
+
const response = await onSubmit({ ...visibleValues }, context);
|
|
2725
2760
|
if (resetOnSuccess) setValues({ ...initialValues });
|
|
2726
2761
|
setSubmitStatus("success");
|
|
2727
2762
|
return response === void 0 ? { status: "success" } : { status: "success", response };
|
|
2728
2763
|
} catch (cause) {
|
|
2729
|
-
const error = cause
|
|
2764
|
+
const error = isServerErrorPayload(cause) ? new FormSubmissionError(
|
|
2765
|
+
cause.formError ?? (cause instanceof Error ? cause.message : "Form submission failed."),
|
|
2766
|
+
cause
|
|
2767
|
+
) : cause instanceof Error ? cause : new Error(String(cause));
|
|
2730
2768
|
setSubmitError(error);
|
|
2731
2769
|
setSubmitStatus("error");
|
|
2732
2770
|
return { status: "error", error };
|
|
@@ -2734,7 +2772,7 @@ function FormProvider({
|
|
|
2734
2772
|
submissionInFlight.current = false;
|
|
2735
2773
|
}
|
|
2736
2774
|
},
|
|
2737
|
-
[initialValues, onSubmit, resetOnSuccess, validSchema, values]
|
|
2775
|
+
[initialValues, locale, onSubmit, resetOnSuccess, validSchema, values]
|
|
2738
2776
|
);
|
|
2739
2777
|
const translate = (0, import_react3.useCallback)(
|
|
2740
2778
|
(key, params) => translator.translate(key, locale, params),
|
|
@@ -2914,18 +2952,6 @@ function useSubmissionReceipts(store, queries) {
|
|
|
2914
2952
|
// src/renderer.tsx
|
|
2915
2953
|
var import_core4 = require("@form-engine-ts/core");
|
|
2916
2954
|
var import_react5 = require("react");
|
|
2917
|
-
|
|
2918
|
-
// src/types.ts
|
|
2919
|
-
var FormSubmissionError = class extends Error {
|
|
2920
|
-
payload;
|
|
2921
|
-
constructor(message, payload) {
|
|
2922
|
-
super(message);
|
|
2923
|
-
this.name = "FormSubmissionError";
|
|
2924
|
-
this.payload = payload ?? { formError: message };
|
|
2925
|
-
}
|
|
2926
|
-
};
|
|
2927
|
-
|
|
2928
|
-
// src/renderer.tsx
|
|
2929
2955
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
2930
2956
|
function describedBy(field, error, helpId, errorId) {
|
|
2931
2957
|
const ids = [field.description === void 0 ? void 0 : helpId, error === void 0 ? void 0 : errorId].filter(
|
|
@@ -3112,7 +3138,11 @@ function DefaultField(props) {
|
|
|
3112
3138
|
fieldId: field.id,
|
|
3113
3139
|
current: typeof value === "string" ? value.length : 0,
|
|
3114
3140
|
max: field.maxLength
|
|
3115
|
-
}) :
|
|
3141
|
+
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-character-count", "aria-live": "polite", children: [
|
|
3142
|
+
typeof value === "string" ? value.length : 0,
|
|
3143
|
+
" / ",
|
|
3144
|
+
field.maxLength
|
|
3145
|
+
] }) : null,
|
|
3116
3146
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FieldMessage, { props })
|
|
3117
3147
|
] });
|
|
3118
3148
|
}
|
|
@@ -3164,11 +3194,58 @@ function parseDraft(serialized) {
|
|
|
3164
3194
|
return null;
|
|
3165
3195
|
}
|
|
3166
3196
|
}
|
|
3197
|
+
var DEFAULT_RENDERER_MESSAGES = {
|
|
3198
|
+
en: {
|
|
3199
|
+
submitButton: "Submit",
|
|
3200
|
+
submittingButton: "Submitting...",
|
|
3201
|
+
retryButton: "Retry",
|
|
3202
|
+
requiredField: "This field is required.",
|
|
3203
|
+
alreadySubmittedTitle: "Already Submitted",
|
|
3204
|
+
alreadySubmittedMessage: "Already submitted.",
|
|
3205
|
+
serverErrorSummary: "Submission failed. Please check your answers and try again.",
|
|
3206
|
+
confirmSensitiveDataTitle: "Sensitive data may be included",
|
|
3207
|
+
confirmSensitiveDataMessage: "The following answers may contain personal information. Continue submitting?",
|
|
3208
|
+
confirmButton: "Proceed",
|
|
3209
|
+
cancelButton: "Cancel"
|
|
3210
|
+
},
|
|
3211
|
+
ja: {
|
|
3212
|
+
submitButton: "\u9001\u4FE1\u3059\u308B",
|
|
3213
|
+
submittingButton: "\u9001\u4FE1\u4E2D...",
|
|
3214
|
+
retryButton: "\u518D\u9001\u4FE1\u3059\u308B",
|
|
3215
|
+
requiredField: "\u3053\u306E\u9805\u76EE\u306F\u5FC5\u9808\u3067\u3059",
|
|
3216
|
+
alreadySubmittedTitle: "\u56DE\u7B54\u6E08\u307F\u3067\u3059",
|
|
3217
|
+
alreadySubmittedMessage: "\u3053\u306E\u30A2\u30F3\u30B1\u30FC\u30C8\u306B\u306F\u3059\u3067\u306B\u56DE\u7B54\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
3218
|
+
serverErrorSummary: "\u9001\u4FE1\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002\u5185\u5BB9\u3092\u3054\u78BA\u8A8D\u306E\u4E0A\u3001\u518D\u5EA6\u304A\u8A66\u3057\u304F\u3060\u3055\u3044\u3002",
|
|
3219
|
+
confirmSensitiveDataTitle: "\u500B\u4EBA\u60C5\u5831\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059",
|
|
3220
|
+
confirmSensitiveDataMessage: "\u4EE5\u4E0B\u306E\u9805\u76EE\u306B\u500B\u4EBA\u60C5\u5831\u3068\u307F\u3089\u308C\u308B\u8A18\u8FF0\u304C\u3042\u308A\u307E\u3059\u3002\u3053\u306E\u307E\u307E\u9001\u4FE1\u3057\u3066\u3082\u3088\u308D\u3057\u3044\u3067\u3059\u304B\uFF1F",
|
|
3221
|
+
confirmButton: "\u3053\u306E\u307E\u307E\u9001\u4FE1",
|
|
3222
|
+
cancelButton: "\u4FEE\u6B63\u3059\u308B"
|
|
3223
|
+
}
|
|
3224
|
+
};
|
|
3225
|
+
function createRendererAttemptId() {
|
|
3226
|
+
const randomUuid = globalThis.crypto?.randomUUID;
|
|
3227
|
+
if (typeof randomUuid === "function") return randomUuid.call(globalThis.crypto);
|
|
3228
|
+
return `attempt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
3229
|
+
}
|
|
3230
|
+
function maskSensitiveValue(finding) {
|
|
3231
|
+
if (finding.maskedText !== void 0) return finding.maskedText;
|
|
3232
|
+
const value = finding.matchedText;
|
|
3233
|
+
if (value === void 0) return void 0;
|
|
3234
|
+
if (finding.type === "email") {
|
|
3235
|
+
const separator = value.indexOf("@");
|
|
3236
|
+
if (separator > 0) return `${value.slice(0, Math.min(2, separator))}***${value.slice(separator)}`;
|
|
3237
|
+
}
|
|
3238
|
+
if (finding.type === "phone" || finding.type === "postal_code") return "***";
|
|
3239
|
+
return value.length <= 2 ? "***" : `${value.slice(0, 2)}***`;
|
|
3240
|
+
}
|
|
3167
3241
|
function ContextFormRenderer({
|
|
3168
3242
|
components = {},
|
|
3169
3243
|
className = "",
|
|
3170
3244
|
successMessageKey,
|
|
3171
3245
|
errorMessageKey,
|
|
3246
|
+
attemptIdFactory,
|
|
3247
|
+
messages = {},
|
|
3248
|
+
messageResolver,
|
|
3172
3249
|
autoSaveKey,
|
|
3173
3250
|
beforeSubmit,
|
|
3174
3251
|
onDraftSave,
|
|
@@ -3197,6 +3274,7 @@ function ContextFormRenderer({
|
|
|
3197
3274
|
const [completionData, setCompletionData] = (0, import_react5.useState)(null);
|
|
3198
3275
|
const [receiptLoaded, setReceiptLoaded] = (0, import_react5.useState)(receiptStore === void 0);
|
|
3199
3276
|
const rendererSubmissionInFlight = (0, import_react5.useRef)(false);
|
|
3277
|
+
const fallbackAttemptId = (0, import_react5.useRef)(null);
|
|
3200
3278
|
const completionRef = (0, import_react5.useRef)(null);
|
|
3201
3279
|
const confirmationRef = (0, import_react5.useRef)(null);
|
|
3202
3280
|
const pages = form.schema.pages;
|
|
@@ -3211,6 +3289,18 @@ function ContextFormRenderer({
|
|
|
3211
3289
|
const submitState = confirmation === null && !guardsPending ? form.submitStatus : "confirming";
|
|
3212
3290
|
const interactionLocked = submitState === "confirming" || submitState === "submitting";
|
|
3213
3291
|
const isReplaceMode = successRenderMode === "replace" || hideFormOnSuccess;
|
|
3292
|
+
const resolveMessage = (0, import_react5.useCallback)(
|
|
3293
|
+
(key, fallback) => {
|
|
3294
|
+
const defaultText = fallback ?? DEFAULT_RENDERER_MESSAGES[form.locale.toLowerCase().startsWith("ja") ? "ja" : "en"][key] ?? key;
|
|
3295
|
+
const configured = messages[key];
|
|
3296
|
+
return messageResolver?.(key, configured ?? defaultText) ?? configured ?? defaultText;
|
|
3297
|
+
},
|
|
3298
|
+
[form.locale, messageResolver, messages]
|
|
3299
|
+
);
|
|
3300
|
+
const fieldTranslate = (0, import_react5.useCallback)(
|
|
3301
|
+
(key, params) => key === "validation.required" && (messages.requiredField !== void 0 || messageResolver !== void 0) ? resolveMessage("requiredField") : form.translate(key, params),
|
|
3302
|
+
[form.translate, messageResolver, messages.requiredField, resolveMessage]
|
|
3303
|
+
);
|
|
3214
3304
|
const focusSubmitButton = (0, import_react5.useCallback)(() => {
|
|
3215
3305
|
const button = formRef.current?.querySelector(".fe-submit, button[type='submit'], button");
|
|
3216
3306
|
button?.focus();
|
|
@@ -3250,6 +3340,7 @@ function ContextFormRenderer({
|
|
|
3250
3340
|
);
|
|
3251
3341
|
const control = fieldContainer?.querySelector("input, select, textarea");
|
|
3252
3342
|
if (control !== void 0 && control !== null) {
|
|
3343
|
+
control.scrollIntoView?.({ behavior: "smooth", block: "center" });
|
|
3253
3344
|
control.focus();
|
|
3254
3345
|
setFocusFieldId(null);
|
|
3255
3346
|
}
|
|
@@ -3268,6 +3359,23 @@ function ContextFormRenderer({
|
|
|
3268
3359
|
event.preventDefault();
|
|
3269
3360
|
setConfirmation(null);
|
|
3270
3361
|
globalThis.setTimeout(focusSubmitButton, 0);
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3364
|
+
if (event.key !== "Tab") return;
|
|
3365
|
+
const focusable = [
|
|
3366
|
+
...confirmationRef.current?.querySelectorAll(
|
|
3367
|
+
"button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])"
|
|
3368
|
+
) ?? []
|
|
3369
|
+
].filter((element) => !element.hasAttribute("disabled"));
|
|
3370
|
+
if (focusable.length === 0) return;
|
|
3371
|
+
const first = focusable[0];
|
|
3372
|
+
const last = focusable[focusable.length - 1];
|
|
3373
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
3374
|
+
event.preventDefault();
|
|
3375
|
+
last?.focus();
|
|
3376
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
3377
|
+
event.preventDefault();
|
|
3378
|
+
first?.focus();
|
|
3271
3379
|
}
|
|
3272
3380
|
};
|
|
3273
3381
|
globalThis.addEventListener("keydown", onKeyDown);
|
|
@@ -3369,17 +3477,24 @@ function ContextFormRenderer({
|
|
|
3369
3477
|
rendererSubmissionInFlight.current = true;
|
|
3370
3478
|
try {
|
|
3371
3479
|
let submissionAttempt;
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3480
|
+
let attemptId = fallbackAttemptId.current;
|
|
3481
|
+
if (attemptStore !== void 0) {
|
|
3482
|
+
submissionAttempt = await attemptStore.getOrCreate(form.schema.id, form.schema.version, attemptIdFactory);
|
|
3483
|
+
attemptId = submissionAttempt.attemptId;
|
|
3484
|
+
} else if (attemptId === null) {
|
|
3485
|
+
attemptId = attemptIdFactory?.() ?? createRendererAttemptId();
|
|
3486
|
+
fallbackAttemptId.current = attemptId;
|
|
3487
|
+
}
|
|
3488
|
+
if (attemptId === null) throw new Error("Unable to create a submission attempt id.");
|
|
3489
|
+
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3490
|
+
const submitContext = {
|
|
3491
|
+
attemptId,
|
|
3492
|
+
formId: form.schema.id,
|
|
3493
|
+
formVersion: form.schema.version,
|
|
3494
|
+
locale: form.locale,
|
|
3495
|
+
submittedAt
|
|
3496
|
+
};
|
|
3497
|
+
const result = await form.submit(beforeSubmit, submitContext);
|
|
3383
3498
|
if (result.status === "invalid") {
|
|
3384
3499
|
const invalidPageIndex = pages?.findIndex(
|
|
3385
3500
|
(page) => firstInvalidFieldId === void 0 ? false : page.questionIds.includes(firstInvalidFieldId)
|
|
@@ -3392,7 +3507,7 @@ function ContextFormRenderer({
|
|
|
3392
3507
|
if (result.error instanceof FormSubmissionError) {
|
|
3393
3508
|
const fieldErrors = result.error.payload.fieldErrors ?? {};
|
|
3394
3509
|
form.setServerErrors?.(fieldErrors);
|
|
3395
|
-
const firstServerFieldId = Object.keys(fieldErrors)[0];
|
|
3510
|
+
const firstServerFieldId = form.schema.fields.find((field) => Object.hasOwn(fieldErrors, field.id))?.id ?? Object.keys(fieldErrors)[0];
|
|
3396
3511
|
if (firstServerFieldId !== void 0) {
|
|
3397
3512
|
const invalidPageIndex = pages?.findIndex((page) => page.questionIds.includes(firstServerFieldId));
|
|
3398
3513
|
if (invalidPageIndex !== void 0 && invalidPageIndex >= 0) setCurrentPageIndex(invalidPageIndex);
|
|
@@ -3417,11 +3532,11 @@ function ContextFormRenderer({
|
|
|
3417
3532
|
});
|
|
3418
3533
|
if (receiptStore !== void 0) {
|
|
3419
3534
|
const response = result.response;
|
|
3420
|
-
const submissionId = response?.submissionId ?? submissionAttempt?.attemptId;
|
|
3535
|
+
const submissionId = response?.submissionId ?? submissionAttempt?.attemptId ?? attemptId;
|
|
3421
3536
|
const storedReceipt = {
|
|
3422
3537
|
formId: form.schema.id,
|
|
3423
3538
|
formVersion: form.schema.version,
|
|
3424
|
-
submittedAt: response?.submittedAt ??
|
|
3539
|
+
submittedAt: response?.submittedAt ?? submittedAt,
|
|
3425
3540
|
...submissionId === void 0 ? {} : { submissionId }
|
|
3426
3541
|
};
|
|
3427
3542
|
try {
|
|
@@ -3440,6 +3555,7 @@ function ContextFormRenderer({
|
|
|
3440
3555
|
} catch {
|
|
3441
3556
|
}
|
|
3442
3557
|
}
|
|
3558
|
+
if (attemptStore === void 0) fallbackAttemptId.current = null;
|
|
3443
3559
|
if (autoSaveKey !== void 0 && typeof globalThis.localStorage !== "undefined") {
|
|
3444
3560
|
globalThis.localStorage.removeItem(autoSaveKey);
|
|
3445
3561
|
setDraftRestored(false);
|
|
@@ -3479,7 +3595,10 @@ function ContextFormRenderer({
|
|
|
3479
3595
|
disabled: interactionLocked || submitState === "success",
|
|
3480
3596
|
onSubmit: () => void submitValues()
|
|
3481
3597
|
};
|
|
3482
|
-
return slots.renderSubmitButton?.(submitButtonProps) ?? /* @__PURE__ */ (0, import_jsx_runtime3.
|
|
3598
|
+
return slots.renderSubmitButton?.(submitButtonProps) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { className: "fe-submit", type: "submit", disabled: submitButtonProps.disabled, children: [
|
|
3599
|
+
submitState === "submitting" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fe-spinner", "aria-hidden": "true" }) : null,
|
|
3600
|
+
submitState === "submitting" ? resolveMessage("submittingButton") : resolveMessage("submitButton", form.translate(form.schema.submitLabelKey ?? "form.submit"))
|
|
3601
|
+
] });
|
|
3483
3602
|
};
|
|
3484
3603
|
const completionMessage = form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey));
|
|
3485
3604
|
const activeCompletionData = completionData ?? {
|
|
@@ -3512,15 +3631,31 @@ function ContextFormRenderer({
|
|
|
3512
3631
|
role: submissionConfirmationRenderMode === "dialog" ? void 0 : "dialog",
|
|
3513
3632
|
children: slots.renderSubmissionConfirmation?.({
|
|
3514
3633
|
findings: confirmation?.findings ?? [],
|
|
3515
|
-
message: confirmation?.message ?? form.translate("form.confirmSensitiveData"),
|
|
3634
|
+
message: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")),
|
|
3516
3635
|
schema: form.schema,
|
|
3517
3636
|
visibleValues,
|
|
3518
3637
|
onConfirm: confirmSubmission,
|
|
3519
3638
|
onCancel: cancelSubmission
|
|
3520
3639
|
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
3521
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("
|
|
3522
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("
|
|
3523
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("
|
|
3640
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: resolveMessage("confirmSensitiveDataTitle") }),
|
|
3641
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")) }),
|
|
3642
|
+
(confirmation?.findings ?? []).length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { children: (confirmation?.findings ?? []).map((finding, index) => {
|
|
3643
|
+
const field = form.schema.fields.find((candidate) => candidate.id === finding.fieldId);
|
|
3644
|
+
const typeLabels = form.locale.toLowerCase().startsWith("ja") ? { email: "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9", phone: "\u96FB\u8A71\u756A\u53F7", url: "URL", postal_code: "\u90F5\u4FBF\u756A\u53F7" } : { email: "Email address", phone: "Phone number", url: "URL", postal_code: "Postal code" };
|
|
3645
|
+
const typeLabel = finding.typeLabel ?? typeLabels[finding.type] ?? finding.type;
|
|
3646
|
+
const value = maskSensitiveValue(finding);
|
|
3647
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
|
|
3648
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: finding.fieldTitle ?? field?.title ?? finding.fieldId }),
|
|
3649
|
+
" ",
|
|
3650
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fe-sensitive-type", children: typeLabel }),
|
|
3651
|
+
value === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "fe-sensitive-value", children: [
|
|
3652
|
+
" ",
|
|
3653
|
+
value
|
|
3654
|
+
] })
|
|
3655
|
+
] }, `${finding.fieldId}-${finding.type}-${finding.start ?? index}`);
|
|
3656
|
+
}) }),
|
|
3657
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "data-fe-confirm": "true", onClick: confirmSubmission, children: resolveMessage("confirmButton", form.translate("form.confirmSubmission")) }),
|
|
3658
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: cancelSubmission, children: resolveMessage("cancelButton", form.translate("form.cancelSubmission")) })
|
|
3524
3659
|
] })
|
|
3525
3660
|
}
|
|
3526
3661
|
);
|
|
@@ -3530,8 +3665,9 @@ function ContextFormRenderer({
|
|
|
3530
3665
|
receipt,
|
|
3531
3666
|
...receiptStore === void 0 ? {} : { onReset: () => void resetReceipt() }
|
|
3532
3667
|
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "status", children: [
|
|
3533
|
-
|
|
3534
|
-
|
|
3668
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: resolveMessage("alreadySubmittedTitle") }),
|
|
3669
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: resolveMessage("alreadySubmittedMessage", form.translate("form.alreadySubmitted")) }),
|
|
3670
|
+
receiptStore === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => void resetReceipt(), children: resolveMessage("submitButton", form.translate("form.submitAnother")) })
|
|
3535
3671
|
] }) });
|
|
3536
3672
|
}
|
|
3537
3673
|
if (form.submitStatus === "success" && isReplaceMode) {
|
|
@@ -3594,7 +3730,7 @@ function ContextFormRenderer({
|
|
|
3594
3730
|
value: form.values[field.id],
|
|
3595
3731
|
error,
|
|
3596
3732
|
setValue: (value) => form.setValue(field.id, value),
|
|
3597
|
-
translate:
|
|
3733
|
+
translate: fieldTranslate,
|
|
3598
3734
|
inputId: `${prefix}-${field.id}`,
|
|
3599
3735
|
errorId: `${prefix}-${field.id}-error`,
|
|
3600
3736
|
helpId: `${prefix}-${field.id}-help`,
|
|
@@ -3661,7 +3797,13 @@ function ContextFormRenderer({
|
|
|
3661
3797
|
] }),
|
|
3662
3798
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-status", "aria-live": "polite", children: [
|
|
3663
3799
|
form.submitStatus === "success" ? completionRegion : null,
|
|
3664
|
-
form.submitStatus === "error" && form.submitError !== null ? slots.renderSubmitError?.({ error: form.submitError, onRetry: () => void submitValues() }) ?? (form.submitError instanceof FormSubmissionError && form.submitError.payload.formError !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.
|
|
3800
|
+
form.submitStatus === "error" && form.submitError !== null ? slots.renderSubmitError?.({ error: form.submitError, onRetry: () => void submitValues() }) ?? (form.submitError instanceof FormSubmissionError && form.submitError.payload.formError !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", children: [
|
|
3801
|
+
form.submitError.payload.formError,
|
|
3802
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => void submitValues(), children: resolveMessage("retryButton") })
|
|
3803
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", children: [
|
|
3804
|
+
errorMessageKey === void 0 ? resolveMessage("serverErrorSummary") : form.translate(errorMessageKey),
|
|
3805
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => void submitValues(), children: resolveMessage("retryButton") })
|
|
3806
|
+
] })) : null
|
|
3665
3807
|
] })
|
|
3666
3808
|
]
|
|
3667
3809
|
}
|
|
@@ -3677,7 +3819,7 @@ var RENDERER_MESSAGES = {
|
|
|
3677
3819
|
"form.draftRestored": "Draft restored",
|
|
3678
3820
|
"form.submissionBlocked": "Submission blocked because sensitive data was detected.",
|
|
3679
3821
|
"form.confirmSensitiveData": "Sensitive data may be included. Confirm before submitting.",
|
|
3680
|
-
"form.confirmSubmission": "
|
|
3822
|
+
"form.confirmSubmission": "Proceed",
|
|
3681
3823
|
"form.cancelSubmission": "Cancel",
|
|
3682
3824
|
"form.yes": "Yes",
|
|
3683
3825
|
"form.no": "No",
|
|
@@ -3685,9 +3827,26 @@ var RENDERER_MESSAGES = {
|
|
|
3685
3827
|
"form.submitAnother": "Submit another response",
|
|
3686
3828
|
"validation.required": "This field is required."
|
|
3687
3829
|
};
|
|
3830
|
+
var RENDERER_MESSAGES_JA = {
|
|
3831
|
+
"form.submit": "\u9001\u4FE1\u3059\u308B",
|
|
3832
|
+
"form.back": "\u623B\u308B",
|
|
3833
|
+
"form.next": "\u6B21\u3078",
|
|
3834
|
+
"form.step": "{{current}} / {{total}}",
|
|
3835
|
+
"form.draftRestored": "\u4E0B\u66F8\u304D\u3092\u5FA9\u5143\u3057\u307E\u3057\u305F",
|
|
3836
|
+
"form.submissionBlocked": "\u500B\u4EBA\u60C5\u5831\u304C\u691C\u51FA\u3055\u308C\u305F\u305F\u3081\u9001\u4FE1\u3067\u304D\u307E\u305B\u3093\u3002",
|
|
3837
|
+
"form.confirmSensitiveData": "\u500B\u4EBA\u60C5\u5831\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u9001\u4FE1\u524D\u306B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
3838
|
+
"form.confirmSubmission": "\u3053\u306E\u307E\u307E\u9001\u4FE1",
|
|
3839
|
+
"form.cancelSubmission": "\u4FEE\u6B63\u3059\u308B",
|
|
3840
|
+
"form.yes": "\u306F\u3044",
|
|
3841
|
+
"form.no": "\u3044\u3044\u3048",
|
|
3842
|
+
"form.alreadySubmitted": "\u56DE\u7B54\u6E08\u307F\u3067\u3059",
|
|
3843
|
+
"form.submitAnother": "\u5225\u306E\u56DE\u7B54\u3092\u9001\u4FE1",
|
|
3844
|
+
"validation.required": "\u3053\u306E\u9805\u76EE\u306F\u5FC5\u9808\u3067\u3059"
|
|
3845
|
+
};
|
|
3688
3846
|
var defaultRendererTranslator = {
|
|
3689
|
-
translate(key,
|
|
3690
|
-
|
|
3847
|
+
translate(key, locale, params = {}) {
|
|
3848
|
+
const localizedMessages = locale.toLowerCase().startsWith("ja") ? RENDERER_MESSAGES_JA : RENDERER_MESSAGES;
|
|
3849
|
+
return (localizedMessages[key] ?? key).replace(
|
|
3691
3850
|
/\{\{(\w+)\}\}/g,
|
|
3692
3851
|
(token, name) => Object.hasOwn(params, name) ? String(params[name]) : token
|
|
3693
3852
|
);
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode, ComponentType, MouseEvent } from 'react';
|
|
2
|
+
import { ReactNode, ComponentType, KeyboardEvent, MouseEvent } from 'react';
|
|
3
3
|
import { QuestionType, FormField, ChoiceOption, FormPage, FormSchema, DisplayCondition, JsonValue, SchemaIssue, FormPolicy, FieldOption, TranslationReport, ValidationError, FormValues, TranslationAdapter, AsyncTranslationAdapter, PopulateTranslationOptions, FormValue, ValidationIssue, AnswerValidationResult, FieldType } from '@form-engine-ts/core';
|
|
4
4
|
import { SensitiveDataFinding } from '@form-engine-ts/privacy';
|
|
5
5
|
|
|
@@ -139,6 +139,7 @@ interface ComponentBaseProps {
|
|
|
139
139
|
type BuilderActionIconType = "moveUp" | "moveDown" | "delete" | "add" | "edit" | "settings" | "translate" | "close" | "dragHandle";
|
|
140
140
|
interface BuilderButtonProps extends ComponentBaseProps {
|
|
141
141
|
readonly onClick?: () => void;
|
|
142
|
+
readonly noWrap?: boolean;
|
|
142
143
|
readonly variant?: "primary" | "secondary" | "danger";
|
|
143
144
|
readonly children: ReactNode;
|
|
144
145
|
readonly title?: string;
|
|
@@ -165,6 +166,7 @@ interface InputComponentProps extends ComponentBaseProps {
|
|
|
165
166
|
readonly helperText?: string;
|
|
166
167
|
readonly value: string;
|
|
167
168
|
readonly onChange: (value: string) => void;
|
|
169
|
+
readonly onKeyDown?: (event: KeyboardEvent<HTMLElement>) => void;
|
|
168
170
|
readonly placeholder?: string;
|
|
169
171
|
readonly maxLength?: number;
|
|
170
172
|
}
|
|
@@ -289,6 +291,11 @@ interface BuilderLocalizationSlotProps extends BuilderSlotBaseProps {
|
|
|
289
291
|
readonly policy?: FormPolicy;
|
|
290
292
|
readonly translationAdapterAvailable?: boolean;
|
|
291
293
|
}
|
|
294
|
+
interface LocalizationSummaryContext {
|
|
295
|
+
readonly defaultLocale: string;
|
|
296
|
+
readonly supportedLocales: readonly string[];
|
|
297
|
+
readonly totalLocales: number;
|
|
298
|
+
}
|
|
292
299
|
interface BuilderTranslationActionsSlotProps extends BuilderSlotBaseProps {
|
|
293
300
|
readonly currentLocale: string;
|
|
294
301
|
readonly onAutoTranslate: () => void;
|
|
@@ -338,6 +345,26 @@ interface SubmitResponse {
|
|
|
338
345
|
readonly submissionId?: string;
|
|
339
346
|
readonly submittedAt?: string;
|
|
340
347
|
}
|
|
348
|
+
interface SubmitContext {
|
|
349
|
+
readonly attemptId: string;
|
|
350
|
+
readonly formId: string;
|
|
351
|
+
readonly formVersion: number;
|
|
352
|
+
readonly locale?: string;
|
|
353
|
+
readonly submittedAt: string;
|
|
354
|
+
}
|
|
355
|
+
interface FormRendererMessages {
|
|
356
|
+
readonly submitButton?: string;
|
|
357
|
+
readonly submittingButton?: string;
|
|
358
|
+
readonly retryButton?: string;
|
|
359
|
+
readonly requiredField?: string;
|
|
360
|
+
readonly alreadySubmittedTitle?: string;
|
|
361
|
+
readonly alreadySubmittedMessage?: string;
|
|
362
|
+
readonly serverErrorSummary?: string;
|
|
363
|
+
readonly confirmSensitiveDataTitle?: string;
|
|
364
|
+
readonly confirmSensitiveDataMessage?: string;
|
|
365
|
+
readonly confirmButton?: string;
|
|
366
|
+
readonly cancelButton?: string;
|
|
367
|
+
}
|
|
341
368
|
interface FormSubmittedAnswerItem {
|
|
342
369
|
readonly fieldId: string;
|
|
343
370
|
readonly title: string;
|
|
@@ -350,7 +377,7 @@ interface FormSubmittedAnswerItem {
|
|
|
350
377
|
interface FormCompletionSlotProps {
|
|
351
378
|
readonly message?: string;
|
|
352
379
|
readonly schema: FormSchema;
|
|
353
|
-
readonly answers: Record<string, unknown
|
|
380
|
+
readonly answers: Readonly<Record<string, unknown>>;
|
|
354
381
|
readonly submittedItems: readonly FormSubmittedAnswerItem[];
|
|
355
382
|
readonly response?: SubmitResponse;
|
|
356
383
|
readonly onReset?: () => void;
|
|
@@ -363,7 +390,7 @@ declare class FormSubmissionError extends Error {
|
|
|
363
390
|
readonly payload: FormServerErrorPayload;
|
|
364
391
|
constructor(message: string, payload?: FormServerErrorPayload);
|
|
365
392
|
}
|
|
366
|
-
type FormSubmitHandler = (answers: FormValues) => SubmitResponse | void | Promise<SubmitResponse | undefined> | Promise<void>;
|
|
393
|
+
type FormSubmitHandler = (answers: FormValues, context: SubmitContext) => SubmitResponse | void | Promise<SubmitResponse | undefined> | Promise<void>;
|
|
367
394
|
type SubmissionGuardResult = {
|
|
368
395
|
readonly status: "allow";
|
|
369
396
|
} | {
|
|
@@ -427,8 +454,7 @@ interface FormRendererSlots {
|
|
|
427
454
|
readonly renderValidationSummary?: (props: {
|
|
428
455
|
readonly issues: readonly ValidationError[];
|
|
429
456
|
}) => ReactNode;
|
|
430
|
-
|
|
431
|
-
readonly renderCompletion?: (props: {
|
|
457
|
+
readonly renderCompletion?: (props: FormCompletionSlotProps & {
|
|
432
458
|
readonly message: string;
|
|
433
459
|
}) => ReactNode;
|
|
434
460
|
readonly renderSubmittedValues?: (props: {
|
|
@@ -507,7 +533,7 @@ interface FormContextValue {
|
|
|
507
533
|
readonly restoreValues: (values: FormValues) => void;
|
|
508
534
|
readonly validatePage: (pageIndex: number) => AnswerValidationResult;
|
|
509
535
|
readonly reset: () => void;
|
|
510
|
-
readonly submit: (beforeSubmit?: BeforeSubmit,
|
|
536
|
+
readonly submit: (beforeSubmit?: BeforeSubmit, submitContext?: SubmitContext) => Promise<SubmitResult>;
|
|
511
537
|
readonly translate: (key: string, params?: Readonly<Record<string, string | number>>) => string;
|
|
512
538
|
}
|
|
513
539
|
interface FormProviderProps {
|
|
@@ -556,6 +582,9 @@ interface FormRendererPresentationProps extends SubmissionProtectionProps {
|
|
|
556
582
|
readonly hideFormOnSuccess?: boolean;
|
|
557
583
|
readonly successMessageKey?: string;
|
|
558
584
|
readonly errorMessageKey?: string;
|
|
585
|
+
readonly attemptIdFactory?: () => string;
|
|
586
|
+
readonly messages?: Partial<FormRendererMessages>;
|
|
587
|
+
readonly messageResolver?: (key: keyof FormRendererMessages, defaultText: string) => string;
|
|
559
588
|
readonly autoSaveKey?: string;
|
|
560
589
|
readonly beforeSubmit?: BeforeSubmit;
|
|
561
590
|
readonly onDraftSave?: (draft: FormValues) => void;
|
|
@@ -572,4 +601,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
|
|
|
572
601
|
type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
|
|
573
602
|
declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
|
|
574
603
|
|
|
575
|
-
export { 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 ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldState, 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 FormRendererPresentationProps, type FormRendererProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitResponse, type SubmitResult, type SubmitStatus, type UseSubmissionReceiptsResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, resolveInitialFieldType, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts };
|
|
604
|
+
export { 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 ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldState, 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 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, resolveInitialFieldType, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode, ComponentType, MouseEvent } from 'react';
|
|
2
|
+
import { ReactNode, ComponentType, KeyboardEvent, MouseEvent } from 'react';
|
|
3
3
|
import { QuestionType, FormField, ChoiceOption, FormPage, FormSchema, DisplayCondition, JsonValue, SchemaIssue, FormPolicy, FieldOption, TranslationReport, ValidationError, FormValues, TranslationAdapter, AsyncTranslationAdapter, PopulateTranslationOptions, FormValue, ValidationIssue, AnswerValidationResult, FieldType } from '@form-engine-ts/core';
|
|
4
4
|
import { SensitiveDataFinding } from '@form-engine-ts/privacy';
|
|
5
5
|
|
|
@@ -139,6 +139,7 @@ interface ComponentBaseProps {
|
|
|
139
139
|
type BuilderActionIconType = "moveUp" | "moveDown" | "delete" | "add" | "edit" | "settings" | "translate" | "close" | "dragHandle";
|
|
140
140
|
interface BuilderButtonProps extends ComponentBaseProps {
|
|
141
141
|
readonly onClick?: () => void;
|
|
142
|
+
readonly noWrap?: boolean;
|
|
142
143
|
readonly variant?: "primary" | "secondary" | "danger";
|
|
143
144
|
readonly children: ReactNode;
|
|
144
145
|
readonly title?: string;
|
|
@@ -165,6 +166,7 @@ interface InputComponentProps extends ComponentBaseProps {
|
|
|
165
166
|
readonly helperText?: string;
|
|
166
167
|
readonly value: string;
|
|
167
168
|
readonly onChange: (value: string) => void;
|
|
169
|
+
readonly onKeyDown?: (event: KeyboardEvent<HTMLElement>) => void;
|
|
168
170
|
readonly placeholder?: string;
|
|
169
171
|
readonly maxLength?: number;
|
|
170
172
|
}
|
|
@@ -289,6 +291,11 @@ interface BuilderLocalizationSlotProps extends BuilderSlotBaseProps {
|
|
|
289
291
|
readonly policy?: FormPolicy;
|
|
290
292
|
readonly translationAdapterAvailable?: boolean;
|
|
291
293
|
}
|
|
294
|
+
interface LocalizationSummaryContext {
|
|
295
|
+
readonly defaultLocale: string;
|
|
296
|
+
readonly supportedLocales: readonly string[];
|
|
297
|
+
readonly totalLocales: number;
|
|
298
|
+
}
|
|
292
299
|
interface BuilderTranslationActionsSlotProps extends BuilderSlotBaseProps {
|
|
293
300
|
readonly currentLocale: string;
|
|
294
301
|
readonly onAutoTranslate: () => void;
|
|
@@ -338,6 +345,26 @@ interface SubmitResponse {
|
|
|
338
345
|
readonly submissionId?: string;
|
|
339
346
|
readonly submittedAt?: string;
|
|
340
347
|
}
|
|
348
|
+
interface SubmitContext {
|
|
349
|
+
readonly attemptId: string;
|
|
350
|
+
readonly formId: string;
|
|
351
|
+
readonly formVersion: number;
|
|
352
|
+
readonly locale?: string;
|
|
353
|
+
readonly submittedAt: string;
|
|
354
|
+
}
|
|
355
|
+
interface FormRendererMessages {
|
|
356
|
+
readonly submitButton?: string;
|
|
357
|
+
readonly submittingButton?: string;
|
|
358
|
+
readonly retryButton?: string;
|
|
359
|
+
readonly requiredField?: string;
|
|
360
|
+
readonly alreadySubmittedTitle?: string;
|
|
361
|
+
readonly alreadySubmittedMessage?: string;
|
|
362
|
+
readonly serverErrorSummary?: string;
|
|
363
|
+
readonly confirmSensitiveDataTitle?: string;
|
|
364
|
+
readonly confirmSensitiveDataMessage?: string;
|
|
365
|
+
readonly confirmButton?: string;
|
|
366
|
+
readonly cancelButton?: string;
|
|
367
|
+
}
|
|
341
368
|
interface FormSubmittedAnswerItem {
|
|
342
369
|
readonly fieldId: string;
|
|
343
370
|
readonly title: string;
|
|
@@ -350,7 +377,7 @@ interface FormSubmittedAnswerItem {
|
|
|
350
377
|
interface FormCompletionSlotProps {
|
|
351
378
|
readonly message?: string;
|
|
352
379
|
readonly schema: FormSchema;
|
|
353
|
-
readonly answers: Record<string, unknown
|
|
380
|
+
readonly answers: Readonly<Record<string, unknown>>;
|
|
354
381
|
readonly submittedItems: readonly FormSubmittedAnswerItem[];
|
|
355
382
|
readonly response?: SubmitResponse;
|
|
356
383
|
readonly onReset?: () => void;
|
|
@@ -363,7 +390,7 @@ declare class FormSubmissionError extends Error {
|
|
|
363
390
|
readonly payload: FormServerErrorPayload;
|
|
364
391
|
constructor(message: string, payload?: FormServerErrorPayload);
|
|
365
392
|
}
|
|
366
|
-
type FormSubmitHandler = (answers: FormValues) => SubmitResponse | void | Promise<SubmitResponse | undefined> | Promise<void>;
|
|
393
|
+
type FormSubmitHandler = (answers: FormValues, context: SubmitContext) => SubmitResponse | void | Promise<SubmitResponse | undefined> | Promise<void>;
|
|
367
394
|
type SubmissionGuardResult = {
|
|
368
395
|
readonly status: "allow";
|
|
369
396
|
} | {
|
|
@@ -427,8 +454,7 @@ interface FormRendererSlots {
|
|
|
427
454
|
readonly renderValidationSummary?: (props: {
|
|
428
455
|
readonly issues: readonly ValidationError[];
|
|
429
456
|
}) => ReactNode;
|
|
430
|
-
|
|
431
|
-
readonly renderCompletion?: (props: {
|
|
457
|
+
readonly renderCompletion?: (props: FormCompletionSlotProps & {
|
|
432
458
|
readonly message: string;
|
|
433
459
|
}) => ReactNode;
|
|
434
460
|
readonly renderSubmittedValues?: (props: {
|
|
@@ -507,7 +533,7 @@ interface FormContextValue {
|
|
|
507
533
|
readonly restoreValues: (values: FormValues) => void;
|
|
508
534
|
readonly validatePage: (pageIndex: number) => AnswerValidationResult;
|
|
509
535
|
readonly reset: () => void;
|
|
510
|
-
readonly submit: (beforeSubmit?: BeforeSubmit,
|
|
536
|
+
readonly submit: (beforeSubmit?: BeforeSubmit, submitContext?: SubmitContext) => Promise<SubmitResult>;
|
|
511
537
|
readonly translate: (key: string, params?: Readonly<Record<string, string | number>>) => string;
|
|
512
538
|
}
|
|
513
539
|
interface FormProviderProps {
|
|
@@ -556,6 +582,9 @@ interface FormRendererPresentationProps extends SubmissionProtectionProps {
|
|
|
556
582
|
readonly hideFormOnSuccess?: boolean;
|
|
557
583
|
readonly successMessageKey?: string;
|
|
558
584
|
readonly errorMessageKey?: string;
|
|
585
|
+
readonly attemptIdFactory?: () => string;
|
|
586
|
+
readonly messages?: Partial<FormRendererMessages>;
|
|
587
|
+
readonly messageResolver?: (key: keyof FormRendererMessages, defaultText: string) => string;
|
|
559
588
|
readonly autoSaveKey?: string;
|
|
560
589
|
readonly beforeSubmit?: BeforeSubmit;
|
|
561
590
|
readonly onDraftSave?: (draft: FormValues) => void;
|
|
@@ -572,4 +601,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
|
|
|
572
601
|
type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
|
|
573
602
|
declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
|
|
574
603
|
|
|
575
|
-
export { 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 ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldState, 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 FormRendererPresentationProps, type FormRendererProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitResponse, type SubmitResult, type SubmitStatus, type UseSubmissionReceiptsResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, resolveInitialFieldType, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts };
|
|
604
|
+
export { 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 ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldState, 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 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, resolveInitialFieldType, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts };
|
package/dist/index.js
CHANGED
|
@@ -1214,6 +1214,11 @@ var BUILDER_DEFAULTS = {
|
|
|
1214
1214
|
"builder.translating": "Translating\u2026",
|
|
1215
1215
|
"builder.translationLocale": "Translation locale",
|
|
1216
1216
|
"builder.selectLocale": "Select a locale to edit translations.",
|
|
1217
|
+
"builder.localization.selectLocaleToAdd": "Select a locale to add",
|
|
1218
|
+
"builder.localization.noLocalesConfigured": "Translations not configured",
|
|
1219
|
+
"builder.localization.localesConfiguredSummary": "{{count}} locales configured",
|
|
1220
|
+
"builder.localization.allLocalesAdded": "\u3059\u3079\u3066\u306E\u5019\u88DC\u8A00\u8A9E\u304C\u8FFD\u52A0\u6E08\u307F\u3067\u3059",
|
|
1221
|
+
"builder.localization.maxLocalesReached": "\u767B\u9332\u53EF\u80FD\u306A\u6700\u5927\u8A00\u8A9E\u6570\uFF08{{max}}\uFF09\u306B\u9054\u3057\u307E\u3057\u305F",
|
|
1217
1222
|
"builder.translation": "{{locale}} translation",
|
|
1218
1223
|
"builder.translatedFormTitle": "Translated form title",
|
|
1219
1224
|
"builder.translatedFormDescription": "Translated form description",
|
|
@@ -2585,6 +2590,18 @@ import {
|
|
|
2585
2590
|
validatePageAnswers
|
|
2586
2591
|
} from "@form-engine-ts/core";
|
|
2587
2592
|
import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect, useMemo as useMemo2, useRef, useState as useState2 } from "react";
|
|
2593
|
+
|
|
2594
|
+
// src/types.ts
|
|
2595
|
+
var FormSubmissionError = class extends Error {
|
|
2596
|
+
payload;
|
|
2597
|
+
constructor(message, payload) {
|
|
2598
|
+
super(message);
|
|
2599
|
+
this.name = "FormSubmissionError";
|
|
2600
|
+
this.payload = payload ?? { formError: message };
|
|
2601
|
+
}
|
|
2602
|
+
};
|
|
2603
|
+
|
|
2604
|
+
// src/context.tsx
|
|
2588
2605
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
2589
2606
|
var FormContext = createContext2(null);
|
|
2590
2607
|
function issuesByField(issues) {
|
|
@@ -2592,6 +2609,18 @@ function issuesByField(issues) {
|
|
|
2592
2609
|
for (const issue of issues) result[issue.fieldId] ??= issue;
|
|
2593
2610
|
return result;
|
|
2594
2611
|
}
|
|
2612
|
+
function defaultAttemptId2() {
|
|
2613
|
+
const randomUuid = globalThis.crypto?.randomUUID;
|
|
2614
|
+
if (typeof randomUuid === "function") return randomUuid.call(globalThis.crypto);
|
|
2615
|
+
return `attempt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
2616
|
+
}
|
|
2617
|
+
function isServerErrorPayload(value) {
|
|
2618
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
2619
|
+
const record = value;
|
|
2620
|
+
const fieldErrors = record.fieldErrors;
|
|
2621
|
+
const formError = record.formError;
|
|
2622
|
+
return (Object.hasOwn(record, "fieldErrors") || Object.hasOwn(record, "formError")) && (fieldErrors === void 0 || typeof fieldErrors === "object" && fieldErrors !== null && !Array.isArray(fieldErrors) && Object.values(fieldErrors).every((message) => typeof message === "string")) && (formError === void 0 || typeof formError === "string");
|
|
2623
|
+
}
|
|
2595
2624
|
function FormProvider({
|
|
2596
2625
|
schema,
|
|
2597
2626
|
locale,
|
|
@@ -2676,7 +2705,7 @@ function FormProvider({
|
|
|
2676
2705
|
setSubmitError(null);
|
|
2677
2706
|
}, [initialValues]);
|
|
2678
2707
|
const submit = useCallback2(
|
|
2679
|
-
async (beforeSubmit,
|
|
2708
|
+
async (beforeSubmit, submitContext) => {
|
|
2680
2709
|
if (submissionInFlight.current) return { status: "cancelled" };
|
|
2681
2710
|
const validation = validateAnswers(validSchema, values);
|
|
2682
2711
|
if (!validation.valid) {
|
|
@@ -2697,13 +2726,22 @@ function FormProvider({
|
|
|
2697
2726
|
setSubmitStatus("idle");
|
|
2698
2727
|
return { status: "cancelled" };
|
|
2699
2728
|
}
|
|
2700
|
-
const
|
|
2701
|
-
|
|
2729
|
+
const context = submitContext ?? {
|
|
2730
|
+
attemptId: defaultAttemptId2(),
|
|
2731
|
+
formId: validSchema.id,
|
|
2732
|
+
formVersion: validSchema.version,
|
|
2733
|
+
locale,
|
|
2734
|
+
submittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2735
|
+
};
|
|
2736
|
+
const response = await onSubmit({ ...visibleValues }, context);
|
|
2702
2737
|
if (resetOnSuccess) setValues({ ...initialValues });
|
|
2703
2738
|
setSubmitStatus("success");
|
|
2704
2739
|
return response === void 0 ? { status: "success" } : { status: "success", response };
|
|
2705
2740
|
} catch (cause) {
|
|
2706
|
-
const error = cause
|
|
2741
|
+
const error = isServerErrorPayload(cause) ? new FormSubmissionError(
|
|
2742
|
+
cause.formError ?? (cause instanceof Error ? cause.message : "Form submission failed."),
|
|
2743
|
+
cause
|
|
2744
|
+
) : cause instanceof Error ? cause : new Error(String(cause));
|
|
2707
2745
|
setSubmitError(error);
|
|
2708
2746
|
setSubmitStatus("error");
|
|
2709
2747
|
return { status: "error", error };
|
|
@@ -2711,7 +2749,7 @@ function FormProvider({
|
|
|
2711
2749
|
submissionInFlight.current = false;
|
|
2712
2750
|
}
|
|
2713
2751
|
},
|
|
2714
|
-
[initialValues, onSubmit, resetOnSuccess, validSchema, values]
|
|
2752
|
+
[initialValues, locale, onSubmit, resetOnSuccess, validSchema, values]
|
|
2715
2753
|
);
|
|
2716
2754
|
const translate = useCallback2(
|
|
2717
2755
|
(key, params) => translator.translate(key, locale, params),
|
|
@@ -2902,18 +2940,6 @@ import {
|
|
|
2902
2940
|
useRef as useRef2,
|
|
2903
2941
|
useState as useState4
|
|
2904
2942
|
} from "react";
|
|
2905
|
-
|
|
2906
|
-
// src/types.ts
|
|
2907
|
-
var FormSubmissionError = class extends Error {
|
|
2908
|
-
payload;
|
|
2909
|
-
constructor(message, payload) {
|
|
2910
|
-
super(message);
|
|
2911
|
-
this.name = "FormSubmissionError";
|
|
2912
|
-
this.payload = payload ?? { formError: message };
|
|
2913
|
-
}
|
|
2914
|
-
};
|
|
2915
|
-
|
|
2916
|
-
// src/renderer.tsx
|
|
2917
2943
|
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2918
2944
|
function describedBy(field, error, helpId, errorId) {
|
|
2919
2945
|
const ids = [field.description === void 0 ? void 0 : helpId, error === void 0 ? void 0 : errorId].filter(
|
|
@@ -3100,7 +3126,11 @@ function DefaultField(props) {
|
|
|
3100
3126
|
fieldId: field.id,
|
|
3101
3127
|
current: typeof value === "string" ? value.length : 0,
|
|
3102
3128
|
max: field.maxLength
|
|
3103
|
-
}) :
|
|
3129
|
+
}) ?? /* @__PURE__ */ jsxs2("div", { className: "fe-character-count", "aria-live": "polite", children: [
|
|
3130
|
+
typeof value === "string" ? value.length : 0,
|
|
3131
|
+
" / ",
|
|
3132
|
+
field.maxLength
|
|
3133
|
+
] }) : null,
|
|
3104
3134
|
/* @__PURE__ */ jsx3(FieldMessage, { props })
|
|
3105
3135
|
] });
|
|
3106
3136
|
}
|
|
@@ -3152,11 +3182,58 @@ function parseDraft(serialized) {
|
|
|
3152
3182
|
return null;
|
|
3153
3183
|
}
|
|
3154
3184
|
}
|
|
3185
|
+
var DEFAULT_RENDERER_MESSAGES = {
|
|
3186
|
+
en: {
|
|
3187
|
+
submitButton: "Submit",
|
|
3188
|
+
submittingButton: "Submitting...",
|
|
3189
|
+
retryButton: "Retry",
|
|
3190
|
+
requiredField: "This field is required.",
|
|
3191
|
+
alreadySubmittedTitle: "Already Submitted",
|
|
3192
|
+
alreadySubmittedMessage: "Already submitted.",
|
|
3193
|
+
serverErrorSummary: "Submission failed. Please check your answers and try again.",
|
|
3194
|
+
confirmSensitiveDataTitle: "Sensitive data may be included",
|
|
3195
|
+
confirmSensitiveDataMessage: "The following answers may contain personal information. Continue submitting?",
|
|
3196
|
+
confirmButton: "Proceed",
|
|
3197
|
+
cancelButton: "Cancel"
|
|
3198
|
+
},
|
|
3199
|
+
ja: {
|
|
3200
|
+
submitButton: "\u9001\u4FE1\u3059\u308B",
|
|
3201
|
+
submittingButton: "\u9001\u4FE1\u4E2D...",
|
|
3202
|
+
retryButton: "\u518D\u9001\u4FE1\u3059\u308B",
|
|
3203
|
+
requiredField: "\u3053\u306E\u9805\u76EE\u306F\u5FC5\u9808\u3067\u3059",
|
|
3204
|
+
alreadySubmittedTitle: "\u56DE\u7B54\u6E08\u307F\u3067\u3059",
|
|
3205
|
+
alreadySubmittedMessage: "\u3053\u306E\u30A2\u30F3\u30B1\u30FC\u30C8\u306B\u306F\u3059\u3067\u306B\u56DE\u7B54\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
3206
|
+
serverErrorSummary: "\u9001\u4FE1\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002\u5185\u5BB9\u3092\u3054\u78BA\u8A8D\u306E\u4E0A\u3001\u518D\u5EA6\u304A\u8A66\u3057\u304F\u3060\u3055\u3044\u3002",
|
|
3207
|
+
confirmSensitiveDataTitle: "\u500B\u4EBA\u60C5\u5831\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059",
|
|
3208
|
+
confirmSensitiveDataMessage: "\u4EE5\u4E0B\u306E\u9805\u76EE\u306B\u500B\u4EBA\u60C5\u5831\u3068\u307F\u3089\u308C\u308B\u8A18\u8FF0\u304C\u3042\u308A\u307E\u3059\u3002\u3053\u306E\u307E\u307E\u9001\u4FE1\u3057\u3066\u3082\u3088\u308D\u3057\u3044\u3067\u3059\u304B\uFF1F",
|
|
3209
|
+
confirmButton: "\u3053\u306E\u307E\u307E\u9001\u4FE1",
|
|
3210
|
+
cancelButton: "\u4FEE\u6B63\u3059\u308B"
|
|
3211
|
+
}
|
|
3212
|
+
};
|
|
3213
|
+
function createRendererAttemptId() {
|
|
3214
|
+
const randomUuid = globalThis.crypto?.randomUUID;
|
|
3215
|
+
if (typeof randomUuid === "function") return randomUuid.call(globalThis.crypto);
|
|
3216
|
+
return `attempt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
3217
|
+
}
|
|
3218
|
+
function maskSensitiveValue(finding) {
|
|
3219
|
+
if (finding.maskedText !== void 0) return finding.maskedText;
|
|
3220
|
+
const value = finding.matchedText;
|
|
3221
|
+
if (value === void 0) return void 0;
|
|
3222
|
+
if (finding.type === "email") {
|
|
3223
|
+
const separator = value.indexOf("@");
|
|
3224
|
+
if (separator > 0) return `${value.slice(0, Math.min(2, separator))}***${value.slice(separator)}`;
|
|
3225
|
+
}
|
|
3226
|
+
if (finding.type === "phone" || finding.type === "postal_code") return "***";
|
|
3227
|
+
return value.length <= 2 ? "***" : `${value.slice(0, 2)}***`;
|
|
3228
|
+
}
|
|
3155
3229
|
function ContextFormRenderer({
|
|
3156
3230
|
components = {},
|
|
3157
3231
|
className = "",
|
|
3158
3232
|
successMessageKey,
|
|
3159
3233
|
errorMessageKey,
|
|
3234
|
+
attemptIdFactory,
|
|
3235
|
+
messages = {},
|
|
3236
|
+
messageResolver,
|
|
3160
3237
|
autoSaveKey,
|
|
3161
3238
|
beforeSubmit,
|
|
3162
3239
|
onDraftSave,
|
|
@@ -3185,6 +3262,7 @@ function ContextFormRenderer({
|
|
|
3185
3262
|
const [completionData, setCompletionData] = useState4(null);
|
|
3186
3263
|
const [receiptLoaded, setReceiptLoaded] = useState4(receiptStore === void 0);
|
|
3187
3264
|
const rendererSubmissionInFlight = useRef2(false);
|
|
3265
|
+
const fallbackAttemptId = useRef2(null);
|
|
3188
3266
|
const completionRef = useRef2(null);
|
|
3189
3267
|
const confirmationRef = useRef2(null);
|
|
3190
3268
|
const pages = form.schema.pages;
|
|
@@ -3199,6 +3277,18 @@ function ContextFormRenderer({
|
|
|
3199
3277
|
const submitState = confirmation === null && !guardsPending ? form.submitStatus : "confirming";
|
|
3200
3278
|
const interactionLocked = submitState === "confirming" || submitState === "submitting";
|
|
3201
3279
|
const isReplaceMode = successRenderMode === "replace" || hideFormOnSuccess;
|
|
3280
|
+
const resolveMessage = useCallback3(
|
|
3281
|
+
(key, fallback) => {
|
|
3282
|
+
const defaultText = fallback ?? DEFAULT_RENDERER_MESSAGES[form.locale.toLowerCase().startsWith("ja") ? "ja" : "en"][key] ?? key;
|
|
3283
|
+
const configured = messages[key];
|
|
3284
|
+
return messageResolver?.(key, configured ?? defaultText) ?? configured ?? defaultText;
|
|
3285
|
+
},
|
|
3286
|
+
[form.locale, messageResolver, messages]
|
|
3287
|
+
);
|
|
3288
|
+
const fieldTranslate = useCallback3(
|
|
3289
|
+
(key, params) => key === "validation.required" && (messages.requiredField !== void 0 || messageResolver !== void 0) ? resolveMessage("requiredField") : form.translate(key, params),
|
|
3290
|
+
[form.translate, messageResolver, messages.requiredField, resolveMessage]
|
|
3291
|
+
);
|
|
3202
3292
|
const focusSubmitButton = useCallback3(() => {
|
|
3203
3293
|
const button = formRef.current?.querySelector(".fe-submit, button[type='submit'], button");
|
|
3204
3294
|
button?.focus();
|
|
@@ -3238,6 +3328,7 @@ function ContextFormRenderer({
|
|
|
3238
3328
|
);
|
|
3239
3329
|
const control = fieldContainer?.querySelector("input, select, textarea");
|
|
3240
3330
|
if (control !== void 0 && control !== null) {
|
|
3331
|
+
control.scrollIntoView?.({ behavior: "smooth", block: "center" });
|
|
3241
3332
|
control.focus();
|
|
3242
3333
|
setFocusFieldId(null);
|
|
3243
3334
|
}
|
|
@@ -3256,6 +3347,23 @@ function ContextFormRenderer({
|
|
|
3256
3347
|
event.preventDefault();
|
|
3257
3348
|
setConfirmation(null);
|
|
3258
3349
|
globalThis.setTimeout(focusSubmitButton, 0);
|
|
3350
|
+
return;
|
|
3351
|
+
}
|
|
3352
|
+
if (event.key !== "Tab") return;
|
|
3353
|
+
const focusable = [
|
|
3354
|
+
...confirmationRef.current?.querySelectorAll(
|
|
3355
|
+
"button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])"
|
|
3356
|
+
) ?? []
|
|
3357
|
+
].filter((element) => !element.hasAttribute("disabled"));
|
|
3358
|
+
if (focusable.length === 0) return;
|
|
3359
|
+
const first = focusable[0];
|
|
3360
|
+
const last = focusable[focusable.length - 1];
|
|
3361
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
3362
|
+
event.preventDefault();
|
|
3363
|
+
last?.focus();
|
|
3364
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
3365
|
+
event.preventDefault();
|
|
3366
|
+
first?.focus();
|
|
3259
3367
|
}
|
|
3260
3368
|
};
|
|
3261
3369
|
globalThis.addEventListener("keydown", onKeyDown);
|
|
@@ -3357,17 +3465,24 @@ function ContextFormRenderer({
|
|
|
3357
3465
|
rendererSubmissionInFlight.current = true;
|
|
3358
3466
|
try {
|
|
3359
3467
|
let submissionAttempt;
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3468
|
+
let attemptId = fallbackAttemptId.current;
|
|
3469
|
+
if (attemptStore !== void 0) {
|
|
3470
|
+
submissionAttempt = await attemptStore.getOrCreate(form.schema.id, form.schema.version, attemptIdFactory);
|
|
3471
|
+
attemptId = submissionAttempt.attemptId;
|
|
3472
|
+
} else if (attemptId === null) {
|
|
3473
|
+
attemptId = attemptIdFactory?.() ?? createRendererAttemptId();
|
|
3474
|
+
fallbackAttemptId.current = attemptId;
|
|
3475
|
+
}
|
|
3476
|
+
if (attemptId === null) throw new Error("Unable to create a submission attempt id.");
|
|
3477
|
+
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3478
|
+
const submitContext = {
|
|
3479
|
+
attemptId,
|
|
3480
|
+
formId: form.schema.id,
|
|
3481
|
+
formVersion: form.schema.version,
|
|
3482
|
+
locale: form.locale,
|
|
3483
|
+
submittedAt
|
|
3484
|
+
};
|
|
3485
|
+
const result = await form.submit(beforeSubmit, submitContext);
|
|
3371
3486
|
if (result.status === "invalid") {
|
|
3372
3487
|
const invalidPageIndex = pages?.findIndex(
|
|
3373
3488
|
(page) => firstInvalidFieldId === void 0 ? false : page.questionIds.includes(firstInvalidFieldId)
|
|
@@ -3380,7 +3495,7 @@ function ContextFormRenderer({
|
|
|
3380
3495
|
if (result.error instanceof FormSubmissionError) {
|
|
3381
3496
|
const fieldErrors = result.error.payload.fieldErrors ?? {};
|
|
3382
3497
|
form.setServerErrors?.(fieldErrors);
|
|
3383
|
-
const firstServerFieldId = Object.keys(fieldErrors)[0];
|
|
3498
|
+
const firstServerFieldId = form.schema.fields.find((field) => Object.hasOwn(fieldErrors, field.id))?.id ?? Object.keys(fieldErrors)[0];
|
|
3384
3499
|
if (firstServerFieldId !== void 0) {
|
|
3385
3500
|
const invalidPageIndex = pages?.findIndex((page) => page.questionIds.includes(firstServerFieldId));
|
|
3386
3501
|
if (invalidPageIndex !== void 0 && invalidPageIndex >= 0) setCurrentPageIndex(invalidPageIndex);
|
|
@@ -3405,11 +3520,11 @@ function ContextFormRenderer({
|
|
|
3405
3520
|
});
|
|
3406
3521
|
if (receiptStore !== void 0) {
|
|
3407
3522
|
const response = result.response;
|
|
3408
|
-
const submissionId = response?.submissionId ?? submissionAttempt?.attemptId;
|
|
3523
|
+
const submissionId = response?.submissionId ?? submissionAttempt?.attemptId ?? attemptId;
|
|
3409
3524
|
const storedReceipt = {
|
|
3410
3525
|
formId: form.schema.id,
|
|
3411
3526
|
formVersion: form.schema.version,
|
|
3412
|
-
submittedAt: response?.submittedAt ??
|
|
3527
|
+
submittedAt: response?.submittedAt ?? submittedAt,
|
|
3413
3528
|
...submissionId === void 0 ? {} : { submissionId }
|
|
3414
3529
|
};
|
|
3415
3530
|
try {
|
|
@@ -3428,6 +3543,7 @@ function ContextFormRenderer({
|
|
|
3428
3543
|
} catch {
|
|
3429
3544
|
}
|
|
3430
3545
|
}
|
|
3546
|
+
if (attemptStore === void 0) fallbackAttemptId.current = null;
|
|
3431
3547
|
if (autoSaveKey !== void 0 && typeof globalThis.localStorage !== "undefined") {
|
|
3432
3548
|
globalThis.localStorage.removeItem(autoSaveKey);
|
|
3433
3549
|
setDraftRestored(false);
|
|
@@ -3467,7 +3583,10 @@ function ContextFormRenderer({
|
|
|
3467
3583
|
disabled: interactionLocked || submitState === "success",
|
|
3468
3584
|
onSubmit: () => void submitValues()
|
|
3469
3585
|
};
|
|
3470
|
-
return slots.renderSubmitButton?.(submitButtonProps) ?? /* @__PURE__ */
|
|
3586
|
+
return slots.renderSubmitButton?.(submitButtonProps) ?? /* @__PURE__ */ jsxs2("button", { className: "fe-submit", type: "submit", disabled: submitButtonProps.disabled, children: [
|
|
3587
|
+
submitState === "submitting" ? /* @__PURE__ */ jsx3("span", { className: "fe-spinner", "aria-hidden": "true" }) : null,
|
|
3588
|
+
submitState === "submitting" ? resolveMessage("submittingButton") : resolveMessage("submitButton", form.translate(form.schema.submitLabelKey ?? "form.submit"))
|
|
3589
|
+
] });
|
|
3471
3590
|
};
|
|
3472
3591
|
const completionMessage = form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey));
|
|
3473
3592
|
const activeCompletionData = completionData ?? {
|
|
@@ -3500,15 +3619,31 @@ function ContextFormRenderer({
|
|
|
3500
3619
|
role: submissionConfirmationRenderMode === "dialog" ? void 0 : "dialog",
|
|
3501
3620
|
children: slots.renderSubmissionConfirmation?.({
|
|
3502
3621
|
findings: confirmation?.findings ?? [],
|
|
3503
|
-
message: confirmation?.message ?? form.translate("form.confirmSensitiveData"),
|
|
3622
|
+
message: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")),
|
|
3504
3623
|
schema: form.schema,
|
|
3505
3624
|
visibleValues,
|
|
3506
3625
|
onConfirm: confirmSubmission,
|
|
3507
3626
|
onCancel: cancelSubmission
|
|
3508
3627
|
}) ?? /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
3509
|
-
/* @__PURE__ */ jsx3("
|
|
3510
|
-
/* @__PURE__ */ jsx3("
|
|
3511
|
-
/* @__PURE__ */ jsx3("
|
|
3628
|
+
/* @__PURE__ */ jsx3("h2", { children: resolveMessage("confirmSensitiveDataTitle") }),
|
|
3629
|
+
/* @__PURE__ */ jsx3("p", { children: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")) }),
|
|
3630
|
+
(confirmation?.findings ?? []).length === 0 ? null : /* @__PURE__ */ jsx3("ul", { children: (confirmation?.findings ?? []).map((finding, index) => {
|
|
3631
|
+
const field = form.schema.fields.find((candidate) => candidate.id === finding.fieldId);
|
|
3632
|
+
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" };
|
|
3633
|
+
const typeLabel = finding.typeLabel ?? typeLabels[finding.type] ?? finding.type;
|
|
3634
|
+
const value = maskSensitiveValue(finding);
|
|
3635
|
+
return /* @__PURE__ */ jsxs2("li", { children: [
|
|
3636
|
+
/* @__PURE__ */ jsx3("span", { children: finding.fieldTitle ?? field?.title ?? finding.fieldId }),
|
|
3637
|
+
" ",
|
|
3638
|
+
/* @__PURE__ */ jsx3("span", { className: "fe-sensitive-type", children: typeLabel }),
|
|
3639
|
+
value === void 0 ? null : /* @__PURE__ */ jsxs2("span", { className: "fe-sensitive-value", children: [
|
|
3640
|
+
" ",
|
|
3641
|
+
value
|
|
3642
|
+
] })
|
|
3643
|
+
] }, `${finding.fieldId}-${finding.type}-${finding.start ?? index}`);
|
|
3644
|
+
}) }),
|
|
3645
|
+
/* @__PURE__ */ jsx3("button", { type: "button", "data-fe-confirm": "true", onClick: confirmSubmission, children: resolveMessage("confirmButton", form.translate("form.confirmSubmission")) }),
|
|
3646
|
+
/* @__PURE__ */ jsx3("button", { type: "button", onClick: cancelSubmission, children: resolveMessage("cancelButton", form.translate("form.cancelSubmission")) })
|
|
3512
3647
|
] })
|
|
3513
3648
|
}
|
|
3514
3649
|
);
|
|
@@ -3518,8 +3653,9 @@ function ContextFormRenderer({
|
|
|
3518
3653
|
receipt,
|
|
3519
3654
|
...receiptStore === void 0 ? {} : { onReset: () => void resetReceipt() }
|
|
3520
3655
|
}) ?? /* @__PURE__ */ jsxs2("div", { role: "status", children: [
|
|
3521
|
-
|
|
3522
|
-
|
|
3656
|
+
/* @__PURE__ */ jsx3("h2", { children: resolveMessage("alreadySubmittedTitle") }),
|
|
3657
|
+
/* @__PURE__ */ jsx3("p", { children: resolveMessage("alreadySubmittedMessage", form.translate("form.alreadySubmitted")) }),
|
|
3658
|
+
receiptStore === void 0 ? null : /* @__PURE__ */ jsx3("button", { type: "button", onClick: () => void resetReceipt(), children: resolveMessage("submitButton", form.translate("form.submitAnother")) })
|
|
3523
3659
|
] }) });
|
|
3524
3660
|
}
|
|
3525
3661
|
if (form.submitStatus === "success" && isReplaceMode) {
|
|
@@ -3582,7 +3718,7 @@ function ContextFormRenderer({
|
|
|
3582
3718
|
value: form.values[field.id],
|
|
3583
3719
|
error,
|
|
3584
3720
|
setValue: (value) => form.setValue(field.id, value),
|
|
3585
|
-
translate:
|
|
3721
|
+
translate: fieldTranslate,
|
|
3586
3722
|
inputId: `${prefix}-${field.id}`,
|
|
3587
3723
|
errorId: `${prefix}-${field.id}-error`,
|
|
3588
3724
|
helpId: `${prefix}-${field.id}-help`,
|
|
@@ -3649,7 +3785,13 @@ function ContextFormRenderer({
|
|
|
3649
3785
|
] }),
|
|
3650
3786
|
/* @__PURE__ */ jsxs2("div", { className: "fe-status", "aria-live": "polite", children: [
|
|
3651
3787
|
form.submitStatus === "success" ? completionRegion : null,
|
|
3652
|
-
form.submitStatus === "error" && form.submitError !== null ? slots.renderSubmitError?.({ error: form.submitError, onRetry: () => void submitValues() }) ?? (form.submitError instanceof FormSubmissionError && form.submitError.payload.formError !== void 0 ? /* @__PURE__ */
|
|
3788
|
+
form.submitStatus === "error" && form.submitError !== null ? slots.renderSubmitError?.({ error: form.submitError, onRetry: () => void submitValues() }) ?? (form.submitError instanceof FormSubmissionError && form.submitError.payload.formError !== void 0 ? /* @__PURE__ */ jsxs2("div", { role: "alert", children: [
|
|
3789
|
+
form.submitError.payload.formError,
|
|
3790
|
+
/* @__PURE__ */ jsx3("button", { type: "button", onClick: () => void submitValues(), children: resolveMessage("retryButton") })
|
|
3791
|
+
] }) : /* @__PURE__ */ jsxs2("div", { role: "alert", children: [
|
|
3792
|
+
errorMessageKey === void 0 ? resolveMessage("serverErrorSummary") : form.translate(errorMessageKey),
|
|
3793
|
+
/* @__PURE__ */ jsx3("button", { type: "button", onClick: () => void submitValues(), children: resolveMessage("retryButton") })
|
|
3794
|
+
] })) : null
|
|
3653
3795
|
] })
|
|
3654
3796
|
]
|
|
3655
3797
|
}
|
|
@@ -3665,7 +3807,7 @@ var RENDERER_MESSAGES = {
|
|
|
3665
3807
|
"form.draftRestored": "Draft restored",
|
|
3666
3808
|
"form.submissionBlocked": "Submission blocked because sensitive data was detected.",
|
|
3667
3809
|
"form.confirmSensitiveData": "Sensitive data may be included. Confirm before submitting.",
|
|
3668
|
-
"form.confirmSubmission": "
|
|
3810
|
+
"form.confirmSubmission": "Proceed",
|
|
3669
3811
|
"form.cancelSubmission": "Cancel",
|
|
3670
3812
|
"form.yes": "Yes",
|
|
3671
3813
|
"form.no": "No",
|
|
@@ -3673,9 +3815,26 @@ var RENDERER_MESSAGES = {
|
|
|
3673
3815
|
"form.submitAnother": "Submit another response",
|
|
3674
3816
|
"validation.required": "This field is required."
|
|
3675
3817
|
};
|
|
3818
|
+
var RENDERER_MESSAGES_JA = {
|
|
3819
|
+
"form.submit": "\u9001\u4FE1\u3059\u308B",
|
|
3820
|
+
"form.back": "\u623B\u308B",
|
|
3821
|
+
"form.next": "\u6B21\u3078",
|
|
3822
|
+
"form.step": "{{current}} / {{total}}",
|
|
3823
|
+
"form.draftRestored": "\u4E0B\u66F8\u304D\u3092\u5FA9\u5143\u3057\u307E\u3057\u305F",
|
|
3824
|
+
"form.submissionBlocked": "\u500B\u4EBA\u60C5\u5831\u304C\u691C\u51FA\u3055\u308C\u305F\u305F\u3081\u9001\u4FE1\u3067\u304D\u307E\u305B\u3093\u3002",
|
|
3825
|
+
"form.confirmSensitiveData": "\u500B\u4EBA\u60C5\u5831\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u9001\u4FE1\u524D\u306B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
3826
|
+
"form.confirmSubmission": "\u3053\u306E\u307E\u307E\u9001\u4FE1",
|
|
3827
|
+
"form.cancelSubmission": "\u4FEE\u6B63\u3059\u308B",
|
|
3828
|
+
"form.yes": "\u306F\u3044",
|
|
3829
|
+
"form.no": "\u3044\u3044\u3048",
|
|
3830
|
+
"form.alreadySubmitted": "\u56DE\u7B54\u6E08\u307F\u3067\u3059",
|
|
3831
|
+
"form.submitAnother": "\u5225\u306E\u56DE\u7B54\u3092\u9001\u4FE1",
|
|
3832
|
+
"validation.required": "\u3053\u306E\u9805\u76EE\u306F\u5FC5\u9808\u3067\u3059"
|
|
3833
|
+
};
|
|
3676
3834
|
var defaultRendererTranslator = {
|
|
3677
|
-
translate(key,
|
|
3678
|
-
|
|
3835
|
+
translate(key, locale, params = {}) {
|
|
3836
|
+
const localizedMessages = locale.toLowerCase().startsWith("ja") ? RENDERER_MESSAGES_JA : RENDERER_MESSAGES;
|
|
3837
|
+
return (localizedMessages[key] ?? key).replace(
|
|
3679
3838
|
/\{\{(\w+)\}\}/g,
|
|
3680
3839
|
(token, name) => Object.hasOwn(params, name) ? String(params[name]) : token
|
|
3681
3840
|
);
|
package/dist/styles.css
CHANGED
|
@@ -93,6 +93,27 @@
|
|
|
93
93
|
font-size: 0.875rem;
|
|
94
94
|
font-weight: 600;
|
|
95
95
|
}
|
|
96
|
+
.fe-character-count {
|
|
97
|
+
color: #667085;
|
|
98
|
+
font-size: 0.8rem;
|
|
99
|
+
text-align: right;
|
|
100
|
+
}
|
|
101
|
+
.fe-spinner {
|
|
102
|
+
display: inline-block;
|
|
103
|
+
width: 0.85em;
|
|
104
|
+
height: 0.85em;
|
|
105
|
+
margin-right: 0.4em;
|
|
106
|
+
border: 0.12em solid currentColor;
|
|
107
|
+
border-right-color: transparent;
|
|
108
|
+
border-radius: 50%;
|
|
109
|
+
animation: fe-spin 0.8s linear infinite;
|
|
110
|
+
vertical-align: -0.1em;
|
|
111
|
+
}
|
|
112
|
+
@keyframes fe-spin {
|
|
113
|
+
to {
|
|
114
|
+
transform: rotate(360deg);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
96
117
|
.fe-check-label {
|
|
97
118
|
align-items: flex-start;
|
|
98
119
|
display: flex;
|
|
@@ -298,3 +319,16 @@
|
|
|
298
319
|
border-radius: 0.5rem;
|
|
299
320
|
box-shadow: 0 1rem 3rem rgb(0 0 0 / 25%);
|
|
300
321
|
}
|
|
322
|
+
.fe-sensitive-type {
|
|
323
|
+
border-radius: 999px;
|
|
324
|
+
background: #f2f4f7;
|
|
325
|
+
padding: 0.15rem 0.45rem;
|
|
326
|
+
font-size: 0.8rem;
|
|
327
|
+
}
|
|
328
|
+
.fe-sensitive-value {
|
|
329
|
+
font-family:
|
|
330
|
+
ui-monospace,
|
|
331
|
+
SFMono-Regular,
|
|
332
|
+
Menlo,
|
|
333
|
+
monospace;
|
|
334
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@form-engine-ts/react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
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": "
|
|
46
|
-
"@form-engine-ts/privacy": "
|
|
45
|
+
"@form-engine-ts/core": "3.0.0",
|
|
46
|
+
"@form-engine-ts/privacy": "3.0.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": ">=18.2 <20",
|