feat(Form): add valibot supprt (#615)

Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
SevicheCC
2023-09-07 07:58:25 -05:00
committed by Benjamin Canac
parent eebff72d01
commit ab5153ac19
5 changed files with 130 additions and 4 deletions

View File

@@ -0,0 +1,42 @@
<script setup lang="ts">
import { ref } from 'vue'
import { string, object, email, minLength, Input } from 'valibot'
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
const schema = object({
email: string([email('Invalid email')]),
password: string([minLength(8, 'Must be at least 8 characters')])
})
type Schema = Input<typeof schema>
const state = ref({
email: undefined,
password: undefined
})
async function submit (event: FormSubmitEvent<Schema>) {
// Do something with event.data
console.log(event.data)
}
</script>
<template>
<UForm
:schema="schema"
:state="state"
@submit="submit"
>
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
</UFormGroup>
<UFormGroup label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormGroup>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>

View File

@@ -8,7 +8,7 @@ links:
## Usage
Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi) or your own validation logic. It works seamlessly with the FormGroup component to automatically display error messages around form elements.
Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://valibot.dev/) or your own validation logic. It works seamlessly with the FormGroup component to automatically display error messages around form elements.
The Form component requires the `validate` and `state` props for form validation.
@@ -69,7 +69,7 @@ async function submit (event: FormSubmitEvent<any>) {
## Schema
You can provide a schema from [Yup](#yup), [Zod](#zod) or [Joi](#joi) through the `schema` prop to validate the state. It's important to note that **none of these libraries are included** by default, so make sure to **install the one you need**.
You can provide a schema from [Yup](#yup), [Zod](#zod) or [Joi](#joi), [Valibot](#valibot) through the `schema` prop to validate the state. It's important to note that **none of these libraries are included** by default, so make sure to **install the one you need**.
### Yup
@@ -232,6 +232,59 @@ async function submit (event: FormSubmitEvent<any>) {
```
::
### Valibot
::component-example
#default
:form-example-valibot{class="space-y-4 w-60"}
#code
```vue
<script setup lang="ts">
import { ref } from 'vue'
import { string, object, email, minLength, Input } from 'valibot'
import type { FormSubmitEvent } from '@nuxt/ui/dist/runtime/types'
const schema = object({
email: string([email('Invalid email')]),
password: string([minLength(8, 'Must be at least 8 characters')])
})
type Schema = Input<typeof schema>
const state = ref({
email: undefined,
password: undefined
})
async function submit (event: FormSubmitEvent<Schema>) {
// Do something with event.data
console.log(event.data)
}
</script>
<template>
<UForm
:schema="schema"
:state="state"
@submit="submit"
>
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
</UFormGroup>
<UFormGroup label="Password" name="password">
<UInput v-model="state.password" type="password" />
</UFormGroup>
<UButton type="submit">
Submit
</UButton>
</UForm>
</template>
```
::
## Other libraries
For other validation libraries, you can define your own component with custom validation logic.

View File

@@ -62,7 +62,8 @@
"unbuild": "^2.0.0",
"vue-tsc": "^1.8.10",
"yup": "^1.2.0",
"zod": "^3.22.2"
"zod": "^3.22.2",
"valibot": "^0.13.1"
},
"pnpm": {
"patchedDependencies": {

7
pnpm-lock.yaml generated
View File

@@ -98,6 +98,9 @@ importers:
unbuild:
specifier: ^2.0.0
version: 2.0.0(typescript@5.2.2)
valibot:
specifier: ^0.13.1
version: 0.13.1
vue-tsc:
specifier: ^1.8.10
version: 1.8.10(typescript@5.2.2)
@@ -12570,6 +12573,10 @@ packages:
vue-screen-utils: 1.0.0-beta.13(vue@3.3.4)
dev: true
/valibot@0.13.1:
resolution: {integrity: sha512-SG2W1RHqE2LShl3p6tyERt6I+G6PQa9ZFVfkyNKXz01HBzL+tBeH5kXw/5AQeAzPJSjI3djVGBl1CyozA1kyBQ==}
dev: true
/validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
dependencies:

View File

@@ -10,6 +10,8 @@ import { useEventBus } from '@vueuse/core'
import type { ZodSchema } from 'zod'
import type { ValidationError as JoiError, Schema as JoiSchema } from 'joi'
import type { ObjectSchema as YupObjectSchema, ValidationError as YupError } from 'yup'
import type { ObjectSchema as ValibotObjectSchema } from 'valibot'
import { safeParseAsync } from 'valibot'
import type { FormError, FormEvent, FormEventType, FormSubmitEvent, Form } from '../../types/form'
export default defineComponent({
@@ -18,7 +20,8 @@ export default defineComponent({
type: Object as
| PropType<ZodSchema>
| PropType<YupObjectSchema<any>>
| PropType<JoiSchema>,
| PropType<JoiSchema>
| PropType<ValibotObjectSchema<any>>,
default: undefined
},
state: {
@@ -61,6 +64,8 @@ export default defineComponent({
errs = errs.concat(await getYupErrors(props.state, props.schema))
} else if (isJoiSchema(props.schema)) {
errs = errs.concat(await getJoiErrors(props.state, props.schema))
} else if (isValibotSchema(props.schema)) {
errs = errs.concat(await getValibotError(props.state, props.schema))
} else {
throw new Error('Form validation failed: Unsupported form schema')
}
@@ -204,4 +209,22 @@ async function getJoiErrors (
}
}
}
function isValibotSchema (schema: any): schema is ValibotObjectSchema<any> {
return schema._parse !== undefined
}
async function getValibotError (
state: any,
schema: ValibotObjectSchema<any>
): Promise<FormError[]> {
const result = await safeParseAsync(schema, state)
if (result.success === false) {
return result.issues.map((issue) => ({
path: issue.path.map(p => p.key).join('.'),
message: issue.message
}))
}
return []
}
</script>