diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 9caef3c0..db30a80c 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -12,5 +12,6 @@ module.exports = {
'no-console': 'warn',
'no-tabs': 'off',
'no-invalid-character': 'off',
+ 'import/first': 'off',
},
}
diff --git a/apps/www/.vitepress/theme/components/TabPreview.vue b/apps/www/.vitepress/theme/components/TabPreview.vue
index 025e1c34..46bb64ee 100644
--- a/apps/www/.vitepress/theme/components/TabPreview.vue
+++ b/apps/www/.vitepress/theme/components/TabPreview.vue
@@ -3,10 +3,14 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/default
const props = withDefaults(defineProps<{
name: string
+ names?: string[]
align?: 'center' | 'start' | 'end'
sfcTsCode?: string
sfcTsHtml?: string
-}>(), { align: 'center' })
+}>(), {
+ align: 'center',
+ names: () => ['CLI', 'Manual'],
+})
@@ -15,24 +19,17 @@ const props = withDefaults(defineProps<{
- CLI
-
-
- Manual
+ {{ tab }}
-
-
-
-
-
+
+
diff --git a/apps/www/.vitepress/theme/config/docs.ts b/apps/www/.vitepress/theme/config/docs.ts
index 9c6d2c53..43908ea9 100644
--- a/apps/www/.vitepress/theme/config/docs.ts
+++ b/apps/www/.vitepress/theme/config/docs.ts
@@ -217,9 +217,8 @@ export const docsConfig: DocsConfig = {
},
{
title: 'Form',
- href: '#',
- label: 'Soon',
- disabled: true,
+ href: '/docs/components/form',
+ label: 'New',
items: [],
},
{
@@ -352,10 +351,10 @@ export const examples: Example[] = [
code: 'https://github.com/radix-vue/shadcn-vue/tree/dev/apps/www/src/examples/cards',
},
// {
- // name: "Tasks",
- // href: "/examples/tasks",
- // label: "New",
- // code: "https://github.com/radix-vue/shadcn-vue/tree/dev/apps/www/src/examples/tasks"
+ // name: "Tasks",
+ // href: "/examples/tasks",
+ // label: "New",
+ // code: "https://github.com/radix-vue/shadcn-vue/tree/dev/apps/www/src/examples/tasks"
// },
{
name: 'Playground',
diff --git a/apps/www/package.json b/apps/www/package.json
index 78f712c6..dc6bb0d0 100644
--- a/apps/www/package.json
+++ b/apps/www/package.json
@@ -14,19 +14,21 @@
"dependencies": {
"@morev/vue-transitions": "^2.3.6",
"@radix-icons/vue": "^1.0.0",
- "@tanstack/vue-table": "^8.9.9",
+ "@tanstack/vue-table": "^8.10.3",
"@unovis/ts": "^1.2.1",
"@unovis/vue": "1.3.0-alpha.3",
+ "@vee-validate/zod": "^4.11.7",
"@vueuse/core": "^10.4.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"date-fns": "^2.30.0",
"lucide-vue-next": "^0.276.0",
"tailwindcss-animate": "^1.0.7",
- "v-calendar": "^3.0.3",
+ "v-calendar": "^3.1.0",
+ "vee-validate": "4.11.7",
"vue": "^3.3.4",
"vue-wrap-balancer": "^1.1.3",
- "zod": "^3.22.2"
+ "zod": "^3.22.4"
},
"devDependencies": {
"@iconify-json/radix-icons": "^1.1.11",
@@ -35,21 +37,21 @@
"@iconify/vue": "^4.1.1",
"@types/lodash.template": "^4.5.1",
"@types/node": "^20.6.0",
- "@vitejs/plugin-vue": "^4.3.4",
+ "@vitejs/plugin-vue": "^4.4.0",
"@vitejs/plugin-vue-jsx": "^3.0.2",
"@vue/compiler-core": "^3.3.4",
"@vue/compiler-dom": "^3.3.4",
- "autoprefixer": "^10.4.15",
+ "autoprefixer": "^10.4.16",
"lodash.template": "^4.5.0",
"radix-vue": "^0.4.1",
"rimraf": "^5.0.1",
"tailwind-merge": "^1.14.0",
"tailwindcss": "^3.3.3",
- "tsx": "^3.12.10",
+ "tsx": "^3.13.0",
"typescript": "^5.2.2",
"unplugin-icons": "^0.17.0",
- "vite": "^4.4.9",
- "vitepress": "^1.0.0-rc.13",
- "vue-tsc": "^1.8.11"
+ "vite": "^4.4.11",
+ "vitepress": "^1.0.0-rc.20",
+ "vue-tsc": "^1.8.15"
}
}
diff --git a/apps/www/src/content/docs/components/checkbox.md b/apps/www/src/content/docs/components/checkbox.md
index 4eb6ea46..07e24bfc 100644
--- a/apps/www/src/content/docs/components/checkbox.md
+++ b/apps/www/src/content/docs/components/checkbox.md
@@ -30,6 +30,16 @@ import { Checkbox } from '@/components/ui/checkbox'
## Examples
+### With text
+
+
+
### Disabled
-
\ No newline at end of file
+
+
+### Form
+
+
+
+
diff --git a/apps/www/src/content/docs/components/combobox.md b/apps/www/src/content/docs/components/combobox.md
index e8f021da..8f909099 100644
--- a/apps/www/src/content/docs/components/combobox.md
+++ b/apps/www/src/content/docs/components/combobox.md
@@ -86,4 +86,20 @@ const value = ref({})
```
-
\ No newline at end of file
+## Examples
+
+### Combobox
+
+
+
+### Popover
+
+
+
+### Dropdown menu
+
+
+
+### Form
+
+
diff --git a/apps/www/src/content/docs/components/date-picker.md b/apps/www/src/content/docs/components/date-picker.md
index 4acfd90b..43aef149 100644
--- a/apps/www/src/content/docs/components/date-picker.md
+++ b/apps/www/src/content/docs/components/date-picker.md
@@ -56,12 +56,18 @@ const date = ref()
## Examples
-### Date Range Picker
-
-
-
-
### Date Picker
+
-
\ No newline at end of file
+### Date Range Picker
+
+
+
+### With Presets
+
+
+
+### Form
+
+
diff --git a/apps/www/src/content/docs/components/form.md b/apps/www/src/content/docs/components/form.md
new file mode 100644
index 00000000..4721788b
--- /dev/null
+++ b/apps/www/src/content/docs/components/form.md
@@ -0,0 +1,331 @@
+---
+title: VeeValidate
+description: Building forms with VeeValidate and Zod.
+---
+
+Forms are tricky. They are one of the most common things you'll build in a web application, but also one of the most complex.
+
+Well-designed HTML forms are:
+
+- Well-structured and semantically correct.
+- Easy to use and navigate (keyboard).
+- Accessible with ARIA attributes and proper labels.
+- Has support for client and server side validation.
+- Well-styled and consistent with the rest of the application.
+
+In this guide, we will take a look at building forms with [`vee-validate`](https://vee-validate.logaretm.com/v4/) and [`zod`](https://zod.dev). We're going to use a `` component to compose accessible forms using Radix Vue components.
+
+
+## Features
+
+The `` component is a wrapper around the `vee-validate` library. It provides a few things:
+
+
+- Composable components for building forms.
+- A `` component for building controlled form fields.
+- Form validation using `zod`.
+- Applies the correct `aria` attributes to form fields based on states, handle unqiue IDs
+- Built to work with all Radix Vue components.
+- Bring your own schema library. We use `zod` but you can use any other supported schema validation you want, like [`yup`](https://github.com/jquense/yup) or [`valibot`](https://valibot.dev/).
+- **You have full control over the markup and styling.**
+
+[`vee-validate`](https://vee-validate.logaretm.com/v4/) makes use of two flavors to add validation to your forms.
+- Composition API
+- Higher-order components (HOC)
+
+## Anatomy
+
+```vue
+
+```
+
+## Example
+
+
+
+
+
+#### `Input` Component
+
+```vue
+
+
+ Username
+
+
+
+
+
+
+
+```
+
+
+
+
+
+#### native `input` element
+
+```vue
+
+
+ Username
+
+
+
+
+
+
+
+```
+
+
+
+
+## Installation
+
+
+
+
+```bash
+npx shadcn-vue@latest add form
+```
+
+
+
+
+
+
+### Install the following dependency:
+
+```bash
+npm install radix-vue vee-validate @vee-validate/zod zod
+```
+
+### Copy and paste the following codes into your project:
+
+`index.ts`
+
+<<< @/lib/registry/default/ui/form/index.ts
+
+`FormItem.vue`
+
+<<< @/lib/registry/default/ui/form/FormItem.vue
+
+`FormLabel.vue`
+
+<<< @/lib/registry/default/ui/form/FormLabel.vue
+
+`FormControl.vue`
+
+<<< @/lib/registry/default/ui/form/FormControl.vue
+
+`FormMessage.vue`
+
+<<< @/lib/registry/default/ui/form/FormMessage.vue
+
+`FormDescription.vue`
+
+<<< @/lib/registry/default/ui/form/FormDescription.vue
+
+### Update the import paths to match your project setup.
+
+
+
+
+
+
+## Usage
+
+
+
+### Create a form schema
+
+Define the shape of your form using a Zod schema. You can read more about using Zod in the [Zod documentation](https://zod.dev).
+
+Use `@vee-validate/zod` to integrate Zod schema validation with `vee-validate`
+
+`toTypedSchema` also makes the form values and submitted values typed automatically and caters for both input and output types of that schema.
+
+```vue showLineNumbers {2-3,5-7}
+
+```
+
+
+### Define a form
+
+Use the `useForm` composable from `vee-validate` or use `` component to create a from.
+
+
+
+
+
+```vue showLineNumbers {2,19-21}
+
+
+
+
+
+```
+
+
+
+
+
+```vue showLineNumbers {5,24-26}
+
+
+
+
+
+```
+
+
+
+
+### Build your form
+
+Based on last step we can either use `` component or `useForm` composable
+`useForm` is recommended cause values are typed automatically
+
+```vue showLineNumbers {2}
+
+
+
+
+
+```
+
+### Done
+
+That's it. You now have a fully accessible form that is type-safe with client-side validation.
+
+
+
+
+
+## Examples
+
+See the following links for more examples on how to use the `vee-validate` features with other components:
+
+- [Checkbox](/docs/components/checkbox#form)
+- [Date Picker](/docs/components/date-picker#form)
+- [Input](/docs/components/input#form)
+- [Radio Group](/docs/components/radio-group#form)
+- [Select](/docs/components/select#form)
+- [Switch](/docs/components/switch#form)
+- [Textarea](/docs/components/textarea#form)
+- [Combobox](/docs/components/combobox#form)
diff --git a/apps/www/src/content/docs/components/input.md b/apps/www/src/content/docs/components/input.md
index bfa96c75..6c120676 100644
--- a/apps/www/src/content/docs/components/input.md
+++ b/apps/www/src/content/docs/components/input.md
@@ -41,4 +41,28 @@ import { Input } from '@/components/ui/input'
-```
\ No newline at end of file
+```
+
+### Default
+
+
+
+### File
+
+
+
+### Disabled
+
+
+
+### With Label
+
+
+
+### With Button
+
+
+
+### Form
+
+
diff --git a/apps/www/src/content/docs/components/radio-group.md b/apps/www/src/content/docs/components/radio-group.md
index a2b9a762..d7db0bc9 100644
--- a/apps/www/src/content/docs/components/radio-group.md
+++ b/apps/www/src/content/docs/components/radio-group.md
@@ -34,4 +34,10 @@ import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
-```
\ No newline at end of file
+```
+
+## Examples
+
+### Form
+
+
diff --git a/apps/www/src/content/docs/components/select.md b/apps/www/src/content/docs/components/select.md
index d653bf0a..caae327d 100644
--- a/apps/www/src/content/docs/components/select.md
+++ b/apps/www/src/content/docs/components/select.md
@@ -46,3 +46,9 @@ import {
```
+
+## Examples
+
+### Form
+
+
diff --git a/apps/www/src/content/docs/components/switch.md b/apps/www/src/content/docs/components/switch.md
index ccb8c1ad..a359a4df 100644
--- a/apps/www/src/content/docs/components/switch.md
+++ b/apps/www/src/content/docs/components/switch.md
@@ -47,4 +47,10 @@ import { Switch } from '@/components/ui/switch'
-```
\ No newline at end of file
+```
+
+## Examples
+
+### Form
+
+
diff --git a/apps/www/src/content/docs/components/textarea.md b/apps/www/src/content/docs/components/textarea.md
index 39229b03..24b31da8 100644
--- a/apps/www/src/content/docs/components/textarea.md
+++ b/apps/www/src/content/docs/components/textarea.md
@@ -45,4 +45,30 @@ import { Textarea } from '@/components/ui/textarea'
-```
\ No newline at end of file
+```
+
+## Examples
+
+### Default
+
+
+
+### Disabled
+
+
+
+### With Label
+
+
+
+### With Text
+
+
+
+### With Button
+
+
+
+### Form
+
+
diff --git a/apps/www/src/examples/forms/components/AccountForm.vue b/apps/www/src/examples/forms/components/AccountForm.vue
index b6a488a5..43d21c13 100644
--- a/apps/www/src/examples/forms/components/AccountForm.vue
+++ b/apps/www/src/examples/forms/components/AccountForm.vue
@@ -2,21 +2,23 @@
import { ref } from 'vue'
import * as z from 'zod'
import { format } from 'date-fns'
+import { toTypedSchema } from '@vee-validate/zod'
+import { configure } from 'vee-validate'
+import { Check, ChevronsUpDown } from 'lucide-vue-next'
import { cn } from '@/lib/utils'
import RadixIconsCalendar from '~icons/radix-icons/calendar'
+import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/default/ui/form'
import { Input } from '@/lib/registry/new-york/ui/input'
-import { Label } from '@/lib/registry/new-york/ui/label'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import {
- Select,
- SelectContent,
- SelectGroup,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from '@/lib/registry/new-york/ui/select'
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+} from '@/lib/registry/default/ui/command'
import { Button } from '@/lib/registry/new-york/ui/button'
import {
Popover,
@@ -25,11 +27,7 @@ import {
} from '@/lib/registry/default/ui/popover'
import { Calendar } from '@/lib/registry/new-york/ui/calendar'
-const accountForm = ref({
- name: '',
- dob: null,
- language: '',
-})
+const open = ref(false)
const languages = [
{ label: 'English', value: 'en' },
@@ -43,7 +41,7 @@ const languages = [
{ label: 'Chinese', value: 'zh' },
] as const
-const accountFormSchema = z.object({
+const accountFormSchema = toTypedSchema(z.object({
name: z
.string()
.min(2, {
@@ -58,20 +56,14 @@ const accountFormSchema = z.object({
language: z.string().nonempty({
message: 'Please select a language.',
}),
-})
+}))
-type AccountFormValues = z.infer
-const errors = ref | null>(null)
+const filterFunction = (list: typeof languages, search: string) => list.filter(i => i.value.toLowerCase().includes(search.toLowerCase()))
-async function handleSubmit() {
- const result = accountFormSchema.safeParse(accountForm.value)
- if (!result.success) {
- errors.value = result.error.format()
- console.log(errors.value)
- return
- }
-
- console.log('Form submitted!', accountForm.value)
+// https://github.com/logaretm/vee-validate/issues/3521
+// https://github.com/logaretm/vee-validate/discussions/3571
+async function onSubmit(values: any) {
+ console.log('Form submitted!', values)
}
@@ -85,74 +77,107 @@ async function handleSubmit() {
-
+
diff --git a/apps/www/src/examples/forms/components/AppearanceForm.vue b/apps/www/src/examples/forms/components/AppearanceForm.vue
index 190d5be2..9b58d9c1 100644
--- a/apps/www/src/examples/forms/components/AppearanceForm.vue
+++ b/apps/www/src/examples/forms/components/AppearanceForm.vue
@@ -1,27 +1,16 @@
@@ -56,109 +43,106 @@ async function handleSubmit() {
-