@teamnovu/kit-vue-forms 0.1.23 → 0.1.25
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 +190 -184
- package/dist/types/form.d.ts +2 -1
- package/package.json +1 -1
- package/src/composables/useFieldArray.ts +14 -0
- package/src/types/form.ts +2 -1
- package/tests/useFieldArray.test.ts +243 -136
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var Q = Object.defineProperty;
|
|
2
2
|
var X = (r, t, e) => t in r ? Q(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
|
|
3
|
-
var
|
|
4
|
-
import { toValue as Y, toRaw as ee, unref as
|
|
3
|
+
var D = (r, t, e) => X(r, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
import { toValue as Y, toRaw as ee, unref as h, shallowRef as $, watch as R, computed as v, isRef as C, reactive as k, toRefs as K, shallowReactive as te, toRef as j, onScopeDispose as re, triggerRef as ae, ref as J, getCurrentScope as se, onBeforeUnmount as ne, defineComponent as T, renderSlot as b, normalizeProps as x, guardReactiveProps as N, resolveComponent as ie, createBlock as z, openBlock as H, withCtx as U, resolveDynamicComponent as oe, mergeProps as le, createSlots as ce, renderList as ue } from "vue";
|
|
5
5
|
import { cloneDeep as de } from "lodash-es";
|
|
6
6
|
import "zod";
|
|
7
7
|
function g(r) {
|
|
@@ -10,14 +10,14 @@ function g(r) {
|
|
|
10
10
|
}
|
|
11
11
|
function L(r, t) {
|
|
12
12
|
return (e) => async (a) => {
|
|
13
|
-
a.preventDefault(),
|
|
13
|
+
a.preventDefault(), h(t.validationStrategy) !== "none" && await r.validateForm(), r.isValid.value && await e(r.data.value);
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
class fe {
|
|
17
17
|
constructor(t = (e) => e) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
D(this, "weakMap", /* @__PURE__ */ new WeakMap());
|
|
19
|
+
D(this, "map", /* @__PURE__ */ new Map());
|
|
20
|
+
D(this, "hashFn");
|
|
21
21
|
this.hashFn = t;
|
|
22
22
|
}
|
|
23
23
|
isReferenceType(t) {
|
|
@@ -39,16 +39,16 @@ class fe {
|
|
|
39
39
|
function he(r, t, e) {
|
|
40
40
|
const a = /* @__PURE__ */ new Set();
|
|
41
41
|
return t.map((s, n) => {
|
|
42
|
-
const d = [...r.get(s) ?? []],
|
|
42
|
+
const d = [...r.get(s) ?? []], m = d.findIndex((f) => !a.has(f)), c = (m === -1 ? [] : d.slice(m))[0];
|
|
43
43
|
if (c)
|
|
44
44
|
return a.add(c), {
|
|
45
45
|
id: c,
|
|
46
46
|
item: s,
|
|
47
47
|
path: `${e}.${n}`
|
|
48
48
|
};
|
|
49
|
-
const
|
|
50
|
-
return r.set(s, d.concat([
|
|
51
|
-
id:
|
|
49
|
+
const i = crypto.randomUUID();
|
|
50
|
+
return r.set(s, d.concat([i])), a.add(i), {
|
|
51
|
+
id: i,
|
|
52
52
|
item: s,
|
|
53
53
|
path: `${e}.${n}`
|
|
54
54
|
};
|
|
@@ -56,10 +56,10 @@ function he(r, t, e) {
|
|
|
56
56
|
}
|
|
57
57
|
function G(r, t, e) {
|
|
58
58
|
const a = new fe(e == null ? void 0 : e.hashFn), s = r.getField(t), n = $([]);
|
|
59
|
-
return
|
|
59
|
+
return R(
|
|
60
60
|
s.data,
|
|
61
|
-
(
|
|
62
|
-
n.value = he(a,
|
|
61
|
+
(c) => {
|
|
62
|
+
n.value = he(a, c, t);
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
65
|
immediate: !0,
|
|
@@ -67,18 +67,24 @@ function G(r, t, e) {
|
|
|
67
67
|
}
|
|
68
68
|
), {
|
|
69
69
|
items: n,
|
|
70
|
-
push: (
|
|
71
|
-
const
|
|
72
|
-
return s.setData([...
|
|
70
|
+
push: (c) => {
|
|
71
|
+
const i = s.data.value ?? [];
|
|
72
|
+
return s.setData([...i, c]), n.value.at(-1);
|
|
73
73
|
},
|
|
74
|
-
remove: (
|
|
75
|
-
const
|
|
76
|
-
({ id:
|
|
74
|
+
remove: (c) => {
|
|
75
|
+
const i = s.data.value ?? [], f = n.value.findIndex(
|
|
76
|
+
({ id: l }) => l === c
|
|
77
77
|
);
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
f !== -1 && s.setData(
|
|
79
|
+
i.slice(0, f).concat(i.slice(f + 1))
|
|
80
80
|
);
|
|
81
81
|
},
|
|
82
|
+
insert: (c, i) => {
|
|
83
|
+
const f = s.data.value ?? [];
|
|
84
|
+
return s.setData(
|
|
85
|
+
f.slice(0, i).concat([c]).concat(f.slice(i))
|
|
86
|
+
), n.value[i];
|
|
87
|
+
},
|
|
82
88
|
field: s
|
|
83
89
|
};
|
|
84
90
|
}
|
|
@@ -99,23 +105,23 @@ function ve(r, t, e) {
|
|
|
99
105
|
const a = Array.isArray(t) ? t : W(t), s = a.at(-1);
|
|
100
106
|
if (s) {
|
|
101
107
|
const n = a.slice(0, -1).reduce(
|
|
102
|
-
(d,
|
|
108
|
+
(d, m) => ((d == null ? void 0 : d[m]) === void 0 && (d[m] = {}), d == null ? void 0 : d[m]),
|
|
103
109
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
104
|
-
|
|
110
|
+
h(r)
|
|
105
111
|
);
|
|
106
112
|
n[s] = e;
|
|
107
113
|
} else {
|
|
108
|
-
if (!
|
|
114
|
+
if (!C(r))
|
|
109
115
|
return;
|
|
110
116
|
r.value = e;
|
|
111
117
|
}
|
|
112
118
|
}
|
|
113
|
-
const Z = (r, t) =>
|
|
119
|
+
const Z = (r, t) => v({
|
|
114
120
|
get() {
|
|
115
|
-
return S(
|
|
121
|
+
return S(h(r), h(t));
|
|
116
122
|
},
|
|
117
123
|
set(e) {
|
|
118
|
-
ve(r,
|
|
124
|
+
ve(r, h(t), e);
|
|
119
125
|
}
|
|
120
126
|
});
|
|
121
127
|
function I(r, t) {
|
|
@@ -138,7 +144,7 @@ function me(r, t) {
|
|
|
138
144
|
}
|
|
139
145
|
class Ve {
|
|
140
146
|
constructor(t) {
|
|
141
|
-
|
|
147
|
+
D(this, "rc", 1);
|
|
142
148
|
this.drop = t;
|
|
143
149
|
}
|
|
144
150
|
inc() {
|
|
@@ -161,44 +167,44 @@ function Fe(r) {
|
|
|
161
167
|
errors: e.errors,
|
|
162
168
|
touched: !1
|
|
163
169
|
});
|
|
164
|
-
|
|
170
|
+
R(
|
|
165
171
|
$(e.initialValue),
|
|
166
172
|
() => {
|
|
167
|
-
a.value = Object.freeze(g(e.initialValue)), s.value !==
|
|
173
|
+
a.value = Object.freeze(g(e.initialValue)), s.value !== h(e.initialValue) && (s.value = g(e.initialValue));
|
|
168
174
|
},
|
|
169
175
|
{ flush: "sync" }
|
|
170
176
|
);
|
|
171
|
-
const n =
|
|
172
|
-
s.value =
|
|
173
|
-
},
|
|
174
|
-
var
|
|
175
|
-
s.touched = !0, s.errors = [], (
|
|
176
|
-
},
|
|
177
|
-
var
|
|
178
|
-
(
|
|
177
|
+
const n = v(() => JSON.stringify(s.value) !== JSON.stringify(s.initialValue)), d = (p) => {
|
|
178
|
+
s.value = p;
|
|
179
|
+
}, m = () => {
|
|
180
|
+
var p;
|
|
181
|
+
s.touched = !0, s.errors = [], (p = e.onBlur) == null || p.call(e);
|
|
182
|
+
}, F = () => {
|
|
183
|
+
var p;
|
|
184
|
+
(p = e.onFocus) == null || p.call(e);
|
|
179
185
|
}, c = () => {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
},
|
|
183
|
-
n.value || d(g(
|
|
184
|
-
},
|
|
185
|
-
s.errors =
|
|
186
|
+
const p = s.path.split(".").at(-1) || "";
|
|
187
|
+
h(e.existsInForm) && !/^\d+$/.test(p) && (s.value = g(s.initialValue)), s.touched = !1, s.errors = [];
|
|
188
|
+
}, i = (p) => {
|
|
189
|
+
n.value || d(g(p)), s.initialValue = p;
|
|
190
|
+
}, f = (p) => {
|
|
191
|
+
s.errors = p;
|
|
186
192
|
}, l = () => {
|
|
187
193
|
s.errors = [];
|
|
188
|
-
},
|
|
194
|
+
}, o = K(s);
|
|
189
195
|
return {
|
|
190
|
-
data:
|
|
191
|
-
path:
|
|
192
|
-
initialValue:
|
|
193
|
-
errors:
|
|
194
|
-
touched:
|
|
196
|
+
data: o.value,
|
|
197
|
+
path: o.path,
|
|
198
|
+
initialValue: o.initialValue,
|
|
199
|
+
errors: o.errors,
|
|
200
|
+
touched: o.touched,
|
|
195
201
|
dirty: n,
|
|
196
202
|
setData: d,
|
|
197
|
-
setInitialData:
|
|
198
|
-
onBlur:
|
|
199
|
-
onFocus:
|
|
203
|
+
setInitialData: i,
|
|
204
|
+
onBlur: m,
|
|
205
|
+
onFocus: F,
|
|
200
206
|
reset: c,
|
|
201
|
-
setErrors:
|
|
207
|
+
setErrors: f,
|
|
202
208
|
clearErrors: l
|
|
203
209
|
};
|
|
204
210
|
}
|
|
@@ -207,7 +213,7 @@ const ye = {
|
|
|
207
213
|
};
|
|
208
214
|
function ge(r, t) {
|
|
209
215
|
const e = () => S(r.initialData, t), a = $(e());
|
|
210
|
-
return
|
|
216
|
+
return R(
|
|
211
217
|
() => r.initialData,
|
|
212
218
|
() => {
|
|
213
219
|
a.value = e(), ae(a);
|
|
@@ -220,71 +226,71 @@ function Ee(r, t, e) {
|
|
|
220
226
|
...ye,
|
|
221
227
|
...e
|
|
222
228
|
}, d = (l) => {
|
|
223
|
-
const
|
|
224
|
-
s.set(
|
|
225
|
-
},
|
|
226
|
-
var
|
|
227
|
-
n != null && n.keepValuesOnUnmount || (
|
|
228
|
-
},
|
|
229
|
-
var
|
|
230
|
-
a.has(l) ? (
|
|
229
|
+
const o = h(l.path);
|
|
230
|
+
s.set(o, l);
|
|
231
|
+
}, m = (l) => {
|
|
232
|
+
var o;
|
|
233
|
+
n != null && n.keepValuesOnUnmount || (o = s.get(l)) == null || o.reset(), s.delete(l);
|
|
234
|
+
}, F = (l) => {
|
|
235
|
+
var o;
|
|
236
|
+
a.has(l) ? (o = a.get(l)) == null || o.inc() : a.set(l, new Ve(() => m(l)));
|
|
231
237
|
}, c = (l) => {
|
|
232
|
-
var
|
|
233
|
-
a.has(l) && ((
|
|
234
|
-
},
|
|
235
|
-
const { path:
|
|
236
|
-
if (!s.has(
|
|
238
|
+
var o;
|
|
239
|
+
a.has(l) && ((o = a.get(l)) == null || o.dec());
|
|
240
|
+
}, i = (l) => {
|
|
241
|
+
const { path: o } = l;
|
|
242
|
+
if (!s.has(o)) {
|
|
237
243
|
const A = Fe({
|
|
238
|
-
path:
|
|
239
|
-
value: Z(j(r, "data"),
|
|
240
|
-
initialValue: ge(r,
|
|
241
|
-
existsInForm:
|
|
242
|
-
errors:
|
|
244
|
+
path: o,
|
|
245
|
+
value: Z(j(r, "data"), o),
|
|
246
|
+
initialValue: ge(r, o),
|
|
247
|
+
existsInForm: v(() => pe(r.data, h(o))),
|
|
248
|
+
errors: v({
|
|
243
249
|
get() {
|
|
244
|
-
return t.errors.value.propertyErrors[
|
|
250
|
+
return t.errors.value.propertyErrors[o] || [];
|
|
245
251
|
},
|
|
246
252
|
set(E) {
|
|
247
|
-
t.errors.value.propertyErrors[
|
|
253
|
+
t.errors.value.propertyErrors[o] = E;
|
|
248
254
|
}
|
|
249
255
|
}),
|
|
250
256
|
onBlur: async () => {
|
|
251
257
|
var E, P;
|
|
252
258
|
await Promise.all([
|
|
253
|
-
(E = n == null ? void 0 : n.onBlur) == null ? void 0 : E.call(n,
|
|
259
|
+
(E = n == null ? void 0 : n.onBlur) == null ? void 0 : E.call(n, h(o)),
|
|
254
260
|
(P = l.onBlur) == null ? void 0 : P.call(l)
|
|
255
261
|
]);
|
|
256
262
|
},
|
|
257
263
|
onFocus: async () => {
|
|
258
264
|
var E, P;
|
|
259
265
|
await Promise.all([
|
|
260
|
-
(E = n == null ? void 0 : n.onFocus) == null ? void 0 : E.call(n,
|
|
266
|
+
(E = n == null ? void 0 : n.onFocus) == null ? void 0 : E.call(n, h(o)),
|
|
261
267
|
(P = l.onFocus) == null ? void 0 : P.call(l)
|
|
262
268
|
]);
|
|
263
269
|
}
|
|
264
270
|
});
|
|
265
271
|
d(A);
|
|
266
272
|
}
|
|
267
|
-
const
|
|
268
|
-
return
|
|
269
|
-
c(
|
|
270
|
-
}),
|
|
271
|
-
},
|
|
273
|
+
const p = s.get(o);
|
|
274
|
+
return F(o), re(() => {
|
|
275
|
+
c(o);
|
|
276
|
+
}), p;
|
|
277
|
+
}, f = (l) => i(l);
|
|
272
278
|
return {
|
|
273
|
-
fields:
|
|
274
|
-
getField: (l) =>
|
|
279
|
+
fields: v(() => [...s.values()]),
|
|
280
|
+
getField: (l) => i({ path: l }),
|
|
275
281
|
registerField: d,
|
|
276
|
-
deregisterField:
|
|
277
|
-
defineField:
|
|
282
|
+
deregisterField: m,
|
|
283
|
+
defineField: f
|
|
278
284
|
};
|
|
279
285
|
}
|
|
280
286
|
function we(r) {
|
|
281
|
-
const t =
|
|
287
|
+
const t = v(() => r.fields.value.some((a) => h(a.dirty))), e = v(() => r.fields.value.some((a) => h(a.touched)));
|
|
282
288
|
return {
|
|
283
289
|
isDirty: t,
|
|
284
290
|
isTouched: e
|
|
285
291
|
};
|
|
286
292
|
}
|
|
287
|
-
function
|
|
293
|
+
function De(r) {
|
|
288
294
|
return r.filter(
|
|
289
295
|
(t, e, a) => a.indexOf(t) === e
|
|
290
296
|
);
|
|
@@ -299,10 +305,10 @@ function q(...r) {
|
|
|
299
305
|
if (!a)
|
|
300
306
|
return t;
|
|
301
307
|
const s = (t ?? []).concat(e);
|
|
302
|
-
return
|
|
308
|
+
return De(s);
|
|
303
309
|
}, r[0]);
|
|
304
310
|
}
|
|
305
|
-
function
|
|
311
|
+
function Re(...r) {
|
|
306
312
|
return r.map((e) => Object.keys(e)).flat().reduce((e, a) => {
|
|
307
313
|
const s = r.map((n) => n[a]).filter(Boolean);
|
|
308
314
|
return {
|
|
@@ -321,7 +327,7 @@ function B(...r) {
|
|
|
321
327
|
return r.length === 1 ? t : r.slice(1).reduce(
|
|
322
328
|
(e, a) => ({
|
|
323
329
|
general: q(e.general, a.general),
|
|
324
|
-
propertyErrors:
|
|
330
|
+
propertyErrors: Re(e.propertyErrors ?? {}, a.propertyErrors ?? {})
|
|
325
331
|
}),
|
|
326
332
|
t
|
|
327
333
|
);
|
|
@@ -394,8 +400,8 @@ class Ie {
|
|
|
394
400
|
}
|
|
395
401
|
class $e {
|
|
396
402
|
constructor(t, e) {
|
|
397
|
-
|
|
398
|
-
|
|
403
|
+
D(this, "schemaValidator");
|
|
404
|
+
D(this, "functionValidator");
|
|
399
405
|
this.schema = t, this.validateFn = e, this.schemaValidator = new Se(this.schema), this.functionValidator = new Ie(this.validateFn);
|
|
400
406
|
}
|
|
401
407
|
async validate(t) {
|
|
@@ -410,87 +416,87 @@ class $e {
|
|
|
410
416
|
}
|
|
411
417
|
}
|
|
412
418
|
function O(r) {
|
|
413
|
-
return
|
|
414
|
-
|
|
415
|
-
|
|
419
|
+
return v(() => new $e(
|
|
420
|
+
h(r.schema),
|
|
421
|
+
h(r.validateFn)
|
|
416
422
|
));
|
|
417
423
|
}
|
|
418
424
|
function be(r, t) {
|
|
419
425
|
const e = k({
|
|
420
426
|
validators: J([O(t)]),
|
|
421
427
|
isValidated: !1,
|
|
422
|
-
errors:
|
|
423
|
-
}), a = (
|
|
424
|
-
e.errors = B(
|
|
428
|
+
errors: h(t.errors) ?? y.errors
|
|
429
|
+
}), a = (i = y.errors) => {
|
|
430
|
+
e.errors = B(h(t.errors) ?? y.errors, i);
|
|
425
431
|
};
|
|
426
|
-
|
|
432
|
+
R(() => h(t.errors), async () => {
|
|
427
433
|
if (e.isValidated) {
|
|
428
|
-
const
|
|
429
|
-
a(
|
|
434
|
+
const i = await n();
|
|
435
|
+
a(i.errors);
|
|
430
436
|
} else
|
|
431
437
|
a();
|
|
432
|
-
}, { immediate: !0 }),
|
|
438
|
+
}, { immediate: !0 }), R(
|
|
433
439
|
[() => e.validators],
|
|
434
|
-
async (
|
|
440
|
+
async (i) => {
|
|
435
441
|
if (e.isValidated)
|
|
436
|
-
if (
|
|
437
|
-
const
|
|
438
|
-
e.errors =
|
|
442
|
+
if (i) {
|
|
443
|
+
const f = await n();
|
|
444
|
+
e.errors = f.errors;
|
|
439
445
|
} else
|
|
440
446
|
e.errors = y.errors;
|
|
441
447
|
},
|
|
442
448
|
{ immediate: !0 }
|
|
443
|
-
),
|
|
449
|
+
), R([() => r.data, () => h(t.schema)], () => {
|
|
444
450
|
e.isValidated && d();
|
|
445
451
|
});
|
|
446
|
-
const s = (
|
|
447
|
-
const
|
|
448
|
-
return e.validators.push(
|
|
452
|
+
const s = (i) => {
|
|
453
|
+
const f = C(i) ? i : O(i);
|
|
454
|
+
return e.validators.push(f), se() && ne(() => {
|
|
449
455
|
e.validators = e.validators.filter(
|
|
450
|
-
(l) => l !==
|
|
456
|
+
(l) => l !== f
|
|
451
457
|
);
|
|
452
|
-
}),
|
|
458
|
+
}), f;
|
|
453
459
|
};
|
|
454
460
|
async function n() {
|
|
455
|
-
const
|
|
456
|
-
e.validators.filter((
|
|
457
|
-
),
|
|
461
|
+
const i = await Promise.all(
|
|
462
|
+
e.validators.filter((o) => h(o) !== void 0).map((o) => h(o).validate(r.data))
|
|
463
|
+
), f = i.every((o) => o.isValid);
|
|
458
464
|
let { errors: l } = y;
|
|
459
|
-
if (!
|
|
460
|
-
const
|
|
461
|
-
l = B(...
|
|
465
|
+
if (!f) {
|
|
466
|
+
const o = i.map((p) => p.errors);
|
|
467
|
+
l = B(...o);
|
|
462
468
|
}
|
|
463
469
|
return {
|
|
464
470
|
errors: l,
|
|
465
|
-
isValid:
|
|
471
|
+
isValid: f
|
|
466
472
|
};
|
|
467
473
|
}
|
|
468
474
|
const d = async () => {
|
|
469
|
-
const
|
|
470
|
-
return a(
|
|
471
|
-
isValid: !_(
|
|
475
|
+
const i = await n();
|
|
476
|
+
return a(i.errors), e.isValidated = !0, {
|
|
477
|
+
isValid: !_(i.errors),
|
|
472
478
|
errors: e.errors
|
|
473
479
|
};
|
|
474
|
-
},
|
|
475
|
-
const
|
|
480
|
+
}, m = async (i) => {
|
|
481
|
+
const f = await n();
|
|
476
482
|
return a({
|
|
477
|
-
general:
|
|
483
|
+
general: f.errors.general,
|
|
478
484
|
propertyErrors: {
|
|
479
|
-
[
|
|
485
|
+
[i]: f.errors.propertyErrors[i]
|
|
480
486
|
}
|
|
481
487
|
}), {
|
|
482
|
-
isValid: !_(
|
|
488
|
+
isValid: !_(f.errors),
|
|
483
489
|
errors: e.errors
|
|
484
490
|
};
|
|
485
|
-
},
|
|
486
|
-
e.isValidated = !1, e.errors =
|
|
491
|
+
}, F = v(() => !_(e.errors)), c = () => {
|
|
492
|
+
e.isValidated = !1, e.errors = h(t.errors) ?? y.errors;
|
|
487
493
|
};
|
|
488
494
|
return {
|
|
489
495
|
...K(e),
|
|
490
496
|
validateForm: d,
|
|
491
|
-
validateField:
|
|
497
|
+
validateField: m,
|
|
492
498
|
defineValidator: s,
|
|
493
|
-
isValid:
|
|
499
|
+
isValid: F,
|
|
494
500
|
reset: c
|
|
495
501
|
};
|
|
496
502
|
}
|
|
@@ -517,64 +523,64 @@ class ke {
|
|
|
517
523
|
}
|
|
518
524
|
}
|
|
519
525
|
function Ae(r, t, e, a) {
|
|
520
|
-
const s = Z(r.data, t), n =
|
|
526
|
+
const s = Z(r.data, t), n = v(() => S(r.initialData.value, t)), d = (u) => ({
|
|
521
527
|
...u,
|
|
522
|
-
path:
|
|
523
|
-
setData: (
|
|
524
|
-
u.setData(
|
|
528
|
+
path: v(() => h(u.path).replace(t + ".", "")),
|
|
529
|
+
setData: (V) => {
|
|
530
|
+
u.setData(V);
|
|
525
531
|
}
|
|
526
|
-
}),
|
|
527
|
-
const
|
|
532
|
+
}), m = (u) => {
|
|
533
|
+
const V = I(t, u), w = r.getField(V);
|
|
528
534
|
return w ? d(w) : {};
|
|
529
|
-
},
|
|
530
|
-
const
|
|
535
|
+
}, F = (u) => {
|
|
536
|
+
const V = I(t, u.path), w = r.defineField({
|
|
531
537
|
...u,
|
|
532
|
-
path:
|
|
538
|
+
path: V
|
|
533
539
|
});
|
|
534
540
|
return d(w);
|
|
535
|
-
}, c =
|
|
536
|
-
const
|
|
537
|
-
return
|
|
538
|
-
}).map((u) => d(u))),
|
|
539
|
-
const
|
|
540
|
-
return
|
|
541
|
-
}),
|
|
541
|
+
}, c = v(() => r.fields.value.filter((u) => {
|
|
542
|
+
const V = u.path.value;
|
|
543
|
+
return V.startsWith(t + ".") || V === t;
|
|
544
|
+
}).map((u) => d(u))), i = () => r.fields.value.filter((u) => {
|
|
545
|
+
const V = u.path.value;
|
|
546
|
+
return V.startsWith(t + ".") || V === t;
|
|
547
|
+
}), f = v(() => i().some((u) => u.dirty.value)), l = v(() => i().some((u) => u.touched.value)), o = v(() => r.isValid.value), p = v(() => r.isValidated.value), A = v(() => me(h(r.errors), t)), M = {
|
|
542
548
|
data: s,
|
|
543
549
|
fields: c,
|
|
544
550
|
initialData: n,
|
|
545
|
-
defineField:
|
|
546
|
-
getField:
|
|
547
|
-
isDirty:
|
|
551
|
+
defineField: F,
|
|
552
|
+
getField: m,
|
|
553
|
+
isDirty: f,
|
|
548
554
|
isTouched: l,
|
|
549
|
-
isValid:
|
|
550
|
-
isValidated:
|
|
555
|
+
isValid: o,
|
|
556
|
+
isValidated: p,
|
|
551
557
|
errors: A,
|
|
552
558
|
defineValidator: (u) => {
|
|
553
|
-
const
|
|
554
|
-
() => new ke(t,
|
|
559
|
+
const V = C(u) ? u : O(u), w = v(
|
|
560
|
+
() => new ke(t, h(V))
|
|
555
561
|
);
|
|
556
|
-
return r.defineValidator(w),
|
|
562
|
+
return r.defineValidator(w), V;
|
|
557
563
|
},
|
|
558
|
-
reset: () =>
|
|
564
|
+
reset: () => i().forEach((u) => u.reset()),
|
|
559
565
|
validateForm: () => r.validateForm(),
|
|
560
|
-
getSubForm: (u,
|
|
566
|
+
getSubForm: (u, V) => {
|
|
561
567
|
const w = I(t, u);
|
|
562
568
|
return r.getSubForm(
|
|
563
569
|
w,
|
|
564
|
-
|
|
570
|
+
V
|
|
565
571
|
);
|
|
566
572
|
},
|
|
567
573
|
submitHandler: (u) => L(M, e ?? {})(u),
|
|
568
|
-
useFieldArray: (u,
|
|
574
|
+
useFieldArray: (u, V) => G(M, u, V)
|
|
569
575
|
};
|
|
570
576
|
return M;
|
|
571
577
|
}
|
|
572
|
-
function
|
|
573
|
-
const t =
|
|
578
|
+
function Ce(r) {
|
|
579
|
+
const t = v(() => g(r.initialData)), e = J(g(t)), a = k({
|
|
574
580
|
initialData: t,
|
|
575
581
|
data: e
|
|
576
582
|
});
|
|
577
|
-
|
|
583
|
+
R(
|
|
578
584
|
t,
|
|
579
585
|
(c) => {
|
|
580
586
|
a.data = g(c);
|
|
@@ -584,28 +590,28 @@ function xe(r) {
|
|
|
584
590
|
const s = be(a, r), n = Ee(a, s, {
|
|
585
591
|
keepValuesOnUnmount: r.keepValuesOnUnmount,
|
|
586
592
|
onBlur: async (c) => {
|
|
587
|
-
|
|
593
|
+
h(r.validationStrategy) === "onTouch" && s.validateField(c);
|
|
588
594
|
}
|
|
589
|
-
}), d = we(n),
|
|
595
|
+
}), d = we(n), m = () => {
|
|
590
596
|
e.value = g(t), s.reset();
|
|
591
597
|
for (const c of n.fields.value)
|
|
592
598
|
c.reset();
|
|
593
599
|
};
|
|
594
|
-
|
|
595
|
-
const
|
|
600
|
+
h(r.validationStrategy) === "onFormOpen" && s.validateForm();
|
|
601
|
+
const F = {
|
|
596
602
|
...n,
|
|
597
603
|
...s,
|
|
598
604
|
...d,
|
|
599
|
-
reset:
|
|
605
|
+
reset: m,
|
|
600
606
|
initialData: j(a, "initialData"),
|
|
601
607
|
data: j(a, "data"),
|
|
602
|
-
submitHandler: (c) => L(
|
|
603
|
-
getSubForm: (c,
|
|
604
|
-
useFieldArray: (c,
|
|
608
|
+
submitHandler: (c) => L(F, r)(c),
|
|
609
|
+
getSubForm: (c, i) => Ae(F, c, r),
|
|
610
|
+
useFieldArray: (c, i) => G(F, c, i)
|
|
605
611
|
};
|
|
606
|
-
return
|
|
612
|
+
return F;
|
|
607
613
|
}
|
|
608
|
-
const
|
|
614
|
+
const Te = /* @__PURE__ */ T({
|
|
609
615
|
__name: "Field",
|
|
610
616
|
props: {
|
|
611
617
|
form: {},
|
|
@@ -621,9 +627,9 @@ const Ce = /* @__PURE__ */ C({
|
|
|
621
627
|
const t = r, e = t.form.defineField({
|
|
622
628
|
path: t.path
|
|
623
629
|
}), a = k(e);
|
|
624
|
-
return (s, n) => b(s.$slots, "default",
|
|
630
|
+
return (s, n) => b(s.$slots, "default", x(N(a)));
|
|
625
631
|
}
|
|
626
|
-
}),
|
|
632
|
+
}), xe = /* @__PURE__ */ T({
|
|
627
633
|
inheritAttrs: !1,
|
|
628
634
|
__name: "FormFieldWrapper",
|
|
629
635
|
props: {
|
|
@@ -651,10 +657,10 @@ const Ce = /* @__PURE__ */ C({
|
|
|
651
657
|
]),
|
|
652
658
|
_: 2
|
|
653
659
|
}, [
|
|
654
|
-
ue(t.$slots, (
|
|
655
|
-
name:
|
|
660
|
+
ue(t.$slots, (m, F) => ({
|
|
661
|
+
name: F,
|
|
656
662
|
fn: U((c) => [
|
|
657
|
-
b(t.$slots,
|
|
663
|
+
b(t.$slots, F, x(N(c ?? {})))
|
|
658
664
|
])
|
|
659
665
|
}))
|
|
660
666
|
]), 1040, ["model-value", "errors", "name", "onUpdate:modelValue"]))
|
|
@@ -663,20 +669,20 @@ const Ce = /* @__PURE__ */ C({
|
|
|
663
669
|
}, 8, ["form", "path"]);
|
|
664
670
|
};
|
|
665
671
|
}
|
|
666
|
-
}), Ne = /* @__PURE__ */
|
|
672
|
+
}), Ne = /* @__PURE__ */ T({
|
|
667
673
|
__name: "FormPart",
|
|
668
674
|
props: {
|
|
669
675
|
form: {},
|
|
670
676
|
path: {}
|
|
671
677
|
},
|
|
672
678
|
setup(r) {
|
|
673
|
-
const t = r, e =
|
|
674
|
-
return (a, s) => b(a.$slots, "default",
|
|
679
|
+
const t = r, e = v(() => t.form.getSubForm(t.path));
|
|
680
|
+
return (a, s) => b(a.$slots, "default", x(N({ subform: e.value })));
|
|
675
681
|
}
|
|
676
682
|
});
|
|
677
683
|
export {
|
|
678
|
-
|
|
679
|
-
|
|
684
|
+
Te as Field,
|
|
685
|
+
xe as FormFieldWrapper,
|
|
680
686
|
Ne as FormPart,
|
|
681
|
-
|
|
687
|
+
Ce as useForm
|
|
682
688
|
};
|
package/dist/types/form.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface FieldArray<Item, Path extends string> {
|
|
|
19
19
|
items: ShallowRef<FieldItem<Item, Path>[]>;
|
|
20
20
|
push: (item: Item) => FieldItem<Item, Path>;
|
|
21
21
|
remove: (id: string) => void;
|
|
22
|
+
insert: (item: Item, index: number) => FieldItem<Item, Path>;
|
|
22
23
|
field: FormField<Item[], Path>;
|
|
23
24
|
}
|
|
24
25
|
export interface FormField<T, P extends string> {
|
|
@@ -60,5 +61,5 @@ export interface Form<T extends FormDataDefault> {
|
|
|
60
61
|
validateForm: () => Promise<ValidationResult>;
|
|
61
62
|
submitHandler: (onSubmit: (data: T) => Awaitable<void>) => (event: SubmitEvent) => Promise<void>;
|
|
62
63
|
getSubForm: <P extends EntityPaths<T>>(path: P, options?: SubformOptions<PickEntity<T, P>>) => Form<PickEntity<T, P>>;
|
|
63
|
-
useFieldArray: <K extends Paths<T>>(path: PickProps<T, K> extends unknown[] ? K : never, options?: FieldArrayOptions<PickProps<T, K> extends (infer U)[] ? U : never>) => FieldArray<PickProps<T, K> extends (infer U)[] ? U : never>;
|
|
64
|
+
useFieldArray: <K extends Paths<T>>(path: PickProps<T, K> extends unknown[] ? K : never, options?: FieldArrayOptions<PickProps<T, K> extends (infer U)[] ? U : never>) => FieldArray<PickProps<T, K> extends (infer U)[] ? U : never, typeof path>;
|
|
64
65
|
}
|
package/package.json
CHANGED
|
@@ -149,10 +149,24 @@ export function useFieldArray<T extends FormDataDefault, K extends Paths<T>>(
|
|
|
149
149
|
)
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
const insert = (item: Item, index: number) => {
|
|
153
|
+
const currentData = (arrayField.data.value ?? []) as Item[]
|
|
154
|
+
|
|
155
|
+
arrayField.setData(
|
|
156
|
+
currentData
|
|
157
|
+
.slice(0, index)
|
|
158
|
+
.concat([item])
|
|
159
|
+
.concat(currentData.slice(index)) as Items,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return items.value[index]!
|
|
163
|
+
}
|
|
164
|
+
|
|
152
165
|
return {
|
|
153
166
|
items,
|
|
154
167
|
push,
|
|
155
168
|
remove,
|
|
169
|
+
insert,
|
|
156
170
|
field: arrayField,
|
|
157
171
|
}
|
|
158
172
|
}
|
package/src/types/form.ts
CHANGED
|
@@ -30,6 +30,7 @@ export interface FieldArray<Item, Path extends string> {
|
|
|
30
30
|
items: ShallowRef<FieldItem<Item, Path>[]>
|
|
31
31
|
push: (item: Item) => FieldItem<Item, Path>
|
|
32
32
|
remove: (id: string) => void
|
|
33
|
+
insert: (item: Item, index: number) => FieldItem<Item, Path>
|
|
33
34
|
field: FormField<Item[], Path>
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -107,5 +108,5 @@ export interface Form<T extends FormDataDefault> {
|
|
|
107
108
|
options?: FieldArrayOptions<
|
|
108
109
|
PickProps<T, K> extends (infer U)[] ? U : never
|
|
109
110
|
>,
|
|
110
|
-
) => FieldArray<PickProps<T, K> extends (infer U)[] ? U : never>
|
|
111
|
+
) => FieldArray<PickProps<T, K> extends (infer U)[] ? U : never, typeof path>
|
|
111
112
|
}
|
|
@@ -1,185 +1,185 @@
|
|
|
1
|
-
import { describe, expect, it } from
|
|
2
|
-
import { nextTick } from
|
|
3
|
-
import { useForm } from
|
|
4
|
-
import { HashStore, useFieldArray } from
|
|
5
|
-
|
|
6
|
-
describe(
|
|
7
|
-
describe(
|
|
8
|
-
it(
|
|
9
|
-
const store = new HashStore<string[]>()
|
|
10
|
-
|
|
11
|
-
store.set(5, [
|
|
12
|
-
expect(store.has(5)).toBe(true)
|
|
13
|
-
expect(store.get(5)).toEqual([
|
|
14
|
-
|
|
15
|
-
store.set(10, [
|
|
16
|
-
expect(store.get(10)).toEqual([
|
|
17
|
-
expect(store.get(5)).toEqual([
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it(
|
|
21
|
-
const store = new HashStore<string[]>()
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { nextTick } from "vue";
|
|
3
|
+
import { useForm } from "../src/composables/useForm";
|
|
4
|
+
import { HashStore, useFieldArray } from "../src/composables/useFieldArray";
|
|
5
|
+
|
|
6
|
+
describe("useFieldArray", () => {
|
|
7
|
+
describe("HashStore", () => {
|
|
8
|
+
it("should store and retrieve primitive values with identity hash", () => {
|
|
9
|
+
const store = new HashStore<string[]>();
|
|
10
|
+
|
|
11
|
+
store.set(5, ["id-1"]);
|
|
12
|
+
expect(store.has(5)).toBe(true);
|
|
13
|
+
expect(store.get(5)).toEqual(["id-1"]);
|
|
14
|
+
|
|
15
|
+
store.set(10, ["id-2"]);
|
|
16
|
+
expect(store.get(10)).toEqual(["id-2"]);
|
|
17
|
+
expect(store.get(5)).toEqual(["id-1"]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should store objects by reference with identity hash", () => {
|
|
21
|
+
const store = new HashStore<string[]>();
|
|
22
22
|
const obj1 = {
|
|
23
23
|
id: 1,
|
|
24
|
-
name:
|
|
25
|
-
}
|
|
24
|
+
name: "A",
|
|
25
|
+
};
|
|
26
26
|
const obj2 = {
|
|
27
27
|
id: 1,
|
|
28
|
-
name:
|
|
29
|
-
} // Same content, different reference
|
|
28
|
+
name: "A",
|
|
29
|
+
}; // Same content, different reference
|
|
30
30
|
|
|
31
|
-
store.set(obj1, [
|
|
31
|
+
store.set(obj1, ["uuid-1"]);
|
|
32
32
|
|
|
33
|
-
expect(store.has(obj1)).toBe(true)
|
|
34
|
-
expect(store.has(obj2)).toBe(false)
|
|
35
|
-
expect(store.get(obj1)).toEqual([
|
|
36
|
-
expect(store.get(obj2)).toBeUndefined()
|
|
37
|
-
})
|
|
33
|
+
expect(store.has(obj1)).toBe(true);
|
|
34
|
+
expect(store.has(obj2)).toBe(false);
|
|
35
|
+
expect(store.get(obj1)).toEqual(["uuid-1"]);
|
|
36
|
+
expect(store.get(obj2)).toBeUndefined();
|
|
37
|
+
});
|
|
38
38
|
|
|
39
|
-
it(
|
|
40
|
-
const store = new HashStore<string[], { id: number }>(item => item.id)
|
|
39
|
+
it("should use custom hash function for semantic equality", () => {
|
|
40
|
+
const store = new HashStore<string[], { id: number }>((item) => item.id);
|
|
41
41
|
const obj1 = {
|
|
42
42
|
id: 1,
|
|
43
|
-
name:
|
|
44
|
-
}
|
|
43
|
+
name: "A",
|
|
44
|
+
};
|
|
45
45
|
const obj2 = {
|
|
46
46
|
id: 1,
|
|
47
|
-
name:
|
|
48
|
-
} // Same ID, different name
|
|
47
|
+
name: "B",
|
|
48
|
+
}; // Same ID, different name
|
|
49
49
|
const obj3 = {
|
|
50
50
|
id: 2,
|
|
51
|
-
name:
|
|
52
|
-
}
|
|
51
|
+
name: "A",
|
|
52
|
+
};
|
|
53
53
|
|
|
54
|
-
store.set(obj1, [
|
|
54
|
+
store.set(obj1, ["uuid-1"]);
|
|
55
55
|
|
|
56
|
-
expect(store.has(obj2)).toBe(true)
|
|
57
|
-
expect(store.get(obj2)).toEqual([
|
|
58
|
-
expect(store.has(obj3)).toBe(false)
|
|
59
|
-
})
|
|
56
|
+
expect(store.has(obj2)).toBe(true);
|
|
57
|
+
expect(store.get(obj2)).toEqual(["uuid-1"]);
|
|
58
|
+
expect(store.has(obj3)).toBe(false);
|
|
59
|
+
});
|
|
60
60
|
|
|
61
|
-
it(
|
|
62
|
-
const store = new HashStore<string[]>()
|
|
63
|
-
const item = { id: 1 }
|
|
61
|
+
it("should overwrite value on subsequent set calls", () => {
|
|
62
|
+
const store = new HashStore<string[]>();
|
|
63
|
+
const item = { id: 1 };
|
|
64
64
|
|
|
65
|
-
store.set(item, [
|
|
66
|
-
expect(store.get(item)).toEqual([
|
|
65
|
+
store.set(item, ["id-1"]);
|
|
66
|
+
expect(store.get(item)).toEqual(["id-1"]);
|
|
67
67
|
|
|
68
|
-
store.set(item, [
|
|
69
|
-
expect(store.get(item)).toEqual([
|
|
70
|
-
})
|
|
71
|
-
})
|
|
68
|
+
store.set(item, ["id-1", "id-2"]);
|
|
69
|
+
expect(store.get(item)).toEqual(["id-1", "id-2"]);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
72
|
|
|
73
|
-
describe(
|
|
74
|
-
it(
|
|
73
|
+
describe("ID Persistence", () => {
|
|
74
|
+
it("should maintain IDs when items are reordered", async () => {
|
|
75
75
|
const form = useForm({
|
|
76
76
|
initialData: {
|
|
77
77
|
items: [
|
|
78
78
|
{
|
|
79
79
|
id: 1,
|
|
80
|
-
name:
|
|
80
|
+
name: "First",
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
id: 2,
|
|
84
|
-
name:
|
|
84
|
+
name: "Second",
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
id: 3,
|
|
88
|
-
name:
|
|
88
|
+
name: "Third",
|
|
89
89
|
},
|
|
90
90
|
],
|
|
91
91
|
},
|
|
92
|
-
})
|
|
93
|
-
const fieldArray = useFieldArray(form,
|
|
92
|
+
});
|
|
93
|
+
const fieldArray = useFieldArray(form, "items", {
|
|
94
94
|
hashFn: (item: { id: number }) => item.id,
|
|
95
|
-
})
|
|
95
|
+
});
|
|
96
96
|
|
|
97
97
|
// Capture initial IDs
|
|
98
|
-
const id1 = fieldArray.items.value[0].id
|
|
99
|
-
const id2 = fieldArray.items.value[1].id
|
|
100
|
-
const id3 = fieldArray.items.value[2].id
|
|
98
|
+
const id1 = fieldArray.items.value[0].id;
|
|
99
|
+
const id2 = fieldArray.items.value[1].id;
|
|
100
|
+
const id3 = fieldArray.items.value[2].id;
|
|
101
101
|
|
|
102
102
|
// Reverse the array
|
|
103
|
-
const arrayField = form.getField(
|
|
104
|
-
arrayField.setData([...arrayField.data.value].reverse())
|
|
105
|
-
await nextTick()
|
|
103
|
+
const arrayField = form.getField("items");
|
|
104
|
+
arrayField.setData([...arrayField.data.value].reverse());
|
|
105
|
+
await nextTick();
|
|
106
106
|
|
|
107
107
|
// Verify order changed but IDs followed the items
|
|
108
|
-
expect(fieldArray.items.value[0].item.name).toBe(
|
|
109
|
-
expect(fieldArray.items.value[0].id).toBe(id3)
|
|
110
|
-
expect(fieldArray.items.value[0].path).toBe(
|
|
111
|
-
expect(fieldArray.items.value[1].item.name).toBe(
|
|
112
|
-
expect(fieldArray.items.value[1].id).toBe(id2)
|
|
113
|
-
expect(fieldArray.items.value[1].path).toBe(
|
|
114
|
-
expect(fieldArray.items.value[2].item.name).toBe(
|
|
115
|
-
expect(fieldArray.items.value[2].id).toBe(id1)
|
|
116
|
-
expect(fieldArray.items.value[2].path).toBe(
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it(
|
|
108
|
+
expect(fieldArray.items.value[0].item.name).toBe("Third");
|
|
109
|
+
expect(fieldArray.items.value[0].id).toBe(id3);
|
|
110
|
+
expect(fieldArray.items.value[0].path).toBe("items.0");
|
|
111
|
+
expect(fieldArray.items.value[1].item.name).toBe("Second");
|
|
112
|
+
expect(fieldArray.items.value[1].id).toBe(id2);
|
|
113
|
+
expect(fieldArray.items.value[1].path).toBe("items.1");
|
|
114
|
+
expect(fieldArray.items.value[2].item.name).toBe("First");
|
|
115
|
+
expect(fieldArray.items.value[2].id).toBe(id1);
|
|
116
|
+
expect(fieldArray.items.value[2].path).toBe("items.2");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should maintain IDs based on custom hash function", async () => {
|
|
120
120
|
const form = useForm({
|
|
121
121
|
initialData: {
|
|
122
122
|
products: [
|
|
123
123
|
{
|
|
124
|
-
sku:
|
|
125
|
-
name:
|
|
124
|
+
sku: "ABC",
|
|
125
|
+
name: "Widget",
|
|
126
126
|
price: 10,
|
|
127
127
|
},
|
|
128
128
|
{
|
|
129
|
-
sku:
|
|
130
|
-
name:
|
|
129
|
+
sku: "DEF",
|
|
130
|
+
name: "Gadget",
|
|
131
131
|
price: 20,
|
|
132
132
|
},
|
|
133
133
|
],
|
|
134
134
|
},
|
|
135
|
-
})
|
|
136
|
-
const fieldArray = useFieldArray(form,
|
|
135
|
+
});
|
|
136
|
+
const fieldArray = useFieldArray(form, "products", {
|
|
137
137
|
hashFn: (item: { sku: string }) => item.sku,
|
|
138
|
-
})
|
|
138
|
+
});
|
|
139
139
|
|
|
140
|
-
const idABC = fieldArray.items.value[0].id
|
|
141
|
-
const idDEF = fieldArray.items.value[1].id
|
|
140
|
+
const idABC = fieldArray.items.value[0].id;
|
|
141
|
+
const idDEF = fieldArray.items.value[1].id;
|
|
142
142
|
|
|
143
143
|
// Update price and swap order
|
|
144
|
-
const arrayField = form.getField(
|
|
144
|
+
const arrayField = form.getField("products");
|
|
145
145
|
arrayField.setData([
|
|
146
146
|
{
|
|
147
|
-
sku:
|
|
148
|
-
name:
|
|
147
|
+
sku: "DEF",
|
|
148
|
+
name: "Gadget",
|
|
149
149
|
price: 25,
|
|
150
150
|
},
|
|
151
151
|
{
|
|
152
|
-
sku:
|
|
153
|
-
name:
|
|
152
|
+
sku: "ABC",
|
|
153
|
+
name: "Widget",
|
|
154
154
|
price: 15,
|
|
155
155
|
},
|
|
156
|
-
])
|
|
157
|
-
await nextTick()
|
|
156
|
+
]);
|
|
157
|
+
await nextTick();
|
|
158
158
|
|
|
159
159
|
// IDs should persist because SKUs didn't change
|
|
160
|
-
expect(fieldArray.items.value[0].id).toBe(idDEF)
|
|
161
|
-
expect(fieldArray.items.value[1].id).toBe(idABC)
|
|
160
|
+
expect(fieldArray.items.value[0].id).toBe(idDEF);
|
|
161
|
+
expect(fieldArray.items.value[1].id).toBe(idABC);
|
|
162
162
|
|
|
163
163
|
// Change SKU - should get new ID
|
|
164
164
|
arrayField.setData([
|
|
165
165
|
{
|
|
166
|
-
sku:
|
|
167
|
-
name:
|
|
166
|
+
sku: "XYZ",
|
|
167
|
+
name: "Widget",
|
|
168
168
|
price: 15,
|
|
169
169
|
},
|
|
170
170
|
{
|
|
171
|
-
sku:
|
|
172
|
-
name:
|
|
171
|
+
sku: "DEF",
|
|
172
|
+
name: "Gadget",
|
|
173
173
|
price: 25,
|
|
174
174
|
},
|
|
175
|
-
])
|
|
176
|
-
await nextTick()
|
|
175
|
+
]);
|
|
176
|
+
await nextTick();
|
|
177
177
|
|
|
178
|
-
expect(fieldArray.items.value[0].id).not.toBe(idABC)
|
|
179
|
-
expect(fieldArray.items.value[1].id).toBe(idDEF)
|
|
180
|
-
})
|
|
178
|
+
expect(fieldArray.items.value[0].id).not.toBe(idABC);
|
|
179
|
+
expect(fieldArray.items.value[1].id).toBe(idDEF);
|
|
180
|
+
});
|
|
181
181
|
|
|
182
|
-
it(
|
|
182
|
+
it("should assign IDs in order for items with same hash", async () => {
|
|
183
183
|
// When multiple items share the same hash, IDs are assigned in order
|
|
184
184
|
// from the stored ID list. This means reordering items with same hash
|
|
185
185
|
// will NOT preserve ID-to-item mapping (IDs follow position, not identity).
|
|
@@ -187,57 +187,164 @@ describe('useFieldArray', () => {
|
|
|
187
187
|
initialData: {
|
|
188
188
|
items: [
|
|
189
189
|
{
|
|
190
|
-
type:
|
|
191
|
-
value:
|
|
190
|
+
type: "tag",
|
|
191
|
+
value: "vue",
|
|
192
192
|
},
|
|
193
193
|
{
|
|
194
|
-
type:
|
|
195
|
-
value:
|
|
194
|
+
type: "tag",
|
|
195
|
+
value: "react",
|
|
196
196
|
},
|
|
197
197
|
{
|
|
198
|
-
type:
|
|
199
|
-
value:
|
|
198
|
+
type: "tag",
|
|
199
|
+
value: "svelte",
|
|
200
200
|
},
|
|
201
201
|
],
|
|
202
202
|
},
|
|
203
|
-
})
|
|
203
|
+
});
|
|
204
204
|
// Hash by type only - all items have same hash 'tag'
|
|
205
|
-
const fieldArray = useFieldArray(form,
|
|
205
|
+
const fieldArray = useFieldArray(form, "items", {
|
|
206
206
|
hashFn: (item: { type: string }) => item.type,
|
|
207
|
-
})
|
|
207
|
+
});
|
|
208
208
|
|
|
209
209
|
// All items get unique IDs despite same hash
|
|
210
|
-
const [id1, id2, id3] = fieldArray.items.value.map(f => f.id)
|
|
211
|
-
expect(new Set([id1, id2, id3]).size).toBe(3)
|
|
210
|
+
const [id1, id2, id3] = fieldArray.items.value.map((f) => f.id);
|
|
211
|
+
expect(new Set([id1, id2, id3]).size).toBe(3);
|
|
212
212
|
|
|
213
213
|
// Reorder: move last item to first position
|
|
214
|
-
const arrayField = form.getField(
|
|
214
|
+
const arrayField = form.getField("items");
|
|
215
215
|
arrayField.setData([
|
|
216
216
|
{
|
|
217
|
-
type:
|
|
218
|
-
value:
|
|
217
|
+
type: "tag",
|
|
218
|
+
value: "svelte",
|
|
219
219
|
},
|
|
220
220
|
{
|
|
221
|
-
type:
|
|
222
|
-
value:
|
|
221
|
+
type: "tag",
|
|
222
|
+
value: "vue",
|
|
223
223
|
},
|
|
224
224
|
{
|
|
225
|
-
type:
|
|
226
|
-
value:
|
|
225
|
+
type: "tag",
|
|
226
|
+
value: "react",
|
|
227
227
|
},
|
|
228
|
-
])
|
|
229
|
-
await nextTick()
|
|
228
|
+
]);
|
|
229
|
+
await nextTick();
|
|
230
230
|
|
|
231
231
|
// IDs are assigned in order from stored list, NOT following items
|
|
232
232
|
// This is expected behavior with hash collisions
|
|
233
|
-
expect(fieldArray.items.value[0].id).toBe(id1) // svelte gets id1 (was vue's)
|
|
234
|
-
expect(fieldArray.items.value[1].id).toBe(id2) // vue gets id2 (was react's)
|
|
235
|
-
expect(fieldArray.items.value[2].id).toBe(id3) // react gets id3 (was svelte's)
|
|
233
|
+
expect(fieldArray.items.value[0].id).toBe(id1); // svelte gets id1 (was vue's)
|
|
234
|
+
expect(fieldArray.items.value[1].id).toBe(id2); // vue gets id2 (was react's)
|
|
235
|
+
expect(fieldArray.items.value[2].id).toBe(id3); // react gets id3 (was svelte's)
|
|
236
236
|
|
|
237
237
|
// The values confirm the reorder happened
|
|
238
|
-
expect(fieldArray.items.value[0].item.value).toBe(
|
|
239
|
-
expect(fieldArray.items.value[1].item.value).toBe(
|
|
240
|
-
expect(fieldArray.items.value[2].item.value).toBe(
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
|
|
238
|
+
expect(fieldArray.items.value[0].item.value).toBe("svelte");
|
|
239
|
+
expect(fieldArray.items.value[1].item.value).toBe("vue");
|
|
240
|
+
expect(fieldArray.items.value[2].item.value).toBe("react");
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("should push an item to the array", async () => {
|
|
244
|
+
const form = useForm({
|
|
245
|
+
initialData: {
|
|
246
|
+
items: [
|
|
247
|
+
{
|
|
248
|
+
id: 1,
|
|
249
|
+
name: "First",
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
const fieldArray = useFieldArray(form, "items", {
|
|
255
|
+
hashFn: (item: { id: number }) => item.id,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Capture initial IDs
|
|
259
|
+
const id1 = fieldArray.items.value[0].id;
|
|
260
|
+
|
|
261
|
+
// Push a new item
|
|
262
|
+
const newItem = fieldArray.push({
|
|
263
|
+
id: 2,
|
|
264
|
+
name: "Second",
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
expect(newItem.item.name).toBe("Second");
|
|
268
|
+
expect(newItem.path).toBe("items.1");
|
|
269
|
+
expect(newItem.id).toBeDefined();
|
|
270
|
+
|
|
271
|
+
await nextTick();
|
|
272
|
+
|
|
273
|
+
// Verify existing item's ID is unchanged
|
|
274
|
+
expect(fieldArray.items.value[0].id).toBe(id1);
|
|
275
|
+
expect(fieldArray.items.value[1].id).toBe(newItem.id);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("should remove an item from the array", async () => {
|
|
279
|
+
const form = useForm({
|
|
280
|
+
initialData: {
|
|
281
|
+
items: [
|
|
282
|
+
{
|
|
283
|
+
id: 1,
|
|
284
|
+
name: "First",
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
id: 2,
|
|
288
|
+
name: "Second",
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
const fieldArray = useFieldArray(form, "items", {
|
|
294
|
+
hashFn: (item: { id: number }) => item.id,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Capture initial IDs
|
|
298
|
+
const id1 = fieldArray.items.value[0].id;
|
|
299
|
+
|
|
300
|
+
// Remove an item
|
|
301
|
+
fieldArray.remove(id1);
|
|
302
|
+
|
|
303
|
+
await nextTick();
|
|
304
|
+
|
|
305
|
+
// Verify the correct item was removed
|
|
306
|
+
expect(fieldArray.items.value.length).toBe(1);
|
|
307
|
+
expect(fieldArray.items.value[0].item.name).toBe("Second");
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("should insert an item into the array", async () => {
|
|
311
|
+
const form = useForm({
|
|
312
|
+
initialData: {
|
|
313
|
+
items: [
|
|
314
|
+
{
|
|
315
|
+
id: 1,
|
|
316
|
+
name: "First",
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
id: 3,
|
|
320
|
+
name: "Third",
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
const fieldArray = useFieldArray(form, "items", {
|
|
326
|
+
hashFn: (item: { id: number }) => item.id,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Capture initial IDs
|
|
330
|
+
const id1 = fieldArray.items.value[0].id;
|
|
331
|
+
const id3 = fieldArray.items.value[1].id;
|
|
332
|
+
|
|
333
|
+
// Insert an item
|
|
334
|
+
const insertedItem = fieldArray.insert({ id: 2, name: "Second" }, 1);
|
|
335
|
+
expect(insertedItem.item.name).toBe("Second");
|
|
336
|
+
expect(insertedItem.path).toBe("items.1");
|
|
337
|
+
expect(insertedItem.id).toBeDefined();
|
|
338
|
+
|
|
339
|
+
await nextTick();
|
|
340
|
+
|
|
341
|
+
// Verify the item was inserted correctly
|
|
342
|
+
expect(fieldArray.items.value.length).toBe(3);
|
|
343
|
+
expect(fieldArray.items.value[0].id).toBe(id1);
|
|
344
|
+
expect(fieldArray.items.value[0].item.name).toBe("First");
|
|
345
|
+
expect(fieldArray.items.value[1].item.name).toBe("Second");
|
|
346
|
+
expect(fieldArray.items.value[2].id).toBe(id3);
|
|
347
|
+
expect(fieldArray.items.value[2].item.name).toBe("Third");
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
});
|