@teamnovu/kit-vue-forms 0.1.2 → 0.1.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/dist/index.js +281 -262
- package/dist/utils/rc.d.ts +10 -0
- package/package.json +1 -1
- package/src/composables/useFieldRegistry.ts +33 -15
- package/src/composables/useValidation.ts +7 -3
- package/src/utils/rc.ts +22 -0
- package/tests/useForm.test.ts +56 -4
package/dist/index.js
CHANGED
|
@@ -1,77 +1,89 @@
|
|
|
1
1
|
var T = Object.defineProperty;
|
|
2
|
-
var G = (
|
|
3
|
-
var
|
|
4
|
-
import { toValue as L, toRaw as Z, computed as
|
|
5
|
-
import { cloneDeep as
|
|
2
|
+
var G = (r, e, t) => e in r ? T(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
|
|
3
|
+
var E = (r, e, t) => G(r, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
+
import { toValue as L, toRaw as Z, computed as d, unref as f, reactive as R, toRefs as N, shallowReactive as q, toRef as _, onScopeDispose as H, ref as W, watch as F, isRef as k, getCurrentScope as Q, onBeforeUnmount as X, defineComponent as j, renderSlot as O, normalizeProps as z, guardReactiveProps as B, resolveComponent as Y, createBlock as $, openBlock as A, withCtx as C, resolveDynamicComponent as x, mergeProps as ee } from "vue";
|
|
5
|
+
import { cloneDeep as re } from "lodash-es";
|
|
6
6
|
import "zod";
|
|
7
|
-
function y(
|
|
8
|
-
const
|
|
9
|
-
return
|
|
7
|
+
function y(r) {
|
|
8
|
+
const e = L(r), t = Z(e);
|
|
9
|
+
return re(t);
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
return
|
|
11
|
+
function K(r) {
|
|
12
|
+
return r === "" ? [] : r.split(/\s*\.\s*/).filter(Boolean);
|
|
13
13
|
}
|
|
14
|
-
function
|
|
15
|
-
return (Array.isArray(
|
|
16
|
-
(
|
|
17
|
-
|
|
14
|
+
function D(r, e) {
|
|
15
|
+
return (Array.isArray(e) ? e : K(e)).reduce(
|
|
16
|
+
(s, o) => s == null ? void 0 : s[o],
|
|
17
|
+
r
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
|
-
function
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
20
|
+
function te(r, e, t) {
|
|
21
|
+
const s = Array.isArray(e) ? e : K(e);
|
|
22
|
+
if (s.length === 0)
|
|
23
23
|
throw new Error("Path cannot be empty");
|
|
24
|
-
const
|
|
25
|
-
(h,
|
|
24
|
+
const o = s.at(-1), n = s.slice(0, -1).reduce(
|
|
25
|
+
(h, v) => h[v],
|
|
26
26
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
-
|
|
27
|
+
r
|
|
28
28
|
);
|
|
29
|
-
o
|
|
29
|
+
n[o] = t;
|
|
30
30
|
}
|
|
31
|
-
const
|
|
31
|
+
const U = (r, e) => d({
|
|
32
32
|
get() {
|
|
33
|
-
return
|
|
33
|
+
return D(f(r), f(e));
|
|
34
34
|
},
|
|
35
35
|
set(t) {
|
|
36
|
-
|
|
36
|
+
te(f(r), f(e), t);
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
|
-
function
|
|
40
|
-
return !
|
|
39
|
+
function w(r, e) {
|
|
40
|
+
return !r && !e ? "" : !r && e ? e : !e && r ? r : `${r}.${e}`;
|
|
41
41
|
}
|
|
42
|
-
function
|
|
43
|
-
if (!
|
|
44
|
-
return
|
|
45
|
-
const t = `${
|
|
46
|
-
Object.entries(
|
|
47
|
-
([
|
|
42
|
+
function se(r, e) {
|
|
43
|
+
if (!e)
|
|
44
|
+
return r;
|
|
45
|
+
const t = `${e}.`, s = Object.fromEntries(
|
|
46
|
+
Object.entries(r.propertyErrors).filter(([o]) => o.startsWith(t)).map(
|
|
47
|
+
([o, n]) => [o.slice(t.length), n]
|
|
48
48
|
)
|
|
49
49
|
);
|
|
50
50
|
return {
|
|
51
|
-
general:
|
|
51
|
+
general: r.general,
|
|
52
52
|
// Keep general errors
|
|
53
|
-
propertyErrors:
|
|
53
|
+
propertyErrors: s
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
class ae {
|
|
57
|
+
constructor(e) {
|
|
58
|
+
E(this, "rc", 1);
|
|
59
|
+
this.drop = e;
|
|
60
|
+
}
|
|
61
|
+
inc() {
|
|
62
|
+
this.rc += 1;
|
|
63
|
+
}
|
|
64
|
+
dec() {
|
|
65
|
+
this.rc > 0 && (this.rc -= 1, this.rc === 0 && this.drop && this.drop());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function oe(r) {
|
|
69
|
+
const e = R({
|
|
70
|
+
value: r.value,
|
|
71
|
+
path: r.path,
|
|
72
|
+
initialValue: d(() => Object.freeze(y(r.initialValue))),
|
|
73
|
+
errors: r.errors,
|
|
62
74
|
touched: !1
|
|
63
|
-
}), t =
|
|
64
|
-
|
|
65
|
-
}, s = () => {
|
|
66
|
-
r.touched = !0;
|
|
75
|
+
}), t = d(() => JSON.stringify(e.value) !== JSON.stringify(e.initialValue)), s = (a) => {
|
|
76
|
+
e.value = a;
|
|
67
77
|
}, o = () => {
|
|
68
|
-
|
|
69
|
-
r.value = y(r.initialValue), r.touched = !1, r.errors = [];
|
|
70
|
-
}, l = (f) => {
|
|
71
|
-
r.errors = f;
|
|
78
|
+
e.touched = !0;
|
|
72
79
|
}, n = () => {
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
}, h = () => {
|
|
81
|
+
e.value = y(e.initialValue), e.touched = !1, e.errors = [];
|
|
82
|
+
}, v = (a) => {
|
|
83
|
+
e.errors = a;
|
|
84
|
+
}, l = () => {
|
|
85
|
+
e.errors = [];
|
|
86
|
+
}, c = N(e);
|
|
75
87
|
return {
|
|
76
88
|
data: c.value,
|
|
77
89
|
path: c.path,
|
|
@@ -79,116 +91,120 @@ function sr(e) {
|
|
|
79
91
|
errors: c.errors,
|
|
80
92
|
touched: c.touched,
|
|
81
93
|
dirty: t,
|
|
82
|
-
setData:
|
|
83
|
-
onBlur:
|
|
84
|
-
onFocus:
|
|
94
|
+
setData: s,
|
|
95
|
+
onBlur: o,
|
|
96
|
+
onFocus: n,
|
|
85
97
|
reset: h,
|
|
86
|
-
setErrors:
|
|
87
|
-
clearErrors:
|
|
98
|
+
setErrors: v,
|
|
99
|
+
clearErrors: l
|
|
88
100
|
};
|
|
89
101
|
}
|
|
90
|
-
function
|
|
91
|
-
const t =
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
},
|
|
95
|
-
delete
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
function ne(r, e) {
|
|
103
|
+
const t = /* @__PURE__ */ new Map(), s = q(/* @__PURE__ */ new Map()), o = (a) => {
|
|
104
|
+
const u = f(a.path);
|
|
105
|
+
s.set(u, a);
|
|
106
|
+
}, n = (a) => {
|
|
107
|
+
s.delete(a);
|
|
108
|
+
}, h = (a) => {
|
|
109
|
+
var u;
|
|
110
|
+
t.has(a) ? (u = t.get(a)) == null || u.inc() : t.set(a, new ae(() => n(a)));
|
|
111
|
+
}, v = (a) => {
|
|
112
|
+
var u;
|
|
113
|
+
t.has(a) && ((u = t.get(a)) == null || u.dec());
|
|
114
|
+
}, l = (a) => {
|
|
115
|
+
if (!s.has(a)) {
|
|
116
|
+
const g = oe({
|
|
117
|
+
path: a,
|
|
118
|
+
value: U(_(r, "data"), a),
|
|
119
|
+
initialValue: d(() => D(r.initialData, a)),
|
|
120
|
+
errors: d({
|
|
103
121
|
get() {
|
|
104
|
-
return
|
|
122
|
+
return e.errors.value.propertyErrors[a] || [];
|
|
105
123
|
},
|
|
106
|
-
set(
|
|
107
|
-
|
|
124
|
+
set(P) {
|
|
125
|
+
e.errors.value.propertyErrors[a] = P;
|
|
108
126
|
}
|
|
109
127
|
})
|
|
110
128
|
});
|
|
111
|
-
|
|
129
|
+
o(g);
|
|
112
130
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}), n;
|
|
119
|
-
};
|
|
131
|
+
const u = s.get(a);
|
|
132
|
+
return h(a), H(() => {
|
|
133
|
+
v(a);
|
|
134
|
+
}), u;
|
|
135
|
+
}, c = (a) => l(a.path);
|
|
120
136
|
return {
|
|
121
|
-
fields:
|
|
122
|
-
getField:
|
|
123
|
-
registerField:
|
|
124
|
-
deregisterField:
|
|
125
|
-
defineField:
|
|
137
|
+
fields: d(() => [...s.values()]),
|
|
138
|
+
getField: l,
|
|
139
|
+
registerField: o,
|
|
140
|
+
deregisterField: n,
|
|
141
|
+
defineField: c
|
|
126
142
|
};
|
|
127
143
|
}
|
|
128
|
-
function
|
|
129
|
-
const
|
|
144
|
+
function ie(r) {
|
|
145
|
+
const e = d(() => r.fields.value.some((s) => f(s.dirty))), t = d(() => r.fields.value.some((s) => f(s.touched)));
|
|
130
146
|
return {
|
|
131
|
-
isDirty:
|
|
147
|
+
isDirty: e,
|
|
132
148
|
isTouched: t
|
|
133
149
|
};
|
|
134
150
|
}
|
|
135
|
-
function
|
|
136
|
-
return
|
|
137
|
-
(
|
|
151
|
+
function le(r) {
|
|
152
|
+
return r.filter(
|
|
153
|
+
(e, t, s) => s.indexOf(e) === t
|
|
138
154
|
);
|
|
139
155
|
}
|
|
140
|
-
function
|
|
141
|
-
return
|
|
142
|
-
if (!
|
|
156
|
+
function I(...r) {
|
|
157
|
+
return r.slice(1).reduce((e, t) => {
|
|
158
|
+
if (!e && !t)
|
|
143
159
|
return;
|
|
144
|
-
const
|
|
145
|
-
if (!
|
|
160
|
+
const s = ((t == null ? void 0 : t.length) ?? 0) > 0;
|
|
161
|
+
if (!e && ((t == null ? void 0 : t.length) ?? 0) > 0)
|
|
146
162
|
return t;
|
|
147
|
-
if (!
|
|
148
|
-
return
|
|
149
|
-
const
|
|
150
|
-
return
|
|
151
|
-
},
|
|
163
|
+
if (!s)
|
|
164
|
+
return e;
|
|
165
|
+
const o = (e ?? []).concat(t);
|
|
166
|
+
return le(o);
|
|
167
|
+
}, r[0]);
|
|
152
168
|
}
|
|
153
|
-
function
|
|
154
|
-
return
|
|
155
|
-
const
|
|
169
|
+
function ce(...r) {
|
|
170
|
+
return r.map((t) => Object.keys(t)).flat().reduce((t, s) => {
|
|
171
|
+
const o = r.map((n) => n[s]).filter(Boolean);
|
|
156
172
|
return {
|
|
157
173
|
...t,
|
|
158
|
-
[
|
|
174
|
+
[s]: I(...o)
|
|
159
175
|
};
|
|
160
176
|
}, {});
|
|
161
177
|
}
|
|
162
|
-
function
|
|
163
|
-
if (!
|
|
178
|
+
function S(...r) {
|
|
179
|
+
if (!r.length)
|
|
164
180
|
return {
|
|
165
181
|
general: [],
|
|
166
182
|
propertyErrors: {}
|
|
167
183
|
};
|
|
168
|
-
const
|
|
169
|
-
return
|
|
170
|
-
(t,
|
|
171
|
-
general:
|
|
172
|
-
propertyErrors:
|
|
184
|
+
const e = r[0];
|
|
185
|
+
return r.length === 1 ? e : r.slice(1).reduce(
|
|
186
|
+
(t, s) => ({
|
|
187
|
+
general: I(t.general, s.general),
|
|
188
|
+
propertyErrors: ce(t.propertyErrors ?? {}, s.propertyErrors ?? {})
|
|
173
189
|
}),
|
|
174
|
-
|
|
190
|
+
e
|
|
175
191
|
);
|
|
176
192
|
}
|
|
177
|
-
function
|
|
178
|
-
var
|
|
179
|
-
const
|
|
180
|
-
return
|
|
193
|
+
function M(r) {
|
|
194
|
+
var s;
|
|
195
|
+
const e = (((s = r.general) == null ? void 0 : s.length) ?? 0) > 0, t = Object.entries(r.propertyErrors).filter(([, o]) => o == null ? void 0 : o.length).length > 0;
|
|
196
|
+
return e || t;
|
|
181
197
|
}
|
|
182
|
-
function
|
|
183
|
-
const
|
|
184
|
-
const
|
|
198
|
+
function ue(r) {
|
|
199
|
+
const e = r.issues.filter((s) => s.path.length === 0).map((s) => s.message), t = r.issues.filter((s) => s.path.length > 0).reduce((s, o) => {
|
|
200
|
+
const n = o.path.join(".");
|
|
185
201
|
return {
|
|
186
|
-
...
|
|
187
|
-
[
|
|
202
|
+
...s,
|
|
203
|
+
[n]: [...s[n] ?? [], o.message]
|
|
188
204
|
};
|
|
189
205
|
}, {});
|
|
190
206
|
return {
|
|
191
|
-
general:
|
|
207
|
+
general: e,
|
|
192
208
|
propertyErrors: t
|
|
193
209
|
};
|
|
194
210
|
}
|
|
@@ -199,35 +215,35 @@ const m = {
|
|
|
199
215
|
propertyErrors: {}
|
|
200
216
|
}
|
|
201
217
|
};
|
|
202
|
-
class
|
|
203
|
-
constructor(
|
|
204
|
-
this.schema =
|
|
218
|
+
class de {
|
|
219
|
+
constructor(e) {
|
|
220
|
+
this.schema = e;
|
|
205
221
|
}
|
|
206
|
-
async validate(
|
|
222
|
+
async validate(e) {
|
|
207
223
|
if (!this.schema)
|
|
208
224
|
return m;
|
|
209
|
-
const t = await this.schema.safeParseAsync(
|
|
225
|
+
const t = await this.schema.safeParseAsync(e);
|
|
210
226
|
if (t.success)
|
|
211
227
|
return m;
|
|
212
|
-
const
|
|
228
|
+
const s = ue(t.error);
|
|
213
229
|
return {
|
|
214
230
|
isValid: !1,
|
|
215
231
|
errors: {
|
|
216
|
-
general:
|
|
217
|
-
propertyErrors:
|
|
232
|
+
general: s.general ?? [],
|
|
233
|
+
propertyErrors: s.propertyErrors ?? {}
|
|
218
234
|
}
|
|
219
235
|
};
|
|
220
236
|
}
|
|
221
237
|
}
|
|
222
|
-
class
|
|
223
|
-
constructor(
|
|
224
|
-
this.validateFn =
|
|
238
|
+
class fe {
|
|
239
|
+
constructor(e) {
|
|
240
|
+
this.validateFn = e;
|
|
225
241
|
}
|
|
226
|
-
async validate(
|
|
242
|
+
async validate(e) {
|
|
227
243
|
if (!this.validateFn)
|
|
228
244
|
return m;
|
|
229
245
|
try {
|
|
230
|
-
const t = await this.validateFn(
|
|
246
|
+
const t = await this.validateFn(e);
|
|
231
247
|
return t.isValid ? m : t;
|
|
232
248
|
} catch (t) {
|
|
233
249
|
return {
|
|
@@ -240,192 +256,195 @@ class dr {
|
|
|
240
256
|
}
|
|
241
257
|
}
|
|
242
258
|
}
|
|
243
|
-
class
|
|
244
|
-
constructor(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
this.schema =
|
|
259
|
+
class pe {
|
|
260
|
+
constructor(e, t) {
|
|
261
|
+
E(this, "schemaValidator");
|
|
262
|
+
E(this, "functionValidator");
|
|
263
|
+
this.schema = e, this.validateFn = t, this.schemaValidator = new de(this.schema), this.functionValidator = new fe(this.validateFn);
|
|
248
264
|
}
|
|
249
|
-
async validate(
|
|
250
|
-
const [t,
|
|
251
|
-
this.schemaValidator.validate(
|
|
252
|
-
this.functionValidator.validate(
|
|
265
|
+
async validate(e) {
|
|
266
|
+
const [t, s] = await Promise.all([
|
|
267
|
+
this.schemaValidator.validate(e),
|
|
268
|
+
this.functionValidator.validate(e)
|
|
253
269
|
]);
|
|
254
270
|
return {
|
|
255
|
-
isValid: t.isValid &&
|
|
256
|
-
errors:
|
|
271
|
+
isValid: t.isValid && s.isValid,
|
|
272
|
+
errors: S(t.errors, s.errors)
|
|
257
273
|
};
|
|
258
274
|
}
|
|
259
275
|
}
|
|
260
|
-
function b(
|
|
261
|
-
return
|
|
262
|
-
|
|
263
|
-
|
|
276
|
+
function b(r) {
|
|
277
|
+
return d(() => new pe(
|
|
278
|
+
f(r.schema),
|
|
279
|
+
f(r.validateFn)
|
|
264
280
|
));
|
|
265
281
|
}
|
|
266
|
-
function
|
|
267
|
-
const t =
|
|
268
|
-
validators: W([b(
|
|
282
|
+
function he(r, e) {
|
|
283
|
+
const t = R({
|
|
284
|
+
validators: W([b(e)]),
|
|
269
285
|
isValidated: !1,
|
|
270
|
-
errors:
|
|
286
|
+
errors: f(e.errors) ?? m.errors
|
|
271
287
|
});
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
288
|
+
F(() => f(e.errors), async () => {
|
|
289
|
+
if (t.isValidated) {
|
|
290
|
+
const l = await o();
|
|
291
|
+
n(l.errors);
|
|
292
|
+
} else
|
|
293
|
+
n();
|
|
294
|
+
}, { immediate: !0 }), F(
|
|
276
295
|
[() => t.validators],
|
|
277
|
-
async (
|
|
296
|
+
async (l) => {
|
|
278
297
|
if (t.isValidated)
|
|
279
|
-
if (
|
|
280
|
-
const c = await
|
|
298
|
+
if (l) {
|
|
299
|
+
const c = await o();
|
|
281
300
|
t.errors = c.errors;
|
|
282
301
|
} else
|
|
283
302
|
t.errors = m.errors;
|
|
284
303
|
},
|
|
285
304
|
{ immediate: !0 }
|
|
286
|
-
),
|
|
305
|
+
), F(() => r.data, () => {
|
|
287
306
|
t.isValidated && h();
|
|
288
307
|
});
|
|
289
|
-
const
|
|
290
|
-
const c =
|
|
308
|
+
const s = (l) => {
|
|
309
|
+
const c = k(l) ? l : b(l);
|
|
291
310
|
return t.validators.push(c), Q() && X(() => {
|
|
292
311
|
t.validators = t.validators.filter(
|
|
293
|
-
(
|
|
312
|
+
(a) => a !== c
|
|
294
313
|
);
|
|
295
314
|
}), c;
|
|
296
315
|
};
|
|
297
|
-
async function
|
|
298
|
-
const
|
|
299
|
-
t.validators.filter((
|
|
300
|
-
), c =
|
|
301
|
-
let { errors:
|
|
316
|
+
async function o() {
|
|
317
|
+
const l = await Promise.all(
|
|
318
|
+
t.validators.filter((u) => f(u) !== void 0).map((u) => f(u).validate(r.data))
|
|
319
|
+
), c = l.every((u) => u.isValid);
|
|
320
|
+
let { errors: a } = m;
|
|
302
321
|
if (!c) {
|
|
303
|
-
const
|
|
304
|
-
|
|
322
|
+
const u = l.map((g) => g.errors);
|
|
323
|
+
a = S(...u);
|
|
305
324
|
}
|
|
306
325
|
return {
|
|
307
|
-
errors:
|
|
326
|
+
errors: a,
|
|
308
327
|
isValid: c
|
|
309
328
|
};
|
|
310
329
|
}
|
|
311
|
-
const
|
|
312
|
-
t.errors =
|
|
330
|
+
const n = (l = m.errors) => {
|
|
331
|
+
t.errors = S(f(e.errors) ?? m.errors, l);
|
|
313
332
|
}, h = async () => {
|
|
314
|
-
const
|
|
315
|
-
return
|
|
316
|
-
isValid: !
|
|
333
|
+
const l = await o();
|
|
334
|
+
return n(l.errors), t.isValidated = !0, {
|
|
335
|
+
isValid: !M(l.errors),
|
|
317
336
|
errors: t.errors
|
|
318
337
|
};
|
|
319
|
-
},
|
|
338
|
+
}, v = d(() => !M(t.errors));
|
|
320
339
|
return {
|
|
321
340
|
...N(t),
|
|
322
341
|
validateForm: h,
|
|
323
|
-
defineValidator:
|
|
324
|
-
isValid:
|
|
342
|
+
defineValidator: s,
|
|
343
|
+
isValid: v
|
|
325
344
|
};
|
|
326
345
|
}
|
|
327
|
-
class
|
|
328
|
-
constructor(
|
|
329
|
-
this.path =
|
|
346
|
+
class ve {
|
|
347
|
+
constructor(e, t) {
|
|
348
|
+
this.path = e, this.validator = t;
|
|
330
349
|
}
|
|
331
|
-
async validate(
|
|
332
|
-
const t =
|
|
350
|
+
async validate(e) {
|
|
351
|
+
const t = D(e, this.path);
|
|
333
352
|
if (!this.validator)
|
|
334
353
|
return m;
|
|
335
|
-
const
|
|
354
|
+
const s = await this.validator.validate(t);
|
|
336
355
|
return {
|
|
337
|
-
isValid:
|
|
356
|
+
isValid: s.isValid,
|
|
338
357
|
errors: {
|
|
339
|
-
general:
|
|
340
|
-
propertyErrors:
|
|
341
|
-
Object.entries(
|
|
342
|
-
|
|
343
|
-
|
|
358
|
+
general: s.errors.general || [],
|
|
359
|
+
propertyErrors: s.errors.propertyErrors ? Object.fromEntries(
|
|
360
|
+
Object.entries(s.errors.propertyErrors).map(([o, n]) => [
|
|
361
|
+
w(this.path, o),
|
|
362
|
+
n
|
|
344
363
|
])
|
|
345
364
|
) : {}
|
|
346
365
|
}
|
|
347
366
|
};
|
|
348
367
|
}
|
|
349
368
|
}
|
|
350
|
-
function
|
|
351
|
-
const
|
|
369
|
+
function me(r, e, t) {
|
|
370
|
+
const s = U(r.data, e), o = d(() => D(r.initialData.value, e)), n = (i) => ({
|
|
352
371
|
...i,
|
|
353
|
-
path:
|
|
372
|
+
path: d(() => f(i.path).replace(e + ".", "")),
|
|
354
373
|
setData: (p) => {
|
|
355
374
|
i.setData(p);
|
|
356
375
|
}
|
|
357
376
|
}), h = (i) => {
|
|
358
|
-
const p =
|
|
359
|
-
return V ?
|
|
360
|
-
},
|
|
361
|
-
const p =
|
|
377
|
+
const p = w(e, i), V = r.getField(p);
|
|
378
|
+
return V ? n(V) : {};
|
|
379
|
+
}, v = (i) => {
|
|
380
|
+
const p = w(e, i.path), V = r.defineField({
|
|
362
381
|
...i,
|
|
363
382
|
path: p
|
|
364
383
|
});
|
|
365
|
-
return
|
|
366
|
-
},
|
|
384
|
+
return n(V);
|
|
385
|
+
}, l = d(() => r.fields.value.filter((i) => {
|
|
367
386
|
const p = i.path.value;
|
|
368
|
-
return p.startsWith(
|
|
369
|
-
}).map((i) =>
|
|
387
|
+
return p.startsWith(e + ".") || p === e;
|
|
388
|
+
}).map((i) => n(i))), c = () => r.fields.value.filter((i) => {
|
|
370
389
|
const p = i.path.value;
|
|
371
|
-
return p.startsWith(
|
|
372
|
-
}),
|
|
390
|
+
return p.startsWith(e + ".") || p === e;
|
|
391
|
+
}), a = d(() => c().some((i) => i.dirty.value)), u = d(() => c().some((i) => i.touched.value)), g = d(() => r.isValid.value), P = d(() => r.isValidated.value), J = d(() => se(f(r.errors), e));
|
|
373
392
|
return {
|
|
374
|
-
data:
|
|
375
|
-
fields:
|
|
376
|
-
initialData:
|
|
377
|
-
defineField:
|
|
393
|
+
data: s,
|
|
394
|
+
fields: l,
|
|
395
|
+
initialData: o,
|
|
396
|
+
defineField: v,
|
|
378
397
|
getField: h,
|
|
379
|
-
isDirty:
|
|
380
|
-
isTouched:
|
|
381
|
-
isValid:
|
|
382
|
-
isValidated:
|
|
398
|
+
isDirty: a,
|
|
399
|
+
isTouched: u,
|
|
400
|
+
isValid: g,
|
|
401
|
+
isValidated: P,
|
|
383
402
|
errors: J,
|
|
384
403
|
defineValidator: (i) => {
|
|
385
|
-
const p =
|
|
386
|
-
() => new
|
|
404
|
+
const p = k(i) ? i : b(i), V = d(
|
|
405
|
+
() => new ve(e, f(p))
|
|
387
406
|
);
|
|
388
|
-
return
|
|
407
|
+
return r.defineValidator(V), p;
|
|
389
408
|
},
|
|
390
409
|
reset: () => c().forEach((i) => i.reset()),
|
|
391
|
-
validateForm: () =>
|
|
410
|
+
validateForm: () => r.validateForm(),
|
|
392
411
|
getSubForm: (i, p) => {
|
|
393
|
-
const V =
|
|
394
|
-
return
|
|
412
|
+
const V = w(e, i);
|
|
413
|
+
return r.getSubForm(
|
|
395
414
|
V,
|
|
396
415
|
p
|
|
397
416
|
);
|
|
398
417
|
}
|
|
399
418
|
};
|
|
400
419
|
}
|
|
401
|
-
function
|
|
402
|
-
const
|
|
403
|
-
initialData:
|
|
420
|
+
function Pe(r) {
|
|
421
|
+
const e = d(() => Object.freeze(y(r.initialData))), t = W(y(e)), s = R({
|
|
422
|
+
initialData: e,
|
|
404
423
|
data: t
|
|
405
424
|
});
|
|
406
|
-
|
|
407
|
-
|
|
425
|
+
F(e, (a) => {
|
|
426
|
+
s.data = y(a);
|
|
408
427
|
});
|
|
409
|
-
const
|
|
410
|
-
t.value = y(
|
|
411
|
-
(
|
|
428
|
+
const o = he(s, r), n = ne(s, o), h = ie(n), v = () => {
|
|
429
|
+
t.value = y(e), n.fields.value.forEach(
|
|
430
|
+
(a) => a.reset()
|
|
412
431
|
);
|
|
413
432
|
};
|
|
414
|
-
function
|
|
415
|
-
return
|
|
433
|
+
function l(a, u) {
|
|
434
|
+
return me(c, a);
|
|
416
435
|
}
|
|
417
436
|
const c = {
|
|
437
|
+
...n,
|
|
418
438
|
...o,
|
|
419
|
-
...s,
|
|
420
439
|
...h,
|
|
421
|
-
reset:
|
|
422
|
-
getSubForm:
|
|
423
|
-
initialData:
|
|
424
|
-
data:
|
|
440
|
+
reset: v,
|
|
441
|
+
getSubForm: l,
|
|
442
|
+
initialData: _(s, "initialData"),
|
|
443
|
+
data: _(s, "data")
|
|
425
444
|
};
|
|
426
445
|
return c;
|
|
427
446
|
}
|
|
428
|
-
const
|
|
447
|
+
const _e = /* @__PURE__ */ j({
|
|
429
448
|
__name: "Field",
|
|
430
449
|
props: {
|
|
431
450
|
form: {},
|
|
@@ -434,13 +453,13 @@ const Dr = /* @__PURE__ */ S({
|
|
|
434
453
|
path: {},
|
|
435
454
|
errors: {}
|
|
436
455
|
},
|
|
437
|
-
setup(
|
|
438
|
-
const
|
|
439
|
-
path:
|
|
440
|
-
}),
|
|
441
|
-
return (
|
|
456
|
+
setup(r) {
|
|
457
|
+
const e = r, t = e.form.defineField({
|
|
458
|
+
path: e.path
|
|
459
|
+
}), s = R(t);
|
|
460
|
+
return (o, n) => O(o.$slots, "default", z(B(s)));
|
|
442
461
|
}
|
|
443
|
-
}),
|
|
462
|
+
}), Se = /* @__PURE__ */ j({
|
|
444
463
|
inheritAttrs: !1,
|
|
445
464
|
__name: "FormFieldWrapper",
|
|
446
465
|
props: {
|
|
@@ -449,22 +468,22 @@ const Dr = /* @__PURE__ */ S({
|
|
|
449
468
|
form: {},
|
|
450
469
|
path: {}
|
|
451
470
|
},
|
|
452
|
-
setup(
|
|
453
|
-
return (
|
|
454
|
-
const
|
|
455
|
-
return
|
|
456
|
-
form:
|
|
457
|
-
path:
|
|
471
|
+
setup(r) {
|
|
472
|
+
return (e, t) => {
|
|
473
|
+
const s = Y("Field");
|
|
474
|
+
return A(), $(s, {
|
|
475
|
+
form: e.form,
|
|
476
|
+
path: e.path
|
|
458
477
|
}, {
|
|
459
|
-
default:
|
|
460
|
-
(
|
|
461
|
-
"model-value":
|
|
462
|
-
errors:
|
|
463
|
-
name:
|
|
478
|
+
default: C(({ errors: o, data: n, setData: h }) => [
|
|
479
|
+
(A(), $(x(e.component), ee({ ...e.componentProps, ...e.$attrs }, {
|
|
480
|
+
"model-value": n,
|
|
481
|
+
errors: o,
|
|
482
|
+
name: e.path,
|
|
464
483
|
"onUpdate:modelValue": h
|
|
465
484
|
}), {
|
|
466
|
-
default:
|
|
467
|
-
|
|
485
|
+
default: C(() => [
|
|
486
|
+
O(e.$slots, "default")
|
|
468
487
|
]),
|
|
469
488
|
_: 2
|
|
470
489
|
}, 1040, ["model-value", "errors", "name", "onUpdate:modelValue"]))
|
|
@@ -473,20 +492,20 @@ const Dr = /* @__PURE__ */ S({
|
|
|
473
492
|
}, 8, ["form", "path"]);
|
|
474
493
|
};
|
|
475
494
|
}
|
|
476
|
-
}),
|
|
495
|
+
}), be = /* @__PURE__ */ j({
|
|
477
496
|
__name: "FormPart",
|
|
478
497
|
props: {
|
|
479
498
|
form: {},
|
|
480
499
|
path: {}
|
|
481
500
|
},
|
|
482
|
-
setup(
|
|
483
|
-
const
|
|
484
|
-
return (
|
|
501
|
+
setup(r) {
|
|
502
|
+
const e = r, t = d(() => e.form.getSubForm(e.path));
|
|
503
|
+
return (s, o) => O(s.$slots, "default", z(B({ subform: t.value })));
|
|
485
504
|
}
|
|
486
505
|
});
|
|
487
506
|
export {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
507
|
+
_e as Field,
|
|
508
|
+
Se as FormFieldWrapper,
|
|
509
|
+
be as FormPart,
|
|
510
|
+
Pe as useForm
|
|
492
511
|
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { computed,
|
|
1
|
+
import { computed, onScopeDispose, shallowReactive, toRef, unref } from 'vue'
|
|
2
2
|
import type { FieldsTuple, FormDataDefault, FormField } from '../types/form'
|
|
3
3
|
import type { Paths, PickProps } from '../types/util'
|
|
4
4
|
import { getLens, getNestedValue } from '../utils/path'
|
|
5
|
+
import { Rc } from '../utils/rc'
|
|
5
6
|
import { useField, type UseFieldOptions } from './useField'
|
|
6
7
|
import type { ValidationState } from './useValidation'
|
|
7
8
|
|
|
8
9
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
-
type FieldRegistryCache<T> =
|
|
10
|
+
type FieldRegistryCache<T> = Map<Paths<T>, FormField<any, string>>
|
|
10
11
|
|
|
11
12
|
export type ResolvedFormField<T, K extends Paths<T>> = FormField<PickProps<T, K>, K>
|
|
12
13
|
|
|
@@ -21,19 +22,34 @@ export function useFieldRegistry<T extends FormDataDefault>(
|
|
|
21
22
|
formState: FormState<T>,
|
|
22
23
|
validationState: ValidationState<T>,
|
|
23
24
|
) {
|
|
24
|
-
const
|
|
25
|
+
const fieldReferenceCounter = new Map<Paths<T>, Rc>()
|
|
26
|
+
const fields = shallowReactive(new Map()) as FieldRegistryCache<T>
|
|
25
27
|
|
|
26
28
|
const registerField = <K extends Paths<T>>(field: ResolvedFormField<T, K>) => {
|
|
27
29
|
const path = unref(field.path) as Paths<T>
|
|
28
|
-
fields
|
|
30
|
+
fields.set(path, field)
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
const deregisterField = (path: Paths<T>) => {
|
|
32
|
-
delete
|
|
34
|
+
fields.delete(path)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const track = (path: Paths<T>) => {
|
|
38
|
+
if (!fieldReferenceCounter.has(path)) {
|
|
39
|
+
fieldReferenceCounter.set(path, new Rc(() => deregisterField(path)))
|
|
40
|
+
} else {
|
|
41
|
+
fieldReferenceCounter.get(path)?.inc()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const untrack = (path: Paths<T>) => {
|
|
46
|
+
if (fieldReferenceCounter.has(path)) {
|
|
47
|
+
fieldReferenceCounter.get(path)?.dec()
|
|
48
|
+
}
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
const getField = <K extends Paths<T>>(path: K): ResolvedFormField<T, K> => {
|
|
36
|
-
if (!fields
|
|
52
|
+
if (!fields.has(path)) {
|
|
37
53
|
const field = useField({
|
|
38
54
|
path,
|
|
39
55
|
value: getLens(toRef(formState, 'data'), path),
|
|
@@ -49,11 +65,18 @@ export function useFieldRegistry<T extends FormDataDefault>(
|
|
|
49
65
|
})
|
|
50
66
|
|
|
51
67
|
registerField(field)
|
|
52
|
-
|
|
53
|
-
return field
|
|
54
68
|
}
|
|
55
69
|
|
|
56
|
-
|
|
70
|
+
const field = fields.get(path) as ResolvedFormField<T, K>
|
|
71
|
+
|
|
72
|
+
track(path)
|
|
73
|
+
|
|
74
|
+
// Clean up field on unmount
|
|
75
|
+
onScopeDispose(() => {
|
|
76
|
+
untrack(path)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
return field
|
|
57
80
|
}
|
|
58
81
|
|
|
59
82
|
const defineField = <K extends Paths<T>>(options: DefineFieldOptions<PickProps<T, K>, K>): ResolvedFormField<T, K> => {
|
|
@@ -61,17 +84,12 @@ export function useFieldRegistry<T extends FormDataDefault>(
|
|
|
61
84
|
|
|
62
85
|
// TODO: If more options are ever needed than only the path we have to update the field
|
|
63
86
|
// here with the new options
|
|
64
|
-
|
|
65
|
-
onUnmounted(() => {
|
|
66
|
-
// Clean up field on unmount
|
|
67
|
-
deregisterField(unref(field.path))
|
|
68
|
-
})
|
|
69
87
|
|
|
70
88
|
return field
|
|
71
89
|
}
|
|
72
90
|
|
|
73
91
|
return {
|
|
74
|
-
fields: computed(() =>
|
|
92
|
+
fields: computed(() => [...fields.values()] as FieldsTuple<T>),
|
|
75
93
|
getField,
|
|
76
94
|
registerField,
|
|
77
95
|
deregisterField,
|
|
@@ -124,9 +124,13 @@ export function useValidation<T extends FormDataDefault>(
|
|
|
124
124
|
|
|
125
125
|
// Watch for changes in the error bag and update validation state
|
|
126
126
|
watch(() => unref(options.errors), async () => {
|
|
127
|
-
|
|
127
|
+
if (validationState.isValidated) {
|
|
128
|
+
const validationResults = await getValidationResults()
|
|
128
129
|
|
|
129
|
-
|
|
130
|
+
updateErrors(validationResults.errors)
|
|
131
|
+
} else {
|
|
132
|
+
updateErrors()
|
|
133
|
+
}
|
|
130
134
|
}, { immediate: true })
|
|
131
135
|
|
|
132
136
|
// Watch for changes in validation function or schema
|
|
@@ -194,7 +198,7 @@ export function useValidation<T extends FormDataDefault>(
|
|
|
194
198
|
}
|
|
195
199
|
}
|
|
196
200
|
|
|
197
|
-
const updateErrors = (newErrors: ErrorBag) => {
|
|
201
|
+
const updateErrors = (newErrors: ErrorBag = SuccessValidationResult.errors) => {
|
|
198
202
|
validationState.errors = mergeErrors(unref(options.errors) ?? SuccessValidationResult.errors, newErrors)
|
|
199
203
|
}
|
|
200
204
|
|
package/src/utils/rc.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference counter for fields
|
|
3
|
+
*/
|
|
4
|
+
export class Rc {
|
|
5
|
+
private rc: number = 1
|
|
6
|
+
|
|
7
|
+
constructor(private drop?: () => void) {}
|
|
8
|
+
|
|
9
|
+
inc() {
|
|
10
|
+
this.rc += 1
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
dec() {
|
|
14
|
+
if (this.rc > 0) {
|
|
15
|
+
this.rc -= 1
|
|
16
|
+
|
|
17
|
+
if (this.rc === 0 && this.drop) {
|
|
18
|
+
this.drop()
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
package/tests/useForm.test.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import {
|
|
3
|
-
import { useForm } from '../src/composables/useForm'
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { effectScope, isReactive, nextTick, reactive, ref } from 'vue'
|
|
4
3
|
import { z } from 'zod'
|
|
4
|
+
import { useForm } from '../src/composables/useForm'
|
|
5
|
+
|
|
6
|
+
const scope = effectScope()
|
|
5
7
|
|
|
6
8
|
describe('useForm', () => {
|
|
9
|
+
beforeAll(() => {
|
|
10
|
+
|
|
11
|
+
})
|
|
7
12
|
it('should initialize form with initial data', () => {
|
|
8
13
|
const initialData = {
|
|
9
14
|
name: 'John',
|
|
@@ -230,7 +235,6 @@ describe('useForm', () => {
|
|
|
230
235
|
|
|
231
236
|
const nameField = form.defineField({ path: 'name' })
|
|
232
237
|
|
|
233
|
-
|
|
234
238
|
expect(form.fields.value.length).toBe(1)
|
|
235
239
|
expect(form.isDirty.value).toBe(false)
|
|
236
240
|
|
|
@@ -238,4 +242,52 @@ describe('useForm', () => {
|
|
|
238
242
|
|
|
239
243
|
expect(form.isDirty.value).toBe(true)
|
|
240
244
|
})
|
|
245
|
+
|
|
246
|
+
it('fields can be made reactive', async () => {
|
|
247
|
+
const form = useForm({
|
|
248
|
+
initialData: {
|
|
249
|
+
name: 'John',
|
|
250
|
+
age: 30,
|
|
251
|
+
},
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
const nameField = form.defineField({ path: 'name' })
|
|
255
|
+
|
|
256
|
+
expect(isReactive(reactive(nameField))).toBe(true)
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('fields should only be removed when all references are gone', async () => {
|
|
260
|
+
const form = useForm({
|
|
261
|
+
initialData: {
|
|
262
|
+
name: 'John',
|
|
263
|
+
age: 30,
|
|
264
|
+
},
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
expect(form.fields.value).toEqual([])
|
|
268
|
+
|
|
269
|
+
const scope1 = effectScope()
|
|
270
|
+
|
|
271
|
+
scope1.run(() => {
|
|
272
|
+
form.defineField({ path: 'name' })
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
expect(form.fields.value.length).toBe(1)
|
|
276
|
+
|
|
277
|
+
const scope2 = effectScope()
|
|
278
|
+
|
|
279
|
+
scope2.run(() => {
|
|
280
|
+
form.defineField({ path: 'name' })
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
expect(form.fields.value.length).toBe(1)
|
|
284
|
+
|
|
285
|
+
scope1.stop()
|
|
286
|
+
|
|
287
|
+
expect(form.fields.value.length).toBe(1)
|
|
288
|
+
|
|
289
|
+
scope2.stop()
|
|
290
|
+
|
|
291
|
+
expect(form.fields.value.length).toBe(0)
|
|
292
|
+
})
|
|
241
293
|
})
|