@vuehookform/core 0.2.11 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -29
- package/dist/core/domSync.d.ts +0 -7
- package/dist/core/formContext.d.ts +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +324 -35
- package/dist/utils/devWarnings.d.ts +55 -0
- package/dist/vuehookform.cjs +398 -21
- package/dist/vuehookform.js +397 -21
- package/package.json +22 -18
package/README.md
CHANGED
|
@@ -7,10 +7,10 @@ A TypeScript-first form library for Vue 3, inspired by React Hook Form.
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- **TypeScript First** - Perfect type inference with zero manual typing
|
|
10
|
-
- **
|
|
10
|
+
- **Field Arrays** - Dynamic lists with stable keys built-in
|
|
11
11
|
- **Performant** - Minimal re-renders using uncontrolled inputs
|
|
12
12
|
- **Zod Native** - First-class Zod integration for validation
|
|
13
|
-
- **Tiny Bundle** -
|
|
13
|
+
- **Tiny Bundle** - ~10kb gzipped, tree-shakable
|
|
14
14
|
- **UI Agnostic** - Works with any UI library or custom components
|
|
15
15
|
|
|
16
16
|
## Quick Start
|
|
@@ -73,6 +73,13 @@ type UserForm = z.infer<typeof userSchema>
|
|
|
73
73
|
<script setup>
|
|
74
74
|
const { register, fields } = useForm({ schema })
|
|
75
75
|
const addresses = fields('addresses')
|
|
76
|
+
|
|
77
|
+
// Available methods:
|
|
78
|
+
// addresses.append(value) - Add to end
|
|
79
|
+
// addresses.remove(index) - Remove at index
|
|
80
|
+
// addresses.insert(index, value)
|
|
81
|
+
// addresses.swap(i, j)
|
|
82
|
+
// addresses.move(from, to)
|
|
76
83
|
</script>
|
|
77
84
|
|
|
78
85
|
<template>
|
|
@@ -96,40 +103,31 @@ useForm({
|
|
|
96
103
|
})
|
|
97
104
|
```
|
|
98
105
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
### `useForm(options)`
|
|
106
|
+
- `disabled: ref(true)` - Disable entire form (reactive, blocks submission)
|
|
107
|
+
- `shouldUseNativeValidation: true` - Enable CSS `:valid`/`:invalid` selectors
|
|
102
108
|
|
|
103
|
-
|
|
104
|
-
const {
|
|
105
|
-
register, // Register input field
|
|
106
|
-
handleSubmit, // Create submit handler with validation
|
|
107
|
-
formState, // Reactive form state (errors, isSubmitting, etc.)
|
|
108
|
-
fields, // Manage dynamic field arrays
|
|
109
|
-
setValue, // Programmatically set field value
|
|
110
|
-
getValue, // Get current field value
|
|
111
|
-
reset, // Reset form to default values
|
|
112
|
-
watch, // Watch field value changes
|
|
113
|
-
validate, // Manually trigger validation
|
|
114
|
-
} = useForm({
|
|
115
|
-
schema, // Zod schema for validation
|
|
116
|
-
defaultValues: {}, // Initial form values
|
|
117
|
-
mode: 'onSubmit', // When to validate
|
|
118
|
-
})
|
|
119
|
-
```
|
|
109
|
+
## Tips
|
|
120
110
|
|
|
121
|
-
###
|
|
111
|
+
### Controlled vs Uncontrolled
|
|
122
112
|
|
|
123
113
|
```typescript
|
|
124
|
-
|
|
114
|
+
// Default (uncontrolled) - for native inputs
|
|
115
|
+
<input v-bind="register('email')" />
|
|
125
116
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
addresses.swap(0, 1)
|
|
130
|
-
addresses.move(0, 2)
|
|
117
|
+
// Controlled - for v-model / custom components
|
|
118
|
+
const { value, ...bindings } = register('field', { controlled: true })
|
|
119
|
+
<CustomInput v-model="value" v-bind="bindings" />
|
|
131
120
|
```
|
|
132
121
|
|
|
122
|
+
### Common Mistakes
|
|
123
|
+
|
|
124
|
+
| Wrong | Right |
|
|
125
|
+
| ------------------------ | ------------------------ |
|
|
126
|
+
| `items[0].name` | `items.0.name` |
|
|
127
|
+
| `:key="index"` | `:key="field.key"` |
|
|
128
|
+
| `formState.errors` | `formState.value.errors` |
|
|
129
|
+
| `v-model` + `register()` | Either one, not both |
|
|
130
|
+
|
|
133
131
|
## Contributing
|
|
134
132
|
|
|
135
133
|
Contributions welcome! Feel free to report bugs, suggest features, or submit PRs.
|
package/dist/core/domSync.d.ts
CHANGED
|
@@ -20,10 +20,3 @@ export declare function syncUncontrolledInputs(fieldRefs: Map<string, Ref<HTMLIn
|
|
|
20
20
|
* @param value - The value to set
|
|
21
21
|
*/
|
|
22
22
|
export declare function updateDomElement(el: HTMLInputElement, value: unknown): void;
|
|
23
|
-
/**
|
|
24
|
-
* Get value from a DOM element based on its type
|
|
25
|
-
*
|
|
26
|
-
* @param el - The DOM input element
|
|
27
|
-
* @returns The current value (checked state for checkboxes, value for others)
|
|
28
|
-
*/
|
|
29
|
-
export declare function getDomElementValue(el: HTMLInputElement): unknown;
|
|
@@ -44,6 +44,7 @@ export interface FormContext<FormValues> {
|
|
|
44
44
|
debounceTimers: Map<string, ReturnType<typeof setTimeout>>;
|
|
45
45
|
validationRequestIds: Map<string, number>;
|
|
46
46
|
resetGeneration: Ref<number>;
|
|
47
|
+
isDisabled: Ref<boolean>;
|
|
47
48
|
options: UseFormOptions<ZodType>;
|
|
48
49
|
}
|
|
49
50
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -20,4 +20,4 @@ export { useWatch, type UseWatchOptions } from './useWatch';
|
|
|
20
20
|
export { useController, type UseControllerOptions, type UseControllerReturn, type ControllerFieldProps, } from './useController';
|
|
21
21
|
export { useFormState, type UseFormStateOptions, type FormStateKey } from './useFormState';
|
|
22
22
|
export { isFieldError } from './types';
|
|
23
|
-
export type { UseFormOptions, UseFormReturn, RegisterOptions, RegisterReturn, FormState, FieldState, FieldErrors, FieldError, FieldErrorValue, ErrorOption, FieldArray, FieldArrayItem, InferSchema, FormValues, FormPath, Path, PathValue, ArrayElement, ArrayPath, ValidationMode, SetFocusOptions, ResetOptions, ResetFieldOptions, AsyncDefaultValues, } from './types';
|
|
23
|
+
export type { UseFormOptions, UseFormReturn, RegisterOptions, RegisterReturn, FormState, FieldState, FieldErrors, FieldError, FieldErrorValue, ErrorOption, SetErrorsOptions, FieldArray, FieldArrayItem, InferSchema, FormValues, FormPath, Path, PathValue, ArrayElement, ArrayPath, FieldPath, ValidationMode, SetFocusOptions, ResetOptions, ResetFieldOptions, AsyncDefaultValues, } from './types';
|
package/dist/types.d.ts
CHANGED
|
@@ -63,9 +63,20 @@ export type FormPath<TSchema extends ZodType> = Path<FormValues<TSchema>>;
|
|
|
63
63
|
export type ArrayPath<T> = {
|
|
64
64
|
[K in Path<T>]: PathValue<T, K> extends Array<unknown> ? K : never;
|
|
65
65
|
}[Path<T>];
|
|
66
|
+
/**
|
|
67
|
+
* Get non-array field paths (primitive fields and nested objects, excluding arrays).
|
|
68
|
+
* Useful for methods like register() that work with individual fields.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* type Form = { name: string; addresses: Address[] }
|
|
72
|
+
* type Fields = FieldPath<Form> // 'name' (excludes 'addresses')
|
|
73
|
+
*/
|
|
74
|
+
export type FieldPath<T> = {
|
|
75
|
+
[K in Path<T>]: PathValue<T, K> extends Array<unknown> ? never : K;
|
|
76
|
+
}[Path<T>];
|
|
66
77
|
/**
|
|
67
78
|
* Extract the value type at a given dot-notation path.
|
|
68
|
-
* Used internally to ensure setValue/
|
|
79
|
+
* Used internally to ensure setValue/getValues have correct types.
|
|
69
80
|
* Supports numeric string indices for array access (e.g., 'items.0.name').
|
|
70
81
|
*
|
|
71
82
|
* @example
|
|
@@ -76,14 +87,42 @@ export type ArrayPath<T> = {
|
|
|
76
87
|
*/
|
|
77
88
|
export type PathValue<T, P extends string> = T extends unknown ? P extends `${infer K}.${infer Rest}` ? K extends keyof T ? PathValue<T[K], Rest> : T extends Array<infer U> ? K extends `${number}` ? PathValue<U, Rest> : never : never : P extends keyof T ? T[P] : T extends Array<infer U> ? P extends `${number}` ? U : never : never : never;
|
|
78
89
|
/**
|
|
79
|
-
* Single field error with type and message
|
|
90
|
+
* Single field error with type and message.
|
|
91
|
+
* When criteriaMode is 'all', the `types` property contains all validation errors.
|
|
80
92
|
*/
|
|
81
93
|
export interface FieldError {
|
|
82
|
-
/** Error type identifier (e.g., 'required', '
|
|
94
|
+
/** Error type identifier (e.g., 'required', 'too_small', 'invalid_string', 'custom') */
|
|
83
95
|
type: string;
|
|
84
|
-
/**
|
|
96
|
+
/** Primary error message to display */
|
|
85
97
|
message: string;
|
|
86
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* Additional error types when multiple validations fail.
|
|
100
|
+
* Only populated when `criteriaMode: 'all'` is set in useForm options.
|
|
101
|
+
*
|
|
102
|
+
* @example Password with multiple requirements
|
|
103
|
+
* ```ts
|
|
104
|
+
* // Schema:
|
|
105
|
+
* const schema = z.object({
|
|
106
|
+
* password: z.string()
|
|
107
|
+
* .min(8, 'At least 8 characters')
|
|
108
|
+
* .regex(/[A-Z]/, 'Needs uppercase letter')
|
|
109
|
+
* .regex(/[0-9]/, 'Needs a number')
|
|
110
|
+
* })
|
|
111
|
+
*
|
|
112
|
+
* // With criteriaMode: 'all', error.types might be:
|
|
113
|
+
* // {
|
|
114
|
+
* // too_small: 'At least 8 characters',
|
|
115
|
+
* // invalid_string: ['Needs uppercase letter', 'Needs a number']
|
|
116
|
+
* // }
|
|
117
|
+
*
|
|
118
|
+
* // Template usage:
|
|
119
|
+
* // <ul v-if="isFieldError(error) && error.types">
|
|
120
|
+
* // <li v-for="(messages, type) in error.types" :key="type">
|
|
121
|
+
* // {{ Array.isArray(messages) ? messages.join(', ') : messages }}
|
|
122
|
+
* // </li>
|
|
123
|
+
* // </ul>
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
87
126
|
types?: Record<string, string | string[]>;
|
|
88
127
|
}
|
|
89
128
|
/**
|
|
@@ -145,6 +184,8 @@ export interface FormState<T> {
|
|
|
145
184
|
isSubmitted: boolean;
|
|
146
185
|
/** Whether the last submission was successful */
|
|
147
186
|
isSubmitSuccessful: boolean;
|
|
187
|
+
/** Whether the form is disabled */
|
|
188
|
+
disabled: boolean;
|
|
148
189
|
}
|
|
149
190
|
/**
|
|
150
191
|
* State of an individual field
|
|
@@ -236,6 +277,13 @@ export interface ResetOptions {
|
|
|
236
277
|
/** Keep isSubmitSuccessful state after reset */
|
|
237
278
|
keepIsSubmitSuccessful?: boolean;
|
|
238
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Options for setErrors() bulk operation
|
|
282
|
+
*/
|
|
283
|
+
export interface SetErrorsOptions {
|
|
284
|
+
/** Replace all existing errors instead of merging (default: false) */
|
|
285
|
+
shouldReplace?: boolean;
|
|
286
|
+
}
|
|
239
287
|
/**
|
|
240
288
|
* Options for registering a field
|
|
241
289
|
* @template TValue - The type of the field value (inferred from field path)
|
|
@@ -296,6 +344,8 @@ export interface RegisterReturn<TValue = unknown> {
|
|
|
296
344
|
onBlur: (e: Event) => void;
|
|
297
345
|
/** Current value (for controlled mode) - only present when controlled: true */
|
|
298
346
|
value?: Ref<TValue>;
|
|
347
|
+
/** Disabled state from form-level disabled option */
|
|
348
|
+
disabled?: boolean;
|
|
299
349
|
}
|
|
300
350
|
/**
|
|
301
351
|
* Field metadata for dynamic arrays
|
|
@@ -347,32 +397,56 @@ export interface FieldArrayOptions<T = unknown> {
|
|
|
347
397
|
* API for managing dynamic field arrays.
|
|
348
398
|
* All methods that accept values are typed to match the array item type.
|
|
349
399
|
*
|
|
400
|
+
* Most operations return a boolean indicating success or failure.
|
|
401
|
+
* Operations fail (return false) when:
|
|
402
|
+
* - maxLength rule would be exceeded (append, prepend, insert)
|
|
403
|
+
* - minLength rule would be violated (remove, removeAll, removeMany)
|
|
404
|
+
* - Index is out of bounds (remove, update, swap, move)
|
|
405
|
+
*
|
|
350
406
|
* @template TItem - The type of items in the array (inferred from field path)
|
|
351
407
|
*
|
|
352
408
|
* @example
|
|
353
409
|
* interface Address { street: string; city: string }
|
|
354
410
|
* const addresses = fields('addresses') // FieldArray<Address>
|
|
355
411
|
* addresses.append({ street: '123 Main', city: 'NYC' }) // Typed!
|
|
412
|
+
*
|
|
413
|
+
* @example Check if operation succeeded
|
|
414
|
+
* const success = addresses.append({ street: '', city: '' })
|
|
415
|
+
* if (!success) {
|
|
416
|
+
* // Operation was rejected (e.g., maxLength exceeded)
|
|
417
|
+
* showNotification('Cannot add more addresses')
|
|
418
|
+
* }
|
|
356
419
|
*/
|
|
357
420
|
export interface FieldArray<TItem = unknown> {
|
|
358
421
|
/** Current field items with metadata */
|
|
359
422
|
value: FieldArrayItem[];
|
|
360
|
-
/** Append item(s) to end of array */
|
|
361
|
-
append: (value: TItem | TItem[], options?: FieldArrayFocusOptions) =>
|
|
362
|
-
/** Prepend item(s) to beginning of array */
|
|
363
|
-
prepend: (value: TItem | TItem[], options?: FieldArrayFocusOptions) =>
|
|
364
|
-
/** Remove item at index */
|
|
365
|
-
remove: (index: number) =>
|
|
366
|
-
/**
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
423
|
+
/** Append item(s) to end of array. Returns false if maxLength exceeded. */
|
|
424
|
+
append: (value: TItem | TItem[], options?: FieldArrayFocusOptions) => boolean;
|
|
425
|
+
/** Prepend item(s) to beginning of array. Returns false if maxLength exceeded. */
|
|
426
|
+
prepend: (value: TItem | TItem[], options?: FieldArrayFocusOptions) => boolean;
|
|
427
|
+
/** Remove item at index. Returns false if minLength violated or index out of bounds. */
|
|
428
|
+
remove: (index: number) => boolean;
|
|
429
|
+
/**
|
|
430
|
+
* Remove all items from the array.
|
|
431
|
+
* Returns false if minLength > 0.
|
|
432
|
+
*/
|
|
433
|
+
removeAll: () => boolean;
|
|
434
|
+
/**
|
|
435
|
+
* Remove multiple items by indices (handles any order, removes from highest to lowest).
|
|
436
|
+
* Returns false if minLength would be violated.
|
|
437
|
+
* @param indices - Array of indices to remove
|
|
438
|
+
*/
|
|
439
|
+
removeMany: (indices: number[]) => boolean;
|
|
440
|
+
/** Insert item(s) at index. Returns false if maxLength exceeded. */
|
|
441
|
+
insert: (index: number, value: TItem | TItem[], options?: FieldArrayFocusOptions) => boolean;
|
|
442
|
+
/** Swap two items. Returns false if either index is out of bounds. */
|
|
443
|
+
swap: (indexA: number, indexB: number) => boolean;
|
|
444
|
+
/** Move item from one index to another. Returns false if from index is out of bounds. */
|
|
445
|
+
move: (from: number, to: number) => boolean;
|
|
446
|
+
/** Update item at index (preserves key/identity). Returns false if index out of bounds. */
|
|
447
|
+
update: (index: number, value: TItem) => boolean;
|
|
448
|
+
/** Replace all items with new values. Always succeeds (returns true). */
|
|
449
|
+
replace: (values: TItem[]) => boolean;
|
|
376
450
|
}
|
|
377
451
|
/**
|
|
378
452
|
* Async default values function type
|
|
@@ -388,11 +462,65 @@ export type CriteriaMode = 'firstError' | 'all';
|
|
|
388
462
|
export interface UseFormOptions<TSchema extends ZodType> {
|
|
389
463
|
/** Zod schema for validation */
|
|
390
464
|
schema: TSchema;
|
|
391
|
-
/**
|
|
465
|
+
/**
|
|
466
|
+
* Default form values. Can be a sync object or async function.
|
|
467
|
+
* Async function is useful for fetching initial values from an API.
|
|
468
|
+
*
|
|
469
|
+
* @example Sync default values
|
|
470
|
+
* ```ts
|
|
471
|
+
* useForm({
|
|
472
|
+
* schema,
|
|
473
|
+
* defaultValues: { email: '', name: '' }
|
|
474
|
+
* })
|
|
475
|
+
* ```
|
|
476
|
+
*
|
|
477
|
+
* @example Async default values (API fetch)
|
|
478
|
+
* ```ts
|
|
479
|
+
* useForm({
|
|
480
|
+
* schema,
|
|
481
|
+
* defaultValues: async () => {
|
|
482
|
+
* const user = await api.getCurrentUser()
|
|
483
|
+
* return { email: user.email, name: user.name }
|
|
484
|
+
* },
|
|
485
|
+
* onDefaultValuesError: (err) => console.error('Failed to load user:', err)
|
|
486
|
+
* })
|
|
487
|
+
* // Check formState.isLoading to show loading indicator
|
|
488
|
+
* ```
|
|
489
|
+
*/
|
|
392
490
|
defaultValues?: Partial<InferSchema<TSchema>> | AsyncDefaultValues<InferSchema<TSchema>>;
|
|
393
|
-
/**
|
|
491
|
+
/**
|
|
492
|
+
* When to run validation.
|
|
493
|
+
* - 'onSubmit' (default): Only validate on form submission
|
|
494
|
+
* - 'onBlur': Validate when field loses focus
|
|
495
|
+
* - 'onChange': Validate on every input change
|
|
496
|
+
* - 'onTouched': Validate after field touched, then on every change
|
|
497
|
+
*
|
|
498
|
+
* @example Different validation modes
|
|
499
|
+
* ```ts
|
|
500
|
+
* // Most performant - only show errors after submit attempt
|
|
501
|
+
* useForm({ schema, mode: 'onSubmit' })
|
|
502
|
+
*
|
|
503
|
+
* // Real-time feedback - validate as user types
|
|
504
|
+
* useForm({ schema, mode: 'onChange' })
|
|
505
|
+
*
|
|
506
|
+
* // Balanced - validate after first interaction
|
|
507
|
+
* useForm({ schema, mode: 'onTouched' })
|
|
508
|
+
* ```
|
|
509
|
+
*/
|
|
394
510
|
mode?: ValidationMode;
|
|
395
|
-
/**
|
|
511
|
+
/**
|
|
512
|
+
* Validation mode after first submission.
|
|
513
|
+
* Useful for "validate on submit, then real-time revalidation" pattern.
|
|
514
|
+
*
|
|
515
|
+
* @example Submit first, then real-time
|
|
516
|
+
* ```ts
|
|
517
|
+
* useForm({
|
|
518
|
+
* schema,
|
|
519
|
+
* mode: 'onSubmit', // First submit validates all
|
|
520
|
+
* reValidateMode: 'onChange' // After submit, revalidate on change
|
|
521
|
+
* })
|
|
522
|
+
* ```
|
|
523
|
+
*/
|
|
396
524
|
reValidateMode?: ValidationMode;
|
|
397
525
|
/** Remove field data when unmounted (default: false) */
|
|
398
526
|
shouldUnregister?: boolean;
|
|
@@ -400,20 +528,135 @@ export interface UseFormOptions<TSchema extends ZodType> {
|
|
|
400
528
|
onDefaultValuesError?: (error: unknown) => void;
|
|
401
529
|
/** Focus first field with error on submit (default: true) */
|
|
402
530
|
shouldFocusError?: boolean;
|
|
403
|
-
/**
|
|
531
|
+
/**
|
|
532
|
+
* How to collect validation errors.
|
|
533
|
+
* - 'firstError' (default): Stop at first error per field
|
|
534
|
+
* - 'all': Collect all errors for each field (populates FieldError.types)
|
|
535
|
+
*
|
|
536
|
+
* @example Show all validation errors (password requirements)
|
|
537
|
+
* ```ts
|
|
538
|
+
* const schema = z.object({
|
|
539
|
+
* password: z.string()
|
|
540
|
+
* .min(8, 'At least 8 characters')
|
|
541
|
+
* .regex(/[A-Z]/, 'Needs uppercase letter')
|
|
542
|
+
* .regex(/[0-9]/, 'Needs a number')
|
|
543
|
+
* })
|
|
544
|
+
*
|
|
545
|
+
* useForm({ schema, criteriaMode: 'all' })
|
|
546
|
+
*
|
|
547
|
+
* // In template - display all password requirements:
|
|
548
|
+
* // <ul v-if="formState.errors.password?.types">
|
|
549
|
+
* // <li v-for="(msg, type) in formState.errors.password.types" :key="type">
|
|
550
|
+
* // {{ msg }}
|
|
551
|
+
* // </li>
|
|
552
|
+
* // </ul>
|
|
553
|
+
* ```
|
|
554
|
+
*
|
|
555
|
+
* @see isFieldError - Type guard for structured errors
|
|
556
|
+
* @see FieldError.types - Contains all error types when criteriaMode is 'all'
|
|
557
|
+
*/
|
|
404
558
|
criteriaMode?: CriteriaMode;
|
|
405
|
-
/**
|
|
559
|
+
/**
|
|
560
|
+
* Delay in milliseconds before displaying validation errors.
|
|
561
|
+
* Prevents error flash during fast typing.
|
|
562
|
+
*
|
|
563
|
+
* @example Delay error display by 500ms
|
|
564
|
+
* ```ts
|
|
565
|
+
* useForm({
|
|
566
|
+
* schema,
|
|
567
|
+
* mode: 'onChange',
|
|
568
|
+
* delayError: 500 // Wait 500ms before showing error
|
|
569
|
+
* })
|
|
570
|
+
* // If user fixes error within 500ms, error never appears
|
|
571
|
+
* ```
|
|
572
|
+
*/
|
|
406
573
|
delayError?: number;
|
|
407
574
|
/**
|
|
408
575
|
* External values to sync to form. Changes update formData without marking dirty.
|
|
409
576
|
* Useful for server-fetched data or parent component state.
|
|
577
|
+
*
|
|
578
|
+
* @example Sync with parent component state
|
|
579
|
+
* ```ts
|
|
580
|
+
* const props = defineProps<{ userData: UserData }>()
|
|
581
|
+
*
|
|
582
|
+
* useForm({
|
|
583
|
+
* schema,
|
|
584
|
+
* values: computed(() => props.userData) // Reactive sync
|
|
585
|
+
* })
|
|
586
|
+
* // When props.userData changes, form updates without becoming dirty
|
|
587
|
+
* ```
|
|
588
|
+
*
|
|
589
|
+
* @example Sync with API polling
|
|
590
|
+
* ```ts
|
|
591
|
+
* const { data: serverData } = useQuery('user', fetchUser)
|
|
592
|
+
*
|
|
593
|
+
* useForm({
|
|
594
|
+
* schema,
|
|
595
|
+
* values: serverData // Ref from useQuery
|
|
596
|
+
* })
|
|
597
|
+
* ```
|
|
410
598
|
*/
|
|
411
599
|
values?: MaybeRef<Partial<InferSchema<TSchema>>>;
|
|
412
600
|
/**
|
|
413
601
|
* External/server errors to merge with validation errors.
|
|
414
|
-
*
|
|
602
|
+
* These take precedence over client-side validation errors.
|
|
603
|
+
*
|
|
604
|
+
* @example Display server validation errors
|
|
605
|
+
* ```ts
|
|
606
|
+
* const serverErrors = ref({})
|
|
607
|
+
*
|
|
608
|
+
* const { handleSubmit } = useForm({
|
|
609
|
+
* schema,
|
|
610
|
+
* errors: serverErrors
|
|
611
|
+
* })
|
|
612
|
+
*
|
|
613
|
+
* const onSubmit = async (data) => {
|
|
614
|
+
* try {
|
|
615
|
+
* await api.submit(data)
|
|
616
|
+
* } catch (err) {
|
|
617
|
+
* if (err.validationErrors) {
|
|
618
|
+
* serverErrors.value = err.validationErrors
|
|
619
|
+
* // e.g., { email: 'Email already registered' }
|
|
620
|
+
* }
|
|
621
|
+
* }
|
|
622
|
+
* }
|
|
623
|
+
* ```
|
|
415
624
|
*/
|
|
416
625
|
errors?: MaybeRef<Partial<FieldErrors<InferSchema<TSchema>>>>;
|
|
626
|
+
/**
|
|
627
|
+
* Disable the entire form. When true:
|
|
628
|
+
* - All registered fields receive `disabled` attribute
|
|
629
|
+
* - Form submission is prevented
|
|
630
|
+
* - Can be reactive (MaybeRef) to toggle dynamically
|
|
631
|
+
*
|
|
632
|
+
* @example Disable form during submission
|
|
633
|
+
* ```ts
|
|
634
|
+
* const isSubmitting = ref(false)
|
|
635
|
+
*
|
|
636
|
+
* useForm({
|
|
637
|
+
* schema,
|
|
638
|
+
* disabled: isSubmitting
|
|
639
|
+
* })
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
642
|
+
disabled?: MaybeRef<boolean>;
|
|
643
|
+
/**
|
|
644
|
+
* Enable browser's native validation API.
|
|
645
|
+
* When true:
|
|
646
|
+
* - Calls setCustomValidity() on inputs with error messages
|
|
647
|
+
* - Enables :valid/:invalid CSS pseudo-selectors
|
|
648
|
+
* - Shows native browser validation tooltips
|
|
649
|
+
*
|
|
650
|
+
* @example
|
|
651
|
+
* ```ts
|
|
652
|
+
* useForm({ schema, shouldUseNativeValidation: true })
|
|
653
|
+
*
|
|
654
|
+
* // CSS styling:
|
|
655
|
+
* // input:invalid { border-color: red; }
|
|
656
|
+
* // input:valid { border-color: green; }
|
|
657
|
+
* ```
|
|
658
|
+
*/
|
|
659
|
+
shouldUseNativeValidation?: boolean;
|
|
417
660
|
}
|
|
418
661
|
/**
|
|
419
662
|
* Return value from useForm composable.
|
|
@@ -454,7 +697,7 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
454
697
|
* Manage dynamic field arrays.
|
|
455
698
|
* Returns a typed API for adding, removing, and reordering array items.
|
|
456
699
|
*
|
|
457
|
-
* @param name - Array field path
|
|
700
|
+
* @param name - Array field path (must be an array field in the schema)
|
|
458
701
|
* @param options - Optional configuration including validation rules
|
|
459
702
|
* @returns Typed FieldArray API
|
|
460
703
|
*
|
|
@@ -462,7 +705,7 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
462
705
|
* const addresses = fields('addresses')
|
|
463
706
|
* addresses.append({ street: '', city: '' }) // Typed to Address
|
|
464
707
|
*/
|
|
465
|
-
fields: <TPath extends
|
|
708
|
+
fields: <TPath extends ArrayPath<InferSchema<TSchema>>>(name: TPath, options?: FieldArrayOptions<ArrayElement<PathValue<InferSchema<TSchema>, TPath>>>) => FieldArray<ArrayElement<PathValue<InferSchema<TSchema>, TPath>>>;
|
|
466
709
|
/**
|
|
467
710
|
* Set field value programmatically
|
|
468
711
|
* @param name - Field path
|
|
@@ -470,12 +713,6 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
470
713
|
* @param options - Options for validation/dirty/touched behavior
|
|
471
714
|
*/
|
|
472
715
|
setValue: <TPath extends Path<InferSchema<TSchema>>>(name: TPath, value: PathValue<InferSchema<TSchema>, TPath>, options?: SetValueOptions) => void;
|
|
473
|
-
/**
|
|
474
|
-
* Get field value
|
|
475
|
-
* @param name - Field path
|
|
476
|
-
* @returns The field value (typed) or undefined if not set
|
|
477
|
-
*/
|
|
478
|
-
getValue: <TPath extends Path<InferSchema<TSchema>>>(name: TPath) => PathValue<InferSchema<TSchema>, TPath> | undefined;
|
|
479
716
|
/**
|
|
480
717
|
* Reset form to default values
|
|
481
718
|
* @param values - Optional new default values
|
|
@@ -515,6 +752,58 @@ export interface UseFormReturn<TSchema extends ZodType> {
|
|
|
515
752
|
* @param error - Error option with message
|
|
516
753
|
*/
|
|
517
754
|
setError: <TPath extends Path<InferSchema<TSchema>>>(name: TPath | 'root' | `root.${string}`, error: ErrorOption) => void;
|
|
755
|
+
/**
|
|
756
|
+
* Set multiple errors at once. Useful for server-side validation errors
|
|
757
|
+
* or bulk error handling scenarios.
|
|
758
|
+
*
|
|
759
|
+
* @param errors - Record of field paths to error messages or ErrorOption objects
|
|
760
|
+
* @param options - Optional configuration for merge behavior
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* // Simple string errors
|
|
764
|
+
* setErrors({
|
|
765
|
+
* email: 'Email already exists',
|
|
766
|
+
* 'user.name': 'Name is too short'
|
|
767
|
+
* })
|
|
768
|
+
*
|
|
769
|
+
* @example
|
|
770
|
+
* // Replace all errors
|
|
771
|
+
* setErrors({ email: 'New error' }, { shouldReplace: true })
|
|
772
|
+
*/
|
|
773
|
+
setErrors: <TPath extends Path<InferSchema<TSchema>>>(errors: Partial<Record<TPath | 'root' | `root.${string}`, string | ErrorOption>>, options?: SetErrorsOptions) => void;
|
|
774
|
+
/**
|
|
775
|
+
* Check if the form or a specific field has validation errors
|
|
776
|
+
*
|
|
777
|
+
* @param fieldPath - Optional field path to check. If omitted, checks entire form.
|
|
778
|
+
* @returns true if errors exist, false otherwise
|
|
779
|
+
*
|
|
780
|
+
* @example
|
|
781
|
+
* if (hasErrors()) {
|
|
782
|
+
* console.log('Form has validation errors')
|
|
783
|
+
* }
|
|
784
|
+
*
|
|
785
|
+
* @example
|
|
786
|
+
* if (hasErrors('email')) {
|
|
787
|
+
* focusField('email')
|
|
788
|
+
* }
|
|
789
|
+
*/
|
|
790
|
+
hasErrors: <TPath extends Path<InferSchema<TSchema>>>(fieldPath?: TPath | 'root' | `root.${string}`) => boolean;
|
|
791
|
+
/**
|
|
792
|
+
* Get validation errors for the form or a specific field
|
|
793
|
+
*
|
|
794
|
+
* @overload Get all form errors
|
|
795
|
+
* @overload Get error for a specific field
|
|
796
|
+
*
|
|
797
|
+
* @example
|
|
798
|
+
* const allErrors = getErrors()
|
|
799
|
+
*
|
|
800
|
+
* @example
|
|
801
|
+
* const emailError = getErrors('email')
|
|
802
|
+
*/
|
|
803
|
+
getErrors: {
|
|
804
|
+
(): FieldErrors<InferSchema<TSchema>>;
|
|
805
|
+
<TPath extends Path<InferSchema<TSchema>>>(fieldPath: TPath | 'root' | `root.${string}`): FieldErrorValue | undefined;
|
|
806
|
+
};
|
|
518
807
|
/**
|
|
519
808
|
* Get all form values, a single value, or multiple values
|
|
520
809
|
* @overload Get all form values
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ZodType } from 'zod';
|
|
2
|
+
export declare const __DEV__: boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Warn once per unique message (prevents spam on re-renders)
|
|
5
|
+
*/
|
|
6
|
+
export declare function warnOnce(message: string, key?: string): void;
|
|
7
|
+
/**
|
|
8
|
+
* Warn every time (for errors that should always be shown)
|
|
9
|
+
*/
|
|
10
|
+
export declare function warn(message: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Clear warning cache (useful for testing)
|
|
13
|
+
*/
|
|
14
|
+
export declare function clearWarningCache(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Validate a dot-notation path string for common syntax errors
|
|
17
|
+
* @returns Error message or null if valid
|
|
18
|
+
*/
|
|
19
|
+
export declare function validatePathSyntax(path: string): string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Check if a path exists in a Zod schema
|
|
22
|
+
* This is a runtime check to validate paths against the schema structure
|
|
23
|
+
*/
|
|
24
|
+
export declare function validatePathAgainstSchema(schema: ZodType, path: string): {
|
|
25
|
+
valid: boolean;
|
|
26
|
+
reason?: string;
|
|
27
|
+
availableFields?: string[];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Check if a path points to an array field in the schema
|
|
31
|
+
*/
|
|
32
|
+
export declare function isArrayFieldInSchema(schema: ZodType, path: string): boolean | null;
|
|
33
|
+
/**
|
|
34
|
+
* Warn about registering an invalid path with fix suggestion
|
|
35
|
+
*/
|
|
36
|
+
export declare function warnInvalidPath(fnName: string, path: string, reason: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Warn about path not in schema with suggestions
|
|
39
|
+
*/
|
|
40
|
+
export declare function warnPathNotInSchema(fnName: string, path: string, availableFields?: string[]): void;
|
|
41
|
+
/**
|
|
42
|
+
* Warn about calling fields() on non-array path
|
|
43
|
+
*/
|
|
44
|
+
export declare function warnFieldsOnNonArray(path: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Warn about silent field array operation failures
|
|
47
|
+
*/
|
|
48
|
+
export declare function warnArrayOperationRejected(operation: string, path: string, reason: 'maxLength' | 'minLength', details?: {
|
|
49
|
+
current: number;
|
|
50
|
+
limit: number;
|
|
51
|
+
}): void;
|
|
52
|
+
/**
|
|
53
|
+
* Warn about array operation with out of bounds index
|
|
54
|
+
*/
|
|
55
|
+
export declare function warnArrayIndexOutOfBounds(operation: string, path: string, index: number, length: number): void;
|