fix: warning in console
This commit is contained in:
parent
d674549b67
commit
f255983693
|
|
@ -2,7 +2,7 @@
|
||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
import { computed, provide } from 'vue'
|
import { computed, provide } from 'vue'
|
||||||
import { PlusIcon, TrashIcon } from 'lucide-vue-next'
|
import { PlusIcon, TrashIcon } from 'lucide-vue-next'
|
||||||
import { FieldArray, FieldContextKey, useField, useFieldArray } from 'vee-validate'
|
import { FieldArray, FieldContextKey, useField } from 'vee-validate'
|
||||||
import type { Config, ConfigItem } from './interface'
|
import type { Config, ConfigItem } from './interface'
|
||||||
import { beautifyObjectName, getBaseType } from './utils'
|
import { beautifyObjectName, getBaseType } from './utils'
|
||||||
import AutoFormField from './AutoFormField.vue'
|
import AutoFormField from './AutoFormField.vue'
|
||||||
|
|
@ -10,7 +10,7 @@ import AutoFormLabel from './AutoFormLabel.vue'
|
||||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'
|
||||||
import { Button } from '@/lib/registry/default/ui/button'
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
import { Separator } from '@/lib/registry/default/ui/separator'
|
import { Separator } from '@/lib/registry/default/ui/separator'
|
||||||
import { FormMessage } from '@/lib/registry/default/ui/form'
|
import { FormItem, FormMessage } from '@/lib/registry/default/ui/form'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
fieldName: string
|
fieldName: string
|
||||||
|
|
@ -58,51 +58,53 @@ provide(FieldContextKey, fieldContext)
|
||||||
<template>
|
<template>
|
||||||
<FieldArray v-slot="{ fields, remove, push }" as="section" :name="fieldName">
|
<FieldArray v-slot="{ fields, remove, push }" as="section" :name="fieldName">
|
||||||
<slot v-bind="props">
|
<slot v-bind="props">
|
||||||
<Accordion type="multiple" class="w-full" collapsible :disabled="disabled">
|
<Accordion type="multiple" class="w-full" collapsible :disabled="disabled" as-child>
|
||||||
<AccordionItem :value="fieldName" class="border-none">
|
<FormItem>
|
||||||
<AccordionTrigger>
|
<AccordionItem :value="fieldName" class="border-none">
|
||||||
<AutoFormLabel class="text-base" :required="required">
|
<AccordionTrigger>
|
||||||
{{ schema?.description || beautifyObjectName(fieldName) }}
|
<AutoFormLabel class="text-base" :required="required">
|
||||||
</AutoFormLabel>
|
{{ schema?.description || beautifyObjectName(fieldName) }}
|
||||||
</AccordionTrigger>
|
</AutoFormLabel>
|
||||||
|
</AccordionTrigger>
|
||||||
|
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
<template v-for="(field, index) of fields" :key="field.key">
|
<template v-for="(field, index) of fields" :key="field.key">
|
||||||
<div class="mb-4 p-1">
|
<div class="mb-4 p-1">
|
||||||
<AutoFormField
|
<AutoFormField
|
||||||
:field-name="`${fieldName}[${index}]`"
|
:field-name="`${fieldName}[${index}]`"
|
||||||
:label="fieldName"
|
:label="fieldName"
|
||||||
:shape="itemShape!"
|
:shape="itemShape!"
|
||||||
:config="config as ConfigItem"
|
:config="config as ConfigItem"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="!my-4 flex justify-end">
|
<div class="!my-4 flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
size="icon"
|
size="icon"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
@click="remove(index)"
|
@click="remove(index)"
|
||||||
>
|
>
|
||||||
<TrashIcon :size="16" />
|
<TrashIcon :size="16" />
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Separator v-if="!field.isLast" />
|
||||||
</div>
|
</div>
|
||||||
<Separator v-if="!field.isLast" />
|
</template>
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
class="mt-4 flex items-center"
|
class="mt-4 flex items-center"
|
||||||
@click="push(null)"
|
@click="push(null)"
|
||||||
>
|
>
|
||||||
<PlusIcon class="mr-2" :size="16" />
|
<PlusIcon class="mr-2" :size="16" />
|
||||||
Add
|
Add
|
||||||
</Button>
|
</Button>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
|
</FormItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</slot>
|
</slot>
|
||||||
</FieldArray>
|
</FieldArray>
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,46 @@
|
||||||
import type * as z from 'zod'
|
import type * as z from 'zod'
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import { computed, inject, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { FieldContextKey, FormContextKey } from 'vee-validate'
|
import { useFieldValue, useFormValues } from 'vee-validate'
|
||||||
import { createContext } from 'radix-vue'
|
import { createContext } from 'radix-vue'
|
||||||
import { type Dependency, DependencyType, type EnumValues } from './interface'
|
import { type Dependency, DependencyType, type EnumValues } from './interface'
|
||||||
import { getIndexIfArray } from './utils'
|
import { getFromPath, getIndexIfArray } from './utils'
|
||||||
|
|
||||||
export const [injectDependencies, provideDependencies] = createContext<Ref<Dependency<z.infer<z.ZodObject<any>>>[] | undefined>>('AutoFormDependencies')
|
export const [injectDependencies, provideDependencies] = createContext<Ref<Dependency<z.infer<z.ZodObject<any>>>[] | undefined>>('AutoFormDependencies')
|
||||||
|
|
||||||
function getValueByPath<T extends Record<string, any>>(obj: T, path: string): any {
|
|
||||||
const keys = path.split('.')
|
|
||||||
let value = obj
|
|
||||||
|
|
||||||
for (const key of keys) {
|
|
||||||
if (value && typeof value === 'object' && key in value)
|
|
||||||
value = value[key]
|
|
||||||
else
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function useDependencies(
|
export default function useDependencies(
|
||||||
fieldName: string,
|
fieldName: string,
|
||||||
) {
|
) {
|
||||||
const form = inject(FormContextKey)
|
const form = useFormValues()
|
||||||
const field = inject(FieldContextKey)
|
// parsed test[0].age => test.age
|
||||||
|
|
||||||
const currentFieldName = fieldName.replace(/\[\d+\]/g, '')
|
const currentFieldName = fieldName.replace(/\[\d+\]/g, '')
|
||||||
|
const currentFieldValue = useFieldValue<any>(fieldName)
|
||||||
|
|
||||||
if (!form)
|
if (!form)
|
||||||
throw new Error('useDependencies should be used within <AutoForm>')
|
throw new Error('useDependencies should be used within <AutoForm>')
|
||||||
|
|
||||||
const { controlledValues } = form
|
|
||||||
const dependencies = injectDependencies()
|
const dependencies = injectDependencies()
|
||||||
const isDisabled = ref(false)
|
const isDisabled = ref(false)
|
||||||
const isHidden = ref(false)
|
const isHidden = ref(false)
|
||||||
const isRequired = ref(false)
|
const isRequired = ref(false)
|
||||||
const overrideOptions = ref<EnumValues | undefined>()
|
const overrideOptions = ref<EnumValues | undefined>()
|
||||||
|
|
||||||
const currentFieldValue = computed(() => field?.value.value)
|
|
||||||
const currentFieldDependencies = computed(() => dependencies.value?.filter(
|
const currentFieldDependencies = computed(() => dependencies.value?.filter(
|
||||||
dependency => dependency.targetField === currentFieldName,
|
dependency => dependency.targetField === currentFieldName,
|
||||||
))
|
))
|
||||||
|
|
||||||
function getSourceValue(dep: Dependency<any>) {
|
function getSourceValue(dep: Dependency<any>) {
|
||||||
const source = dep.sourceField as string
|
const source = dep.sourceField as string
|
||||||
const lastKey = source.split('.').at(-1)
|
const index = getIndexIfArray(fieldName) ?? -1
|
||||||
if (source.includes('.') && lastKey) {
|
const [sourceLast, ...sourceInitial] = source.split('.').toReversed()
|
||||||
if (Array.isArray(field?.value.value)) {
|
const [_targetLast, ...targetInitial] = (dep.targetField as string).split('.').toReversed()
|
||||||
const index = getIndexIfArray(fieldName) ?? -1
|
|
||||||
return field?.value.value[index][lastKey]
|
|
||||||
}
|
|
||||||
|
|
||||||
return getValueByPath(form!.values, source)
|
if (index >= 0 && sourceInitial.join(',') === targetInitial.join(',')) {
|
||||||
|
const [_currentLast, ...currentInitial] = fieldName.split('.').toReversed()
|
||||||
|
return getFromPath(form.value, currentInitial.join('.') + sourceLast)
|
||||||
}
|
}
|
||||||
|
|
||||||
return controlledValues.value[source as string]
|
return getFromPath(form.value, source)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceFieldValues = computed(() => currentFieldDependencies.value?.map(dep => getSourceValue(dep)))
|
const sourceFieldValues = computed(() => currentFieldDependencies.value?.map(dep => getSourceValue(dep)))
|
||||||
|
|
@ -73,7 +56,6 @@ export default function useDependencies(
|
||||||
resetConditionState()
|
resetConditionState()
|
||||||
currentFieldDependencies.value?.forEach((dep) => {
|
currentFieldDependencies.value?.forEach((dep) => {
|
||||||
const sourceValue = getSourceValue(dep)
|
const sourceValue = getSourceValue(dep)
|
||||||
|
|
||||||
const conditionMet = dep.when(sourceValue, currentFieldValue.value)
|
const conditionMet = dep.when(sourceValue, currentFieldValue.value)
|
||||||
|
|
||||||
switch (dep.type) {
|
switch (dep.type) {
|
||||||
|
|
|
||||||
|
|
@ -92,3 +92,80 @@ export function getObjectFormSchema(
|
||||||
}
|
}
|
||||||
return schema as z.ZodObject<any, any>
|
return schema as z.ZodObject<any, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isIndex(value: unknown): value is number {
|
||||||
|
return Number(value) >= 0
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Constructs a path with dot paths for arrays to use brackets to be compatible with vee-validate path syntax
|
||||||
|
*/
|
||||||
|
export function normalizeFormPath(path: string): string {
|
||||||
|
const pathArr = path.split('.')
|
||||||
|
if (!pathArr.length)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
let fullPath = String(pathArr[0])
|
||||||
|
for (let i = 1; i < pathArr.length; i++) {
|
||||||
|
if (isIndex(pathArr[i])) {
|
||||||
|
fullPath += `[${pathArr[i]}]`
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath += `.${pathArr[i]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullPath
|
||||||
|
}
|
||||||
|
|
||||||
|
type NestedRecord = Record<string, unknown> | { [k: string]: NestedRecord }
|
||||||
|
/**
|
||||||
|
* Checks if the path opted out of nested fields using `[fieldName]` syntax
|
||||||
|
*/
|
||||||
|
export function isNotNestedPath(path: string) {
|
||||||
|
return /^\[.+\]$/i.test(path)
|
||||||
|
}
|
||||||
|
function isObject(obj: unknown): obj is Record<string, unknown> {
|
||||||
|
return obj !== null && !!obj && typeof obj === 'object' && !Array.isArray(obj)
|
||||||
|
}
|
||||||
|
function isContainerValue(value: unknown): value is Record<string, unknown> {
|
||||||
|
return isObject(value) || Array.isArray(value)
|
||||||
|
}
|
||||||
|
function cleanupNonNestedPath(path: string) {
|
||||||
|
if (isNotNestedPath(path))
|
||||||
|
return path.replace(/\[|\]/gi, '')
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a nested property value from an object
|
||||||
|
*/
|
||||||
|
export function getFromPath<TValue = unknown>(object: NestedRecord | undefined, path: string): TValue | undefined
|
||||||
|
export function getFromPath<TValue = unknown, TFallback = TValue>(
|
||||||
|
object: NestedRecord | undefined,
|
||||||
|
path: string,
|
||||||
|
fallback?: TFallback,
|
||||||
|
): TValue | TFallback
|
||||||
|
export function getFromPath<TValue = unknown, TFallback = TValue>(
|
||||||
|
object: NestedRecord | undefined,
|
||||||
|
path: string,
|
||||||
|
fallback?: TFallback,
|
||||||
|
): TValue | TFallback | undefined {
|
||||||
|
if (!object)
|
||||||
|
return fallback
|
||||||
|
|
||||||
|
if (isNotNestedPath(path))
|
||||||
|
return object[cleanupNonNestedPath(path)] as TValue | undefined
|
||||||
|
|
||||||
|
const resolvedValue = (path || '')
|
||||||
|
.split(/\.|\[(\d+)\]/)
|
||||||
|
.filter(Boolean)
|
||||||
|
.reduce((acc, propKey) => {
|
||||||
|
if (isContainerValue(acc) && propKey in acc)
|
||||||
|
return acc[propKey]
|
||||||
|
|
||||||
|
return fallback
|
||||||
|
}, object as unknown)
|
||||||
|
|
||||||
|
return resolvedValue as TValue | undefined
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<script setup lang="ts" generic="T extends z.ZodAny">
|
<script setup lang="ts" generic="T extends z.ZodAny">
|
||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
import { computed, provide } from 'vue'
|
import { computed, provide } from 'vue'
|
||||||
import { PlusIcon, TrashIcon } from '@radix-icons/vue'
|
import { PlusIcon, TrashIcon } from 'lucide-vue-next'
|
||||||
import { FieldArray, FieldContextKey, useField, useFieldArray } from 'vee-validate'
|
import { FieldArray, FieldContextKey, useField } from 'vee-validate'
|
||||||
import type { Config, ConfigItem } from './interface'
|
import type { Config, ConfigItem } from './interface'
|
||||||
import { beautifyObjectName, getBaseType } from './utils'
|
import { beautifyObjectName, getBaseType } from './utils'
|
||||||
import AutoFormField from './AutoFormField.vue'
|
import AutoFormField from './AutoFormField.vue'
|
||||||
|
|
@ -10,7 +10,7 @@ import AutoFormLabel from './AutoFormLabel.vue'
|
||||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/new-york/ui/accordion'
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/new-york/ui/accordion'
|
||||||
import { Button } from '@/lib/registry/new-york/ui/button'
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
import { Separator } from '@/lib/registry/new-york/ui/separator'
|
import { Separator } from '@/lib/registry/new-york/ui/separator'
|
||||||
import { FormMessage } from '@/lib/registry/new-york/ui/form'
|
import { FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
fieldName: string
|
fieldName: string
|
||||||
|
|
@ -58,51 +58,53 @@ provide(FieldContextKey, fieldContext)
|
||||||
<template>
|
<template>
|
||||||
<FieldArray v-slot="{ fields, remove, push }" as="section" :name="fieldName">
|
<FieldArray v-slot="{ fields, remove, push }" as="section" :name="fieldName">
|
||||||
<slot v-bind="props">
|
<slot v-bind="props">
|
||||||
<Accordion type="multiple" class="w-full" collapsible :disabled="disabled">
|
<Accordion type="multiple" class="w-full" collapsible :disabled="disabled" as-child>
|
||||||
<AccordionItem :value="fieldName" class="border-none">
|
<FormItem>
|
||||||
<AccordionTrigger>
|
<AccordionItem :value="fieldName" class="border-none">
|
||||||
<AutoFormLabel class="text-base" :required="required">
|
<AccordionTrigger>
|
||||||
{{ schema?.description || beautifyObjectName(fieldName) }}
|
<AutoFormLabel class="text-base" :required="required">
|
||||||
</AutoFormLabel>
|
{{ schema?.description || beautifyObjectName(fieldName) }}
|
||||||
</AccordionTrigger>
|
</AutoFormLabel>
|
||||||
|
</AccordionTrigger>
|
||||||
|
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
<template v-for="(field, index) of fields" :key="field.key">
|
<template v-for="(field, index) of fields" :key="field.key">
|
||||||
<div class="mb-4 p-[1px]">
|
<div class="mb-4 p-1">
|
||||||
<AutoFormField
|
<AutoFormField
|
||||||
:field-name="`${fieldName}[${index}]`"
|
:field-name="`${fieldName}[${index}]`"
|
||||||
:label="fieldName"
|
:label="fieldName"
|
||||||
:shape="itemShape!"
|
:shape="itemShape!"
|
||||||
:config="config as ConfigItem"
|
:config="config as ConfigItem"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="!my-4 flex justify-end">
|
<div class="!my-4 flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
size="icon"
|
size="icon"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
@click="remove(index)"
|
@click="remove(index)"
|
||||||
>
|
>
|
||||||
<TrashIcon />
|
<TrashIcon :size="16" />
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Separator v-if="!field.isLast" />
|
||||||
</div>
|
</div>
|
||||||
<Separator v-if="!field.isLast" />
|
</template>
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
class="mt-4 flex items-center"
|
class="mt-4 flex items-center"
|
||||||
@click="push(null)"
|
@click="push(null)"
|
||||||
>
|
>
|
||||||
<PlusIcon class="mr-2" />
|
<PlusIcon class="mr-2" :size="16" />
|
||||||
Add
|
Add
|
||||||
</Button>
|
</Button>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
|
</FormItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</slot>
|
</slot>
|
||||||
</FieldArray>
|
</FieldArray>
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,46 @@
|
||||||
import type * as z from 'zod'
|
import type * as z from 'zod'
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import { computed, inject, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { FieldContextKey, FormContextKey } from 'vee-validate'
|
import { useFieldValue, useFormValues } from 'vee-validate'
|
||||||
import { createContext } from 'radix-vue'
|
import { createContext } from 'radix-vue'
|
||||||
import { type Dependency, DependencyType, type EnumValues } from './interface'
|
import { type Dependency, DependencyType, type EnumValues } from './interface'
|
||||||
import { getIndexIfArray } from './utils'
|
import { getFromPath, getIndexIfArray } from './utils'
|
||||||
|
|
||||||
export const [injectDependencies, provideDependencies] = createContext<Ref<Dependency<z.infer<z.ZodObject<any>>>[] | undefined>>('AutoFormDependencies')
|
export const [injectDependencies, provideDependencies] = createContext<Ref<Dependency<z.infer<z.ZodObject<any>>>[] | undefined>>('AutoFormDependencies')
|
||||||
|
|
||||||
function getValueByPath<T extends Record<string, any>>(obj: T, path: string): any {
|
|
||||||
const keys = path.split('.')
|
|
||||||
let value = obj
|
|
||||||
|
|
||||||
for (const key of keys) {
|
|
||||||
if (value && typeof value === 'object' && key in value)
|
|
||||||
value = value[key]
|
|
||||||
else
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function useDependencies(
|
export default function useDependencies(
|
||||||
fieldName: string,
|
fieldName: string,
|
||||||
) {
|
) {
|
||||||
const form = inject(FormContextKey)
|
const form = useFormValues()
|
||||||
const field = inject(FieldContextKey)
|
// parsed test[0].age => test.age
|
||||||
|
|
||||||
const currentFieldName = fieldName.replace(/\[\d+\]/g, '')
|
const currentFieldName = fieldName.replace(/\[\d+\]/g, '')
|
||||||
|
const currentFieldValue = useFieldValue<any>(fieldName)
|
||||||
|
|
||||||
if (!form)
|
if (!form)
|
||||||
throw new Error('useDependencies should be used within <AutoForm>')
|
throw new Error('useDependencies should be used within <AutoForm>')
|
||||||
|
|
||||||
const { controlledValues } = form
|
|
||||||
const dependencies = injectDependencies()
|
const dependencies = injectDependencies()
|
||||||
const isDisabled = ref(false)
|
const isDisabled = ref(false)
|
||||||
const isHidden = ref(false)
|
const isHidden = ref(false)
|
||||||
const isRequired = ref(false)
|
const isRequired = ref(false)
|
||||||
const overrideOptions = ref<EnumValues | undefined>()
|
const overrideOptions = ref<EnumValues | undefined>()
|
||||||
|
|
||||||
const currentFieldValue = computed(() => field?.value.value)
|
|
||||||
const currentFieldDependencies = computed(() => dependencies.value?.filter(
|
const currentFieldDependencies = computed(() => dependencies.value?.filter(
|
||||||
dependency => dependency.targetField === currentFieldName,
|
dependency => dependency.targetField === currentFieldName,
|
||||||
))
|
))
|
||||||
|
|
||||||
function getSourceValue(dep: Dependency<any>) {
|
function getSourceValue(dep: Dependency<any>) {
|
||||||
const source = dep.sourceField as string
|
const source = dep.sourceField as string
|
||||||
const lastKey = source.split('.').at(-1)
|
const index = getIndexIfArray(fieldName) ?? -1
|
||||||
if (source.includes('.') && lastKey) {
|
const [sourceLast, ...sourceInitial] = source.split('.').toReversed()
|
||||||
if (Array.isArray(field?.value.value)) {
|
const [_targetLast, ...targetInitial] = (dep.targetField as string).split('.').toReversed()
|
||||||
const index = getIndexIfArray(fieldName) ?? -1
|
|
||||||
return field?.value.value[index][lastKey]
|
|
||||||
}
|
|
||||||
|
|
||||||
return getValueByPath(form!.values, source)
|
if (index >= 0 && sourceInitial.join(',') === targetInitial.join(',')) {
|
||||||
|
const [_currentLast, ...currentInitial] = fieldName.split('.').toReversed()
|
||||||
|
return getFromPath(form.value, currentInitial.join('.') + sourceLast)
|
||||||
}
|
}
|
||||||
|
|
||||||
return controlledValues.value[source as string]
|
return getFromPath(form.value, source)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceFieldValues = computed(() => currentFieldDependencies.value?.map(dep => getSourceValue(dep)))
|
const sourceFieldValues = computed(() => currentFieldDependencies.value?.map(dep => getSourceValue(dep)))
|
||||||
|
|
@ -73,7 +56,6 @@ export default function useDependencies(
|
||||||
resetConditionState()
|
resetConditionState()
|
||||||
currentFieldDependencies.value?.forEach((dep) => {
|
currentFieldDependencies.value?.forEach((dep) => {
|
||||||
const sourceValue = getSourceValue(dep)
|
const sourceValue = getSourceValue(dep)
|
||||||
|
|
||||||
const conditionMet = dep.when(sourceValue, currentFieldValue.value)
|
const conditionMet = dep.when(sourceValue, currentFieldValue.value)
|
||||||
|
|
||||||
switch (dep.type) {
|
switch (dep.type) {
|
||||||
|
|
|
||||||
|
|
@ -92,3 +92,80 @@ export function getObjectFormSchema(
|
||||||
}
|
}
|
||||||
return schema as z.ZodObject<any, any>
|
return schema as z.ZodObject<any, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isIndex(value: unknown): value is number {
|
||||||
|
return Number(value) >= 0
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Constructs a path with dot paths for arrays to use brackets to be compatible with vee-validate path syntax
|
||||||
|
*/
|
||||||
|
export function normalizeFormPath(path: string): string {
|
||||||
|
const pathArr = path.split('.')
|
||||||
|
if (!pathArr.length)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
let fullPath = String(pathArr[0])
|
||||||
|
for (let i = 1; i < pathArr.length; i++) {
|
||||||
|
if (isIndex(pathArr[i])) {
|
||||||
|
fullPath += `[${pathArr[i]}]`
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath += `.${pathArr[i]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullPath
|
||||||
|
}
|
||||||
|
|
||||||
|
type NestedRecord = Record<string, unknown> | { [k: string]: NestedRecord }
|
||||||
|
/**
|
||||||
|
* Checks if the path opted out of nested fields using `[fieldName]` syntax
|
||||||
|
*/
|
||||||
|
export function isNotNestedPath(path: string) {
|
||||||
|
return /^\[.+\]$/i.test(path)
|
||||||
|
}
|
||||||
|
function isObject(obj: unknown): obj is Record<string, unknown> {
|
||||||
|
return obj !== null && !!obj && typeof obj === 'object' && !Array.isArray(obj)
|
||||||
|
}
|
||||||
|
function isContainerValue(value: unknown): value is Record<string, unknown> {
|
||||||
|
return isObject(value) || Array.isArray(value)
|
||||||
|
}
|
||||||
|
function cleanupNonNestedPath(path: string) {
|
||||||
|
if (isNotNestedPath(path))
|
||||||
|
return path.replace(/\[|\]/gi, '')
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a nested property value from an object
|
||||||
|
*/
|
||||||
|
export function getFromPath<TValue = unknown>(object: NestedRecord | undefined, path: string): TValue | undefined
|
||||||
|
export function getFromPath<TValue = unknown, TFallback = TValue>(
|
||||||
|
object: NestedRecord | undefined,
|
||||||
|
path: string,
|
||||||
|
fallback?: TFallback,
|
||||||
|
): TValue | TFallback
|
||||||
|
export function getFromPath<TValue = unknown, TFallback = TValue>(
|
||||||
|
object: NestedRecord | undefined,
|
||||||
|
path: string,
|
||||||
|
fallback?: TFallback,
|
||||||
|
): TValue | TFallback | undefined {
|
||||||
|
if (!object)
|
||||||
|
return fallback
|
||||||
|
|
||||||
|
if (isNotNestedPath(path))
|
||||||
|
return object[cleanupNonNestedPath(path)] as TValue | undefined
|
||||||
|
|
||||||
|
const resolvedValue = (path || '')
|
||||||
|
.split(/\.|\[(\d+)\]/)
|
||||||
|
.filter(Boolean)
|
||||||
|
.reduce((acc, propKey) => {
|
||||||
|
if (isContainerValue(acc) && propKey in acc)
|
||||||
|
return acc[propKey]
|
||||||
|
|
||||||
|
return fallback
|
||||||
|
}, object as unknown)
|
||||||
|
|
||||||
|
return resolvedValue as TValue | undefined
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user