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 { format } from 'date-fns'
|
||||||
import { toTypedSchema } from '@vee-validate/zod'
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
import { configure } from 'vee-validate'
|
import { configure } from 'vee-validate'
|
||||||
|
import { Check, ChevronsUpDown } from 'lucide-vue-next'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
import RadixIconsCalendar from '~icons/radix-icons/calendar'
|
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 { 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 { Separator } from '@/lib/registry/new-york/ui/separator'
|
||||||
import {
|
import {
|
||||||
Select,
|
Command,
|
||||||
SelectContent,
|
CommandEmpty,
|
||||||
SelectGroup,
|
CommandGroup,
|
||||||
SelectItem,
|
CommandInput,
|
||||||
SelectTrigger,
|
CommandItem,
|
||||||
SelectValue,
|
} from '@/lib/registry/default/ui/command'
|
||||||
} from '@/lib/registry/new-york/ui/select'
|
|
||||||
import { Button } from '@/lib/registry/new-york/ui/button'
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
|
|
@ -28,11 +27,7 @@ import {
|
||||||
} from '@/lib/registry/default/ui/popover'
|
} from '@/lib/registry/default/ui/popover'
|
||||||
import { Calendar } from '@/lib/registry/new-york/ui/calendar'
|
import { Calendar } from '@/lib/registry/new-york/ui/calendar'
|
||||||
|
|
||||||
const accountForm = ref({
|
const open = ref(false)
|
||||||
name: '',
|
|
||||||
dob: null,
|
|
||||||
language: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
const languages = [
|
const languages = [
|
||||||
{ label: 'English', value: 'en' },
|
{ label: 'English', value: 'en' },
|
||||||
|
|
@ -47,37 +42,31 @@ const languages = [
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
const accountFormSchema = toTypedSchema(z.object({
|
const accountFormSchema = toTypedSchema(z.object({
|
||||||
// name: z
|
name: z
|
||||||
// .string()
|
.string()
|
||||||
// .min(2, {
|
.min(2, {
|
||||||
// message: 'Name must be at least 2 characters.',
|
message: 'Name must be at least 2 characters.',
|
||||||
// })
|
})
|
||||||
// .max(30, {
|
.max(30, {
|
||||||
// message: 'Name must not be longer than 30 characters.',
|
message: 'Name must not be longer than 30 characters.',
|
||||||
// }),
|
}),
|
||||||
// dob: z.date({
|
dob: z.date({
|
||||||
// required_error: 'A date of birth is required.',
|
required_error: 'A date of birth is required.',
|
||||||
// }),
|
}),
|
||||||
// language: z.string().nonempty({
|
language: z.string().nonempty({
|
||||||
// message: 'Please select a language.',
|
|
||||||
// }),
|
|
||||||
example: z.string().nonempty({
|
|
||||||
message: 'Please select a language.',
|
message: 'Please select a language.',
|
||||||
}).min(5),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// type AccountFormValues = z.infer<typeof accountFormSchema>
|
// type AccountFormValues = z.infer<typeof accountFormSchema>
|
||||||
// const errors = ref<z.ZodFormattedError<AccountFormValues> | null>(null)
|
// const errors = ref<z.ZodFormattedError<AccountFormValues> | null>(null)
|
||||||
|
|
||||||
async function handleSubmit() {
|
const filterFunction = (list: typeof languages, search: string) => list.filter(i => i.value.toLowerCase().includes(search.toLowerCase()))
|
||||||
// const result = accountFormSchema.safeParse(accountForm.value)
|
|
||||||
// if (!result.success) {
|
|
||||||
// errors.value = result.error.format()
|
|
||||||
// console.log(errors.value)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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>
|
</script>
|
||||||
|
|
||||||
|
|
@ -91,82 +80,113 @@ async function handleSubmit() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
<Form :validation-schema="accountFormSchema" class="space-y-8" @submit="handleSubmit">
|
<Form
|
||||||
<FormField v-slot="{ field }" name="example">
|
v-slot="{
|
||||||
|
setValues,
|
||||||
|
}" :validation-schema="accountFormSchema" class="space-y-8"
|
||||||
|
@submit="handleSubmit"
|
||||||
|
>
|
||||||
|
<FormField v-slot="{ field }" name="name">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Name</FormLabel>
|
<FormLabel>Name</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input type="text" placeholder="Your name" v-bind="field" />
|
<Input type="text" placeholder="Your name" v-bind="field" />
|
||||||
</FormControl>
|
</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 />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<Label for="dob">
|
<FormField v-slot="{ field, value }" name="dob">
|
||||||
Date of Birth
|
<FormItem>
|
||||||
</Label>
|
<FormLabel>Date of birth</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
:class="cn(
|
||||||
|
'w-[280px] pl-3 text-left font-normal',
|
||||||
|
!value && 'text-muted-foreground',
|
||||||
|
)"
|
||||||
|
>
|
||||||
|
<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-bind="field" />
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<FormDescription>
|
||||||
|
Your date of birth is used to calculate your age.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
<!-- <div class="grid gap-2">
|
<FormField v-slot="{ value }" name="language">
|
||||||
<Label for="name" :class="cn('text-sm', errors?.name && 'text-destructive')">
|
<FormItem>
|
||||||
Name
|
<FormLabel>Language</FormLabel>
|
||||||
</Label>
|
|
||||||
<Input id="name" v-model="accountForm.name" placeholder="Your name" />
|
<Popover v-model:open="open">
|
||||||
<span class="text-muted-foreground text-sm">
|
<PopoverTrigger as-child>
|
||||||
This is the name that will be displayed on your profile and in emails.
|
<FormControl>
|
||||||
</span>
|
<Button
|
||||||
<div v-if="errors?.name" class="text-sm text-destructive">
|
variant="outline"
|
||||||
<span v-for="error in errors.name._errors" :key="error">{{ error }}</span>
|
role="combobox"
|
||||||
</div>
|
:aria-expanded="open"
|
||||||
</div> -->
|
:class="cn(
|
||||||
<!-- <div class="grid gap-2">
|
'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 }}
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<FormDescription>
|
||||||
|
This is the language that will be used in the dashboard.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger as-child>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
:class="cn(
|
|
||||||
'w-[280px] pl-3 text-left font-normal',
|
|
||||||
!accountForm.dob && 'text-muted-foreground',
|
|
||||||
)"
|
|
||||||
>
|
|
||||||
<span>{{ accountForm.dob ? format(accountForm.dob, "PPP") : "Pick a date" }}</span>
|
|
||||||
<RadixIconsCalendar class="ml-auto h-4 w-4 opacity-50" />
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent class="p-0">
|
|
||||||
<Calendar v-model="accountForm.dob" />
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<span class="text-muted-foreground text-sm">
|
|
||||||
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">
|
|
||||||
{{ language.label }}
|
|
||||||
</SelectItem>
|
|
||||||
</SelectGroup>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<span class="text-muted-foreground text-sm">
|
|
||||||
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> -->
|
|
||||||
<div class="flex justify-start">
|
<div class="flex justify-start">
|
||||||
<Button type="submit">
|
<Button type="submit">
|
||||||
Update account
|
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 props = defineProps<LabelProps>()
|
||||||
|
|
||||||
const { error, id } = useFormField()
|
const { error, id, formItemId } = useFormField()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -15,7 +15,7 @@ const { error, id } = useFormField()
|
||||||
error && 'text-destructive',
|
error && 'text-destructive',
|
||||||
$attrs.class ?? '',
|
$attrs.class ?? '',
|
||||||
)"
|
)"
|
||||||
:for="`${id}-form-item`"
|
:for="formItemId"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</Label>
|
</Label>
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@ export { default as FormItem } from './FormItem.vue'
|
||||||
export { default as FormLabel } from './FormLabel.vue'
|
export { default as FormLabel } from './FormLabel.vue'
|
||||||
export { default as FormControl } from './FormControl.vue'
|
export { default as FormControl } from './FormControl.vue'
|
||||||
export { default as FormMessage } from './FormMessage.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": {
|
"compilerOptions": {
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
|
"lib": ["esnext", "dom"],
|
||||||
|
"jsx": "preserve",
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"strict": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
"sourceMap": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"declaration": false,
|
|
||||||
"lib": ["esnext", "dom"],
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"skipLibCheck": true,
|
|
||||||
"outDir": "dist",
|
|
||||||
"types": ["unplugin-icons/types/vue", "node"],
|
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./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