notform 2.1.2 → 2.2.0
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 +40 -19
- package/dist/index.d.ts +22 -5
- package/dist/index.js +166 -43
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<p><strong>Vue Forms Without the Friction</strong></p>
|
|
4
4
|
<p>
|
|
5
5
|
<a href="https://npmx.dev/package/notform"><img src="https://img.shields.io/npm/v/notform.svg?style=plastic&label=NPM%20Version&color=blue" alt="NPM Version"></a>
|
|
6
|
-
<a href="https://npmx.dev/package/notform"><img src="https://img.shields.io/npm/
|
|
6
|
+
<a href="https://npmx.dev/package/notform"><img src="https://img.shields.io/npm/dt/notform.svg?style=plastic&label=NPM%20Downloads&color=blue" alt="NPM Downloads"></a>
|
|
7
7
|
<a href="https://npmx.dev/package/notform"><img src="https://img.shields.io/npm/unpacked-size/notform?style=plastic&label=NPM%20Unpacked%20Size&color=blue" alt="NPM Unpacked Size"></a>
|
|
8
8
|
</p>
|
|
9
9
|
</div>
|
|
@@ -37,13 +37,13 @@ import { NotField, NotForm, NotMessage, useNotForm } from 'notform'
|
|
|
37
37
|
import { z } from 'zod'
|
|
38
38
|
|
|
39
39
|
const schema = z.object({
|
|
40
|
-
email: z.
|
|
40
|
+
email: z.email('Invalid email'),
|
|
41
41
|
name: z.string().min(1, 'Name is required'),
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
const form = useNotForm({
|
|
45
|
-
onSubmit
|
|
46
|
-
console.log('Form
|
|
45
|
+
onSubmit(values) {
|
|
46
|
+
console.log('Form:', values)
|
|
47
47
|
},
|
|
48
48
|
schema,
|
|
49
49
|
})
|
|
@@ -51,30 +51,51 @@ const form = useNotForm({
|
|
|
51
51
|
|
|
52
52
|
<template>
|
|
53
53
|
<NotForm
|
|
54
|
-
:form
|
|
54
|
+
:form
|
|
55
55
|
@submit="form.submit"
|
|
56
|
+
@reset="form.reset()"
|
|
56
57
|
>
|
|
57
|
-
<NotField
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
>
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
<NotField
|
|
59
|
+
v-slot="{events,path}"
|
|
60
|
+
path="name"
|
|
61
|
+
>
|
|
62
|
+
<label :for="path">
|
|
63
|
+
Name
|
|
64
|
+
<input
|
|
65
|
+
v-bind="events"
|
|
66
|
+
:id="path"
|
|
67
|
+
v-model="form.values.name"
|
|
68
|
+
type="text"
|
|
69
|
+
>
|
|
70
|
+
</label>
|
|
71
|
+
|
|
72
|
+
<NotMessage :path="path" />
|
|
64
73
|
</NotField>
|
|
65
74
|
|
|
66
|
-
<NotField
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
>
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
<NotField
|
|
76
|
+
v-slot="{events,path}"
|
|
77
|
+
path="email"
|
|
78
|
+
>
|
|
79
|
+
<label :for="path">
|
|
80
|
+
Email
|
|
81
|
+
<input
|
|
82
|
+
v-bind="events"
|
|
83
|
+
:id="path"
|
|
84
|
+
v-model="form.values.email"
|
|
85
|
+
type="email"
|
|
86
|
+
>
|
|
87
|
+
</label>
|
|
88
|
+
|
|
89
|
+
<NotMessage :path="path" />
|
|
73
90
|
</NotField>
|
|
74
91
|
|
|
75
92
|
<button type="submit">
|
|
76
93
|
Submit
|
|
77
94
|
</button>
|
|
95
|
+
|
|
96
|
+
<button type="reset">
|
|
97
|
+
Reset Form
|
|
98
|
+
</button>
|
|
78
99
|
</NotForm>
|
|
79
100
|
</template>
|
|
80
101
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,23 @@ type ObjectSchema = StandardSchemaV1 & {
|
|
|
44
44
|
};
|
|
45
45
|
};
|
|
46
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* Represents a segment of a validation path.
|
|
49
|
+
* It can be either a string (for object properties) or a number (for array indices).
|
|
50
|
+
*/
|
|
51
|
+
type Segment = PropertyKey | StandardSchemaV1.PathSegment;
|
|
52
|
+
/**
|
|
53
|
+
* Maps the index an array item occupied before a mutation to the index it
|
|
54
|
+
* occupies after that mutation, or to `undefined` when the mutation removed the
|
|
55
|
+
* item entirely.
|
|
56
|
+
*
|
|
57
|
+
* Each array mutation in `NotArrayField` (`prepend`, `remove`, `insert`,
|
|
58
|
+
* `swap`, `move`) builds one of these to describe exactly how it reshuffles
|
|
59
|
+
* indices, then passes it to `remapArrayFieldState`.
|
|
60
|
+
* @param previousIndex The index the item occupied before the mutation.
|
|
61
|
+
* @returns The index the item occupies after the mutation, or `undefined` if the item was removed.
|
|
62
|
+
*/
|
|
63
|
+
type ArrayItemIndexMap = (previousIndex: number) => number | undefined;
|
|
47
64
|
//#endregion
|
|
48
65
|
//#region src/types/use-not-form.d.ts
|
|
49
66
|
/**
|
|
@@ -64,9 +81,9 @@ interface UseNotFormConfig<TSchema extends ObjectSchema> {
|
|
|
64
81
|
validateOn?: Partial<Record<ValidationTrigger, boolean>>;
|
|
65
82
|
/**
|
|
66
83
|
* The validation mode of the form.
|
|
67
|
-
* @default
|
|
84
|
+
* @default 'eager'
|
|
68
85
|
*/
|
|
69
|
-
validationMode?:
|
|
86
|
+
validationMode?: ValidationMode;
|
|
70
87
|
/**
|
|
71
88
|
* Callback triggered when form validation passes and the form is submitted.
|
|
72
89
|
* @param values The validated output data from the schema.
|
|
@@ -349,7 +366,7 @@ declare const __VLS_export$3: <TSchema extends ObjectSchema, TItemSchema extends
|
|
|
349
366
|
slots: NotArrayFieldSlots<TSchema, TItem>;
|
|
350
367
|
emit: {};
|
|
351
368
|
}>) => import("vue").VNode & {
|
|
352
|
-
__ctx?: Awaited<typeof __VLS_setup
|
|
369
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
353
370
|
};
|
|
354
371
|
declare const _default: typeof __VLS_export$3;
|
|
355
372
|
type __VLS_PrettifyLocal$1<T> = (T extends any ? { [K in keyof T]: T[K] } : { [K in keyof T as K]: T[K] }) & {};
|
|
@@ -466,7 +483,7 @@ declare const __VLS_export$2: <TSchema extends ObjectSchema>(__VLS_props: NonNul
|
|
|
466
483
|
slots: NotFieldSlots<TSchema>;
|
|
467
484
|
emit: {};
|
|
468
485
|
}>) => import("vue").VNode & {
|
|
469
|
-
__ctx?: Awaited<typeof __VLS_setup
|
|
486
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
470
487
|
};
|
|
471
488
|
declare const _default$1: typeof __VLS_export$2;
|
|
472
489
|
type __VLS_PrettifyLocal<T> = (T extends any ? { [K in keyof T]: T[K] } : { [K in keyof T as K]: T[K] }) & {};
|
|
@@ -535,4 +552,4 @@ type __VLS_WithSlots<T, S> = T & {
|
|
|
535
552
|
*/
|
|
536
553
|
declare function useNotForm<TSchema extends ObjectSchema>(config: UseNotFormConfig<TSchema>): NotFormInstance<TSchema>;
|
|
537
554
|
//#endregion
|
|
538
|
-
export { type DeepPartial, _default as NotArrayField, type NotArrayFieldItem, type NotArrayFieldProps, type NotArrayFieldSlots, _default$1 as NotField, type NotFieldProps, type NotFieldSlots, _default$2 as NotForm, type NotFormInstance, type NotFormProps, type NotFormSlots, _default$3 as NotMessage, type NotMessageProps, type NotMessageSlots, type ObjectSchema, type Paths, type UseNotFormConfig, type ValidationMode, type ValidationTrigger, useNotForm };
|
|
555
|
+
export { type ArrayItemIndexMap, type DeepPartial, _default as NotArrayField, type NotArrayFieldItem, type NotArrayFieldProps, type NotArrayFieldSlots, _default$1 as NotField, type NotFieldProps, type NotFieldSlots, _default$2 as NotForm, type NotFormInstance, type NotFormProps, type NotFormSlots, _default$3 as NotMessage, type NotMessageProps, type NotMessageSlots, type ObjectSchema, type Paths, type Segment, type UseNotFormConfig, type ValidationMode, type ValidationTrigger, useNotForm };
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,137 @@ import { computed, createBlock, createCommentVNode, createElementBlock, createTe
|
|
|
2
2
|
import { dequal } from "dequal";
|
|
3
3
|
import { deepKeys, deleteProperty, getProperty, hasProperty, parsePath, setProperty } from "dot-prop";
|
|
4
4
|
import { klona } from "klona/full";
|
|
5
|
-
//#region src/utils/
|
|
5
|
+
//#region src/utils/form.ts
|
|
6
|
+
/**
|
|
7
|
+
* Normalizes a validation path segment into a standard property key.
|
|
8
|
+
* @param segment The path segment to normalize.
|
|
9
|
+
* @returns The normalized key.
|
|
10
|
+
*/
|
|
11
|
+
function normalizeSegment(segment) {
|
|
12
|
+
if (typeof segment === "object" && segment !== null && "key" in segment) return segment.key;
|
|
13
|
+
return segment;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Compares two individual path segments for equality
|
|
17
|
+
* @param firstSegment The first segment to compare.
|
|
18
|
+
* @param secondSegment The second segment to compare.
|
|
19
|
+
* @returns True if the two segments refer to the same object key or array index.
|
|
20
|
+
*/
|
|
21
|
+
function arePathSegmentsEqual(firstSegment, secondSegment) {
|
|
22
|
+
if (typeof firstSegment === "number" || typeof secondSegment === "number") return Number(firstSegment) === Number(secondSegment);
|
|
23
|
+
return firstSegment === secondSegment;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Checks if a validation issue path matches a target field path.
|
|
27
|
+
* @param issuePath The path array from the validation issue.
|
|
28
|
+
* @param targetPath The normalized path to compare against.
|
|
29
|
+
* @returns True if the paths are equivalent.
|
|
30
|
+
*/
|
|
31
|
+
function isIssuePathEqual(issuePath, targetPath) {
|
|
32
|
+
if (!issuePath) return false;
|
|
33
|
+
if (issuePath.length !== targetPath.length) return false;
|
|
34
|
+
return issuePath.every((segment, segmentIndex) => {
|
|
35
|
+
const normalizedSegment = normalizeSegment(segment);
|
|
36
|
+
const targetSegment = targetPath[segmentIndex];
|
|
37
|
+
return arePathSegmentsEqual(normalizedSegment, targetSegment);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/utils/array-field.ts
|
|
42
|
+
/**
|
|
43
|
+
* Finds where a path sits inside an array field, if at all.
|
|
44
|
+
* @param pathSegments The full path segments to test, e.g. the parsed segments of `"tags.1.label"`.
|
|
45
|
+
* @param arrayFieldPathSegments The parsed path segments of the array field itself, e.g. the segments of `"tags"`.
|
|
46
|
+
* @returns The item's index and any trailing segments, or undefined if the path isn't inside this array.
|
|
47
|
+
*/
|
|
48
|
+
function getArrayItemLocation(pathSegments, arrayFieldPathSegments) {
|
|
49
|
+
if (pathSegments.length <= arrayFieldPathSegments.length) return;
|
|
50
|
+
for (const [segmentIndex, arrayFieldPathSegment] of arrayFieldPathSegments.entries()) if (!arePathSegmentsEqual(pathSegments[segmentIndex], arrayFieldPathSegment)) return;
|
|
51
|
+
const itemIndex = Number(pathSegments[arrayFieldPathSegments.length]);
|
|
52
|
+
if (Number.isNaN(itemIndex)) return;
|
|
53
|
+
return {
|
|
54
|
+
itemIndex,
|
|
55
|
+
remainingSegments: pathSegments.slice(arrayFieldPathSegments.length + 1)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Rebuilds a dot-separated path from an array field's path, an item index, and any trailing segments.
|
|
60
|
+
* @param arrayFieldPath The array field's dot-separated path, e.g. `"tags"`.
|
|
61
|
+
* @param itemIndex The item's index within the array.
|
|
62
|
+
* @param remainingSegments Segments after the index, e.g. `['label']` for a nested field.
|
|
63
|
+
* @returns The reconstructed path, e.g. `"tags.0.label"`.
|
|
64
|
+
*/
|
|
65
|
+
function buildArrayItemPath(arrayFieldPath, itemIndex, remainingSegments) {
|
|
66
|
+
return [
|
|
67
|
+
arrayFieldPath,
|
|
68
|
+
itemIndex,
|
|
69
|
+
...remainingSegments
|
|
70
|
+
].join(".");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Relocates or drops `form.errors` entries that belong to an item that moved or was removed.
|
|
74
|
+
* Errors on the array field itself (no index segment) are left untouched.
|
|
75
|
+
* @param form The form instance whose errors should be remapped.
|
|
76
|
+
* @param arrayFieldPathSegments The parsed path segments of the array field.
|
|
77
|
+
* @param arrayItemIndexMap Maps each previous item index to its new index, or undefined if removed.
|
|
78
|
+
*/
|
|
79
|
+
function remapArrayFieldErrors(form, arrayFieldPathSegments, arrayItemIndexMap) {
|
|
80
|
+
for (let errorIndex = form.errors.length - 1; errorIndex >= 0; errorIndex--) {
|
|
81
|
+
const issue = form.errors[errorIndex];
|
|
82
|
+
if (issue.path) {
|
|
83
|
+
const itemLocation = getArrayItemLocation(issue.path.map((segment) => normalizeSegment(segment)), arrayFieldPathSegments);
|
|
84
|
+
if (itemLocation) {
|
|
85
|
+
const newItemIndex = arrayItemIndexMap(itemLocation.itemIndex);
|
|
86
|
+
if (typeof newItemIndex === "number") form.errors[errorIndex] = {
|
|
87
|
+
...issue,
|
|
88
|
+
path: [
|
|
89
|
+
...arrayFieldPathSegments,
|
|
90
|
+
newItemIndex,
|
|
91
|
+
...itemLocation.remainingSegments
|
|
92
|
+
]
|
|
93
|
+
};
|
|
94
|
+
else form.errors.splice(errorIndex, 1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Relocates or drops entries in a `touchedFields`/`dirtyFields` set that belong to an item that moved or was removed.
|
|
101
|
+
* @param fieldPathSet The reactive set of field paths to remap.
|
|
102
|
+
* @param arrayFieldPath The dot-separated path of the array field.
|
|
103
|
+
* @param arrayFieldPathSegments The parsed path segments of the array field.
|
|
104
|
+
* @param arrayItemIndexMap Maps each previous item index to its new index, or undefined if removed.
|
|
105
|
+
*/
|
|
106
|
+
function remapArrayFieldPathSet(fieldPathSet, arrayFieldPath, arrayFieldPathSegments, arrayItemIndexMap) {
|
|
107
|
+
const pathsToRemove = [];
|
|
108
|
+
const pathsToAdd = [];
|
|
109
|
+
for (const fieldPath of fieldPathSet) {
|
|
110
|
+
const itemLocation = getArrayItemLocation(parsePath(fieldPath), arrayFieldPathSegments);
|
|
111
|
+
if (itemLocation) {
|
|
112
|
+
pathsToRemove.push(fieldPath);
|
|
113
|
+
const newItemIndex = arrayItemIndexMap(itemLocation.itemIndex);
|
|
114
|
+
if (typeof newItemIndex === "number") pathsToAdd.push(buildArrayItemPath(arrayFieldPath, newItemIndex, itemLocation.remainingSegments));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
for (const fieldPath of pathsToRemove) fieldPathSet.delete(fieldPath);
|
|
118
|
+
for (const fieldPath of pathsToAdd) fieldPathSet.add(fieldPath);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Keeps an array field's errors, touched paths, and dirty paths attached to the item they
|
|
122
|
+
* describe, rather than the array slot that item happened to occupy. Call before applying
|
|
123
|
+
* the mutation to `form.values`.
|
|
124
|
+
* @param form The form instance whose error, touched, and dirty state should be remapped.
|
|
125
|
+
* @param arrayFieldPath The dot-separated path of the array field being mutated, e.g. `"tags"`.
|
|
126
|
+
* @param arrayItemIndexMap Maps each previous item index to its new index, or undefined if removed.
|
|
127
|
+
*/
|
|
128
|
+
function remapArrayFieldState(form, arrayFieldPath, arrayItemIndexMap) {
|
|
129
|
+
const arrayFieldPathSegments = parsePath(arrayFieldPath);
|
|
130
|
+
remapArrayFieldErrors(form, arrayFieldPathSegments, arrayItemIndexMap);
|
|
131
|
+
remapArrayFieldPathSet(form.touchedFields, arrayFieldPath, arrayFieldPathSegments, arrayItemIndexMap);
|
|
132
|
+
remapArrayFieldPathSet(form.dirtyFields, arrayFieldPath, arrayFieldPathSegments, arrayItemIndexMap);
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/utils/instance.ts
|
|
6
136
|
/** The injection key for the NotForm instance. */
|
|
7
137
|
const NOT_FORM_INSTANCE_KEY = Symbol("notform:instance");
|
|
8
138
|
/**
|
|
@@ -137,6 +267,7 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
137
267
|
*/
|
|
138
268
|
function prepend(value) {
|
|
139
269
|
itemKeys.value.unshift(generateKey());
|
|
270
|
+
remapArrayFieldState(form, props.path, (previousIndex) => previousIndex + 1);
|
|
140
271
|
mutate((current) => current.unshift(value));
|
|
141
272
|
}
|
|
142
273
|
/**
|
|
@@ -145,6 +276,10 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
145
276
|
*/
|
|
146
277
|
function remove(index) {
|
|
147
278
|
itemKeys.value.splice(index, 1);
|
|
279
|
+
remapArrayFieldState(form, props.path, (previousIndex) => {
|
|
280
|
+
if (previousIndex === index) return;
|
|
281
|
+
return previousIndex > index ? previousIndex - 1 : previousIndex;
|
|
282
|
+
});
|
|
148
283
|
mutate((current) => current.splice(index, 1));
|
|
149
284
|
}
|
|
150
285
|
/**
|
|
@@ -154,6 +289,10 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
154
289
|
*/
|
|
155
290
|
function insert(index, value) {
|
|
156
291
|
itemKeys.value.splice(index, 0, generateKey());
|
|
292
|
+
remapArrayFieldState(form, props.path, (previousIndex) => {
|
|
293
|
+
if (previousIndex >= index) return previousIndex + 1;
|
|
294
|
+
return previousIndex;
|
|
295
|
+
});
|
|
157
296
|
mutate((current) => current.splice(index, 0, value));
|
|
158
297
|
}
|
|
159
298
|
/**
|
|
@@ -172,13 +311,18 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
172
311
|
* @param indexB The second index
|
|
173
312
|
*/
|
|
174
313
|
function swap(indexA, indexB) {
|
|
175
|
-
const
|
|
314
|
+
const temporaryKey = itemKeys.value[indexA];
|
|
176
315
|
itemKeys.value[indexA] = itemKeys.value[indexB];
|
|
177
|
-
itemKeys.value[indexB] =
|
|
316
|
+
itemKeys.value[indexB] = temporaryKey;
|
|
317
|
+
remapArrayFieldState(form, props.path, (previousIndex) => {
|
|
318
|
+
if (previousIndex === indexA) return indexB;
|
|
319
|
+
if (previousIndex === indexB) return indexA;
|
|
320
|
+
return previousIndex;
|
|
321
|
+
});
|
|
178
322
|
mutate((current) => {
|
|
179
|
-
const
|
|
323
|
+
const temporaryValue = current[indexA];
|
|
180
324
|
current[indexA] = current[indexB];
|
|
181
|
-
current[indexB] =
|
|
325
|
+
current[indexB] = temporaryValue;
|
|
182
326
|
});
|
|
183
327
|
}
|
|
184
328
|
/**
|
|
@@ -187,11 +331,20 @@ var not_array_field_default = /* @__PURE__ */ defineComponent({
|
|
|
187
331
|
* @param to The destination index
|
|
188
332
|
*/
|
|
189
333
|
function move(from, to) {
|
|
190
|
-
const [
|
|
191
|
-
itemKeys.value.splice(to, 0,
|
|
334
|
+
const [movedItemKey] = itemKeys.value.splice(from, 1);
|
|
335
|
+
itemKeys.value.splice(to, 0, movedItemKey);
|
|
336
|
+
remapArrayFieldState(form, props.path, (previousIndex) => {
|
|
337
|
+
if (previousIndex === from) return to;
|
|
338
|
+
if (from < to) {
|
|
339
|
+
if (previousIndex > from && previousIndex <= to) return previousIndex - 1;
|
|
340
|
+
return previousIndex;
|
|
341
|
+
}
|
|
342
|
+
if (previousIndex >= to && previousIndex < from) return previousIndex + 1;
|
|
343
|
+
return previousIndex;
|
|
344
|
+
});
|
|
192
345
|
mutate((current) => {
|
|
193
|
-
const [
|
|
194
|
-
current.splice(to, 0,
|
|
346
|
+
const [movedItem] = current.splice(from, 1);
|
|
347
|
+
current.splice(to, 0, movedItem);
|
|
195
348
|
});
|
|
196
349
|
}
|
|
197
350
|
onMounted(async () => {
|
|
@@ -301,13 +454,13 @@ var not_field_default = /* @__PURE__ */ defineComponent({
|
|
|
301
454
|
function onInput() {
|
|
302
455
|
updateDirty();
|
|
303
456
|
if (!validateOn.value.onInput) return;
|
|
304
|
-
if (
|
|
457
|
+
if (form.validationMode === "eager" && !isValid.value) scheduleValidation();
|
|
305
458
|
}
|
|
306
459
|
/** Handles the change event for the field. */
|
|
307
460
|
function onChange() {
|
|
308
461
|
updateDirty();
|
|
309
462
|
if (!validateOn.value.onChange) return;
|
|
310
|
-
if (
|
|
463
|
+
if (form.validationMode === "eager" && !isValid.value) scheduleValidation();
|
|
311
464
|
}
|
|
312
465
|
/** Handles the focus event for the field. */
|
|
313
466
|
function onFocus() {
|
|
@@ -388,33 +541,6 @@ var not_message_default = /* @__PURE__ */ defineComponent({
|
|
|
388
541
|
}
|
|
389
542
|
});
|
|
390
543
|
//#endregion
|
|
391
|
-
//#region src/utils/form-utils.ts
|
|
392
|
-
/**
|
|
393
|
-
* Normalizes a validation path segment into a standard property key.
|
|
394
|
-
* @param segment The path segment to normalize.
|
|
395
|
-
* @returns The normalized key.
|
|
396
|
-
*/
|
|
397
|
-
function normalizeSegment(segment) {
|
|
398
|
-
if (typeof segment === "object" && segment !== null && "key" in segment) return segment.key;
|
|
399
|
-
return segment;
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Checks if a validation issue path matches a target field path.
|
|
403
|
-
* @param issuePath The path array from the validation issue.
|
|
404
|
-
* @param targetPath The normalized path to compare against.
|
|
405
|
-
* @returns True if the paths are equivalent.
|
|
406
|
-
*/
|
|
407
|
-
function isIssuePathEqual(issuePath, targetPath) {
|
|
408
|
-
if (!issuePath) return false;
|
|
409
|
-
if (issuePath.length !== targetPath.length) return false;
|
|
410
|
-
return issuePath.every((segment, index) => {
|
|
411
|
-
const normalizedSegment = normalizeSegment(segment);
|
|
412
|
-
const targetSegment = targetPath[index];
|
|
413
|
-
if (typeof normalizedSegment === "number" || typeof targetSegment === "number") return Number(normalizedSegment) === Number(targetSegment);
|
|
414
|
-
return normalizedSegment === targetSegment;
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
//#endregion
|
|
418
544
|
//#region src/composables/use-not-form.ts
|
|
419
545
|
/**
|
|
420
546
|
* Creates a reactive NotFormInstance for managing form state and validation.
|
|
@@ -432,10 +558,7 @@ function useNotForm(config) {
|
|
|
432
558
|
onInput: config.validateOn?.onInput ?? true,
|
|
433
559
|
onMount: config.validateOn?.onMount ?? false
|
|
434
560
|
};
|
|
435
|
-
const validationMode =
|
|
436
|
-
eager: config.validationMode?.eager ?? true,
|
|
437
|
-
lazy: config.validationMode?.lazy ?? false
|
|
438
|
-
};
|
|
561
|
+
const validationMode = config.validationMode || "eager";
|
|
439
562
|
const values = reactive(klona(initialValues));
|
|
440
563
|
const errors = reactive([...initialErrors]);
|
|
441
564
|
const touchedFields = reactive(/* @__PURE__ */ new Set());
|
|
@@ -590,7 +713,7 @@ function useNotForm(config) {
|
|
|
590
713
|
const pathSegments = parsePath(path);
|
|
591
714
|
const staleIndices = [];
|
|
592
715
|
for (const [index, error] of errors.entries()) if (isIssuePathEqual(error.path, pathSegments)) staleIndices.push(index);
|
|
593
|
-
for (let
|
|
716
|
+
for (let index = staleIndices.length - 1; index >= 0; index--) errors.splice(staleIndices[index], 1);
|
|
594
717
|
if (result?.issues) {
|
|
595
718
|
const fieldIssues = result.issues.filter((issue) => isIssuePathEqual(issue.path, pathSegments));
|
|
596
719
|
if (fieldIssues.length > 0) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notform",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Vue Forms Without the Friction",
|
|
7
7
|
"author": "Favour Emeka <favorodera@gmail.com>",
|
|
@@ -49,18 +49,18 @@
|
|
|
49
49
|
"klona": "^2.0.6"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@favorodera/eslint-config": "^0.
|
|
53
|
-
"@types/node": "^
|
|
54
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
55
|
-
"@vitest/ui": "^4.1.
|
|
56
|
-
"@vue/test-utils": "^2.4.
|
|
52
|
+
"@favorodera/eslint-config": "^1.0.3",
|
|
53
|
+
"@types/node": "^26.1.0",
|
|
54
|
+
"@vitejs/plugin-vue": "^6.0.7",
|
|
55
|
+
"@vitest/ui": "^4.1.9",
|
|
56
|
+
"@vue/test-utils": "^2.4.11",
|
|
57
57
|
"happy-dom": "^20.10.6",
|
|
58
|
-
"tsdown": "^0.22.
|
|
59
|
-
"type-fest": "^5.
|
|
60
|
-
"vite": "^8.
|
|
61
|
-
"vitest": "^4.1.
|
|
62
|
-
"vue": "^3.5.
|
|
63
|
-
"vue-tsc": "^3.3.
|
|
58
|
+
"tsdown": "^0.22.3",
|
|
59
|
+
"type-fest": "^5.7.0",
|
|
60
|
+
"vite": "^8.1.3",
|
|
61
|
+
"vitest": "^4.1.9",
|
|
62
|
+
"vue": "^3.5.39",
|
|
63
|
+
"vue-tsc": "^3.3.6"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
66
66
|
"build": "tsdown",
|