chore: update AccountForm example
- add `FormDescription` component - include `src` in tsconfig
This commit is contained in:
parent
55cf147f66
commit
5ee0a4641e
|
|
@ -4,22 +4,21 @@ import * as z from 'zod'
|
|||
import { format } from 'date-fns'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { configure } from 'vee-validate'
|
||||
import { Check, ChevronsUpDown } from 'lucide-vue-next'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
import RadixIconsCalendar from '~icons/radix-icons/calendar'
|
||||
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/default/ui/form'
|
||||
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/default/ui/form'
|
||||
import { Input } from '@/lib/registry/new-york/ui/input'
|
||||
import { Label } from '@/lib/registry/new-york/ui/label'
|
||||
import { Separator } from '@/lib/registry/new-york/ui/separator'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/lib/registry/new-york/ui/select'
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
} from '@/lib/registry/default/ui/command'
|
||||
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||
import {
|
||||
Popover,
|
||||
|
|
@ -28,11 +27,7 @@ import {
|
|||
} from '@/lib/registry/default/ui/popover'
|
||||
import { Calendar } from '@/lib/registry/new-york/ui/calendar'
|
||||
|
||||
const accountForm = ref({
|
||||
name: '',
|
||||
dob: null,
|
||||
language: '',
|
||||
})
|
||||
const open = ref(false)
|
||||
|
||||
const languages = [
|
||||
{ label: 'English', value: 'en' },
|
||||
|
|
@ -47,37 +42,31 @@ const languages = [
|
|||
] as const
|
||||
|
||||
const accountFormSchema = toTypedSchema(z.object({
|
||||
// name: z
|
||||
// .string()
|
||||
// .min(2, {
|
||||
// message: 'Name must be at least 2 characters.',
|
||||
// })
|
||||
// .max(30, {
|
||||
// message: 'Name must not be longer than 30 characters.',
|
||||
// }),
|
||||
// dob: z.date({
|
||||
// required_error: 'A date of birth is required.',
|
||||
// }),
|
||||
// language: z.string().nonempty({
|
||||
// message: 'Please select a language.',
|
||||
// }),
|
||||
example: z.string().nonempty({
|
||||
name: z
|
||||
.string()
|
||||
.min(2, {
|
||||
message: 'Name must be at least 2 characters.',
|
||||
})
|
||||
.max(30, {
|
||||
message: 'Name must not be longer than 30 characters.',
|
||||
}),
|
||||
dob: z.date({
|
||||
required_error: 'A date of birth is required.',
|
||||
}),
|
||||
language: z.string().nonempty({
|
||||
message: 'Please select a language.',
|
||||
}).min(5),
|
||||
}),
|
||||
}))
|
||||
|
||||
// type AccountFormValues = z.infer<typeof accountFormSchema>
|
||||
// const errors = ref<z.ZodFormattedError<AccountFormValues> | null>(null)
|
||||
|
||||
async function handleSubmit() {
|
||||
// const result = accountFormSchema.safeParse(accountForm.value)
|
||||
// if (!result.success) {
|
||||
// errors.value = result.error.format()
|
||||
// console.log(errors.value)
|
||||
// return
|
||||
// }
|
||||
const filterFunction = (list: typeof languages, search: string) => list.filter(i => i.value.toLowerCase().includes(search.toLowerCase()))
|
||||
|
||||
// console.log('Form submitted!', accountForm.value)
|
||||
// https://github.com/logaretm/vee-validate/issues/3521
|
||||
// https://github.com/logaretm/vee-validate/discussions/3571
|
||||
async function handleSubmit(values: any) {
|
||||
console.log('Form submitted!', values)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -91,82 +80,113 @@ async function handleSubmit() {
|
|||
</p>
|
||||
</div>
|
||||
<Separator />
|
||||
<Form :validation-schema="accountFormSchema" class="space-y-8" @submit="handleSubmit">
|
||||
<FormField v-slot="{ field }" name="example">
|
||||
<Form
|
||||
v-slot="{
|
||||
setValues,
|
||||
}" :validation-schema="accountFormSchema" class="space-y-8"
|
||||
@submit="handleSubmit"
|
||||
>
|
||||
<FormField v-slot="{ field }" name="name">
|
||||
<FormItem>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="text" placeholder="Your name" v-bind="field" />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
This is your public display name. It can be your real name or a
|
||||
pseudonym. You can only change this once every 30 days.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<Label for="dob">
|
||||
Date of Birth
|
||||
</Label>
|
||||
|
||||
<!-- <div class="grid gap-2">
|
||||
<Label for="name" :class="cn('text-sm', errors?.name && 'text-destructive')">
|
||||
Name
|
||||
</Label>
|
||||
<Input id="name" v-model="accountForm.name" placeholder="Your name" />
|
||||
<span class="text-muted-foreground text-sm">
|
||||
This is the name that will be displayed on your profile and in emails.
|
||||
</span>
|
||||
<div v-if="errors?.name" class="text-sm text-destructive">
|
||||
<span v-for="error in errors.name._errors" :key="error">{{ error }}</span>
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- <div class="grid gap-2">
|
||||
|
||||
<FormField v-slot="{ field, value }" name="dob">
|
||||
<FormItem>
|
||||
<FormLabel>Date of birth</FormLabel>
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<FormControl>
|
||||
<Button
|
||||
variant="outline"
|
||||
:class="cn(
|
||||
'w-[280px] pl-3 text-left font-normal',
|
||||
!accountForm.dob && 'text-muted-foreground',
|
||||
!value && 'text-muted-foreground',
|
||||
)"
|
||||
>
|
||||
<span>{{ accountForm.dob ? format(accountForm.dob, "PPP") : "Pick a date" }}</span>
|
||||
<span>{{ value ? format(value, "PPP") : "Pick a date" }}</span>
|
||||
<RadixIconsCalendar class="ml-auto h-4 w-4 opacity-50" />
|
||||
</Button>
|
||||
</FormControl>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="p-0">
|
||||
<Calendar v-model="accountForm.dob" />
|
||||
<Calendar v-bind="field" />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<span class="text-muted-foreground text-sm">
|
||||
<FormDescription>
|
||||
Your date of birth is used to calculate your age.
|
||||
</span>
|
||||
<div v-if="errors?.dob" class="text-sm text-destructive">
|
||||
<span v-for="error in errors.dob._errors" :key="error">{{ error }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="language" :class="cn('text-sm', errors?.language && 'text-destructive')">
|
||||
Language
|
||||
</Label>
|
||||
<Select id="language" v-model="accountForm.language">
|
||||
<SelectTrigger class="w-[200px]">
|
||||
<SelectValue placeholder="Select a language" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem v-for="language in languages" :key="language.value" :value="language.value">
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ value }" name="language">
|
||||
<FormItem>
|
||||
<FormLabel>Language</FormLabel>
|
||||
|
||||
<Popover v-model:open="open">
|
||||
<PopoverTrigger as-child>
|
||||
<FormControl>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
:aria-expanded="open"
|
||||
:class="cn(
|
||||
'w-[200px] justify-between',
|
||||
!value && 'text-muted-foreground',
|
||||
)"
|
||||
>
|
||||
{{ value ? languages.find(
|
||||
(language) => language.value === value,
|
||||
)?.label : 'Select language...' }}
|
||||
|
||||
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</FormControl>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-[200px] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search language..." />
|
||||
<CommandEmpty>No framework found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
v-for="language in languages"
|
||||
:key="language.value"
|
||||
:value="language.value"
|
||||
@select="(val) => {
|
||||
setValues({ language: val })
|
||||
open = false
|
||||
}"
|
||||
>
|
||||
<Check
|
||||
:class="cn(
|
||||
'mr-2 h-4 w-4',
|
||||
value === language.label ? 'opacity-100' : 'opacity-0',
|
||||
)"
|
||||
/>
|
||||
{{ language.label }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<span class="text-muted-foreground text-sm">
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
<FormDescription>
|
||||
This is the language that will be used in the dashboard.
|
||||
</span>
|
||||
<div v-if="errors?.language" class="text-sm text-destructive">
|
||||
<span v-for="error in errors.language._errors" :key="error">{{ error }}</span>
|
||||
</div>
|
||||
</div> -->
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<div class="flex justify-start">
|
||||
<Button type="submit">
|
||||
Update account
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
<script lang="ts" setup>
|
||||
import { useFormField } from './FormItem.vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const { formDescriptionId } = useFormField()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p
|
||||
:id="formDescriptionId"
|
||||
:class="cn('text-sm text-muted-foreground', $attrs.class ?? '')"
|
||||
>
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
|
|
@ -5,7 +5,7 @@ import { cn } from '@/lib/utils'
|
|||
|
||||
const props = defineProps<LabelProps>()
|
||||
|
||||
const { error, id } = useFormField()
|
||||
const { error, id, formItemId } = useFormField()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -15,7 +15,7 @@ const { error, id } = useFormField()
|
|||
error && 'text-destructive',
|
||||
$attrs.class ?? '',
|
||||
)"
|
||||
:for="`${id}-form-item`"
|
||||
:for="formItemId"
|
||||
>
|
||||
<slot />
|
||||
</Label>
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@ export { default as FormItem } from './FormItem.vue'
|
|||
export { default as FormLabel } from './FormLabel.vue'
|
||||
export { default as FormControl } from './FormControl.vue'
|
||||
export { default as FormMessage } from './FormMessage.vue'
|
||||
export { default as FormDescription } from './FormDescription.vue'
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
{
|
||||
"include": ["/**/*.vue", ".vitepress/**/*.vue", "/**/*.ts", ".vitepress/**/*.mts", ".vitepress/**/*.vue", "src/lib/**/*"],
|
||||
"exclude": ["node_modules", "./scripts/build-registry.ts"],
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"lib": ["esnext", "dom"],
|
||||
"jsx": "preserve",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"declaration": false,
|
||||
"lib": ["esnext", "dom"],
|
||||
"baseUrl": ".",
|
||||
"skipLibCheck": true,
|
||||
"outDir": "dist",
|
||||
"types": ["unplugin-icons/types/vue", "node"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"types": ["unplugin-icons/types/vue", "node"],
|
||||
"resolveJsonModule": true,
|
||||
"declaration": false,
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["/**/*.vue", "src", ".vitepress/**/*.vue", "/**/*.ts", ".vitepress/**/*.mts", ".vitepress/**/*.vue", "src/lib/**/*"],
|
||||
"exclude": ["node_modules", "./scripts/build-registry.ts"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user