import type { ValidationRule as SyValidationRule } from '@/composables/validation/useValidation' import { computed, ref, toValue, type Ref } from 'vue' import type { ValidationRule as VuetifyValidationRule } from 'vuetify' import { useCustomValidation } from './useCustomValidation' import { useVuetifyValidation as useVuetifyValidationComposable } from './useVuetifyValidation' export type { VuetifyValidationRule } export type { SyValidationRule as ValidationRule } export interface FieldValidationProps { customRules?: SyValidationRule[] customSuccessRules?: SyValidationRule[] customWarningRules?: SyValidationRule[] disableErrorHandling?: boolean disabled?: boolean errorMessages?: string[] | null hasError?: boolean hasSuccess?: boolean hasWarning?: boolean isValidateOnBlur?: boolean label?: string maxErrors?: number modelValue?: unknown readonly?: boolean required?: boolean rules?: VuetifyValidationRule[] showSuccessMessages?: boolean successMessages?: string[] | null // When true (Vuetify native mode), the controller should not handle errors/successes useVuetifyValidation?: boolean warningMessages?: string[] | null } /** * Point d'entrée de la validation pour les composants de champ. * Gère à la fois la validation native Vuetify (si useVuetifyValidation vaut true) * et la validation custom Synapse (si customRules/customWarningRules/customSuccessRules sont fournis). * customRules correspond aux règles d'erreur bloquantes. * errorMessages/warningMessages/successMessages sont des messages externes injectés par le parent * et ne déclenchent aucun calcul de validation. * Expose aussi une interface unifiée pour les erreurs, avertissements, succès et la validation à la demande. */ export const validationPropsDefaults = { readonly: false, disabled: false, required: false, isValidateOnBlur: true, showSuccessMessages: true, disableErrorHandling: false, customRules: () => [], customWarningRules: () => [], customSuccessRules: () => [], errorMessages: null, warningMessages: null, successMessages: null, useVuetifyValidation: false, hasError: false, hasWarning: false, hasSuccess: false, maxErrors: 1, } export function useValidation(params: { modelValue: Ref readonly: Ref disabled: Ref required: Ref isValidateOnBlur: Ref showSuccessMessages: Ref disableErrorHandling: Ref label: Ref focused: Ref errorMessages?: Ref warningMessages?: Ref successMessages?: Ref hasErrorProp?: Ref hasWarningProp?: Ref hasSuccessProp?: Ref } & ({ useVuetifyValidation: true rules: Ref customRules?: never customWarningRules?: never customSuccessRules?: never maxErrors?: Ref } | { useVuetifyValidation: false customRules: Ref customWarningRules?: Ref customSuccessRules?: Ref rules?: never } | { useVuetifyValidation: Ref customRules: Ref customWarningRules?: Ref customSuccessRules?: Ref rules: Ref maxErrors?: Ref })) { if (params.disableErrorHandling.value) { return { errors: ref([]), warnings: ref([]), successes: ref([]), hasError: computed(() => params.hasErrorProp?.value ?? false), hasWarning: computed(() => params.hasWarningProp?.value ?? false), hasSuccess: computed(() => params.hasSuccessProp?.value ?? false), validate: async () => true, clearValidation: () => {}, } } const vuetifyErrors = ref([]) const customErrors = ref([]) const innerWarnings = ref([]) const innerSuccesses = ref([]) let vuetifyValidator: ReturnType | null = null if (params.useVuetifyValidation !== false) { vuetifyValidator = useVuetifyValidationComposable( params.modelValue, params.rules, params.disabled, vuetifyErrors, params.hasErrorProp || ref(false), computed(() => params.errorMessages?.value || []), params.focused, params.maxErrors, params.label, params.label, params.readonly, computed(() => params.isValidateOnBlur.value ? 'blur' : 'input'), ) } const customValidator = useCustomValidation( params.modelValue, params.customRules, params.customWarningRules, params.customSuccessRules, customErrors, innerWarnings, innerSuccesses, params.showSuccessMessages, params.label, params.focused, params.isValidateOnBlur, params.disableErrorHandling, params.readonly, params.disabled, ) async function validate(): Promise { if (params.readonly.value || params.disabled.value || params.disableErrorHandling.value) { vuetifyErrors.value = [] customErrors.value = [] innerWarnings.value = [] innerSuccesses.value = [] return true } if (toValue(params.useVuetifyValidation)) { const result = await vuetifyValidator!.validate() return result?.length === 0 } else { const result = await customValidator.validate() return result.state.errors.length === 0 } } const errors = computed(() => [...new Set([ ...vuetifyErrors.value, ...customErrors.value, ...(params.errorMessages?.value || []), ])]) const warnings = computed(() => [...new Set([ ...innerWarnings.value, ...(params.warningMessages?.value || []), ])]) const successes = computed(() => [...new Set([ ...(params.showSuccessMessages.value ? innerSuccesses.value : []), ...(params.successMessages?.value || []), ])]) const internalHasSuccess = computed(() => customValidator.hasSuccess.value) const hasError = computed(() => errors.value.length > 0 || params.hasErrorProp?.value) const hasWarning = computed(() => warnings.value.length > 0 || params.hasWarningProp?.value) // TODO: vérifier si c'est la meilleure approche pour supprimer le succès en mode Vuetify const hasSuccess = computed(() => { if (toValue(params.useVuetifyValidation)) { return params.hasSuccessProp?.value ?? false } return ( (internalHasSuccess.value || (params.successMessages?.value?.length ?? 0) > 0) && !hasError.value && !hasWarning.value ) || (params.hasSuccessProp?.value ?? false) }) function clearValidation() { vuetifyErrors.value = [] customValidator.clearValidation() } return { errors, warnings, successes, hasError, hasWarning, hasSuccess, validate, clearValidation, } }