refactor: update
- add new-york style - off eslint import/first rule - use `useId` from radix-vue
This commit is contained in:
parent
76be3fb332
commit
23a3d2ac9c
|
|
@ -12,5 +12,6 @@ module.exports = {
|
||||||
'no-console': 'warn',
|
'no-console': 'warn',
|
||||||
'no-tabs': 'off',
|
'no-tabs': 'off',
|
||||||
'no-invalid-character': 'off',
|
'no-invalid-character': 'off',
|
||||||
|
'import/first': 'off',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,14 +58,11 @@ const accountFormSchema = toTypedSchema(z.object({
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// type AccountFormValues = z.infer<typeof accountFormSchema>
|
|
||||||
// const errors = ref<z.ZodFormattedError<AccountFormValues> | null>(null)
|
|
||||||
|
|
||||||
const filterFunction = (list: typeof languages, search: string) => list.filter(i => i.value.toLowerCase().includes(search.toLowerCase()))
|
const filterFunction = (list: typeof languages, search: string) => list.filter(i => i.value.toLowerCase().includes(search.toLowerCase()))
|
||||||
|
|
||||||
// https://github.com/logaretm/vee-validate/issues/3521
|
// https://github.com/logaretm/vee-validate/issues/3521
|
||||||
// https://github.com/logaretm/vee-validate/discussions/3571
|
// https://github.com/logaretm/vee-validate/discussions/3571
|
||||||
async function handleSubmit(values: any) {
|
async function onSubmit(values: any) {
|
||||||
console.log('Form submitted!', values)
|
console.log('Form submitted!', values)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -83,7 +80,7 @@ async function handleSubmit(values: any) {
|
||||||
<Form
|
<Form
|
||||||
v-slot="{
|
v-slot="{
|
||||||
setValues,
|
setValues,
|
||||||
}" :validation-schema="accountFormSchema" class="space-y-8" @submit="handleSubmit"
|
}" :validation-schema="accountFormSchema" class="space-y-8" @submit="onSubmit"
|
||||||
>
|
>
|
||||||
<FormField v-slot="{ componentField }" name="name">
|
<FormField v-slot="{ componentField }" name="name">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useForm } from 'vee-validate'
|
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 { cn } from '@/lib/utils'
|
|
||||||
|
|
||||||
import { Form, 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 {
|
||||||
|
|
@ -28,16 +26,16 @@ const appearanceFormSchema = toTypedSchema(z.object({
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const { handleSubmit, resetForm } = useForm({
|
const { handleSubmit } = useForm({
|
||||||
validationSchema: appearanceFormSchema,
|
validationSchema: appearanceFormSchema,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
theme: 'dark',
|
theme: 'light',
|
||||||
font: 'inter',
|
font: 'inter',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSubmit = handleSubmit((values, { resetForm }) => {
|
const onSubmit = handleSubmit((values) => {
|
||||||
console.log(values)
|
console.log('Form submitted!', values)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -84,7 +82,7 @@ const onSubmit = handleSubmit((values, { resetForm }) => {
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField v-slot="{ componentField, value }" type="radio" name="theme">
|
<FormField v-slot="{ componentField }" type="radio" name="theme">
|
||||||
<FormItem class-name="space-y-1">
|
<FormItem class-name="space-y-1">
|
||||||
<FormLabel>Theme</FormLabel>
|
<FormLabel>Theme</FormLabel>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
|
|
@ -95,7 +93,6 @@ const onSubmit = handleSubmit((values, { resetForm }) => {
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
class="grid max-w-md grid-cols-2 gap-8 pt-2"
|
class="grid max-w-md grid-cols-2 gap-8 pt-2"
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
:default-value="value"
|
|
||||||
>
|
>
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel class="[&:has([data-state=checked])>div]:border-primary">
|
<FormLabel class="[&:has([data-state=checked])>div]:border-primary">
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,13 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
import { cn } from '@/lib/utils'
|
|
||||||
|
|
||||||
import { Label } from '@/lib/registry/new-york/ui/label'
|
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 { Checkbox } from '@/lib/registry/new-york/ui/checkbox'
|
import { Checkbox } from '@/lib/registry/new-york/ui/checkbox'
|
||||||
import { Button } from '@/lib/registry/new-york/ui/button'
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
|
||||||
const displayForm = ref({
|
|
||||||
items: ['recents', 'home'],
|
|
||||||
})
|
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
id: 'recents',
|
id: 'recents',
|
||||||
|
|
@ -43,25 +39,22 @@ const items = [
|
||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
const displayFormSchema = z.object({
|
const displayFormSchema = toTypedSchema(z.object({
|
||||||
items: z.array(z.string()).refine(value => value.some(item => item), {
|
items: z.array(z.string()).refine(value => value.some(item => item), {
|
||||||
message: 'You have to select at least one item.',
|
message: 'You have to select at least one item.',
|
||||||
}),
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit } = useForm({
|
||||||
|
validationSchema: displayFormSchema,
|
||||||
|
initialValues: {
|
||||||
|
items: ['recents', 'home'],
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
type DisplayFormValues = z.infer<typeof displayFormSchema>
|
const onSubmit = handleSubmit((values) => {
|
||||||
const errors = ref<z.ZodFormattedError<DisplayFormValues> | null>(null)
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
async function handleSubmit() {
|
|
||||||
const result = displayFormSchema.safeParse(displayForm.value)
|
|
||||||
if (!result.success) {
|
|
||||||
errors.value = result.error.format()
|
|
||||||
console.log(errors.value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Form submitted!', displayForm.value)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -74,21 +67,37 @@ async function handleSubmit() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
<form @submit.prevent="handleSubmit">
|
<form @submit="onSubmit">
|
||||||
|
<FormField name="items">
|
||||||
|
<FormItem>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<Label for="sidebar" :class="cn('text-md', errors?.items && 'text-destructive')">
|
<FormLabel class="text-base">
|
||||||
Sidebar
|
Sidebar
|
||||||
</Label>
|
</FormLabel>
|
||||||
<span class="text-xs text-muted-foreground">
|
<FormDescription>
|
||||||
Select the items you want to display in the sidebar.
|
Select the items you want to display in the sidebar.
|
||||||
</span>
|
</FormDescription>
|
||||||
</div>
|
|
||||||
<div v-for="item in items" :key="item.id" class="pb-1">
|
|
||||||
<div class="flex flex-row items-center space-x-3 space-y-0">
|
|
||||||
<Checkbox :id="item.id" :checked="displayForm.items.includes(item.id)" @change="displayForm.items.includes(item.id) ? displayForm.items.splice(displayForm.items.indexOf(item.id), 1) : displayForm.items.push(item.id)" />
|
|
||||||
<Label :for="item.id">{{ item.label }}</Label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<FormField v-for="item in items" v-slot="{ value, handleChange }" :key="item.id" name="items">
|
||||||
|
<FormItem :key="item.id" class="flex flex-row items-start space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
:checked="value.includes(item.id)"
|
||||||
|
@update:checked="(checked) => {
|
||||||
|
handleChange(checked ? [...value, item.id] : value.filter(id => id !== item.id))
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel class="font-normal">
|
||||||
|
{{ item.label }}
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
<div class="flex justify-start mt-4">
|
<div class="flex justify-start mt-4">
|
||||||
<Button type="submit">
|
<Button type="submit">
|
||||||
Update display
|
Update display
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,16 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { useForm } from 'vee-validate'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
import { cn } from '@/lib/utils'
|
|
||||||
|
|
||||||
import { Label } from '@/lib/registry/new-york/ui/label'
|
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/new-york/ui/radio-group'
|
import { RadioGroup, RadioGroupItem } from '@/lib/registry/new-york/ui/radio-group'
|
||||||
import { Switch } from '@/lib/registry/new-york/ui/switch'
|
import { Switch } from '@/lib/registry/new-york/ui/switch'
|
||||||
import { Checkbox } from '@/lib/registry/new-york/ui/checkbox'
|
import { Checkbox } from '@/lib/registry/new-york/ui/checkbox'
|
||||||
import { Button } from '@/lib/registry/new-york/ui/button'
|
import { Button } from '@/lib/registry/new-york/ui/button'
|
||||||
|
|
||||||
const notificationsForm = ref({
|
const notificationsFormSchema = toTypedSchema(z.object({
|
||||||
type: '',
|
|
||||||
mobile: false,
|
|
||||||
communication_emails: false,
|
|
||||||
social_emails: true,
|
|
||||||
marketing_emails: false,
|
|
||||||
security_emails: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const notificationsFormSchema = z.object({
|
|
||||||
type: z.enum(['all', 'mentions', 'none'], {
|
type: z.enum(['all', 'mentions', 'none'], {
|
||||||
required_error: 'You need to select a notification type.',
|
required_error: 'You need to select a notification type.',
|
||||||
}),
|
}),
|
||||||
|
|
@ -28,21 +19,21 @@ const notificationsFormSchema = z.object({
|
||||||
social_emails: z.boolean().default(false).optional(),
|
social_emails: z.boolean().default(false).optional(),
|
||||||
marketing_emails: z.boolean().default(false).optional(),
|
marketing_emails: z.boolean().default(false).optional(),
|
||||||
security_emails: z.boolean(),
|
security_emails: z.boolean(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { handleSubmit } = useForm({
|
||||||
|
validationSchema: notificationsFormSchema,
|
||||||
|
initialValues: {
|
||||||
|
communication_emails: false,
|
||||||
|
marketing_emails: false,
|
||||||
|
social_emails: true,
|
||||||
|
security_emails: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
type notificationsFormValues = z.infer<typeof notificationsFormSchema>
|
const onSubmit = handleSubmit((values, { resetForm }) => {
|
||||||
const errors = ref<z.ZodFormattedError<notificationsFormValues> | null>(null)
|
console.log('Form submitted!', values)
|
||||||
|
})
|
||||||
async function handleSubmit() {
|
|
||||||
const result = notificationsFormSchema.safeParse(notificationsForm.value)
|
|
||||||
if (!result.success) {
|
|
||||||
errors.value = result.error.format()
|
|
||||||
console.log(errors.value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Form submitted!', notificationsForm.value)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -55,117 +46,149 @@ async function handleSubmit() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
<form class="space-y-8" @submit.prevent="handleSubmit">
|
<form class="space-y-8" @submit="onSubmit">
|
||||||
<div class="grid gap-2">
|
<FormField v-slot="{ componentField }" type="radio" name="type">
|
||||||
<Label for="font" :class="cn('text-sm', errors?.type && 'text-destructive')">
|
<FormItem class="space-y-3">
|
||||||
Notify me about...
|
<FormLabel>Notify me about...</FormLabel>
|
||||||
</Label>
|
<FormControl>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
v-model="notificationsForm.type"
|
|
||||||
default-value="all"
|
|
||||||
class="flex flex-col space-y-1"
|
class="flex flex-col space-y-1"
|
||||||
|
v-bind="componentField"
|
||||||
>
|
>
|
||||||
<div class="flex items-center space-x-3 space-y-0">
|
<FormItem class="flex items-center space-x-3 space-y-0">
|
||||||
<RadioGroupItem id="all" value="all" />
|
<FormControl>
|
||||||
<Label for="all">All new messages</Label>
|
<RadioGroupItem value="all" />
|
||||||
</div>
|
</FormControl>
|
||||||
<div class="flex items-center space-x-3 space-y-0">
|
<FormLabel class="font-normal">
|
||||||
<RadioGroupItem id="mentions" value="mentions" />
|
All new messages
|
||||||
<Label for="mentions">Direct messages and mentions</Label>
|
</FormLabel>
|
||||||
</div>
|
</FormItem>
|
||||||
<div class="flex items-center space-x-3 space-y-0">
|
<FormItem class="flex items-center space-x-3 space-y-0">
|
||||||
<RadioGroupItem id="none" value="none" />
|
<FormControl>
|
||||||
<Label for="none">Nothing</Label>
|
<RadioGroupItem value="mentions" />
|
||||||
</div>
|
</FormControl>
|
||||||
<div v-if="errors?.type" class="text-sm text-destructive">
|
<FormLabel class="font-normal">
|
||||||
<span v-for="error in errors.type._errors" :key="error">{{ error }}</span>
|
Direct messages and mentions
|
||||||
</div>
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem class="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="none" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel class="font-normal">
|
||||||
|
Nothing
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
<div class="grid gap-2">
|
<div>
|
||||||
<h3 class="mb-4 text-lg font-medium">
|
<h3 class="mb-4 text-lg font-medium">
|
||||||
Email Notifications
|
Email Notifications
|
||||||
</h3>
|
</h3>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div class="flex flex-row items-center justify-between rounded-lg border p-4">
|
<FormField v-slot="{ handleChange, value }" type="checkbox" name="communication_emails">
|
||||||
|
<FormItem class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
<Label class="text-base" for="communication_emails">
|
<FormLabel class="text-base">
|
||||||
Communication emails
|
Communication emails
|
||||||
</Label>
|
</FormLabel>
|
||||||
<span class="text-xs text-muted-foreground">
|
<FormDescription>
|
||||||
Receive emails about your account activity.
|
Receive emails about your account activity.
|
||||||
</span>
|
</FormDescription>
|
||||||
</div>
|
</div>
|
||||||
|
<FormControl>
|
||||||
<Switch
|
<Switch
|
||||||
id="communication_emails"
|
:checked="value"
|
||||||
v-model:checked="notificationsForm.communication_emails"
|
@update:checked="handleChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormControl>
|
||||||
</div>
|
</FormItem>
|
||||||
<div class="flex flex-row items-center justify-between rounded-lg border p-4">
|
</FormField>
|
||||||
|
|
||||||
|
<FormField v-slot="{ handleChange, value }" type="checkbox" name="marketing_emails">
|
||||||
|
<FormItem class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
<Label class="text-base" for="marketing_emails">
|
<FormLabel class="text-base">
|
||||||
Marketing emails
|
Marketing emails
|
||||||
</Label>
|
</FormLabel>
|
||||||
<span class="text-xs text-muted-foreground">
|
<FormDescription>
|
||||||
Receive emails about new products, features, and more.
|
Receive emails about new products, features, and more.
|
||||||
</span>
|
</FormDescription>
|
||||||
</div>
|
</div>
|
||||||
|
<FormControl>
|
||||||
<Switch
|
<Switch
|
||||||
id="marketing_emails"
|
:checked="value"
|
||||||
v-model:checked="notificationsForm.marketing_emails"
|
@update:checked="handleChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormControl>
|
||||||
<div class="flex flex-row items-center justify-between rounded-lg border p-4">
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<FormField v-slot="{ handleChange, value }" type="checkbox" name="social_emails">
|
||||||
|
<FormItem class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
<Label class="text-base" for="social_emails">
|
<FormLabel class="text-base">
|
||||||
Social emails
|
Social emails
|
||||||
</Label>
|
</FormLabel>
|
||||||
<span class="text-xs text-muted-foreground">
|
<FormDescription>
|
||||||
Receive emails for friend requests, follows, and more.
|
Receive emails for friend requests, follows, and more.
|
||||||
</span>
|
</FormDescription>
|
||||||
</div>
|
</div>
|
||||||
|
<FormControl>
|
||||||
<Switch
|
<Switch
|
||||||
id="social_emails"
|
:checked="value"
|
||||||
v-model:checked="notificationsForm.social_emails"
|
@update:checked="handleChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
<div class="flex flex-row items-center justify-between rounded-lg border p-4">
|
<FormField v-slot="{ handleChange, value }" type="checkbox" name="security_emails">
|
||||||
|
<FormItem class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
<Label class="text-base" for="security_emails">
|
<FormLabel class="text-base">
|
||||||
Security emails
|
Security emails
|
||||||
</Label>
|
</FormLabel>
|
||||||
<span class="text-xs text-muted-foreground">
|
<FormDescription>
|
||||||
Receive emails about your account activity and security.
|
Receive emails about your account activity and security.
|
||||||
</span>
|
</FormDescription>
|
||||||
</div>
|
</div>
|
||||||
|
<FormControl>
|
||||||
<Switch
|
<Switch
|
||||||
id="security_emails"
|
:checked="value"
|
||||||
v-model:checked="notificationsForm.security_emails"
|
@update:checked="handleChange"
|
||||||
disabled
|
|
||||||
/>
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid gap-2">
|
<FormField v-slot="{ handleChange, value }" type="checkbox" name="mobile">
|
||||||
<div class="flex flex-row items-start space-x-3 space-y-0">
|
<FormItem class="flex flex-row items-start space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="mobile"
|
:checked="value"
|
||||||
v-model:checked="notificationsForm.mobile"
|
@update:checked="handleChange"
|
||||||
/>
|
/>
|
||||||
<div>
|
</FormControl>
|
||||||
<Label for="mobile">
|
<div class="space-y-1 leading-none">
|
||||||
|
<FormLabel>
|
||||||
Use different settings for my mobile devices
|
Use different settings for my mobile devices
|
||||||
</Label>
|
</FormLabel>
|
||||||
<span class="text-xs text-muted-foreground">
|
<FormDescription>
|
||||||
You can manage your mobile notifications in the {{ " " }}
|
You can manage your mobile notifications in the{" "}
|
||||||
<a href="/examples/forms">mobile settings</a> page.
|
<a href="/examples/forms">
|
||||||
</span>
|
mobile settings
|
||||||
</div>
|
</a> page.
|
||||||
</div>
|
</FormDescription>
|
||||||
</div>
|
</div>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
<div class="flex justify-start">
|
<div class="flex justify-start">
|
||||||
<Button type="submit">
|
<Button type="submit">
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,8 @@ const { handleSubmit, resetForm } = useForm({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSubmit = handleSubmit((values, { resetForm }) => {
|
const onSubmit = handleSubmit((values) => {
|
||||||
console.log(values)
|
console.log('Form submitted!', values)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -89,11 +89,13 @@ const onSubmit = handleSubmit((values, { resetForm }) => {
|
||||||
<FormField v-slot="{ componentField }" name="email">
|
<FormField v-slot="{ componentField }" name="email">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Email</FormLabel>
|
<FormLabel>Email</FormLabel>
|
||||||
<FormControl>
|
|
||||||
<Select v-bind="componentField">
|
<Select v-bind="componentField">
|
||||||
|
<FormControl>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Select an email" />
|
<SelectValue placeholder="Select an email" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
<SelectItem v-for="email in verifiedEmails" :key="email" :value="email">
|
<SelectItem v-for="email in verifiedEmails" :key="email" :value="email">
|
||||||
|
|
@ -102,7 +104,6 @@ const onSubmit = handleSubmit((values, { resetForm }) => {
|
||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
You can manage verified email addresses in your email settings.
|
You can manage verified email addresses in your email settings.
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Slot } from 'radix-vue'
|
import { Slot } from 'radix-vue'
|
||||||
import { useFormField } from './FormItem.vue'
|
import { useFormField } from './useFormField'
|
||||||
|
|
||||||
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useFormField } from './FormItem.vue'
|
import { useFormField } from './useFormField'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const { formDescriptionId } = useFormField()
|
const { formDescriptionId } = useFormField()
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,17 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { FieldContextKey, useFieldError, useIsFieldDirty, useIsFieldTouched, useIsFieldValid } from 'vee-validate'
|
import { type InjectionKey } from 'vue'
|
||||||
import { inject } from 'vue'
|
|
||||||
|
|
||||||
export function useFormField() {
|
export const FORMI_TEM_INJECTION_KEY
|
||||||
const fieldContext = inject(FieldContextKey)
|
= Symbol() as InjectionKey<string>
|
||||||
|
|
||||||
const fieldState = {
|
|
||||||
valid: useIsFieldValid(),
|
|
||||||
isDirty: useIsFieldDirty(),
|
|
||||||
isTouched: useIsFieldTouched(),
|
|
||||||
error: useFieldError(),
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fieldContext)
|
|
||||||
throw new Error('useFormField should be used within <FormField>')
|
|
||||||
|
|
||||||
const { id, name } = fieldContext
|
|
||||||
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
formItemId: `${id}-form-item`,
|
|
||||||
formDescriptionId: `${id}-form-item-description`,
|
|
||||||
formMessageId: `${id}-form-item-message`,
|
|
||||||
...fieldState,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// eslint-disable-next-line import/first
|
import { provide } from 'vue'
|
||||||
|
import { useId } from 'radix-vue'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const id = useId()
|
||||||
|
provide(FORMI_TEM_INJECTION_KEY, id)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Label, type LabelProps } from 'radix-vue'
|
import { Label, type LabelProps } from 'radix-vue'
|
||||||
import { useFormField } from './FormItem.vue'
|
import { useFormField } from './useFormField'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<LabelProps>()
|
const props = defineProps<LabelProps>()
|
||||||
|
|
||||||
const { error, id, formItemId } = useFormField()
|
const { error, formItemId } = useFormField()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ErrorMessage } from 'vee-validate'
|
import { ErrorMessage } from 'vee-validate'
|
||||||
import { toValue } from 'vue'
|
import { toValue } from 'vue'
|
||||||
import { useFormField } from './FormItem.vue'
|
import { useFormField } from './useFormField'
|
||||||
|
|
||||||
const { name, formMessageId } = useFormField()
|
const { name, formMessageId } = useFormField()
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -11,6 +11,6 @@ const { name, formMessageId } = useFormField()
|
||||||
:id="formMessageId"
|
:id="formMessageId"
|
||||||
as="p"
|
as="p"
|
||||||
:name="toValue(name)"
|
:name="toValue(name)"
|
||||||
class="text-[0.8rem] font-medium text-destructive"
|
class="text-sm font-medium text-destructive"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
30
apps/www/src/lib/registry/default/ui/form/useFormField.ts
Normal file
30
apps/www/src/lib/registry/default/ui/form/useFormField.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { FieldContextKey, useFieldError, useIsFieldDirty, useIsFieldTouched, useIsFieldValid } from 'vee-validate'
|
||||||
|
import { inject } from 'vue'
|
||||||
|
import { FORMI_TEM_INJECTION_KEY } from './FormItem.vue'
|
||||||
|
|
||||||
|
export function useFormField() {
|
||||||
|
const fieldContext = inject(FieldContextKey)
|
||||||
|
const fieldItemContext = inject(FORMI_TEM_INJECTION_KEY)
|
||||||
|
|
||||||
|
const fieldState = {
|
||||||
|
valid: useIsFieldValid(),
|
||||||
|
isDirty: useIsFieldDirty(),
|
||||||
|
isTouched: useIsFieldTouched(),
|
||||||
|
error: useFieldError(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fieldContext)
|
||||||
|
throw new Error('useFormField should be used within <FormField>')
|
||||||
|
|
||||||
|
const { name } = fieldContext
|
||||||
|
const id = fieldItemContext
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
formItemId: `${id}-form-item`,
|
||||||
|
formDescriptionId: `${id}-form-item-description`,
|
||||||
|
formMessageId: `${id}-form-item-message`,
|
||||||
|
...fieldState,
|
||||||
|
}
|
||||||
|
}
|
||||||
16
apps/www/src/lib/registry/new-york/ui/form/FormControl.vue
Normal file
16
apps/www/src/lib/registry/new-york/ui/form/FormControl.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Slot } from 'radix-vue'
|
||||||
|
import { useFormField } from './useFormField'
|
||||||
|
|
||||||
|
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Slot
|
||||||
|
:id="formItemId"
|
||||||
|
:aria-describedby="!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`"
|
||||||
|
:aria-invalid="!!error"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</Slot>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useFormField } from './useFormField'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const { formDescriptionId } = useFormField()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<p
|
||||||
|
:id="formDescriptionId"
|
||||||
|
:class="cn('text-[0.8rem] text-muted-foreground', $attrs.class ?? '')"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
21
apps/www/src/lib/registry/new-york/ui/form/FormItem.vue
Normal file
21
apps/www/src/lib/registry/new-york/ui/form/FormItem.vue
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { type InjectionKey } from 'vue'
|
||||||
|
|
||||||
|
export const FORMI_TEM_INJECTION_KEY
|
||||||
|
= Symbol() as InjectionKey<string>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { provide } from 'vue'
|
||||||
|
import { useId } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const id = useId()
|
||||||
|
provide(FORMI_TEM_INJECTION_KEY, id)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn('space-y-2', $attrs.class ?? '')">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
22
apps/www/src/lib/registry/new-york/ui/form/FormLabel.vue
Normal file
22
apps/www/src/lib/registry/new-york/ui/form/FormLabel.vue
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Label, type LabelProps } from 'radix-vue'
|
||||||
|
import { useFormField } from './useFormField'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<LabelProps>()
|
||||||
|
|
||||||
|
const { error, formItemId } = useFormField()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Label
|
||||||
|
:class="cn(
|
||||||
|
'block text-sm tracking-tight font-medium text-foreground text-left',
|
||||||
|
error && 'text-destructive',
|
||||||
|
$attrs.class ?? '',
|
||||||
|
)"
|
||||||
|
:for="formItemId"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</Label>
|
||||||
|
</template>
|
||||||
16
apps/www/src/lib/registry/new-york/ui/form/FormMessage.vue
Normal file
16
apps/www/src/lib/registry/new-york/ui/form/FormMessage.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ErrorMessage } from 'vee-validate'
|
||||||
|
import { toValue } from 'vue'
|
||||||
|
import { useFormField } from './useFormField'
|
||||||
|
|
||||||
|
const { name, formMessageId } = useFormField()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ErrorMessage
|
||||||
|
:id="formMessageId"
|
||||||
|
as="p"
|
||||||
|
:name="toValue(name)"
|
||||||
|
class="text-[0.8rem] font-medium text-destructive"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
6
apps/www/src/lib/registry/new-york/ui/form/index.ts
Normal file
6
apps/www/src/lib/registry/new-york/ui/form/index.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
export { Form, Field as FormField } from 'vee-validate'
|
||||||
|
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'
|
||||||
30
apps/www/src/lib/registry/new-york/ui/form/useFormField.ts
Normal file
30
apps/www/src/lib/registry/new-york/ui/form/useFormField.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { FieldContextKey, useFieldError, useIsFieldDirty, useIsFieldTouched, useIsFieldValid } from 'vee-validate'
|
||||||
|
import { inject } from 'vue'
|
||||||
|
import { FORMI_TEM_INJECTION_KEY } from './FormItem.vue'
|
||||||
|
|
||||||
|
export function useFormField() {
|
||||||
|
const fieldContext = inject(FieldContextKey)
|
||||||
|
const fieldItemContext = inject(FORMI_TEM_INJECTION_KEY)
|
||||||
|
|
||||||
|
const fieldState = {
|
||||||
|
valid: useIsFieldValid(),
|
||||||
|
isDirty: useIsFieldDirty(),
|
||||||
|
isTouched: useIsFieldTouched(),
|
||||||
|
error: useFieldError(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fieldContext)
|
||||||
|
throw new Error('useFormField should be used within <FormField>')
|
||||||
|
|
||||||
|
const { name } = fieldContext
|
||||||
|
const id = fieldItemContext
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
formItemId: `${id}-form-item`,
|
||||||
|
formDescriptionId: `${id}-form-item-description`,
|
||||||
|
formMessageId: `${id}-form-item-message`,
|
||||||
|
...fieldState,
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user