@form-engine-ts/react 2.9.6 → 3.1.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 +32 -13
- package/dist/index.cjs +288 -50
- package/dist/index.d.cts +84 -8
- package/dist/index.d.ts +84 -8
- package/dist/index.js +287 -50
- package/dist/styles.css +34 -0
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
BUILDER_TRANSLATION_ALIASES: () => BUILDER_TRANSLATION_ALIASES,
|
|
24
|
+
BUILDER_TRANSLATION_KEYS: () => BUILDER_TRANSLATION_KEYS,
|
|
23
25
|
FormBuilder: () => FormBuilder,
|
|
24
26
|
FormProvider: () => FormProvider,
|
|
25
27
|
FormRenderer: () => FormRenderer,
|
|
@@ -774,6 +776,33 @@ function useFormBuilder({
|
|
|
774
776
|
};
|
|
775
777
|
}
|
|
776
778
|
|
|
779
|
+
// src/i18n.ts
|
|
780
|
+
var BUILDER_TRANSLATION_KEYS = {
|
|
781
|
+
ADD_FIELD: "builder.actions.addField",
|
|
782
|
+
SELECT_FIELD_TYPE: "builder.fields.selectType",
|
|
783
|
+
SELECT_LOCALE_TO_ADD: "builder.localization.selectLocaleToAdd",
|
|
784
|
+
FIELD_TYPE_TEXT: "builder.fields.typeText",
|
|
785
|
+
FIELD_TYPE_TEXTAREA: "builder.fields.typeTextarea",
|
|
786
|
+
FIELD_TYPE_NUMBER: "builder.fields.typeNumber",
|
|
787
|
+
FIELD_TYPE_RATING: "builder.fields.typeRating",
|
|
788
|
+
FIELD_TYPE_RADIO: "builder.fields.typeRadio",
|
|
789
|
+
FIELD_TYPE_CHECKBOX: "builder.fields.typeCheckbox",
|
|
790
|
+
FIELD_TYPE_SELECT: "builder.fields.typeSelect",
|
|
791
|
+
FIELD_TYPE_MULTI_SELECT: "builder.fields.typeMultiSelect"
|
|
792
|
+
};
|
|
793
|
+
var BUILDER_TRANSLATION_ALIASES = {
|
|
794
|
+
"builder.actions.addField": "builder.addQuestion",
|
|
795
|
+
"builder.localization.selectLocaleToAdd": "builder.selectLocaleToAdd",
|
|
796
|
+
"builder.fields.typeText": "builder.fieldType.text",
|
|
797
|
+
"builder.fields.typeTextarea": "builder.fieldType.textarea",
|
|
798
|
+
"builder.fields.typeNumber": "builder.fieldType.number",
|
|
799
|
+
"builder.fields.typeRating": "builder.fieldType.rating",
|
|
800
|
+
"builder.fields.typeRadio": "builder.fieldType.radio",
|
|
801
|
+
"builder.fields.typeCheckbox": "builder.fieldType.checkbox",
|
|
802
|
+
"builder.fields.typeSelect": "builder.fieldType.select",
|
|
803
|
+
"builder.fields.typeMultiSelect": "builder.fieldType.multi-select"
|
|
804
|
+
};
|
|
805
|
+
|
|
777
806
|
// src/builder.tsx
|
|
778
807
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
779
808
|
function DefaultButton({
|
|
@@ -844,6 +873,26 @@ function defaultIconFor(actionType) {
|
|
|
844
873
|
return "\u6587";
|
|
845
874
|
}
|
|
846
875
|
}
|
|
876
|
+
function defaultFieldTypeIcon(type) {
|
|
877
|
+
switch (type) {
|
|
878
|
+
case "text":
|
|
879
|
+
return "T";
|
|
880
|
+
case "textarea":
|
|
881
|
+
return "\u2261";
|
|
882
|
+
case "number":
|
|
883
|
+
return "#";
|
|
884
|
+
case "rating":
|
|
885
|
+
return "\u2605";
|
|
886
|
+
case "select":
|
|
887
|
+
return "\u25BE";
|
|
888
|
+
case "multi-select":
|
|
889
|
+
return "\u2611";
|
|
890
|
+
case "checkbox":
|
|
891
|
+
return "\u25A1";
|
|
892
|
+
case "radio":
|
|
893
|
+
return "\u25C9";
|
|
894
|
+
}
|
|
895
|
+
}
|
|
847
896
|
function DefaultTextInput({
|
|
848
897
|
id,
|
|
849
898
|
className,
|
|
@@ -956,8 +1005,15 @@ function DefaultSelect({
|
|
|
956
1005
|
helperText,
|
|
957
1006
|
value,
|
|
958
1007
|
onChange,
|
|
959
|
-
|
|
1008
|
+
onKeyDown,
|
|
1009
|
+
options,
|
|
1010
|
+
renderOption,
|
|
1011
|
+
renderValue
|
|
960
1012
|
}) {
|
|
1013
|
+
const normalizedOptions = options.map(
|
|
1014
|
+
(option) => typeof option === "string" ? { value: option, label: option } : option
|
|
1015
|
+
);
|
|
1016
|
+
const selectedOption = normalizedOptions.find((option) => option.value === value);
|
|
961
1017
|
const labelElement = label === void 0 ? null : id === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: id, children: label });
|
|
962
1018
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
963
1019
|
labelElement,
|
|
@@ -975,9 +1031,11 @@ function DefaultSelect({
|
|
|
975
1031
|
"aria-invalid": error === true ? true : void 0,
|
|
976
1032
|
value,
|
|
977
1033
|
onChange: (event) => onChange(event.currentTarget.value),
|
|
978
|
-
|
|
1034
|
+
onKeyDown,
|
|
1035
|
+
children: normalizedOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, disabled: option.disabled, children: renderOption === void 0 ? option.label : renderOption(option) }, option.value))
|
|
979
1036
|
}
|
|
980
1037
|
),
|
|
1038
|
+
renderValue === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("output", { children: renderValue(selectedOption) }),
|
|
981
1039
|
helperText === void 0 || helperText.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { id: ariaDescribedBy, children: helperText })
|
|
982
1040
|
] });
|
|
983
1041
|
}
|
|
@@ -1065,7 +1123,8 @@ var DEFAULT_COMPONENTS = {
|
|
|
1065
1123
|
Section: DefaultSection,
|
|
1066
1124
|
Fieldset: DefaultFieldset,
|
|
1067
1125
|
ErrorMessage: DefaultErrorMessage,
|
|
1068
|
-
renderIcon: defaultIconFor
|
|
1126
|
+
renderIcon: defaultIconFor,
|
|
1127
|
+
renderFieldTypeIcon: defaultFieldTypeIcon
|
|
1069
1128
|
};
|
|
1070
1129
|
var BuilderPrimitiveContext = (0, import_react2.createContext)({
|
|
1071
1130
|
components: DEFAULT_COMPONENTS,
|
|
@@ -1245,6 +1304,11 @@ var BUILDER_DEFAULTS = {
|
|
|
1245
1304
|
"builder.translating": "Translating\u2026",
|
|
1246
1305
|
"builder.translationLocale": "Translation locale",
|
|
1247
1306
|
"builder.selectLocale": "Select a locale to edit translations.",
|
|
1307
|
+
"builder.localization.selectLocaleToAdd": "Select a locale to add",
|
|
1308
|
+
"builder.localization.noLocalesConfigured": "Translations not configured",
|
|
1309
|
+
"builder.localization.localesConfiguredSummary": "{{count}} locales configured",
|
|
1310
|
+
"builder.localization.allLocalesAdded": "\u3059\u3079\u3066\u306E\u5019\u88DC\u8A00\u8A9E\u304C\u8FFD\u52A0\u6E08\u307F\u3067\u3059",
|
|
1311
|
+
"builder.localization.maxLocalesReached": "\u767B\u9332\u53EF\u80FD\u306A\u6700\u5927\u8A00\u8A9E\u6570\uFF08{{max}}\uFF09\u306B\u9054\u3057\u307E\u3057\u305F",
|
|
1248
1312
|
"builder.translation": "{{locale}} translation",
|
|
1249
1313
|
"builder.translatedFormTitle": "Translated form title",
|
|
1250
1314
|
"builder.translatedFormDescription": "Translated form description",
|
|
@@ -1261,6 +1325,16 @@ var BUILDER_DEFAULTS = {
|
|
|
1261
1325
|
"builder.actions.close": "Close",
|
|
1262
1326
|
"builder.actions.dragHandle": "Reorder",
|
|
1263
1327
|
"builder.translationUnavailable": "Provide an async translation adapter to enable automatic translation.",
|
|
1328
|
+
"builder.fields.selectType": "Select field type",
|
|
1329
|
+
"builder.fields.typeText": "Text",
|
|
1330
|
+
"builder.fields.typeTextarea": "Textarea",
|
|
1331
|
+
"builder.fields.typeNumber": "Number",
|
|
1332
|
+
"builder.fields.typeRating": "Rating",
|
|
1333
|
+
"builder.fields.typeSelect": "Select",
|
|
1334
|
+
"builder.fields.typeMultiSelect": "Multi-select",
|
|
1335
|
+
"builder.fields.typeCheckbox": "Checkbox",
|
|
1336
|
+
"builder.fields.typeRadio": "Radio",
|
|
1337
|
+
"builder.actions.addField": "Add question",
|
|
1264
1338
|
"builder.fieldType.text": "Text",
|
|
1265
1339
|
"builder.fieldType.textarea": "Textarea",
|
|
1266
1340
|
"builder.fieldType.number": "Number",
|
|
@@ -1297,7 +1371,7 @@ function OrderedBuilderSections({
|
|
|
1297
1371
|
return groups.map((group, index) => ({ group, index, rank: ranks.get(sectionGroupName(group) ?? "questions") ?? order.length })).sort((left, right) => left.rank - right.rank || left.index - right.index).map(({ group }) => group);
|
|
1298
1372
|
}
|
|
1299
1373
|
function fieldTypeKey(type) {
|
|
1300
|
-
return `builder.fieldType.${type}`;
|
|
1374
|
+
return import_core2.DEFAULT_FIELD_TYPE_DEFINITIONS.find((definition) => definition.type === type)?.labelKey ?? `builder.fieldType.${type}`;
|
|
1301
1375
|
}
|
|
1302
1376
|
function operatorKey(operator) {
|
|
1303
1377
|
return `builder.operator.${operator}`;
|
|
@@ -1458,7 +1532,8 @@ function FormBuilder({
|
|
|
1458
1532
|
const resolvedComponents = { ...DEFAULT_COMPONENTS, ...componentOverrides };
|
|
1459
1533
|
const components = {
|
|
1460
1534
|
...GUARDED_COMPONENTS,
|
|
1461
|
-
renderIcon: resolvedComponents.renderIcon
|
|
1535
|
+
renderIcon: resolvedComponents.renderIcon,
|
|
1536
|
+
renderFieldTypeIcon: resolvedComponents.renderFieldTypeIcon
|
|
1462
1537
|
};
|
|
1463
1538
|
const defaultStylesDisabled = disableDefaultStyles || unstyled;
|
|
1464
1539
|
const builderClass = (value) => defaultStylesDisabled ? void 0 : value;
|
|
@@ -1489,6 +1564,12 @@ function FormBuilder({
|
|
|
1489
1564
|
if (typeof value === "string" || typeof value === "number") translatorParams[name] = value;
|
|
1490
1565
|
}
|
|
1491
1566
|
const translated = translator?.translate(key, locale, translatorParams);
|
|
1567
|
+
if (translated !== void 0 && translated !== key) return translated;
|
|
1568
|
+
const alias = BUILDER_TRANSLATION_ALIASES[key];
|
|
1569
|
+
if (alias !== void 0) {
|
|
1570
|
+
const aliased = translator?.translate(alias, locale, translatorParams);
|
|
1571
|
+
if (aliased !== void 0 && aliased !== alias) return aliased;
|
|
1572
|
+
}
|
|
1492
1573
|
return translated === void 0 ? interpolate(BUILDER_DEFAULTS[key] ?? key, params) : translated;
|
|
1493
1574
|
};
|
|
1494
1575
|
const pagesEnabled = features?.pages ?? true;
|
|
@@ -2224,6 +2305,7 @@ function FormBuilder({
|
|
|
2224
2305
|
index,
|
|
2225
2306
|
currentLocale: editingLocale,
|
|
2226
2307
|
translate,
|
|
2308
|
+
...slots === void 0 ? {} : { slots },
|
|
2227
2309
|
...policy === void 0 ? {} : { policy },
|
|
2228
2310
|
...features === void 0 ? {} : { features },
|
|
2229
2311
|
readOnly,
|
|
@@ -2595,7 +2677,7 @@ function FormBuilder({
|
|
|
2595
2677
|
action: "addField",
|
|
2596
2678
|
disabled: initialFieldType === null || maxFieldsReached,
|
|
2597
2679
|
onClick: addField,
|
|
2598
|
-
children: translate(
|
|
2680
|
+
children: translate(BUILDER_TRANSLATION_KEYS.ADD_FIELD)
|
|
2599
2681
|
}
|
|
2600
2682
|
) })
|
|
2601
2683
|
] }) })
|
|
@@ -2608,6 +2690,18 @@ function FormBuilder({
|
|
|
2608
2690
|
// src/context.tsx
|
|
2609
2691
|
var import_core3 = require("@form-engine-ts/core");
|
|
2610
2692
|
var import_react3 = require("react");
|
|
2693
|
+
|
|
2694
|
+
// src/types.ts
|
|
2695
|
+
var FormSubmissionError = class extends Error {
|
|
2696
|
+
payload;
|
|
2697
|
+
constructor(message, payload) {
|
|
2698
|
+
super(message);
|
|
2699
|
+
this.name = "FormSubmissionError";
|
|
2700
|
+
this.payload = payload ?? { formError: message };
|
|
2701
|
+
}
|
|
2702
|
+
};
|
|
2703
|
+
|
|
2704
|
+
// src/context.tsx
|
|
2611
2705
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
2612
2706
|
var FormContext = (0, import_react3.createContext)(null);
|
|
2613
2707
|
function issuesByField(issues) {
|
|
@@ -2615,6 +2709,18 @@ function issuesByField(issues) {
|
|
|
2615
2709
|
for (const issue of issues) result[issue.fieldId] ??= issue;
|
|
2616
2710
|
return result;
|
|
2617
2711
|
}
|
|
2712
|
+
function defaultAttemptId2() {
|
|
2713
|
+
const randomUuid = globalThis.crypto?.randomUUID;
|
|
2714
|
+
if (typeof randomUuid === "function") return randomUuid.call(globalThis.crypto);
|
|
2715
|
+
return `attempt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
2716
|
+
}
|
|
2717
|
+
function isServerErrorPayload(value) {
|
|
2718
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
2719
|
+
const record = value;
|
|
2720
|
+
const fieldErrors = record.fieldErrors;
|
|
2721
|
+
const formError = record.formError;
|
|
2722
|
+
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");
|
|
2723
|
+
}
|
|
2618
2724
|
function FormProvider({
|
|
2619
2725
|
schema,
|
|
2620
2726
|
locale,
|
|
@@ -2699,7 +2805,7 @@ function FormProvider({
|
|
|
2699
2805
|
setSubmitError(null);
|
|
2700
2806
|
}, [initialValues]);
|
|
2701
2807
|
const submit = (0, import_react3.useCallback)(
|
|
2702
|
-
async (beforeSubmit,
|
|
2808
|
+
async (beforeSubmit, submitContext) => {
|
|
2703
2809
|
if (submissionInFlight.current) return { status: "cancelled" };
|
|
2704
2810
|
const validation = (0, import_core3.validateAnswers)(validSchema, values);
|
|
2705
2811
|
if (!validation.valid) {
|
|
@@ -2720,13 +2826,22 @@ function FormProvider({
|
|
|
2720
2826
|
setSubmitStatus("idle");
|
|
2721
2827
|
return { status: "cancelled" };
|
|
2722
2828
|
}
|
|
2723
|
-
const
|
|
2724
|
-
|
|
2829
|
+
const context = submitContext ?? {
|
|
2830
|
+
attemptId: defaultAttemptId2(),
|
|
2831
|
+
formId: validSchema.id,
|
|
2832
|
+
formVersion: validSchema.version,
|
|
2833
|
+
locale,
|
|
2834
|
+
submittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2835
|
+
};
|
|
2836
|
+
const response = await onSubmit({ ...visibleValues }, context);
|
|
2725
2837
|
if (resetOnSuccess) setValues({ ...initialValues });
|
|
2726
2838
|
setSubmitStatus("success");
|
|
2727
2839
|
return response === void 0 ? { status: "success" } : { status: "success", response };
|
|
2728
2840
|
} catch (cause) {
|
|
2729
|
-
const error = cause
|
|
2841
|
+
const error = isServerErrorPayload(cause) ? new FormSubmissionError(
|
|
2842
|
+
cause.formError ?? (cause instanceof Error ? cause.message : "Form submission failed."),
|
|
2843
|
+
cause
|
|
2844
|
+
) : cause instanceof Error ? cause : new Error(String(cause));
|
|
2730
2845
|
setSubmitError(error);
|
|
2731
2846
|
setSubmitStatus("error");
|
|
2732
2847
|
return { status: "error", error };
|
|
@@ -2734,7 +2849,7 @@ function FormProvider({
|
|
|
2734
2849
|
submissionInFlight.current = false;
|
|
2735
2850
|
}
|
|
2736
2851
|
},
|
|
2737
|
-
[initialValues, onSubmit, resetOnSuccess, validSchema, values]
|
|
2852
|
+
[initialValues, locale, onSubmit, resetOnSuccess, validSchema, values]
|
|
2738
2853
|
);
|
|
2739
2854
|
const translate = (0, import_react3.useCallback)(
|
|
2740
2855
|
(key, params) => translator.translate(key, locale, params),
|
|
@@ -2914,18 +3029,6 @@ function useSubmissionReceipts(store, queries) {
|
|
|
2914
3029
|
// src/renderer.tsx
|
|
2915
3030
|
var import_core4 = require("@form-engine-ts/core");
|
|
2916
3031
|
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
3032
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
2930
3033
|
function describedBy(field, error, helpId, errorId) {
|
|
2931
3034
|
const ids = [field.description === void 0 ? void 0 : helpId, error === void 0 ? void 0 : errorId].filter(
|
|
@@ -3112,7 +3215,11 @@ function DefaultField(props) {
|
|
|
3112
3215
|
fieldId: field.id,
|
|
3113
3216
|
current: typeof value === "string" ? value.length : 0,
|
|
3114
3217
|
max: field.maxLength
|
|
3115
|
-
}) :
|
|
3218
|
+
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-character-count", "aria-live": "polite", children: [
|
|
3219
|
+
typeof value === "string" ? value.length : 0,
|
|
3220
|
+
" / ",
|
|
3221
|
+
field.maxLength
|
|
3222
|
+
] }) : null,
|
|
3116
3223
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FieldMessage, { props })
|
|
3117
3224
|
] });
|
|
3118
3225
|
}
|
|
@@ -3164,11 +3271,58 @@ function parseDraft(serialized) {
|
|
|
3164
3271
|
return null;
|
|
3165
3272
|
}
|
|
3166
3273
|
}
|
|
3274
|
+
var DEFAULT_RENDERER_MESSAGES = {
|
|
3275
|
+
en: {
|
|
3276
|
+
submitButton: "Submit",
|
|
3277
|
+
submittingButton: "Submitting...",
|
|
3278
|
+
retryButton: "Retry",
|
|
3279
|
+
requiredField: "This field is required.",
|
|
3280
|
+
alreadySubmittedTitle: "Already Submitted",
|
|
3281
|
+
alreadySubmittedMessage: "Already submitted.",
|
|
3282
|
+
serverErrorSummary: "Submission failed. Please check your answers and try again.",
|
|
3283
|
+
confirmSensitiveDataTitle: "Sensitive data may be included",
|
|
3284
|
+
confirmSensitiveDataMessage: "The following answers may contain personal information. Continue submitting?",
|
|
3285
|
+
confirmButton: "Proceed",
|
|
3286
|
+
cancelButton: "Cancel"
|
|
3287
|
+
},
|
|
3288
|
+
ja: {
|
|
3289
|
+
submitButton: "\u9001\u4FE1\u3059\u308B",
|
|
3290
|
+
submittingButton: "\u9001\u4FE1\u4E2D...",
|
|
3291
|
+
retryButton: "\u518D\u9001\u4FE1\u3059\u308B",
|
|
3292
|
+
requiredField: "\u3053\u306E\u9805\u76EE\u306F\u5FC5\u9808\u3067\u3059",
|
|
3293
|
+
alreadySubmittedTitle: "\u56DE\u7B54\u6E08\u307F\u3067\u3059",
|
|
3294
|
+
alreadySubmittedMessage: "\u3053\u306E\u30A2\u30F3\u30B1\u30FC\u30C8\u306B\u306F\u3059\u3067\u306B\u56DE\u7B54\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
3295
|
+
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",
|
|
3296
|
+
confirmSensitiveDataTitle: "\u500B\u4EBA\u60C5\u5831\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059",
|
|
3297
|
+
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",
|
|
3298
|
+
confirmButton: "\u3053\u306E\u307E\u307E\u9001\u4FE1",
|
|
3299
|
+
cancelButton: "\u4FEE\u6B63\u3059\u308B"
|
|
3300
|
+
}
|
|
3301
|
+
};
|
|
3302
|
+
function createRendererAttemptId() {
|
|
3303
|
+
const randomUuid = globalThis.crypto?.randomUUID;
|
|
3304
|
+
if (typeof randomUuid === "function") return randomUuid.call(globalThis.crypto);
|
|
3305
|
+
return `attempt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
3306
|
+
}
|
|
3307
|
+
function maskSensitiveValue(finding) {
|
|
3308
|
+
if (finding.maskedText !== void 0) return finding.maskedText;
|
|
3309
|
+
const value = finding.matchedText;
|
|
3310
|
+
if (value === void 0) return void 0;
|
|
3311
|
+
if (finding.type === "email") {
|
|
3312
|
+
const separator = value.indexOf("@");
|
|
3313
|
+
if (separator > 0) return `${value.slice(0, Math.min(2, separator))}***${value.slice(separator)}`;
|
|
3314
|
+
}
|
|
3315
|
+
if (finding.type === "phone" || finding.type === "postal_code") return "***";
|
|
3316
|
+
return value.length <= 2 ? "***" : `${value.slice(0, 2)}***`;
|
|
3317
|
+
}
|
|
3167
3318
|
function ContextFormRenderer({
|
|
3168
3319
|
components = {},
|
|
3169
3320
|
className = "",
|
|
3170
3321
|
successMessageKey,
|
|
3171
3322
|
errorMessageKey,
|
|
3323
|
+
attemptIdFactory,
|
|
3324
|
+
messages = {},
|
|
3325
|
+
messageResolver,
|
|
3172
3326
|
autoSaveKey,
|
|
3173
3327
|
beforeSubmit,
|
|
3174
3328
|
onDraftSave,
|
|
@@ -3197,6 +3351,7 @@ function ContextFormRenderer({
|
|
|
3197
3351
|
const [completionData, setCompletionData] = (0, import_react5.useState)(null);
|
|
3198
3352
|
const [receiptLoaded, setReceiptLoaded] = (0, import_react5.useState)(receiptStore === void 0);
|
|
3199
3353
|
const rendererSubmissionInFlight = (0, import_react5.useRef)(false);
|
|
3354
|
+
const fallbackAttemptId = (0, import_react5.useRef)(null);
|
|
3200
3355
|
const completionRef = (0, import_react5.useRef)(null);
|
|
3201
3356
|
const confirmationRef = (0, import_react5.useRef)(null);
|
|
3202
3357
|
const pages = form.schema.pages;
|
|
@@ -3211,6 +3366,18 @@ function ContextFormRenderer({
|
|
|
3211
3366
|
const submitState = confirmation === null && !guardsPending ? form.submitStatus : "confirming";
|
|
3212
3367
|
const interactionLocked = submitState === "confirming" || submitState === "submitting";
|
|
3213
3368
|
const isReplaceMode = successRenderMode === "replace" || hideFormOnSuccess;
|
|
3369
|
+
const resolveMessage = (0, import_react5.useCallback)(
|
|
3370
|
+
(key, fallback) => {
|
|
3371
|
+
const defaultText = fallback ?? DEFAULT_RENDERER_MESSAGES[form.locale.toLowerCase().startsWith("ja") ? "ja" : "en"][key] ?? key;
|
|
3372
|
+
const configured = messages[key];
|
|
3373
|
+
return messageResolver?.(key, configured ?? defaultText) ?? configured ?? defaultText;
|
|
3374
|
+
},
|
|
3375
|
+
[form.locale, messageResolver, messages]
|
|
3376
|
+
);
|
|
3377
|
+
const fieldTranslate = (0, import_react5.useCallback)(
|
|
3378
|
+
(key, params) => key === "validation.required" && (messages.requiredField !== void 0 || messageResolver !== void 0) ? resolveMessage("requiredField") : form.translate(key, params),
|
|
3379
|
+
[form.translate, messageResolver, messages.requiredField, resolveMessage]
|
|
3380
|
+
);
|
|
3214
3381
|
const focusSubmitButton = (0, import_react5.useCallback)(() => {
|
|
3215
3382
|
const button = formRef.current?.querySelector(".fe-submit, button[type='submit'], button");
|
|
3216
3383
|
button?.focus();
|
|
@@ -3250,6 +3417,7 @@ function ContextFormRenderer({
|
|
|
3250
3417
|
);
|
|
3251
3418
|
const control = fieldContainer?.querySelector("input, select, textarea");
|
|
3252
3419
|
if (control !== void 0 && control !== null) {
|
|
3420
|
+
control.scrollIntoView?.({ behavior: "smooth", block: "center" });
|
|
3253
3421
|
control.focus();
|
|
3254
3422
|
setFocusFieldId(null);
|
|
3255
3423
|
}
|
|
@@ -3268,6 +3436,23 @@ function ContextFormRenderer({
|
|
|
3268
3436
|
event.preventDefault();
|
|
3269
3437
|
setConfirmation(null);
|
|
3270
3438
|
globalThis.setTimeout(focusSubmitButton, 0);
|
|
3439
|
+
return;
|
|
3440
|
+
}
|
|
3441
|
+
if (event.key !== "Tab") return;
|
|
3442
|
+
const focusable = [
|
|
3443
|
+
...confirmationRef.current?.querySelectorAll(
|
|
3444
|
+
"button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])"
|
|
3445
|
+
) ?? []
|
|
3446
|
+
].filter((element) => !element.hasAttribute("disabled"));
|
|
3447
|
+
if (focusable.length === 0) return;
|
|
3448
|
+
const first = focusable[0];
|
|
3449
|
+
const last = focusable[focusable.length - 1];
|
|
3450
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
3451
|
+
event.preventDefault();
|
|
3452
|
+
last?.focus();
|
|
3453
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
3454
|
+
event.preventDefault();
|
|
3455
|
+
first?.focus();
|
|
3271
3456
|
}
|
|
3272
3457
|
};
|
|
3273
3458
|
globalThis.addEventListener("keydown", onKeyDown);
|
|
@@ -3369,17 +3554,24 @@ function ContextFormRenderer({
|
|
|
3369
3554
|
rendererSubmissionInFlight.current = true;
|
|
3370
3555
|
try {
|
|
3371
3556
|
let submissionAttempt;
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3557
|
+
let attemptId = fallbackAttemptId.current;
|
|
3558
|
+
if (attemptStore !== void 0) {
|
|
3559
|
+
submissionAttempt = await attemptStore.getOrCreate(form.schema.id, form.schema.version, attemptIdFactory);
|
|
3560
|
+
attemptId = submissionAttempt.attemptId;
|
|
3561
|
+
} else if (attemptId === null) {
|
|
3562
|
+
attemptId = attemptIdFactory?.() ?? createRendererAttemptId();
|
|
3563
|
+
fallbackAttemptId.current = attemptId;
|
|
3564
|
+
}
|
|
3565
|
+
if (attemptId === null) throw new Error("Unable to create a submission attempt id.");
|
|
3566
|
+
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3567
|
+
const submitContext = {
|
|
3568
|
+
attemptId,
|
|
3569
|
+
formId: form.schema.id,
|
|
3570
|
+
formVersion: form.schema.version,
|
|
3571
|
+
locale: form.locale,
|
|
3572
|
+
submittedAt
|
|
3573
|
+
};
|
|
3574
|
+
const result = await form.submit(beforeSubmit, submitContext);
|
|
3383
3575
|
if (result.status === "invalid") {
|
|
3384
3576
|
const invalidPageIndex = pages?.findIndex(
|
|
3385
3577
|
(page) => firstInvalidFieldId === void 0 ? false : page.questionIds.includes(firstInvalidFieldId)
|
|
@@ -3392,7 +3584,7 @@ function ContextFormRenderer({
|
|
|
3392
3584
|
if (result.error instanceof FormSubmissionError) {
|
|
3393
3585
|
const fieldErrors = result.error.payload.fieldErrors ?? {};
|
|
3394
3586
|
form.setServerErrors?.(fieldErrors);
|
|
3395
|
-
const firstServerFieldId = Object.keys(fieldErrors)[0];
|
|
3587
|
+
const firstServerFieldId = form.schema.fields.find((field) => Object.hasOwn(fieldErrors, field.id))?.id ?? Object.keys(fieldErrors)[0];
|
|
3396
3588
|
if (firstServerFieldId !== void 0) {
|
|
3397
3589
|
const invalidPageIndex = pages?.findIndex((page) => page.questionIds.includes(firstServerFieldId));
|
|
3398
3590
|
if (invalidPageIndex !== void 0 && invalidPageIndex >= 0) setCurrentPageIndex(invalidPageIndex);
|
|
@@ -3417,11 +3609,11 @@ function ContextFormRenderer({
|
|
|
3417
3609
|
});
|
|
3418
3610
|
if (receiptStore !== void 0) {
|
|
3419
3611
|
const response = result.response;
|
|
3420
|
-
const submissionId = response?.submissionId ?? submissionAttempt?.attemptId;
|
|
3612
|
+
const submissionId = response?.submissionId ?? submissionAttempt?.attemptId ?? attemptId;
|
|
3421
3613
|
const storedReceipt = {
|
|
3422
3614
|
formId: form.schema.id,
|
|
3423
3615
|
formVersion: form.schema.version,
|
|
3424
|
-
submittedAt: response?.submittedAt ??
|
|
3616
|
+
submittedAt: response?.submittedAt ?? submittedAt,
|
|
3425
3617
|
...submissionId === void 0 ? {} : { submissionId }
|
|
3426
3618
|
};
|
|
3427
3619
|
try {
|
|
@@ -3440,6 +3632,7 @@ function ContextFormRenderer({
|
|
|
3440
3632
|
} catch {
|
|
3441
3633
|
}
|
|
3442
3634
|
}
|
|
3635
|
+
if (attemptStore === void 0) fallbackAttemptId.current = null;
|
|
3443
3636
|
if (autoSaveKey !== void 0 && typeof globalThis.localStorage !== "undefined") {
|
|
3444
3637
|
globalThis.localStorage.removeItem(autoSaveKey);
|
|
3445
3638
|
setDraftRestored(false);
|
|
@@ -3479,7 +3672,10 @@ function ContextFormRenderer({
|
|
|
3479
3672
|
disabled: interactionLocked || submitState === "success",
|
|
3480
3673
|
onSubmit: () => void submitValues()
|
|
3481
3674
|
};
|
|
3482
|
-
return slots.renderSubmitButton?.(submitButtonProps) ?? /* @__PURE__ */ (0, import_jsx_runtime3.
|
|
3675
|
+
return slots.renderSubmitButton?.(submitButtonProps) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { className: "fe-submit", type: "submit", disabled: submitButtonProps.disabled, children: [
|
|
3676
|
+
submitState === "submitting" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fe-spinner", "aria-hidden": "true" }) : null,
|
|
3677
|
+
submitState === "submitting" ? resolveMessage("submittingButton") : resolveMessage("submitButton", form.translate(form.schema.submitLabelKey ?? "form.submit"))
|
|
3678
|
+
] });
|
|
3483
3679
|
};
|
|
3484
3680
|
const completionMessage = form.schema.completionMessage ?? (successMessageKey === void 0 ? "Submitted." : form.translate(successMessageKey));
|
|
3485
3681
|
const activeCompletionData = completionData ?? {
|
|
@@ -3512,15 +3708,31 @@ function ContextFormRenderer({
|
|
|
3512
3708
|
role: submissionConfirmationRenderMode === "dialog" ? void 0 : "dialog",
|
|
3513
3709
|
children: slots.renderSubmissionConfirmation?.({
|
|
3514
3710
|
findings: confirmation?.findings ?? [],
|
|
3515
|
-
message: confirmation?.message ?? form.translate("form.confirmSensitiveData"),
|
|
3711
|
+
message: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")),
|
|
3516
3712
|
schema: form.schema,
|
|
3517
3713
|
visibleValues,
|
|
3518
3714
|
onConfirm: confirmSubmission,
|
|
3519
3715
|
onCancel: cancelSubmission
|
|
3520
3716
|
}) ?? /* @__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)("
|
|
3717
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: resolveMessage("confirmSensitiveDataTitle") }),
|
|
3718
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: confirmation?.message ?? resolveMessage("confirmSensitiveDataMessage", form.translate("form.confirmSensitiveData")) }),
|
|
3719
|
+
(confirmation?.findings ?? []).length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { children: (confirmation?.findings ?? []).map((finding, index) => {
|
|
3720
|
+
const field = form.schema.fields.find((candidate) => candidate.id === finding.fieldId);
|
|
3721
|
+
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" };
|
|
3722
|
+
const typeLabel = finding.typeLabel ?? typeLabels[finding.type] ?? finding.type;
|
|
3723
|
+
const value = maskSensitiveValue(finding);
|
|
3724
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
|
|
3725
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: finding.fieldTitle ?? field?.title ?? finding.fieldId }),
|
|
3726
|
+
" ",
|
|
3727
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fe-sensitive-type", children: typeLabel }),
|
|
3728
|
+
value === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "fe-sensitive-value", children: [
|
|
3729
|
+
" ",
|
|
3730
|
+
value
|
|
3731
|
+
] })
|
|
3732
|
+
] }, `${finding.fieldId}-${finding.type}-${finding.start ?? index}`);
|
|
3733
|
+
}) }),
|
|
3734
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "data-fe-confirm": "true", onClick: confirmSubmission, children: resolveMessage("confirmButton", form.translate("form.confirmSubmission")) }),
|
|
3735
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: cancelSubmission, children: resolveMessage("cancelButton", form.translate("form.cancelSubmission")) })
|
|
3524
3736
|
] })
|
|
3525
3737
|
}
|
|
3526
3738
|
);
|
|
@@ -3530,8 +3742,9 @@ function ContextFormRenderer({
|
|
|
3530
3742
|
receipt,
|
|
3531
3743
|
...receiptStore === void 0 ? {} : { onReset: () => void resetReceipt() }
|
|
3532
3744
|
}) ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "status", children: [
|
|
3533
|
-
|
|
3534
|
-
|
|
3745
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: resolveMessage("alreadySubmittedTitle") }),
|
|
3746
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: resolveMessage("alreadySubmittedMessage", form.translate("form.alreadySubmitted")) }),
|
|
3747
|
+
receiptStore === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => void resetReceipt(), children: resolveMessage("submitButton", form.translate("form.submitAnother")) })
|
|
3535
3748
|
] }) });
|
|
3536
3749
|
}
|
|
3537
3750
|
if (form.submitStatus === "success" && isReplaceMode) {
|
|
@@ -3594,7 +3807,7 @@ function ContextFormRenderer({
|
|
|
3594
3807
|
value: form.values[field.id],
|
|
3595
3808
|
error,
|
|
3596
3809
|
setValue: (value) => form.setValue(field.id, value),
|
|
3597
|
-
translate:
|
|
3810
|
+
translate: fieldTranslate,
|
|
3598
3811
|
inputId: `${prefix}-${field.id}`,
|
|
3599
3812
|
errorId: `${prefix}-${field.id}-error`,
|
|
3600
3813
|
helpId: `${prefix}-${field.id}-help`,
|
|
@@ -3661,7 +3874,13 @@ function ContextFormRenderer({
|
|
|
3661
3874
|
] }),
|
|
3662
3875
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fe-status", "aria-live": "polite", children: [
|
|
3663
3876
|
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.
|
|
3877
|
+
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: [
|
|
3878
|
+
form.submitError.payload.formError,
|
|
3879
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => void submitValues(), children: resolveMessage("retryButton") })
|
|
3880
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "alert", children: [
|
|
3881
|
+
errorMessageKey === void 0 ? resolveMessage("serverErrorSummary") : form.translate(errorMessageKey),
|
|
3882
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => void submitValues(), children: resolveMessage("retryButton") })
|
|
3883
|
+
] })) : null
|
|
3665
3884
|
] })
|
|
3666
3885
|
]
|
|
3667
3886
|
}
|
|
@@ -3677,7 +3896,7 @@ var RENDERER_MESSAGES = {
|
|
|
3677
3896
|
"form.draftRestored": "Draft restored",
|
|
3678
3897
|
"form.submissionBlocked": "Submission blocked because sensitive data was detected.",
|
|
3679
3898
|
"form.confirmSensitiveData": "Sensitive data may be included. Confirm before submitting.",
|
|
3680
|
-
"form.confirmSubmission": "
|
|
3899
|
+
"form.confirmSubmission": "Proceed",
|
|
3681
3900
|
"form.cancelSubmission": "Cancel",
|
|
3682
3901
|
"form.yes": "Yes",
|
|
3683
3902
|
"form.no": "No",
|
|
@@ -3685,9 +3904,26 @@ var RENDERER_MESSAGES = {
|
|
|
3685
3904
|
"form.submitAnother": "Submit another response",
|
|
3686
3905
|
"validation.required": "This field is required."
|
|
3687
3906
|
};
|
|
3907
|
+
var RENDERER_MESSAGES_JA = {
|
|
3908
|
+
"form.submit": "\u9001\u4FE1\u3059\u308B",
|
|
3909
|
+
"form.back": "\u623B\u308B",
|
|
3910
|
+
"form.next": "\u6B21\u3078",
|
|
3911
|
+
"form.step": "{{current}} / {{total}}",
|
|
3912
|
+
"form.draftRestored": "\u4E0B\u66F8\u304D\u3092\u5FA9\u5143\u3057\u307E\u3057\u305F",
|
|
3913
|
+
"form.submissionBlocked": "\u500B\u4EBA\u60C5\u5831\u304C\u691C\u51FA\u3055\u308C\u305F\u305F\u3081\u9001\u4FE1\u3067\u304D\u307E\u305B\u3093\u3002",
|
|
3914
|
+
"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",
|
|
3915
|
+
"form.confirmSubmission": "\u3053\u306E\u307E\u307E\u9001\u4FE1",
|
|
3916
|
+
"form.cancelSubmission": "\u4FEE\u6B63\u3059\u308B",
|
|
3917
|
+
"form.yes": "\u306F\u3044",
|
|
3918
|
+
"form.no": "\u3044\u3044\u3048",
|
|
3919
|
+
"form.alreadySubmitted": "\u56DE\u7B54\u6E08\u307F\u3067\u3059",
|
|
3920
|
+
"form.submitAnother": "\u5225\u306E\u56DE\u7B54\u3092\u9001\u4FE1",
|
|
3921
|
+
"validation.required": "\u3053\u306E\u9805\u76EE\u306F\u5FC5\u9808\u3067\u3059"
|
|
3922
|
+
};
|
|
3688
3923
|
var defaultRendererTranslator = {
|
|
3689
|
-
translate(key,
|
|
3690
|
-
|
|
3924
|
+
translate(key, locale, params = {}) {
|
|
3925
|
+
const localizedMessages = locale.toLowerCase().startsWith("ja") ? RENDERER_MESSAGES_JA : RENDERER_MESSAGES;
|
|
3926
|
+
return (localizedMessages[key] ?? key).replace(
|
|
3691
3927
|
/\{\{(\w+)\}\}/g,
|
|
3692
3928
|
(token, name) => Object.hasOwn(params, name) ? String(params[name]) : token
|
|
3693
3929
|
);
|
|
@@ -3719,6 +3955,8 @@ function FormRenderer(props) {
|
|
|
3719
3955
|
}
|
|
3720
3956
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3721
3957
|
0 && (module.exports = {
|
|
3958
|
+
BUILDER_TRANSLATION_ALIASES,
|
|
3959
|
+
BUILDER_TRANSLATION_KEYS,
|
|
3722
3960
|
FormBuilder,
|
|
3723
3961
|
FormProvider,
|
|
3724
3962
|
FormRenderer,
|