chore: update AccountForm example

- add `FormDescription` component
- include `src` in tsconfig
This commit is contained in:
Sadegh Barati 2023-09-25 19:21:19 +03:30
parent 55cf147f66
commit 5ee0a4641e
5 changed files with 155 additions and 119 deletions

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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'

View File

@ -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"]
}