@volverjs/form-vue
Version:
Vue 3 Forms with @volverjs/ui-vue
199 lines (193 loc) • 7.82 kB
text/typescript
import type { Component, DeepReadonly, InjectionKey, PropType, SlotsType, VNode } from 'vue'
import type {
FormSchema,
InjectedFormData,
FormTemplate,
RenderFunctionOutput,
InferFormattedError,
InferSchema,
} from './types'
import type { FormStatus } from './enums'
import { getProperty } from 'dot-prop'
import {
defineComponent,
h,
inject,
unref,
} from 'vue'
export function defineFormTemplate<Schema extends FormSchema, Type = undefined>(formProvideKey: InjectionKey<InjectedFormData<Schema, Type>>, VvFormField: Component) {
const VvFormTemplate = defineComponent({
name: 'VvFormTemplate',
props: {
schema: {
type: [Array, Function] as PropType<FormTemplate<Schema, Type>>,
required: true,
},
scope: {
type: Object as PropType<Record<string, unknown>>,
default: () => ({}),
},
},
slots: Object as SlotsType<{
default: {
errors?: DeepReadonly<InferFormattedError<Schema>>
formData?: undefined extends Type ? Partial<InferSchema<Schema>> : Type
invalid: boolean
status?: FormStatus
submit?: InjectedFormData<Schema, Type>['submit']
validate?: InjectedFormData<Schema, Type>['validate']
clear?: InjectedFormData<Schema, Type>['clear']
reset?: InjectedFormData<Schema, Type>['reset']
}
}>,
setup(templateProps, { slots: templateSlots }) {
const injectedFormData = inject(formProvideKey)
if (!injectedFormData?.formData)
return
return () => {
const normalizedSchema = typeof templateProps.schema === 'function'
? templateProps.schema(
injectedFormData,
templateProps.scope,
)
: templateProps.schema
let lastIf: boolean | undefined
const toReturn = normalizedSchema.reduce<
(VNode | VNode[] | undefined)[]
>((acc, field) => {
const normalizedField = typeof field === 'function'
? field(injectedFormData, templateProps.scope)
: field
const {
vvIs,
vvName,
vvSlots,
vvChildren,
vvIf,
vvElseIf,
vvType,
vvDefaultValue,
vvShowValid,
vvContent,
...props
} = normalizedField
// conditions
if (vvIf !== undefined) {
if (typeof vvIf === 'string') {
lastIf = Boolean(
getProperty(
new Object(injectedFormData.formData.value),
vvIf,
),
)
}
else if (typeof vvIf === 'function') {
lastIf = unref(vvIf(injectedFormData))
}
else {
lastIf = unref(vvIf)
}
if (!lastIf) {
return acc
}
}
else if (vvElseIf !== undefined && lastIf !== undefined) {
if (lastIf) {
return acc
}
if (typeof vvElseIf === 'string') {
lastIf = Boolean(
getProperty(
new Object(injectedFormData.formData.value),
vvElseIf,
),
)
}
else if (typeof vvElseIf === 'function') {
lastIf = unref(vvElseIf(injectedFormData))
}
else {
lastIf = unref(vvElseIf)
}
if (!lastIf) {
return acc
}
}
else {
lastIf = undefined
}
// children
let hChildren: RenderFunctionOutput | { default: (scope: Record<string, unknown>) => RenderFunctionOutput } | undefined
if (vvChildren) {
if (typeof vvIs === 'string') {
hChildren = h(VvFormTemplate, {
schema: vvChildren,
})
}
else {
hChildren = {
default: (scope: Record<string, unknown>) =>
h(VvFormTemplate, {
schema: vvChildren,
scope,
}),
}
}
}
// render
if (vvName) {
acc.push(
h(
VvFormField,
{
name: vvName,
is: vvIs,
type: vvType,
defaultValue: vvDefaultValue,
showValid: vvShowValid,
props,
},
vvSlots ?? hChildren ?? vvContent,
),
)
return acc
}
if (vvIs) {
acc.push(
h(
vvIs as Component,
props,
vvSlots ?? hChildren ?? vvContent,
),
)
return acc
}
if (hChildren) {
if ('default' in hChildren) {
acc.push(hChildren.default(templateProps.scope))
}
else {
acc.push(hChildren)
}
return acc
}
return acc
}, [])
toReturn.push(
templateSlots?.default?.({
errors: injectedFormData?.errors.value,
formData: injectedFormData?.formData.value,
invalid: injectedFormData?.invalid.value,
status: injectedFormData?.status.value,
submit: injectedFormData?.submit,
validate: injectedFormData?.validate,
clear: injectedFormData?.clear,
reset: injectedFormData?.reset,
}),
)
return toReturn
}
},
})
return VvFormTemplate
}