notform 2.2.3 → 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 +94 -119
- package/dist/index.d.ts +57 -69
- package/dist/index.js +134 -64
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1><code>notform</code></h1>
|
|
3
|
-
<p><strong>Headless
|
|
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
|
-
`notform` is the core
|
|
11
|
+
`notform` is the core NotForm package for Vue 3.
|
|
12
|
+
|
|
13
|
+
It provides reactive form state, schema-based validation, field state, submission handling, and renderless components that work with any UI you choose.
|
|
12
14
|
|
|
13
15
|
## Installation
|
|
14
16
|
|
|
@@ -16,35 +18,31 @@
|
|
|
16
18
|
pnpm add notform
|
|
17
19
|
```
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
NotForm follows a **headless, composable-first** approach. Forms are managed through the `useNotForm` composable which handles validation, state, and submission logic in a type-safe manner.
|
|
21
|
+
Install a Standard Schema-compatible validator separately, for example:
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- **Form state** — Managed reactively with full TypeScript support
|
|
27
|
-
- **Validation** — Automatic validation based on your schema with error messages
|
|
28
|
-
- **Submission handling** — Built-in submission lifecycle with loading states
|
|
29
|
-
- **Your UI** — NotForm renders nothing—you bring your own components
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add zod
|
|
25
|
+
```
|
|
30
26
|
|
|
31
27
|
## Basic Usage
|
|
32
28
|
|
|
33
|
-
Import the components and composable you need:
|
|
34
|
-
|
|
35
29
|
```vue
|
|
36
30
|
<script setup lang="ts">
|
|
37
31
|
import { NotField, NotForm, NotMessage, useNotForm } from 'notform'
|
|
38
32
|
import { z } from 'zod'
|
|
39
33
|
|
|
40
34
|
const schema = z.object({
|
|
41
|
-
email: z.email('
|
|
42
|
-
name: z.string(
|
|
35
|
+
email: z.email('Enter a valid email address'),
|
|
36
|
+
name: z.string('Enter a valid name'),
|
|
43
37
|
})
|
|
44
38
|
|
|
45
39
|
const form = useNotForm({
|
|
40
|
+
initialValues: {
|
|
41
|
+
email: '',
|
|
42
|
+
name: '',
|
|
43
|
+
},
|
|
46
44
|
onSubmit(values) {
|
|
47
|
-
console.log('
|
|
45
|
+
console.log('Submitted:', values)
|
|
48
46
|
},
|
|
49
47
|
schema,
|
|
50
48
|
})
|
|
@@ -52,42 +50,48 @@ const form = useNotForm({
|
|
|
52
50
|
|
|
53
51
|
<template>
|
|
54
52
|
<NotForm
|
|
55
|
-
:form
|
|
53
|
+
:form="form"
|
|
56
54
|
@submit.prevent="form.submit"
|
|
57
55
|
@reset="form.reset()"
|
|
58
56
|
>
|
|
59
57
|
<NotField
|
|
60
|
-
v-slot="{events,path}"
|
|
58
|
+
v-slot="{ events, path }"
|
|
61
59
|
path="name"
|
|
62
60
|
>
|
|
63
|
-
<
|
|
64
|
-
Name
|
|
61
|
+
<div>
|
|
62
|
+
<label :for="path">Name</label>
|
|
63
|
+
|
|
65
64
|
<input
|
|
66
|
-
v-bind="events"
|
|
67
65
|
:id="path"
|
|
68
66
|
v-model="form.values.name"
|
|
67
|
+
v-bind="events"
|
|
68
|
+
name="name"
|
|
69
69
|
type="text"
|
|
70
|
+
autocomplete="name"
|
|
70
71
|
>
|
|
71
|
-
</label>
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
<NotMessage :path="path" />
|
|
74
|
+
</div>
|
|
74
75
|
</NotField>
|
|
75
76
|
|
|
76
77
|
<NotField
|
|
77
|
-
v-slot="{events,path}"
|
|
78
|
+
v-slot="{ events, path }"
|
|
78
79
|
path="email"
|
|
79
80
|
>
|
|
80
|
-
<
|
|
81
|
-
Email
|
|
81
|
+
<div>
|
|
82
|
+
<label :for="path">Email address</label>
|
|
83
|
+
|
|
82
84
|
<input
|
|
83
|
-
v-bind="events"
|
|
84
85
|
:id="path"
|
|
85
86
|
v-model="form.values.email"
|
|
87
|
+
v-bind="events"
|
|
88
|
+
name="email"
|
|
86
89
|
type="email"
|
|
90
|
+
autocomplete="email"
|
|
87
91
|
>
|
|
88
|
-
</label>
|
|
89
92
|
|
|
90
|
-
|
|
93
|
+
<NotMessage :path="path" />
|
|
94
|
+
</div>
|
|
91
95
|
</NotField>
|
|
92
96
|
|
|
93
97
|
<button type="submit">
|
|
@@ -95,116 +99,87 @@ const form = useNotForm({
|
|
|
95
99
|
</button>
|
|
96
100
|
|
|
97
101
|
<button type="reset">
|
|
98
|
-
Reset
|
|
102
|
+
Reset
|
|
99
103
|
</button>
|
|
100
104
|
</NotForm>
|
|
101
105
|
</template>
|
|
102
106
|
```
|
|
103
107
|
|
|
104
|
-
##
|
|
108
|
+
## API
|
|
105
109
|
|
|
106
|
-
|
|
110
|
+
The core package exports:
|
|
107
111
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
- `useNotForm`
|
|
113
|
+
- `NotForm`
|
|
114
|
+
- `NotField`
|
|
115
|
+
- `NotMessage`
|
|
116
|
+
- `NotArrayField`
|
|
112
117
|
|
|
113
|
-
|
|
118
|
+
The form instance manages values, errors, touched and dirty state, validation, submission state, and reset behavior.
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
tags: z.array(tagSchema).min(1, 'At least one tag is required'),
|
|
117
|
-
})
|
|
120
|
+
## Array Fields
|
|
118
121
|
|
|
119
|
-
|
|
120
|
-
schema,
|
|
121
|
-
})
|
|
122
|
-
</script>
|
|
122
|
+
`NotArrayField` provides renderless operations for dynamic arrays while preserving stable item keys during reordering.
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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"
|
|
128
132
|
>
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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"
|
|
137
141
|
>
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
:path="item.path"
|
|
141
|
-
>
|
|
142
|
-
<input
|
|
143
|
-
v-bind="events"
|
|
144
|
-
:id="item.path"
|
|
145
|
-
v-model="form.values.tags[index]"
|
|
146
|
-
type="text"
|
|
147
|
-
>
|
|
148
|
-
</NotField>
|
|
149
|
-
|
|
150
|
-
<button
|
|
151
|
-
type="button"
|
|
152
|
-
@click="remove(index)"
|
|
153
|
-
>
|
|
154
|
-
Remove
|
|
155
|
-
</button>
|
|
156
|
-
</div>
|
|
142
|
+
<NotMessage :path="path" />
|
|
143
|
+
</NotField>
|
|
157
144
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
Add Tag
|
|
163
|
-
</button>
|
|
145
|
+
<button type="button" @click="remove(index)">
|
|
146
|
+
Remove
|
|
147
|
+
</button>
|
|
148
|
+
</div>
|
|
164
149
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
</
|
|
168
|
-
</
|
|
150
|
+
<button type="button" @click="append('')">
|
|
151
|
+
Add tag
|
|
152
|
+
</button>
|
|
153
|
+
</NotArrayField>
|
|
169
154
|
```
|
|
170
155
|
|
|
171
|
-
##
|
|
172
|
-
|
|
173
|
-
NotForm works with any validation library that implements the Standard Schema interface:
|
|
156
|
+
## Type Safety
|
|
174
157
|
|
|
175
|
-
|
|
176
|
-
- **Valibot** — Modular and type-safe schema validation
|
|
177
|
-
- **ArkType** — High-performance runtime type checking
|
|
178
|
-
- And any other Standard Schema-compatible library
|
|
158
|
+
Field paths and related APIs are inferred from your schema, including nested paths.
|
|
179
159
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
### Components
|
|
160
|
+
```ts
|
|
161
|
+
form.setValue('email', 'jane@example.com')
|
|
162
|
+
await form.validateField('email')
|
|
163
|
+
const errors = form.getFieldErrors('email')
|
|
164
|
+
```
|
|
187
165
|
|
|
188
|
-
|
|
189
|
-
- `NotField` — Field component for individual form inputs
|
|
190
|
-
- `NotMessage` — Error message display component
|
|
191
|
-
- `NotArrayField` — Array field component for dynamic form arrays
|
|
166
|
+
## Requirements
|
|
192
167
|
|
|
193
|
-
|
|
168
|
+
- Vue 3
|
|
169
|
+
- Node.js 24 or later for development in this repository
|
|
170
|
+
- A Standard Schema-compatible validator
|
|
194
171
|
|
|
195
|
-
|
|
196
|
-
- **Schema-agnostic** — Works with any Standard Schema validator (Zod, Valibot, ArkType, etc.)
|
|
197
|
-
- **Type-safe** — Full TypeScript support with inferred types from your schema
|
|
198
|
-
- **Composable** — Built for Vue 3 Composition API with a clean, intuitive API
|
|
199
|
-
- **Lightweight** — Tiny footprint with tree-shaking support
|
|
200
|
-
- **Array fields** — Built-in support for dynamic array fields with add/remove operations
|
|
201
|
-
- **Flexible** — Use with any UI library—native HTML, Nuxt UI, Shadcn, or your own components
|
|
172
|
+
## Development
|
|
202
173
|
|
|
203
|
-
|
|
174
|
+
From the repository root:
|
|
204
175
|
|
|
205
|
-
|
|
206
|
-
|
|
176
|
+
```bash
|
|
177
|
+
pnpm install
|
|
178
|
+
pnpm --filter notform test
|
|
179
|
+
pnpm --filter notform typecheck
|
|
180
|
+
pnpm --filter notform build
|
|
181
|
+
```
|
|
207
182
|
|
|
208
183
|
## License
|
|
209
184
|
|
|
210
|
-
[MIT](../../LICENSE)
|
|
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,14 +242,8 @@ 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
248
|
submit: (event: SubmitEvent) => Promise<void>;
|
|
219
249
|
/**
|
|
@@ -221,13 +251,20 @@ type NotFormInstance<TSchema extends ObjectSchema> = Raw<{
|
|
|
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,6 +184,7 @@ 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. */
|
|
@@ -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
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,20 +261,20 @@ 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());
|
|
@@ -264,8 +283,8 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
264
283
|
});
|
|
265
284
|
}
|
|
266
285
|
/**
|
|
267
|
-
* Prepends an item to the array
|
|
268
|
-
* @param value The value to prepend
|
|
286
|
+
* Prepends an item to the array.
|
|
287
|
+
* @param value The value to prepend.
|
|
269
288
|
*/
|
|
270
289
|
function prepend(value) {
|
|
271
290
|
itemKeys.value.unshift(generateKey());
|
|
@@ -275,8 +294,8 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
275
294
|
});
|
|
276
295
|
}
|
|
277
296
|
/**
|
|
278
|
-
* Removes an item from the array
|
|
279
|
-
* @param index The index
|
|
297
|
+
* Removes an item from the array.
|
|
298
|
+
* @param index The item index to remove.
|
|
280
299
|
*/
|
|
281
300
|
function remove(index) {
|
|
282
301
|
itemKeys.value.splice(index, 1);
|
|
@@ -287,9 +306,9 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
287
306
|
mutate((current) => current.splice(index, 1));
|
|
288
307
|
}
|
|
289
308
|
/**
|
|
290
|
-
* Inserts an item at a specific index
|
|
291
|
-
* @param index The index
|
|
292
|
-
* @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.
|
|
293
312
|
*/
|
|
294
313
|
function insert(index, value) {
|
|
295
314
|
itemKeys.value.splice(index, 0, generateKey());
|
|
@@ -300,9 +319,9 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
300
319
|
mutate((current) => current.splice(index, 0, value));
|
|
301
320
|
}
|
|
302
321
|
/**
|
|
303
|
-
* Updates an item at a specific index
|
|
304
|
-
* @param index The index to update
|
|
305
|
-
* @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.
|
|
306
325
|
*/
|
|
307
326
|
function update(index, value) {
|
|
308
327
|
mutate((current) => {
|
|
@@ -310,9 +329,9 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
310
329
|
});
|
|
311
330
|
}
|
|
312
331
|
/**
|
|
313
|
-
* Swaps two items in the array
|
|
314
|
-
* @param indexA The first index
|
|
315
|
-
* @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.
|
|
316
335
|
*/
|
|
317
336
|
function swap(indexA, indexB) {
|
|
318
337
|
const temporaryKey = itemKeys.value[indexA];
|
|
@@ -330,9 +349,9 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
330
349
|
});
|
|
331
350
|
}
|
|
332
351
|
/**
|
|
333
|
-
* Moves an item from one index to another
|
|
334
|
-
* @param from The source index
|
|
335
|
-
* @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.
|
|
336
355
|
*/
|
|
337
356
|
function move(from, to) {
|
|
338
357
|
const [movedItemKey] = itemKeys.value.splice(from, 1);
|
|
@@ -403,9 +422,10 @@ var not_field_default = /* @__PURE__ */ defineComponent({
|
|
|
403
422
|
const props = __props;
|
|
404
423
|
const form = useNotFormInstance(props.form);
|
|
405
424
|
const isValidating = ref(false);
|
|
425
|
+
const validatingCount = ref(0);
|
|
406
426
|
/** Timer handle for the current pending debounced validation. */
|
|
407
427
|
const debounceTimer = ref();
|
|
408
|
-
/** Merges per-field overrides with the form-wide validation config */
|
|
428
|
+
/** Merges per-field overrides with the form-wide validation config. */
|
|
409
429
|
const validateOn = computed(() => ({
|
|
410
430
|
...form.validateOn,
|
|
411
431
|
...props.validateOn
|
|
@@ -415,6 +435,16 @@ var not_field_default = /* @__PURE__ */ defineComponent({
|
|
|
415
435
|
const isValid = computed(() => errors.value.length === 0);
|
|
416
436
|
const isTouched = computed(() => form.touchedFields.has(props.path));
|
|
417
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
|
+
}
|
|
418
448
|
/** Cancels pending debounced validation on blur/unmount to prevent rogue execution. */
|
|
419
449
|
function clearDebounce() {
|
|
420
450
|
if (debounceTimer.value === void 0) return;
|
|
@@ -426,7 +456,7 @@ var not_field_default = /* @__PURE__ */ defineComponent({
|
|
|
426
456
|
if (dequal(value.value, getProperty(form.initialValues, props.path))) form.dirtyFields.delete(props.path);
|
|
427
457
|
else form.dirtyField(props.path);
|
|
428
458
|
}
|
|
429
|
-
/** Replaces pending
|
|
459
|
+
/** Replaces pending debounced validation with a new timer, or runs immediately when no debounce is configured. */
|
|
430
460
|
function scheduleValidation() {
|
|
431
461
|
if (!props.debounce) {
|
|
432
462
|
validate();
|
|
@@ -440,11 +470,11 @@ var not_field_default = /* @__PURE__ */ defineComponent({
|
|
|
440
470
|
* @returns A promise that resolves to the validation result.
|
|
441
471
|
*/
|
|
442
472
|
async function validate() {
|
|
443
|
-
|
|
473
|
+
beginValidation();
|
|
444
474
|
try {
|
|
445
475
|
return await form.validateField(props.path);
|
|
446
476
|
} finally {
|
|
447
|
-
|
|
477
|
+
endValidation();
|
|
448
478
|
}
|
|
449
479
|
}
|
|
450
480
|
/** Handles the blur event for the field. */
|
|
@@ -567,12 +597,6 @@ function useNotForm(config) {
|
|
|
567
597
|
const touchedFields = reactive(/* @__PURE__ */ new Set());
|
|
568
598
|
const dirtyFields = reactive(/* @__PURE__ */ new Set());
|
|
569
599
|
const isSubmitting = ref(false);
|
|
570
|
-
const isValidating = ref(false);
|
|
571
|
-
/**
|
|
572
|
-
* Tracks concurrent validations to keep `isValidating` true until all finish.
|
|
573
|
-
* @internal
|
|
574
|
-
*/
|
|
575
|
-
let validatingCount = 0;
|
|
576
600
|
const isValid = computed(() => errors.length === 0);
|
|
577
601
|
const isDirty = computed(() => dirtyFields.size > 0);
|
|
578
602
|
const isTouched = computed(() => touchedFields.size > 0);
|
|
@@ -585,49 +609,77 @@ function useNotForm(config) {
|
|
|
585
609
|
}
|
|
586
610
|
return result;
|
|
587
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();
|
|
588
620
|
/**
|
|
589
|
-
*
|
|
590
|
-
* @
|
|
621
|
+
* Starts a validation operation.
|
|
622
|
+
* @returns The identifier of the new validation cycle.
|
|
591
623
|
*/
|
|
592
|
-
function
|
|
593
|
-
validatingCount++;
|
|
624
|
+
function beginValidation() {
|
|
625
|
+
validatingCount.value++;
|
|
594
626
|
isValidating.value = true;
|
|
627
|
+
return ++validationCycle;
|
|
595
628
|
}
|
|
596
629
|
/**
|
|
597
|
-
*
|
|
598
|
-
* @
|
|
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.
|
|
599
633
|
*/
|
|
600
|
-
function
|
|
601
|
-
validatingCount
|
|
602
|
-
|
|
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;
|
|
603
663
|
}
|
|
604
664
|
/**
|
|
605
665
|
* Resolves and executes the standard schema validation.
|
|
606
|
-
* @internal
|
|
607
666
|
* @returns The validation result containing either `value` or `issues`.
|
|
608
667
|
*/
|
|
609
668
|
function runSchema() {
|
|
610
669
|
return toValue(config.schema)["~standard"].validate(values);
|
|
611
670
|
}
|
|
612
|
-
/**
|
|
613
|
-
* Iterates through deeply nested keys to mark all as touched.
|
|
614
|
-
* @internal
|
|
615
|
-
*/
|
|
671
|
+
/** Iterates through deeply nested keys to mark all as touched. */
|
|
616
672
|
function touchAllFields() {
|
|
617
673
|
for (const path of deepKeys(values)) touchedFields.add(path);
|
|
618
674
|
}
|
|
619
675
|
/**
|
|
620
676
|
* Removes a specific field path from the dirty tracking set.
|
|
621
|
-
* @internal
|
|
622
677
|
* @param path The field path to mark as clean.
|
|
623
678
|
*/
|
|
624
679
|
function unDirtyField(path) {
|
|
625
680
|
dirtyFields.delete(path);
|
|
626
681
|
}
|
|
627
|
-
/**
|
|
628
|
-
* Iterates through deeply nested keys to mark all as dirty.
|
|
629
|
-
* @internal
|
|
630
|
-
*/
|
|
682
|
+
/** Iterates through deeply nested keys to mark all as dirty. */
|
|
631
683
|
function dirtyAllFields() {
|
|
632
684
|
for (const path of deepKeys(values)) dirtyFields.add(path);
|
|
633
685
|
}
|
|
@@ -668,7 +720,7 @@ function useNotForm(config) {
|
|
|
668
720
|
else errors[existingIndex] = newIssue;
|
|
669
721
|
}
|
|
670
722
|
/**
|
|
671
|
-
* Sets multiple error issues in the errors array, replacing
|
|
723
|
+
* Sets multiple error issues in the errors array, replacing all existing issues.
|
|
672
724
|
* @param newIssues The array of issues to set.
|
|
673
725
|
*/
|
|
674
726
|
function setErrors(newIssues) {
|
|
@@ -689,31 +741,38 @@ function useNotForm(config) {
|
|
|
689
741
|
}
|
|
690
742
|
/**
|
|
691
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.
|
|
692
746
|
* @returns An object containing either `value` or `issues` based on validation outcome.
|
|
693
747
|
*/
|
|
694
748
|
async function validate() {
|
|
695
|
-
|
|
749
|
+
const cycle = beginValidation();
|
|
696
750
|
try {
|
|
697
751
|
const result = await runSchema();
|
|
698
|
-
if (
|
|
699
|
-
|
|
700
|
-
|
|
752
|
+
if (!isCurrentValidation(cycle)) return result;
|
|
753
|
+
if (result.issues) {
|
|
754
|
+
const issues = [...result.issues];
|
|
755
|
+
setErrors(issues);
|
|
756
|
+
return { issues };
|
|
701
757
|
}
|
|
702
758
|
clearErrors();
|
|
703
759
|
return { value: result.value };
|
|
704
760
|
} finally {
|
|
705
|
-
|
|
761
|
+
endValidation();
|
|
706
762
|
}
|
|
707
763
|
}
|
|
708
764
|
/**
|
|
709
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.
|
|
710
768
|
* @param path The field path to validate.
|
|
711
769
|
* @returns An object containing either `value` or `issues` based on validation outcome.
|
|
712
770
|
*/
|
|
713
771
|
async function validateField(path) {
|
|
714
|
-
|
|
772
|
+
const cycle = beginFieldValidation(path);
|
|
715
773
|
try {
|
|
716
774
|
const result = await runSchema();
|
|
775
|
+
if (!isCurrentFieldValidation(path, cycle)) return result;
|
|
717
776
|
const pathSegments = parsePath(path);
|
|
718
777
|
const staleIndices = [];
|
|
719
778
|
for (const [index, error] of errors.entries()) if (isIssuePathEqual(error.path, pathSegments)) staleIndices.push(index);
|
|
@@ -728,23 +787,33 @@ function useNotForm(config) {
|
|
|
728
787
|
}
|
|
729
788
|
return { value: getProperty(result.value, path) };
|
|
730
789
|
} finally {
|
|
731
|
-
|
|
790
|
+
endValidation();
|
|
732
791
|
}
|
|
733
792
|
}
|
|
734
793
|
/**
|
|
735
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.
|
|
736
797
|
* @param event The submit event.
|
|
737
798
|
*/
|
|
738
799
|
async function submit(event) {
|
|
739
800
|
event.preventDefault();
|
|
801
|
+
if (isSubmitting.value) return;
|
|
740
802
|
isSubmitting.value = true;
|
|
803
|
+
const cycle = beginValidation();
|
|
741
804
|
try {
|
|
742
805
|
touchAllFields();
|
|
743
806
|
dirtyAllFields();
|
|
744
|
-
const result = await
|
|
745
|
-
if (
|
|
807
|
+
const result = await runSchema();
|
|
808
|
+
if (!isCurrentValidation(cycle)) return;
|
|
809
|
+
if (result.issues) {
|
|
810
|
+
setErrors([...result.issues]);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
clearErrors();
|
|
746
814
|
if (config.onSubmit) await config.onSubmit(result.value);
|
|
747
815
|
} finally {
|
|
816
|
+
endValidation();
|
|
748
817
|
isSubmitting.value = false;
|
|
749
818
|
}
|
|
750
819
|
}
|
|
@@ -754,6 +823,7 @@ function useNotForm(config) {
|
|
|
754
823
|
* @param newErrors The new errors to reset the form with.
|
|
755
824
|
*/
|
|
756
825
|
function reset(newValues, newErrors) {
|
|
826
|
+
validationCycle++;
|
|
757
827
|
if (newValues) initialValues = klona(newValues);
|
|
758
828
|
if (newErrors) initialErrors = klona(newErrors);
|
|
759
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,17 +49,17 @@
|
|
|
49
49
|
"klona": "^2.0.6"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@favorodera/eslint-config": "^1.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
55
|
"@vitest/ui": "^4.1.11",
|
|
56
|
-
"@vue/test-utils": "^2.
|
|
57
|
-
"happy-dom": "^20.
|
|
56
|
+
"@vue/test-utils": "^2.5.0",
|
|
57
|
+
"happy-dom": "^20.14.0",
|
|
58
58
|
"tsdown": "^0.22.14",
|
|
59
|
-
"type-fest": "^5.
|
|
59
|
+
"type-fest": "^5.9.0",
|
|
60
60
|
"vite": "^8.2.2",
|
|
61
61
|
"vitest": "^4.1.11",
|
|
62
|
-
"vue": "^3.
|
|
62
|
+
"vue": "^3.5.0",
|
|
63
63
|
"vue-tsc": "^3.3.11"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|