UNPKG

vuestic-ui

Version:
1 lines 13.5 kB
{"version":3,"file":"useValidation.mjs","sources":["../../../../src/composables/useValidation.ts"],"sourcesContent":["import {\n watch,\n computed,\n type PropType,\n type ExtractPropTypes,\n nextTick,\n type WritableComputedRef,\n ref,\n toRef,\n type Ref,\n watchEffect,\n} from 'vue'\n\nimport { useSyncProp } from './useSyncProp'\nimport { useFormChild } from './useForm'\nimport { type ExtractReadonlyArrayKeys } from '../utils/types/readonly-array-keys'\nimport { watchSetter } from './../utils/watch-setter'\nimport { isFunction } from '../utils/is-function'\nimport { isString } from '../utils/is-string'\n\nexport type ValidationRule<V = any> = ((v: V) => any | string) | Promise<((v: V) => any | string)>\n\ntype UseValidationOptions = {\n reset: () => void\n focus: () => void\n value: WritableComputedRef<any> | Ref<any>\n}\n\nconst normalizeValidationRules = (rules: string | ValidationRule[] = [], callArguments: unknown = null) => {\n if (isString(rules)) { rules = [rules] as any }\n\n return (rules as ValidationRule[])\n .map((rule) => isFunction(rule) ? rule(callArguments) : rule)\n}\n\nexport const useValidationProps = {\n name: { type: String, default: undefined },\n rules: { type: Array as PropType<ValidationRule<any>[]>, default: () => [] as any },\n dirty: { type: Boolean, default: false },\n error: { type: Boolean, default: undefined },\n errorMessages: { type: [Array, String] as PropType<string[] | string>, default: undefined },\n errorCount: { type: [String, Number], default: 1 },\n success: { type: Boolean, default: false },\n messages: { type: [Array, String] as PropType<string[] | string>, default: () => [] },\n immediateValidation: { type: Boolean, default: false },\n modelValue: {},\n}\n\nexport type ValidationProps<V, RulesArgument extends V = V> = {\n rules: { type: PropType<ValidationRule<RulesArgument>[]>, default: () => any, required: false }\n modelValue: { type: PropType<V>, default: V }\n} & Omit<typeof useValidationProps, 'modelValue' | 'rules'>\n\nexport const useValidationEmits = ['update:error', 'update:errorMessages', 'update:dirty'] as const\n\nconst isPromise = (value: any): value is Promise<any> => {\n return typeof value === 'object' && typeof value.then === 'function'\n}\n\nconst useDirtyValue = (\n value: Ref<any>,\n props: ExtractPropTypes<typeof useValidationProps>,\n emit: (event: ExtractReadonlyArrayKeys<typeof useValidationEmits>, ...args: any[]) => void,\n) => {\n const isDirty = ref(props.dirty || false)\n\n watchSetter(value, () => {\n isDirty.value = true\n emit('update:dirty', true)\n })\n\n watch(value, (newValue, oldValue) => {\n // Watch only if object keys changed, not the object itself\n if (newValue === oldValue) {\n isDirty.value = true\n }\n }, { deep: true })\n\n watch(() => props.dirty, (newValue) => {\n if (isDirty.value === newValue) { return }\n isDirty.value = newValue\n })\n\n return { isDirty }\n}\n\nconst useTouched = () => {\n const isTouched = ref(false)\n\n const onBlur = () => {\n isTouched.value = true\n }\n\n return { isTouched, onBlur }\n}\n\nconst useOncePerTick = <T extends (...args: any[]) => any>(fn: T) => {\n let canBeCalled = true\n\n return (...args: Parameters<T>) => {\n if (!canBeCalled) { return }\n canBeCalled = false\n const result = fn(...args)\n nextTick(() => { canBeCalled = true })\n return result\n }\n}\n\nexport const useValidation = <V, P extends ExtractPropTypes<typeof useValidationProps>>(\n props: P,\n emit: (event: any, ...args: any[]) => void,\n options: UseValidationOptions,\n) => {\n const { reset, focus } = options\n const [isError] = useSyncProp('error', props, emit, false)\n const [errorMessages] = useSyncProp('errorMessages', props, emit, [] as string[])\n const isLoading = ref(false)\n const { isTouched, onBlur } = useTouched()\n\n const validationAriaAttributes = computed(() => ({\n 'aria-invalid': isError.value,\n 'aria-errormessage': typeof errorMessages.value === 'string'\n ? errorMessages.value\n : errorMessages.value.join(', '),\n }))\n\n const resetValidation = () => {\n errorMessages.value = []\n isError.value = false\n isDirty.value = false\n isTouched.value = false\n isLoading.value = false\n }\n\n const processResults = (results: any[]) => {\n let error = false\n let eMessages: string[] = []\n\n results.forEach((result: any) => {\n if (isString(result)) {\n eMessages = [...eMessages, result]\n error = true\n } else if (result === false) {\n error = true\n } // Ignore if result is Promise\n })\n\n errorMessages.value = eMessages\n isError.value = error\n\n return !error\n }\n\n const validateAsync = async (): Promise<boolean> => {\n if (!props.rules || !props.rules.length) {\n return true\n }\n\n const results = normalizeValidationRules(props.rules.flat(), options.value.value)\n const asyncPromiseResults = results.filter((result) => isPromise(result))\n const syncRules = results.filter((result) => !isPromise(result))\n\n if (!asyncPromiseResults.length) { return processResults(syncRules) }\n\n isLoading.value = true\n return Promise.all(asyncPromiseResults)\n .then((asyncResults) => {\n return processResults([...syncRules, ...asyncResults])\n })\n .finally(() => {\n isLoading.value = false\n })\n }\n\n const validate = useOncePerTick((): boolean => {\n if (!props.rules || !props.rules.length) {\n return true\n }\n\n const rules = props.rules.flat()\n\n const results = normalizeValidationRules(rules, options.value.value)\n const asyncPromiseResults = results.filter((result) => isPromise(result))\n const syncRules = results.filter((result) => !isPromise(result))\n const isSyncedError = syncRules.some((result: string | boolean) => isString(result) ? result : result === false)\n\n // Prevent async rules from being executed if sync rules are invalid\n if (asyncPromiseResults.length && !isSyncedError) {\n isLoading.value = true\n Promise.all(asyncPromiseResults).then((asyncResults) => {\n processResults([...syncRules, ...asyncResults]) // Process sync rules and async rules at the same time\n isLoading.value = false\n })\n return isSyncedError\n }\n\n return processResults(syncRules)\n })\n\n watchEffect(() => validate())\n\n const { isDirty } = useDirtyValue(options.value, props, emit)\n\n const {\n // Renamed to forceHideError because it's not clear what it does\n forceHideErrors,\n forceHideLoading,\n forceHideErrorMessages,\n forceDirty,\n immediate: isFormImmediate,\n } = useFormChild({\n isTouched,\n isDirty,\n isValid: computed(() => !isError.value),\n isLoading: isLoading,\n errorMessages: errorMessages,\n validate,\n validateAsync,\n resetValidation,\n focus,\n reset: () => {\n reset()\n resetValidation()\n validate()\n },\n value: computed(() => options.value || props.modelValue),\n name: toRef(props, 'name'),\n })\n\n const immediateValidation = computed(() => props.immediateValidation || isFormImmediate.value)\n\n let canValidate = true\n const withoutValidation = (cb: () => any): void => {\n canValidate = false\n cb()\n // NextTick because we update props in the same tick, but they are updated in the next one\n nextTick(() => { canValidate = true })\n }\n watch(options.value, () => {\n if (!canValidate) { return }\n\n return validate()\n }, { immediate: immediateValidation.value })\n\n return {\n isDirty,\n isValid: computed(() => !isError.value),\n isError: isError,\n isTouched,\n isLoading: computed({\n get: () => {\n if (forceHideErrors.value) { return false }\n if (immediateValidation.value) {\n return isLoading.value\n }\n\n if (isTouched.value || isDirty.value || forceDirty.value) {\n return isLoading.value\n }\n\n return false\n },\n set (value) {\n isLoading.value = value\n },\n }),\n computedError: computed(() => {\n if (forceHideErrors.value) { return false }\n\n if (immediateValidation.value) {\n return isError.value\n }\n\n if (isTouched.value || isDirty.value || forceDirty.value) {\n return isError.value\n }\n\n return false\n }),\n computedErrorMessages: computed(() => forceHideErrorMessages.value ? [] : errorMessages.value),\n listeners: { onBlur },\n validate,\n resetValidation,\n withoutValidation,\n validationAriaAttributes,\n }\n}\n"],"names":[],"mappings":"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"}