@form-engine-ts/react 4.5.0 → 4.6.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 +5 -0
- package/dist/index.cjs +70 -8
- package/dist/index.d.cts +16 -3
- package/dist/index.d.ts +16 -3
- package/dist/index.js +72 -9
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -79,6 +79,11 @@ single-slot or batch translation with stale/manual status handling. The MUI pack
|
|
|
79
79
|
`setActiveFieldId` to keep one field editor open at a time. Set `submissionSettingsOptions={{ enabled: true }}` to expose
|
|
80
80
|
schema-driven pre-submit confirmation controls in the builder.
|
|
81
81
|
|
|
82
|
+
`useTranslationWorkspace` validates added locales against `policy.allowedLocales` and `policy.maxLocales` and returns
|
|
83
|
+
a structured `{ success, error }` result from `addLocale`. Use `validateLocale` for application-specific BCP 47 or
|
|
84
|
+
tenant rules, and `isAddLocaleAllowed` to disable locale controls before submission. Removing a locale also clears its
|
|
85
|
+
localized values and metadata; the default locale remains protected.
|
|
86
|
+
|
|
82
87
|
## Headless builder and renderer lifecycle
|
|
83
88
|
|
|
84
89
|
`useFormBuilder({ schema, onChange, policy, idFactory, factories })` exposes controlled field, option, page, condition,
|
package/dist/index.cjs
CHANGED
|
@@ -3281,6 +3281,38 @@ function asAsyncAdapter(adapter) {
|
|
|
3281
3281
|
)
|
|
3282
3282
|
};
|
|
3283
3283
|
}
|
|
3284
|
+
function validateLocaleByPolicy(locale, currentLocales, policy) {
|
|
3285
|
+
if (locale.length === 0) {
|
|
3286
|
+
return {
|
|
3287
|
+
valid: false,
|
|
3288
|
+
error: { type: "invalid_locale_format", message: "Locale must not be empty." }
|
|
3289
|
+
};
|
|
3290
|
+
}
|
|
3291
|
+
try {
|
|
3292
|
+
Intl.getCanonicalLocales(locale);
|
|
3293
|
+
} catch {
|
|
3294
|
+
return {
|
|
3295
|
+
valid: false,
|
|
3296
|
+
error: { type: "invalid_locale_format", message: `Locale "${locale}" is not a valid BCP 47 locale.` }
|
|
3297
|
+
};
|
|
3298
|
+
}
|
|
3299
|
+
if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.includes(locale)) {
|
|
3300
|
+
return {
|
|
3301
|
+
valid: false,
|
|
3302
|
+
error: { type: "locale_not_allowed", message: `Locale "${locale}" is not allowed by the form policy.` }
|
|
3303
|
+
};
|
|
3304
|
+
}
|
|
3305
|
+
if (policy?.maxLocales !== void 0 && !currentLocales.includes(locale) && currentLocales.length >= policy.maxLocales) {
|
|
3306
|
+
return {
|
|
3307
|
+
valid: false,
|
|
3308
|
+
error: {
|
|
3309
|
+
type: "max_locales_exceeded",
|
|
3310
|
+
message: `At most ${policy.maxLocales} locales are allowed by the form policy.`
|
|
3311
|
+
}
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3314
|
+
return { valid: true };
|
|
3315
|
+
}
|
|
3284
3316
|
function manualMetadata(sourceText, sourceLocale) {
|
|
3285
3317
|
return {
|
|
3286
3318
|
sourceLocale,
|
|
@@ -3348,7 +3380,9 @@ function useTranslationWorkspace({
|
|
|
3348
3380
|
sourceLocale = schema.defaultLocale ?? "en",
|
|
3349
3381
|
targetLocale,
|
|
3350
3382
|
translationAdapter,
|
|
3351
|
-
readOnly = false
|
|
3383
|
+
readOnly = false,
|
|
3384
|
+
policy,
|
|
3385
|
+
validateLocale
|
|
3352
3386
|
}) {
|
|
3353
3387
|
const [draftSchema, setDraftSchema] = (0, import_react4.useState)(schema);
|
|
3354
3388
|
const [selectedLocale, setSelectedLocale] = (0, import_react4.useState)(targetLocale ?? "");
|
|
@@ -3401,23 +3435,50 @@ function useTranslationWorkspace({
|
|
|
3401
3435
|
);
|
|
3402
3436
|
const addLocale = (0, import_react4.useCallback)(
|
|
3403
3437
|
(locale) => {
|
|
3404
|
-
if (readOnly
|
|
3438
|
+
if (readOnly) return { success: false, error: "Workspace is read-only." };
|
|
3405
3439
|
const normalized = locale.trim();
|
|
3440
|
+
const currentLocales = [
|
|
3441
|
+
.../* @__PURE__ */ new Set([
|
|
3442
|
+
...currentSchema.defaultLocale === void 0 ? [] : [currentSchema.defaultLocale],
|
|
3443
|
+
sourceLocale,
|
|
3444
|
+
...(0, import_core4.collectSchemaLocales)(currentSchema).allUniqueLocales
|
|
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 };
|
|
3453
|
+
}
|
|
3406
3454
|
commit({
|
|
3407
3455
|
...currentSchema,
|
|
3408
3456
|
supportedLocales: [.../* @__PURE__ */ new Set([...currentSchema.supportedLocales ?? [], normalized])]
|
|
3409
3457
|
});
|
|
3410
3458
|
setSelectedLocale(normalized);
|
|
3459
|
+
return { success: true };
|
|
3460
|
+
},
|
|
3461
|
+
[commit, currentSchema, policy, readOnly, sourceLocale, validateLocale]
|
|
3462
|
+
);
|
|
3463
|
+
const isAddLocaleAllowed = (0, import_react4.useCallback)(
|
|
3464
|
+
(locale) => {
|
|
3465
|
+
if (readOnly) return false;
|
|
3466
|
+
const normalized = locale.trim();
|
|
3467
|
+
const currentLocales = [
|
|
3468
|
+
.../* @__PURE__ */ new Set([
|
|
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;
|
|
3411
3475
|
},
|
|
3412
|
-
[
|
|
3476
|
+
[currentSchema, policy, readOnly, sourceLocale, validateLocale]
|
|
3413
3477
|
);
|
|
3414
3478
|
const removeLocale = (0, import_react4.useCallback)(
|
|
3415
3479
|
(locale) => {
|
|
3416
|
-
if (readOnly || locale === sourceLocale) return;
|
|
3417
|
-
commit(
|
|
3418
|
-
...currentSchema,
|
|
3419
|
-
supportedLocales: (currentSchema.supportedLocales ?? []).filter((candidate) => candidate !== locale)
|
|
3420
|
-
});
|
|
3480
|
+
if (readOnly || locale === sourceLocale || locale === currentSchema.defaultLocale) return;
|
|
3481
|
+
commit((0, import_core4.removeLocaleFromSchema)(currentSchema, locale));
|
|
3421
3482
|
if (activeLocale === locale) setSelectedLocale("");
|
|
3422
3483
|
},
|
|
3423
3484
|
[activeLocale, commit, currentSchema, readOnly, sourceLocale]
|
|
@@ -3478,6 +3539,7 @@ function useTranslationWorkspace({
|
|
|
3478
3539
|
slots,
|
|
3479
3540
|
summary,
|
|
3480
3541
|
addLocale,
|
|
3542
|
+
isAddLocaleAllowed,
|
|
3481
3543
|
removeLocale,
|
|
3482
3544
|
setTranslation,
|
|
3483
3545
|
translateAll,
|
package/dist/index.d.cts
CHANGED
|
@@ -707,6 +707,15 @@ interface UseTranslationWorkspaceOptions {
|
|
|
707
707
|
readonly targetLocale?: string;
|
|
708
708
|
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
709
709
|
readonly readOnly?: boolean;
|
|
710
|
+
readonly policy?: FormPolicy;
|
|
711
|
+
readonly validateLocale?: (locale: string, currentLocales: readonly string[]) => LocaleValidationResult;
|
|
712
|
+
}
|
|
713
|
+
interface LocaleValidationResult {
|
|
714
|
+
readonly valid: boolean;
|
|
715
|
+
readonly error?: {
|
|
716
|
+
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format";
|
|
717
|
+
readonly message: string;
|
|
718
|
+
};
|
|
710
719
|
}
|
|
711
720
|
interface TranslationSummary {
|
|
712
721
|
readonly totalSlots: number;
|
|
@@ -723,7 +732,11 @@ interface UseTranslationWorkspaceResult {
|
|
|
723
732
|
readonly setTargetLocale: (locale: string) => void;
|
|
724
733
|
readonly slots: readonly TranslationSlot[];
|
|
725
734
|
readonly summary: TranslationSummary;
|
|
726
|
-
readonly addLocale: (locale: string) =>
|
|
735
|
+
readonly addLocale: (locale: string) => {
|
|
736
|
+
readonly success: boolean;
|
|
737
|
+
readonly error?: string;
|
|
738
|
+
};
|
|
739
|
+
readonly isAddLocaleAllowed: (locale: string) => boolean;
|
|
727
740
|
readonly removeLocale: (locale: string) => void;
|
|
728
741
|
readonly setTranslation: (slot: TranslationSlot, text: string) => void;
|
|
729
742
|
readonly translateAll: (options?: PopulateTranslationOptions) => Promise<TranslationReport>;
|
|
@@ -731,7 +744,7 @@ interface UseTranslationWorkspaceResult {
|
|
|
731
744
|
readonly isTranslating: boolean;
|
|
732
745
|
readonly error?: string;
|
|
733
746
|
}
|
|
734
|
-
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, readOnly }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
747
|
+
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, readOnly, policy, validateLocale }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
735
748
|
|
|
736
749
|
declare const BUILDER_TRANSLATION_KEYS: {
|
|
737
750
|
readonly ADD_FIELD: "builder.actions.addField";
|
|
@@ -805,4 +818,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
|
|
|
805
818
|
type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
|
|
806
819
|
declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
|
|
807
820
|
|
|
808
|
-
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 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 };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -707,6 +707,15 @@ interface UseTranslationWorkspaceOptions {
|
|
|
707
707
|
readonly targetLocale?: string;
|
|
708
708
|
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
709
709
|
readonly readOnly?: boolean;
|
|
710
|
+
readonly policy?: FormPolicy;
|
|
711
|
+
readonly validateLocale?: (locale: string, currentLocales: readonly string[]) => LocaleValidationResult;
|
|
712
|
+
}
|
|
713
|
+
interface LocaleValidationResult {
|
|
714
|
+
readonly valid: boolean;
|
|
715
|
+
readonly error?: {
|
|
716
|
+
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format";
|
|
717
|
+
readonly message: string;
|
|
718
|
+
};
|
|
710
719
|
}
|
|
711
720
|
interface TranslationSummary {
|
|
712
721
|
readonly totalSlots: number;
|
|
@@ -723,7 +732,11 @@ interface UseTranslationWorkspaceResult {
|
|
|
723
732
|
readonly setTargetLocale: (locale: string) => void;
|
|
724
733
|
readonly slots: readonly TranslationSlot[];
|
|
725
734
|
readonly summary: TranslationSummary;
|
|
726
|
-
readonly addLocale: (locale: string) =>
|
|
735
|
+
readonly addLocale: (locale: string) => {
|
|
736
|
+
readonly success: boolean;
|
|
737
|
+
readonly error?: string;
|
|
738
|
+
};
|
|
739
|
+
readonly isAddLocaleAllowed: (locale: string) => boolean;
|
|
727
740
|
readonly removeLocale: (locale: string) => void;
|
|
728
741
|
readonly setTranslation: (slot: TranslationSlot, text: string) => void;
|
|
729
742
|
readonly translateAll: (options?: PopulateTranslationOptions) => Promise<TranslationReport>;
|
|
@@ -731,7 +744,7 @@ interface UseTranslationWorkspaceResult {
|
|
|
731
744
|
readonly isTranslating: boolean;
|
|
732
745
|
readonly error?: string;
|
|
733
746
|
}
|
|
734
|
-
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, readOnly }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
747
|
+
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, readOnly, policy, validateLocale }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
735
748
|
|
|
736
749
|
declare const BUILDER_TRANSLATION_KEYS: {
|
|
737
750
|
readonly ADD_FIELD: "builder.actions.addField";
|
|
@@ -805,4 +818,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
|
|
|
805
818
|
type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
|
|
806
819
|
declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
|
|
807
820
|
|
|
808
|
-
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 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 };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -3238,7 +3238,8 @@ import {
|
|
|
3238
3238
|
collectSchemaLocales as collectSchemaLocales2,
|
|
3239
3239
|
collectTranslationSlots,
|
|
3240
3240
|
computeSourceTextHash,
|
|
3241
|
-
populateSchemaTranslations as populateSchemaTranslations2
|
|
3241
|
+
populateSchemaTranslations as populateSchemaTranslations2,
|
|
3242
|
+
removeLocaleFromSchema
|
|
3242
3243
|
} from "@form-engine-ts/core";
|
|
3243
3244
|
import { useCallback as useCallback3, useMemo as useMemo3, useState as useState4 } from "react";
|
|
3244
3245
|
function updateTranslationMap(translations, locale, property, text) {
|
|
@@ -3256,6 +3257,38 @@ function asAsyncAdapter(adapter) {
|
|
|
3256
3257
|
)
|
|
3257
3258
|
};
|
|
3258
3259
|
}
|
|
3260
|
+
function validateLocaleByPolicy(locale, currentLocales, policy) {
|
|
3261
|
+
if (locale.length === 0) {
|
|
3262
|
+
return {
|
|
3263
|
+
valid: false,
|
|
3264
|
+
error: { type: "invalid_locale_format", message: "Locale must not be empty." }
|
|
3265
|
+
};
|
|
3266
|
+
}
|
|
3267
|
+
try {
|
|
3268
|
+
Intl.getCanonicalLocales(locale);
|
|
3269
|
+
} catch {
|
|
3270
|
+
return {
|
|
3271
|
+
valid: false,
|
|
3272
|
+
error: { type: "invalid_locale_format", message: `Locale "${locale}" is not a valid BCP 47 locale.` }
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.includes(locale)) {
|
|
3276
|
+
return {
|
|
3277
|
+
valid: false,
|
|
3278
|
+
error: { type: "locale_not_allowed", message: `Locale "${locale}" is not allowed by the form policy.` }
|
|
3279
|
+
};
|
|
3280
|
+
}
|
|
3281
|
+
if (policy?.maxLocales !== void 0 && !currentLocales.includes(locale) && currentLocales.length >= policy.maxLocales) {
|
|
3282
|
+
return {
|
|
3283
|
+
valid: false,
|
|
3284
|
+
error: {
|
|
3285
|
+
type: "max_locales_exceeded",
|
|
3286
|
+
message: `At most ${policy.maxLocales} locales are allowed by the form policy.`
|
|
3287
|
+
}
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
return { valid: true };
|
|
3291
|
+
}
|
|
3259
3292
|
function manualMetadata(sourceText, sourceLocale) {
|
|
3260
3293
|
return {
|
|
3261
3294
|
sourceLocale,
|
|
@@ -3323,7 +3356,9 @@ function useTranslationWorkspace({
|
|
|
3323
3356
|
sourceLocale = schema.defaultLocale ?? "en",
|
|
3324
3357
|
targetLocale,
|
|
3325
3358
|
translationAdapter,
|
|
3326
|
-
readOnly = false
|
|
3359
|
+
readOnly = false,
|
|
3360
|
+
policy,
|
|
3361
|
+
validateLocale
|
|
3327
3362
|
}) {
|
|
3328
3363
|
const [draftSchema, setDraftSchema] = useState4(schema);
|
|
3329
3364
|
const [selectedLocale, setSelectedLocale] = useState4(targetLocale ?? "");
|
|
@@ -3376,23 +3411,50 @@ function useTranslationWorkspace({
|
|
|
3376
3411
|
);
|
|
3377
3412
|
const addLocale = useCallback3(
|
|
3378
3413
|
(locale) => {
|
|
3379
|
-
if (readOnly
|
|
3414
|
+
if (readOnly) return { success: false, error: "Workspace is read-only." };
|
|
3380
3415
|
const normalized = locale.trim();
|
|
3416
|
+
const currentLocales = [
|
|
3417
|
+
.../* @__PURE__ */ new Set([
|
|
3418
|
+
...currentSchema.defaultLocale === void 0 ? [] : [currentSchema.defaultLocale],
|
|
3419
|
+
sourceLocale,
|
|
3420
|
+
...collectSchemaLocales2(currentSchema).allUniqueLocales
|
|
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 };
|
|
3429
|
+
}
|
|
3381
3430
|
commit({
|
|
3382
3431
|
...currentSchema,
|
|
3383
3432
|
supportedLocales: [.../* @__PURE__ */ new Set([...currentSchema.supportedLocales ?? [], normalized])]
|
|
3384
3433
|
});
|
|
3385
3434
|
setSelectedLocale(normalized);
|
|
3435
|
+
return { success: true };
|
|
3436
|
+
},
|
|
3437
|
+
[commit, currentSchema, policy, readOnly, sourceLocale, validateLocale]
|
|
3438
|
+
);
|
|
3439
|
+
const isAddLocaleAllowed = useCallback3(
|
|
3440
|
+
(locale) => {
|
|
3441
|
+
if (readOnly) return false;
|
|
3442
|
+
const normalized = locale.trim();
|
|
3443
|
+
const currentLocales = [
|
|
3444
|
+
.../* @__PURE__ */ new Set([
|
|
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;
|
|
3386
3451
|
},
|
|
3387
|
-
[
|
|
3452
|
+
[currentSchema, policy, readOnly, sourceLocale, validateLocale]
|
|
3388
3453
|
);
|
|
3389
3454
|
const removeLocale = useCallback3(
|
|
3390
3455
|
(locale) => {
|
|
3391
|
-
if (readOnly || locale === sourceLocale) return;
|
|
3392
|
-
commit(
|
|
3393
|
-
...currentSchema,
|
|
3394
|
-
supportedLocales: (currentSchema.supportedLocales ?? []).filter((candidate) => candidate !== locale)
|
|
3395
|
-
});
|
|
3456
|
+
if (readOnly || locale === sourceLocale || locale === currentSchema.defaultLocale) return;
|
|
3457
|
+
commit(removeLocaleFromSchema(currentSchema, locale));
|
|
3396
3458
|
if (activeLocale === locale) setSelectedLocale("");
|
|
3397
3459
|
},
|
|
3398
3460
|
[activeLocale, commit, currentSchema, readOnly, sourceLocale]
|
|
@@ -3453,6 +3515,7 @@ function useTranslationWorkspace({
|
|
|
3453
3515
|
slots,
|
|
3454
3516
|
summary,
|
|
3455
3517
|
addLocale,
|
|
3518
|
+
isAddLocaleAllowed,
|
|
3456
3519
|
removeLocale,
|
|
3457
3520
|
setTranslation,
|
|
3458
3521
|
translateAll,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@form-engine-ts/react",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.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": "4.
|
|
46
|
-
"@form-engine-ts/privacy": "4.
|
|
45
|
+
"@form-engine-ts/core": "4.6.0",
|
|
46
|
+
"@form-engine-ts/privacy": "4.6.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": ">=18.2 <20",
|