@teamnovu/kit-vue-forms 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PLAN.md +3 -1
- package/dist/composables/useFormState.d.ts +2 -2
- package/dist/composables/useValidation.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +73 -76
- package/dist/types/form.d.ts +4 -4
- package/dist/utils/path.d.ts +1 -1
- package/package.json +10 -10
- package/src/composables/useField.ts +4 -4
- package/src/composables/useFieldRegistry.ts +1 -1
- package/src/composables/useForm.ts +5 -5
- package/src/composables/useFormData.ts +4 -4
- package/src/composables/useFormState.ts +2 -3
- package/src/composables/useSubform.ts +4 -4
- package/src/composables/useValidation.ts +5 -8
- package/src/index.ts +1 -1
- package/src/types/form.ts +4 -4
- package/src/utils/path.ts +3 -3
- package/tests/formState.test.ts +36 -65
- package/tests/integration.test.ts +7 -7
- package/tests/nestedPath.test.ts +70 -70
- package/tests/subform.test.ts +64 -64
- package/tests/useField.test.ts +10 -10
- package/tests/useForm.test.ts +3 -3
- package/tests/useValidation.test.ts +9 -9
package/PLAN.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# DEPRECATED
|
|
2
|
+
|
|
1
3
|
# Vue Forms Library - MVP Plan
|
|
2
4
|
|
|
3
5
|
## Overview
|
|
@@ -206,4 +208,4 @@ src/
|
|
|
206
208
|
- Direct form instance passing maintains type safety
|
|
207
209
|
- Subform extraction enables component reusability
|
|
208
210
|
- Validation strategies can be overridden per field
|
|
209
|
-
- Backend validation seamlessly integrates with Zod
|
|
211
|
+
- Backend validation seamlessly integrates with Zod
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { FormDataDefault
|
|
1
|
+
import { FormDataDefault } from '../types/form';
|
|
2
2
|
import { FieldRegistry } from './useFieldRegistry';
|
|
3
3
|
import { ComputedRef } from 'vue';
|
|
4
|
-
export declare function useFormState<T extends FormDataDefault>(
|
|
4
|
+
export declare function useFormState<T extends FormDataDefault>(formFieldRegistry: FieldRegistry<T>): {
|
|
5
5
|
isDirty: ComputedRef<boolean>;
|
|
6
6
|
isTouched: ComputedRef<boolean>;
|
|
7
7
|
};
|
|
@@ -7,12 +7,12 @@ export interface ValidatorOptions<T> {
|
|
|
7
7
|
validateFn?: MaybeRef<ValidationFunction<T>>;
|
|
8
8
|
}
|
|
9
9
|
export interface ValidationOptions<T> extends ValidatorOptions<T> {
|
|
10
|
-
errors?: MaybeRef<ErrorBag>;
|
|
10
|
+
errors?: MaybeRef<ErrorBag | undefined>;
|
|
11
11
|
}
|
|
12
12
|
export declare const SuccessValidationResult: ValidationResult;
|
|
13
13
|
export declare function createValidator<T extends FormDataDefault>(options: ValidatorOptions<T>): Ref<Validator<T> | undefined>;
|
|
14
14
|
export declare function useValidation<T extends FormDataDefault>(formState: {
|
|
15
|
-
|
|
15
|
+
data: T;
|
|
16
16
|
}, options: ValidationOptions<T>): {
|
|
17
17
|
validateForm: () => Promise<ValidationResult>;
|
|
18
18
|
defineValidator: (options: ValidatorOptions<T> | Ref<Validator<T>>) => Ref<Validator<T> | undefined, Validator<T> | undefined>;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export type { UseFormOptions } from './composables/useForm';
|
|
|
3
3
|
export { useField } from './composables/useField';
|
|
4
4
|
export type { UseFieldOptions } from './composables/useField';
|
|
5
5
|
export type { ValidationStrategy, ValidationErrorMessage as ErrorMessage, ValidationResult, ErrorBag } from './types/validation';
|
|
6
|
-
export type { DeepPartial
|
|
6
|
+
export type { DeepPartial } from './utils/type-helpers';
|
package/dist/index.mjs
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import { toValue as
|
|
1
|
+
var W = Object.defineProperty;
|
|
2
|
+
var $ = (t, r, e) => r in t ? W(t, r, { enumerable: !0, configurable: !0, writable: !0, value: e }) : t[r] = e;
|
|
3
|
+
var R = (t, r, e) => $(t, typeof r != "symbol" ? r + "" : r, e);
|
|
4
|
+
import { toValue as I, toRaw as J, computed as d, unref as c, reactive as j, watch as E, toRefs as P, toRef as D, ref as A, isRef as N, getCurrentScope as T, onBeforeUnmount as B } from "vue";
|
|
5
5
|
import "zod";
|
|
6
6
|
function y(t) {
|
|
7
|
-
const r =
|
|
7
|
+
const r = I(t), e = J(r);
|
|
8
8
|
return structuredClone(e);
|
|
9
9
|
}
|
|
10
|
-
function
|
|
10
|
+
function M(t) {
|
|
11
11
|
return t === "" ? [] : t.split(/\s*\.\s*/).filter(Boolean);
|
|
12
12
|
}
|
|
13
13
|
function m(t, r) {
|
|
14
|
-
return (Array.isArray(r) ? r :
|
|
14
|
+
return (Array.isArray(r) ? r : M(r)).reduce(
|
|
15
15
|
(a, s) => a == null ? void 0 : a[s],
|
|
16
16
|
t
|
|
17
17
|
);
|
|
18
18
|
}
|
|
19
19
|
function G(t, r, e) {
|
|
20
|
-
const a = Array.isArray(r) ? r :
|
|
20
|
+
const a = Array.isArray(r) ? r : M(r);
|
|
21
21
|
if (a.length === 0)
|
|
22
22
|
throw new Error("Path cannot be empty");
|
|
23
23
|
const s = a.at(-1), n = a.slice(0, -1).reduce(
|
|
24
|
-
(
|
|
24
|
+
(u, v) => u[v],
|
|
25
25
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
26
|
t
|
|
27
27
|
);
|
|
28
28
|
n[s] = e;
|
|
29
29
|
}
|
|
30
|
-
const
|
|
30
|
+
const x = (t, r) => d({
|
|
31
31
|
get() {
|
|
32
|
-
return m(
|
|
32
|
+
return m(c(t), c(r));
|
|
33
33
|
},
|
|
34
34
|
set(e) {
|
|
35
|
-
G(
|
|
35
|
+
G(c(t), c(r), e);
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
38
|
function F(t, r) {
|
|
@@ -53,14 +53,14 @@ function L(t, r) {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
function U(t) {
|
|
56
|
-
const r =
|
|
56
|
+
const r = j({
|
|
57
57
|
value: t.value,
|
|
58
58
|
path: t.path,
|
|
59
59
|
initialValue: d(() => Object.freeze(y(t.initialValue))),
|
|
60
|
-
errors:
|
|
60
|
+
errors: c(t.errors) || [],
|
|
61
61
|
touched: !1
|
|
62
62
|
});
|
|
63
|
-
E(() =>
|
|
63
|
+
E(() => c(t.errors), (f) => {
|
|
64
64
|
r.errors = f || [];
|
|
65
65
|
});
|
|
66
66
|
const e = d(() => JSON.stringify(r.value) !== JSON.stringify(r.initialValue)), a = (f) => {
|
|
@@ -68,7 +68,7 @@ function U(t) {
|
|
|
68
68
|
}, s = () => {
|
|
69
69
|
r.touched = !0;
|
|
70
70
|
}, n = () => {
|
|
71
|
-
},
|
|
71
|
+
}, u = () => {
|
|
72
72
|
r.value = y(r.initialValue), r.touched = !1, r.errors = [];
|
|
73
73
|
}, v = (f) => {
|
|
74
74
|
r.errors = f;
|
|
@@ -76,45 +76,45 @@ function U(t) {
|
|
|
76
76
|
r.errors = [];
|
|
77
77
|
}, l = P(r);
|
|
78
78
|
return {
|
|
79
|
-
|
|
79
|
+
data: l.value,
|
|
80
80
|
path: l.path,
|
|
81
81
|
initialValue: l.initialValue,
|
|
82
82
|
errors: l.errors,
|
|
83
83
|
touched: l.touched,
|
|
84
84
|
dirty: e,
|
|
85
|
-
|
|
85
|
+
setData: a,
|
|
86
86
|
onBlur: s,
|
|
87
87
|
onFocus: n,
|
|
88
|
-
reset:
|
|
88
|
+
reset: u,
|
|
89
89
|
setErrors: v,
|
|
90
90
|
clearErrors: o
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
93
|
function Z(t) {
|
|
94
|
-
const r = {}, e = (
|
|
95
|
-
const v = u
|
|
96
|
-
r[v] =
|
|
94
|
+
const r = {}, e = (u) => {
|
|
95
|
+
const v = c(u.path);
|
|
96
|
+
r[v] = u;
|
|
97
97
|
};
|
|
98
98
|
return {
|
|
99
99
|
fields: r,
|
|
100
|
-
getField: (
|
|
100
|
+
getField: (u) => r[u],
|
|
101
101
|
getFields: () => Object.values(r),
|
|
102
102
|
registerField: e,
|
|
103
|
-
defineField: (
|
|
103
|
+
defineField: (u) => {
|
|
104
104
|
const v = U({
|
|
105
|
-
...
|
|
106
|
-
value:
|
|
107
|
-
initialValue: d(() => m(t.initialData, u
|
|
105
|
+
...u,
|
|
106
|
+
value: x(D(t, "data"), u.path),
|
|
107
|
+
initialValue: d(() => m(t.initialData, c(u.path)))
|
|
108
108
|
});
|
|
109
109
|
return e(v), v;
|
|
110
110
|
}
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
|
-
function _(t
|
|
114
|
-
const
|
|
113
|
+
function _(t) {
|
|
114
|
+
const r = d(() => t.getFields().some((a) => c(a.dirty))), e = d(() => t.getFields().some((a) => c(a.touched)));
|
|
115
115
|
return {
|
|
116
|
-
isDirty:
|
|
117
|
-
isTouched:
|
|
116
|
+
isDirty: r,
|
|
117
|
+
isTouched: e
|
|
118
118
|
};
|
|
119
119
|
}
|
|
120
120
|
function k(t) {
|
|
@@ -122,7 +122,7 @@ function k(t) {
|
|
|
122
122
|
(r, e, a) => a.indexOf(r) === e
|
|
123
123
|
);
|
|
124
124
|
}
|
|
125
|
-
function
|
|
125
|
+
function z(...t) {
|
|
126
126
|
return t.slice(1).reduce((r, e) => {
|
|
127
127
|
if (!r && !e)
|
|
128
128
|
return;
|
|
@@ -140,7 +140,7 @@ function q(...t) {
|
|
|
140
140
|
const s = t.map((n) => n[a]).filter(Boolean);
|
|
141
141
|
return {
|
|
142
142
|
...e,
|
|
143
|
-
[a]:
|
|
143
|
+
[a]: z(...s)
|
|
144
144
|
};
|
|
145
145
|
}, {});
|
|
146
146
|
}
|
|
@@ -153,13 +153,13 @@ function S(...t) {
|
|
|
153
153
|
const r = t[0];
|
|
154
154
|
return t.length === 1 ? r : t.slice(1).reduce(
|
|
155
155
|
(e, a) => ({
|
|
156
|
-
general:
|
|
156
|
+
general: z(e.general, a.general),
|
|
157
157
|
propertyErrors: q(e.propertyErrors ?? {}, a.propertyErrors ?? {})
|
|
158
158
|
}),
|
|
159
159
|
r
|
|
160
160
|
);
|
|
161
161
|
}
|
|
162
|
-
function
|
|
162
|
+
function O(t) {
|
|
163
163
|
var a;
|
|
164
164
|
const r = (((a = t.general) == null ? void 0 : a.length) ?? 0) > 0, e = Object.entries(t.propertyErrors).filter(([, s]) => s == null ? void 0 : s.length).length > 0;
|
|
165
165
|
return r || e;
|
|
@@ -227,8 +227,8 @@ class X {
|
|
|
227
227
|
}
|
|
228
228
|
class Y {
|
|
229
229
|
constructor(r, e) {
|
|
230
|
-
|
|
231
|
-
|
|
230
|
+
R(this, "schemaValidator");
|
|
231
|
+
R(this, "functionValidator");
|
|
232
232
|
this.schema = r, this.validateFn = e, this.schemaValidator = new Q(this.schema), this.functionValidator = new X(this.validateFn);
|
|
233
233
|
}
|
|
234
234
|
async validate(r) {
|
|
@@ -242,22 +242,19 @@ class Y {
|
|
|
242
242
|
};
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
|
-
function
|
|
245
|
+
function b(t) {
|
|
246
246
|
return d(() => new Y(
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
c(t.schema),
|
|
248
|
+
c(t.validateFn)
|
|
249
249
|
));
|
|
250
250
|
}
|
|
251
251
|
function rr(t, r) {
|
|
252
|
-
const e =
|
|
253
|
-
validators:
|
|
252
|
+
const e = j({
|
|
253
|
+
validators: A([b(r)]),
|
|
254
254
|
isValidated: !1,
|
|
255
|
-
errors:
|
|
256
|
-
general: [],
|
|
257
|
-
propertyErrors: {}
|
|
258
|
-
}
|
|
255
|
+
errors: c(r.errors) ?? p.errors
|
|
259
256
|
});
|
|
260
|
-
E(() =>
|
|
257
|
+
E(() => c(r.errors), async () => {
|
|
261
258
|
const o = await s();
|
|
262
259
|
n(o.errors);
|
|
263
260
|
}, { immediate: !0 }), E(
|
|
@@ -271,11 +268,11 @@ function rr(t, r) {
|
|
|
271
268
|
e.errors = p.errors;
|
|
272
269
|
},
|
|
273
270
|
{ immediate: !0 }
|
|
274
|
-
), E(() => t.
|
|
275
|
-
e.isValidated &&
|
|
271
|
+
), E(() => t.data, () => {
|
|
272
|
+
e.isValidated && u();
|
|
276
273
|
});
|
|
277
274
|
const a = (o) => {
|
|
278
|
-
const l =
|
|
275
|
+
const l = N(o) ? o : b(o);
|
|
279
276
|
return e.validators.push(l), T() && B(() => {
|
|
280
277
|
e.validators = e.validators.filter(
|
|
281
278
|
(f) => f !== l
|
|
@@ -284,11 +281,11 @@ function rr(t, r) {
|
|
|
284
281
|
};
|
|
285
282
|
async function s() {
|
|
286
283
|
const o = await Promise.all(
|
|
287
|
-
e.validators.filter((V) =>
|
|
284
|
+
e.validators.filter((V) => c(V) !== void 0).map((V) => c(V).validate(t.data))
|
|
288
285
|
), l = o.every((V) => V.isValid);
|
|
289
286
|
let { errors: f } = p;
|
|
290
287
|
if (!l) {
|
|
291
|
-
const V = o.map((
|
|
288
|
+
const V = o.map((w) => w.errors);
|
|
292
289
|
f = S(...V);
|
|
293
290
|
}
|
|
294
291
|
return {
|
|
@@ -297,17 +294,17 @@ function rr(t, r) {
|
|
|
297
294
|
};
|
|
298
295
|
}
|
|
299
296
|
const n = (o) => {
|
|
300
|
-
e.errors = S(
|
|
301
|
-
},
|
|
297
|
+
e.errors = S(c(r.errors) ?? p.errors, o);
|
|
298
|
+
}, u = async () => {
|
|
302
299
|
const o = await s();
|
|
303
300
|
return n(o.errors), e.isValidated = !0, {
|
|
304
|
-
isValid: !
|
|
301
|
+
isValid: !O(o.errors),
|
|
305
302
|
errors: e.errors
|
|
306
303
|
};
|
|
307
|
-
}, v = d(() => !
|
|
304
|
+
}, v = d(() => !O(e.errors));
|
|
308
305
|
return {
|
|
309
306
|
...P(e),
|
|
310
|
-
validateForm:
|
|
307
|
+
validateForm: u,
|
|
311
308
|
defineValidator: a,
|
|
312
309
|
isValid: v
|
|
313
310
|
};
|
|
@@ -336,13 +333,13 @@ class tr {
|
|
|
336
333
|
}
|
|
337
334
|
}
|
|
338
335
|
function er(t, r, e) {
|
|
339
|
-
const a =
|
|
336
|
+
const a = x(t.data, r), s = d(() => m(t.initialData.value, r)), n = (i) => ({
|
|
340
337
|
...i,
|
|
341
|
-
path: d(() =>
|
|
342
|
-
|
|
343
|
-
i.
|
|
338
|
+
path: d(() => c(i.path).replace(r + ".", "")),
|
|
339
|
+
setData: (h) => {
|
|
340
|
+
i.setData(h);
|
|
344
341
|
}
|
|
345
|
-
}),
|
|
342
|
+
}), u = (i) => {
|
|
346
343
|
const h = F(r, i), g = t.getField(h);
|
|
347
344
|
if (g)
|
|
348
345
|
return n(g);
|
|
@@ -358,21 +355,21 @@ function er(t, r, e) {
|
|
|
358
355
|
}).map((i) => n(i)), l = () => t.getFields().filter((i) => {
|
|
359
356
|
const h = i.path.value;
|
|
360
357
|
return h.startsWith(r + ".") || h === r;
|
|
361
|
-
}), f = d(() => l().some((i) => i.dirty.value)), V = d(() => l().some((i) => i.touched.value)),
|
|
358
|
+
}), f = d(() => l().some((i) => i.dirty.value)), V = d(() => l().some((i) => i.touched.value)), w = d(() => t.isValid.value), C = d(() => t.isValidated.value), K = d(() => L(c(t.errors), r));
|
|
362
359
|
return {
|
|
363
|
-
|
|
360
|
+
data: a,
|
|
364
361
|
initialData: s,
|
|
365
362
|
defineField: v,
|
|
366
|
-
getField:
|
|
363
|
+
getField: u,
|
|
367
364
|
getFields: o,
|
|
368
365
|
isDirty: f,
|
|
369
366
|
isTouched: V,
|
|
370
|
-
isValid:
|
|
371
|
-
isValidated:
|
|
372
|
-
errors:
|
|
367
|
+
isValid: w,
|
|
368
|
+
isValidated: C,
|
|
369
|
+
errors: K,
|
|
373
370
|
defineValidator: (i) => {
|
|
374
|
-
const h =
|
|
375
|
-
() => new tr(r,
|
|
371
|
+
const h = N(i) ? i : b(i), g = d(
|
|
372
|
+
() => new tr(r, c(h))
|
|
376
373
|
);
|
|
377
374
|
return t.defineValidator(g), h;
|
|
378
375
|
},
|
|
@@ -388,10 +385,10 @@ function er(t, r, e) {
|
|
|
388
385
|
};
|
|
389
386
|
}
|
|
390
387
|
function ur(t) {
|
|
391
|
-
const r = d(() => Object.freeze(y(t.initialData))), e =
|
|
388
|
+
const r = d(() => Object.freeze(y(t.initialData))), e = A(y(r)), a = j({
|
|
392
389
|
initialData: r,
|
|
393
|
-
|
|
394
|
-
}), s = Z(a), n = rr(a, t),
|
|
390
|
+
data: e
|
|
391
|
+
}), s = Z(a), n = rr(a, t), u = _(s), v = () => {
|
|
395
392
|
e.value = y(r), s.getFields().forEach((f) => f.reset());
|
|
396
393
|
};
|
|
397
394
|
function o(f, V) {
|
|
@@ -400,11 +397,11 @@ function ur(t) {
|
|
|
400
397
|
const l = {
|
|
401
398
|
...s,
|
|
402
399
|
...n,
|
|
403
|
-
...
|
|
400
|
+
...u,
|
|
404
401
|
reset: v,
|
|
405
402
|
getSubForm: o,
|
|
406
|
-
initialData:
|
|
407
|
-
|
|
403
|
+
initialData: D(a, "initialData"),
|
|
404
|
+
data: D(a, "data")
|
|
408
405
|
};
|
|
409
406
|
return l;
|
|
410
407
|
}
|
package/dist/types/form.d.ts
CHANGED
|
@@ -6,17 +6,17 @@ import { ErrorBag, ValidationErrorMessage, ValidationErrors, ValidationResult, V
|
|
|
6
6
|
import { ValidatorOptions } from '../composables/useValidation';
|
|
7
7
|
export type FormDataDefault = object;
|
|
8
8
|
export interface FormState<T extends FormDataDefault, TIn extends FormDataDefault = T> {
|
|
9
|
-
|
|
9
|
+
data: T;
|
|
10
10
|
initialData: TIn;
|
|
11
11
|
}
|
|
12
12
|
export interface FormField<T, P extends string> {
|
|
13
|
-
|
|
13
|
+
data: Ref<T>;
|
|
14
14
|
path: Ref<P>;
|
|
15
15
|
initialValue: Readonly<Ref<T>>;
|
|
16
16
|
errors: Ref<ValidationErrors>;
|
|
17
17
|
touched: Ref<boolean>;
|
|
18
18
|
dirty: Ref<boolean>;
|
|
19
|
-
|
|
19
|
+
setData: (newData: T) => void;
|
|
20
20
|
onBlur: () => void;
|
|
21
21
|
onFocus: () => void;
|
|
22
22
|
reset: () => void;
|
|
@@ -24,7 +24,7 @@ export interface FormField<T, P extends string> {
|
|
|
24
24
|
clearErrors: () => void;
|
|
25
25
|
}
|
|
26
26
|
export interface Form<T extends FormDataDefault> {
|
|
27
|
-
|
|
27
|
+
data: Ref<T>;
|
|
28
28
|
initialData: Readonly<Ref<T>>;
|
|
29
29
|
defineField: <P extends Paths<T>>(options: DefineFieldOptions<PickProps<T, P>, P>) => FormField<PickProps<T, P>, P>;
|
|
30
30
|
getField: <P extends Paths<T>>(path: P) => FormField<PickProps<T, P>, P> | undefined;
|
package/dist/utils/path.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { ErrorBag } from '../types/validation';
|
|
|
4
4
|
export declare function splitPath(path: string): string[];
|
|
5
5
|
export declare function getNestedValue<T, K extends Paths<T>>(obj: T, path: K | SplitPath<K>): PickProps<T, K>;
|
|
6
6
|
export declare function setNestedValue<T, K extends Paths<T>>(obj: T, path: K | SplitPath<K>, value: PickProps<T, K>): void;
|
|
7
|
-
export declare const getLens: <T, K extends Paths<T>>(
|
|
7
|
+
export declare const getLens: <T, K extends Paths<T>>(data: MaybeRef<T>, key: MaybeRef<K | SplitPath<K>>) => WritableComputedRef<PickProps<T, K>, PickProps<T, K>>;
|
|
8
8
|
type JoinPath<Base extends string, Sub extends string> = `${Base}${Base extends '' ? '' : Sub extends '' ? '' : '.'}${Sub}`;
|
|
9
9
|
export declare function joinPath<Base extends string, Sub extends string>(basePath: Base, subPath: Sub): JoinPath<Base, Sub>;
|
|
10
10
|
export declare function filterErrorsForPath(errors: ErrorBag, path: string): ErrorBag;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamnovu/kit-vue-forms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
@@ -9,14 +9,6 @@
|
|
|
9
9
|
"import": "./dist/index.mjs"
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
-
"scripts": {
|
|
13
|
-
"build": "vite build",
|
|
14
|
-
"watch": "NODE_ENV=development vite build --watch",
|
|
15
|
-
"lint": "eslint --fix --ignore-pattern 'dist/**' .",
|
|
16
|
-
"test": "vitest run",
|
|
17
|
-
"test:watch": "vitest",
|
|
18
|
-
"test:ui": "vitest --ui"
|
|
19
|
-
},
|
|
20
12
|
"keywords": [
|
|
21
13
|
"vue",
|
|
22
14
|
"forms",
|
|
@@ -37,5 +29,13 @@
|
|
|
37
29
|
"dependencies": {
|
|
38
30
|
"@vueuse/core": "^13.5.0",
|
|
39
31
|
"zod": "^4"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "vite build",
|
|
35
|
+
"watch": "NODE_ENV=development vite build --watch",
|
|
36
|
+
"lint": "eslint --fix --ignore-pattern 'dist/**' .",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"test:ui": "vitest --ui"
|
|
40
40
|
}
|
|
41
|
-
}
|
|
41
|
+
}
|
|
@@ -29,8 +29,8 @@ export function useField<T, K extends string>(options: UseFieldOptions<T, K>): F
|
|
|
29
29
|
return JSON.stringify(state.value) !== JSON.stringify(state.initialValue)
|
|
30
30
|
})
|
|
31
31
|
|
|
32
|
-
const
|
|
33
|
-
state.value =
|
|
32
|
+
const setData = (newData: T): void => {
|
|
33
|
+
state.value = newData
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const onBlur = (): void => {
|
|
@@ -58,13 +58,13 @@ export function useField<T, K extends string>(options: UseFieldOptions<T, K>): F
|
|
|
58
58
|
const refs = toRefs(state)
|
|
59
59
|
|
|
60
60
|
return {
|
|
61
|
-
|
|
61
|
+
data: refs.value as FormField<T, K>['data'],
|
|
62
62
|
path: refs.path as FormField<T, K>['path'],
|
|
63
63
|
initialValue: refs.initialValue as FormField<T, K>['initialValue'],
|
|
64
64
|
errors: refs.errors as FormField<T, K>['errors'],
|
|
65
65
|
touched: refs.touched as FormField<T, K>['touched'],
|
|
66
66
|
dirty,
|
|
67
|
-
|
|
67
|
+
setData,
|
|
68
68
|
onBlur,
|
|
69
69
|
onFocus,
|
|
70
70
|
reset,
|
|
@@ -32,7 +32,7 @@ export function useFieldRegistry<T extends FormDataDefault>(
|
|
|
32
32
|
const defineField = <K extends Paths<T>>(options: DefineFieldOptions<PickProps<T, K>, K>) => {
|
|
33
33
|
const field = useField({
|
|
34
34
|
...options,
|
|
35
|
-
value: getLens(toRef(formState, '
|
|
35
|
+
value: getLens(toRef(formState, 'data'), options.path),
|
|
36
36
|
initialValue: computed(() => getNestedValue(formState.initialData, unref(options.path))),
|
|
37
37
|
})
|
|
38
38
|
|
|
@@ -17,19 +17,19 @@ export interface UseFormOptions<T extends FormDataDefault> extends ValidationOpt
|
|
|
17
17
|
export function useForm<T extends FormDataDefault>(options: UseFormOptions<T>) {
|
|
18
18
|
const initialData = computed(() => Object.freeze(cloneRefValue(options.initialData)))
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const data = ref<T>(cloneRefValue(initialData)) as Ref<T>
|
|
21
21
|
|
|
22
22
|
const state = reactive({
|
|
23
23
|
initialData: initialData,
|
|
24
|
-
|
|
24
|
+
data,
|
|
25
25
|
})
|
|
26
26
|
|
|
27
27
|
const fields = useFieldRegistry(state)
|
|
28
28
|
const validationState = useValidation(state, options)
|
|
29
|
-
const formState = useFormState(
|
|
29
|
+
const formState = useFormState(fields)
|
|
30
30
|
|
|
31
31
|
const reset = () => {
|
|
32
|
-
|
|
32
|
+
data.value = cloneRefValue(initialData)
|
|
33
33
|
fields.getFields().forEach(field => field.reset())
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -47,7 +47,7 @@ export function useForm<T extends FormDataDefault>(options: UseFormOptions<T>) {
|
|
|
47
47
|
reset,
|
|
48
48
|
getSubForm,
|
|
49
49
|
initialData: toRef(state, 'initialData') as Form<T>['initialData'],
|
|
50
|
-
|
|
50
|
+
data: toRef(state, 'data') as Form<T>['data'],
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
return formInterface
|
|
@@ -4,13 +4,13 @@ import type { FormDataDefault } from '../types/form'
|
|
|
4
4
|
export function useFormData<T extends FormDataDefault>(
|
|
5
5
|
initialData: Ref<T>,
|
|
6
6
|
) {
|
|
7
|
-
const
|
|
7
|
+
const data = ref(unref(initialData))
|
|
8
8
|
|
|
9
9
|
watch(initialData, (newData) => {
|
|
10
|
-
if (newData !==
|
|
11
|
-
|
|
10
|
+
if (newData !== data.value) {
|
|
11
|
+
data.value = newData
|
|
12
12
|
}
|
|
13
13
|
})
|
|
14
14
|
|
|
15
|
-
return {
|
|
15
|
+
return { data }
|
|
16
16
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { computed, unref } from 'vue'
|
|
2
|
-
import type { FormDataDefault
|
|
2
|
+
import type { FormDataDefault } from '../types/form'
|
|
3
3
|
import type { FieldRegistry } from './useFieldRegistry'
|
|
4
4
|
|
|
5
5
|
export function useFormState<T extends FormDataDefault>(
|
|
6
|
-
formState: FormState<T>,
|
|
7
6
|
formFieldRegistry: FieldRegistry<T>,
|
|
8
7
|
) {
|
|
9
8
|
const isDirty = computed(() => {
|
|
10
|
-
return
|
|
9
|
+
return formFieldRegistry.getFields().some(field => unref(field.dirty))
|
|
11
10
|
})
|
|
12
11
|
|
|
13
12
|
const isTouched = computed(() => {
|
|
@@ -57,7 +57,7 @@ export function createSubformInterface<
|
|
|
57
57
|
type ScopedMainPaths = Paths<T> & MP<SP>
|
|
58
58
|
|
|
59
59
|
// Create reactive data scoped to subform path
|
|
60
|
-
const
|
|
60
|
+
const data = getLens(mainForm.data, path) as Ref<ST>
|
|
61
61
|
|
|
62
62
|
const initialData = computed(() => {
|
|
63
63
|
return getNestedValue(mainForm.initialData.value, path) as ST
|
|
@@ -70,8 +70,8 @@ export function createSubformInterface<
|
|
|
70
70
|
return {
|
|
71
71
|
...field,
|
|
72
72
|
path: computed(() => unref(field.path).replace(path + '.', '')),
|
|
73
|
-
|
|
74
|
-
field.
|
|
73
|
+
setData: (newData: PickProps<ST, S>) => {
|
|
74
|
+
field.setData(newData as PickProps<T, ScopedMainPaths>)
|
|
75
75
|
},
|
|
76
76
|
} as unknown as FormField<PickProps<ST, S>, S>
|
|
77
77
|
}
|
|
@@ -155,7 +155,7 @@ export function createSubformInterface<
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
return {
|
|
158
|
-
|
|
158
|
+
data: data,
|
|
159
159
|
initialData,
|
|
160
160
|
defineField,
|
|
161
161
|
getField,
|
|
@@ -11,7 +11,7 @@ export interface ValidatorOptions<T> {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export interface ValidationOptions<T> extends ValidatorOptions<T> {
|
|
14
|
-
errors?: MaybeRef<ErrorBag>
|
|
14
|
+
errors?: MaybeRef<ErrorBag | undefined>
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export const SuccessValidationResult: ValidationResult = {
|
|
@@ -113,16 +113,13 @@ export function createValidator<T extends FormDataDefault>(
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
export function useValidation<T extends FormDataDefault>(
|
|
116
|
-
formState: {
|
|
116
|
+
formState: { data: T },
|
|
117
117
|
options: ValidationOptions<T>,
|
|
118
118
|
) {
|
|
119
119
|
const validationState = reactive({
|
|
120
120
|
validators: ref<Ref<Validator<T> | undefined>[]>([createValidator(options)]),
|
|
121
121
|
isValidated: false,
|
|
122
|
-
errors: unref(options.errors) ??
|
|
123
|
-
general: [],
|
|
124
|
-
propertyErrors: {},
|
|
125
|
-
},
|
|
122
|
+
errors: unref(options.errors) ?? SuccessValidationResult.errors,
|
|
126
123
|
})
|
|
127
124
|
|
|
128
125
|
// Watch for changes in the error bag and update validation state
|
|
@@ -152,7 +149,7 @@ export function useValidation<T extends FormDataDefault>(
|
|
|
152
149
|
)
|
|
153
150
|
|
|
154
151
|
// Watch for changes in form data to trigger validation
|
|
155
|
-
watch(() => formState.
|
|
152
|
+
watch(() => formState.data, () => {
|
|
156
153
|
if (validationState.isValidated) {
|
|
157
154
|
validateForm()
|
|
158
155
|
}
|
|
@@ -178,7 +175,7 @@ export function useValidation<T extends FormDataDefault>(
|
|
|
178
175
|
const validationResults = await Promise.all(
|
|
179
176
|
validationState.validators
|
|
180
177
|
.filter(validator => unref(validator) !== undefined)
|
|
181
|
-
.map(validator => unref(validator)!.validate(formState.
|
|
178
|
+
.map(validator => unref(validator)!.validate(formState.data)),
|
|
182
179
|
)
|
|
183
180
|
|
|
184
181
|
const isValid = validationResults.every(result => result.isValid)
|
package/src/index.ts
CHANGED
|
@@ -8,4 +8,4 @@ export type { UseFieldOptions } from './composables/useField'
|
|
|
8
8
|
|
|
9
9
|
// Types
|
|
10
10
|
export type { ValidationStrategy, ValidationErrorMessage as ErrorMessage, ValidationResult, ErrorBag } from './types/validation'
|
|
11
|
-
export type { DeepPartial
|
|
11
|
+
export type { DeepPartial } from './utils/type-helpers'
|