diff --git a/apps/docs/.vitepress/config.mts b/apps/docs/.vitepress/config.mts
index ba7b598..992094c 100644
--- a/apps/docs/.vitepress/config.mts
+++ b/apps/docs/.vitepress/config.mts
@@ -1,5 +1,6 @@
import { defineConfig } from 'vitepress'
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'
+import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
import path from 'path'
// https://vitepress.dev/reference/site-config
@@ -11,6 +12,7 @@ export default defineConfig({
markdown: {
config(md) {
md.use(groupIconMdPlugin)
+ md.use(tabsMarkdownPlugin)
}
},
themeConfig: {
@@ -41,7 +43,8 @@ export default defineConfig({
{ text: 'useFieldEmits', link: '/guide/composables/useFieldEmits' },
{ text: 'useFieldProps', link: '/guide/composables/useFieldProps' },
{ text: 'useFieldValidate', link: '/guide/composables/useFieldValidate' },
- { text: 'useFormModel', link: '/guide/composables/useFormModel' }
+ { text: 'useFormModel', link: '/guide/composables/useFormModel' },
+ { text: 'useValidation', link: '/guide/composables/useValidation' }
]
},
{
diff --git a/apps/docs/.vitepress/theme/index.js b/apps/docs/.vitepress/theme/index.js
index ea4628c..b63094d 100644
--- a/apps/docs/.vitepress/theme/index.js
+++ b/apps/docs/.vitepress/theme/index.js
@@ -1,6 +1,8 @@
import DefaultTheme from 'vitepress/theme'
import VueFormGenerator from '@/index'
+import { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client'
+
import './index.css'
import '@/scss/themes/basic.scss'
import 'virtual:group-icons.css'
@@ -8,6 +10,7 @@ import 'virtual:group-icons.css'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
+ enhanceAppWithTabs(app)
app.use(VueFormGenerator, {
messages: {
productCodeValidator: 'Your product code is invalid'
diff --git a/apps/docs/guide/composables/useFieldEmits.md b/apps/docs/guide/composables/useFieldEmits.md
index 1eadcf9..a3f80d5 100644
--- a/apps/docs/guide/composables/useFieldEmits.md
+++ b/apps/docs/guide/composables/useFieldEmits.md
@@ -13,6 +13,15 @@ const emits = defineEmits(useFieldEmits())
```
+## TypeScript alternative
+```vue
+
+```
+
## Emits
### `onInput`
diff --git a/apps/docs/guide/composables/useFieldProps.md b/apps/docs/guide/composables/useFieldProps.md
index c0ce97a..1907d14 100644
--- a/apps/docs/guide/composables/useFieldProps.md
+++ b/apps/docs/guide/composables/useFieldProps.md
@@ -16,6 +16,20 @@ const { field, model } = toRefs(props)
```
+## TypeScript alternative
+```vue
+
+```
+
## Props
### `id`
diff --git a/apps/docs/guide/composables/useFieldValidate.md b/apps/docs/guide/composables/useFieldValidate.md
index 8219462..8aeee7c 100644
--- a/apps/docs/guide/composables/useFieldValidate.md
+++ b/apps/docs/guide/composables/useFieldValidate.md
@@ -1,8 +1,11 @@
---
outline: [2,3]
---
-# useFieldValidate
-> Used to validate a field against validators defined in a fields schema
+# useFieldValidate
+> Used to validate a field against validators defined in a field's schema
+::: warning
+This composable is deprecated, please use [`useValidation`](/guide/composables/useValidation) instead
+:::
## Usage
```vue
diff --git a/apps/docs/guide/composables/useValidation.md b/apps/docs/guide/composables/useValidation.md
new file mode 100644
index 0000000..cd7f97a
--- /dev/null
+++ b/apps/docs/guide/composables/useValidation.md
@@ -0,0 +1,119 @@
+---
+outline: [2,3]
+---
+# useValidation
+> Used to validate a field against validators defined in a field's schema
+
+## Usage
+::: code-group
+```Vue [Vue]
+
+```
+```Vue [Vue TS]
+
+```
+:::
+
+## Arguments
+
+### `model`
+Model object, as returned by the props
+
+### `field`
+Field schema object, as returned by the props
+
+### `currentModelValue`
+`Ref` of the current value from the field. Returned by [`useFormModel`](/guide/composables/useFormModel).
+
+### `formOptions`
+Form options object, as returned by the props.
+
+### `emits`
+Emit function as returned by `defineEmits()`
+
+### `isDisabled`
+Whether the field is disabled, can be obtained from [`useFieldAttributes()`](/guide/composables/useFieldAttributes)
+
+### `isRequired`
+Whether the field is required, can be obtained from [`useFieldAttributes()`](/guide/composables/useFieldAttributes)
+
+### `isReadonly`
+Whether the field is readonly, can be obtained from [`useFieldAttributes()`](/guide/composables/useFieldAttributes)
+
+## Returns
+
+### `errors`
+An array of errors for the current field. Will be auto-updated on every validation cycle. Must be cleared
+manually when the value of a field has changed.
+
+### `validate`
+A validation function, meant to be called when a validation has to take place. Used when a field is always validated
+at the same moment and isn't affected by validation triggers, such as `'onChanged'` or `'onBlur'`.
+
+### `onChanged`
+A wrapped validation function, only validates when `'onChanged'` validation is enabled. Should always be called if the value is changed, unless the field is unaffected by
+validation triggers.
+
+### `onBlur`
+A wrapped validation function, only validates when `'onBlur'` validation is enabled (which is the default behavior). Should always be called if the value is changed, unless the field is unaffected by
+validation triggers.
diff --git a/apps/docs/guide/customization/custom-components.md b/apps/docs/guide/customization/custom-components.md
index 77f05f3..68f0b99 100644
--- a/apps/docs/guide/customization/custom-components.md
+++ b/apps/docs/guide/customization/custom-components.md
@@ -15,35 +15,21 @@ To create a field component you make use of different composables ([?](https://v
get the behaviour you want the component to have. Different composables handle different functionality inside the field
component.
+:::tabs
+== JavaScript
Every component must at least use these composables to work properly:
- [`useFieldEmits`](/guide/composables/useFieldEmits) - returns all events emitted by a field component;
- [`useFieldProps`](/guide/composables/useFieldProps) - returns all props used by a field component;
- [`useFormModel`](/guide/composables/useFormModel) - used to get the current model value for this field component.
Optional:
-- [`useFieldValidate`](/guide/composables/useFieldValidate) - used for validation of the field;
+- [`useValidation`](/guide/composables/useValidation) - used for validation of the field;
- [`useFieldAttributes`](/guide/composables/useFieldAttributes) - holds different dynamic field attributes like `required` and `readonly`.
### Basic example
::: code-group
-```vue [template]
-
-
-
-```
+
```vue [script setup]
```
:::
+
### Advanced example
For a more advanced example, you can take a look at the [`FieldSelect`](/guide/fields/FieldSelect) ([source](https://github.com/kevinkosterr/vue3-form-generator/blob/69cb6aeb8e8c82926ec3598e7d73be2d1146a3f2/src/fields/core/FieldSelect.vue)) component.
## Compatibility with validation
::: info
If you want your component to be compatible with validation, you'll need to expose the `errors` value that is returned
-by [`useFieldValidate`](/guide/composables/useFieldValidate)
+by [`useValidation`](/guide/composables/useValidation)
:::
## Registering your component
diff --git a/apps/docs/guide/form-generator/props.md b/apps/docs/guide/form-generator/props.md
index 14e8c28..f67b8e9 100644
--- a/apps/docs/guide/form-generator/props.md
+++ b/apps/docs/guide/form-generator/props.md
@@ -12,9 +12,10 @@ outline: [ 2,3 ]
| `idPrefix` | `string` | Prefix for all the generated ids in the form |
## `formOptions`
-| Property | Type | Description |
-|---------------|----------|------------------------------------------------------|
-| `idPrefix` | `string` | Prefix for all the generated ids in the form |
+| Property | Type | Default | Description |
+|------------|--------------------------|--------|-----------------------------------------------------------------------------------------------------|
+| `idPrefix` | `string` | | Prefix for all the generated ids in the form |
+| `validate` | `'onChanged'` \| `'onBlur'` | `onBlur` | Method of validation, can be overwritten by individual fields. Can be either `onChanged` or `onBlur` |
## `model`
Type: `Object`
diff --git a/apps/docs/parts/customization/custom-components-template-example.md b/apps/docs/parts/customization/custom-components-template-example.md
new file mode 100644
index 0000000..16f4308
--- /dev/null
+++ b/apps/docs/parts/customization/custom-components-template-example.md
@@ -0,0 +1,17 @@
+```vue [template]
+
+
+
+```
\ No newline at end of file
diff --git a/apps/docs/parts/shared-field-properties.md b/apps/docs/parts/shared-field-properties.md
index c857ba8..dcc9da8 100644
--- a/apps/docs/parts/shared-field-properties.md
+++ b/apps/docs/parts/shared-field-properties.md
@@ -1,17 +1,18 @@
-| Property | Default | Type | Description |
-|-------------|------------|---------------------------------------------|-------------------------------------------------------------------------------------------------|
-| name | - | `string` | Name of the field |
-| model | - | `string` | Key of model in the form schema model |
-| label | - | `string` | Label for the field |
-| type | - | `string` | Type of field, generally `input` if the field is an input. |
-| inputType | - | `string` | Type of input, only required when `type === 'input'` |
+| Property | Default | Type | Description |
+|-------------|-----------|---------------------------------------------|-------------------------------------------------------------------------------------------------|
+| name | - | `string` | Name of the field |
+| model | - | `string` | Key of model in the form schema model |
+| label | - | `string` | Label for the field |
+| type | - | `string` | Type of field, generally `input` if the field is an input. |
+| inputType | - | `string` | Type of input, only required when `type === 'input'` |
| id | _computed_ | `string` | `id` of the field |
-| visible | `true` | `Boolean \| Function` | Whether the field is visible, method will be passed the `model`, `field` and field component* |
-| required | `false` | `Boolean \| Function` | Whether the field is required, method will be passed the `model`, `field` and field component* |
-| readonly | `false` | `Boolean \| Function` | Whether the field is read only, method will be passed the `model`, `field` and field component* |
-| disabled | `false` | `Boolean \| Function` | Whether the field is disabled, method will be passed the `model`, `field` and field component* |
-| hint | - | `string \| Function` | Hint to display underneath the field, can be passed a method* |
+| visible | `true` | `Boolean \| Function` | Whether the field is visible, method will be passed the `model`, `field` and field component* |
+| required | `false` | `Boolean \| Function` | Whether the field is required, method will be passed the `model`, `field` and field component* |
+| readonly | `false` | `Boolean \| Function` | Whether the field is read only, method will be passed the `model`, `field` and field component* |
+| disabled | `false` | `Boolean \| Function` | Whether the field is disabled, method will be passed the `model`, `field` and field component* |
+| hint | - | `string \| Function` | Hint to display underneath the field, can be passed a method* |
| validator | _computed_ | `Array \| Function \| undefined` | A (list of) validator(s) to be validating the field against. |
-| onValidated | - | `Function \| undefined` | Method to be called after validation has been completed. |
+| validate | `onBlur` | `'onChanged'` \| `'onBlur'` | Method of validation for the field. |
+| onValidated | - | `Function \| undefined` | Method to be called after validation has been completed. |
_*_ see [determineDynamicAttribute()](/guide/mixins/abstractField#determinedynamicattribute) for more information.
\ No newline at end of file
diff --git a/package.json b/package.json
index dbc5e92..8294b37 100644
--- a/package.json
+++ b/package.json
@@ -70,6 +70,7 @@
"vite-plugin-dts": "^4.3.0",
"vitepress": "^1.6.3",
"vitepress-plugin-group-icons": "^1.5.5",
+ "vitepress-plugin-tabs": "^0.7.1",
"vitest": "^2.1.1",
"vue": "^3.5.6",
"vue-eslint-parser": "^9.4.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 65c15ee..50515c5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -83,6 +83,9 @@ importers:
vitepress-plugin-group-icons:
specifier: ^1.5.5
version: 1.5.5(markdown-it@14.1.0)(vite@5.4.19(sass@1.82.0)(terser@5.37.0))
+ vitepress-plugin-tabs:
+ specifier: ^0.7.1
+ version: 0.7.1(vitepress@1.6.3(@algolia/client-search@5.25.0)(postcss@8.4.49)(sass@1.82.0)(search-insights@2.17.3)(terser@5.37.0)(typescript@5.7.2))(vue@3.5.13(typescript@5.7.2))
vitest:
specifier: '>=2.1.9'
version: 3.1.3(jsdom@25.0.1)(sass@1.82.0)(terser@5.37.0)
@@ -3040,6 +3043,12 @@ packages:
markdown-it: '>=14'
vite: '>=3'
+ vitepress-plugin-tabs@0.7.1:
+ resolution: {integrity: sha512-jxJvsicxnMSIYX9b8mAFLD2nwyKUcMO10dEt4nDSbinZhM8cGvAmMFOHPdf6TBX6gYZRl+/++/iYHtoM14fERQ==}
+ peerDependencies:
+ vitepress: ^1.0.0
+ vue: ^3.5.0
+
vitepress@1.6.3:
resolution: {integrity: sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==}
hasBin: true
@@ -6188,6 +6197,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ vitepress-plugin-tabs@0.7.1(vitepress@1.6.3(@algolia/client-search@5.25.0)(postcss@8.4.49)(sass@1.82.0)(search-insights@2.17.3)(terser@5.37.0)(typescript@5.7.2))(vue@3.5.13(typescript@5.7.2)):
+ dependencies:
+ vitepress: 1.6.3(@algolia/client-search@5.25.0)(postcss@8.4.49)(sass@1.82.0)(search-insights@2.17.3)(terser@5.37.0)(typescript@5.7.2)
+ vue: 3.5.13(typescript@5.7.2)
+
vitepress@1.6.3(@algolia/client-search@5.25.0)(postcss@8.4.49)(sass@1.82.0)(search-insights@2.17.3)(terser@5.37.0)(typescript@5.7.2):
dependencies:
'@docsearch/css': 3.8.2
diff --git a/src/FormGenerator.vue b/src/FormGenerator.vue
index 19e0934..141579f 100644
--- a/src/FormGenerator.vue
+++ b/src/FormGenerator.vue
@@ -51,7 +51,9 @@ const props = withDefaults(defineProps(), {
enctype: 'application/x-www-form-urlencoded',
id: '',
idPrefix: '', // Kept for compatibility reasons.
- options: () => ({})
+ options: () => ({
+ validate: 'onBlur' // Always validate onBlur by default.
+ })
})
type FormGroupInstance = ComponentPublicInstance>
diff --git a/src/composables/index.ts b/src/composables/index.ts
index ef00f9b..61a9cac 100644
--- a/src/composables/index.ts
+++ b/src/composables/index.ts
@@ -3,11 +3,15 @@ import { useFieldValidate } from '@/composables/useFieldValidate'
import { useFieldAttributes } from '@/composables/useFieldAttributes'
import { useFieldProps } from '@/composables/useFieldProps'
import { useFieldEmits } from '@/composables/useFieldEmits'
+import { useValidationWrapper } from '@/composables/useValidationWrapper'
+import { useValidation } from '@/composables/useValidation'
export {
useFormModel,
useFieldValidate,
useFieldAttributes,
useFieldProps,
- useFieldEmits
+ useFieldEmits,
+ useValidationWrapper,
+ useValidation
}
\ No newline at end of file
diff --git a/src/composables/useFieldEmits.ts b/src/composables/useFieldEmits.ts
index 760b8c1..fb36aa9 100644
--- a/src/composables/useFieldEmits.ts
+++ b/src/composables/useFieldEmits.ts
@@ -1,3 +1,5 @@
-export function useFieldEmits (): string[] {
+import type { FieldEmits } from '@/resources/types/field/fields'
+
+export function useFieldEmits (): (keyof FieldEmits)[] {
return [ 'onInput', 'validated' ]
}
\ No newline at end of file
diff --git a/src/composables/useFieldValidate.ts b/src/composables/useFieldValidate.ts
index a169fc8..c49c7d9 100644
--- a/src/composables/useFieldValidate.ts
+++ b/src/composables/useFieldValidate.ts
@@ -1,32 +1,14 @@
import { ref, Ref, computed, ComputedRef } from 'vue'
import { getMessage } from '@/validators/messages'
-import { isFunction, isString, toUniqueArray } from '@/helpers'
+import { isFunction, toUniqueArray, getValidator } from '@/helpers'
import { TValidatorFunction } from '@/resources/types/functions'
-import { ValidatorMap } from '@/resources/types/generic'
import type { FormModel } from '@/resources/types/fieldAttributes'
import type { Field } from '@/resources/types/field/fields'
import validators from '@/validators'
/**
- * Get the corresponding validator function for a given string, or function. If a function is passed, the function is
- * assumed to be the validator to use and thus return. If no argument is passed, we'll just return a function that
- * will always return true, thus assuming the value is always valid.
- * @param validator
+ * @deprecated will be removed in 3.0.0, use `useValidation` instead.
*/
-function getValidator (validator: string | TValidatorFunction | undefined): TValidatorFunction {
- if (validator === undefined) return (): boolean => true
-
- if (isFunction(validator)) return validator
-
- if (isString(validator)) {
- if ((validators as ValidatorMap)[validator] === undefined) {
- throw new Error('Invalid validator: ' + validator)
- }
- return (validators as ValidatorMap)[validator]
- }
- return (): boolean => true
-}
-
export function useFieldValidate (
model: FormModel,
field: Field,
diff --git a/src/composables/useValidation.ts b/src/composables/useValidation.ts
new file mode 100644
index 0000000..5ed69eb
--- /dev/null
+++ b/src/composables/useValidation.ts
@@ -0,0 +1,124 @@
+import type { Field, FieldEmits } from '@/resources/types/field/fields'
+import type { FormModel } from '@/resources/types/fieldAttributes'
+import type { FormOptions, ValidationTrigger } from '@/resources/types/generic'
+import { type Ref, type ComputedRef, type EmitFn, computed, ref } from 'vue'
+
+import { isFunction, toUniqueArray, getValidator } from '@/helpers'
+import { getMessage } from '@/validators/messages'
+import { useValidationWrapper } from '@/composables/useValidationWrapper'
+import { TValidatorFunction } from '@/resources/types/functions'
+
+import validators from '@/validators'
+
+/**
+ * Composable for validation of the value(s) from a field.
+ * Houses all the necessary logic for performing a validation and handles the emitted events.
+ * @param model - model object from the form.
+ * @param field - field schema object.
+ * @param currentModelValue - current model value Ref.
+ * @param formOptions
+ * @param emits
+ * @param isDisabled
+ * @param isRequired
+ * @param isReadOnly
+ */
+export function useValidation(
+ model: FormModel,
+ field: Field,
+ currentModelValue: Ref,
+ formOptions: FormOptions,
+ emits: EmitFn,
+ isDisabled: boolean,
+ isRequired: boolean,
+ isReadOnly: boolean
+) {
+
+ const errors: Ref = ref([])
+
+ /**
+ * The method of validation determined by this field.
+ * Can be either 'onChanged', 'onBlur' or undefined.
+ */
+ const validationMethod: ComputedRef = computed(() => {
+ return field.validate
+ })
+
+ /**
+ * Compute all validators that should be present by default.
+ */
+ const defaultValidators: ComputedRef = computed(() => {
+ const fieldValidators: TValidatorFunction[] = []
+ if (!isDisabled && !isReadOnly) {
+ if (isRequired && !fieldValidators.includes(validators.required)) {
+ fieldValidators.push(validators.required)
+ }
+
+ if ('min' in field && field.min) {
+ fieldValidators.push(validators.min)
+ }
+
+ if ('max' in field && field.max) {
+ fieldValidators.push(validators.max)
+ }
+ }
+ return fieldValidators
+ })
+
+ /**
+ * Emit the 'validated' event.
+ * @param isValid - whether the field value is valid.
+ * @param errors - errors found during validation.
+ * @param field - field object.
+ */
+ const emitValidated = (isValid: boolean, errors: string[], field: Field): void => {
+ emits('validated', isValid, errors, field)
+ }
+
+ /**
+ * Validate the field against given validators, plus the validators that are put there by default.
+ */
+ const validate = async (): Promise => {
+ if (!('validator' in field)) {
+ emitValidated(true, [], field)
+ return
+ }
+
+ const results: string[] = []
+ const fieldValidators: TValidatorFunction[] = [ ...defaultValidators.value ]
+
+ if (Array.isArray(field.validator)) {
+ field.validator.forEach((validator: any) => fieldValidators.push(getValidator(validator)))
+ } else {
+ fieldValidators.push(getValidator(field.validator))
+ }
+
+ fieldValidators.forEach((validator: TValidatorFunction): void => {
+ const isValid: boolean = validator(currentModelValue.value, field, model)
+ if (!isValid) results.push(getMessage(validator.name))
+ })
+
+ const uniqueResults = toUniqueArray(results)
+
+ if ('onValidated' in field && field.onValidated) {
+ if (isFunction(field.onValidated)) {
+ field.onValidated.call(null, model, uniqueResults, field)
+ } else {
+ throw new Error('onValidated property must be of type `function` on field: ' + field.name)
+ }
+ }
+
+ errors.value = uniqueResults
+ emitValidated(uniqueResults.length === 0, uniqueResults, field)
+ }
+
+ const onChanged = useValidationWrapper(validate, 'onChanged', validationMethod.value, formOptions.validate)
+ const onBlur = useValidationWrapper(validate, 'onBlur', validationMethod.value, formOptions.validate)
+
+ return {
+ errors,
+ validate,
+ onChanged,
+ onBlur
+ }
+
+}
\ No newline at end of file
diff --git a/src/composables/useValidationWrapper.ts b/src/composables/useValidationWrapper.ts
new file mode 100644
index 0000000..a2be833
--- /dev/null
+++ b/src/composables/useValidationWrapper.ts
@@ -0,0 +1,30 @@
+import { type ValidationTrigger } from '@/resources/types/generic'
+
+
+/**
+ * Use a provided validator function when the trigger of that validator matches
+ * the validation method used for the current form or form field.
+ * @param {Function} fn - validator function to execute upon meeting conditions
+ * @param {ValidationTrigger} trigger - trigger for the validation, 'onBlur' or 'onChanged'
+ * @param {string | undefined} fieldValidationMethod - validation method as set by the field schema, can be undefined.
+ * @param {string | undefined} formValidationMethod - validation method as set by the form options, can be undefined.
+ */
+export function useValidationWrapper(
+ fn: (...args: any[]) => void,
+ trigger: ValidationTrigger,
+ fieldValidationMethod: string | undefined,
+ formValidationMethod: string | undefined
+) {
+ return (...args: any[]) => {
+ if (fieldValidationMethod !== undefined) {
+ return fieldValidationMethod === trigger ? fn(...args) : undefined
+ }
+
+ if (formValidationMethod !== undefined) {
+ return formValidationMethod === trigger ? fn(...args) : undefined
+ }
+
+ return trigger === 'onBlur' ? fn(...args) : undefined
+ }
+
+}
\ No newline at end of file
diff --git a/src/fields/core/FieldColor.vue b/src/fields/core/FieldColor.vue
index 8a7570a..28d41ca 100644
--- a/src/fields/core/FieldColor.vue
+++ b/src/fields/core/FieldColor.vue
@@ -7,6 +7,9 @@
type="text"
:value="currentModelValue"
placeholder="#ffffff"
+ :required="isRequired"
+ :readonly="isReadonly"
+ :disabled="isDisabled"
@input="onFieldValueChanged"
@blur="onBlur"
>
@@ -17,6 +20,8 @@
:name="props.field.name"
:value="currentModelValue"
:required="isRequired"
+ :readonly="isReadonly"
+ :disabled="isDisabled"
@input="onFieldValueChanged"
@blur="onBlur"
>
@@ -29,14 +34,13 @@ import { toRefs, onBeforeMount } from 'vue'
import {
useFormModel,
useFieldAttributes,
- useFieldValidate,
- useFieldEmits
+ useValidation
} from '@/composables'
import { vMaska } from 'maska/vue'
-import type { ColorField, FieldPropRefs, FieldProps } from '@/resources/types/field/fields'
+import type { ColorField, FieldEmits, FieldPropRefs, FieldProps } from '@/resources/types/field/fields'
import type { MaskOptions } from 'maska'
-const emits = defineEmits(useFieldEmits())
+const emits = defineEmits()
const props = defineProps>()
const maskOptions: Readonly = {
@@ -51,31 +55,25 @@ const maskOptions: Readonly = {
const { field, model }: FieldPropRefs = toRefs(props)
const { currentModelValue } = useFormModel(model.value, field.value)
-const { isRequired, isVisible, hint } = useFieldAttributes(model.value, field.value)
-const { errors, validate } = useFieldValidate(
+const { isRequired, isVisible, isDisabled, isReadonly, hint } = useFieldAttributes(model.value, field.value)
+const { errors, onChanged, onBlur } = useValidation(
model.value,
field.value,
- false,
+ currentModelValue,
+ props.formOptions,
+ emits,
+ isDisabled.value,
isRequired.value,
- false
+ isReadonly.value
)
-const onBlur = () => {
- validate(currentModelValue.value).then((validationErrors) => {
- emits('validated',
- validationErrors.length === 0,
- validationErrors,
- field.value
- )
- })
-}
-
const onFieldValueChanged = (event: Event) => {
const target = event.target as HTMLInputElement
errors.value = []
// Ensure a change doesn't emit twice; we need this because both inputs might trigger this function at once.
if (target.value !== currentModelValue.value) {
emits('onInput', target.value)
+ onChanged()
}
}
@@ -88,7 +86,7 @@ onBeforeMount(() => {
} else if (field.value.validator !== undefined) {
fieldValidators.push(field.value.validator)
}
- // Keep in mind that the native color picker only supports 6 digit hex codes,
+ // Keep in mind that the native color picker only supports 6-digit hex codes,
// so even though a value might technically be valid, it won't display the right color on the color picker input.
fieldValidators.push(validators.hexColorValue)
field.value.validator = fieldValidators
diff --git a/src/fields/core/FieldMask.vue b/src/fields/core/FieldMask.vue
index c645bbb..6b616a3 100644
--- a/src/fields/core/FieldMask.vue
+++ b/src/fields/core/FieldMask.vue
@@ -13,13 +13,13 @@