@form-engine-ts/react 4.6.0 → 4.8.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 +6 -2
- package/dist/index.cjs +74 -38
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +73 -37
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -80,8 +80,12 @@ single-slot or batch translation with stale/manual status handling. The MUI pack
|
|
|
80
80
|
schema-driven pre-submit confirmation controls in the builder.
|
|
81
81
|
|
|
82
82
|
`useTranslationWorkspace` validates added locales against `policy.allowedLocales` and `policy.maxLocales` and returns
|
|
83
|
-
a structured `{ success, error }` result from `addLocale`.
|
|
84
|
-
|
|
83
|
+
a structured `{ success, error }` result from `addLocale`. Built-in BCP 47, duplicate, and policy checks always run
|
|
84
|
+
before the optional `validateLocale` callback, which receives the canonical locale and a plain context object containing
|
|
85
|
+
the canonical default locale, current locales, and policy.
|
|
86
|
+
Locale input is normalized to its canonical BCP 47 form before duplicate, policy, custom-validation, and schema updates;
|
|
87
|
+
underscore-separated values such as `EN_us` are accepted as compatibility input.
|
|
88
|
+
Use `isAddLocaleAllowed` to disable locale controls before submission. Removing a locale also clears its
|
|
85
89
|
localized values and metadata; the default locale remains protected.
|
|
86
90
|
|
|
87
91
|
## Headless builder and renderer lifecycle
|
package/dist/index.cjs
CHANGED
|
@@ -39,7 +39,8 @@ __export(index_exports, {
|
|
|
39
39
|
useForm: () => useForm,
|
|
40
40
|
useFormBuilder: () => useFormBuilder,
|
|
41
41
|
useSubmissionReceipts: () => useSubmissionReceipts,
|
|
42
|
-
useTranslationWorkspace: () => useTranslationWorkspace
|
|
42
|
+
useTranslationWorkspace: () => useTranslationWorkspace,
|
|
43
|
+
validateLocalePipeline: () => validateLocalePipeline
|
|
43
44
|
});
|
|
44
45
|
module.exports = __toCommonJS(index_exports);
|
|
45
46
|
|
|
@@ -3281,37 +3282,83 @@ function asAsyncAdapter(adapter) {
|
|
|
3281
3282
|
)
|
|
3282
3283
|
};
|
|
3283
3284
|
}
|
|
3284
|
-
|
|
3285
|
-
|
|
3285
|
+
var validateLocalePipeline = (locale, schema, policy, customValidator) => {
|
|
3286
|
+
const canonicalLocale = (0, import_core4.normalizeLocale)(locale);
|
|
3287
|
+
if (canonicalLocale === null) {
|
|
3286
3288
|
return {
|
|
3287
3289
|
valid: false,
|
|
3288
|
-
error: {
|
|
3290
|
+
error: {
|
|
3291
|
+
type: "invalid_locale_format",
|
|
3292
|
+
message: `Invalid BCP 47 locale format: "${locale}"`
|
|
3293
|
+
}
|
|
3289
3294
|
};
|
|
3290
3295
|
}
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3296
|
+
const currentLocales = (schema.supportedLocales ?? []).map((candidate) => (0, import_core4.normalizeLocale)(candidate) ?? candidate);
|
|
3297
|
+
const defaultLocale = schema.defaultLocale === void 0 ? "" : (0, import_core4.normalizeLocale)(schema.defaultLocale) ?? schema.defaultLocale;
|
|
3298
|
+
if (canonicalLocale === defaultLocale || currentLocales.includes(canonicalLocale)) {
|
|
3294
3299
|
return {
|
|
3295
3300
|
valid: false,
|
|
3296
|
-
error: {
|
|
3301
|
+
error: {
|
|
3302
|
+
type: "locale_already_exists",
|
|
3303
|
+
message: `Locale "${canonicalLocale}" is already registered.`
|
|
3304
|
+
}
|
|
3297
3305
|
};
|
|
3298
3306
|
}
|
|
3299
|
-
if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.
|
|
3307
|
+
if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.some((candidate) => (0, import_core4.normalizeLocale)(candidate) === canonicalLocale)) {
|
|
3300
3308
|
return {
|
|
3301
3309
|
valid: false,
|
|
3302
|
-
error: {
|
|
3310
|
+
error: {
|
|
3311
|
+
type: "locale_not_allowed",
|
|
3312
|
+
message: `Locale "${canonicalLocale}" is not allowed by policy.`
|
|
3313
|
+
}
|
|
3303
3314
|
};
|
|
3304
3315
|
}
|
|
3305
|
-
|
|
3316
|
+
const totalLocalesCount = 1 + currentLocales.length;
|
|
3317
|
+
if (policy?.maxLocales !== void 0 && totalLocalesCount >= policy.maxLocales) {
|
|
3306
3318
|
return {
|
|
3307
3319
|
valid: false,
|
|
3308
3320
|
error: {
|
|
3309
3321
|
type: "max_locales_exceeded",
|
|
3310
|
-
message: `
|
|
3322
|
+
message: `Cannot add locale: maximum allowed locales limit (${policy.maxLocales}) reached.`
|
|
3311
3323
|
}
|
|
3312
3324
|
};
|
|
3313
3325
|
}
|
|
3326
|
+
if (customValidator !== void 0) {
|
|
3327
|
+
const context = {
|
|
3328
|
+
locale: canonicalLocale,
|
|
3329
|
+
defaultLocale,
|
|
3330
|
+
currentLocales: Object.freeze([...currentLocales]),
|
|
3331
|
+
...policy === void 0 ? {} : { policy }
|
|
3332
|
+
};
|
|
3333
|
+
const customResult = Reflect.apply(customValidator, void 0, [canonicalLocale, context]);
|
|
3334
|
+
if (customResult === false) {
|
|
3335
|
+
return {
|
|
3336
|
+
valid: false,
|
|
3337
|
+
error: {
|
|
3338
|
+
type: "custom_validation_failed",
|
|
3339
|
+
message: `Custom validation rejected locale "${locale}".`
|
|
3340
|
+
}
|
|
3341
|
+
};
|
|
3342
|
+
}
|
|
3343
|
+
if (typeof customResult === "string") {
|
|
3344
|
+
return {
|
|
3345
|
+
valid: false,
|
|
3346
|
+
error: { type: "custom_validation_failed", message: customResult }
|
|
3347
|
+
};
|
|
3348
|
+
}
|
|
3349
|
+
if (typeof customResult === "object" && customResult !== null && !customResult.valid) return customResult;
|
|
3350
|
+
}
|
|
3314
3351
|
return { valid: true };
|
|
3352
|
+
};
|
|
3353
|
+
function workspaceLocaleValidationMessage(validation) {
|
|
3354
|
+
if (validation.error?.type === "locale_not_allowed") {
|
|
3355
|
+
return validation.error.message.replace(" is not allowed by policy.", " is not allowed by the form policy.");
|
|
3356
|
+
}
|
|
3357
|
+
if (validation.error?.type === "max_locales_exceeded") {
|
|
3358
|
+
const maximum = validation.error.message.match(/\((\d+)\)/u)?.[1];
|
|
3359
|
+
return maximum === void 0 ? validation.error.message : `At most ${maximum} locales are allowed by the form policy.`;
|
|
3360
|
+
}
|
|
3361
|
+
return validation.error?.message ?? "Locale is not valid.";
|
|
3315
3362
|
}
|
|
3316
3363
|
function manualMetadata(sourceText, sourceLocale) {
|
|
3317
3364
|
return {
|
|
@@ -3436,21 +3483,15 @@ function useTranslationWorkspace({
|
|
|
3436
3483
|
const addLocale = (0, import_react4.useCallback)(
|
|
3437
3484
|
(locale) => {
|
|
3438
3485
|
if (readOnly) return { success: false, error: "Workspace is read-only." };
|
|
3439
|
-
const normalized =
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
])
|
|
3446
|
-
];
|
|
3447
|
-
const validation = validateLocale?.(normalized, currentLocales) ?? validateLocaleByPolicy(normalized, currentLocales, policy);
|
|
3448
|
-
if (!validation.valid) return { success: false, error: validation.error?.message ?? "Locale is not valid." };
|
|
3449
|
-
const alreadyRegistered = normalized === sourceLocale || normalized === currentSchema.defaultLocale || (currentSchema.supportedLocales ?? []).includes(normalized);
|
|
3450
|
-
if (alreadyRegistered) {
|
|
3451
|
-
setSelectedLocale(normalized);
|
|
3452
|
-
return { success: true };
|
|
3486
|
+
const normalized = (0, import_core4.normalizeLocale)(locale);
|
|
3487
|
+
if (normalized === null) {
|
|
3488
|
+
return {
|
|
3489
|
+
success: false,
|
|
3490
|
+
error: workspaceLocaleValidationMessage(validateLocalePipeline(locale, currentSchema, policy, validateLocale))
|
|
3491
|
+
};
|
|
3453
3492
|
}
|
|
3493
|
+
const validation = validateLocalePipeline(normalized, currentSchema, policy, validateLocale);
|
|
3494
|
+
if (!validation.valid) return { success: false, error: workspaceLocaleValidationMessage(validation) };
|
|
3454
3495
|
commit({
|
|
3455
3496
|
...currentSchema,
|
|
3456
3497
|
supportedLocales: [.../* @__PURE__ */ new Set([...currentSchema.supportedLocales ?? [], normalized])]
|
|
@@ -3458,22 +3499,16 @@ function useTranslationWorkspace({
|
|
|
3458
3499
|
setSelectedLocale(normalized);
|
|
3459
3500
|
return { success: true };
|
|
3460
3501
|
},
|
|
3461
|
-
[commit, currentSchema, policy, readOnly,
|
|
3502
|
+
[commit, currentSchema, policy, readOnly, validateLocale]
|
|
3462
3503
|
);
|
|
3463
3504
|
const isAddLocaleAllowed = (0, import_react4.useCallback)(
|
|
3464
3505
|
(locale) => {
|
|
3465
3506
|
if (readOnly) return false;
|
|
3466
|
-
const normalized =
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
...currentSchema.defaultLocale === void 0 ? [] : [currentSchema.defaultLocale],
|
|
3470
|
-
sourceLocale,
|
|
3471
|
-
...(0, import_core4.collectSchemaLocales)(currentSchema).allUniqueLocales
|
|
3472
|
-
])
|
|
3473
|
-
];
|
|
3474
|
-
return (validateLocale?.(normalized, currentLocales) ?? validateLocaleByPolicy(normalized, currentLocales, policy)).valid;
|
|
3507
|
+
const normalized = (0, import_core4.normalizeLocale)(locale);
|
|
3508
|
+
if (normalized === null) return false;
|
|
3509
|
+
return validateLocalePipeline(normalized, currentSchema, policy, validateLocale).valid;
|
|
3475
3510
|
},
|
|
3476
|
-
[currentSchema, policy, readOnly,
|
|
3511
|
+
[currentSchema, policy, readOnly, validateLocale]
|
|
3477
3512
|
);
|
|
3478
3513
|
const removeLocale = (0, import_react4.useCallback)(
|
|
3479
3514
|
(locale) => {
|
|
@@ -4846,5 +4881,6 @@ function FormRenderer(props) {
|
|
|
4846
4881
|
useForm,
|
|
4847
4882
|
useFormBuilder,
|
|
4848
4883
|
useSubmissionReceipts,
|
|
4849
|
-
useTranslationWorkspace
|
|
4884
|
+
useTranslationWorkspace,
|
|
4885
|
+
validateLocalePipeline
|
|
4850
4886
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -708,12 +708,19 @@ interface UseTranslationWorkspaceOptions {
|
|
|
708
708
|
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
709
709
|
readonly readOnly?: boolean;
|
|
710
710
|
readonly policy?: FormPolicy;
|
|
711
|
-
readonly validateLocale?: (locale: string, currentLocales: readonly string[]) => LocaleValidationResult;
|
|
711
|
+
readonly validateLocale?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator;
|
|
712
712
|
}
|
|
713
|
+
interface LocaleValidationContext {
|
|
714
|
+
readonly locale: string;
|
|
715
|
+
readonly defaultLocale: string;
|
|
716
|
+
readonly currentLocales: readonly string[];
|
|
717
|
+
readonly policy?: FormPolicy;
|
|
718
|
+
}
|
|
719
|
+
type CustomLocaleValidator = (locale: string, context: LocaleValidationContext) => LocaleValidationResult | boolean | string | undefined | void;
|
|
713
720
|
interface LocaleValidationResult {
|
|
714
721
|
readonly valid: boolean;
|
|
715
722
|
readonly error?: {
|
|
716
|
-
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format";
|
|
723
|
+
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "custom_validation_failed";
|
|
717
724
|
readonly message: string;
|
|
718
725
|
};
|
|
719
726
|
}
|
|
@@ -744,6 +751,7 @@ interface UseTranslationWorkspaceResult {
|
|
|
744
751
|
readonly isTranslating: boolean;
|
|
745
752
|
readonly error?: string;
|
|
746
753
|
}
|
|
754
|
+
declare const validateLocalePipeline: (locale: string, schema: FormSchema, policy?: FormPolicy, customValidator?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator) => LocaleValidationResult;
|
|
747
755
|
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, readOnly, policy, validateLocale }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
748
756
|
|
|
749
757
|
declare const BUILDER_TRANSLATION_KEYS: {
|
|
@@ -818,4 +826,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
|
|
|
818
826
|
type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
|
|
819
827
|
declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
|
|
820
828
|
|
|
821
|
-
export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ChoiceFieldLayoutMode, type ChoiceFieldTypeLayoutMap, type ChoiceGroupSlotProps, type ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldEditorMode, type FieldError, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormBuilderSubmissionSettingsOptions, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererAppearance, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlotProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocaleValidationResult, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationOptions, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type TranslationSummary, type UseFormBuilderOptions, type UseFormBuilderResult, type UseSubmissionReceiptsResult, type UseTranslationWorkspaceOptions, type UseTranslationWorkspaceResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveChoiceFieldLayout, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts, useTranslationWorkspace };
|
|
829
|
+
export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ChoiceFieldLayoutMode, type ChoiceFieldTypeLayoutMap, type ChoiceGroupSlotProps, type ComponentBaseProps, type CustomLocaleValidator, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldEditorMode, type FieldError, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormBuilderSubmissionSettingsOptions, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererAppearance, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlotProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocaleValidationContext, type LocaleValidationResult, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationOptions, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type TranslationSummary, type UseFormBuilderOptions, type UseFormBuilderResult, type UseSubmissionReceiptsResult, type UseTranslationWorkspaceOptions, type UseTranslationWorkspaceResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveChoiceFieldLayout, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts, useTranslationWorkspace, validateLocalePipeline };
|
package/dist/index.d.ts
CHANGED
|
@@ -708,12 +708,19 @@ interface UseTranslationWorkspaceOptions {
|
|
|
708
708
|
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
709
709
|
readonly readOnly?: boolean;
|
|
710
710
|
readonly policy?: FormPolicy;
|
|
711
|
-
readonly validateLocale?: (locale: string, currentLocales: readonly string[]) => LocaleValidationResult;
|
|
711
|
+
readonly validateLocale?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator;
|
|
712
712
|
}
|
|
713
|
+
interface LocaleValidationContext {
|
|
714
|
+
readonly locale: string;
|
|
715
|
+
readonly defaultLocale: string;
|
|
716
|
+
readonly currentLocales: readonly string[];
|
|
717
|
+
readonly policy?: FormPolicy;
|
|
718
|
+
}
|
|
719
|
+
type CustomLocaleValidator = (locale: string, context: LocaleValidationContext) => LocaleValidationResult | boolean | string | undefined | void;
|
|
713
720
|
interface LocaleValidationResult {
|
|
714
721
|
readonly valid: boolean;
|
|
715
722
|
readonly error?: {
|
|
716
|
-
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format";
|
|
723
|
+
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "custom_validation_failed";
|
|
717
724
|
readonly message: string;
|
|
718
725
|
};
|
|
719
726
|
}
|
|
@@ -744,6 +751,7 @@ interface UseTranslationWorkspaceResult {
|
|
|
744
751
|
readonly isTranslating: boolean;
|
|
745
752
|
readonly error?: string;
|
|
746
753
|
}
|
|
754
|
+
declare const validateLocalePipeline: (locale: string, schema: FormSchema, policy?: FormPolicy, customValidator?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator) => LocaleValidationResult;
|
|
747
755
|
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, readOnly, policy, validateLocale }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
748
756
|
|
|
749
757
|
declare const BUILDER_TRANSLATION_KEYS: {
|
|
@@ -818,4 +826,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
|
|
|
818
826
|
type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
|
|
819
827
|
declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
|
|
820
828
|
|
|
821
|
-
export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ChoiceFieldLayoutMode, type ChoiceFieldTypeLayoutMap, type ChoiceGroupSlotProps, type ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldEditorMode, type FieldError, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormBuilderSubmissionSettingsOptions, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererAppearance, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlotProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocaleValidationResult, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationOptions, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type TranslationSummary, type UseFormBuilderOptions, type UseFormBuilderResult, type UseSubmissionReceiptsResult, type UseTranslationWorkspaceOptions, type UseTranslationWorkspaceResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveChoiceFieldLayout, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts, useTranslationWorkspace };
|
|
829
|
+
export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ChoiceFieldLayoutMode, type ChoiceFieldTypeLayoutMap, type ChoiceGroupSlotProps, type ComponentBaseProps, type CustomLocaleValidator, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldEditorMode, type FieldError, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormBuilderSubmissionSettingsOptions, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererAppearance, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlotProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocaleValidationContext, type LocaleValidationResult, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationOptions, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type TranslationSummary, type UseFormBuilderOptions, type UseFormBuilderResult, type UseSubmissionReceiptsResult, type UseTranslationWorkspaceOptions, type UseTranslationWorkspaceResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveChoiceFieldLayout, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts, useTranslationWorkspace, validateLocalePipeline };
|
package/dist/index.js
CHANGED
|
@@ -3238,6 +3238,7 @@ import {
|
|
|
3238
3238
|
collectSchemaLocales as collectSchemaLocales2,
|
|
3239
3239
|
collectTranslationSlots,
|
|
3240
3240
|
computeSourceTextHash,
|
|
3241
|
+
normalizeLocale,
|
|
3241
3242
|
populateSchemaTranslations as populateSchemaTranslations2,
|
|
3242
3243
|
removeLocaleFromSchema
|
|
3243
3244
|
} from "@form-engine-ts/core";
|
|
@@ -3257,37 +3258,83 @@ function asAsyncAdapter(adapter) {
|
|
|
3257
3258
|
)
|
|
3258
3259
|
};
|
|
3259
3260
|
}
|
|
3260
|
-
|
|
3261
|
-
|
|
3261
|
+
var validateLocalePipeline = (locale, schema, policy, customValidator) => {
|
|
3262
|
+
const canonicalLocale = normalizeLocale(locale);
|
|
3263
|
+
if (canonicalLocale === null) {
|
|
3262
3264
|
return {
|
|
3263
3265
|
valid: false,
|
|
3264
|
-
error: {
|
|
3266
|
+
error: {
|
|
3267
|
+
type: "invalid_locale_format",
|
|
3268
|
+
message: `Invalid BCP 47 locale format: "${locale}"`
|
|
3269
|
+
}
|
|
3265
3270
|
};
|
|
3266
3271
|
}
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3272
|
+
const currentLocales = (schema.supportedLocales ?? []).map((candidate) => normalizeLocale(candidate) ?? candidate);
|
|
3273
|
+
const defaultLocale = schema.defaultLocale === void 0 ? "" : normalizeLocale(schema.defaultLocale) ?? schema.defaultLocale;
|
|
3274
|
+
if (canonicalLocale === defaultLocale || currentLocales.includes(canonicalLocale)) {
|
|
3270
3275
|
return {
|
|
3271
3276
|
valid: false,
|
|
3272
|
-
error: {
|
|
3277
|
+
error: {
|
|
3278
|
+
type: "locale_already_exists",
|
|
3279
|
+
message: `Locale "${canonicalLocale}" is already registered.`
|
|
3280
|
+
}
|
|
3273
3281
|
};
|
|
3274
3282
|
}
|
|
3275
|
-
if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.
|
|
3283
|
+
if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.some((candidate) => normalizeLocale(candidate) === canonicalLocale)) {
|
|
3276
3284
|
return {
|
|
3277
3285
|
valid: false,
|
|
3278
|
-
error: {
|
|
3286
|
+
error: {
|
|
3287
|
+
type: "locale_not_allowed",
|
|
3288
|
+
message: `Locale "${canonicalLocale}" is not allowed by policy.`
|
|
3289
|
+
}
|
|
3279
3290
|
};
|
|
3280
3291
|
}
|
|
3281
|
-
|
|
3292
|
+
const totalLocalesCount = 1 + currentLocales.length;
|
|
3293
|
+
if (policy?.maxLocales !== void 0 && totalLocalesCount >= policy.maxLocales) {
|
|
3282
3294
|
return {
|
|
3283
3295
|
valid: false,
|
|
3284
3296
|
error: {
|
|
3285
3297
|
type: "max_locales_exceeded",
|
|
3286
|
-
message: `
|
|
3298
|
+
message: `Cannot add locale: maximum allowed locales limit (${policy.maxLocales}) reached.`
|
|
3287
3299
|
}
|
|
3288
3300
|
};
|
|
3289
3301
|
}
|
|
3302
|
+
if (customValidator !== void 0) {
|
|
3303
|
+
const context = {
|
|
3304
|
+
locale: canonicalLocale,
|
|
3305
|
+
defaultLocale,
|
|
3306
|
+
currentLocales: Object.freeze([...currentLocales]),
|
|
3307
|
+
...policy === void 0 ? {} : { policy }
|
|
3308
|
+
};
|
|
3309
|
+
const customResult = Reflect.apply(customValidator, void 0, [canonicalLocale, context]);
|
|
3310
|
+
if (customResult === false) {
|
|
3311
|
+
return {
|
|
3312
|
+
valid: false,
|
|
3313
|
+
error: {
|
|
3314
|
+
type: "custom_validation_failed",
|
|
3315
|
+
message: `Custom validation rejected locale "${locale}".`
|
|
3316
|
+
}
|
|
3317
|
+
};
|
|
3318
|
+
}
|
|
3319
|
+
if (typeof customResult === "string") {
|
|
3320
|
+
return {
|
|
3321
|
+
valid: false,
|
|
3322
|
+
error: { type: "custom_validation_failed", message: customResult }
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
if (typeof customResult === "object" && customResult !== null && !customResult.valid) return customResult;
|
|
3326
|
+
}
|
|
3290
3327
|
return { valid: true };
|
|
3328
|
+
};
|
|
3329
|
+
function workspaceLocaleValidationMessage(validation) {
|
|
3330
|
+
if (validation.error?.type === "locale_not_allowed") {
|
|
3331
|
+
return validation.error.message.replace(" is not allowed by policy.", " is not allowed by the form policy.");
|
|
3332
|
+
}
|
|
3333
|
+
if (validation.error?.type === "max_locales_exceeded") {
|
|
3334
|
+
const maximum = validation.error.message.match(/\((\d+)\)/u)?.[1];
|
|
3335
|
+
return maximum === void 0 ? validation.error.message : `At most ${maximum} locales are allowed by the form policy.`;
|
|
3336
|
+
}
|
|
3337
|
+
return validation.error?.message ?? "Locale is not valid.";
|
|
3291
3338
|
}
|
|
3292
3339
|
function manualMetadata(sourceText, sourceLocale) {
|
|
3293
3340
|
return {
|
|
@@ -3412,21 +3459,15 @@ function useTranslationWorkspace({
|
|
|
3412
3459
|
const addLocale = useCallback3(
|
|
3413
3460
|
(locale) => {
|
|
3414
3461
|
if (readOnly) return { success: false, error: "Workspace is read-only." };
|
|
3415
|
-
const normalized = locale
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
])
|
|
3422
|
-
];
|
|
3423
|
-
const validation = validateLocale?.(normalized, currentLocales) ?? validateLocaleByPolicy(normalized, currentLocales, policy);
|
|
3424
|
-
if (!validation.valid) return { success: false, error: validation.error?.message ?? "Locale is not valid." };
|
|
3425
|
-
const alreadyRegistered = normalized === sourceLocale || normalized === currentSchema.defaultLocale || (currentSchema.supportedLocales ?? []).includes(normalized);
|
|
3426
|
-
if (alreadyRegistered) {
|
|
3427
|
-
setSelectedLocale(normalized);
|
|
3428
|
-
return { success: true };
|
|
3462
|
+
const normalized = normalizeLocale(locale);
|
|
3463
|
+
if (normalized === null) {
|
|
3464
|
+
return {
|
|
3465
|
+
success: false,
|
|
3466
|
+
error: workspaceLocaleValidationMessage(validateLocalePipeline(locale, currentSchema, policy, validateLocale))
|
|
3467
|
+
};
|
|
3429
3468
|
}
|
|
3469
|
+
const validation = validateLocalePipeline(normalized, currentSchema, policy, validateLocale);
|
|
3470
|
+
if (!validation.valid) return { success: false, error: workspaceLocaleValidationMessage(validation) };
|
|
3430
3471
|
commit({
|
|
3431
3472
|
...currentSchema,
|
|
3432
3473
|
supportedLocales: [.../* @__PURE__ */ new Set([...currentSchema.supportedLocales ?? [], normalized])]
|
|
@@ -3434,22 +3475,16 @@ function useTranslationWorkspace({
|
|
|
3434
3475
|
setSelectedLocale(normalized);
|
|
3435
3476
|
return { success: true };
|
|
3436
3477
|
},
|
|
3437
|
-
[commit, currentSchema, policy, readOnly,
|
|
3478
|
+
[commit, currentSchema, policy, readOnly, validateLocale]
|
|
3438
3479
|
);
|
|
3439
3480
|
const isAddLocaleAllowed = useCallback3(
|
|
3440
3481
|
(locale) => {
|
|
3441
3482
|
if (readOnly) return false;
|
|
3442
|
-
const normalized = locale
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
...currentSchema.defaultLocale === void 0 ? [] : [currentSchema.defaultLocale],
|
|
3446
|
-
sourceLocale,
|
|
3447
|
-
...collectSchemaLocales2(currentSchema).allUniqueLocales
|
|
3448
|
-
])
|
|
3449
|
-
];
|
|
3450
|
-
return (validateLocale?.(normalized, currentLocales) ?? validateLocaleByPolicy(normalized, currentLocales, policy)).valid;
|
|
3483
|
+
const normalized = normalizeLocale(locale);
|
|
3484
|
+
if (normalized === null) return false;
|
|
3485
|
+
return validateLocalePipeline(normalized, currentSchema, policy, validateLocale).valid;
|
|
3451
3486
|
},
|
|
3452
|
-
[currentSchema, policy, readOnly,
|
|
3487
|
+
[currentSchema, policy, readOnly, validateLocale]
|
|
3453
3488
|
);
|
|
3454
3489
|
const removeLocale = useCallback3(
|
|
3455
3490
|
(locale) => {
|
|
@@ -4832,5 +4867,6 @@ export {
|
|
|
4832
4867
|
useForm,
|
|
4833
4868
|
useFormBuilder,
|
|
4834
4869
|
useSubmissionReceipts,
|
|
4835
|
-
useTranslationWorkspace
|
|
4870
|
+
useTranslationWorkspace,
|
|
4871
|
+
validateLocalePipeline
|
|
4836
4872
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@form-engine-ts/react",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.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/
|
|
46
|
-
"@form-engine-ts/
|
|
45
|
+
"@form-engine-ts/privacy": "4.8.0",
|
|
46
|
+
"@form-engine-ts/core": "4.8.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": ">=18.2 <20",
|