notform 2.2.2 → 2.2.4
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 +109 -53
- package/dist/index.d.ts +58 -70
- package/dist/index.js +152 -85
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<h1>notform</h1>
|
|
3
|
-
<p><strong>
|
|
2
|
+
<h1><code>notform</code></h1>
|
|
3
|
+
<p><strong>Headless form management for Vue 3</strong></p>
|
|
4
4
|
<p>
|
|
5
|
-
<a href="https://npmx.dev/package/notform"><img src="https://img.shields.io/npm/v/notform.svg?style=plastic&label=
|
|
6
|
-
<a href="https://npmx.dev/package/notform"><img src="https://img.shields.io/npm/
|
|
7
|
-
<a href="https://npmx.dev/package/notform"><img src="https://img.shields.io/npm/unpacked-size/notform?style=plastic&label=
|
|
5
|
+
<a href="https://npmx.dev/package/notform"><img src="https://img.shields.io/npm/v/notform.svg?style=plastic&label=Version" alt="Version"></a>
|
|
6
|
+
<a href="https://npmx.dev/package/notform"><img src="https://img.shields.io/npm/dm/notform.svg?style=plastic&label=Downloads&color=blue" alt="Downloads"></a>
|
|
7
|
+
<a href="https://npmx.dev/package/notform"><img src="https://img.shields.io/npm/unpacked-size/notform?style=plastic&label=Unpacked%20Size" alt="Unpacked Size"></a>
|
|
8
8
|
</p>
|
|
9
9
|
</div>
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
`notform` is the core NotForm package for Vue 3.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
It provides reactive form state, schema-based validation, field state, submission handling, and renderless components that work with any UI you choose.
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
@@ -18,18 +18,13 @@
|
|
|
18
18
|
pnpm add notform
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Install a Standard Schema-compatible validator separately, for example:
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- **A schema** — Defined using any validation library that supports [Standard Schema](https://standard-schema.dev/) (Zod, Yup, etc.)
|
|
28
|
-
- **Form state** — Managed reactively with full TypeScript support
|
|
29
|
-
- **Validation** — Automatic validation based on your schema with error messages
|
|
30
|
-
- **Submission handling** — Built-in submission lifecycle with loading states
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add zod
|
|
25
|
+
```
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
## Basic Usage
|
|
33
28
|
|
|
34
29
|
```vue
|
|
35
30
|
<script setup lang="ts">
|
|
@@ -37,13 +32,17 @@ import { NotField, NotForm, NotMessage, useNotForm } from 'notform'
|
|
|
37
32
|
import { z } from 'zod'
|
|
38
33
|
|
|
39
34
|
const schema = z.object({
|
|
40
|
-
email: z.email('
|
|
41
|
-
name: z.string(
|
|
35
|
+
email: z.email('Enter a valid email address'),
|
|
36
|
+
name: z.string('Enter a valid name'),
|
|
42
37
|
})
|
|
43
38
|
|
|
44
39
|
const form = useNotForm({
|
|
40
|
+
initialValues: {
|
|
41
|
+
email: '',
|
|
42
|
+
name: '',
|
|
43
|
+
},
|
|
45
44
|
onSubmit(values) {
|
|
46
|
-
console.log('
|
|
45
|
+
console.log('Submitted:', values)
|
|
47
46
|
},
|
|
48
47
|
schema,
|
|
49
48
|
})
|
|
@@ -51,42 +50,48 @@ const form = useNotForm({
|
|
|
51
50
|
|
|
52
51
|
<template>
|
|
53
52
|
<NotForm
|
|
54
|
-
:form
|
|
55
|
-
@submit="form.submit"
|
|
53
|
+
:form="form"
|
|
54
|
+
@submit.prevent="form.submit"
|
|
56
55
|
@reset="form.reset()"
|
|
57
56
|
>
|
|
58
57
|
<NotField
|
|
59
|
-
v-slot="{events,path}"
|
|
58
|
+
v-slot="{ events, path }"
|
|
60
59
|
path="name"
|
|
61
60
|
>
|
|
62
|
-
<
|
|
63
|
-
Name
|
|
61
|
+
<div>
|
|
62
|
+
<label :for="path">Name</label>
|
|
63
|
+
|
|
64
64
|
<input
|
|
65
|
-
v-bind="events"
|
|
66
65
|
:id="path"
|
|
67
66
|
v-model="form.values.name"
|
|
67
|
+
v-bind="events"
|
|
68
|
+
name="name"
|
|
68
69
|
type="text"
|
|
70
|
+
autocomplete="name"
|
|
69
71
|
>
|
|
70
|
-
</label>
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
<NotMessage :path="path" />
|
|
74
|
+
</div>
|
|
73
75
|
</NotField>
|
|
74
76
|
|
|
75
77
|
<NotField
|
|
76
|
-
v-slot="{events,path}"
|
|
78
|
+
v-slot="{ events, path }"
|
|
77
79
|
path="email"
|
|
78
80
|
>
|
|
79
|
-
<
|
|
80
|
-
Email
|
|
81
|
+
<div>
|
|
82
|
+
<label :for="path">Email address</label>
|
|
83
|
+
|
|
81
84
|
<input
|
|
82
|
-
v-bind="events"
|
|
83
85
|
:id="path"
|
|
84
86
|
v-model="form.values.email"
|
|
87
|
+
v-bind="events"
|
|
88
|
+
name="email"
|
|
85
89
|
type="email"
|
|
90
|
+
autocomplete="email"
|
|
86
91
|
>
|
|
87
|
-
</label>
|
|
88
92
|
|
|
89
|
-
|
|
93
|
+
<NotMessage :path="path" />
|
|
94
|
+
</div>
|
|
90
95
|
</NotField>
|
|
91
96
|
|
|
92
97
|
<button type="submit">
|
|
@@ -94,36 +99,87 @@ const form = useNotForm({
|
|
|
94
99
|
</button>
|
|
95
100
|
|
|
96
101
|
<button type="reset">
|
|
97
|
-
Reset
|
|
102
|
+
Reset
|
|
98
103
|
</button>
|
|
99
104
|
</NotForm>
|
|
100
105
|
</template>
|
|
101
106
|
```
|
|
102
107
|
|
|
103
|
-
|
|
108
|
+
## API
|
|
104
109
|
|
|
105
|
-
|
|
110
|
+
The core package exports:
|
|
106
111
|
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
-
|
|
112
|
+
- `useNotForm`
|
|
113
|
+
- `NotForm`
|
|
114
|
+
- `NotField`
|
|
115
|
+
- `NotMessage`
|
|
116
|
+
- `NotArrayField`
|
|
111
117
|
|
|
112
|
-
|
|
118
|
+
The form instance manages values, errors, touched and dirty state, validation, submission state, and reset behavior.
|
|
113
119
|
|
|
114
|
-
|
|
115
|
-
- **Composable** — Use with Vue 3 Composition API for flexible form management
|
|
116
|
-
- **Lightweight** — Tiny footprint with tree-shaking support
|
|
117
|
-
- **Flexible** — Works with any Standard Schema-compatible validation library
|
|
118
|
-
- **Array fields** — Built-in support for dynamic array fields with add/remove operations
|
|
119
|
-
- **Minimal boilerplate** — Get started quickly with simple, intuitive APIs
|
|
120
|
+
## Array Fields
|
|
120
121
|
|
|
121
|
-
|
|
122
|
+
`NotArrayField` provides renderless operations for dynamic arrays while preserving stable item keys during reordering.
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
```vue
|
|
125
|
+
<NotArrayField
|
|
126
|
+
path="tags"
|
|
127
|
+
v-slot="{ items, append, remove }"
|
|
128
|
+
>
|
|
129
|
+
<div
|
|
130
|
+
v-for="(item, index) in items"
|
|
131
|
+
:key="item.key"
|
|
132
|
+
>
|
|
133
|
+
<NotField :path="item.path" v-slot="{ events, path }">
|
|
134
|
+
<label :for="path">Tag {{ index + 1 }}</label>
|
|
135
|
+
<input
|
|
136
|
+
:id="path"
|
|
137
|
+
v-model="form.values.tags[index]"
|
|
138
|
+
v-bind="events"
|
|
139
|
+
:name="path"
|
|
140
|
+
type="text"
|
|
141
|
+
>
|
|
142
|
+
<NotMessage :path="path" />
|
|
143
|
+
</NotField>
|
|
125
144
|
|
|
126
|
-
|
|
145
|
+
<button type="button" @click="remove(index)">
|
|
146
|
+
Remove
|
|
147
|
+
</button>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<button type="button" @click="append('')">
|
|
151
|
+
Add tag
|
|
152
|
+
</button>
|
|
153
|
+
</NotArrayField>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Type Safety
|
|
157
|
+
|
|
158
|
+
Field paths and related APIs are inferred from your schema, including nested paths.
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
form.setValue('email', 'jane@example.com')
|
|
162
|
+
await form.validateField('email')
|
|
163
|
+
const errors = form.getFieldErrors('email')
|
|
164
|
+
```
|
|
127
165
|
|
|
128
|
-
|
|
166
|
+
## Requirements
|
|
167
|
+
|
|
168
|
+
- Vue 3
|
|
169
|
+
- Node.js 24 or later for development in this repository
|
|
170
|
+
- A Standard Schema-compatible validator
|
|
171
|
+
|
|
172
|
+
## Development
|
|
173
|
+
|
|
174
|
+
From the repository root:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
pnpm install
|
|
178
|
+
pnpm --filter notform test
|
|
179
|
+
pnpm --filter notform typecheck
|
|
180
|
+
pnpm --filter notform build
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## License
|
|
129
184
|
|
|
185
|
+
[MIT](../../LICENSE) © [Favour Emeka](https://github.com/favorodera)
|
package/dist/index.d.ts
CHANGED
|
@@ -125,12 +125,14 @@ type NotFormInstance<TSchema extends ObjectSchema> = Raw<{
|
|
|
125
125
|
*
|
|
126
126
|
* Useful for deeply nested paths or custom inputs that do not use `v-model`.
|
|
127
127
|
* Does **not** trigger validation — the field's event handlers are responsible for that.
|
|
128
|
+
* @param path The dot-separated path to the field.
|
|
129
|
+
* @param value The value to set.
|
|
128
130
|
* @example
|
|
129
131
|
* ```ts
|
|
130
132
|
* form.setValue('address.city', 'Lagos')
|
|
131
133
|
* ```
|
|
132
134
|
*/
|
|
133
|
-
setValue: <
|
|
135
|
+
setValue: <TPath extends Paths<StandardSchemaV1.InferInput<TSchema>>>(path: TPath, value: Get<StandardSchemaV1.InferInput<TSchema>, TPath, {
|
|
134
136
|
strict: false;
|
|
135
137
|
}>) => void;
|
|
136
138
|
/**
|
|
@@ -143,6 +145,11 @@ type NotFormInstance<TSchema extends ObjectSchema> = Raw<{
|
|
|
143
145
|
/**
|
|
144
146
|
* Marks a field as touched.
|
|
145
147
|
* Called automatically by the field's `onBlur` handler.
|
|
148
|
+
* @param path The dot-separated path to the field.
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* form.touchField('address.city')
|
|
152
|
+
* ```
|
|
146
153
|
*/
|
|
147
154
|
touchField: (path: Paths<StandardSchemaV1.InferInput<TSchema>>) => void;
|
|
148
155
|
/** The set of field paths whose current value differs from the initial value. */
|
|
@@ -152,6 +159,11 @@ type NotFormInstance<TSchema extends ObjectSchema> = Raw<{
|
|
|
152
159
|
/**
|
|
153
160
|
* Marks a field as dirty.
|
|
154
161
|
* Called automatically when a field value changes.
|
|
162
|
+
* @param path The dot-separated path to the field.
|
|
163
|
+
* @example
|
|
164
|
+
* ```ts
|
|
165
|
+
* form.dirtyField('address.city')
|
|
166
|
+
* ```
|
|
155
167
|
*/
|
|
156
168
|
dirtyField: (path: Paths<StandardSchemaV1.InferInput<TSchema>>) => void;
|
|
157
169
|
/** The raw validation issues produced by the last validation run. */
|
|
@@ -171,14 +183,31 @@ type NotFormInstance<TSchema extends ObjectSchema> = Raw<{
|
|
|
171
183
|
/**
|
|
172
184
|
* Replaces an existing error for the same path, or appends it if none exists.
|
|
173
185
|
* Useful for setting server-side errors after submission.
|
|
186
|
+
* @param error The validation issue to set.
|
|
187
|
+
* @example
|
|
188
|
+
* ```ts
|
|
189
|
+
* form.setError({ message: 'Invalid email', path: ['email'] })
|
|
190
|
+
* ```
|
|
174
191
|
*/
|
|
175
192
|
setError: (error: StandardSchemaV1.Issue) => void;
|
|
176
|
-
/**
|
|
193
|
+
/**
|
|
194
|
+
* Replaces all current errors with the provided issues.
|
|
195
|
+
* @param errors The validation issues to set.
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* form.setErrors([
|
|
199
|
+
* { message: 'Invalid email', path: ['email'] },
|
|
200
|
+
* { message: 'Phone number must be at least 11 digits', path: ['phone'] }
|
|
201
|
+
* ])
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
177
204
|
setErrors: (errors: Array<StandardSchemaV1.Issue>) => void;
|
|
178
205
|
/** Removes all active validation errors. */
|
|
179
206
|
clearErrors: () => void;
|
|
180
207
|
/**
|
|
181
|
-
*
|
|
208
|
+
* Get all the validation issues for a specific field path.
|
|
209
|
+
* @param path The dot-separated path to the field.
|
|
210
|
+
* @returns An array of validation issues for the given field path.
|
|
182
211
|
* @example
|
|
183
212
|
* ```ts
|
|
184
213
|
* form.getFieldErrors('address.city')
|
|
@@ -190,11 +219,18 @@ type NotFormInstance<TSchema extends ObjectSchema> = Raw<{
|
|
|
190
219
|
/**
|
|
191
220
|
* Validates the entire form against the schema.
|
|
192
221
|
* Replaces all current errors with the result.
|
|
222
|
+
* @returns A promise that resolves to the result of the validation.
|
|
193
223
|
*/
|
|
194
224
|
validate: () => Promise<StandardSchemaV1.Result<StandardSchemaV1.InferOutput<TSchema>>>;
|
|
195
225
|
/**
|
|
196
226
|
* Validates a single field against the schema.
|
|
197
227
|
* Only replaces errors for that field — all other fields are left untouched.
|
|
228
|
+
* @param path The dot-separated path to the field.
|
|
229
|
+
* @returns A promise that resolves to the result of the validation.
|
|
230
|
+
* @example
|
|
231
|
+
* ```ts
|
|
232
|
+
* form.validateField('email')
|
|
233
|
+
* ```
|
|
198
234
|
*/
|
|
199
235
|
validateField: (path: Paths<StandardSchemaV1.InferInput<TSchema>>) => Promise<StandardSchemaV1.Result<StandardSchemaV1.InferOutput<TSchema>>>;
|
|
200
236
|
/** Whether the form currently has no validation errors. */
|
|
@@ -206,28 +242,29 @@ type NotFormInstance<TSchema extends ObjectSchema> = Raw<{
|
|
|
206
242
|
*
|
|
207
243
|
* Marks all fields as touched and dirty before validating so all errors surface.
|
|
208
244
|
* If validation fails, submission is aborted.
|
|
209
|
-
*
|
|
210
|
-
* @
|
|
211
|
-
* ```vue
|
|
212
|
-
* <template>
|
|
213
|
-
* <form v-on:submit="form.submit">
|
|
214
|
-
* </form>
|
|
215
|
-
* </template>
|
|
216
|
-
* ```
|
|
245
|
+
* @param event The submit event.
|
|
246
|
+
* @returns A promise that resolves when the form is submitted.
|
|
217
247
|
*/
|
|
218
|
-
submit: (event:
|
|
248
|
+
submit: (event: SubmitEvent) => Promise<void>;
|
|
219
249
|
/**
|
|
220
250
|
* Resets the form to its initial state, or to new values and errors if provided.
|
|
221
251
|
*
|
|
222
252
|
* Clears all touched and dirty tracking. If `values` or `errors` are passed,
|
|
223
253
|
* they replace the stored baseline so subsequent resets return to the new state.
|
|
254
|
+
* @param values Optional new values to reset the form to.
|
|
255
|
+
* @param errors Optional new errors to reset the form to.
|
|
224
256
|
* @example
|
|
225
257
|
* ```ts
|
|
226
258
|
* // Reset to original initial values
|
|
227
259
|
* form.reset()
|
|
228
260
|
*
|
|
229
|
-
* // Reset to new values (becomes the new baseline)
|
|
230
|
-
* form.reset(
|
|
261
|
+
* // Reset to new values and errors (becomes the new baseline)
|
|
262
|
+
* form.reset(
|
|
263
|
+
* { name: 'Jane' },
|
|
264
|
+
* [
|
|
265
|
+
* { message: 'Invalid email', path: ['email'] }
|
|
266
|
+
* ]
|
|
267
|
+
* )
|
|
231
268
|
* ```
|
|
232
269
|
*/
|
|
233
270
|
reset: (values?: DeepPartial<StandardSchemaV1.InferInput<TSchema>>, errors?: Array<StandardSchemaV1.Issue>) => void;
|
|
@@ -254,14 +291,6 @@ interface NotArrayFieldProps<TItemSchema extends StandardSchemaV1 = StandardSche
|
|
|
254
291
|
/**
|
|
255
292
|
* Schema for a single array item — used purely for type inference.
|
|
256
293
|
* Enables typed `append`, `prepend`, `insert`, and `update` methods in the slot.
|
|
257
|
-
* @example
|
|
258
|
-
* ```vue
|
|
259
|
-
* <template>
|
|
260
|
-
* <NotArrayField path="tags" :item-schema="z.string()">
|
|
261
|
-
* <!-- append now expects a string -->
|
|
262
|
-
* </NotArrayField>
|
|
263
|
-
* </template>
|
|
264
|
-
* ```
|
|
265
294
|
*/
|
|
266
295
|
itemSchema?: TItemSchema;
|
|
267
296
|
/**
|
|
@@ -276,22 +305,9 @@ interface NotArrayFieldProps<TItemSchema extends StandardSchemaV1 = StandardSche
|
|
|
276
305
|
*/
|
|
277
306
|
validateOn?: Partial<Record<Extract<ValidationTrigger, 'onChange' | 'onMount'>, boolean>>;
|
|
278
307
|
}
|
|
279
|
-
/**
|
|
280
|
-
* Represents a single item in the array field.
|
|
281
|
-
* Use `key` for `v-for` tracking and `path` to pass to a nested `NotField`.
|
|
282
|
-
* @example
|
|
283
|
-
* ```vue
|
|
284
|
-
* <template>
|
|
285
|
-
* <NotArrayField path="tags" v-slot="{ items }">
|
|
286
|
-
* <NotField v-for="item in items" :key="item.key" :path="item.path" v-slot="{ events }">
|
|
287
|
-
* <input v-model="form.values.tags[item.index]" v-bind="events" />
|
|
288
|
-
* </NotField>
|
|
289
|
-
* </NotArrayField>
|
|
290
|
-
* </template>
|
|
291
|
-
* ```
|
|
292
|
-
*/
|
|
308
|
+
/** Represents a single item in the array field. */
|
|
293
309
|
interface NotArrayFieldItem {
|
|
294
|
-
/** Stable key
|
|
310
|
+
/** Stable key used for looping — does not change when items are reordered. */
|
|
295
311
|
key: string;
|
|
296
312
|
/** Current index of this item in the array. */
|
|
297
313
|
index: number;
|
|
@@ -308,7 +324,7 @@ interface NotArrayFieldSlots<TSchema extends ObjectSchema, TItem = any> {
|
|
|
308
324
|
default: (props: {
|
|
309
325
|
/** The dot-separated path to this array field. */
|
|
310
326
|
path: string;
|
|
311
|
-
/** The array items with stable keys and paths
|
|
327
|
+
/** The array items with stable keys and paths. */
|
|
312
328
|
items: Array<NotArrayFieldItem>;
|
|
313
329
|
/** All validation issues for this array field from the last validation run. */
|
|
314
330
|
errors: Array<StandardSchemaV1.Issue>;
|
|
@@ -329,6 +345,7 @@ interface NotArrayFieldSlots<TSchema extends ObjectSchema, TItem = any> {
|
|
|
329
345
|
/**
|
|
330
346
|
* Manually triggers validation for this array field.
|
|
331
347
|
* Useful when mutations are performed programmatically outside of the normal flow.
|
|
348
|
+
* @returns A promise that resolves to the result of the validation.
|
|
332
349
|
*/
|
|
333
350
|
validate: () => ReturnType<NotFormInstance<TSchema>['validateField']>;
|
|
334
351
|
/**
|
|
@@ -396,27 +413,12 @@ interface NotFieldProps {
|
|
|
396
413
|
/**
|
|
397
414
|
* Explicit form instance override.
|
|
398
415
|
* Takes priority over the instance provided by a `NotForm` ancestor.
|
|
399
|
-
* Required when using `NotField` outside of a `NotForm
|
|
400
|
-
* @example
|
|
401
|
-
* ```vue
|
|
402
|
-
* <template>
|
|
403
|
-
* <NotField :form="form" path="email" v-slot="{ events }">
|
|
404
|
-
* <input v-bind="events" />
|
|
405
|
-
* </NotField>
|
|
406
|
-
* </template>
|
|
407
|
-
* ```
|
|
416
|
+
* Required when using `NotField` outside of a `NotForm`.
|
|
408
417
|
*/
|
|
409
418
|
form?: NotFormInstance<any>;
|
|
410
419
|
/**
|
|
411
420
|
* Per-field validation trigger overrides.
|
|
412
421
|
* Merged over the form-wide `validateOn` — only the keys you specify are overridden.
|
|
413
|
-
* @example
|
|
414
|
-
* ```vue
|
|
415
|
-
* <template>
|
|
416
|
-
* <!-- form validates on blur only, but this field also validates on every input -->
|
|
417
|
-
* <NotField :validateOn="{ onInput: true }" path="username" />
|
|
418
|
-
* </template>
|
|
419
|
-
* ```
|
|
420
422
|
*/
|
|
421
423
|
validateOn?: Partial<Record<ValidationTrigger, boolean>>;
|
|
422
424
|
/**
|
|
@@ -429,15 +431,6 @@ interface NotFieldProps {
|
|
|
429
431
|
*
|
|
430
432
|
* Blur- and submit-triggered validation always runs immediately, regardless
|
|
431
433
|
* of this setting, so the field never feels unresponsive when the user leaves.
|
|
432
|
-
* @example
|
|
433
|
-
* ```vue
|
|
434
|
-
* <template>
|
|
435
|
-
* <!-- validate 400ms after the user stops typing -->
|
|
436
|
-
* <NotField path="username" :debounce="400" v-slot="{ events }">
|
|
437
|
-
* <input v-model="form.values.username" v-bind="events" />
|
|
438
|
-
* </NotField>
|
|
439
|
-
* </template>
|
|
440
|
-
* ```
|
|
441
434
|
*
|
|
442
435
|
* Omit or set to `0` to disable debouncing (default behaviour).
|
|
443
436
|
*/
|
|
@@ -471,6 +464,7 @@ interface NotFieldSlots<TSchema extends ObjectSchema> {
|
|
|
471
464
|
/**
|
|
472
465
|
* Manually triggers validation for this field.
|
|
473
466
|
* Useful for custom inputs that manage their own interaction events.
|
|
467
|
+
* @returns A promise that resolves to the result of the validation.
|
|
474
468
|
*/
|
|
475
469
|
validate: () => ReturnType<NotFormInstance<TSchema>['validateField']>;
|
|
476
470
|
/**
|
|
@@ -539,13 +533,7 @@ interface NotMessageProps {
|
|
|
539
533
|
/**
|
|
540
534
|
* Explicit form instance override.
|
|
541
535
|
* Takes priority over the instance provided by a `NotForm` ancestor.
|
|
542
|
-
* Required when using `NotMessage` outside of a `NotForm
|
|
543
|
-
* @example
|
|
544
|
-
* ```vue
|
|
545
|
-
* <template>
|
|
546
|
-
* <NotMessage :form="form" path="email" />
|
|
547
|
-
* </template>
|
|
548
|
-
* ```
|
|
536
|
+
* Required when using `NotMessage` outside of a `NotForm`.
|
|
549
537
|
*/
|
|
550
538
|
form?: NotFormInstance<any>;
|
|
551
539
|
}
|
package/dist/index.js
CHANGED
|
@@ -184,13 +184,14 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
184
184
|
const props = __props;
|
|
185
185
|
const form = useNotFormInstance(props.form);
|
|
186
186
|
const isValidating = ref(false);
|
|
187
|
+
const validatingCount = ref(0);
|
|
187
188
|
/** Counter to ensure absolute uniqueness for generated keys. */
|
|
188
|
-
|
|
189
|
+
const keyCounter = ref(0);
|
|
189
190
|
/** Stable keys per item that survive reorders, removals, and inserts. */
|
|
190
191
|
const itemKeys = ref((() => {
|
|
191
192
|
const initial = getProperty(form.values, props.path);
|
|
192
193
|
const length = Array.isArray(initial) ? initial.length : 0;
|
|
193
|
-
return Array.from({ length }, () => `${props.path}-${keyCounter++}`);
|
|
194
|
+
return Array.from({ length }, () => `${props.path}-${keyCounter.value++}`);
|
|
194
195
|
})());
|
|
195
196
|
const validateOn = computed(() => ({
|
|
196
197
|
onChange: props.validateOn?.onChange ?? form.validateOn.onChange,
|
|
@@ -216,13 +217,31 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
216
217
|
return form.dirtyFields.has(props.path) || [...form.dirtyFields].some((path) => path.startsWith(`${props.path}.`));
|
|
217
218
|
});
|
|
218
219
|
/**
|
|
220
|
+
* Starts a local field validation execution.
|
|
221
|
+
*/
|
|
222
|
+
function beginValidation() {
|
|
223
|
+
validatingCount.value++;
|
|
224
|
+
isValidating.value = true;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Ends a local field validation execution.
|
|
228
|
+
*/
|
|
229
|
+
function endValidation() {
|
|
230
|
+
validatingCount.value--;
|
|
231
|
+
if (validatingCount.value === 0) isValidating.value = false;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
219
234
|
* Generates a unique key string using the field path and counter.
|
|
220
|
-
* @returns A unique key string
|
|
235
|
+
* @returns A unique key string.
|
|
221
236
|
*/
|
|
222
237
|
function generateKey() {
|
|
223
|
-
return `${props.path}-${keyCounter++}`;
|
|
238
|
+
return `${props.path}-${keyCounter.value++}`;
|
|
224
239
|
}
|
|
225
|
-
/**
|
|
240
|
+
/**
|
|
241
|
+
* Re-aligns itemKeys with current array length.
|
|
242
|
+
*
|
|
243
|
+
* Used mostly after external resets.
|
|
244
|
+
*/
|
|
226
245
|
function syncKeys() {
|
|
227
246
|
const arrayLength = array.value.length;
|
|
228
247
|
if (itemKeys.value.length > arrayLength) itemKeys.value.length = arrayLength;
|
|
@@ -230,7 +249,7 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
230
249
|
}
|
|
231
250
|
/**
|
|
232
251
|
* Applies an array update, syncs form dirty/touched state, and triggers validation.
|
|
233
|
-
* @param updater Function that modifies the array
|
|
252
|
+
* @param updater Function that modifies the array.
|
|
234
253
|
*/
|
|
235
254
|
function mutate(updater) {
|
|
236
255
|
const current = [...array.value];
|
|
@@ -242,37 +261,41 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
242
261
|
if (validateOn.value.onChange) validate();
|
|
243
262
|
}
|
|
244
263
|
/**
|
|
245
|
-
* Validates the array field
|
|
246
|
-
* @returns
|
|
264
|
+
* Validates the array field.
|
|
265
|
+
* @returns A promise that resolves to the validation result.
|
|
247
266
|
*/
|
|
248
267
|
async function validate() {
|
|
249
|
-
|
|
268
|
+
beginValidation();
|
|
250
269
|
try {
|
|
251
270
|
return await form.validateField(props.path);
|
|
252
271
|
} finally {
|
|
253
|
-
|
|
272
|
+
endValidation();
|
|
254
273
|
}
|
|
255
274
|
}
|
|
256
275
|
/**
|
|
257
|
-
* Appends an item to the array
|
|
258
|
-
* @param value The value to append
|
|
276
|
+
* Appends an item to the array.
|
|
277
|
+
* @param value The value to append.
|
|
259
278
|
*/
|
|
260
279
|
function append(value) {
|
|
261
280
|
itemKeys.value.push(generateKey());
|
|
262
|
-
mutate((current) =>
|
|
281
|
+
mutate((current) => {
|
|
282
|
+
current.push(value);
|
|
283
|
+
});
|
|
263
284
|
}
|
|
264
285
|
/**
|
|
265
|
-
* Prepends an item to the array
|
|
266
|
-
* @param value The value to prepend
|
|
286
|
+
* Prepends an item to the array.
|
|
287
|
+
* @param value The value to prepend.
|
|
267
288
|
*/
|
|
268
289
|
function prepend(value) {
|
|
269
290
|
itemKeys.value.unshift(generateKey());
|
|
270
291
|
remapArrayFieldState(form, props.path, (previousIndex) => previousIndex + 1);
|
|
271
|
-
mutate((current) =>
|
|
292
|
+
mutate((current) => {
|
|
293
|
+
current.unshift(value);
|
|
294
|
+
});
|
|
272
295
|
}
|
|
273
296
|
/**
|
|
274
|
-
* Removes an item from the array
|
|
275
|
-
* @param index The index
|
|
297
|
+
* Removes an item from the array.
|
|
298
|
+
* @param index The item index to remove.
|
|
276
299
|
*/
|
|
277
300
|
function remove(index) {
|
|
278
301
|
itemKeys.value.splice(index, 1);
|
|
@@ -283,9 +306,9 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
283
306
|
mutate((current) => current.splice(index, 1));
|
|
284
307
|
}
|
|
285
308
|
/**
|
|
286
|
-
* Inserts an item at a specific index
|
|
287
|
-
* @param index The index
|
|
288
|
-
* @param value The value to insert
|
|
309
|
+
* Inserts an item at a specific index.
|
|
310
|
+
* @param index The insertion index.
|
|
311
|
+
* @param value The value to insert.
|
|
289
312
|
*/
|
|
290
313
|
function insert(index, value) {
|
|
291
314
|
itemKeys.value.splice(index, 0, generateKey());
|
|
@@ -296,9 +319,9 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
296
319
|
mutate((current) => current.splice(index, 0, value));
|
|
297
320
|
}
|
|
298
321
|
/**
|
|
299
|
-
* Updates an item at a specific index
|
|
300
|
-
* @param index The index to update
|
|
301
|
-
* @param value The new value
|
|
322
|
+
* Updates an item at a specific index.
|
|
323
|
+
* @param index The item index to update.
|
|
324
|
+
* @param value The new item value.
|
|
302
325
|
*/
|
|
303
326
|
function update(index, value) {
|
|
304
327
|
mutate((current) => {
|
|
@@ -306,9 +329,9 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
306
329
|
});
|
|
307
330
|
}
|
|
308
331
|
/**
|
|
309
|
-
* Swaps two items in the array
|
|
310
|
-
* @param indexA The first index
|
|
311
|
-
* @param indexB The second index
|
|
332
|
+
* Swaps two items in the array.
|
|
333
|
+
* @param indexA The first item index.
|
|
334
|
+
* @param indexB The second item index.
|
|
312
335
|
*/
|
|
313
336
|
function swap(indexA, indexB) {
|
|
314
337
|
const temporaryKey = itemKeys.value[indexA];
|
|
@@ -326,9 +349,9 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
326
349
|
});
|
|
327
350
|
}
|
|
328
351
|
/**
|
|
329
|
-
* Moves an item from one index to another
|
|
330
|
-
* @param from The source index
|
|
331
|
-
* @param to The destination index
|
|
352
|
+
* Moves an item from one index to another.
|
|
353
|
+
* @param from The source index.
|
|
354
|
+
* @param to The destination index.
|
|
332
355
|
*/
|
|
333
356
|
function move(from, to) {
|
|
334
357
|
const [movedItemKey] = itemKeys.value.splice(from, 1);
|
|
@@ -399,9 +422,10 @@ var not_field_default = /* @__PURE__ */ defineComponent({
|
|
|
399
422
|
const props = __props;
|
|
400
423
|
const form = useNotFormInstance(props.form);
|
|
401
424
|
const isValidating = ref(false);
|
|
425
|
+
const validatingCount = ref(0);
|
|
402
426
|
/** Timer handle for the current pending debounced validation. */
|
|
403
|
-
|
|
404
|
-
/** Merges per-field overrides with the form-wide validation config */
|
|
427
|
+
const debounceTimer = ref();
|
|
428
|
+
/** Merges per-field overrides with the form-wide validation config. */
|
|
405
429
|
const validateOn = computed(() => ({
|
|
406
430
|
...form.validateOn,
|
|
407
431
|
...props.validateOn
|
|
@@ -411,37 +435,46 @@ var not_field_default = /* @__PURE__ */ defineComponent({
|
|
|
411
435
|
const isValid = computed(() => errors.value.length === 0);
|
|
412
436
|
const isTouched = computed(() => form.touchedFields.has(props.path));
|
|
413
437
|
const isDirty = computed(() => form.dirtyFields.has(props.path));
|
|
438
|
+
/** Starts a local field validation execution. */
|
|
439
|
+
function beginValidation() {
|
|
440
|
+
validatingCount.value++;
|
|
441
|
+
isValidating.value = true;
|
|
442
|
+
}
|
|
443
|
+
/** Ends a local field validation execution. */
|
|
444
|
+
function endValidation() {
|
|
445
|
+
validatingCount.value--;
|
|
446
|
+
if (validatingCount.value === 0) isValidating.value = false;
|
|
447
|
+
}
|
|
414
448
|
/** Cancels pending debounced validation on blur/unmount to prevent rogue execution. */
|
|
415
449
|
function clearDebounce() {
|
|
416
|
-
if (debounceTimer
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
450
|
+
if (debounceTimer.value === void 0) return;
|
|
451
|
+
clearTimeout(debounceTimer.value);
|
|
452
|
+
debounceTimer.value = void 0;
|
|
420
453
|
}
|
|
421
454
|
/** Compares current value against baseline to sync dirty state tracking. */
|
|
422
455
|
function updateDirty() {
|
|
423
456
|
if (dequal(value.value, getProperty(form.initialValues, props.path))) form.dirtyFields.delete(props.path);
|
|
424
457
|
else form.dirtyField(props.path);
|
|
425
458
|
}
|
|
426
|
-
/** Replaces pending
|
|
459
|
+
/** Replaces pending debounced validation with a new timer, or runs immediately when no debounce is configured. */
|
|
427
460
|
function scheduleValidation() {
|
|
428
461
|
if (!props.debounce) {
|
|
429
462
|
validate();
|
|
430
463
|
return;
|
|
431
464
|
}
|
|
432
465
|
clearDebounce();
|
|
433
|
-
debounceTimer = setTimeout(validate, props.debounce);
|
|
466
|
+
debounceTimer.value = setTimeout(validate, props.debounce);
|
|
434
467
|
}
|
|
435
468
|
/**
|
|
436
469
|
* Validates the field and returns the validation result.
|
|
437
470
|
* @returns A promise that resolves to the validation result.
|
|
438
471
|
*/
|
|
439
472
|
async function validate() {
|
|
440
|
-
|
|
473
|
+
beginValidation();
|
|
441
474
|
try {
|
|
442
475
|
return await form.validateField(props.path);
|
|
443
476
|
} finally {
|
|
444
|
-
|
|
477
|
+
endValidation();
|
|
445
478
|
}
|
|
446
479
|
}
|
|
447
480
|
/** Handles the blur event for the field. */
|
|
@@ -564,66 +597,89 @@ function useNotForm(config) {
|
|
|
564
597
|
const touchedFields = reactive(/* @__PURE__ */ new Set());
|
|
565
598
|
const dirtyFields = reactive(/* @__PURE__ */ new Set());
|
|
566
599
|
const isSubmitting = ref(false);
|
|
567
|
-
const isValidating = ref(false);
|
|
568
|
-
/**
|
|
569
|
-
* Tracks concurrent validations to keep `isValidating` true until all finish.
|
|
570
|
-
* @internal
|
|
571
|
-
*/
|
|
572
|
-
let validatingCount = 0;
|
|
573
600
|
const isValid = computed(() => errors.length === 0);
|
|
574
601
|
const isDirty = computed(() => dirtyFields.size > 0);
|
|
575
602
|
const isTouched = computed(() => touchedFields.size > 0);
|
|
576
603
|
const errorsMap = computed(() => {
|
|
577
604
|
const result = {};
|
|
578
|
-
for (const issue of errors)
|
|
605
|
+
for (const issue of errors) {
|
|
606
|
+
if (!issue.path) continue;
|
|
579
607
|
const path = issue.path.map((element) => normalizeSegment(element)).join(".");
|
|
580
|
-
if (path && !result
|
|
608
|
+
if (path && !Object.hasOwn(result, path)) result[path] = issue.message;
|
|
581
609
|
}
|
|
582
610
|
return result;
|
|
583
611
|
});
|
|
612
|
+
/** Whether one or more validation operations are currently running. */
|
|
613
|
+
const isValidating = ref(false);
|
|
614
|
+
/** Number of validation operations currently in progress. */
|
|
615
|
+
const validatingCount = ref(0);
|
|
616
|
+
/** Identifier of the latest validation cycle. */
|
|
617
|
+
let validationCycle = 0;
|
|
618
|
+
/** Identifier of the latest validation cycle for each field. */
|
|
619
|
+
const fieldValidationCycles = /* @__PURE__ */ new Map();
|
|
584
620
|
/**
|
|
585
|
-
*
|
|
586
|
-
* @
|
|
621
|
+
* Starts a validation operation.
|
|
622
|
+
* @returns The identifier of the new validation cycle.
|
|
587
623
|
*/
|
|
588
|
-
function
|
|
589
|
-
validatingCount++;
|
|
624
|
+
function beginValidation() {
|
|
625
|
+
validatingCount.value++;
|
|
590
626
|
isValidating.value = true;
|
|
627
|
+
return ++validationCycle;
|
|
591
628
|
}
|
|
592
629
|
/**
|
|
593
|
-
*
|
|
594
|
-
* @
|
|
630
|
+
* Starts a validation operation for a specific field path.
|
|
631
|
+
* @param path The field path to start validation for.
|
|
632
|
+
* @returns The identifier of the new validation cycle.
|
|
595
633
|
*/
|
|
596
|
-
function
|
|
597
|
-
validatingCount
|
|
598
|
-
|
|
634
|
+
function beginFieldValidation(path) {
|
|
635
|
+
validatingCount.value++;
|
|
636
|
+
isValidating.value = true;
|
|
637
|
+
const key = String(path);
|
|
638
|
+
const cycle = (fieldValidationCycles.get(key) ?? 0) + 1;
|
|
639
|
+
fieldValidationCycles.set(key, cycle);
|
|
640
|
+
return cycle;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Checks if the validation cycle for a specific field path is current.
|
|
644
|
+
* @param path The field path to check.
|
|
645
|
+
* @param cycle The validation cycle identifier.
|
|
646
|
+
* @returns Whether the cycle is still current.
|
|
647
|
+
*/
|
|
648
|
+
function isCurrentFieldValidation(path, cycle) {
|
|
649
|
+
return fieldValidationCycles.get(String(path)) === cycle;
|
|
650
|
+
}
|
|
651
|
+
/** Marks a validation operation as finished. */
|
|
652
|
+
function endValidation() {
|
|
653
|
+
validatingCount.value--;
|
|
654
|
+
if (validatingCount.value === 0) isValidating.value = false;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Checks whether a validation cycle is still the latest cycle.
|
|
658
|
+
* @param cycle The validation cycle identifier.
|
|
659
|
+
* @returns Whether the cycle is still current.
|
|
660
|
+
*/
|
|
661
|
+
function isCurrentValidation(cycle) {
|
|
662
|
+
return cycle === validationCycle;
|
|
599
663
|
}
|
|
600
664
|
/**
|
|
601
665
|
* Resolves and executes the standard schema validation.
|
|
602
|
-
* @internal
|
|
603
666
|
* @returns The validation result containing either `value` or `issues`.
|
|
604
667
|
*/
|
|
605
668
|
function runSchema() {
|
|
606
669
|
return toValue(config.schema)["~standard"].validate(values);
|
|
607
670
|
}
|
|
608
|
-
/**
|
|
609
|
-
* Iterates through deeply nested keys to mark all as touched.
|
|
610
|
-
* @internal
|
|
611
|
-
*/
|
|
671
|
+
/** Iterates through deeply nested keys to mark all as touched. */
|
|
612
672
|
function touchAllFields() {
|
|
613
673
|
for (const path of deepKeys(values)) touchedFields.add(path);
|
|
614
674
|
}
|
|
615
675
|
/**
|
|
616
676
|
* Removes a specific field path from the dirty tracking set.
|
|
617
|
-
* @internal
|
|
618
677
|
* @param path The field path to mark as clean.
|
|
619
678
|
*/
|
|
620
679
|
function unDirtyField(path) {
|
|
621
680
|
dirtyFields.delete(path);
|
|
622
681
|
}
|
|
623
|
-
/**
|
|
624
|
-
* Iterates through deeply nested keys to mark all as dirty.
|
|
625
|
-
* @internal
|
|
626
|
-
*/
|
|
682
|
+
/** Iterates through deeply nested keys to mark all as dirty. */
|
|
627
683
|
function dirtyAllFields() {
|
|
628
684
|
for (const path of deepKeys(values)) dirtyFields.add(path);
|
|
629
685
|
}
|
|
@@ -664,7 +720,7 @@ function useNotForm(config) {
|
|
|
664
720
|
else errors[existingIndex] = newIssue;
|
|
665
721
|
}
|
|
666
722
|
/**
|
|
667
|
-
* Sets multiple error issues in the errors array, replacing
|
|
723
|
+
* Sets multiple error issues in the errors array, replacing all existing issues.
|
|
668
724
|
* @param newIssues The array of issues to set.
|
|
669
725
|
*/
|
|
670
726
|
function setErrors(newIssues) {
|
|
@@ -672,7 +728,7 @@ function useNotForm(config) {
|
|
|
672
728
|
}
|
|
673
729
|
/** Clears all error issues from the errors array. */
|
|
674
730
|
function clearErrors() {
|
|
675
|
-
errors.
|
|
731
|
+
errors.length = 0;
|
|
676
732
|
}
|
|
677
733
|
/**
|
|
678
734
|
* Retrieves all error issues associated with a specific field path.
|
|
@@ -685,31 +741,38 @@ function useNotForm(config) {
|
|
|
685
741
|
}
|
|
686
742
|
/**
|
|
687
743
|
* Validates the entire form against the schema, updating errors and returning the result.
|
|
744
|
+
*
|
|
745
|
+
* Only the latest validation cycle is allowed to mutate form validation state.
|
|
688
746
|
* @returns An object containing either `value` or `issues` based on validation outcome.
|
|
689
747
|
*/
|
|
690
748
|
async function validate() {
|
|
691
|
-
|
|
749
|
+
const cycle = beginValidation();
|
|
692
750
|
try {
|
|
693
751
|
const result = await runSchema();
|
|
694
|
-
if (
|
|
695
|
-
|
|
696
|
-
|
|
752
|
+
if (!isCurrentValidation(cycle)) return result;
|
|
753
|
+
if (result.issues) {
|
|
754
|
+
const issues = [...result.issues];
|
|
755
|
+
setErrors(issues);
|
|
756
|
+
return { issues };
|
|
697
757
|
}
|
|
698
758
|
clearErrors();
|
|
699
759
|
return { value: result.value };
|
|
700
760
|
} finally {
|
|
701
|
-
|
|
761
|
+
endValidation();
|
|
702
762
|
}
|
|
703
763
|
}
|
|
704
764
|
/**
|
|
705
765
|
* Validates a specific field against the schema, updating errors and returning the result.
|
|
766
|
+
*
|
|
767
|
+
* Only the latest validation cycle is allowed to mutate form validation state.
|
|
706
768
|
* @param path The field path to validate.
|
|
707
769
|
* @returns An object containing either `value` or `issues` based on validation outcome.
|
|
708
770
|
*/
|
|
709
771
|
async function validateField(path) {
|
|
710
|
-
|
|
772
|
+
const cycle = beginFieldValidation(path);
|
|
711
773
|
try {
|
|
712
774
|
const result = await runSchema();
|
|
775
|
+
if (!isCurrentFieldValidation(path, cycle)) return result;
|
|
713
776
|
const pathSegments = parsePath(path);
|
|
714
777
|
const staleIndices = [];
|
|
715
778
|
for (const [index, error] of errors.entries()) if (isIssuePathEqual(error.path, pathSegments)) staleIndices.push(index);
|
|
@@ -724,30 +787,33 @@ function useNotForm(config) {
|
|
|
724
787
|
}
|
|
725
788
|
return { value: getProperty(result.value, path) };
|
|
726
789
|
} finally {
|
|
727
|
-
|
|
790
|
+
endValidation();
|
|
728
791
|
}
|
|
729
792
|
}
|
|
730
793
|
/**
|
|
731
794
|
* Submits the form, validating it and executing the submit handler if provided.
|
|
795
|
+
*
|
|
796
|
+
* Duplicate submissions are ignored while a submission is already in progress.
|
|
732
797
|
* @param event The submit event.
|
|
733
798
|
*/
|
|
734
799
|
async function submit(event) {
|
|
800
|
+
event.preventDefault();
|
|
801
|
+
if (isSubmitting.value) return;
|
|
735
802
|
isSubmitting.value = true;
|
|
803
|
+
const cycle = beginValidation();
|
|
736
804
|
try {
|
|
737
805
|
touchAllFields();
|
|
738
806
|
dirtyAllFields();
|
|
739
|
-
const result = await
|
|
740
|
-
if (
|
|
741
|
-
|
|
807
|
+
const result = await runSchema();
|
|
808
|
+
if (!isCurrentValidation(cycle)) return;
|
|
809
|
+
if (result.issues) {
|
|
810
|
+
setErrors([...result.issues]);
|
|
742
811
|
return;
|
|
743
812
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
await config.onSubmit(result.value);
|
|
747
|
-
}
|
|
748
|
-
} catch {
|
|
749
|
-
event.preventDefault();
|
|
813
|
+
clearErrors();
|
|
814
|
+
if (config.onSubmit) await config.onSubmit(result.value);
|
|
750
815
|
} finally {
|
|
816
|
+
endValidation();
|
|
751
817
|
isSubmitting.value = false;
|
|
752
818
|
}
|
|
753
819
|
}
|
|
@@ -757,6 +823,7 @@ function useNotForm(config) {
|
|
|
757
823
|
* @param newErrors The new errors to reset the form with.
|
|
758
824
|
*/
|
|
759
825
|
function reset(newValues, newErrors) {
|
|
826
|
+
validationCycle++;
|
|
760
827
|
if (newValues) initialValues = klona(newValues);
|
|
761
828
|
if (newErrors) initialErrors = klona(newErrors);
|
|
762
829
|
const freshValues = klona(initialValues);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notform",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.2.
|
|
4
|
+
"version": "2.2.4",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Vue Forms Without the Friction",
|
|
7
7
|
"author": "Favour Emeka <favorodera@gmail.com>",
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
39
|
+
"node": ">=24.0.0",
|
|
40
40
|
"pnpm": ">=11.0.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"vue": "^3.
|
|
43
|
+
"vue": "^3.5.0"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@standard-schema/spec": "^1.1.0",
|
|
@@ -49,18 +49,18 @@
|
|
|
49
49
|
"klona": "^2.0.6"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@favorodera/eslint-config": "^1.
|
|
53
|
-
"@types/node": "^26.
|
|
52
|
+
"@favorodera/eslint-config": "^1.1.3",
|
|
53
|
+
"@types/node": "^26.5.0",
|
|
54
54
|
"@vitejs/plugin-vue": "^6.0.8",
|
|
55
|
-
"@vitest/ui": "^4.1.
|
|
56
|
-
"@vue/test-utils": "^2.
|
|
57
|
-
"happy-dom": "^20.
|
|
55
|
+
"@vitest/ui": "^4.1.11",
|
|
56
|
+
"@vue/test-utils": "^2.5.0",
|
|
57
|
+
"happy-dom": "^20.14.0",
|
|
58
58
|
"tsdown": "^0.22.14",
|
|
59
|
-
"type-fest": "^5.
|
|
60
|
-
"vite": "^8.2.
|
|
61
|
-
"vitest": "^4.1.
|
|
62
|
-
"vue": "^3.
|
|
63
|
-
"vue-tsc": "^3.3.
|
|
59
|
+
"type-fest": "^5.9.0",
|
|
60
|
+
"vite": "^8.2.2",
|
|
61
|
+
"vitest": "^4.1.11",
|
|
62
|
+
"vue": "^3.5.0",
|
|
63
|
+
"vue-tsc": "^3.3.11"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
66
66
|
"build": "tsdown",
|