Files
better-auth/examples/nuxt-example/components/ui/auto-form/dependencies.ts
2024-09-27 13:36:20 +03:00

101 lines
2.8 KiB
TypeScript

import type * as z from "zod";
import type { Ref } from "vue";
import { computed, ref, watch } from "vue";
import { useFieldValue, useFormValues } from "vee-validate";
import { createContext } from "radix-vue";
import { type Dependency, DependencyType, type EnumValues } from "./interface";
import { getFromPath, getIndexIfArray } from "./utils";
export const [injectDependencies, provideDependencies] = createContext<
Ref<Dependency<z.infer<z.ZodObject<any>>>[] | undefined>
>("AutoFormDependencies");
export default function useDependencies(fieldName: string) {
const form = useFormValues();
// parsed test[0].age => test.age
const currentFieldName = fieldName.replace(/\[\d+\]/g, "");
const currentFieldValue = useFieldValue<any>(fieldName);
if (!form)
throw new Error("useDependencies should be used within <AutoForm>");
const dependencies = injectDependencies();
const isDisabled = ref(false);
const isHidden = ref(false);
const isRequired = ref(false);
const overrideOptions = ref<EnumValues | undefined>();
const currentFieldDependencies = computed(() =>
dependencies.value?.filter(
(dependency) => dependency.targetField === currentFieldName,
),
);
function getSourceValue(dep: Dependency<any>) {
const source = dep.sourceField as string;
const index = getIndexIfArray(fieldName) ?? -1;
const [sourceLast, ...sourceInitial] = source.split(".").toReversed();
const [_targetLast, ...targetInitial] = (dep.targetField as string)
.split(".")
.toReversed();
if (index >= 0 && sourceInitial.join(",") === targetInitial.join(",")) {
const [_currentLast, ...currentInitial] = fieldName
.split(".")
.toReversed();
return getFromPath(form.value, currentInitial.join(".") + sourceLast);
}
return getFromPath(form.value, source);
}
const sourceFieldValues = computed(() =>
currentFieldDependencies.value?.map((dep) => getSourceValue(dep)),
);
const resetConditionState = () => {
isDisabled.value = false;
isHidden.value = false;
isRequired.value = false;
overrideOptions.value = undefined;
};
watch(
[sourceFieldValues, dependencies],
() => {
resetConditionState();
currentFieldDependencies.value?.forEach((dep) => {
const sourceValue = getSourceValue(dep);
const conditionMet = dep.when(sourceValue, currentFieldValue.value);
switch (dep.type) {
case DependencyType.DISABLES:
if (conditionMet) isDisabled.value = true;
break;
case DependencyType.REQUIRES:
if (conditionMet) isRequired.value = true;
break;
case DependencyType.HIDES:
if (conditionMet) isHidden.value = true;
break;
case DependencyType.SETS_OPTIONS:
if (conditionMet) overrideOptions.value = dep.options;
break;
}
});
},
{ immediate: true, deep: true },
);
return {
isDisabled,
isHidden,
isRequired,
overrideOptions,
};
}