UNPKG

@adbros/vue-validation

Version:

Composable for schema-based form validation in Vue 3 using Valibot

104 lines (103 loc) 3.83 kB
// src/validation.ts import { ref, computed, watch, toValue } from "vue"; import { omit } from "radash"; import { safeParseAsync, flatten } from "valibot"; var getFirstOfArray = (value) => Array.isArray(value) ? value[0] ?? null : value ?? null; var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value); var getKeysFromObject = (obj, prefix = "") => { if (!isRecord(obj)) { throw new Error("getKeysFromObject: argument must be an object"); } return Object.entries(obj).reduce((keys, [key, value]) => { const fullKey = prefix ? `${prefix}.${key}` : key; return [ ...keys, fullKey, ...isRecord(value) ? getKeysFromObject(value, fullKey) : [], ...Array.isArray(value) && value.length > 0 && isRecord(value[0]) ? value.reduce((arrKeys, item, index) => [...arrKeys, ...getKeysFromObject(item, `${fullKey}.${index}`)], []) : [] ]; }, []); }; var useValidationKey = (obj) => getKeysFromObject(obj); var useValidation = (schema, data) => { const silentErrors = ref(); const output = ref(); const dirtyFields = ref([]); const validDirtyFields = ref([]); const customErrors = ref({}); const errors = computed(() => { const valibotErrors = Object.fromEntries(Object.entries(silentErrors.value?.nested ?? {}).filter(([key]) => isDirty(key) && !isDirtyAndValid(key)).map(([key, value]) => [key, String(getFirstOfArray(value))])); return { ...valibotErrors, ...customErrors.value }; }); const makeFieldDirty = (name) => { dirtyFields.value = [.../* @__PURE__ */ new Set([...dirtyFields.value, name])]; validDirtyFields.value = hasAnyError(name) ? validDirtyFields.value.filter((field) => field !== name) : [.../* @__PURE__ */ new Set([...validDirtyFields.value, name])]; }; const cleanField = (name) => { dirtyFields.value = dirtyFields.value.filter((field) => field !== name); validDirtyFields.value = validDirtyFields.value.filter((field) => field !== name); }; const makeFormDirty = () => { dirtyFields.value = getKeysFromObject(toValue(data)); validDirtyFields.value = dirtyFields.value.filter((field) => !hasAnyError(field)); }; const cleanForm = () => { dirtyFields.value = []; validDirtyFields.value = []; customErrors.value = {}; }; const setCustomError = (field, message) => { customErrors.value = { ...customErrors.value, [field]: message }; }; const clearCustomError = (field) => { customErrors.value = omit(customErrors.value, [field]); }; const clearAllCustomErrors = () => { customErrors.value = {}; }; const hasAnyError = (name) => Object.keys(silentErrors.value?.nested ?? {}).includes(name); const isDirty = (name) => dirtyFields.value.includes(name); const isDirtyAndValid = (name) => validDirtyFields.value.includes(name); const isFormValid = computed(() => silentErrors.value === void 0); const handleSubmit = async (onSubmit, onError) => { makeFormDirty(); await validate(); return isFormValid.value ? onSubmit(output.value) : onError != null ? onError() : void 0; }; const validate = async () => { const result = await safeParseAsync(toValue(schema), toValue(data), { abortPipeEarly: true }); silentErrors.value = !result.success ? flatten(result.issues) : void 0; output.value = result.output; return result; }; watch([() => toValue(schema), () => toValue(data)], () => { validate(); }, { immediate: true, deep: true }); return { validate, errors, silentErrors, output, dirtyFields, validDirtyFields, makeFieldDirty, makeFormDirty, cleanField, cleanForm, setCustomError, clearCustomError, clearAllCustomErrors, isDirty, isFormValid, handleSubmit }; }; export { useValidation, useValidationKey };