@teamnovu/kit-vue-forms 0.2.9 → 0.2.11

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