@teamnovu/kit-vue-forms 0.2.9 → 0.2.11
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/dist/composables/useField.d.ts +1 -0
- package/dist/composables/useFieldRegistry.d.ts +2 -0
- package/dist/index.js +300 -282
- package/dist/types/validation.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/FormFieldWrapper.vue +3 -1
- package/src/composables/useField.ts +5 -0
- package/src/composables/useFieldRegistry.ts +8 -0
- package/src/composables/useForm.ts +10 -4
- package/src/composables/useValidation.ts +4 -0
- package/src/types/validation.ts +1 -1
- package/tests/useValidation.test.ts +54 -0
|
@@ -10,5 +10,6 @@ export interface UseFieldOptions<T, K extends string> {
|
|
|
10
10
|
existsInForm?: MaybeRef<boolean>;
|
|
11
11
|
onBlur?: () => Awaitable<void>;
|
|
12
12
|
onFocus?: () => Awaitable<void>;
|
|
13
|
+
onChange?: (value: T) => Awaitable<void>;
|
|
13
14
|
}
|
|
14
15
|
export declare function useField<T, K extends string>(fieldOptions: UseFieldOptions<T, K>): FormField<T, K>;
|
|
@@ -8,6 +8,7 @@ export type ResolvedFormField<T, K extends Paths<T>> = FormField<PickProps<T, K>
|
|
|
8
8
|
export type DefineFieldOptions<F, K extends string> = Pick<UseFieldOptions<F, K>, 'path'> & {
|
|
9
9
|
onBlur?: () => void;
|
|
10
10
|
onFocus?: () => void;
|
|
11
|
+
onChange?: <T>(value: T) => void;
|
|
11
12
|
};
|
|
12
13
|
interface FormState<T extends FormDataDefault, TIn extends FormDataDefault = T> {
|
|
13
14
|
data: T;
|
|
@@ -17,6 +18,7 @@ interface FieldRegistryOptions {
|
|
|
17
18
|
keepValuesOnUnmount?: MaybeRef<boolean>;
|
|
18
19
|
onBlur?: (path: string) => Awaitable<void>;
|
|
19
20
|
onFocus?: (path: string) => Awaitable<void>;
|
|
21
|
+
onChange?: <T>(path: string, value: T) => Awaitable<void>;
|
|
20
22
|
}
|
|
21
23
|
export declare function useFieldRegistry<T extends FormDataDefault, TOut = T>(formState: FormState<T>, validationState: ValidationState<T, TOut>, fieldRegistryOptions?: FieldRegistryOptions): {
|
|
22
24
|
fields: ComputedRef<FieldsTuple<T>>;
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import { toValue as
|
|
5
|
-
import { cloneDeep as
|
|
1
|
+
var Y = Object.defineProperty;
|
|
2
|
+
var ee = (r, t, e) => t in r ? Y(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
|
|
3
|
+
var S = (r, t, e) => ee(r, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
import { toValue as te, toRaw as re, unref as h, shallowRef as $, watch as P, computed as p, isRef as T, reactive as k, toRefs as J, shallowReactive as ae, toRef as j, onScopeDispose as se, triggerRef as ne, ref as G, getCurrentScope as oe, onBeforeUnmount as ie, defineComponent as x, renderSlot as C, normalizeProps as N, guardReactiveProps as W, createBlock as K, openBlock as L, withCtx as U, resolveDynamicComponent as le, mergeProps as ce, createSlots as ue, renderList as de } from "vue";
|
|
5
|
+
import { cloneDeep as fe } from "lodash-es";
|
|
6
6
|
import "zod";
|
|
7
|
-
function
|
|
8
|
-
const t =
|
|
9
|
-
return
|
|
7
|
+
function E(r) {
|
|
8
|
+
const t = te(r), e = re(t);
|
|
9
|
+
return fe(e);
|
|
10
10
|
}
|
|
11
|
-
function
|
|
11
|
+
function he(r) {
|
|
12
12
|
return r.filter(
|
|
13
13
|
(t, e, a) => a.indexOf(t) === e
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
|
-
function
|
|
16
|
+
function Z(...r) {
|
|
17
17
|
return r.slice(1).reduce((t, e) => {
|
|
18
18
|
if (!t && !e)
|
|
19
19
|
return;
|
|
@@ -23,15 +23,15 @@ function J(...r) {
|
|
|
23
23
|
if (!a)
|
|
24
24
|
return t;
|
|
25
25
|
const s = (t ?? []).concat(e);
|
|
26
|
-
return
|
|
26
|
+
return he(s);
|
|
27
27
|
}, r[0]);
|
|
28
28
|
}
|
|
29
|
-
function
|
|
29
|
+
function ve(...r) {
|
|
30
30
|
return r.map((e) => Object.keys(e)).flat().reduce((e, a) => {
|
|
31
31
|
const s = r.map((n) => n[a]).filter(Boolean);
|
|
32
32
|
return {
|
|
33
33
|
...e,
|
|
34
|
-
[a]:
|
|
34
|
+
[a]: Z(...s)
|
|
35
35
|
};
|
|
36
36
|
}, {});
|
|
37
37
|
}
|
|
@@ -44,38 +44,38 @@ function B(...r) {
|
|
|
44
44
|
const t = r[0];
|
|
45
45
|
return r.length === 1 ? t : r.slice(1).reduce(
|
|
46
46
|
(e, a) => ({
|
|
47
|
-
general:
|
|
48
|
-
propertyErrors:
|
|
47
|
+
general: Z(e.general, a.general),
|
|
48
|
+
propertyErrors: ve(e.propertyErrors ?? {}, a.propertyErrors ?? {})
|
|
49
49
|
}),
|
|
50
50
|
t
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
|
-
function
|
|
53
|
+
function q(r) {
|
|
54
54
|
var a;
|
|
55
55
|
const t = (((a = r.general) == null ? void 0 : a.length) ?? 0) > 0, e = Object.entries(r.propertyErrors).filter(([, s]) => s == null ? void 0 : s.length).length > 0;
|
|
56
56
|
return t || e;
|
|
57
57
|
}
|
|
58
|
-
function
|
|
59
|
-
return !
|
|
58
|
+
function z(r) {
|
|
59
|
+
return !q(r.errors);
|
|
60
60
|
}
|
|
61
|
-
function
|
|
61
|
+
function O(r, t) {
|
|
62
62
|
return (e) => async (a) => {
|
|
63
63
|
if (a == null || a.preventDefault(), h(t.validationStrategy) === "none") {
|
|
64
64
|
await e(r.data.value);
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
67
|
const n = await r.validateForm();
|
|
68
|
-
if (!
|
|
68
|
+
if (!z(n))
|
|
69
69
|
return;
|
|
70
70
|
const u = n.data ?? r.data.value;
|
|
71
71
|
await e(u);
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
-
class
|
|
74
|
+
class me {
|
|
75
75
|
constructor(t = (e) => e) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
S(this, "weakMap", /* @__PURE__ */ new WeakMap());
|
|
77
|
+
S(this, "map", /* @__PURE__ */ new Map());
|
|
78
|
+
S(this, "hashFn");
|
|
79
79
|
this.hashFn = t;
|
|
80
80
|
}
|
|
81
81
|
isReferenceType(t) {
|
|
@@ -94,30 +94,30 @@ class pe {
|
|
|
94
94
|
this.isReferenceType(a) ? this.weakMap.set(a, e) : this.map.set(a, e);
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
-
function
|
|
97
|
+
function pe(r, t, e) {
|
|
98
98
|
const a = /* @__PURE__ */ new Set();
|
|
99
99
|
return t.map((s, n) => {
|
|
100
|
-
const u = [...r.get(s) ?? []],
|
|
101
|
-
if (
|
|
102
|
-
return a.add(
|
|
103
|
-
id:
|
|
100
|
+
const u = [...r.get(s) ?? []], m = u.findIndex((v) => !a.has(v)), l = (m === -1 ? [] : u.slice(m))[0];
|
|
101
|
+
if (l)
|
|
102
|
+
return a.add(l), {
|
|
103
|
+
id: l,
|
|
104
104
|
item: s,
|
|
105
105
|
path: `${e}.${n}`
|
|
106
106
|
};
|
|
107
|
-
const
|
|
108
|
-
return r.set(s, u.concat([
|
|
109
|
-
id:
|
|
107
|
+
const o = crypto.randomUUID();
|
|
108
|
+
return r.set(s, u.concat([o])), a.add(o), {
|
|
109
|
+
id: o,
|
|
110
110
|
item: s,
|
|
111
111
|
path: `${e}.${n}`
|
|
112
112
|
};
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
|
-
function
|
|
116
|
-
const a = new
|
|
117
|
-
return
|
|
115
|
+
function Q(r, t, e) {
|
|
116
|
+
const a = new me(e == null ? void 0 : e.hashFn), s = r.getField(t), n = $([]);
|
|
117
|
+
return P(
|
|
118
118
|
s.data,
|
|
119
|
-
(
|
|
120
|
-
n.value =
|
|
119
|
+
(l) => {
|
|
120
|
+
n.value = pe(a, l, t);
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
immediate: !0,
|
|
@@ -125,67 +125,67 @@ function q(r, t, e) {
|
|
|
125
125
|
}
|
|
126
126
|
), {
|
|
127
127
|
items: n,
|
|
128
|
-
push: (
|
|
129
|
-
const
|
|
130
|
-
return s.setData([...
|
|
128
|
+
push: (l) => {
|
|
129
|
+
const o = s.data.value ?? [];
|
|
130
|
+
return s.setData([...o, l]), n.value.at(-1);
|
|
131
131
|
},
|
|
132
|
-
remove: (
|
|
133
|
-
const
|
|
134
|
-
({ id:
|
|
132
|
+
remove: (l) => {
|
|
133
|
+
const o = s.data.value ?? [], v = n.value.findIndex(
|
|
134
|
+
({ id: i }) => i === l
|
|
135
135
|
);
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
v !== -1 && s.setData(
|
|
137
|
+
o.slice(0, v).concat(o.slice(v + 1))
|
|
138
138
|
);
|
|
139
139
|
},
|
|
140
|
-
insert: (
|
|
141
|
-
const
|
|
140
|
+
insert: (l, o) => {
|
|
141
|
+
const v = s.data.value ?? [];
|
|
142
142
|
return s.setData(
|
|
143
|
-
|
|
144
|
-
), n.value[
|
|
143
|
+
v.slice(0, o).concat([l]).concat(v.slice(o))
|
|
144
|
+
), n.value[o];
|
|
145
145
|
},
|
|
146
146
|
field: s
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
|
-
function
|
|
149
|
+
function H(r) {
|
|
150
150
|
return r === "" ? [] : r.split(/\s*\.\s*/).filter(Boolean);
|
|
151
151
|
}
|
|
152
|
-
function
|
|
153
|
-
const e = Array.isArray(t) ? t :
|
|
154
|
-
return !!
|
|
152
|
+
function Fe(r, t) {
|
|
153
|
+
const e = Array.isArray(t) ? t : H(t);
|
|
154
|
+
return !!b(r, e.slice(0, -1));
|
|
155
155
|
}
|
|
156
|
-
function
|
|
157
|
-
return (Array.isArray(t) ? t :
|
|
156
|
+
function b(r, t) {
|
|
157
|
+
return (Array.isArray(t) ? t : H(t)).reduce(
|
|
158
158
|
(a, s) => a == null ? void 0 : a[s],
|
|
159
159
|
r
|
|
160
160
|
);
|
|
161
161
|
}
|
|
162
162
|
function ye(r, t, e) {
|
|
163
|
-
const a = Array.isArray(t) ? t :
|
|
163
|
+
const a = Array.isArray(t) ? t : H(t), s = a.at(-1);
|
|
164
164
|
if (s) {
|
|
165
165
|
const n = a.slice(0, -1).reduce(
|
|
166
|
-
(u,
|
|
166
|
+
(u, m) => ((u == null ? void 0 : u[m]) === void 0 && (u[m] = {}), u == null ? void 0 : u[m]),
|
|
167
167
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
168
168
|
h(r)
|
|
169
169
|
);
|
|
170
170
|
n[s] = e;
|
|
171
171
|
} else {
|
|
172
|
-
if (!
|
|
172
|
+
if (!T(r))
|
|
173
173
|
return;
|
|
174
174
|
r.value = e;
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
-
const
|
|
177
|
+
const X = (r, t) => p({
|
|
178
178
|
get() {
|
|
179
|
-
return
|
|
179
|
+
return b(h(r), h(t));
|
|
180
180
|
},
|
|
181
181
|
set(e) {
|
|
182
182
|
ye(r, h(t), e);
|
|
183
183
|
}
|
|
184
184
|
});
|
|
185
|
-
function
|
|
185
|
+
function I(r, t) {
|
|
186
186
|
return !r && !t ? "" : !r && t ? t : !t && r ? r : `${r}.${t}`;
|
|
187
187
|
}
|
|
188
|
-
function
|
|
188
|
+
function ge(r, t) {
|
|
189
189
|
if (!t)
|
|
190
190
|
return r;
|
|
191
191
|
const e = `${t}.`, a = Object.fromEntries(
|
|
@@ -202,7 +202,7 @@ function Fe(r, t) {
|
|
|
202
202
|
}
|
|
203
203
|
class Ve {
|
|
204
204
|
constructor(t) {
|
|
205
|
-
|
|
205
|
+
S(this, "rc", 1);
|
|
206
206
|
this.drop = t;
|
|
207
207
|
}
|
|
208
208
|
inc() {
|
|
@@ -212,143 +212,153 @@ class Ve {
|
|
|
212
212
|
this.rc > 0 && (this.rc -= 1, this.rc === 0 && this.drop && this.drop());
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
|
-
function
|
|
215
|
+
function we(r) {
|
|
216
216
|
const e = {
|
|
217
217
|
...{
|
|
218
218
|
existsInForm: !0
|
|
219
219
|
},
|
|
220
220
|
...r
|
|
221
|
-
}, a =
|
|
221
|
+
}, a = $(Object.freeze(E(e.initialValue))), s = k({
|
|
222
222
|
value: e.value,
|
|
223
223
|
path: e.path,
|
|
224
224
|
initialValue: a,
|
|
225
225
|
errors: e.errors,
|
|
226
226
|
touched: !1
|
|
227
227
|
});
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
P(
|
|
229
|
+
$(e.initialValue),
|
|
230
230
|
() => {
|
|
231
|
-
a.value = Object.freeze(
|
|
231
|
+
a.value = Object.freeze(E(e.initialValue)), s.value !== h(e.initialValue) && (s.value = E(e.initialValue));
|
|
232
232
|
},
|
|
233
233
|
{ flush: "sync" }
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
},
|
|
238
|
-
|
|
239
|
-
s.
|
|
240
|
-
},
|
|
241
|
-
var
|
|
242
|
-
(
|
|
243
|
-
},
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
},
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
234
|
+
), P(() => s.value, (d) => {
|
|
235
|
+
var D;
|
|
236
|
+
(D = r.onChange) == null || D.call(r, d);
|
|
237
|
+
}, { deep: !0 });
|
|
238
|
+
const n = p(() => JSON.stringify(s.value) !== JSON.stringify(s.initialValue)), u = (d) => {
|
|
239
|
+
s.value = d;
|
|
240
|
+
}, m = () => {
|
|
241
|
+
var d;
|
|
242
|
+
s.touched = !0, s.errors = [], (d = e.onBlur) == null || d.call(e);
|
|
243
|
+
}, y = () => {
|
|
244
|
+
var d;
|
|
245
|
+
(d = e.onFocus) == null || d.call(e);
|
|
246
|
+
}, l = () => {
|
|
247
|
+
const d = s.path.split(".").at(-1) || "";
|
|
248
|
+
h(e.existsInForm) && !/^\d+$/.test(d) && (s.value = E(s.initialValue)), s.touched = !1, s.errors = [];
|
|
249
|
+
}, o = (d) => {
|
|
250
|
+
n.value || u(E(d)), s.initialValue = d;
|
|
251
|
+
}, v = (d) => {
|
|
252
|
+
s.errors = d;
|
|
253
|
+
}, i = () => {
|
|
251
254
|
s.errors = [];
|
|
252
|
-
},
|
|
255
|
+
}, c = J(s);
|
|
253
256
|
return {
|
|
254
|
-
data:
|
|
255
|
-
path:
|
|
256
|
-
initialValue:
|
|
257
|
-
errors:
|
|
258
|
-
touched:
|
|
257
|
+
data: c.value,
|
|
258
|
+
path: c.path,
|
|
259
|
+
initialValue: c.initialValue,
|
|
260
|
+
errors: c.errors,
|
|
261
|
+
touched: c.touched,
|
|
259
262
|
dirty: n,
|
|
260
263
|
setData: u,
|
|
261
|
-
setInitialData:
|
|
262
|
-
onBlur:
|
|
263
|
-
onFocus:
|
|
264
|
-
reset:
|
|
265
|
-
setErrors:
|
|
266
|
-
clearErrors:
|
|
264
|
+
setInitialData: o,
|
|
265
|
+
onBlur: m,
|
|
266
|
+
onFocus: y,
|
|
267
|
+
reset: l,
|
|
268
|
+
setErrors: v,
|
|
269
|
+
clearErrors: i
|
|
267
270
|
};
|
|
268
271
|
}
|
|
269
|
-
const
|
|
272
|
+
const Ee = {
|
|
270
273
|
keepValuesOnUnmount: !0
|
|
271
274
|
};
|
|
272
|
-
function
|
|
273
|
-
const e = () =>
|
|
274
|
-
return
|
|
275
|
+
function De(r, t) {
|
|
276
|
+
const e = () => b(r.initialData, t), a = $(e());
|
|
277
|
+
return P(
|
|
275
278
|
() => r.initialData,
|
|
276
279
|
() => {
|
|
277
|
-
a.value = e(),
|
|
280
|
+
a.value = e(), ne(a);
|
|
278
281
|
},
|
|
279
282
|
{ flush: "sync" }
|
|
280
283
|
), a;
|
|
281
284
|
}
|
|
282
285
|
function Re(r, t, e) {
|
|
283
|
-
const a = /* @__PURE__ */ new Map(), s =
|
|
284
|
-
...
|
|
286
|
+
const a = /* @__PURE__ */ new Map(), s = ae(/* @__PURE__ */ new Map()), n = {
|
|
287
|
+
...Ee,
|
|
285
288
|
...e
|
|
286
|
-
}, u = (
|
|
287
|
-
const
|
|
288
|
-
s.set(
|
|
289
|
-
},
|
|
290
|
-
var
|
|
291
|
-
n != null && n.keepValuesOnUnmount || (
|
|
292
|
-
},
|
|
293
|
-
var
|
|
294
|
-
a.has(
|
|
295
|
-
},
|
|
296
|
-
var
|
|
297
|
-
a.has(
|
|
298
|
-
},
|
|
299
|
-
const { path:
|
|
300
|
-
if (!s.has(
|
|
301
|
-
const
|
|
302
|
-
path:
|
|
303
|
-
value:
|
|
304
|
-
initialValue:
|
|
305
|
-
existsInForm:
|
|
306
|
-
errors:
|
|
289
|
+
}, u = (i) => {
|
|
290
|
+
const c = h(i.path);
|
|
291
|
+
s.set(c, i);
|
|
292
|
+
}, m = (i) => {
|
|
293
|
+
var c;
|
|
294
|
+
n != null && n.keepValuesOnUnmount || (c = s.get(i)) == null || c.reset(), s.delete(i);
|
|
295
|
+
}, y = (i) => {
|
|
296
|
+
var c;
|
|
297
|
+
a.has(i) ? (c = a.get(i)) == null || c.inc() : a.set(i, new Ve(() => m(i)));
|
|
298
|
+
}, l = (i) => {
|
|
299
|
+
var c;
|
|
300
|
+
a.has(i) && ((c = a.get(i)) == null || c.dec());
|
|
301
|
+
}, o = (i) => {
|
|
302
|
+
const { path: c } = i;
|
|
303
|
+
if (!s.has(c)) {
|
|
304
|
+
const D = we({
|
|
305
|
+
path: c,
|
|
306
|
+
value: X(j(r, "data"), c),
|
|
307
|
+
initialValue: De(r, c),
|
|
308
|
+
existsInForm: p(() => Fe(r.data, h(c))),
|
|
309
|
+
errors: p({
|
|
307
310
|
get() {
|
|
308
|
-
return t.errors.value.propertyErrors[
|
|
311
|
+
return t.errors.value.propertyErrors[c] || [];
|
|
309
312
|
},
|
|
310
|
-
set(
|
|
311
|
-
t.errors.value.propertyErrors[
|
|
313
|
+
set(V) {
|
|
314
|
+
t.errors.value.propertyErrors[c] = V;
|
|
312
315
|
}
|
|
313
316
|
}),
|
|
314
317
|
onBlur: async () => {
|
|
315
|
-
var
|
|
318
|
+
var V, w;
|
|
316
319
|
await Promise.all([
|
|
317
|
-
(
|
|
318
|
-
(
|
|
320
|
+
(V = n == null ? void 0 : n.onBlur) == null ? void 0 : V.call(n, h(c)),
|
|
321
|
+
(w = i.onBlur) == null ? void 0 : w.call(i)
|
|
319
322
|
]);
|
|
320
323
|
},
|
|
321
324
|
onFocus: async () => {
|
|
322
|
-
var
|
|
325
|
+
var V, w;
|
|
323
326
|
await Promise.all([
|
|
324
|
-
(
|
|
325
|
-
(
|
|
327
|
+
(V = n == null ? void 0 : n.onFocus) == null ? void 0 : V.call(n, h(c)),
|
|
328
|
+
(w = i.onFocus) == null ? void 0 : w.call(i)
|
|
329
|
+
]);
|
|
330
|
+
},
|
|
331
|
+
onChange: async (V) => {
|
|
332
|
+
var w, A;
|
|
333
|
+
await Promise.all([
|
|
334
|
+
(w = n == null ? void 0 : n.onChange) == null ? void 0 : w.call(n, h(c), V),
|
|
335
|
+
(A = i.onChange) == null ? void 0 : A.call(i, V)
|
|
326
336
|
]);
|
|
327
337
|
}
|
|
328
338
|
});
|
|
329
|
-
u(
|
|
339
|
+
u(D);
|
|
330
340
|
}
|
|
331
|
-
const
|
|
332
|
-
return
|
|
333
|
-
c
|
|
334
|
-
}),
|
|
335
|
-
},
|
|
341
|
+
const d = s.get(c);
|
|
342
|
+
return y(c), se(() => {
|
|
343
|
+
l(c);
|
|
344
|
+
}), d;
|
|
345
|
+
}, v = (i) => o(i);
|
|
336
346
|
return {
|
|
337
|
-
fields:
|
|
338
|
-
getField: (
|
|
347
|
+
fields: p(() => [...s.values()]),
|
|
348
|
+
getField: (i) => o({ path: i }),
|
|
339
349
|
registerField: u,
|
|
340
|
-
deregisterField:
|
|
341
|
-
defineField:
|
|
350
|
+
deregisterField: m,
|
|
351
|
+
defineField: v
|
|
342
352
|
};
|
|
343
353
|
}
|
|
344
|
-
function
|
|
345
|
-
const t =
|
|
354
|
+
function Pe(r) {
|
|
355
|
+
const t = p(() => r.fields.value.some((a) => h(a.dirty))), e = p(() => r.fields.value.some((a) => h(a.touched)));
|
|
346
356
|
return {
|
|
347
357
|
isDirty: t,
|
|
348
358
|
isTouched: e
|
|
349
359
|
};
|
|
350
360
|
}
|
|
351
|
-
function
|
|
361
|
+
function Se(r) {
|
|
352
362
|
const t = r.issues.filter((a) => a.path.length === 0).map((a) => a.message), e = r.issues.filter((a) => a.path.length > 0).reduce((a, s) => {
|
|
353
363
|
const n = s.path.join(".");
|
|
354
364
|
return {
|
|
@@ -361,23 +371,23 @@ function Pe(r) {
|
|
|
361
371
|
propertyErrors: e
|
|
362
372
|
};
|
|
363
373
|
}
|
|
364
|
-
const
|
|
374
|
+
const g = {
|
|
365
375
|
errors: {
|
|
366
376
|
general: [],
|
|
367
377
|
propertyErrors: {}
|
|
368
378
|
}
|
|
369
379
|
};
|
|
370
|
-
class
|
|
380
|
+
class be {
|
|
371
381
|
constructor(t) {
|
|
372
382
|
this.schema = t;
|
|
373
383
|
}
|
|
374
384
|
async validate(t) {
|
|
375
385
|
if (!this.schema)
|
|
376
|
-
return
|
|
386
|
+
return g;
|
|
377
387
|
const e = await this.schema.safeParseAsync(t);
|
|
378
388
|
if (e.success)
|
|
379
|
-
return
|
|
380
|
-
const a =
|
|
389
|
+
return g;
|
|
390
|
+
const a = Se(e.error);
|
|
381
391
|
return {
|
|
382
392
|
data: e.data,
|
|
383
393
|
errors: {
|
|
@@ -393,10 +403,10 @@ class Ie {
|
|
|
393
403
|
}
|
|
394
404
|
async validate(t) {
|
|
395
405
|
if (!this.validateFn)
|
|
396
|
-
return
|
|
406
|
+
return g;
|
|
397
407
|
try {
|
|
398
408
|
const e = await this.validateFn(t);
|
|
399
|
-
return
|
|
409
|
+
return z(e) ? g : e;
|
|
400
410
|
} catch (e) {
|
|
401
411
|
return {
|
|
402
412
|
errors: {
|
|
@@ -409,9 +419,9 @@ class Ie {
|
|
|
409
419
|
}
|
|
410
420
|
class $e {
|
|
411
421
|
constructor(t, e) {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
this.schema = t, this.validateFn = e, this.schemaValidator = new
|
|
422
|
+
S(this, "schemaValidator");
|
|
423
|
+
S(this, "functionValidator");
|
|
424
|
+
this.schema = t, this.validateFn = e, this.schemaValidator = new be(this.schema), this.functionValidator = new Ie(this.validateFn);
|
|
415
425
|
}
|
|
416
426
|
async validate(t) {
|
|
417
427
|
const [e, a] = await Promise.all([
|
|
@@ -424,90 +434,92 @@ class $e {
|
|
|
424
434
|
};
|
|
425
435
|
}
|
|
426
436
|
}
|
|
427
|
-
function
|
|
428
|
-
return
|
|
437
|
+
function _(r) {
|
|
438
|
+
return p(() => new $e(
|
|
429
439
|
h(r.schema),
|
|
430
440
|
h(r.validateFn)
|
|
431
441
|
));
|
|
432
442
|
}
|
|
433
|
-
function
|
|
434
|
-
const e =
|
|
435
|
-
validators:
|
|
443
|
+
function Ce(r, t) {
|
|
444
|
+
const e = k({
|
|
445
|
+
validators: G([_(t)]),
|
|
436
446
|
isValidated: !1,
|
|
437
|
-
errors: h(t.errors) ??
|
|
438
|
-
}), a = (
|
|
439
|
-
e.errors = B(h(t.errors) ??
|
|
447
|
+
errors: h(t.errors) ?? g.errors
|
|
448
|
+
}), a = (o = g.errors) => {
|
|
449
|
+
e.errors = B(h(t.errors) ?? g.errors, o);
|
|
440
450
|
};
|
|
441
|
-
|
|
451
|
+
P(() => h(t.errors), async () => {
|
|
442
452
|
if (e.isValidated) {
|
|
443
|
-
const
|
|
444
|
-
a(
|
|
453
|
+
const o = await n();
|
|
454
|
+
a(o.errors);
|
|
445
455
|
} else
|
|
446
456
|
a();
|
|
447
|
-
}, { immediate: !0 }),
|
|
457
|
+
}, { immediate: !0 }), P(
|
|
448
458
|
[() => e.validators],
|
|
449
|
-
async (
|
|
459
|
+
async (o) => {
|
|
450
460
|
if (e.isValidated)
|
|
451
|
-
if (
|
|
452
|
-
const
|
|
453
|
-
e.errors =
|
|
461
|
+
if (o) {
|
|
462
|
+
const v = await n();
|
|
463
|
+
e.errors = v.errors;
|
|
454
464
|
} else
|
|
455
|
-
e.errors =
|
|
465
|
+
e.errors = g.errors;
|
|
456
466
|
},
|
|
457
467
|
{ immediate: !0 }
|
|
458
|
-
),
|
|
468
|
+
), P([() => r.data, () => h(t.schema)], () => {
|
|
459
469
|
e.isValidated && u();
|
|
460
470
|
});
|
|
461
|
-
const s = (
|
|
462
|
-
const
|
|
463
|
-
return e.validators.push(
|
|
471
|
+
const s = (o) => {
|
|
472
|
+
const v = T(o) ? o : _(o);
|
|
473
|
+
return e.validators.push(v), oe() && ie(() => {
|
|
464
474
|
e.validators = e.validators.filter(
|
|
465
|
-
(
|
|
475
|
+
(i) => i !== v
|
|
466
476
|
);
|
|
467
|
-
}),
|
|
477
|
+
}), v;
|
|
468
478
|
};
|
|
469
479
|
async function n() {
|
|
470
|
-
var
|
|
471
|
-
const
|
|
472
|
-
e.validators.filter((
|
|
473
|
-
),
|
|
474
|
-
let { errors:
|
|
475
|
-
if (!
|
|
476
|
-
const
|
|
477
|
-
|
|
480
|
+
var c;
|
|
481
|
+
const o = await Promise.all(
|
|
482
|
+
e.validators.filter((d) => h(d) !== void 0).map((d) => h(d).validate(r.data))
|
|
483
|
+
), v = o.every((d) => z(d));
|
|
484
|
+
let { errors: i } = g;
|
|
485
|
+
if (!v) {
|
|
486
|
+
const d = o.map((D) => D.errors);
|
|
487
|
+
i = B(...d);
|
|
478
488
|
}
|
|
479
489
|
return {
|
|
480
|
-
errors:
|
|
490
|
+
errors: i,
|
|
481
491
|
// TODO: Implement data disambiguation strategy
|
|
482
|
-
data: (
|
|
492
|
+
data: (c = o.findLast((d) => !!d.data)) == null ? void 0 : c.data
|
|
483
493
|
};
|
|
484
494
|
}
|
|
485
495
|
const u = async () => {
|
|
486
|
-
const
|
|
487
|
-
return a(
|
|
496
|
+
const o = await n();
|
|
497
|
+
return a(o.errors), e.isValidated = !0, {
|
|
488
498
|
errors: e.errors
|
|
489
499
|
};
|
|
490
|
-
},
|
|
491
|
-
|
|
500
|
+
}, m = async (o) => {
|
|
501
|
+
if (!e.isValidated)
|
|
502
|
+
return g;
|
|
503
|
+
const v = await n();
|
|
492
504
|
return a({
|
|
493
|
-
general:
|
|
505
|
+
general: v.errors.general,
|
|
494
506
|
propertyErrors: {
|
|
495
|
-
[
|
|
507
|
+
[o]: v.errors.propertyErrors[o]
|
|
496
508
|
}
|
|
497
509
|
}), {
|
|
498
|
-
data:
|
|
510
|
+
data: v.data,
|
|
499
511
|
errors: e.errors
|
|
500
512
|
};
|
|
501
|
-
},
|
|
502
|
-
e.isValidated = !1, e.errors = h(t.errors) ??
|
|
513
|
+
}, y = p(() => !q(e.errors)), l = () => {
|
|
514
|
+
e.isValidated = !1, e.errors = h(t.errors) ?? g.errors;
|
|
503
515
|
};
|
|
504
516
|
return {
|
|
505
|
-
...
|
|
517
|
+
...J(e),
|
|
506
518
|
validateForm: u,
|
|
507
|
-
validateField:
|
|
519
|
+
validateField: m,
|
|
508
520
|
defineValidator: s,
|
|
509
|
-
isValid:
|
|
510
|
-
reset:
|
|
521
|
+
isValid: y,
|
|
522
|
+
reset: l
|
|
511
523
|
};
|
|
512
524
|
}
|
|
513
525
|
class ke {
|
|
@@ -515,16 +527,16 @@ class ke {
|
|
|
515
527
|
this.path = t, this.validator = e;
|
|
516
528
|
}
|
|
517
529
|
async validate(t) {
|
|
518
|
-
const e =
|
|
530
|
+
const e = b(t, this.path);
|
|
519
531
|
if (!this.validator)
|
|
520
|
-
return
|
|
532
|
+
return g;
|
|
521
533
|
const a = await this.validator.validate(e);
|
|
522
534
|
return {
|
|
523
535
|
errors: {
|
|
524
536
|
general: a.errors.general || [],
|
|
525
537
|
propertyErrors: a.errors.propertyErrors ? Object.fromEntries(
|
|
526
538
|
Object.entries(a.errors.propertyErrors).map(
|
|
527
|
-
([s, n]) => [
|
|
539
|
+
([s, n]) => [I(this.path, s), n]
|
|
528
540
|
)
|
|
529
541
|
) : {}
|
|
530
542
|
}
|
|
@@ -532,96 +544,99 @@ class ke {
|
|
|
532
544
|
}
|
|
533
545
|
}
|
|
534
546
|
function Ae(r, t, e, a) {
|
|
535
|
-
const s =
|
|
536
|
-
...
|
|
537
|
-
path:
|
|
538
|
-
setData: (
|
|
539
|
-
|
|
547
|
+
const s = X(r.data, t), n = p(() => b(r.initialData.value, t)), u = (f) => ({
|
|
548
|
+
...f,
|
|
549
|
+
path: p(() => h(f.path).replace(t + ".", "")),
|
|
550
|
+
setData: (F) => {
|
|
551
|
+
f.setData(F);
|
|
540
552
|
}
|
|
541
|
-
}),
|
|
542
|
-
const
|
|
543
|
-
return
|
|
544
|
-
},
|
|
545
|
-
const
|
|
546
|
-
...
|
|
547
|
-
path:
|
|
553
|
+
}), m = (f) => {
|
|
554
|
+
const F = I(t, f), R = r.getField(F);
|
|
555
|
+
return R ? u(R) : {};
|
|
556
|
+
}, y = (f) => {
|
|
557
|
+
const F = I(t, f.path), R = r.defineField({
|
|
558
|
+
...f,
|
|
559
|
+
path: F
|
|
548
560
|
});
|
|
549
|
-
return u(
|
|
550
|
-
},
|
|
551
|
-
const
|
|
552
|
-
return
|
|
553
|
-
}).map((
|
|
554
|
-
const
|
|
555
|
-
return
|
|
556
|
-
}),
|
|
561
|
+
return u(R);
|
|
562
|
+
}, l = p(() => r.fields.value.filter((f) => {
|
|
563
|
+
const F = f.path.value;
|
|
564
|
+
return F.startsWith(t + ".") || F === t;
|
|
565
|
+
}).map((f) => u(f))), o = () => r.fields.value.filter((f) => {
|
|
566
|
+
const F = f.path.value;
|
|
567
|
+
return F.startsWith(t + ".") || F === t;
|
|
568
|
+
}), v = p(() => o().some((f) => f.dirty.value)), i = p(() => o().some((f) => f.touched.value)), c = p(() => r.isValid.value), d = p(() => r.isValidated.value), D = p(() => ge(h(r.errors), t)), M = {
|
|
557
569
|
data: s,
|
|
558
|
-
fields:
|
|
570
|
+
fields: l,
|
|
559
571
|
initialData: n,
|
|
560
|
-
defineField:
|
|
561
|
-
getField:
|
|
562
|
-
isDirty:
|
|
563
|
-
isTouched:
|
|
564
|
-
isValid:
|
|
565
|
-
isValidated:
|
|
566
|
-
errors:
|
|
567
|
-
defineValidator: (
|
|
568
|
-
const
|
|
569
|
-
() => new ke(t, h(
|
|
572
|
+
defineField: y,
|
|
573
|
+
getField: m,
|
|
574
|
+
isDirty: v,
|
|
575
|
+
isTouched: i,
|
|
576
|
+
isValid: c,
|
|
577
|
+
isValidated: d,
|
|
578
|
+
errors: D,
|
|
579
|
+
defineValidator: (f) => {
|
|
580
|
+
const F = T(f) ? f : _(f), R = p(
|
|
581
|
+
() => new ke(t, h(F))
|
|
570
582
|
);
|
|
571
|
-
return r.defineValidator(
|
|
583
|
+
return r.defineValidator(R), F;
|
|
572
584
|
},
|
|
573
|
-
reset: () =>
|
|
585
|
+
reset: () => o().forEach((f) => f.reset()),
|
|
574
586
|
validateForm: () => r.validateForm(),
|
|
575
|
-
getSubForm: (
|
|
576
|
-
const
|
|
587
|
+
getSubForm: (f, F) => {
|
|
588
|
+
const R = I(t, f);
|
|
577
589
|
return r.getSubForm(
|
|
578
|
-
|
|
579
|
-
|
|
590
|
+
R,
|
|
591
|
+
F
|
|
580
592
|
);
|
|
581
593
|
},
|
|
582
|
-
submitHandler: (
|
|
583
|
-
getFieldArray: (
|
|
594
|
+
submitHandler: (f) => O(M, e ?? {})(f),
|
|
595
|
+
getFieldArray: (f, F) => Q(M, f, F)
|
|
584
596
|
};
|
|
585
597
|
return M;
|
|
586
598
|
}
|
|
587
599
|
function xe(r) {
|
|
588
|
-
const t =
|
|
600
|
+
const t = p(() => E(r.initialData)), e = G(E(t)), a = k({
|
|
589
601
|
initialData: t,
|
|
590
602
|
data: e
|
|
591
603
|
});
|
|
592
|
-
|
|
604
|
+
P(
|
|
593
605
|
t,
|
|
594
|
-
(
|
|
595
|
-
a.data =
|
|
606
|
+
(l) => {
|
|
607
|
+
a.data = E(l);
|
|
596
608
|
},
|
|
597
609
|
{ flush: "sync" }
|
|
598
610
|
);
|
|
599
|
-
const s =
|
|
611
|
+
const s = Ce(a, r), n = Re(a, s, {
|
|
600
612
|
keepValuesOnUnmount: r.keepValuesOnUnmount,
|
|
601
|
-
onBlur: async (
|
|
602
|
-
h(r.validationStrategy) === "onTouch" && s.validateField(
|
|
613
|
+
onBlur: async (l) => {
|
|
614
|
+
h(r.validationStrategy) === "onTouch" && s.validateField(l);
|
|
615
|
+
},
|
|
616
|
+
onChange: async (l) => {
|
|
617
|
+
h(r.validationStrategy) === "onDataChange" && s.validateField(l);
|
|
603
618
|
}
|
|
604
|
-
}), u =
|
|
605
|
-
e.value =
|
|
606
|
-
for (const
|
|
607
|
-
|
|
619
|
+
}), u = Pe(n), m = () => {
|
|
620
|
+
e.value = E(t), s.reset();
|
|
621
|
+
for (const l of n.fields.value)
|
|
622
|
+
l.reset();
|
|
608
623
|
};
|
|
609
624
|
h(r.validationStrategy) === "onFormOpen" && s.validateForm();
|
|
610
|
-
const
|
|
625
|
+
const y = {
|
|
611
626
|
...n,
|
|
612
627
|
...s,
|
|
613
628
|
...u,
|
|
614
|
-
reset:
|
|
629
|
+
reset: m,
|
|
615
630
|
initialData: j(a, "initialData"),
|
|
616
631
|
data: j(a, "data"),
|
|
617
632
|
validateForm: s.validateForm,
|
|
618
|
-
submitHandler: (
|
|
619
|
-
getSubForm: (
|
|
620
|
-
getFieldArray: (
|
|
633
|
+
submitHandler: (l) => O(y, r)(l),
|
|
634
|
+
getSubForm: (l, o) => Ae(y, l, r),
|
|
635
|
+
getFieldArray: (l, o) => Q(y, l, o)
|
|
621
636
|
};
|
|
622
|
-
return
|
|
637
|
+
return y;
|
|
623
638
|
}
|
|
624
|
-
const Me = /* @__PURE__ */
|
|
639
|
+
const Me = /* @__PURE__ */ x({
|
|
625
640
|
__name: "Field",
|
|
626
641
|
props: {
|
|
627
642
|
form: {},
|
|
@@ -631,15 +646,16 @@ const Me = /* @__PURE__ */ T({
|
|
|
631
646
|
errors: {},
|
|
632
647
|
existsInForm: {},
|
|
633
648
|
onBlur: { type: Function },
|
|
634
|
-
onFocus: { type: Function }
|
|
649
|
+
onFocus: { type: Function },
|
|
650
|
+
onChange: { type: Function }
|
|
635
651
|
},
|
|
636
652
|
setup(r) {
|
|
637
653
|
const t = r, e = t.form.defineField({
|
|
638
654
|
path: t.path
|
|
639
|
-
}), a =
|
|
640
|
-
return (s, n) =>
|
|
655
|
+
}), a = k(e);
|
|
656
|
+
return (s, n) => C(s.$slots, "default", N(W(a)));
|
|
641
657
|
}
|
|
642
|
-
}),
|
|
658
|
+
}), Ne = /* @__PURE__ */ x({
|
|
643
659
|
inheritAttrs: !1,
|
|
644
660
|
__name: "FormFieldWrapper",
|
|
645
661
|
props: {
|
|
@@ -649,47 +665,49 @@ const Me = /* @__PURE__ */ T({
|
|
|
649
665
|
path: {}
|
|
650
666
|
},
|
|
651
667
|
setup(r) {
|
|
652
|
-
return (t, e) => (
|
|
668
|
+
return (t, e) => (L(), K(Me, {
|
|
653
669
|
form: r.form,
|
|
654
670
|
path: r.path
|
|
655
671
|
}, {
|
|
656
|
-
default: U(({ errors: a, data: s, setData: n }) => [
|
|
657
|
-
(
|
|
672
|
+
default: U(({ errors: a, data: s, setData: n, onBlur: u, onFocus: m }) => [
|
|
673
|
+
(L(), K(le(r.component), ce({ ...r.componentProps, ...t.$attrs }, {
|
|
674
|
+
"on-blur": u,
|
|
675
|
+
"on-focus": m,
|
|
658
676
|
"model-value": s,
|
|
659
677
|
errors: a,
|
|
660
678
|
name: r.path,
|
|
661
679
|
"onUpdate:modelValue": n
|
|
662
|
-
}),
|
|
680
|
+
}), ue({
|
|
663
681
|
default: U(() => [
|
|
664
|
-
|
|
682
|
+
C(t.$slots, "default")
|
|
665
683
|
]),
|
|
666
684
|
_: 2
|
|
667
685
|
}, [
|
|
668
|
-
|
|
669
|
-
name:
|
|
670
|
-
fn: U((
|
|
671
|
-
|
|
686
|
+
de(t.$slots, (y, l) => ({
|
|
687
|
+
name: l,
|
|
688
|
+
fn: U((o) => [
|
|
689
|
+
C(t.$slots, l, N(W(o ?? {})))
|
|
672
690
|
])
|
|
673
691
|
}))
|
|
674
|
-
]), 1040, ["model-value", "errors", "name", "onUpdate:modelValue"]))
|
|
692
|
+
]), 1040, ["on-blur", "on-focus", "model-value", "errors", "name", "onUpdate:modelValue"]))
|
|
675
693
|
]),
|
|
676
694
|
_: 3
|
|
677
695
|
}, 8, ["form", "path"]));
|
|
678
696
|
}
|
|
679
|
-
}),
|
|
697
|
+
}), We = /* @__PURE__ */ x({
|
|
680
698
|
__name: "FormPart",
|
|
681
699
|
props: {
|
|
682
700
|
form: {},
|
|
683
701
|
path: {}
|
|
684
702
|
},
|
|
685
703
|
setup(r) {
|
|
686
|
-
const t = r, e =
|
|
687
|
-
return (a, s) =>
|
|
704
|
+
const t = r, e = p(() => t.form.getSubForm(t.path));
|
|
705
|
+
return (a, s) => C(a.$slots, "default", N(W({ subform: e.value })));
|
|
688
706
|
}
|
|
689
707
|
});
|
|
690
708
|
export {
|
|
691
709
|
Me as Field,
|
|
692
|
-
|
|
693
|
-
|
|
710
|
+
Ne as FormFieldWrapper,
|
|
711
|
+
We as FormPart,
|
|
694
712
|
xe as useForm
|
|
695
713
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FormDataDefault } from './form';
|
|
2
|
-
export type ValidationStrategy = 'onTouch' | 'onFormOpen' | 'none' | 'preSubmit';
|
|
2
|
+
export type ValidationStrategy = 'onTouch' | 'onFormOpen' | 'none' | 'preSubmit' | 'onDataChange';
|
|
3
3
|
export type ValidationErrorMessage = string;
|
|
4
4
|
export type ValidationErrors = ValidationErrorMessage[] | undefined;
|
|
5
5
|
export interface ErrorBag {
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Field
|
|
3
|
-
v-slot="{ errors, data, setData }"
|
|
3
|
+
v-slot="{ errors, data, setData, onBlur, onFocus }"
|
|
4
4
|
:form="form"
|
|
5
5
|
:path="path"
|
|
6
6
|
>
|
|
7
7
|
<component
|
|
8
8
|
:is="component"
|
|
9
9
|
v-bind="{...componentProps, ...$attrs}"
|
|
10
|
+
:on-blur="onBlur"
|
|
11
|
+
:on-focus="onFocus"
|
|
10
12
|
:model-value="data"
|
|
11
13
|
:errors="errors"
|
|
12
14
|
:name="path"
|
|
@@ -12,6 +12,7 @@ export interface UseFieldOptions<T, K extends string> {
|
|
|
12
12
|
existsInForm?: MaybeRef<boolean>
|
|
13
13
|
onBlur?: () => Awaitable<void>
|
|
14
14
|
onFocus?: () => Awaitable<void>
|
|
15
|
+
onChange?: (value: T) => Awaitable<void>
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function useField<T, K extends string>(fieldOptions: UseFieldOptions<T, K>): FormField<T, K> {
|
|
@@ -44,6 +45,10 @@ export function useField<T, K extends string>(fieldOptions: UseFieldOptions<T, K
|
|
|
44
45
|
{ flush: 'sync' },
|
|
45
46
|
)
|
|
46
47
|
|
|
48
|
+
watch(() => state.value, (newData) => {
|
|
49
|
+
fieldOptions.onChange?.(newData as T)
|
|
50
|
+
}, { deep: true })
|
|
51
|
+
|
|
47
52
|
const dirty = computed(() => {
|
|
48
53
|
return JSON.stringify(state.value) !== JSON.stringify(state.initialValue)
|
|
49
54
|
})
|
|
@@ -31,6 +31,7 @@ export type DefineFieldOptions<F, K extends string> = Pick<
|
|
|
31
31
|
> & {
|
|
32
32
|
onBlur?: () => void
|
|
33
33
|
onFocus?: () => void
|
|
34
|
+
onChange?: <T>(value: T) => void
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
interface FormState<
|
|
@@ -45,6 +46,7 @@ interface FieldRegistryOptions {
|
|
|
45
46
|
keepValuesOnUnmount?: MaybeRef<boolean>
|
|
46
47
|
onBlur?: (path: string) => Awaitable<void>
|
|
47
48
|
onFocus?: (path: string) => Awaitable<void>
|
|
49
|
+
onChange?: <T>(path: string, value: T) => Awaitable<void>
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
const optionDefaults = {
|
|
@@ -143,6 +145,12 @@ export function useFieldRegistry<T extends FormDataDefault, TOut = T>(
|
|
|
143
145
|
options.onFocus?.(),
|
|
144
146
|
])
|
|
145
147
|
},
|
|
148
|
+
onChange: async (value: PickProps<T, K>) => {
|
|
149
|
+
await Promise.all([
|
|
150
|
+
registryOptions?.onChange?.(unref(path), value),
|
|
151
|
+
options.onChange?.(value),
|
|
152
|
+
])
|
|
153
|
+
},
|
|
146
154
|
})
|
|
147
155
|
|
|
148
156
|
registerField(field)
|
|
@@ -30,10 +30,11 @@ export interface UseFormOptions<T extends FormDataDefault, TOut = T>
|
|
|
30
30
|
/* eslint-disable no-redeclare */
|
|
31
31
|
// Overload: with schema - infer types from schema
|
|
32
32
|
// initialData can be partial, but provided fields must match schema types
|
|
33
|
-
export function useForm<
|
|
34
|
-
T
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
export function useForm<T extends FormDataDefault, TOut = T>(
|
|
34
|
+
options: UseFormOptions<T, TOut> & {
|
|
35
|
+
schema: MaybeRef<z.ZodType<TOut, unknown>>
|
|
36
|
+
},
|
|
37
|
+
): Form<T, TOut>
|
|
37
38
|
|
|
38
39
|
// Overload: without schema - infer types from initialData
|
|
39
40
|
export function useForm<T extends FormDataDefault>(
|
|
@@ -70,6 +71,11 @@ export function useForm<T extends FormDataDefault, TOut = T>(
|
|
|
70
71
|
validationState.validateField(path)
|
|
71
72
|
}
|
|
72
73
|
},
|
|
74
|
+
onChange: async (path: string) => {
|
|
75
|
+
if (unref(options.validationStrategy) === 'onDataChange') {
|
|
76
|
+
validationState.validateField(path)
|
|
77
|
+
}
|
|
78
|
+
},
|
|
73
79
|
})
|
|
74
80
|
const formState = useFormState(fieldRegistry)
|
|
75
81
|
|
|
@@ -214,6 +214,10 @@ export function useValidation<T extends FormDataDefault, TOut = T>(
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
const validateField = async (path: string): Promise<ValidationResult<TOut>> => {
|
|
217
|
+
if (!validationState.isValidated) {
|
|
218
|
+
return SuccessValidationResult
|
|
219
|
+
}
|
|
220
|
+
|
|
217
221
|
const validationResults = await getValidationResults()
|
|
218
222
|
|
|
219
223
|
updateErrors({
|
package/src/types/validation.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FormDataDefault } from './form'
|
|
2
2
|
|
|
3
|
-
export type ValidationStrategy = 'onTouch' | 'onFormOpen' | 'none' | 'preSubmit'
|
|
3
|
+
export type ValidationStrategy = 'onTouch' | 'onFormOpen' | 'none' | 'preSubmit' | 'onDataChange'
|
|
4
4
|
|
|
5
5
|
export type ValidationErrorMessage = string
|
|
6
6
|
export type ValidationErrors = ValidationErrorMessage[] | undefined
|
|
@@ -280,6 +280,30 @@ describe('useValidation', () => {
|
|
|
280
280
|
expect(validation.errors.value).toEqual(SuccessValidationResult.errors)
|
|
281
281
|
})
|
|
282
282
|
|
|
283
|
+
it('should not the form on blur if configured but the form was never validated yet', async () => {
|
|
284
|
+
const schema = z.object({
|
|
285
|
+
name: z.string().min(2),
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
const initialData = { name: 'A' }
|
|
289
|
+
const form = useForm({
|
|
290
|
+
initialData,
|
|
291
|
+
schema,
|
|
292
|
+
validationStrategy: 'onTouch',
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
const nameField = form.getField('name')
|
|
296
|
+
|
|
297
|
+
// Simulate blur event
|
|
298
|
+
nameField.onBlur()
|
|
299
|
+
|
|
300
|
+
// onBlur is not async but the validation runs async
|
|
301
|
+
await delay()
|
|
302
|
+
|
|
303
|
+
expect(form.isValid.value).toBe(true)
|
|
304
|
+
expect(form.errors.value.propertyErrors.name).toHaveLength(0)
|
|
305
|
+
})
|
|
306
|
+
|
|
283
307
|
it('should validate the form on blur if configured', async () => {
|
|
284
308
|
const schema = z.object({
|
|
285
309
|
name: z.string().min(2),
|
|
@@ -294,6 +318,8 @@ describe('useValidation', () => {
|
|
|
294
318
|
|
|
295
319
|
const nameField = form.getField('name')
|
|
296
320
|
|
|
321
|
+
await form.validateForm()
|
|
322
|
+
|
|
297
323
|
// Simulate blur event
|
|
298
324
|
nameField.onBlur()
|
|
299
325
|
|
|
@@ -324,6 +350,8 @@ describe('useValidation', () => {
|
|
|
324
350
|
const nameField = form.getField('name')
|
|
325
351
|
form.getField('email')
|
|
326
352
|
|
|
353
|
+
await form.validateForm()
|
|
354
|
+
|
|
327
355
|
// Simulate blur event
|
|
328
356
|
nameField.onBlur()
|
|
329
357
|
|
|
@@ -379,4 +407,30 @@ describe('useValidation', () => {
|
|
|
379
407
|
expect(form.isValidated.value).toBe(false)
|
|
380
408
|
expect(form.isValid.value).toBe(true)
|
|
381
409
|
})
|
|
410
|
+
|
|
411
|
+
it('should validate the form on data change if configured', async () => {
|
|
412
|
+
const schema = z.object({
|
|
413
|
+
name: z.string().min(2),
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
const initialData = { name: 'ABC' }
|
|
417
|
+
const form = useForm({
|
|
418
|
+
initialData,
|
|
419
|
+
schema,
|
|
420
|
+
validationStrategy: 'onDataChange',
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
const nameField = form.getField('name')
|
|
424
|
+
|
|
425
|
+
await form.validateForm()
|
|
426
|
+
|
|
427
|
+
expect(form.isValid.value).toBe(true)
|
|
428
|
+
|
|
429
|
+
nameField.data.value = 'a'
|
|
430
|
+
|
|
431
|
+
await delay()
|
|
432
|
+
|
|
433
|
+
expect(form.isValid.value).toBe(false)
|
|
434
|
+
expect(form.errors.value.propertyErrors.name).toHaveLength(1)
|
|
435
|
+
})
|
|
382
436
|
})
|