chore: add examples
This commit is contained in:
parent
bdc4ccc5bf
commit
07a0b9a910
|
|
@ -38,12 +38,54 @@ export const Index = {
|
||||||
component: () => import("../src/lib/registry/default/example/AspectRatioDemo.vue").then((m) => m.default),
|
component: () => import("../src/lib/registry/default/example/AspectRatioDemo.vue").then((m) => m.default),
|
||||||
files: ["../src/lib/registry/default/example/AspectRatioDemo.vue"],
|
files: ["../src/lib/registry/default/example/AspectRatioDemo.vue"],
|
||||||
},
|
},
|
||||||
"AutoForm": {
|
"AutoFormArray": {
|
||||||
name: "AutoForm",
|
name: "AutoFormArray",
|
||||||
type: "components:example",
|
type: "components:example",
|
||||||
registryDependencies: ["button","toast","auto-form"],
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
component: () => import("../src/lib/registry/default/example/AutoForm.vue").then((m) => m.default),
|
component: () => import("../src/lib/registry/default/example/AutoFormArray.vue").then((m) => m.default),
|
||||||
files: ["../src/lib/registry/default/example/AutoForm.vue"],
|
files: ["../src/lib/registry/default/example/AutoFormArray.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormBasic": {
|
||||||
|
name: "AutoFormBasic",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/default/example/AutoFormBasic.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/default/example/AutoFormBasic.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormConfirmPassword": {
|
||||||
|
name: "AutoFormConfirmPassword",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/default/example/AutoFormConfirmPassword.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/default/example/AutoFormConfirmPassword.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormControlled": {
|
||||||
|
name: "AutoFormControlled",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/default/example/AutoFormControlled.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/default/example/AutoFormControlled.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormDependencies": {
|
||||||
|
name: "AutoFormDependencies",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/default/example/AutoFormDependencies.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/default/example/AutoFormDependencies.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormInputWithoutLabel": {
|
||||||
|
name: "AutoFormInputWithoutLabel",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/default/example/AutoFormInputWithoutLabel.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/default/example/AutoFormInputWithoutLabel.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormSubObject": {
|
||||||
|
name: "AutoFormSubObject",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/default/example/AutoFormSubObject.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/default/example/AutoFormSubObject.vue"],
|
||||||
},
|
},
|
||||||
"AvatarDemo": {
|
"AvatarDemo": {
|
||||||
name: "AvatarDemo",
|
name: "AvatarDemo",
|
||||||
|
|
@ -1285,12 +1327,54 @@ export const Index = {
|
||||||
component: () => import("../src/lib/registry/new-york/example/AspectRatioDemo.vue").then((m) => m.default),
|
component: () => import("../src/lib/registry/new-york/example/AspectRatioDemo.vue").then((m) => m.default),
|
||||||
files: ["../src/lib/registry/new-york/example/AspectRatioDemo.vue"],
|
files: ["../src/lib/registry/new-york/example/AspectRatioDemo.vue"],
|
||||||
},
|
},
|
||||||
"AutoForm": {
|
"AutoFormArray": {
|
||||||
name: "AutoForm",
|
name: "AutoFormArray",
|
||||||
type: "components:example",
|
type: "components:example",
|
||||||
registryDependencies: ["button","toast","auto-form"],
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
component: () => import("../src/lib/registry/new-york/example/AutoForm.vue").then((m) => m.default),
|
component: () => import("../src/lib/registry/new-york/example/AutoFormArray.vue").then((m) => m.default),
|
||||||
files: ["../src/lib/registry/new-york/example/AutoForm.vue"],
|
files: ["../src/lib/registry/new-york/example/AutoFormArray.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormBasic": {
|
||||||
|
name: "AutoFormBasic",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/new-york/example/AutoFormBasic.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/new-york/example/AutoFormBasic.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormConfirmPassword": {
|
||||||
|
name: "AutoFormConfirmPassword",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/new-york/example/AutoFormConfirmPassword.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/new-york/example/AutoFormConfirmPassword.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormControlled": {
|
||||||
|
name: "AutoFormControlled",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/new-york/example/AutoFormControlled.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/new-york/example/AutoFormControlled.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormDependencies": {
|
||||||
|
name: "AutoFormDependencies",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/new-york/example/AutoFormDependencies.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/new-york/example/AutoFormDependencies.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormInputWithoutLabel": {
|
||||||
|
name: "AutoFormInputWithoutLabel",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/new-york/example/AutoFormInputWithoutLabel.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/new-york/example/AutoFormInputWithoutLabel.vue"],
|
||||||
|
},
|
||||||
|
"AutoFormSubObject": {
|
||||||
|
name: "AutoFormSubObject",
|
||||||
|
type: "components:example",
|
||||||
|
registryDependencies: ["button","toast","auto-form"],
|
||||||
|
component: () => import("../src/lib/registry/new-york/example/AutoFormSubObject.vue").then((m) => m.default),
|
||||||
|
files: ["../src/lib/registry/new-york/example/AutoFormSubObject.vue"],
|
||||||
},
|
},
|
||||||
"AvatarDemo": {
|
"AvatarDemo": {
|
||||||
name: "AvatarDemo",
|
name: "AvatarDemo",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,39 @@
|
||||||
---
|
---
|
||||||
title: Auto Form
|
title: Auto Form
|
||||||
description: Building forms with VeeValidate and Zod.
|
description: Automatically generate a form from Zod schema.
|
||||||
primitive: https://vee-validate.logaretm.com/v4/guide/overview/
|
primitive: https://vee-validate.logaretm.com/v4/guide/overview/
|
||||||
---
|
---
|
||||||
|
|
||||||
<ComponentPreview name="AutoForm" />
|
### Basic
|
||||||
|
|
||||||
|
<ComponentPreview name="AutoFormBasic" />
|
||||||
|
|
||||||
|
### Input Without Label
|
||||||
|
This example shows how to use AutoForm input without label.
|
||||||
|
|
||||||
|
<ComponentPreview name="AutoFormInputWithoutLabel" />
|
||||||
|
|
||||||
|
### Sub Object
|
||||||
|
Automatically generate a form from a Zod schema.
|
||||||
|
|
||||||
|
<ComponentPreview name="AutoFormSubObject" />
|
||||||
|
|
||||||
|
### Controlled
|
||||||
|
This example shows how to use AutoForm in a controlled way.
|
||||||
|
|
||||||
|
<ComponentPreview name="AutoFormControlled" />
|
||||||
|
|
||||||
|
### Confirm Password
|
||||||
|
Refined schema to validate that two fields match.
|
||||||
|
|
||||||
|
<ComponentPreview name="AutoFormConfirmPassword" />
|
||||||
|
|
||||||
|
### Array support
|
||||||
|
You can use arrays in your schemas to create dynamic forms.
|
||||||
|
|
||||||
|
<ComponentPreview name="AutoFormArray" />
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
Create dependencies between fields.
|
||||||
|
|
||||||
|
<ComponentPreview name="AutoFormDependencies" />
|
||||||
|
|
|
||||||
38
apps/www/src/lib/registry/default/example/AutoFormArray.vue
Normal file
38
apps/www/src/lib/registry/default/example/AutoFormArray.vue
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/default/ui/toast'
|
||||||
|
import { AutoForm } from '@/lib/registry/default/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
guestListName: z.string(),
|
||||||
|
invitedGuests: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
name: z.string(),
|
||||||
|
age: z.coerce.number(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.describe('Guests invited to the party'),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/default/ui/toast'
|
||||||
|
import { AutoForm } from '@/lib/registry/default/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z
|
||||||
|
.object({
|
||||||
|
password: z.string(),
|
||||||
|
confirm: z.string(),
|
||||||
|
})
|
||||||
|
.refine(data => data.password === data.confirm, {
|
||||||
|
message: 'Passwords must match.',
|
||||||
|
path: ['confirm'],
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/default/ui/toast'
|
||||||
|
import { AutoForm } from '@/lib/registry/default/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
username: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
validationSchema: toTypedSchema(schema),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
:form="form"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { DependencyType } from '../ui/auto-form/interface'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/default/ui/toast'
|
||||||
|
import { AutoForm } from '@/lib/registry/default/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
age: z.number(),
|
||||||
|
parentsAllowed: z.boolean().optional(),
|
||||||
|
vegetarian: z.boolean().optional(),
|
||||||
|
mealOptions: z.enum(['Pasta', 'Salad', 'Beef Wellington']).optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
:field-config="{
|
||||||
|
age: {
|
||||||
|
description:
|
||||||
|
'Setting this below 18 will require parents consent.',
|
||||||
|
},
|
||||||
|
parentsAllowed: {
|
||||||
|
label: 'Did your parents allow you to register?',
|
||||||
|
},
|
||||||
|
vegetarian: {
|
||||||
|
label: 'Are you a vegetarian?',
|
||||||
|
description:
|
||||||
|
'Setting this to true will remove non-vegetarian food options.',
|
||||||
|
},
|
||||||
|
mealOptions: {
|
||||||
|
component: 'radio',
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
:dependencies="[
|
||||||
|
{
|
||||||
|
sourceField: 'age',
|
||||||
|
type: DependencyType.HIDES,
|
||||||
|
targetField: 'parentsAllowed',
|
||||||
|
when: (age) => age >= 18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceField: 'age',
|
||||||
|
type: DependencyType.REQUIRES,
|
||||||
|
targetField: 'parentsAllowed',
|
||||||
|
when: (age) => age < 18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceField: 'vegetarian',
|
||||||
|
type: DependencyType.SETS_OPTIONS,
|
||||||
|
targetField: 'mealOptions',
|
||||||
|
when: (vegetarian) => vegetarian,
|
||||||
|
options: ['Pasta', 'Salad'],
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/default/ui/toast'
|
||||||
|
import { AutoForm, AutoFormField } from '@/lib/registry/default/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
username: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
:field-config="{
|
||||||
|
username: {
|
||||||
|
hideLabel: true,
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<template #username="slotProps">
|
||||||
|
<div class="flex items-start gap-3">
|
||||||
|
<div class="flex-1">
|
||||||
|
<AutoFormField v-bind="slotProps" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button type="button">
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/default/ui/toast'
|
||||||
|
import { AutoForm, AutoFormField } from '@/lib/registry/default/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
subObject: z.object({
|
||||||
|
subField: z.string().optional().default('Sub Field'),
|
||||||
|
numberField: z.number().optional().default(1),
|
||||||
|
|
||||||
|
subSubObject: z
|
||||||
|
.object({
|
||||||
|
subSubField: z.string().default('Sub Sub Field'),
|
||||||
|
})
|
||||||
|
.describe('Sub Sub Object Description'),
|
||||||
|
}),
|
||||||
|
optionalSubObject: z
|
||||||
|
.object({
|
||||||
|
optionalSubField: z.string(),
|
||||||
|
otherOptionalSubField: z.string(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
:field-config="{
|
||||||
|
subObject: {
|
||||||
|
numberField: {
|
||||||
|
inputProps: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<script setup lang="ts" generic="U extends ZodRawShape, T extends ZodObject<U>">
|
<script setup lang="ts" generic="T extends ZodObjectOrWrapped">
|
||||||
import { computed, ref, toRef, toRefs } from 'vue'
|
import { computed, toRefs } from 'vue'
|
||||||
import type { ZodAny, ZodObject, ZodRawShape, z } from 'zod'
|
import type { ZodAny, z } from 'zod'
|
||||||
import { toTypedSchema } from '@vee-validate/zod'
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
import type { FormContext, GenericObject } from 'vee-validate'
|
import type { FormContext, GenericObject } from 'vee-validate'
|
||||||
import { getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'
|
import { type ZodObjectOrWrapped, getBaseSchema, getBaseType, getDefaultValueInZodStack, getObjectFormSchema } from './utils'
|
||||||
import type { Config, ConfigItem, Dependency, Shape } from './interface'
|
import type { Config, ConfigItem, Dependency, Shape } from './interface'
|
||||||
import AutoFormField from './AutoFormField.vue'
|
import AutoFormField from './AutoFormField.vue'
|
||||||
import { provideDependencies } from './dependencies'
|
import { provideDependencies } from './dependencies'
|
||||||
|
|
@ -26,7 +26,8 @@ provideDependencies(dependencies)
|
||||||
const shapes = computed(() => {
|
const shapes = computed(() => {
|
||||||
// @ts-expect-error ignore {} not assignable to object
|
// @ts-expect-error ignore {} not assignable to object
|
||||||
const val: { [key in keyof T]: Shape } = {}
|
const val: { [key in keyof T]: Shape } = {}
|
||||||
const shape = props.schema.shape
|
const baseSchema = getObjectFormSchema(props.schema)
|
||||||
|
const shape = baseSchema.shape
|
||||||
Object.keys(shape).forEach((name) => {
|
Object.keys(shape).forEach((name) => {
|
||||||
const item = shape[name] as ZodAny
|
const item = shape[name] as ZodAny
|
||||||
const baseItem = getBaseSchema(item) as ZodAny
|
const baseItem = getBaseSchema(item) as ZodAny
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ provide(FieldContextKey, fieldContext)
|
||||||
|
|
||||||
<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"
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ const shapes = computed(() => {
|
||||||
<AccordionTrigger class="text-base">
|
<AccordionTrigger class="text-base">
|
||||||
{{ schema?.description || beautifyObjectName(fieldName) }}
|
{{ schema?.description || beautifyObjectName(fieldName) }}
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent class="p-[1px] space-y-5">
|
<AccordionContent class="p-1 space-y-5">
|
||||||
<template v-for="(shape, key) in shapes" :key="key">
|
<template v-for="(shape, key) in shapes" :key="key">
|
||||||
<AutoFormField
|
<AutoFormField
|
||||||
:config="config?.[key as keyof typeof config] as ConfigItem"
|
:config="config?.[key as keyof typeof config] as ConfigItem"
|
||||||
|
|
|
||||||
38
apps/www/src/lib/registry/new-york/example/AutoFormArray.vue
Normal file
38
apps/www/src/lib/registry/new-york/example/AutoFormArray.vue
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/new-york/ui/toast'
|
||||||
|
import { AutoForm } from '@/lib/registry/new-york/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
guestListName: z.string(),
|
||||||
|
invitedGuests: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
name: z.string(),
|
||||||
|
age: z.coerce.number(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.describe('Guests invited to the party'),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/new-york/ui/toast'
|
||||||
|
import { AutoForm } from '@/lib/registry/new-york/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z
|
||||||
|
.object({
|
||||||
|
password: z.string(),
|
||||||
|
confirm: z.string(),
|
||||||
|
})
|
||||||
|
.refine(data => data.password === data.confirm, {
|
||||||
|
message: 'Passwords must match.',
|
||||||
|
path: ['confirm'],
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/new-york/ui/toast'
|
||||||
|
import { AutoForm } from '@/lib/registry/new-york/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
username: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
validationSchema: toTypedSchema(schema),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
:form="form"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { DependencyType } from '../ui/auto-form/interface'
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/new-york/ui/toast'
|
||||||
|
import { AutoForm } from '@/lib/registry/new-york/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
age: z.number(),
|
||||||
|
parentsAllowed: z.boolean().optional(),
|
||||||
|
vegetarian: z.boolean().optional(),
|
||||||
|
mealOptions: z.enum(['Pasta', 'Salad', 'Beef Wellington']).optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
:field-config="{
|
||||||
|
age: {
|
||||||
|
description:
|
||||||
|
'Setting this below 18 will require parents consent.',
|
||||||
|
},
|
||||||
|
parentsAllowed: {
|
||||||
|
label: 'Did your parents allow you to register?',
|
||||||
|
},
|
||||||
|
vegetarian: {
|
||||||
|
label: 'Are you a vegetarian?',
|
||||||
|
description:
|
||||||
|
'Setting this to true will remove non-vegetarian food options.',
|
||||||
|
},
|
||||||
|
mealOptions: {
|
||||||
|
component: 'radio',
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
:dependencies="[
|
||||||
|
{
|
||||||
|
sourceField: 'age',
|
||||||
|
type: DependencyType.HIDES,
|
||||||
|
targetField: 'parentsAllowed',
|
||||||
|
when: (age) => age >= 18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceField: 'age',
|
||||||
|
type: DependencyType.REQUIRES,
|
||||||
|
targetField: 'parentsAllowed',
|
||||||
|
when: (age) => age < 18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sourceField: 'vegetarian',
|
||||||
|
type: DependencyType.SETS_OPTIONS,
|
||||||
|
targetField: 'mealOptions',
|
||||||
|
when: (vegetarian) => vegetarian,
|
||||||
|
options: ['Pasta', 'Salad'],
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/new-york/ui/toast'
|
||||||
|
import { AutoForm, AutoFormField } from '@/lib/registry/new-york/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
username: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
:field-config="{
|
||||||
|
username: {
|
||||||
|
hideLabel: true,
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<template #username="slotProps">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="flex-1">
|
||||||
|
<AutoFormField v-bind="slotProps" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button type="button">
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as z from 'zod'
|
||||||
|
import { h } from 'vue'
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import { toast } from '@/lib/registry/new-york/ui/toast'
|
||||||
|
import { AutoForm, AutoFormField } from '@/lib/registry/new-york/ui/auto-form'
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
subObject: z.object({
|
||||||
|
subField: z.string().optional().default('Sub Field'),
|
||||||
|
numberField: z.number().optional().default(1),
|
||||||
|
|
||||||
|
subSubObject: z
|
||||||
|
.object({
|
||||||
|
subSubField: z.string().default('Sub Sub Field'),
|
||||||
|
})
|
||||||
|
.describe('Sub Sub Object Description'),
|
||||||
|
}),
|
||||||
|
optionalSubObject: z
|
||||||
|
.object({
|
||||||
|
optionalSubField: z.string(),
|
||||||
|
otherOptionalSubField: z.string(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(values: Record<string, any>) {
|
||||||
|
toast({
|
||||||
|
title: 'You submitted the following values:',
|
||||||
|
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AutoForm
|
||||||
|
class="w-2/3 space-y-6"
|
||||||
|
:schema="schema"
|
||||||
|
:field-config="{
|
||||||
|
subObject: {
|
||||||
|
numberField: {
|
||||||
|
inputProps: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</AutoForm>
|
||||||
|
</template>
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<script setup lang="ts" generic="U extends ZodRawShape, T extends ZodObject<U>">
|
<script setup lang="ts" generic="T extends ZodObjectOrWrapped">
|
||||||
import { computed, ref, toRef, toRefs } from 'vue'
|
import { computed, toRefs } from 'vue'
|
||||||
import type { ZodAny, ZodObject, ZodRawShape, z } from 'zod'
|
import type { ZodAny, z } from 'zod'
|
||||||
import { toTypedSchema } from '@vee-validate/zod'
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
import type { FormContext, GenericObject } from 'vee-validate'
|
import type { FormContext, GenericObject } from 'vee-validate'
|
||||||
import { getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'
|
import { type ZodObjectOrWrapped, getBaseSchema, getBaseType, getDefaultValueInZodStack, getObjectFormSchema } from './utils'
|
||||||
import type { Config, ConfigItem, Dependency, Shape } from './interface'
|
import type { Config, ConfigItem, Dependency, Shape } from './interface'
|
||||||
import AutoFormField from './AutoFormField.vue'
|
import AutoFormField from './AutoFormField.vue'
|
||||||
import { provideDependencies } from './dependencies'
|
import { provideDependencies } from './dependencies'
|
||||||
|
|
@ -26,7 +26,8 @@ provideDependencies(dependencies)
|
||||||
const shapes = computed(() => {
|
const shapes = computed(() => {
|
||||||
// @ts-expect-error ignore {} not assignable to object
|
// @ts-expect-error ignore {} not assignable to object
|
||||||
const val: { [key in keyof T]: Shape } = {}
|
const val: { [key in keyof T]: Shape } = {}
|
||||||
const shape = props.schema.shape
|
const baseSchema = getObjectFormSchema(props.schema)
|
||||||
|
const shape = baseSchema.shape
|
||||||
Object.keys(shape).forEach((name) => {
|
Object.keys(shape).forEach((name) => {
|
||||||
const item = shape[name] as ZodAny
|
const item = shape[name] as ZodAny
|
||||||
const baseItem = getBaseSchema(item) as ZodAny
|
const baseItem = getBaseSchema(item) as ZodAny
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
"files": [
|
"files": [
|
||||||
{
|
{
|
||||||
"name": "AutoForm.vue",
|
"name": "AutoForm.vue",
|
||||||
"content": "<script setup lang=\"ts\" generic=\"U extends ZodRawShape, T extends ZodObject<U>\">\nimport { computed, ref, toRef, toRefs } from 'vue'\nimport type { ZodAny, ZodObject, ZodRawShape, z } from 'zod'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport type { FormContext, GenericObject } from 'vee-validate'\nimport { getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'\nimport type { Config, ConfigItem, Dependency, Shape } from './interface'\nimport AutoFormField from './AutoFormField.vue'\nimport { provideDependencies } from './dependencies'\nimport { Form } from '@/lib/registry/default/ui/form'\n\nconst props = defineProps<{\n schema: T\n form?: FormContext<GenericObject>\n fieldConfig?: Config<z.infer<T>>\n dependencies?: Dependency<z.infer<T>>[]\n}>()\n\nconst emits = defineEmits<{\n submit: [event: GenericObject]\n}>()\n\nconst { dependencies } = toRefs(props)\nprovideDependencies(dependencies)\n\nconst shapes = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof T]: Shape } = {}\n const shape = props.schema.shape\n Object.keys(shape).forEach((name) => {\n const item = shape[name] as ZodAny\n const baseItem = getBaseSchema(item) as ZodAny\n let options = (baseItem && 'values' in baseItem._def) ? baseItem._def.values as string[] : undefined\n if (!Array.isArray(options) && typeof options === 'object')\n options = Object.values(options)\n\n val[name as keyof T] = {\n type: getBaseType(item),\n default: getDefaultValueInZodStack(item),\n options,\n required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),\n schema: baseItem,\n }\n })\n return val\n})\n\nconst fields = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof z.infer<T>]: { shape: Shape, fieldName: string, config: ConfigItem } } = {}\n for (const key in shapes.value) {\n const shape = shapes.value[key]\n val[key as keyof z.infer<T>] = {\n shape,\n config: props.fieldConfig?.[key] as ConfigItem,\n fieldName: key,\n }\n }\n return val\n})\n\nconst formComponent = computed(() => props.form ? 'form' : Form)\nconst formComponentProps = computed(() => {\n if (props.form) {\n return {\n onSubmit: props.form.handleSubmit(val => emits('submit', val)),\n }\n }\n else {\n const formSchema = toTypedSchema(props.schema)\n return {\n keepValues: true,\n validationSchema: formSchema,\n onSubmit: (val: GenericObject) => emits('submit', val),\n }\n }\n})\n</script>\n\n<template>\n <component\n :is=\"formComponent\"\n v-bind=\"formComponentProps\"\n >\n <slot name=\"customAutoForm\" :fields=\"fields\">\n <template v-for=\"(shape, key) of shapes\" :key=\"key\">\n <slot\n :shape=\"shape\"\n :name=\"key.toString() as keyof z.infer<T>\"\n :field-name=\"key.toString()\"\n :config=\"fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem\"\n >\n <AutoFormField\n :config=\"fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem\"\n :field-name=\"key.toString()\"\n :shape=\"shape\"\n />\n </slot>\n </template>\n </slot>\n\n <slot :shapes=\"shapes\" />\n </component>\n</template>\n"
|
"content": "<script setup lang=\"ts\" generic=\"T extends ZodObjectOrWrapped\">\nimport { computed, toRefs } from 'vue'\nimport type { ZodAny, z } from 'zod'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport type { FormContext, GenericObject } from 'vee-validate'\nimport { type ZodObjectOrWrapped, getBaseSchema, getBaseType, getDefaultValueInZodStack, getObjectFormSchema } from './utils'\nimport type { Config, ConfigItem, Dependency, Shape } from './interface'\nimport AutoFormField from './AutoFormField.vue'\nimport { provideDependencies } from './dependencies'\nimport { Form } from '@/lib/registry/default/ui/form'\n\nconst props = defineProps<{\n schema: T\n form?: FormContext<GenericObject>\n fieldConfig?: Config<z.infer<T>>\n dependencies?: Dependency<z.infer<T>>[]\n}>()\n\nconst emits = defineEmits<{\n submit: [event: GenericObject]\n}>()\n\nconst { dependencies } = toRefs(props)\nprovideDependencies(dependencies)\n\nconst shapes = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof T]: Shape } = {}\n const baseSchema = getObjectFormSchema(props.schema)\n const shape = baseSchema.shape\n Object.keys(shape).forEach((name) => {\n const item = shape[name] as ZodAny\n const baseItem = getBaseSchema(item) as ZodAny\n let options = (baseItem && 'values' in baseItem._def) ? baseItem._def.values as string[] : undefined\n if (!Array.isArray(options) && typeof options === 'object')\n options = Object.values(options)\n\n val[name as keyof T] = {\n type: getBaseType(item),\n default: getDefaultValueInZodStack(item),\n options,\n required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),\n schema: baseItem,\n }\n })\n return val\n})\n\nconst fields = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof z.infer<T>]: { shape: Shape, fieldName: string, config: ConfigItem } } = {}\n for (const key in shapes.value) {\n const shape = shapes.value[key]\n val[key as keyof z.infer<T>] = {\n shape,\n config: props.fieldConfig?.[key] as ConfigItem,\n fieldName: key,\n }\n }\n return val\n})\n\nconst formComponent = computed(() => props.form ? 'form' : Form)\nconst formComponentProps = computed(() => {\n if (props.form) {\n return {\n onSubmit: props.form.handleSubmit(val => emits('submit', val)),\n }\n }\n else {\n const formSchema = toTypedSchema(props.schema)\n return {\n keepValues: true,\n validationSchema: formSchema,\n onSubmit: (val: GenericObject) => emits('submit', val),\n }\n }\n})\n</script>\n\n<template>\n <component\n :is=\"formComponent\"\n v-bind=\"formComponentProps\"\n >\n <slot name=\"customAutoForm\" :fields=\"fields\">\n <template v-for=\"(shape, key) of shapes\" :key=\"key\">\n <slot\n :shape=\"shape\"\n :name=\"key.toString() as keyof z.infer<T>\"\n :field-name=\"key.toString()\"\n :config=\"fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem\"\n >\n <AutoFormField\n :config=\"fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem\"\n :field-name=\"key.toString()\"\n :shape=\"shape\"\n />\n </slot>\n </template>\n </slot>\n\n <slot :shapes=\"shapes\" />\n </component>\n</template>\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "AutoFormField.vue",
|
"name": "AutoFormField.vue",
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "AutoFormFieldArray.vue",
|
"name": "AutoFormFieldArray.vue",
|
||||||
"content": "<script setup lang=\"ts\" generic=\"T extends z.ZodAny\">\nimport * as z from 'zod'\nimport { computed, provide } from 'vue'\nimport { PlusIcon, TrashIcon } from 'lucide-vue-next'\nimport { FieldArray, FieldContextKey, useField, useFieldArray } from 'vee-validate'\nimport type { Config, ConfigItem } from './interface'\nimport { beautifyObjectName, getBaseType } from './utils'\nimport AutoFormField from './AutoFormField.vue'\nimport AutoFormLabel from './AutoFormLabel.vue'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'\nimport { Button } from '@/lib/registry/default/ui/button'\nimport { Separator } from '@/lib/registry/default/ui/separator'\nimport { FormMessage } from '@/lib/registry/default/ui/form'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: z.ZodArray<T>\n disabled?: boolean\n}>()\n\nconst fieldContext = useField(props.fieldName)\n\nfunction isZodArray(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodArray<any> {\n return item instanceof z.ZodArray\n}\n\nfunction isZodDefault(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodDefault<any> {\n return item instanceof z.ZodDefault\n}\n\nconst itemShape = computed(() => {\n if (!props.schema)\n return\n\n const schema: z.ZodAny = isZodArray(props.schema)\n ? props.schema._def.type\n : isZodDefault(props.schema)\n // @ts-expect-error missing schema\n ? props.schema._def.innerType._def.type\n : null\n\n return {\n type: getBaseType(schema),\n schema,\n }\n})\n\n// @ts-expect-error ignore missing `id`\nprovide(FieldContextKey, fieldContext)\n</script>\n\n<template>\n <FieldArray v-slot=\"{ fields, remove, push }\" as=\"section\" :name=\"fieldName\">\n <slot v-bind=\"props\">\n <Accordion type=\"multiple\" class=\"w-full\" collapsible :disabled=\"disabled\">\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger>\n <AutoFormLabel class=\"text-base\" :required=\"required\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AutoFormLabel>\n </AccordionTrigger>\n\n <AccordionContent>\n <template v-for=\"(field, index) of fields\" :key=\"field.key\">\n <div class=\"mb-4 p-[1px]\">\n <AutoFormField\n :field-name=\"`${fieldName}[${index}]`\"\n :label=\"fieldName\"\n :shape=\"itemShape!\"\n :config=\"config as ConfigItem\"\n />\n\n <div class=\"!my-4 flex justify-end\">\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"secondary\"\n @click=\"remove(index)\"\n >\n <TrashIcon :size=\"16\" />\n </Button>\n </div>\n <Separator v-if=\"!field.isLast\" />\n </div>\n </template>\n\n <Button\n type=\"button\"\n variant=\"secondary\"\n class=\"mt-4 flex items-center\"\n @click=\"push(null)\"\n >\n <PlusIcon class=\"mr-2\" :size=\"16\" />\n Add\n </Button>\n </AccordionContent>\n\n <FormMessage />\n </AccordionItem>\n </Accordion>\n </slot>\n </FieldArray>\n</template>\n"
|
"content": "<script setup lang=\"ts\" generic=\"T extends z.ZodAny\">\nimport * as z from 'zod'\nimport { computed, provide } from 'vue'\nimport { PlusIcon, TrashIcon } from 'lucide-vue-next'\nimport { FieldArray, FieldContextKey, useField, useFieldArray } from 'vee-validate'\nimport type { Config, ConfigItem } from './interface'\nimport { beautifyObjectName, getBaseType } from './utils'\nimport AutoFormField from './AutoFormField.vue'\nimport AutoFormLabel from './AutoFormLabel.vue'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'\nimport { Button } from '@/lib/registry/default/ui/button'\nimport { Separator } from '@/lib/registry/default/ui/separator'\nimport { FormMessage } from '@/lib/registry/default/ui/form'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: z.ZodArray<T>\n disabled?: boolean\n}>()\n\nconst fieldContext = useField(props.fieldName)\n\nfunction isZodArray(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodArray<any> {\n return item instanceof z.ZodArray\n}\n\nfunction isZodDefault(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodDefault<any> {\n return item instanceof z.ZodDefault\n}\n\nconst itemShape = computed(() => {\n if (!props.schema)\n return\n\n const schema: z.ZodAny = isZodArray(props.schema)\n ? props.schema._def.type\n : isZodDefault(props.schema)\n // @ts-expect-error missing schema\n ? props.schema._def.innerType._def.type\n : null\n\n return {\n type: getBaseType(schema),\n schema,\n }\n})\n\n// @ts-expect-error ignore missing `id`\nprovide(FieldContextKey, fieldContext)\n</script>\n\n<template>\n <FieldArray v-slot=\"{ fields, remove, push }\" as=\"section\" :name=\"fieldName\">\n <slot v-bind=\"props\">\n <Accordion type=\"multiple\" class=\"w-full\" collapsible :disabled=\"disabled\">\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger>\n <AutoFormLabel class=\"text-base\" :required=\"required\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AutoFormLabel>\n </AccordionTrigger>\n\n <AccordionContent>\n <template v-for=\"(field, index) of fields\" :key=\"field.key\">\n <div class=\"mb-4 p-1\">\n <AutoFormField\n :field-name=\"`${fieldName}[${index}]`\"\n :label=\"fieldName\"\n :shape=\"itemShape!\"\n :config=\"config as ConfigItem\"\n />\n\n <div class=\"!my-4 flex justify-end\">\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"secondary\"\n @click=\"remove(index)\"\n >\n <TrashIcon :size=\"16\" />\n </Button>\n </div>\n <Separator v-if=\"!field.isLast\" />\n </div>\n </template>\n\n <Button\n type=\"button\"\n variant=\"secondary\"\n class=\"mt-4 flex items-center\"\n @click=\"push(null)\"\n >\n <PlusIcon class=\"mr-2\" :size=\"16\" />\n Add\n </Button>\n </AccordionContent>\n\n <FormMessage />\n </AccordionItem>\n </Accordion>\n </slot>\n </FieldArray>\n</template>\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "AutoFormFieldBoolean.vue",
|
"name": "AutoFormFieldBoolean.vue",
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "AutoFormFieldObject.vue",
|
"name": "AutoFormFieldObject.vue",
|
||||||
"content": "<script setup lang=\"ts\" generic=\"T extends ZodRawShape\">\nimport type { ZodAny, ZodObject, ZodRawShape } from 'zod'\nimport { computed } from 'vue'\nimport AutoFormField from './AutoFormField.vue'\nimport type { Config, ConfigItem, Shape } from './interface'\nimport { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: ZodObject<T>\n disabled?: boolean\n}>()\n\nconst shapes = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof T]: Shape } = {}\n\n if (!props.schema)\n return\n const shape = getBaseSchema(props.schema)?.shape\n if (!shape)\n return\n Object.keys(shape).forEach((name) => {\n const item = shape[name] as ZodAny\n let options = 'values' in item._def ? item._def.values as string[] : undefined\n if (!Array.isArray(options) && typeof options === 'object')\n options = Object.values(options)\n\n val[name as keyof T] = {\n type: getBaseType(item),\n default: getDefaultValueInZodStack(item),\n options,\n required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),\n schema: item,\n }\n })\n return val\n})\n</script>\n\n<template>\n <section>\n <slot v-bind=\"props\">\n <Accordion type=\"multiple\" class=\"w-full\" collapsible :disabled=\"disabled\">\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger class=\"text-base\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AccordionTrigger>\n <AccordionContent class=\"p-[1px] space-y-5\">\n <template v-for=\"(shape, key) in shapes\" :key=\"key\">\n <AutoFormField\n :config=\"config?.[key as keyof typeof config] as ConfigItem\"\n :field-name=\"`${fieldName}.${key.toString()}`\"\n :label=\"key.toString()\"\n :shape=\"shape\"\n />\n </template>\n </AccordionContent>\n </AccordionItem>\n </Accordion>\n </slot>\n </section>\n</template>\n"
|
"content": "<script setup lang=\"ts\" generic=\"T extends ZodRawShape\">\nimport type { ZodAny, ZodObject, ZodRawShape } from 'zod'\nimport { computed } from 'vue'\nimport AutoFormField from './AutoFormField.vue'\nimport type { Config, ConfigItem, Shape } from './interface'\nimport { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: ZodObject<T>\n disabled?: boolean\n}>()\n\nconst shapes = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof T]: Shape } = {}\n\n if (!props.schema)\n return\n const shape = getBaseSchema(props.schema)?.shape\n if (!shape)\n return\n Object.keys(shape).forEach((name) => {\n const item = shape[name] as ZodAny\n let options = 'values' in item._def ? item._def.values as string[] : undefined\n if (!Array.isArray(options) && typeof options === 'object')\n options = Object.values(options)\n\n val[name as keyof T] = {\n type: getBaseType(item),\n default: getDefaultValueInZodStack(item),\n options,\n required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),\n schema: item,\n }\n })\n return val\n})\n</script>\n\n<template>\n <section>\n <slot v-bind=\"props\">\n <Accordion type=\"multiple\" class=\"w-full\" collapsible :disabled=\"disabled\">\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger class=\"text-base\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AccordionTrigger>\n <AccordionContent class=\"p-1 space-y-5\">\n <template v-for=\"(shape, key) in shapes\" :key=\"key\">\n <AutoFormField\n :config=\"config?.[key as keyof typeof config] as ConfigItem\"\n :field-name=\"`${fieldName}.${key.toString()}`\"\n :label=\"key.toString()\"\n :shape=\"shape\"\n />\n </template>\n </AccordionContent>\n </AccordionItem>\n </Accordion>\n </slot>\n </section>\n</template>\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "AutoFormLabel.vue",
|
"name": "AutoFormLabel.vue",
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
"files": [
|
"files": [
|
||||||
{
|
{
|
||||||
"name": "AutoForm.vue",
|
"name": "AutoForm.vue",
|
||||||
"content": "<script setup lang=\"ts\" generic=\"U extends ZodRawShape, T extends ZodObject<U>\">\nimport { computed, ref, toRef, toRefs } from 'vue'\nimport type { ZodAny, ZodObject, ZodRawShape, z } from 'zod'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport type { FormContext, GenericObject } from 'vee-validate'\nimport { getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'\nimport type { Config, ConfigItem, Dependency, Shape } from './interface'\nimport AutoFormField from './AutoFormField.vue'\nimport { provideDependencies } from './dependencies'\nimport { Form } from '@/lib/registry/new-york/ui/form'\n\nconst props = defineProps<{\n schema: T\n form?: FormContext<GenericObject>\n fieldConfig?: Config<z.infer<T>>\n dependencies?: Dependency<z.infer<T>>[]\n}>()\n\nconst emits = defineEmits<{\n submit: [event: GenericObject]\n}>()\n\nconst { dependencies } = toRefs(props)\nprovideDependencies(dependencies)\n\nconst shapes = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof T]: Shape } = {}\n const shape = props.schema.shape\n Object.keys(shape).forEach((name) => {\n const item = shape[name] as ZodAny\n const baseItem = getBaseSchema(item) as ZodAny\n let options = (baseItem && 'values' in baseItem._def) ? baseItem._def.values as string[] : undefined\n if (!Array.isArray(options) && typeof options === 'object')\n options = Object.values(options)\n\n val[name as keyof T] = {\n type: getBaseType(item),\n default: getDefaultValueInZodStack(item),\n options,\n required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),\n schema: baseItem,\n }\n })\n return val\n})\n\nconst fields = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof z.infer<T>]: { shape: Shape, fieldName: string, config: ConfigItem } } = {}\n for (const key in shapes.value) {\n const shape = shapes.value[key]\n val[key as keyof z.infer<T>] = {\n shape,\n config: props.fieldConfig?.[key] as ConfigItem,\n fieldName: key,\n }\n }\n return val\n})\n\nconst formComponent = computed(() => props.form ? 'form' : Form)\nconst formComponentProps = computed(() => {\n if (props.form) {\n return {\n onSubmit: props.form.handleSubmit(val => emits('submit', val)),\n }\n }\n else {\n const formSchema = toTypedSchema(props.schema)\n return {\n keepValues: true,\n validationSchema: formSchema,\n onSubmit: (val: GenericObject) => emits('submit', val),\n }\n }\n})\n</script>\n\n<template>\n <component\n :is=\"formComponent\"\n v-bind=\"formComponentProps\"\n >\n <slot name=\"customAutoForm\" :fields=\"fields\">\n <template v-for=\"(shape, key) of shapes\" :key=\"key\">\n <slot\n :shape=\"shape\"\n :name=\"key.toString() as keyof z.infer<T>\"\n :field-name=\"key.toString()\"\n :config=\"fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem\"\n >\n <AutoFormField\n :config=\"fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem\"\n :field-name=\"key.toString()\"\n :shape=\"shape\"\n />\n </slot>\n </template>\n </slot>\n\n <slot :shapes=\"shapes\" />\n </component>\n</template>\n"
|
"content": "<script setup lang=\"ts\" generic=\"T extends ZodObjectOrWrapped\">\nimport { computed, toRefs } from 'vue'\nimport type { ZodAny, z } from 'zod'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport type { FormContext, GenericObject } from 'vee-validate'\nimport { type ZodObjectOrWrapped, getBaseSchema, getBaseType, getDefaultValueInZodStack, getObjectFormSchema } from './utils'\nimport type { Config, ConfigItem, Dependency, Shape } from './interface'\nimport AutoFormField from './AutoFormField.vue'\nimport { provideDependencies } from './dependencies'\nimport { Form } from '@/lib/registry/new-york/ui/form'\n\nconst props = defineProps<{\n schema: T\n form?: FormContext<GenericObject>\n fieldConfig?: Config<z.infer<T>>\n dependencies?: Dependency<z.infer<T>>[]\n}>()\n\nconst emits = defineEmits<{\n submit: [event: GenericObject]\n}>()\n\nconst { dependencies } = toRefs(props)\nprovideDependencies(dependencies)\n\nconst shapes = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof T]: Shape } = {}\n const baseSchema = getObjectFormSchema(props.schema)\n const shape = baseSchema.shape\n Object.keys(shape).forEach((name) => {\n const item = shape[name] as ZodAny\n const baseItem = getBaseSchema(item) as ZodAny\n let options = (baseItem && 'values' in baseItem._def) ? baseItem._def.values as string[] : undefined\n if (!Array.isArray(options) && typeof options === 'object')\n options = Object.values(options)\n\n val[name as keyof T] = {\n type: getBaseType(item),\n default: getDefaultValueInZodStack(item),\n options,\n required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),\n schema: baseItem,\n }\n })\n return val\n})\n\nconst fields = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof z.infer<T>]: { shape: Shape, fieldName: string, config: ConfigItem } } = {}\n for (const key in shapes.value) {\n const shape = shapes.value[key]\n val[key as keyof z.infer<T>] = {\n shape,\n config: props.fieldConfig?.[key] as ConfigItem,\n fieldName: key,\n }\n }\n return val\n})\n\nconst formComponent = computed(() => props.form ? 'form' : Form)\nconst formComponentProps = computed(() => {\n if (props.form) {\n return {\n onSubmit: props.form.handleSubmit(val => emits('submit', val)),\n }\n }\n else {\n const formSchema = toTypedSchema(props.schema)\n return {\n keepValues: true,\n validationSchema: formSchema,\n onSubmit: (val: GenericObject) => emits('submit', val),\n }\n }\n})\n</script>\n\n<template>\n <component\n :is=\"formComponent\"\n v-bind=\"formComponentProps\"\n >\n <slot name=\"customAutoForm\" :fields=\"fields\">\n <template v-for=\"(shape, key) of shapes\" :key=\"key\">\n <slot\n :shape=\"shape\"\n :name=\"key.toString() as keyof z.infer<T>\"\n :field-name=\"key.toString()\"\n :config=\"fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem\"\n >\n <AutoFormField\n :config=\"fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem\"\n :field-name=\"key.toString()\"\n :shape=\"shape\"\n />\n </slot>\n </template>\n </slot>\n\n <slot :shapes=\"shapes\" />\n </component>\n</template>\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "AutoFormField.vue",
|
"name": "AutoFormField.vue",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user