@teamnovu/kit-vue-forms 0.1.19 → 0.1.21
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/useFieldArray.d.ts +3 -0
- package/dist/composables/useFieldRegistry.d.ts +1 -1
- package/dist/composables/useForm.d.ts +4 -25
- package/dist/index.js +46 -46
- package/package.json +1 -1
- package/src/composables/useField.ts +3 -1
- package/src/composables/useFieldArray.ts +15 -0
- package/src/composables/useFieldRegistry.ts +60 -60
- package/src/composables/useForm.ts +40 -39
- package/tests/useForm.test.ts +286 -257
|
@@ -5,7 +5,7 @@ import { Paths, PickProps } from '../types/util';
|
|
|
5
5
|
import { UseFieldOptions } from './useField';
|
|
6
6
|
import { ValidationState } from './useValidation';
|
|
7
7
|
export type ResolvedFormField<T, K extends Paths<T>> = FormField<PickProps<T, K>, K>;
|
|
8
|
-
export type DefineFieldOptions<F, K extends string> = Pick<UseFieldOptions<F, K>,
|
|
8
|
+
export type DefineFieldOptions<F, K extends string> = Pick<UseFieldOptions<F, K>, 'path'> & {
|
|
9
9
|
onBlur?: () => void;
|
|
10
10
|
onFocus?: () => void;
|
|
11
11
|
};
|
|
@@ -1,31 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { Form, FormDataDefault, FieldsTuple } from '../types/form';
|
|
4
|
-
import { EntityPaths, PickEntity } from '../types/util';
|
|
1
|
+
import { MaybeRef, MaybeRefOrGetter } from 'vue';
|
|
2
|
+
import { Form, FormDataDefault } from '../types/form';
|
|
5
3
|
import { ValidationStrategy } from '../types/validation';
|
|
6
|
-
import {
|
|
7
|
-
import { ValidationOptions, ValidatorOptions } from './useValidation';
|
|
8
|
-
import { ErrorBag, Paths, PickProps, FormField, Validator, ValidationResult } from '..';
|
|
9
|
-
import { DefineFieldOptions } from './useFieldRegistry';
|
|
4
|
+
import { ValidationOptions } from './useValidation';
|
|
10
5
|
export interface UseFormOptions<T extends FormDataDefault> extends ValidationOptions<T> {
|
|
11
6
|
initialData: MaybeRefOrGetter<T>;
|
|
12
7
|
validationStrategy?: MaybeRef<ValidationStrategy>;
|
|
13
8
|
keepValuesOnUnmount?: MaybeRef<boolean>;
|
|
14
9
|
}
|
|
15
|
-
export declare function useForm<T extends FormDataDefault>(options: UseFormOptions<T>):
|
|
16
|
-
submitHandler: (onSubmit: (data: T) => Awaitable<void>) => (event: SubmitEvent) => Promise<void>;
|
|
17
|
-
errors: Ref< ErrorBag>;
|
|
18
|
-
data: Ref<T, T>;
|
|
19
|
-
isValid: Ref<boolean>;
|
|
20
|
-
isValidated: Ref<boolean>;
|
|
21
|
-
initialData: Readonly<Ref<T, T>>;
|
|
22
|
-
fields: Ref< FieldsTuple<T, Paths<T>>, FieldsTuple<T, Paths<T>>>;
|
|
23
|
-
defineField: <P extends Paths<T>>(options: DefineFieldOptions<PickProps<T, P>, P>) => FormField<PickProps<T, P>, P>;
|
|
24
|
-
getField: <P extends Paths<T>>(path: P) => FormField<PickProps<T, P>, P>;
|
|
25
|
-
isDirty: Ref<boolean>;
|
|
26
|
-
isTouched: Ref<boolean>;
|
|
27
|
-
defineValidator: <TData extends T>(options: ValidatorOptions<TData> | Ref< Validator<TData>, Validator<TData>>) => Ref< Validator<TData> | undefined, Validator<TData> | undefined>;
|
|
28
|
-
reset: () => void;
|
|
29
|
-
validateForm: () => Promise< ValidationResult>;
|
|
30
|
-
getSubForm: <P extends EntityPaths<T>>(path: P, options?: SubformOptions<PickEntity<T, P>> | undefined) => Form<PickEntity<T, P>>;
|
|
31
|
-
};
|
|
10
|
+
export declare function useForm<T extends FormDataDefault>(options: UseFormOptions<T>): Form<T>;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var Q = Object.defineProperty;
|
|
2
2
|
var X = (t, e, r) => e in t ? Q(t, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : t[e] = r;
|
|
3
3
|
var S = (t, e, r) => X(t, typeof e != "symbol" ? e + "" : e, r);
|
|
4
|
-
import { toValue as Y, toRaw as ee, computed as v, unref as
|
|
4
|
+
import { toValue as Y, toRaw as ee, computed as v, unref as u, isRef as k, shallowRef as j, reactive as _, watch as P, toRefs as T, shallowReactive as re, toRef as I, onScopeDispose as te, triggerRef as ae, ref as J, getCurrentScope as se, onBeforeUnmount as ne, defineComponent as x, renderSlot as $, normalizeProps as M, guardReactiveProps as N, resolveComponent as ie, createBlock as H, openBlock as K, withCtx as B, resolveDynamicComponent as oe, mergeProps as le, createSlots as ue, renderList as ce } from "vue";
|
|
5
5
|
import { cloneDeep as de } from "lodash-es";
|
|
6
6
|
import "zod";
|
|
7
7
|
function g(t) {
|
|
@@ -27,7 +27,7 @@ function he(t, e, r) {
|
|
|
27
27
|
const n = a.slice(0, -1).reduce(
|
|
28
28
|
(f, p) => ((f == null ? void 0 : f[p]) === void 0 && (f[p] = {}), f == null ? void 0 : f[p]),
|
|
29
29
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
-
|
|
30
|
+
u(t)
|
|
31
31
|
);
|
|
32
32
|
n[s] = r;
|
|
33
33
|
} else {
|
|
@@ -38,10 +38,10 @@ function he(t, e, r) {
|
|
|
38
38
|
}
|
|
39
39
|
const L = (t, e) => v({
|
|
40
40
|
get() {
|
|
41
|
-
return D(
|
|
41
|
+
return D(u(t), u(e));
|
|
42
42
|
},
|
|
43
43
|
set(r) {
|
|
44
|
-
he(t,
|
|
44
|
+
he(t, u(e), r);
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
47
|
function b(t, e) {
|
|
@@ -90,7 +90,7 @@ function pe(t) {
|
|
|
90
90
|
P(
|
|
91
91
|
j(r.initialValue),
|
|
92
92
|
() => {
|
|
93
|
-
a.value = Object.freeze(g(r.initialValue)), s.value = g(r.initialValue);
|
|
93
|
+
a.value = Object.freeze(g(r.initialValue)), s.value !== u(r.initialValue) && (s.value = g(r.initialValue));
|
|
94
94
|
},
|
|
95
95
|
{ flush: "sync" }
|
|
96
96
|
);
|
|
@@ -104,7 +104,7 @@ function pe(t) {
|
|
|
104
104
|
(h = r.onFocus) == null || h.call(r);
|
|
105
105
|
}, V = () => {
|
|
106
106
|
const h = s.path.split(".").at(-1) || "";
|
|
107
|
-
|
|
107
|
+
u(r.existsInForm) && !/^\d+$/.test(h) && (s.value = g(s.initialValue)), s.touched = !1, s.errors = [];
|
|
108
108
|
}, l = (h) => {
|
|
109
109
|
n.value || f(g(h)), s.initialValue = h;
|
|
110
110
|
}, c = (h) => {
|
|
@@ -146,7 +146,7 @@ function ye(t, e, r) {
|
|
|
146
146
|
...Ve,
|
|
147
147
|
...r
|
|
148
148
|
}, f = (o) => {
|
|
149
|
-
const i =
|
|
149
|
+
const i = u(o.path);
|
|
150
150
|
s.set(i, o);
|
|
151
151
|
}, p = (o) => {
|
|
152
152
|
var i;
|
|
@@ -164,7 +164,7 @@ function ye(t, e, r) {
|
|
|
164
164
|
path: i,
|
|
165
165
|
value: L(I(t, "data"), i),
|
|
166
166
|
initialValue: Fe(t, i),
|
|
167
|
-
existsInForm: v(() => fe(t.data,
|
|
167
|
+
existsInForm: v(() => fe(t.data, u(i))),
|
|
168
168
|
errors: v({
|
|
169
169
|
get() {
|
|
170
170
|
return e.errors.value.propertyErrors[i] || [];
|
|
@@ -176,14 +176,14 @@ function ye(t, e, r) {
|
|
|
176
176
|
onBlur: async () => {
|
|
177
177
|
var E, R;
|
|
178
178
|
await Promise.all([
|
|
179
|
-
(E = n == null ? void 0 : n.onBlur) == null ? void 0 : E.call(n,
|
|
179
|
+
(E = n == null ? void 0 : n.onBlur) == null ? void 0 : E.call(n, u(i)),
|
|
180
180
|
(R = o.onBlur) == null ? void 0 : R.call(o)
|
|
181
181
|
]);
|
|
182
182
|
},
|
|
183
183
|
onFocus: async () => {
|
|
184
184
|
var E, R;
|
|
185
185
|
await Promise.all([
|
|
186
|
-
(E = n == null ? void 0 : n.onFocus) == null ? void 0 : E.call(n,
|
|
186
|
+
(E = n == null ? void 0 : n.onFocus) == null ? void 0 : E.call(n, u(i)),
|
|
187
187
|
(R = o.onFocus) == null ? void 0 : R.call(o)
|
|
188
188
|
]);
|
|
189
189
|
}
|
|
@@ -204,7 +204,7 @@ function ye(t, e, r) {
|
|
|
204
204
|
};
|
|
205
205
|
}
|
|
206
206
|
function ge(t) {
|
|
207
|
-
const e = v(() => t.fields.value.some((a) =>
|
|
207
|
+
const e = v(() => t.fields.value.some((a) => u(a.dirty))), r = v(() => t.fields.value.some((a) => u(a.touched)));
|
|
208
208
|
return {
|
|
209
209
|
isDirty: e,
|
|
210
210
|
isTouched: r
|
|
@@ -212,7 +212,7 @@ function ge(t) {
|
|
|
212
212
|
}
|
|
213
213
|
function G(t, e) {
|
|
214
214
|
return (r) => async (a) => {
|
|
215
|
-
a.preventDefault(),
|
|
215
|
+
a.preventDefault(), u(e.validationStrategy) !== "none" && await t.validateForm(), t.isValid.value && await r(t.data.value);
|
|
216
216
|
};
|
|
217
217
|
}
|
|
218
218
|
function Ee(t) {
|
|
@@ -342,19 +342,19 @@ class Se {
|
|
|
342
342
|
}
|
|
343
343
|
function U(t) {
|
|
344
344
|
return v(() => new Se(
|
|
345
|
-
|
|
346
|
-
|
|
345
|
+
u(t.schema),
|
|
346
|
+
u(t.validateFn)
|
|
347
347
|
));
|
|
348
348
|
}
|
|
349
349
|
function be(t, e) {
|
|
350
350
|
const r = _({
|
|
351
351
|
validators: J([U(e)]),
|
|
352
352
|
isValidated: !1,
|
|
353
|
-
errors:
|
|
353
|
+
errors: u(e.errors) ?? y.errors
|
|
354
354
|
}), a = (l = y.errors) => {
|
|
355
|
-
r.errors = C(
|
|
355
|
+
r.errors = C(u(e.errors) ?? y.errors, l);
|
|
356
356
|
};
|
|
357
|
-
P(() =>
|
|
357
|
+
P(() => u(e.errors), async () => {
|
|
358
358
|
if (r.isValidated) {
|
|
359
359
|
const l = await n();
|
|
360
360
|
a(l.errors);
|
|
@@ -371,7 +371,7 @@ function be(t, e) {
|
|
|
371
371
|
r.errors = y.errors;
|
|
372
372
|
},
|
|
373
373
|
{ immediate: !0 }
|
|
374
|
-
), P([() => t.data, () =>
|
|
374
|
+
), P([() => t.data, () => u(e.schema)], () => {
|
|
375
375
|
r.isValidated && f();
|
|
376
376
|
});
|
|
377
377
|
const s = (l) => {
|
|
@@ -384,7 +384,7 @@ function be(t, e) {
|
|
|
384
384
|
};
|
|
385
385
|
async function n() {
|
|
386
386
|
const l = await Promise.all(
|
|
387
|
-
r.validators.filter((i) =>
|
|
387
|
+
r.validators.filter((i) => u(i) !== void 0).map((i) => u(i).validate(t.data))
|
|
388
388
|
), c = l.every((i) => i.isValid);
|
|
389
389
|
let { errors: o } = y;
|
|
390
390
|
if (!c) {
|
|
@@ -414,7 +414,7 @@ function be(t, e) {
|
|
|
414
414
|
errors: r.errors
|
|
415
415
|
};
|
|
416
416
|
}, F = v(() => !O(r.errors)), V = () => {
|
|
417
|
-
r.isValidated = !1, r.errors =
|
|
417
|
+
r.isValidated = !1, r.errors = u(e.errors) ?? y.errors;
|
|
418
418
|
};
|
|
419
419
|
return {
|
|
420
420
|
...T(r),
|
|
@@ -448,33 +448,33 @@ class $e {
|
|
|
448
448
|
}
|
|
449
449
|
}
|
|
450
450
|
function _e(t, e, r, a) {
|
|
451
|
-
const s = L(t.data, e), n = v(() => D(t.initialData.value, e)), f = (
|
|
452
|
-
...
|
|
453
|
-
path: v(() => d
|
|
451
|
+
const s = L(t.data, e), n = v(() => D(t.initialData.value, e)), f = (d) => ({
|
|
452
|
+
...d,
|
|
453
|
+
path: v(() => u(d.path).replace(e + ".", "")),
|
|
454
454
|
setData: (m) => {
|
|
455
|
-
|
|
455
|
+
d.setData(m);
|
|
456
456
|
}
|
|
457
|
-
}), p = (
|
|
458
|
-
const m = b(e,
|
|
457
|
+
}), p = (d) => {
|
|
458
|
+
const m = b(e, d), w = t.getField(m);
|
|
459
459
|
return w ? f(w) : {};
|
|
460
|
-
}, F = (
|
|
461
|
-
const m = b(e,
|
|
462
|
-
...
|
|
460
|
+
}, F = (d) => {
|
|
461
|
+
const m = b(e, d.path), w = t.defineField({
|
|
462
|
+
...d,
|
|
463
463
|
path: m
|
|
464
464
|
});
|
|
465
465
|
return f(w);
|
|
466
|
-
}, V = v(() => t.fields.value.filter((
|
|
467
|
-
const m =
|
|
466
|
+
}, V = v(() => t.fields.value.filter((d) => {
|
|
467
|
+
const m = d.path.value;
|
|
468
468
|
return m.startsWith(e + ".") || m === e;
|
|
469
|
-
}).map((
|
|
470
|
-
const m =
|
|
469
|
+
}).map((d) => f(d))), l = () => t.fields.value.filter((d) => {
|
|
470
|
+
const m = d.path.value;
|
|
471
471
|
return m.startsWith(e + ".") || m === e;
|
|
472
472
|
}), c = v(
|
|
473
|
-
() => l().some((
|
|
473
|
+
() => l().some((d) => d.dirty.value)
|
|
474
474
|
), o = v(
|
|
475
|
-
() => l().some((
|
|
475
|
+
() => l().some((d) => d.touched.value)
|
|
476
476
|
), i = v(() => t.isValid.value), h = v(() => t.isValidated.value), A = v(
|
|
477
|
-
() => ve(
|
|
477
|
+
() => ve(u(t.errors), e)
|
|
478
478
|
), z = {
|
|
479
479
|
data: s,
|
|
480
480
|
fields: V,
|
|
@@ -486,16 +486,16 @@ function _e(t, e, r, a) {
|
|
|
486
486
|
isValid: i,
|
|
487
487
|
isValidated: h,
|
|
488
488
|
errors: A,
|
|
489
|
-
defineValidator: (
|
|
490
|
-
const m = k(
|
|
491
|
-
() => new $e(e,
|
|
489
|
+
defineValidator: (d) => {
|
|
490
|
+
const m = k(d) ? d : U(d), w = v(
|
|
491
|
+
() => new $e(e, u(m))
|
|
492
492
|
);
|
|
493
493
|
return t.defineValidator(w), m;
|
|
494
494
|
},
|
|
495
|
-
reset: () => l().forEach((
|
|
495
|
+
reset: () => l().forEach((d) => d.reset()),
|
|
496
496
|
validateForm: () => t.validateForm(),
|
|
497
|
-
getSubForm: (
|
|
498
|
-
const w = b(e,
|
|
497
|
+
getSubForm: (d, m) => {
|
|
498
|
+
const w = b(e, d);
|
|
499
499
|
return t.getSubForm(
|
|
500
500
|
w,
|
|
501
501
|
m
|
|
@@ -522,7 +522,7 @@ function Ue(t) {
|
|
|
522
522
|
const s = be(a, t), n = ye(a, s, {
|
|
523
523
|
keepValuesOnUnmount: t.keepValuesOnUnmount,
|
|
524
524
|
onBlur: async (c) => {
|
|
525
|
-
|
|
525
|
+
u(t.validationStrategy) === "onTouch" && s.validateField(c);
|
|
526
526
|
}
|
|
527
527
|
}), f = ge(n), p = () => {
|
|
528
528
|
r.value = g(e), s.reset();
|
|
@@ -541,7 +541,7 @@ function Ue(t) {
|
|
|
541
541
|
initialData: I(a, "initialData"),
|
|
542
542
|
data: I(a, "data")
|
|
543
543
|
}, l = G(V, t);
|
|
544
|
-
return
|
|
544
|
+
return u(t.validationStrategy) === "onFormOpen" && s.validateForm(), {
|
|
545
545
|
...V,
|
|
546
546
|
submitHandler: l
|
|
547
547
|
};
|
|
@@ -586,13 +586,13 @@ const ke = /* @__PURE__ */ x({
|
|
|
586
586
|
errors: s,
|
|
587
587
|
name: t.path,
|
|
588
588
|
"onUpdate:modelValue": f
|
|
589
|
-
}),
|
|
589
|
+
}), ue({
|
|
590
590
|
default: B(() => [
|
|
591
591
|
$(e.$slots, "default")
|
|
592
592
|
]),
|
|
593
593
|
_: 2
|
|
594
594
|
}, [
|
|
595
|
-
|
|
595
|
+
ce(e.$slots, (p, F) => ({
|
|
596
596
|
name: F,
|
|
597
597
|
fn: B((V) => [
|
|
598
598
|
$(e.$slots, F, M(N(V ?? {})))
|
package/package.json
CHANGED
|
@@ -37,7 +37,9 @@ export function useField<T, K extends string>(fieldOptions: UseFieldOptions<T, K
|
|
|
37
37
|
shallowRef(options.initialValue),
|
|
38
38
|
() => {
|
|
39
39
|
initialValue.value = Object.freeze(cloneRefValue(options.initialValue))
|
|
40
|
-
state.value
|
|
40
|
+
if (state.value !== unref(options.initialValue)) {
|
|
41
|
+
state.value = cloneRefValue(options.initialValue)
|
|
42
|
+
}
|
|
41
43
|
},
|
|
42
44
|
{ flush: 'sync' },
|
|
43
45
|
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Form, FormDataDefault, FormField } from "../types/form";
|
|
2
|
+
import type { Paths, PickProps } from "../types/util";
|
|
3
|
+
|
|
4
|
+
export function useFieldArray<T extends FormDataDefault, K extends Paths<T>>(
|
|
5
|
+
form: Form<T>,
|
|
6
|
+
path: PickProps<T, K> extends unknown[] ? K : never,
|
|
7
|
+
) {
|
|
8
|
+
type Items = PickProps<T, K> & unknown[];
|
|
9
|
+
type Item = Items[number];
|
|
10
|
+
const arrayField = form.getField(path) as FormField<Items, K>;
|
|
11
|
+
|
|
12
|
+
const push = (item: Item) => {
|
|
13
|
+
arrayField.setData([...arrayField.data.value, item] as Items);
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Awaitable } from
|
|
1
|
+
import type { Awaitable } from '@vueuse/core'
|
|
2
2
|
import {
|
|
3
3
|
computed,
|
|
4
4
|
onScopeDispose,
|
|
@@ -9,47 +9,47 @@ import {
|
|
|
9
9
|
unref,
|
|
10
10
|
watch,
|
|
11
11
|
type MaybeRef,
|
|
12
|
-
} from
|
|
13
|
-
import type { FieldsTuple, FormDataDefault, FormField } from
|
|
14
|
-
import type { Paths, PickProps } from
|
|
15
|
-
import { existsPath, getLens, getNestedValue } from
|
|
16
|
-
import { Rc } from
|
|
17
|
-
import { useField, type UseFieldOptions } from
|
|
18
|
-
import type { ValidationState } from
|
|
12
|
+
} from 'vue'
|
|
13
|
+
import type { FieldsTuple, FormDataDefault, FormField } from '../types/form'
|
|
14
|
+
import type { Paths, PickProps } from '../types/util'
|
|
15
|
+
import { existsPath, getLens, getNestedValue } from '../utils/path'
|
|
16
|
+
import { Rc } from '../utils/rc'
|
|
17
|
+
import { useField, type UseFieldOptions } from './useField'
|
|
18
|
+
import type { ValidationState } from './useValidation'
|
|
19
19
|
|
|
20
20
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
-
type FieldRegistryCache<T> = Map<Paths<T>, FormField<any, string
|
|
21
|
+
type FieldRegistryCache<T> = Map<Paths<T>, FormField<any, string>>
|
|
22
22
|
|
|
23
23
|
export type ResolvedFormField<T, K extends Paths<T>> = FormField<
|
|
24
24
|
PickProps<T, K>,
|
|
25
25
|
K
|
|
26
|
-
|
|
26
|
+
>
|
|
27
27
|
|
|
28
28
|
export type DefineFieldOptions<F, K extends string> = Pick<
|
|
29
29
|
UseFieldOptions<F, K>,
|
|
30
|
-
|
|
30
|
+
'path'
|
|
31
31
|
> & {
|
|
32
|
-
onBlur?: () => void
|
|
33
|
-
onFocus?: () => void
|
|
34
|
-
}
|
|
32
|
+
onBlur?: () => void
|
|
33
|
+
onFocus?: () => void
|
|
34
|
+
}
|
|
35
35
|
|
|
36
36
|
interface FormState<
|
|
37
37
|
T extends FormDataDefault,
|
|
38
38
|
TIn extends FormDataDefault = T,
|
|
39
39
|
> {
|
|
40
|
-
data: T
|
|
41
|
-
initialData: TIn
|
|
40
|
+
data: T
|
|
41
|
+
initialData: TIn
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
interface FieldRegistryOptions {
|
|
45
|
-
keepValuesOnUnmount?: MaybeRef<boolean
|
|
46
|
-
onBlur?: (path: string) => Awaitable<void
|
|
47
|
-
onFocus?: (path: string) => Awaitable<void
|
|
45
|
+
keepValuesOnUnmount?: MaybeRef<boolean>
|
|
46
|
+
onBlur?: (path: string) => Awaitable<void>
|
|
47
|
+
onFocus?: (path: string) => Awaitable<void>
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const optionDefaults = {
|
|
51
51
|
keepValuesOnUnmount: true,
|
|
52
|
-
}
|
|
52
|
+
}
|
|
53
53
|
|
|
54
54
|
// A computed that always reflects the latest value from the getter
|
|
55
55
|
// This computed forces updates even if the value is the same (to trigger watchers)
|
|
@@ -57,19 +57,19 @@ function initialDataSync<T extends FormDataDefault>(
|
|
|
57
57
|
formState: FormState<T>,
|
|
58
58
|
path: Paths<T>,
|
|
59
59
|
) {
|
|
60
|
-
const getNewValue = () => getNestedValue(formState.initialData, path)
|
|
61
|
-
const initialValueRef = shallowRef(getNewValue())
|
|
60
|
+
const getNewValue = () => getNestedValue(formState.initialData, path)
|
|
61
|
+
const initialValueRef = shallowRef(getNewValue())
|
|
62
62
|
|
|
63
63
|
watch(
|
|
64
64
|
() => formState.initialData,
|
|
65
65
|
() => {
|
|
66
|
-
initialValueRef.value = getNewValue()
|
|
67
|
-
triggerRef(initialValueRef)
|
|
66
|
+
initialValueRef.value = getNewValue()
|
|
67
|
+
triggerRef(initialValueRef)
|
|
68
68
|
},
|
|
69
|
-
{ flush:
|
|
70
|
-
)
|
|
69
|
+
{ flush: 'sync' },
|
|
70
|
+
)
|
|
71
71
|
|
|
72
|
-
return initialValueRef
|
|
72
|
+
return initialValueRef
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
export function useFieldRegistry<T extends FormDataDefault>(
|
|
@@ -77,99 +77,99 @@ export function useFieldRegistry<T extends FormDataDefault>(
|
|
|
77
77
|
validationState: ValidationState<T>,
|
|
78
78
|
fieldRegistryOptions?: FieldRegistryOptions,
|
|
79
79
|
) {
|
|
80
|
-
const fieldReferenceCounter = new Map<Paths<T>, Rc>()
|
|
81
|
-
const fields = shallowReactive(new Map()) as FieldRegistryCache<T
|
|
80
|
+
const fieldReferenceCounter = new Map<Paths<T>, Rc>()
|
|
81
|
+
const fields = shallowReactive(new Map()) as FieldRegistryCache<T>
|
|
82
82
|
const registryOptions = {
|
|
83
83
|
...optionDefaults,
|
|
84
84
|
...fieldRegistryOptions,
|
|
85
|
-
}
|
|
85
|
+
}
|
|
86
86
|
|
|
87
87
|
const registerField = <K extends Paths<T>>(
|
|
88
88
|
field: ResolvedFormField<T, K>,
|
|
89
89
|
) => {
|
|
90
|
-
const path = unref(field.path) as Paths<T
|
|
91
|
-
fields.set(path, field)
|
|
92
|
-
}
|
|
90
|
+
const path = unref(field.path) as Paths<T>
|
|
91
|
+
fields.set(path, field)
|
|
92
|
+
}
|
|
93
93
|
|
|
94
94
|
const deregisterField = (path: Paths<T>) => {
|
|
95
95
|
if (!registryOptions?.keepValuesOnUnmount) {
|
|
96
|
-
fields.get(path)?.reset()
|
|
96
|
+
fields.get(path)?.reset()
|
|
97
97
|
}
|
|
98
|
-
fields.delete(path)
|
|
99
|
-
}
|
|
98
|
+
fields.delete(path)
|
|
99
|
+
}
|
|
100
100
|
|
|
101
101
|
const track = (path: Paths<T>) => {
|
|
102
102
|
if (!fieldReferenceCounter.has(path)) {
|
|
103
|
-
fieldReferenceCounter.set(path, new Rc(() => deregisterField(path)))
|
|
103
|
+
fieldReferenceCounter.set(path, new Rc(() => deregisterField(path)))
|
|
104
104
|
} else {
|
|
105
|
-
fieldReferenceCounter.get(path)?.inc()
|
|
105
|
+
fieldReferenceCounter.get(path)?.inc()
|
|
106
106
|
}
|
|
107
|
-
}
|
|
107
|
+
}
|
|
108
108
|
|
|
109
109
|
const untrack = (path: Paths<T>) => {
|
|
110
110
|
if (fieldReferenceCounter.has(path)) {
|
|
111
|
-
fieldReferenceCounter.get(path)?.dec()
|
|
111
|
+
fieldReferenceCounter.get(path)?.dec()
|
|
112
112
|
}
|
|
113
|
-
}
|
|
113
|
+
}
|
|
114
114
|
|
|
115
115
|
const getField = <K extends Paths<T>>(
|
|
116
116
|
options: DefineFieldOptions<PickProps<T, K>, K>,
|
|
117
117
|
): ResolvedFormField<T, K> => {
|
|
118
|
-
const { path } = options
|
|
118
|
+
const { path } = options
|
|
119
119
|
|
|
120
120
|
if (!fields.has(path)) {
|
|
121
121
|
const field = useField({
|
|
122
122
|
path,
|
|
123
|
-
value: getLens(toRef(formState,
|
|
123
|
+
value: getLens(toRef(formState, 'data'), path),
|
|
124
124
|
initialValue: initialDataSync(formState, path),
|
|
125
125
|
existsInForm: computed(() => existsPath(formState.data, unref(path))),
|
|
126
126
|
errors: computed({
|
|
127
127
|
get() {
|
|
128
|
-
return validationState.errors.value.propertyErrors[path] || []
|
|
128
|
+
return validationState.errors.value.propertyErrors[path] || []
|
|
129
129
|
},
|
|
130
130
|
set(newErrors) {
|
|
131
|
-
validationState.errors.value.propertyErrors[path] = newErrors
|
|
131
|
+
validationState.errors.value.propertyErrors[path] = newErrors
|
|
132
132
|
},
|
|
133
133
|
}),
|
|
134
134
|
onBlur: async () => {
|
|
135
135
|
await Promise.all([
|
|
136
136
|
registryOptions?.onBlur?.(unref(path)),
|
|
137
137
|
options.onBlur?.(),
|
|
138
|
-
])
|
|
138
|
+
])
|
|
139
139
|
},
|
|
140
140
|
onFocus: async () => {
|
|
141
141
|
await Promise.all([
|
|
142
142
|
registryOptions?.onFocus?.(unref(path)),
|
|
143
143
|
options.onFocus?.(),
|
|
144
|
-
])
|
|
144
|
+
])
|
|
145
145
|
},
|
|
146
|
-
})
|
|
146
|
+
})
|
|
147
147
|
|
|
148
|
-
registerField(field)
|
|
148
|
+
registerField(field)
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
const field = fields.get(path) as ResolvedFormField<T, K
|
|
151
|
+
const field = fields.get(path) as ResolvedFormField<T, K>
|
|
152
152
|
|
|
153
|
-
track(path)
|
|
153
|
+
track(path)
|
|
154
154
|
|
|
155
155
|
// Clean up field on unmount
|
|
156
156
|
onScopeDispose(() => {
|
|
157
|
-
untrack(path)
|
|
158
|
-
})
|
|
157
|
+
untrack(path)
|
|
158
|
+
})
|
|
159
159
|
|
|
160
|
-
return field
|
|
161
|
-
}
|
|
160
|
+
return field
|
|
161
|
+
}
|
|
162
162
|
|
|
163
163
|
const defineField = <K extends Paths<T>>(
|
|
164
164
|
options: DefineFieldOptions<PickProps<T, K>, K>,
|
|
165
165
|
): ResolvedFormField<T, K> => {
|
|
166
|
-
const field = getField(options)
|
|
166
|
+
const field = getField(options)
|
|
167
167
|
|
|
168
168
|
// TODO: If more options are ever needed than only the path we have to update the field
|
|
169
169
|
// here with the new options
|
|
170
170
|
|
|
171
|
-
return field
|
|
172
|
-
}
|
|
171
|
+
return field
|
|
172
|
+
}
|
|
173
173
|
|
|
174
174
|
return {
|
|
175
175
|
fields: computed(() => [...fields.values()] as FieldsTuple<T>),
|
|
@@ -177,9 +177,9 @@ export function useFieldRegistry<T extends FormDataDefault>(
|
|
|
177
177
|
registerField,
|
|
178
178
|
deregisterField,
|
|
179
179
|
defineField,
|
|
180
|
-
}
|
|
180
|
+
}
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
export type FieldRegistry<T extends FormDataDefault> = ReturnType<
|
|
184
184
|
typeof useFieldRegistry<T>
|
|
185
|
-
|
|
185
|
+
>
|