@teamnovu/kit-vue-forms 0.1.1 → 0.1.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/dist/composables/useFieldRegistry.d.ts +1 -0
- package/dist/index.js +289 -265
- package/dist/utils/rc.d.ts +7 -0
- package/package.json +1 -1
- package/src/composables/useFieldRegistry.ts +37 -9
- package/src/utils/rc.ts +19 -0
- package/tests/useForm.test.ts +56 -4
|
@@ -13,6 +13,7 @@ export declare function useFieldRegistry<T extends FormDataDefault>(formState: F
|
|
|
13
13
|
fields: ComputedRef<FieldsTuple<T>>;
|
|
14
14
|
getField: <K extends Paths<T>>(path: K) => ResolvedFormField<T, K>;
|
|
15
15
|
registerField: <K extends Paths<T>>(field: ResolvedFormField<T, K>) => void;
|
|
16
|
+
deregisterField: (path: Paths<T>) => void;
|
|
16
17
|
defineField: <K extends Paths<T>>(options: DefineFieldOptions<PickProps<T, K>, K>) => ResolvedFormField<T, K>;
|
|
17
18
|
};
|
|
18
19
|
export type FieldRegistry<T extends FormDataDefault> = ReturnType<typeof useFieldRegistry<T>>;
|
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
|
-
(
|
|
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
|
-
|
|
70
|
-
}, h = (
|
|
71
|
-
|
|
78
|
+
e.touched = !0;
|
|
79
|
+
}, n = () => {
|
|
80
|
+
}, h = () => {
|
|
81
|
+
e.value = y(e.initialValue), e.touched = !1, e.errors = [];
|
|
82
|
+
}, v = (a) => {
|
|
83
|
+
e.errors = a;
|
|
72
84
|
}, l = () => {
|
|
73
|
-
|
|
74
|
-
}, c = N(
|
|
85
|
+
e.errors = [];
|
|
86
|
+
}, c = N(e);
|
|
75
87
|
return {
|
|
76
88
|
data: c.value,
|
|
77
89
|
path: c.path,
|
|
@@ -79,108 +91,120 @@ function ar(e) {
|
|
|
79
91
|
errors: c.errors,
|
|
80
92
|
touched: c.touched,
|
|
81
93
|
dirty: t,
|
|
82
|
-
setData:
|
|
83
|
-
onBlur:
|
|
84
|
-
onFocus:
|
|
85
|
-
reset:
|
|
86
|
-
setErrors:
|
|
94
|
+
setData: s,
|
|
95
|
+
onBlur: o,
|
|
96
|
+
onFocus: n,
|
|
97
|
+
reset: h,
|
|
98
|
+
setErrors: v,
|
|
87
99
|
clearErrors: l
|
|
88
100
|
};
|
|
89
101
|
}
|
|
90
|
-
function
|
|
91
|
-
const t =
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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({
|
|
101
121
|
get() {
|
|
102
|
-
return
|
|
122
|
+
return e.errors.value.propertyErrors[a] || [];
|
|
103
123
|
},
|
|
104
|
-
set(
|
|
105
|
-
|
|
124
|
+
set(P) {
|
|
125
|
+
e.errors.value.propertyErrors[a] = P;
|
|
106
126
|
}
|
|
107
127
|
})
|
|
108
128
|
});
|
|
109
|
-
|
|
129
|
+
o(g);
|
|
110
130
|
}
|
|
111
|
-
|
|
112
|
-
|
|
131
|
+
const u = s.get(a);
|
|
132
|
+
return h(a), H(() => {
|
|
133
|
+
v(a);
|
|
134
|
+
}), u;
|
|
135
|
+
}, c = (a) => l(a.path);
|
|
113
136
|
return {
|
|
114
|
-
fields:
|
|
115
|
-
getField:
|
|
116
|
-
registerField:
|
|
117
|
-
|
|
137
|
+
fields: d(() => [...s.values()]),
|
|
138
|
+
getField: l,
|
|
139
|
+
registerField: o,
|
|
140
|
+
deregisterField: n,
|
|
141
|
+
defineField: c
|
|
118
142
|
};
|
|
119
143
|
}
|
|
120
|
-
function
|
|
121
|
-
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)));
|
|
122
146
|
return {
|
|
123
|
-
isDirty:
|
|
147
|
+
isDirty: e,
|
|
124
148
|
isTouched: t
|
|
125
149
|
};
|
|
126
150
|
}
|
|
127
|
-
function
|
|
128
|
-
return
|
|
129
|
-
(
|
|
151
|
+
function le(r) {
|
|
152
|
+
return r.filter(
|
|
153
|
+
(e, t, s) => s.indexOf(e) === t
|
|
130
154
|
);
|
|
131
155
|
}
|
|
132
|
-
function
|
|
133
|
-
return
|
|
134
|
-
if (!
|
|
156
|
+
function I(...r) {
|
|
157
|
+
return r.slice(1).reduce((e, t) => {
|
|
158
|
+
if (!e && !t)
|
|
135
159
|
return;
|
|
136
|
-
const
|
|
137
|
-
if (!
|
|
160
|
+
const s = ((t == null ? void 0 : t.length) ?? 0) > 0;
|
|
161
|
+
if (!e && ((t == null ? void 0 : t.length) ?? 0) > 0)
|
|
138
162
|
return t;
|
|
139
|
-
if (!
|
|
140
|
-
return
|
|
141
|
-
const
|
|
142
|
-
return
|
|
143
|
-
},
|
|
163
|
+
if (!s)
|
|
164
|
+
return e;
|
|
165
|
+
const o = (e ?? []).concat(t);
|
|
166
|
+
return le(o);
|
|
167
|
+
}, r[0]);
|
|
144
168
|
}
|
|
145
|
-
function
|
|
146
|
-
return
|
|
147
|
-
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);
|
|
148
172
|
return {
|
|
149
173
|
...t,
|
|
150
|
-
[
|
|
174
|
+
[s]: I(...o)
|
|
151
175
|
};
|
|
152
176
|
}, {});
|
|
153
177
|
}
|
|
154
|
-
function
|
|
155
|
-
if (!
|
|
178
|
+
function S(...r) {
|
|
179
|
+
if (!r.length)
|
|
156
180
|
return {
|
|
157
181
|
general: [],
|
|
158
182
|
propertyErrors: {}
|
|
159
183
|
};
|
|
160
|
-
const
|
|
161
|
-
return
|
|
162
|
-
(t,
|
|
163
|
-
general:
|
|
164
|
-
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 ?? {})
|
|
165
189
|
}),
|
|
166
|
-
|
|
190
|
+
e
|
|
167
191
|
);
|
|
168
192
|
}
|
|
169
|
-
function
|
|
170
|
-
var
|
|
171
|
-
const
|
|
172
|
-
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;
|
|
173
197
|
}
|
|
174
|
-
function
|
|
175
|
-
const
|
|
176
|
-
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(".");
|
|
177
201
|
return {
|
|
178
|
-
...
|
|
179
|
-
[
|
|
202
|
+
...s,
|
|
203
|
+
[n]: [...s[n] ?? [], o.message]
|
|
180
204
|
};
|
|
181
205
|
}, {});
|
|
182
206
|
return {
|
|
183
|
-
general:
|
|
207
|
+
general: e,
|
|
184
208
|
propertyErrors: t
|
|
185
209
|
};
|
|
186
210
|
}
|
|
@@ -191,35 +215,35 @@ const m = {
|
|
|
191
215
|
propertyErrors: {}
|
|
192
216
|
}
|
|
193
217
|
};
|
|
194
|
-
class
|
|
195
|
-
constructor(
|
|
196
|
-
this.schema =
|
|
218
|
+
class de {
|
|
219
|
+
constructor(e) {
|
|
220
|
+
this.schema = e;
|
|
197
221
|
}
|
|
198
|
-
async validate(
|
|
222
|
+
async validate(e) {
|
|
199
223
|
if (!this.schema)
|
|
200
224
|
return m;
|
|
201
|
-
const t = await this.schema.safeParseAsync(
|
|
225
|
+
const t = await this.schema.safeParseAsync(e);
|
|
202
226
|
if (t.success)
|
|
203
227
|
return m;
|
|
204
|
-
const
|
|
228
|
+
const s = ue(t.error);
|
|
205
229
|
return {
|
|
206
230
|
isValid: !1,
|
|
207
231
|
errors: {
|
|
208
|
-
general:
|
|
209
|
-
propertyErrors:
|
|
232
|
+
general: s.general ?? [],
|
|
233
|
+
propertyErrors: s.propertyErrors ?? {}
|
|
210
234
|
}
|
|
211
235
|
};
|
|
212
236
|
}
|
|
213
237
|
}
|
|
214
|
-
class
|
|
215
|
-
constructor(
|
|
216
|
-
this.validateFn =
|
|
238
|
+
class fe {
|
|
239
|
+
constructor(e) {
|
|
240
|
+
this.validateFn = e;
|
|
217
241
|
}
|
|
218
|
-
async validate(
|
|
242
|
+
async validate(e) {
|
|
219
243
|
if (!this.validateFn)
|
|
220
244
|
return m;
|
|
221
245
|
try {
|
|
222
|
-
const t = await this.validateFn(
|
|
246
|
+
const t = await this.validateFn(e);
|
|
223
247
|
return t.isValid ? m : t;
|
|
224
248
|
} catch (t) {
|
|
225
249
|
return {
|
|
@@ -232,192 +256,192 @@ class ur {
|
|
|
232
256
|
}
|
|
233
257
|
}
|
|
234
258
|
}
|
|
235
|
-
class
|
|
236
|
-
constructor(
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
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);
|
|
240
264
|
}
|
|
241
|
-
async validate(
|
|
242
|
-
const [t,
|
|
243
|
-
this.schemaValidator.validate(
|
|
244
|
-
this.functionValidator.validate(
|
|
265
|
+
async validate(e) {
|
|
266
|
+
const [t, s] = await Promise.all([
|
|
267
|
+
this.schemaValidator.validate(e),
|
|
268
|
+
this.functionValidator.validate(e)
|
|
245
269
|
]);
|
|
246
270
|
return {
|
|
247
|
-
isValid: t.isValid &&
|
|
248
|
-
errors:
|
|
271
|
+
isValid: t.isValid && s.isValid,
|
|
272
|
+
errors: S(t.errors, s.errors)
|
|
249
273
|
};
|
|
250
274
|
}
|
|
251
275
|
}
|
|
252
|
-
function b(
|
|
253
|
-
return
|
|
254
|
-
|
|
255
|
-
|
|
276
|
+
function b(r) {
|
|
277
|
+
return d(() => new pe(
|
|
278
|
+
f(r.schema),
|
|
279
|
+
f(r.validateFn)
|
|
256
280
|
));
|
|
257
281
|
}
|
|
258
|
-
function
|
|
259
|
-
const t =
|
|
260
|
-
validators: W([b(
|
|
282
|
+
function he(r, e) {
|
|
283
|
+
const t = R({
|
|
284
|
+
validators: W([b(e)]),
|
|
261
285
|
isValidated: !1,
|
|
262
|
-
errors:
|
|
286
|
+
errors: f(e.errors) ?? m.errors
|
|
263
287
|
});
|
|
264
|
-
|
|
265
|
-
const l = await
|
|
266
|
-
|
|
267
|
-
}, { immediate: !0 }),
|
|
288
|
+
F(() => f(e.errors), async () => {
|
|
289
|
+
const l = await o();
|
|
290
|
+
n(l.errors);
|
|
291
|
+
}, { immediate: !0 }), F(
|
|
268
292
|
[() => t.validators],
|
|
269
293
|
async (l) => {
|
|
270
294
|
if (t.isValidated)
|
|
271
295
|
if (l) {
|
|
272
|
-
const c = await
|
|
296
|
+
const c = await o();
|
|
273
297
|
t.errors = c.errors;
|
|
274
298
|
} else
|
|
275
299
|
t.errors = m.errors;
|
|
276
300
|
},
|
|
277
301
|
{ immediate: !0 }
|
|
278
|
-
),
|
|
279
|
-
t.isValidated &&
|
|
302
|
+
), F(() => r.data, () => {
|
|
303
|
+
t.isValidated && h();
|
|
280
304
|
});
|
|
281
|
-
const
|
|
282
|
-
const c =
|
|
283
|
-
return t.validators.push(c),
|
|
305
|
+
const s = (l) => {
|
|
306
|
+
const c = k(l) ? l : b(l);
|
|
307
|
+
return t.validators.push(c), Q() && X(() => {
|
|
284
308
|
t.validators = t.validators.filter(
|
|
285
|
-
(
|
|
309
|
+
(a) => a !== c
|
|
286
310
|
);
|
|
287
311
|
}), c;
|
|
288
312
|
};
|
|
289
|
-
async function
|
|
313
|
+
async function o() {
|
|
290
314
|
const l = await Promise.all(
|
|
291
|
-
t.validators.filter((
|
|
292
|
-
), c = l.every((
|
|
293
|
-
let { errors:
|
|
315
|
+
t.validators.filter((u) => f(u) !== void 0).map((u) => f(u).validate(r.data))
|
|
316
|
+
), c = l.every((u) => u.isValid);
|
|
317
|
+
let { errors: a } = m;
|
|
294
318
|
if (!c) {
|
|
295
|
-
const
|
|
296
|
-
|
|
319
|
+
const u = l.map((g) => g.errors);
|
|
320
|
+
a = S(...u);
|
|
297
321
|
}
|
|
298
322
|
return {
|
|
299
|
-
errors:
|
|
323
|
+
errors: a,
|
|
300
324
|
isValid: c
|
|
301
325
|
};
|
|
302
326
|
}
|
|
303
|
-
const
|
|
304
|
-
t.errors =
|
|
305
|
-
},
|
|
306
|
-
const l = await
|
|
307
|
-
return
|
|
308
|
-
isValid: !
|
|
327
|
+
const n = (l) => {
|
|
328
|
+
t.errors = S(f(e.errors) ?? m.errors, l);
|
|
329
|
+
}, h = async () => {
|
|
330
|
+
const l = await o();
|
|
331
|
+
return n(l.errors), t.isValidated = !0, {
|
|
332
|
+
isValid: !M(l.errors),
|
|
309
333
|
errors: t.errors
|
|
310
334
|
};
|
|
311
|
-
},
|
|
335
|
+
}, v = d(() => !M(t.errors));
|
|
312
336
|
return {
|
|
313
337
|
...N(t),
|
|
314
|
-
validateForm:
|
|
315
|
-
defineValidator:
|
|
316
|
-
isValid:
|
|
338
|
+
validateForm: h,
|
|
339
|
+
defineValidator: s,
|
|
340
|
+
isValid: v
|
|
317
341
|
};
|
|
318
342
|
}
|
|
319
|
-
class
|
|
320
|
-
constructor(
|
|
321
|
-
this.path =
|
|
343
|
+
class ve {
|
|
344
|
+
constructor(e, t) {
|
|
345
|
+
this.path = e, this.validator = t;
|
|
322
346
|
}
|
|
323
|
-
async validate(
|
|
324
|
-
const t =
|
|
347
|
+
async validate(e) {
|
|
348
|
+
const t = D(e, this.path);
|
|
325
349
|
if (!this.validator)
|
|
326
350
|
return m;
|
|
327
|
-
const
|
|
351
|
+
const s = await this.validator.validate(t);
|
|
328
352
|
return {
|
|
329
|
-
isValid:
|
|
353
|
+
isValid: s.isValid,
|
|
330
354
|
errors: {
|
|
331
|
-
general:
|
|
332
|
-
propertyErrors:
|
|
333
|
-
Object.entries(
|
|
334
|
-
|
|
335
|
-
|
|
355
|
+
general: s.errors.general || [],
|
|
356
|
+
propertyErrors: s.errors.propertyErrors ? Object.fromEntries(
|
|
357
|
+
Object.entries(s.errors.propertyErrors).map(([o, n]) => [
|
|
358
|
+
w(this.path, o),
|
|
359
|
+
n
|
|
336
360
|
])
|
|
337
361
|
) : {}
|
|
338
362
|
}
|
|
339
363
|
};
|
|
340
364
|
}
|
|
341
365
|
}
|
|
342
|
-
function
|
|
343
|
-
const
|
|
344
|
-
...
|
|
345
|
-
path:
|
|
366
|
+
function me(r, e, t) {
|
|
367
|
+
const s = U(r.data, e), o = d(() => D(r.initialData.value, e)), n = (i) => ({
|
|
368
|
+
...i,
|
|
369
|
+
path: d(() => f(i.path).replace(e + ".", "")),
|
|
346
370
|
setData: (p) => {
|
|
347
|
-
|
|
371
|
+
i.setData(p);
|
|
348
372
|
}
|
|
349
|
-
}),
|
|
350
|
-
const p =
|
|
351
|
-
return V ?
|
|
352
|
-
},
|
|
353
|
-
const p =
|
|
354
|
-
...
|
|
373
|
+
}), h = (i) => {
|
|
374
|
+
const p = w(e, i), V = r.getField(p);
|
|
375
|
+
return V ? n(V) : {};
|
|
376
|
+
}, v = (i) => {
|
|
377
|
+
const p = w(e, i.path), V = r.defineField({
|
|
378
|
+
...i,
|
|
355
379
|
path: p
|
|
356
380
|
});
|
|
357
|
-
return
|
|
358
|
-
}, l =
|
|
359
|
-
const p =
|
|
360
|
-
return p.startsWith(
|
|
361
|
-
}).map((
|
|
362
|
-
const p =
|
|
363
|
-
return p.startsWith(
|
|
364
|
-
}),
|
|
381
|
+
return n(V);
|
|
382
|
+
}, l = d(() => r.fields.value.filter((i) => {
|
|
383
|
+
const p = i.path.value;
|
|
384
|
+
return p.startsWith(e + ".") || p === e;
|
|
385
|
+
}).map((i) => n(i))), c = () => r.fields.value.filter((i) => {
|
|
386
|
+
const p = i.path.value;
|
|
387
|
+
return p.startsWith(e + ".") || p === e;
|
|
388
|
+
}), 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));
|
|
365
389
|
return {
|
|
366
|
-
data:
|
|
390
|
+
data: s,
|
|
367
391
|
fields: l,
|
|
368
|
-
initialData:
|
|
369
|
-
defineField:
|
|
370
|
-
getField:
|
|
371
|
-
isDirty:
|
|
372
|
-
isTouched:
|
|
373
|
-
isValid:
|
|
374
|
-
isValidated:
|
|
392
|
+
initialData: o,
|
|
393
|
+
defineField: v,
|
|
394
|
+
getField: h,
|
|
395
|
+
isDirty: a,
|
|
396
|
+
isTouched: u,
|
|
397
|
+
isValid: g,
|
|
398
|
+
isValidated: P,
|
|
375
399
|
errors: J,
|
|
376
|
-
defineValidator: (
|
|
377
|
-
const p =
|
|
378
|
-
() => new
|
|
400
|
+
defineValidator: (i) => {
|
|
401
|
+
const p = k(i) ? i : b(i), V = d(
|
|
402
|
+
() => new ve(e, f(p))
|
|
379
403
|
);
|
|
380
|
-
return
|
|
404
|
+
return r.defineValidator(V), p;
|
|
381
405
|
},
|
|
382
|
-
reset: () => c().forEach((
|
|
383
|
-
validateForm: () =>
|
|
384
|
-
getSubForm: (
|
|
385
|
-
const V =
|
|
386
|
-
return
|
|
406
|
+
reset: () => c().forEach((i) => i.reset()),
|
|
407
|
+
validateForm: () => r.validateForm(),
|
|
408
|
+
getSubForm: (i, p) => {
|
|
409
|
+
const V = w(e, i);
|
|
410
|
+
return r.getSubForm(
|
|
387
411
|
V,
|
|
388
412
|
p
|
|
389
413
|
);
|
|
390
414
|
}
|
|
391
415
|
};
|
|
392
416
|
}
|
|
393
|
-
function
|
|
394
|
-
const
|
|
395
|
-
initialData:
|
|
417
|
+
function Pe(r) {
|
|
418
|
+
const e = d(() => Object.freeze(y(r.initialData))), t = W(y(e)), s = R({
|
|
419
|
+
initialData: e,
|
|
396
420
|
data: t
|
|
397
421
|
});
|
|
398
|
-
|
|
399
|
-
|
|
422
|
+
F(e, (a) => {
|
|
423
|
+
s.data = y(a);
|
|
400
424
|
});
|
|
401
|
-
const
|
|
402
|
-
t.value = y(
|
|
403
|
-
(
|
|
425
|
+
const o = he(s, r), n = ne(s, o), h = ie(n), v = () => {
|
|
426
|
+
t.value = y(e), n.fields.value.forEach(
|
|
427
|
+
(a) => a.reset()
|
|
404
428
|
);
|
|
405
429
|
};
|
|
406
|
-
function l(
|
|
407
|
-
return
|
|
430
|
+
function l(a, u) {
|
|
431
|
+
return me(c, a);
|
|
408
432
|
}
|
|
409
433
|
const c = {
|
|
434
|
+
...n,
|
|
410
435
|
...o,
|
|
411
|
-
...
|
|
412
|
-
|
|
413
|
-
reset: h,
|
|
436
|
+
...h,
|
|
437
|
+
reset: v,
|
|
414
438
|
getSubForm: l,
|
|
415
|
-
initialData:
|
|
416
|
-
data:
|
|
439
|
+
initialData: _(s, "initialData"),
|
|
440
|
+
data: _(s, "data")
|
|
417
441
|
};
|
|
418
442
|
return c;
|
|
419
443
|
}
|
|
420
|
-
const
|
|
444
|
+
const _e = /* @__PURE__ */ j({
|
|
421
445
|
__name: "Field",
|
|
422
446
|
props: {
|
|
423
447
|
form: {},
|
|
@@ -426,13 +450,13 @@ const Rr = /* @__PURE__ */ S({
|
|
|
426
450
|
path: {},
|
|
427
451
|
errors: {}
|
|
428
452
|
},
|
|
429
|
-
setup(
|
|
430
|
-
const
|
|
431
|
-
path:
|
|
432
|
-
}),
|
|
433
|
-
return (
|
|
453
|
+
setup(r) {
|
|
454
|
+
const e = r, t = e.form.defineField({
|
|
455
|
+
path: e.path
|
|
456
|
+
}), s = R(t);
|
|
457
|
+
return (o, n) => O(o.$slots, "default", z(B(s)));
|
|
434
458
|
}
|
|
435
|
-
}),
|
|
459
|
+
}), Se = /* @__PURE__ */ j({
|
|
436
460
|
inheritAttrs: !1,
|
|
437
461
|
__name: "FormFieldWrapper",
|
|
438
462
|
props: {
|
|
@@ -441,22 +465,22 @@ const Rr = /* @__PURE__ */ S({
|
|
|
441
465
|
form: {},
|
|
442
466
|
path: {}
|
|
443
467
|
},
|
|
444
|
-
setup(
|
|
445
|
-
return (
|
|
446
|
-
const
|
|
447
|
-
return
|
|
448
|
-
form:
|
|
449
|
-
path:
|
|
468
|
+
setup(r) {
|
|
469
|
+
return (e, t) => {
|
|
470
|
+
const s = Y("Field");
|
|
471
|
+
return A(), $(s, {
|
|
472
|
+
form: e.form,
|
|
473
|
+
path: e.path
|
|
450
474
|
}, {
|
|
451
|
-
default:
|
|
452
|
-
(
|
|
453
|
-
"model-value":
|
|
454
|
-
errors:
|
|
455
|
-
name:
|
|
456
|
-
"onUpdate:modelValue":
|
|
475
|
+
default: C(({ errors: o, data: n, setData: h }) => [
|
|
476
|
+
(A(), $(x(e.component), ee({ ...e.componentProps, ...e.$attrs }, {
|
|
477
|
+
"model-value": n,
|
|
478
|
+
errors: o,
|
|
479
|
+
name: e.path,
|
|
480
|
+
"onUpdate:modelValue": h
|
|
457
481
|
}), {
|
|
458
|
-
default:
|
|
459
|
-
|
|
482
|
+
default: C(() => [
|
|
483
|
+
O(e.$slots, "default")
|
|
460
484
|
]),
|
|
461
485
|
_: 2
|
|
462
486
|
}, 1040, ["model-value", "errors", "name", "onUpdate:modelValue"]))
|
|
@@ -465,20 +489,20 @@ const Rr = /* @__PURE__ */ S({
|
|
|
465
489
|
}, 8, ["form", "path"]);
|
|
466
490
|
};
|
|
467
491
|
}
|
|
468
|
-
}),
|
|
492
|
+
}), be = /* @__PURE__ */ j({
|
|
469
493
|
__name: "FormPart",
|
|
470
494
|
props: {
|
|
471
495
|
form: {},
|
|
472
496
|
path: {}
|
|
473
497
|
},
|
|
474
|
-
setup(
|
|
475
|
-
const
|
|
476
|
-
return (
|
|
498
|
+
setup(r) {
|
|
499
|
+
const e = r, t = d(() => e.form.getSubForm(e.path));
|
|
500
|
+
return (s, o) => O(s.$slots, "default", z(B({ subform: t.value })));
|
|
477
501
|
}
|
|
478
502
|
});
|
|
479
503
|
export {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
504
|
+
_e as Field,
|
|
505
|
+
Se as FormFieldWrapper,
|
|
506
|
+
be as FormPart,
|
|
507
|
+
Pe as useForm
|
|
484
508
|
};
|
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,15 +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)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const deregisterField = (path: Paths<T>) => {
|
|
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
|
+
}
|
|
29
49
|
}
|
|
30
50
|
|
|
31
51
|
const getField = <K extends Paths<T>>(path: K): ResolvedFormField<T, K> => {
|
|
32
|
-
if (!fields
|
|
52
|
+
if (!fields.has(path)) {
|
|
33
53
|
const field = useField({
|
|
34
54
|
path,
|
|
35
55
|
value: getLens(toRef(formState, 'data'), path),
|
|
@@ -45,11 +65,18 @@ export function useFieldRegistry<T extends FormDataDefault>(
|
|
|
45
65
|
})
|
|
46
66
|
|
|
47
67
|
registerField(field)
|
|
48
|
-
|
|
49
|
-
return field
|
|
50
68
|
}
|
|
51
69
|
|
|
52
|
-
|
|
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
|
|
53
80
|
}
|
|
54
81
|
|
|
55
82
|
const defineField = <K extends Paths<T>>(options: DefineFieldOptions<PickProps<T, K>, K>): ResolvedFormField<T, K> => {
|
|
@@ -62,9 +89,10 @@ export function useFieldRegistry<T extends FormDataDefault>(
|
|
|
62
89
|
}
|
|
63
90
|
|
|
64
91
|
return {
|
|
65
|
-
fields: computed(() =>
|
|
92
|
+
fields: computed(() => [...fields.values()] as FieldsTuple<T>),
|
|
66
93
|
getField,
|
|
67
94
|
registerField,
|
|
95
|
+
deregisterField,
|
|
68
96
|
defineField,
|
|
69
97
|
}
|
|
70
98
|
}
|
package/src/utils/rc.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class Rc {
|
|
2
|
+
private rc: number = 1
|
|
3
|
+
|
|
4
|
+
constructor(private drop?: () => void) {}
|
|
5
|
+
|
|
6
|
+
inc() {
|
|
7
|
+
this.rc += 1
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
dec() {
|
|
11
|
+
if (this.rc > 0) {
|
|
12
|
+
this.rc -= 1
|
|
13
|
+
|
|
14
|
+
if (this.rc === 0 && this.drop) {
|
|
15
|
+
this.drop()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
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
|
})
|