UNPKG

vuetify

Version:

Vue Material Component Framework

197 lines (193 loc) 6.53 kB
// Composables import { makeFocusProps } from "./focus.js"; import { useForm } from "./form.js"; import { useProxiedModel } from "./proxiedModel.js"; import { useToggleScope } from "./toggleScope.js"; // import { useRules } from '@/labs/rules' // Utilities import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, ref, shallowRef, unref, useId, watch } from 'vue'; import { getCurrentInstance, getCurrentInstanceName, propsFactory, wrapInArray } from "../util/index.js"; // Types // type ValidationRuleParams = [any, string?] // type ValidationAlias = string | [string, ...ValidationRuleParams] export const makeValidationProps = propsFactory({ disabled: { type: Boolean, default: null }, error: Boolean, errorMessages: { type: [Array, String], default: () => [] }, maxErrors: { type: [Number, String], default: 1 }, name: String, label: String, readonly: { type: Boolean, default: null }, rules: { type: Array, // type: Array as PropType<readonly (ValidationRule | ValidationAlias)[]>, default: () => [] }, modelValue: null, validateOn: String, validationValue: null, ...makeFocusProps() }, 'validation'); export function useValidation(props) { let name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : getCurrentInstanceName(); let id = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : useId(); const model = useProxiedModel(props, 'modelValue'); const validationModel = computed(() => props.validationValue === undefined ? model.value : props.validationValue); const form = useForm(props); // const rules = useRules() const internalErrorMessages = ref([]); const isPristine = shallowRef(true); const isDirty = computed(() => !!(wrapInArray(model.value === '' ? null : model.value).length || wrapInArray(validationModel.value === '' ? null : validationModel.value).length)); const errorMessages = computed(() => { return props.errorMessages?.length ? wrapInArray(props.errorMessages).concat(internalErrorMessages.value).slice(0, Math.max(0, Number(props.maxErrors))) : internalErrorMessages.value; }); const validateOn = computed(() => { let value = (props.validateOn ?? form.validateOn?.value) || 'input'; if (value === 'lazy') value = 'input lazy'; if (value === 'eager') value = 'input eager'; const set = new Set(value?.split(' ') ?? []); return { input: set.has('input'), blur: set.has('blur') || set.has('input') || set.has('invalid-input'), invalidInput: set.has('invalid-input'), lazy: set.has('lazy'), eager: set.has('eager') }; }); const isValid = computed(() => { if (props.error || props.errorMessages?.length) return false; if (!props.rules.length) return true; if (isPristine.value) { return internalErrorMessages.value.length || validateOn.value.lazy ? null : true; } else { return !internalErrorMessages.value.length; } }); const isValidating = shallowRef(false); const validationClasses = computed(() => { return { [`${name}--error`]: isValid.value === false, [`${name}--dirty`]: isDirty.value, [`${name}--disabled`]: form.isDisabled.value, [`${name}--readonly`]: form.isReadonly.value }; }); const vm = getCurrentInstance('validation'); const uid = computed(() => props.name ?? unref(id)); // const resolvedRules = computed(() => props.rules.map(rule => { // let ruleName: string | null = null // let ruleParams: ValidationRuleParams = [undefined] // if (Array.isArray(rule)) { // ruleName = rule[0] // ruleParams = rule.slice(1) as ValidationRuleParams // } else if (typeof rule === 'string') { // ruleName = rule // } // if (ruleName !== null) { // if (ruleName.startsWith('$')) { // ruleName = ruleName.slice(1) // } // return rules?.[ruleName]?.(...ruleParams) // } else { // return rule // } // })) onBeforeMount(() => { form.register?.({ id: uid.value, vm, validate, reset, resetValidation }); }); onBeforeUnmount(() => { form.unregister?.(uid.value); }); onMounted(async () => { if (!validateOn.value.lazy) { await validate(!validateOn.value.eager); } form.update?.(uid.value, isValid.value, errorMessages.value); }); useToggleScope(() => validateOn.value.input || validateOn.value.invalidInput && isValid.value === false, () => { watch(validationModel, () => { if (validationModel.value != null) { validate(); } else if (props.focused) { const unwatch = watch(() => props.focused, val => { if (!val) validate(); unwatch(); }); } }); }); useToggleScope(() => validateOn.value.blur, () => { watch(() => props.focused, val => { if (!val) validate(); }); }); watch([isValid, errorMessages], () => { form.update?.(uid.value, isValid.value, errorMessages.value); }); async function reset() { model.value = null; await nextTick(); await resetValidation(); } async function resetValidation() { isPristine.value = true; if (!validateOn.value.lazy) { await validate(!validateOn.value.eager); } else { internalErrorMessages.value = []; } } async function validate() { let silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; const results = []; isValidating.value = true; for (const rule of props.rules) { if (results.length >= Number(props.maxErrors ?? 1)) { break; } const handler = typeof rule === 'function' ? rule : () => rule; const result = await handler(validationModel.value); if (result === true) continue; if (result !== false && typeof result !== 'string') { // eslint-disable-next-line no-console console.warn(`${result} is not a valid value. Rule functions must return boolean true or a string.`); continue; } results.push(result || ''); } internalErrorMessages.value = results; isValidating.value = false; isPristine.value = silent; return internalErrorMessages.value; } return { errorMessages, isDirty, isDisabled: form.isDisabled, isReadonly: form.isReadonly, isPristine, isValid, isValidating, reset, resetValidation, validate, validationClasses }; } //# sourceMappingURL=validation.js.map