docs: add combobox, datepicker, radio-group, select, switch and textarea form and some other exmaples
This commit is contained in:
parent
5868374679
commit
fd0087b89b
|
|
@ -86,4 +86,20 @@ const value = ref({})
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Combobox
|
||||||
|
|
||||||
|
<ComponentPreview name="ComboboxDemo" />
|
||||||
|
|
||||||
|
### Popover
|
||||||
|
|
||||||
|
<ComponentPreview name="ComboboxPopover" />
|
||||||
|
|
||||||
|
### Dropdown menu
|
||||||
|
|
||||||
|
<ComponentPreview name="ComboboxDropdownMenu" />
|
||||||
|
|
||||||
|
### Form
|
||||||
|
|
||||||
|
<ComponentPreview name="ComboboxForm" />
|
||||||
|
|
|
||||||
|
|
@ -56,14 +56,18 @@ const date = ref<Date>()
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Date Range Picker
|
|
||||||
|
|
||||||
<ComponentPreview name="DatePickerWithRange" />
|
|
||||||
|
|
||||||
### Date Picker
|
### Date Picker
|
||||||
|
|
||||||
<ComponentPreview name="DatePickerDemo" />
|
<ComponentPreview name="DatePickerDemo" />
|
||||||
|
|
||||||
|
### Date Range Picker
|
||||||
|
|
||||||
|
<ComponentPreview name="DatePickerWithRange" />
|
||||||
|
|
||||||
|
### With Presets
|
||||||
|
|
||||||
|
<ComponentPreview name="DatePickerWithPresets" />
|
||||||
|
|
||||||
### Form
|
### Form
|
||||||
|
|
||||||
<ComponentPreview name="DatePickerForm" />
|
<ComponentPreview name="DatePickerForm" />
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,10 @@ import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Form
|
||||||
|
|
||||||
|
<ComponentPreview name="RadioGroupForm" />
|
||||||
|
|
|
||||||
|
|
@ -45,4 +45,10 @@ import {
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Form
|
||||||
|
|
||||||
|
<ComponentPreview name="SelectForm" />
|
||||||
|
|
|
||||||
|
|
@ -47,4 +47,10 @@ import { Switch } from '@/components/ui/switch'
|
||||||
<template>
|
<template>
|
||||||
<Switch />
|
<Switch />
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Form
|
||||||
|
|
||||||
|
<ComponentPreview name="SwitchForm" />
|
||||||
|
|
|
||||||
|
|
@ -45,4 +45,30 @@ import { Textarea } from '@/components/ui/textarea'
|
||||||
<template>
|
<template>
|
||||||
<Textarea />
|
<Textarea />
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Default
|
||||||
|
|
||||||
|
<ComponentPreview name="TextareaDemo" />
|
||||||
|
|
||||||
|
### Disabled
|
||||||
|
|
||||||
|
<ComponentPreview name="TextareaDisabled" />
|
||||||
|
|
||||||
|
### With Label
|
||||||
|
|
||||||
|
<ComponentPreview name="TextareaWithLabel" className="[&_div.grid]:w-full" />
|
||||||
|
|
||||||
|
### With Text
|
||||||
|
|
||||||
|
<ComponentPreview name="TextareaWithText" />
|
||||||
|
|
||||||
|
### With Button
|
||||||
|
|
||||||
|
<ComponentPreview name="TextareaWithButton" />
|
||||||
|
|
||||||
|
### Form
|
||||||
|
|
||||||
|
<ComponentPreview name="TextareaForm" />
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ async function onSubmit(values: any) {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
<Form :validation-schema="accountFormSchema" class="space-y-8" @submit="onSubmit">
|
<Form v-slot="{ setValues }" :validation-schema="accountFormSchema" class="space-y-8" @submit="onSubmit">
|
||||||
<FormField v-slot="{ componentField }" name="name">
|
<FormField v-slot="{ componentField }" name="name">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Name</FormLabel>
|
<FormLabel>Name</FormLabel>
|
||||||
|
|
@ -119,7 +119,7 @@ async function onSubmit(values: any) {
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ value, componentField }" name="language">
|
<FormField v-slot="{ value }" name="language">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Language</FormLabel>
|
<FormLabel>Language</FormLabel>
|
||||||
|
|
||||||
|
|
@ -141,19 +141,23 @@ async function onSubmit(values: any) {
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent class="w-[200px] p-0">
|
<PopoverContent class="w-[200px] p-0">
|
||||||
<Command v-bind="componentField">
|
<Command>
|
||||||
<CommandInput placeholder="Search language..." />
|
<CommandInput placeholder="Search language..." />
|
||||||
<CommandEmpty>No framework found.</CommandEmpty>
|
<CommandEmpty>No framework found.</CommandEmpty>
|
||||||
<CommandGroup>
|
<CommandGroup>
|
||||||
<CommandItem
|
<CommandItem
|
||||||
v-for="language in languages" :key="language.value" :value="language.value" @select="() => {
|
v-for="language in languages" :key="language.value" :value="language.label"
|
||||||
|
@select="() => {
|
||||||
|
setValues({
|
||||||
|
language: language.value,
|
||||||
|
})
|
||||||
open = false
|
open = false
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<Check
|
<Check
|
||||||
:class="cn(
|
:class="cn(
|
||||||
'mr-2 h-4 w-4',
|
'mr-2 h-4 w-4',
|
||||||
value === language.label ? 'opacity-100' : 'opacity-0',
|
value === language.value ? 'opacity-100' : 'opacity-0',
|
||||||
)"
|
)"
|
||||||
/>
|
/>
|
||||||
{{ language.label }}
|
{{ language.label }}
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,12 @@ import { useForm } from 'vee-validate'
|
||||||
import { toTypedSchema } from '@vee-validate/zod'
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
|
|
||||||
|
import { ChevronDownIcon } from '@radix-icons/vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/default/ui/form'
|
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/default/ui/form'
|
||||||
import { Separator } from '@/lib/registry/new-york/ui/separator'
|
import { Separator } from '@/lib/registry/new-york/ui/separator'
|
||||||
import { RadioGroup, RadioGroupItem } from '@/lib/registry/default/ui/radio-group'
|
import { RadioGroup, RadioGroupItem } from '@/lib/registry/default/ui/radio-group'
|
||||||
import {
|
import { Button, buttonVariants } from '@/lib/registry/new-york/ui/button'
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectGroup,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@/lib/registry/new-york/ui/select'
|
|
||||||
import { Button } from '@/lib/registry/new-york/ui/button'
|
|
||||||
|
|
||||||
const appearanceFormSchema = toTypedSchema(z.object({
|
const appearanceFormSchema = toTypedSchema(z.object({
|
||||||
theme: z.enum(['light', 'dark'], {
|
theme: z.enum(['light', 'dark'], {
|
||||||
|
|
@ -50,30 +44,30 @@ const onSubmit = handleSubmit((values) => {
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
<form class="space-y-8" @submit="onSubmit">
|
<form class="space-y-8" @submit="onSubmit">
|
||||||
<FormField v-slot="{ componentField }" name="font">
|
<FormField v-slot="{ field }" name="font">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Email</FormLabel>
|
<FormLabel>Email</FormLabel>
|
||||||
<div class="relative w-[200px]">
|
<div class="relative w-[200px]">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Select v-bind="componentField">
|
<select
|
||||||
<SelectTrigger>
|
:class="cn(
|
||||||
<SelectValue placeholder="Select a font" />
|
buttonVariants({ variant: 'outline' }),
|
||||||
</SelectTrigger>
|
'w-[200px] appearance-none bg-transparent font-normal',
|
||||||
<SelectContent>
|
)"
|
||||||
<SelectGroup>
|
v-bind="field"
|
||||||
<SelectItem value="inter">
|
>
|
||||||
Inter
|
<option value="inter">
|
||||||
</SelectItem>
|
Inter
|
||||||
<SelectItem value="manrope">
|
</option>
|
||||||
Manrope
|
<option value="manrope">
|
||||||
</SelectItem>
|
Manrope
|
||||||
<SelectItem value="system">
|
</option>
|
||||||
System
|
<option value="system">
|
||||||
</SelectItem>
|
System
|
||||||
</SelectGroup>
|
</option>
|
||||||
</SelectContent>
|
</select>
|
||||||
</Select>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<ChevronDownIcon class="absolute right-3 top-2.5 h-4 w-4 opacity-50" />
|
||||||
</div>
|
</div>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Set the font you want to use in the dashboard.
|
Set the font you want to use in the dashboard.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Calendar, MoreHorizontal, Tags, Trash, User } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from '@/lib/registry/default/ui/command'
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
|
DropdownMenuSub,
|
||||||
|
DropdownMenuSubContent,
|
||||||
|
DropdownMenuSubTrigger,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/lib/registry/default/ui/dropdown-menu'
|
||||||
|
|
||||||
|
const labels = [
|
||||||
|
'feature',
|
||||||
|
'bug',
|
||||||
|
'enhancement',
|
||||||
|
'documentation',
|
||||||
|
'design',
|
||||||
|
'question',
|
||||||
|
'maintenance',
|
||||||
|
]
|
||||||
|
|
||||||
|
const labelRef = ref('feature')
|
||||||
|
const open = ref(false)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center">
|
||||||
|
<p class="text-sm font-medium leading-none">
|
||||||
|
<span class="mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground">
|
||||||
|
{{ labelRef }}
|
||||||
|
</span>
|
||||||
|
<span class="text-muted-foreground">Create a new project</span>
|
||||||
|
</p>
|
||||||
|
<DropdownMenu :open="open">
|
||||||
|
<DropdownMenuTrigger as-child>
|
||||||
|
<Button variant="ghost" size="sm">
|
||||||
|
<MoreHorizontal />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" class="w-[200px]">
|
||||||
|
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<User class="mr-2 h-4 w-4" />
|
||||||
|
Assign to...
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<Calendar class="mr-2 h-4 w-4" />
|
||||||
|
Set due date...
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuSub>
|
||||||
|
<DropdownMenuSubTrigger>
|
||||||
|
<Tags class="mr-2 h-4 w-4" />
|
||||||
|
Apply label
|
||||||
|
</DropdownMenuSubTrigger>
|
||||||
|
<DropdownMenuSubContent class="p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput
|
||||||
|
placeholder="Filter label..."
|
||||||
|
auto-focus
|
||||||
|
/>
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No label found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
v-for="label in labels"
|
||||||
|
:key="label"
|
||||||
|
:value="label"
|
||||||
|
@select="(value) => {
|
||||||
|
labelRef = value as string
|
||||||
|
open = false
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</DropdownMenuSubContent>
|
||||||
|
</DropdownMenuSub>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem class="text-red-600">
|
||||||
|
<Trash class="mr-2 h-4 w-4" />
|
||||||
|
Delete
|
||||||
|
<DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
112
apps/www/src/lib/registry/default/example/ComboboxForm.vue
Normal file
112
apps/www/src/lib/registry/default/example/ComboboxForm.vue
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import * as z from 'zod'
|
||||||
|
|
||||||
|
import { Check, ChevronsUpDown } from 'lucide-vue-next'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
} from '@/lib/registry/default/ui/command'
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/lib/registry/default/ui/form'
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/lib/registry/default/ui/popover'
|
||||||
|
|
||||||
|
const languages = [
|
||||||
|
{ label: 'English', value: 'en' },
|
||||||
|
{ label: 'French', value: 'fr' },
|
||||||
|
{ label: 'German', value: 'de' },
|
||||||
|
{ label: 'Spanish', value: 'es' },
|
||||||
|
{ label: 'Portuguese', value: 'pt' },
|
||||||
|
{ label: 'Russian', value: 'ru' },
|
||||||
|
{ label: 'Japanese', value: 'ja' },
|
||||||
|
{ label: 'Korean', value: 'ko' },
|
||||||
|
{ label: 'Chinese', value: 'zh' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(z.object({
|
||||||
|
language: z.string({
|
||||||
|
required_error: 'Please select a language.',
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit, setValues } = useForm({
|
||||||
|
validationSchema: formSchema,
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((values) => {
|
||||||
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form class="space-y-6" @submit="onSubmit">
|
||||||
|
<FormField v-slot="{ value }" name="language">
|
||||||
|
<FormItem class="flex flex-col">
|
||||||
|
<FormLabel>Language</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
: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>Nothing found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
v-for="language in languages"
|
||||||
|
:key="language.value"
|
||||||
|
:value="language.label"
|
||||||
|
@select="() => {
|
||||||
|
setValues({
|
||||||
|
language: language.value,
|
||||||
|
})
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<Check
|
||||||
|
:class="cn('mr-2 h-4 w-4', language.value === value ? '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>
|
||||||
|
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
118
apps/www/src/lib/registry/default/example/ComboboxPopover.vue
Normal file
118
apps/www/src/lib/registry/default/example/ComboboxPopover.vue
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { h, ref } from 'vue'
|
||||||
|
import {
|
||||||
|
ArrowUpCircle,
|
||||||
|
CheckCircle2,
|
||||||
|
Circle,
|
||||||
|
HelpCircle,
|
||||||
|
XCircle,
|
||||||
|
} from 'lucide-vue-next'
|
||||||
|
import type { Icon } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from '@/lib/registry/default/ui/command'
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/lib/registry/default/ui/popover'
|
||||||
|
|
||||||
|
interface Status {
|
||||||
|
value: string
|
||||||
|
label: string
|
||||||
|
icon: Icon
|
||||||
|
}
|
||||||
|
|
||||||
|
const statuses: Status[] = [
|
||||||
|
{
|
||||||
|
value: 'backlog',
|
||||||
|
label: 'Backlog',
|
||||||
|
icon: HelpCircle,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'todo',
|
||||||
|
label: 'Todo',
|
||||||
|
icon: Circle,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'in progress',
|
||||||
|
label: 'In Progress',
|
||||||
|
icon: ArrowUpCircle,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'done',
|
||||||
|
label: 'Done',
|
||||||
|
icon: CheckCircle2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'canceled',
|
||||||
|
label: 'Canceled',
|
||||||
|
icon: XCircle,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
const value = ref<typeof statuses[number]>()
|
||||||
|
|
||||||
|
const selectedStatus = ref<Status | null>(null)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
Status
|
||||||
|
</p>
|
||||||
|
<Popover :open="open">
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
class="w-[150px] justify-start"
|
||||||
|
>
|
||||||
|
<template v-if="selectedStatus">
|
||||||
|
<component :is="h(selectedStatus?.icon)" class="mr-2 h-4 w-4 shrink-0" />
|
||||||
|
{{ selectedStatus?.label }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
+ Set status
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="p-0" side="right" align="start">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Change status..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
v-for="status in statuses"
|
||||||
|
:key="status.value"
|
||||||
|
:value="status.value"
|
||||||
|
@select="(value) => {
|
||||||
|
selectedStatus = statuses.find((priority) => priority.value === value) || null
|
||||||
|
open = false
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="h(status.icon)"
|
||||||
|
:key="status.value"
|
||||||
|
:class="cn('mr-2 h-4 w-4', status.value === selectedStatus?.value ? 'opacity-100' : 'opacity-40',
|
||||||
|
)"
|
||||||
|
/>
|
||||||
|
<span>{{ status.label }}</span>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { addDays, format } from 'date-fns'
|
||||||
|
import { Calendar as CalendarIcon } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import { Calendar } from '@/lib/registry/default/ui/calendar'
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/lib/registry/default/ui/popover'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/lib/registry/default/ui/select'
|
||||||
|
|
||||||
|
const date = ref<Date>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
:class="cn(
|
||||||
|
'w-[280px] justify-start text-left font-normal',
|
||||||
|
!date && 'text-muted-foreground',
|
||||||
|
)"
|
||||||
|
>
|
||||||
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
|
<template v-if="date">
|
||||||
|
{{ format(date, "PPP") }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span>Pick a date</span>
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="flex w-auto flex-col space-y-2 p-2">
|
||||||
|
<Select
|
||||||
|
@update:model-value="(value) => {
|
||||||
|
date = addDays(new Date(), parseInt(value))
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent position="popper">
|
||||||
|
<SelectItem value="0">
|
||||||
|
Today
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="1">
|
||||||
|
Tomorrow
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="3">
|
||||||
|
In 3 days
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="7">
|
||||||
|
In a week
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<div class="rounded-md border">
|
||||||
|
<Calendar v-model="date" mode="single" />
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</template>
|
||||||
76
apps/www/src/lib/registry/default/example/RadioGroupForm.vue
Normal file
76
apps/www/src/lib/registry/default/example/RadioGroupForm.vue
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import * as z from 'zod'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/lib/registry/default/ui/form'
|
||||||
|
import { RadioGroup, RadioGroupItem } from '@/lib/registry/default/ui/radio-group'
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(z.object({
|
||||||
|
type: z.enum(['all', 'mentions', 'none'], {
|
||||||
|
required_error: 'You need to select a notification type.',
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit } = useForm({
|
||||||
|
validationSchema: formSchema,
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((values) => {
|
||||||
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form className="w-2/3 space-y-6" @submit="onSubmit">
|
||||||
|
<FormField v-slot="{ componentField }" name="type">
|
||||||
|
<FormItem class="space-y-3">
|
||||||
|
<FormLabel>Notify me about...</FormLabel>
|
||||||
|
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
class="flex flex-col space-y-1"
|
||||||
|
v-bind="componentField"
|
||||||
|
>
|
||||||
|
<FormItem class="flex items-center gap-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="all" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel class="font-normal">
|
||||||
|
All new messages
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem class="flex items-center gap-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="mentions" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel class="font-normal">
|
||||||
|
Direct messages and mentions
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem class="flex items-center gap-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="none" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel class="font-normal">
|
||||||
|
Nothing
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
79
apps/www/src/lib/registry/default/example/SelectForm.vue
Normal file
79
apps/www/src/lib/registry/default/example/SelectForm.vue
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import * as z from 'zod'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/lib/registry/default/ui/form'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/lib/registry/default/ui/select'
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(z.object({
|
||||||
|
email: z
|
||||||
|
.string({
|
||||||
|
required_error: 'Please select an email to display.',
|
||||||
|
})
|
||||||
|
.email(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit } = useForm({
|
||||||
|
validationSchema: formSchema,
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((values) => {
|
||||||
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form className="w-2/3 space-y-6" @submit="onSubmit">
|
||||||
|
<FormField v-slot="{ componentField }" name="email">
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
|
||||||
|
<Select v-bind="componentField">
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select a verified email to display" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectItem value="m@example.com">
|
||||||
|
m@example.com
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="m@google.com">
|
||||||
|
m@google.com
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="m@support.com">
|
||||||
|
m@support.com
|
||||||
|
</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormDescription>
|
||||||
|
You can manage email addresses in your
|
||||||
|
<a href="/examples/forms">email settings</a>.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
85
apps/www/src/lib/registry/default/example/SwitchForm.vue
Normal file
85
apps/www/src/lib/registry/default/example/SwitchForm.vue
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import * as z from 'zod'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
} from '@/lib/registry/default/ui/form'
|
||||||
|
import { Switch } from '@/lib/registry/default/ui/switch'
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(z.object({
|
||||||
|
marketing_emails: z.boolean().default(false).optional(),
|
||||||
|
security_emails: z.boolean(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit } = useForm({
|
||||||
|
validationSchema: formSchema,
|
||||||
|
initialValues: {
|
||||||
|
security_emails: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((values) => {
|
||||||
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form class="w-full space-y-6" @submit="onSubmit">
|
||||||
|
<div>
|
||||||
|
<h3 class="mb-4 text-lg font-medium">
|
||||||
|
Email Notifications
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<FormField v-slot="{ value, handleChange }" name="marketing_emails">
|
||||||
|
<FormItem class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
|
<div class="space-y-0.5">
|
||||||
|
<FormLabel class="text-base">
|
||||||
|
Marketing emails
|
||||||
|
</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Receive emails about new products, features, and more.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
:checked="value"
|
||||||
|
@update:checked="handleChange"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
<FormField v-slot="{ value, handleChange }" name="security_emails">
|
||||||
|
<FormItem class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
|
<div class="space-y-0.5">
|
||||||
|
<FormLabel class="text-base">
|
||||||
|
Security emails
|
||||||
|
</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Receive emails about your account security.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
:checked="value"
|
||||||
|
disabled
|
||||||
|
aria-readonly
|
||||||
|
@update:checked="handleChange"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Textarea } from '@/lib/registry/default/ui/textarea'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Textarea placeholder="Type your message here." disabled />
|
||||||
|
</template>
|
||||||
59
apps/www/src/lib/registry/default/example/TextareaForm.vue
Normal file
59
apps/www/src/lib/registry/default/example/TextareaForm.vue
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import * as z from 'zod'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/lib/registry/default/ui/form'
|
||||||
|
import { Textarea } from '@/lib/registry/default/ui/textarea'
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(z.object({
|
||||||
|
bio: z
|
||||||
|
.string()
|
||||||
|
.min(10, {
|
||||||
|
message: 'Bio must be at least 10 characters.',
|
||||||
|
})
|
||||||
|
.max(160, {
|
||||||
|
message: 'Bio must not be longer than 30 characters.',
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit } = useForm({
|
||||||
|
validationSchema: formSchema,
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((values) => {
|
||||||
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form class="w-full space-y-6" @submit="onSubmit">
|
||||||
|
<FormField v-slot="{ componentField }" name="bio">
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Bio</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Tell us a little bit about yourself"
|
||||||
|
class="resize-none"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
You can <span>@mention</span> other users and organizations.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Textarea } from '@/lib/registry/default/ui/textarea'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="grid w-full gap-2">
|
||||||
|
<Textarea placeholder="Type your message here." />
|
||||||
|
<Button>Send message</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Textarea } from '@/lib/registry/default/ui/textarea'
|
||||||
|
import { Label } from '@/lib/registry/default/ui/label'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="grid w-full gap-1.5">
|
||||||
|
<Label for="message">Your message</Label>
|
||||||
|
<Textarea id="message" placeholder="Type your message here." />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Textarea } from '@/lib/registry/default/ui/textarea'
|
||||||
|
import { Label } from '@/lib/registry/default/ui/label'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="grid w-full gap-1.5">
|
||||||
|
<Label for="message-2">Your message</Label>
|
||||||
|
<Textarea id="message-2" placeholder="Type your message here." />
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Your message will be copied to the support team.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { DotsHorizontalIcon } from '@radix-icons/vue'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from '@/lib/registry/new-york/ui/command'
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
|
DropdownMenuSub,
|
||||||
|
DropdownMenuSubContent,
|
||||||
|
DropdownMenuSubTrigger,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/lib/registry/new-york/ui/dropdown-menu'
|
||||||
|
|
||||||
|
const labels = [
|
||||||
|
'feature',
|
||||||
|
'bug',
|
||||||
|
'enhancement',
|
||||||
|
'documentation',
|
||||||
|
'design',
|
||||||
|
'question',
|
||||||
|
'maintenance',
|
||||||
|
]
|
||||||
|
|
||||||
|
const labelRef = ref('feature')
|
||||||
|
const open = ref(false)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center">
|
||||||
|
<p class="text-sm font-medium leading-none">
|
||||||
|
<span class="mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground">
|
||||||
|
{{ labelRef }}
|
||||||
|
</span>
|
||||||
|
<span class="text-muted-foreground">Create a new project</span>
|
||||||
|
</p>
|
||||||
|
<DropdownMenu :open="open">
|
||||||
|
<DropdownMenuTrigger as-child>
|
||||||
|
<Button variant="ghost" size="sm">
|
||||||
|
<DotsHorizontalIcon />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" class="w-[200px]">
|
||||||
|
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Assign to...
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Set due date...
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuSub>
|
||||||
|
<DropdownMenuSubTrigger>
|
||||||
|
Apply label
|
||||||
|
</DropdownMenuSubTrigger>
|
||||||
|
<DropdownMenuSubContent class="p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput
|
||||||
|
placeholder="Filter label..."
|
||||||
|
auto-focus
|
||||||
|
/>
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No label found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
v-for="label in labels"
|
||||||
|
:key="label"
|
||||||
|
:value="label"
|
||||||
|
@select="(value) => {
|
||||||
|
labelRef = value as string
|
||||||
|
open = false
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</DropdownMenuSubContent>
|
||||||
|
</DropdownMenuSub>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem class="text-red-600">
|
||||||
|
Delete
|
||||||
|
<DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
115
apps/www/src/lib/registry/new-york/example/ComboboxForm.vue
Normal file
115
apps/www/src/lib/registry/new-york/example/ComboboxForm.vue
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import * as z from 'zod'
|
||||||
|
|
||||||
|
import { CaretSortIcon, CheckIcon } from '@radix-icons/vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
} from '@/lib/registry/new-york/ui/command'
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/lib/registry/new-york/ui/form'
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/lib/registry/new-york/ui/popover'
|
||||||
|
|
||||||
|
const languages = [
|
||||||
|
{ label: 'English', value: 'en' },
|
||||||
|
{ label: 'French', value: 'fr' },
|
||||||
|
{ label: 'German', value: 'de' },
|
||||||
|
{ label: 'Spanish', value: 'es' },
|
||||||
|
{ label: 'Portuguese', value: 'pt' },
|
||||||
|
{ label: 'Russian', value: 'ru' },
|
||||||
|
{ label: 'Japanese', value: 'ja' },
|
||||||
|
{ label: 'Korean', value: 'ko' },
|
||||||
|
{ label: 'Chinese', value: 'zh' },
|
||||||
|
] as const
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(z.object({
|
||||||
|
language: z.string({
|
||||||
|
required_error: 'Please select a language.',
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit, setValues } = useForm({
|
||||||
|
validationSchema: formSchema,
|
||||||
|
initialValues: {
|
||||||
|
language: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((values) => {
|
||||||
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form class="space-y-6" @submit="onSubmit">
|
||||||
|
<FormField v-slot="{ value }" name="language">
|
||||||
|
<FormItem class="flex flex-col">
|
||||||
|
<FormLabel>Language</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
:class="cn('w-[200px] justify-between', !value && 'text-muted-foreground')"
|
||||||
|
>
|
||||||
|
{{ value ? languages.find(
|
||||||
|
(language) => language.value === value,
|
||||||
|
)?.label : 'Select language...' }}
|
||||||
|
<CaretSortIcon 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>Nothing found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
v-for="language in languages"
|
||||||
|
:key="language.value"
|
||||||
|
:value="language.label"
|
||||||
|
@select="() => {
|
||||||
|
setValues({
|
||||||
|
language: language.value,
|
||||||
|
})
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ language.label }}
|
||||||
|
<CheckIcon
|
||||||
|
:class="cn('ml-auto h-4 w-4', language.value === value ? 'opacity-100' : 'opacity-0')"
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<FormDescription>
|
||||||
|
This is the language that will be used in the dashboard.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from '@/lib/registry/new-york/ui/command'
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/lib/registry/new-york/ui/popover'
|
||||||
|
|
||||||
|
interface Status {
|
||||||
|
value: string
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const statuses: Status[] = [
|
||||||
|
{
|
||||||
|
value: 'backlog',
|
||||||
|
label: 'Backlog',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'todo',
|
||||||
|
label: 'Todo',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'in progress',
|
||||||
|
label: 'In Progress',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'done',
|
||||||
|
label: 'Done',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'canceled',
|
||||||
|
label: 'Canceled',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
const selectedStatus = ref<Status | null>(null)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
Status
|
||||||
|
</p>
|
||||||
|
<Popover :open="open">
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
class="w-[150px] justify-start"
|
||||||
|
>
|
||||||
|
<template v-if="selectedStatus">
|
||||||
|
{{ selectedStatus?.label }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
+ Set status
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="p-0" side="right" align="start">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Change status..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
v-for="status in statuses"
|
||||||
|
:key="status.value"
|
||||||
|
:value="status"
|
||||||
|
@select="(value) => {
|
||||||
|
selectedStatus = statuses.find((priority) => priority.value === value) || null
|
||||||
|
open = false
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ status.label }}
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { addDays, format } from 'date-fns'
|
||||||
|
import { Calendar as CalendarIcon } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import { Calendar } from '@/lib/registry/new-york/ui/calendar'
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/lib/registry/new-york/ui/popover'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/lib/registry/new-york/ui/select'
|
||||||
|
|
||||||
|
const date = ref<Date>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
:class="cn(
|
||||||
|
'w-[280px] justify-start text-left font-normal',
|
||||||
|
!date && 'text-muted-foreground',
|
||||||
|
)"
|
||||||
|
>
|
||||||
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
|
<template v-if="date">
|
||||||
|
{{ format(date, "PPP") }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span>Pick a date</span>
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="flex w-auto flex-col space-y-2 p-2">
|
||||||
|
<Select
|
||||||
|
@update:model-value="(value) => {
|
||||||
|
date = addDays(new Date(), parseInt(value))
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent position="popper">
|
||||||
|
<SelectItem value="0">
|
||||||
|
Today
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="1">
|
||||||
|
Tomorrow
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="3">
|
||||||
|
In 3 days
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="7">
|
||||||
|
In a week
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<div class="rounded-md border">
|
||||||
|
<Calendar v-model="date" mode="single" />
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import * as z from 'zod'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/lib/registry/new-york/ui/form'
|
||||||
|
import { RadioGroup, RadioGroupItem } from '@/lib/registry/new-york/ui/radio-group'
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(z.object({
|
||||||
|
type: z.enum(['all', 'mentions', 'none'], {
|
||||||
|
required_error: 'You need to select a notification type.',
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit } = useForm({
|
||||||
|
validationSchema: formSchema,
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((values) => {
|
||||||
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form className="w-2/3 space-y-6" @submit="onSubmit">
|
||||||
|
<FormField v-slot="{ componentField }" name="type">
|
||||||
|
<FormItem class="space-y-3">
|
||||||
|
<FormLabel>Notify me about...</FormLabel>
|
||||||
|
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
class="flex flex-col space-y-1"
|
||||||
|
v-bind="componentField"
|
||||||
|
>
|
||||||
|
<FormItem class="flex items-center gap-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="all" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel class="font-normal">
|
||||||
|
All new messages
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem class="flex items-center gap-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="mentions" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel class="font-normal">
|
||||||
|
Direct messages and mentions
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem class="flex items-center gap-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="none" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel class="font-normal">
|
||||||
|
Nothing
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
79
apps/www/src/lib/registry/new-york/example/SelectForm.vue
Normal file
79
apps/www/src/lib/registry/new-york/example/SelectForm.vue
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import * as z from 'zod'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/lib/registry/new-york/ui/form'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/lib/registry/new-york/ui/select'
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(z.object({
|
||||||
|
email: z
|
||||||
|
.string({
|
||||||
|
required_error: 'Please select an email to display.',
|
||||||
|
})
|
||||||
|
.email(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit } = useForm({
|
||||||
|
validationSchema: formSchema,
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((values) => {
|
||||||
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form className="w-2/3 space-y-6" @submit="onSubmit">
|
||||||
|
<FormField v-slot="{ componentField }" name="email">
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
|
||||||
|
<Select v-bind="componentField">
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select a verified email to display" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectItem value="m@example.com">
|
||||||
|
m@example.com
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="m@google.com">
|
||||||
|
m@google.com
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="m@support.com">
|
||||||
|
m@support.com
|
||||||
|
</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormDescription>
|
||||||
|
You can manage email addresses in your
|
||||||
|
<a href="/examples/forms">email settings</a>.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
85
apps/www/src/lib/registry/new-york/example/SwitchForm.vue
Normal file
85
apps/www/src/lib/registry/new-york/example/SwitchForm.vue
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import * as z from 'zod'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
} from '@/lib/registry/new-york/ui/form'
|
||||||
|
import { Switch } from '@/lib/registry/new-york/ui/switch'
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(z.object({
|
||||||
|
marketing_emails: z.boolean().default(false).optional(),
|
||||||
|
security_emails: z.boolean(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit } = useForm({
|
||||||
|
validationSchema: formSchema,
|
||||||
|
initialValues: {
|
||||||
|
security_emails: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((values) => {
|
||||||
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form class="w-full space-y-6" @submit="onSubmit">
|
||||||
|
<div>
|
||||||
|
<h3 class="mb-4 text-lg font-medium">
|
||||||
|
Email Notifications
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<FormField v-slot="{ value, handleChange }" name="marketing_emails">
|
||||||
|
<FormItem class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
|
<div class="space-y-0.5">
|
||||||
|
<FormLabel class="text-base">
|
||||||
|
Marketing emails
|
||||||
|
</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Receive emails about new products, features, and more.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
:checked="value"
|
||||||
|
@update:checked="handleChange"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
<FormField v-slot="{ value, handleChange }" name="security_emails">
|
||||||
|
<FormItem class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
|
<div class="space-y-0.5">
|
||||||
|
<FormLabel class="text-base">
|
||||||
|
Security emails
|
||||||
|
</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
Receive emails about your account security.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
:checked="value"
|
||||||
|
disabled
|
||||||
|
aria-readonly
|
||||||
|
@update:checked="handleChange"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Textarea } from '@/lib/registry/new-york/ui/textarea'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Textarea placeholder="Type your message here." disabled />
|
||||||
|
</template>
|
||||||
59
apps/www/src/lib/registry/new-york/example/TextareaForm.vue
Normal file
59
apps/www/src/lib/registry/new-york/example/TextareaForm.vue
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import * as z from 'zod'
|
||||||
|
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/lib/registry/new-york/ui/form'
|
||||||
|
import { Textarea } from '@/lib/registry/new-york/ui/textarea'
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(z.object({
|
||||||
|
bio: z
|
||||||
|
.string()
|
||||||
|
.min(10, {
|
||||||
|
message: 'Bio must be at least 10 characters.',
|
||||||
|
})
|
||||||
|
.max(160, {
|
||||||
|
message: 'Bio must not be longer than 30 characters.',
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit } = useForm({
|
||||||
|
validationSchema: formSchema,
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((values) => {
|
||||||
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form class="w-full space-y-6" @submit="onSubmit">
|
||||||
|
<FormField v-slot="{ componentField }" name="bio">
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Bio</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Tell us a little bit about yourself"
|
||||||
|
class="resize-none"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
You can <span>@mention</span> other users and organizations.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
<Button type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Textarea } from '@/lib/registry/new-york/ui/textarea'
|
||||||
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="grid w-full gap-2">
|
||||||
|
<Textarea placeholder="Type your message here." />
|
||||||
|
<Button>Send message</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Textarea } from '@/lib/registry/new-york/ui/textarea'
|
||||||
|
import { Label } from '@/lib/registry/new-york/ui/label'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="grid w-full gap-1.5">
|
||||||
|
<Label for="message">Your message</Label>
|
||||||
|
<Textarea id="message" placeholder="Type your message here." />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Textarea } from '@/lib/registry/new-york/ui/textarea'
|
||||||
|
import { Label } from '@/lib/registry/new-york/ui/label'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="grid w-full gap-1.5">
|
||||||
|
<Label for="message-2">Your message</Label>
|
||||||
|
<Textarea id="message-2" placeholder="Type your message here." />
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Your message will be copied to the support team.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
Loading…
Reference in New Issue
Block a user