@adbros/vue-validation
Version:
Composable for schema-based form validation in Vue 3 using Valibot
104 lines (103 loc) • 3.83 kB
JavaScript
// 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
};