// Composables import { makeFocusProps } from "./focus.mjs"; import { useForm } from "./form.mjs"; import { useProxiedModel } from "./proxiedModel.mjs"; import { useToggleScope } from "./toggleScope.mjs"; // Utilities import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, ref, shallowRef, unref, watch } from 'vue'; import { getCurrentInstanceName, getUid, propsFactory, wrapInArray } from "../util/index.mjs"; // Types 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, 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] : getUid(); const model = useProxiedModel(props, 'modelValue'); const validationModel = computed(() => props.validationValue === undefined ? model.value : props.validationValue); const form = useForm(); 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 isDisabled = computed(() => !!(props.disabled ?? form?.isDisabled.value)); const isReadonly = computed(() => !!(props.readonly ?? form?.isReadonly.value)); const errorMessages = computed(() => { return props.errorMessages.length ? wrapInArray(props.errorMessages).slice(0, Math.max(0, +props.maxErrors)) : internalErrorMessages.value; }); const validateOn = computed(() => { let value = (props.validateOn ?? form?.validateOn.value) || 'input'; if (value === 'lazy') value = 'input lazy'; const set = new Set(value?.split(' ') ?? []); return { blur: set.has('blur') || set.has('input'), input: set.has('input'), submit: set.has('submit'), lazy: set.has('lazy') }; }); 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`]: isDisabled.value, [`${name}--readonly`]: isReadonly.value }; }); const uid = computed(() => props.name ?? unref(id)); onBeforeMount(() => { form?.register({ id: uid.value, validate, reset, resetValidation }); }); onBeforeUnmount(() => { form?.unregister(uid.value); }); onMounted(async () => { if (!validateOn.value.lazy) { await validate(true); } form?.update(uid.value, isValid.value, errorMessages.value); }); useToggleScope(() => validateOn.value.input, () => { 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, () => { form?.update(uid.value, isValid.value, errorMessages.value); }); function reset() { model.value = null; nextTick(resetValidation); } function resetValidation() { isPristine.value = true; if (!validateOn.value.lazy) { validate(true); } 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 >= +(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, isReadonly, isPristine, isValid, isValidating, reset, resetValidation, validate, validationClasses }; } //# sourceMappingURL=validation.mjs.map