@teamnovu/kit-vue-forms 0.0.16 → 0.0.18

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.
@@ -0,0 +1,25 @@
1
+ import { Component, VNodeProps, AllowedComponentProps, ComponentCustomProps, PublicProps, ShallowUnwrapRef, VNode } from 'vue';
2
+ import { ComponentProps } from 'vue-component-type-helpers';
3
+ import { Paths } from '../types/util.ts';
4
+ import { Form } from '../types/form.ts';
5
+ export interface FormFieldWrapperProps<TData extends object, TPath extends string, TComponent> {
6
+ component: TComponent;
7
+ componentProps: Omit<ComponentProps<TComponent>, 'modelValue' | 'update:modelValue' | 'errors'>;
8
+ form: Form<TData>;
9
+ path: TPath;
10
+ }
11
+ declare const _default: <TData extends object, TPath extends Paths<TData>, TComponent extends Component>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
12
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>, never> & FormFieldWrapperProps<TData, TPath, TComponent> & Partial<{}>> & PublicProps;
13
+ expose(exposed: ShallowUnwrapRef<{}>): void;
14
+ attrs: any;
15
+ slots: {
16
+ default?(_: {}): any;
17
+ };
18
+ emit: {};
19
+ }>) => VNode & {
20
+ __ctx?: Awaited<typeof __VLS_setup>;
21
+ };
22
+ export default _default;
23
+ type __VLS_PrettifyLocal<T> = {
24
+ [K in keyof T]: T[K];
25
+ } & {};
@@ -0,0 +1,24 @@
1
+ import { Form } from '../types/form.ts';
2
+ import { EntityPaths, PickEntity } from '../types/util.ts';
3
+ import { VNodeProps, AllowedComponentProps, ComponentCustomProps, PublicProps, ShallowUnwrapRef, VNode } from 'vue';
4
+ export interface FormPartProps<TData extends object, TPath> {
5
+ form: Form<TData>;
6
+ path: TPath;
7
+ }
8
+ declare const _default: <TData extends object, TPath extends EntityPaths<TData>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
9
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>, never> & FormPartProps<TData, TPath> & Partial<{}>> & PublicProps;
10
+ expose(exposed: ShallowUnwrapRef<{}>): void;
11
+ attrs: any;
12
+ slots: {
13
+ default?(_: {
14
+ subform: Form< PickEntity<TData, TPath>>;
15
+ }): any;
16
+ };
17
+ emit: {};
18
+ }>) => VNode & {
19
+ __ctx?: Awaited<typeof __VLS_setup>;
20
+ };
21
+ export default _default;
22
+ type __VLS_PrettifyLocal<T> = {
23
+ [K in keyof T]: T[K];
24
+ } & {};
package/dist/index.d.ts CHANGED
@@ -5,5 +5,10 @@ export type { ValidationStrategy, ValidationErrorMessage as ErrorMessage, Valida
5
5
  export type { DeepPartial } from './utils/type-helpers';
6
6
  export type { Form, FormField } from './types/form';
7
7
  export type { SplitPath, Paths, PickProps, ObjectOf, EntityPaths } from './types/util';
8
+ export type { FormComponentProps, ExcludedFieldProps } from './types/FormComponentProps';
8
9
  export { default as Field } from './components/Field.vue';
9
10
  export type { FieldProps } from './components/Field.vue';
11
+ export { default as FormFieldWrapper } from './components/FormFieldWrapper.vue';
12
+ export type { FormFieldWrapperProps } from './components/FormFieldWrapper.vue';
13
+ export { default as FormPart } from './components/FormPart.vue';
14
+ export type { FormPartProps } from './components/FormPart.vue';
@@ -1,45 +1,44 @@
1
- var x = Object.defineProperty;
2
- var K = (e, r, t) => r in e ? x(e, r, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[r] = t;
3
- var D = (e, r, t) => K(e, typeof r != "symbol" ? r + "" : r, t);
4
- import { toValue as W, toRaw as I, computed as u, unref as d, reactive as m, toRefs as O, toRef as S, ref as A, watch as E, isRef as _, getCurrentScope as J, onBeforeUnmount as T, defineComponent as B, renderSlot as G, normalizeProps as L, guardReactiveProps as U } from "vue";
1
+ var T = Object.defineProperty;
2
+ var G = (e, r, t) => r in e ? T(e, r, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[r] = t;
3
+ var _ = (e, r, t) => G(e, typeof r != "symbol" ? r + "" : r, t);
4
+ import { toValue as L, toRaw as Z, computed as u, unref as d, reactive as E, toRefs as N, toRef as D, ref as W, watch as y, isRef as z, getCurrentScope as q, onBeforeUnmount as H, defineComponent as S, renderSlot as j, normalizeProps as M, guardReactiveProps as B, resolveComponent as Q, createBlock as O, openBlock as $, withCtx as A, resolveDynamicComponent as X, mergeProps as Y } from "vue";
5
5
  import "zod";
6
- import "@vueuse/core";
7
- function y(e) {
8
- const r = W(e), t = I(r);
6
+ function g(e) {
7
+ const r = L(e), t = Z(r);
9
8
  return structuredClone(t);
10
9
  }
11
- function N(e) {
10
+ function K(e) {
12
11
  return e === "" ? [] : e.split(/\s*\.\s*/).filter(Boolean);
13
12
  }
14
13
  function w(e, r) {
15
- return (Array.isArray(r) ? r : N(r)).reduce(
14
+ return (Array.isArray(r) ? r : K(r)).reduce(
16
15
  (s, a) => s == null ? void 0 : s[a],
17
16
  e
18
17
  );
19
18
  }
20
- function Z(e, r, t) {
21
- const s = Array.isArray(r) ? r : N(r);
19
+ function x(e, r, t) {
20
+ const s = Array.isArray(r) ? r : K(r);
22
21
  if (s.length === 0)
23
22
  throw new Error("Path cannot be empty");
24
23
  const a = s.at(-1), o = s.slice(0, -1).reduce(
25
- (v, c) => v[c],
24
+ (h, c) => h[c],
26
25
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
26
  e
28
27
  );
29
28
  o[a] = t;
30
29
  }
31
- const z = (e, r) => u({
30
+ const U = (e, r) => u({
32
31
  get() {
33
32
  return w(d(e), d(r));
34
33
  },
35
34
  set(t) {
36
- Z(d(e), d(r), t);
35
+ x(d(e), d(r), t);
37
36
  }
38
37
  });
39
38
  function F(e, r) {
40
39
  return !e && !r ? "" : !e && r ? r : !r && e ? e : `${e}.${r}`;
41
40
  }
42
- function k(e, r) {
41
+ function rr(e, r) {
43
42
  if (!r)
44
43
  return e;
45
44
  const t = `${r}.`, s = Object.fromEntries(
@@ -53,11 +52,11 @@ function k(e, r) {
53
52
  propertyErrors: s
54
53
  };
55
54
  }
56
- function q(e) {
57
- const r = m({
55
+ function er(e) {
56
+ const r = E({
58
57
  value: e.value,
59
58
  path: e.path,
60
- initialValue: u(() => Object.freeze(y(e.initialValue))),
59
+ initialValue: u(() => Object.freeze(g(e.initialValue))),
61
60
  errors: e.errors,
62
61
  touched: !1
63
62
  }), t = u(() => JSON.stringify(r.value) !== JSON.stringify(r.initialValue)), s = (f) => {
@@ -65,13 +64,13 @@ function q(e) {
65
64
  }, a = () => {
66
65
  r.touched = !0;
67
66
  }, o = () => {
68
- }, v = () => {
69
- r.value = y(r.initialValue), r.touched = !1, r.errors = [];
67
+ }, h = () => {
68
+ r.value = g(r.initialValue), r.touched = !1, r.errors = [];
70
69
  }, c = (f) => {
71
70
  r.errors = f;
72
- }, i = () => {
71
+ }, n = () => {
73
72
  r.errors = [];
74
- }, l = O(r);
73
+ }, l = N(r);
75
74
  return {
76
75
  data: l.value,
77
76
  path: l.path,
@@ -82,20 +81,20 @@ function q(e) {
82
81
  setData: s,
83
82
  onBlur: a,
84
83
  onFocus: o,
85
- reset: v,
84
+ reset: h,
86
85
  setErrors: c,
87
- clearErrors: i
86
+ clearErrors: n
88
87
  };
89
88
  }
90
- function H(e, r) {
89
+ function tr(e, r) {
91
90
  const t = {}, s = (c) => {
92
- const i = d(c.path);
93
- t[i] = c;
91
+ const n = d(c.path);
92
+ t[n] = c;
94
93
  }, a = (c) => {
95
94
  if (!t[c]) {
96
- const i = q({
95
+ const n = er({
97
96
  path: c,
98
- value: z(S(e, "data"), c),
97
+ value: U(D(e, "data"), c),
99
98
  initialValue: u(() => w(e.initialData, c)),
100
99
  errors: u({
101
100
  get() {
@@ -106,7 +105,7 @@ function H(e, r) {
106
105
  }
107
106
  })
108
107
  });
109
- return s(i), i;
108
+ return s(n), n;
110
109
  }
111
110
  return t[c];
112
111
  };
@@ -118,19 +117,19 @@ function H(e, r) {
118
117
  defineField: (c) => a(c.path)
119
118
  };
120
119
  }
121
- function Q(e) {
120
+ function sr(e) {
122
121
  const r = u(() => e.getFields().some((s) => d(s.dirty))), t = u(() => e.getFields().some((s) => d(s.touched)));
123
122
  return {
124
123
  isDirty: r,
125
124
  isTouched: t
126
125
  };
127
126
  }
128
- function X(e) {
127
+ function ar(e) {
129
128
  return e.filter(
130
129
  (r, t, s) => s.indexOf(r) === t
131
130
  );
132
131
  }
133
- function C(...e) {
132
+ function k(...e) {
134
133
  return e.slice(1).reduce((r, t) => {
135
134
  if (!r && !t)
136
135
  return;
@@ -140,19 +139,19 @@ function C(...e) {
140
139
  if (!s)
141
140
  return r;
142
141
  const a = (r ?? []).concat(t);
143
- return X(a);
142
+ return ar(a);
144
143
  }, e[0]);
145
144
  }
146
- function Y(...e) {
145
+ function or(...e) {
147
146
  return e.map((t) => Object.keys(t)).flat().reduce((t, s) => {
148
147
  const a = e.map((o) => o[s]).filter(Boolean);
149
148
  return {
150
149
  ...t,
151
- [s]: C(...a)
150
+ [s]: k(...a)
152
151
  };
153
152
  }, {});
154
153
  }
155
- function P(...e) {
154
+ function R(...e) {
156
155
  if (!e.length)
157
156
  return {
158
157
  general: [],
@@ -161,18 +160,18 @@ function P(...e) {
161
160
  const r = e[0];
162
161
  return e.length === 1 ? r : e.slice(1).reduce(
163
162
  (t, s) => ({
164
- general: C(t.general, s.general),
165
- propertyErrors: Y(t.propertyErrors ?? {}, s.propertyErrors ?? {})
163
+ general: k(t.general, s.general),
164
+ propertyErrors: or(t.propertyErrors ?? {}, s.propertyErrors ?? {})
166
165
  }),
167
166
  r
168
167
  );
169
168
  }
170
- function j(e) {
169
+ function C(e) {
171
170
  var s;
172
171
  const r = (((s = e.general) == null ? void 0 : s.length) ?? 0) > 0, t = Object.entries(e.propertyErrors).filter(([, a]) => a == null ? void 0 : a.length).length > 0;
173
172
  return r || t;
174
173
  }
175
- function rr(e) {
174
+ function nr(e) {
176
175
  const r = e.issues.filter((s) => s.path.length === 0).map((s) => s.message), t = e.issues.filter((s) => s.path.length > 0).reduce((s, a) => {
177
176
  const o = a.path.join(".");
178
177
  return {
@@ -185,24 +184,24 @@ function rr(e) {
185
184
  propertyErrors: t
186
185
  };
187
186
  }
188
- const V = {
187
+ const m = {
189
188
  isValid: !0,
190
189
  errors: {
191
190
  general: [],
192
191
  propertyErrors: {}
193
192
  }
194
193
  };
195
- class er {
194
+ class ir {
196
195
  constructor(r) {
197
196
  this.schema = r;
198
197
  }
199
198
  async validate(r) {
200
199
  if (!this.schema)
201
- return V;
200
+ return m;
202
201
  const t = await this.schema.safeParseAsync(r);
203
202
  if (t.success)
204
- return V;
205
- const s = rr(t.error);
203
+ return m;
204
+ const s = nr(t.error);
206
205
  return {
207
206
  isValid: !1,
208
207
  errors: {
@@ -212,16 +211,16 @@ class er {
212
211
  };
213
212
  }
214
213
  }
215
- class tr {
214
+ class lr {
216
215
  constructor(r) {
217
216
  this.validateFn = r;
218
217
  }
219
218
  async validate(r) {
220
219
  if (!this.validateFn)
221
- return V;
220
+ return m;
222
221
  try {
223
222
  const t = await this.validateFn(r);
224
- return t.isValid ? V : t;
223
+ return t.isValid ? m : t;
225
224
  } catch (t) {
226
225
  return {
227
226
  isValid: !1,
@@ -233,11 +232,11 @@ class tr {
233
232
  }
234
233
  }
235
234
  }
236
- class sr {
235
+ class cr {
237
236
  constructor(r, t) {
238
- D(this, "schemaValidator");
239
- D(this, "functionValidator");
240
- this.schema = r, this.validateFn = t, this.schemaValidator = new er(this.schema), this.functionValidator = new tr(this.validateFn);
237
+ _(this, "schemaValidator");
238
+ _(this, "functionValidator");
239
+ this.schema = r, this.validateFn = t, this.schemaValidator = new ir(this.schema), this.functionValidator = new lr(this.validateFn);
241
240
  }
242
241
  async validate(r) {
243
242
  const [t, s] = await Promise.all([
@@ -246,85 +245,85 @@ class sr {
246
245
  ]);
247
246
  return {
248
247
  isValid: t.isValid && s.isValid,
249
- errors: P(t.errors, s.errors)
248
+ errors: R(t.errors, s.errors)
250
249
  };
251
250
  }
252
251
  }
253
252
  function b(e) {
254
- return u(() => new sr(
253
+ return u(() => new cr(
255
254
  d(e.schema),
256
255
  d(e.validateFn)
257
256
  ));
258
257
  }
259
- function ar(e, r) {
260
- const t = m({
261
- validators: A([b(r)]),
258
+ function ur(e, r) {
259
+ const t = E({
260
+ validators: W([b(r)]),
262
261
  isValidated: !1,
263
- errors: d(r.errors) ?? V.errors
262
+ errors: d(r.errors) ?? m.errors
264
263
  });
265
- E(() => d(r.errors), async () => {
266
- const i = await a();
267
- o(i.errors);
268
- }, { immediate: !0 }), E(
264
+ y(() => d(r.errors), async () => {
265
+ const n = await a();
266
+ o(n.errors);
267
+ }, { immediate: !0 }), y(
269
268
  [() => t.validators],
270
- async (i) => {
269
+ async (n) => {
271
270
  if (t.isValidated)
272
- if (i) {
271
+ if (n) {
273
272
  const l = await a();
274
273
  t.errors = l.errors;
275
274
  } else
276
- t.errors = V.errors;
275
+ t.errors = m.errors;
277
276
  },
278
277
  { immediate: !0 }
279
- ), E(() => e.data, () => {
280
- t.isValidated && v();
278
+ ), y(() => e.data, () => {
279
+ t.isValidated && h();
281
280
  });
282
- const s = (i) => {
283
- const l = _(i) ? i : b(i);
284
- return t.validators.push(l), J() && T(() => {
281
+ const s = (n) => {
282
+ const l = z(n) ? n : b(n);
283
+ return t.validators.push(l), q() && H(() => {
285
284
  t.validators = t.validators.filter(
286
285
  (f) => f !== l
287
286
  );
288
287
  }), l;
289
288
  };
290
289
  async function a() {
291
- const i = await Promise.all(
292
- t.validators.filter((p) => d(p) !== void 0).map((p) => d(p).validate(e.data))
293
- ), l = i.every((p) => p.isValid);
294
- let { errors: f } = V;
290
+ const n = await Promise.all(
291
+ t.validators.filter((v) => d(v) !== void 0).map((v) => d(v).validate(e.data))
292
+ ), l = n.every((v) => v.isValid);
293
+ let { errors: f } = m;
295
294
  if (!l) {
296
- const p = i.map((R) => R.errors);
297
- f = P(...p);
295
+ const v = n.map((P) => P.errors);
296
+ f = R(...v);
298
297
  }
299
298
  return {
300
299
  errors: f,
301
300
  isValid: l
302
301
  };
303
302
  }
304
- const o = (i) => {
305
- t.errors = P(d(r.errors) ?? V.errors, i);
306
- }, v = async () => {
307
- const i = await a();
308
- return o(i.errors), t.isValidated = !0, {
309
- isValid: !j(i.errors),
303
+ const o = (n) => {
304
+ t.errors = R(d(r.errors) ?? m.errors, n);
305
+ }, h = async () => {
306
+ const n = await a();
307
+ return o(n.errors), t.isValidated = !0, {
308
+ isValid: !C(n.errors),
310
309
  errors: t.errors
311
310
  };
312
- }, c = u(() => !j(t.errors));
311
+ }, c = u(() => !C(t.errors));
313
312
  return {
314
- ...O(t),
315
- validateForm: v,
313
+ ...N(t),
314
+ validateForm: h,
316
315
  defineValidator: s,
317
316
  isValid: c
318
317
  };
319
318
  }
320
- class ir {
319
+ class dr {
321
320
  constructor(r, t) {
322
321
  this.path = r, this.validator = t;
323
322
  }
324
323
  async validate(r) {
325
324
  const t = w(r, this.path);
326
325
  if (!this.validator)
327
- return V;
326
+ return m;
328
327
  const s = await this.validator.validate(t);
329
328
  return {
330
329
  isValid: s.isValid,
@@ -340,85 +339,85 @@ class ir {
340
339
  };
341
340
  }
342
341
  }
343
- function nr(e, r, t) {
344
- const s = z(e.data, r), a = u(() => w(e.initialData.value, r)), o = (n) => ({
345
- ...n,
346
- path: u(() => d(n.path).replace(r + ".", "")),
347
- setData: (h) => {
348
- n.setData(h);
342
+ function fr(e, r, t) {
343
+ const s = U(e.data, r), a = u(() => w(e.initialData.value, r)), o = (i) => ({
344
+ ...i,
345
+ path: u(() => d(i.path).replace(r + ".", "")),
346
+ setData: (p) => {
347
+ i.setData(p);
349
348
  }
350
- }), v = (n) => {
351
- const h = F(r, n), g = e.getField(h);
352
- return g ? o(g) : {};
353
- }, c = (n) => {
354
- const h = F(r, n.path), g = e.defineField({
355
- ...n,
356
- path: h
349
+ }), h = (i) => {
350
+ const p = F(r, i), V = e.getField(p);
351
+ return V ? o(V) : {};
352
+ }, c = (i) => {
353
+ const p = F(r, i.path), V = e.defineField({
354
+ ...i,
355
+ path: p
357
356
  });
358
- return o(g);
359
- }, i = () => e.getFields().filter((n) => {
360
- const h = n.path.value;
361
- return h.startsWith(r + ".") || h === r;
362
- }).map((n) => o(n)), l = () => e.getFields().filter((n) => {
363
- const h = n.path.value;
364
- return h.startsWith(r + ".") || h === r;
365
- }), f = u(() => l().some((n) => n.dirty.value)), p = u(() => l().some((n) => n.touched.value)), R = u(() => e.isValid.value), M = u(() => e.isValidated.value), $ = u(() => k(d(e.errors), r));
357
+ return o(V);
358
+ }, n = () => e.getFields().filter((i) => {
359
+ const p = i.path.value;
360
+ return p.startsWith(r + ".") || p === r;
361
+ }).map((i) => o(i)), l = () => e.getFields().filter((i) => {
362
+ const p = i.path.value;
363
+ return p.startsWith(r + ".") || p === r;
364
+ }), f = u(() => l().some((i) => i.dirty.value)), v = u(() => l().some((i) => i.touched.value)), P = u(() => e.isValid.value), I = u(() => e.isValidated.value), J = u(() => rr(d(e.errors), r));
366
365
  return {
367
366
  data: s,
368
367
  initialData: a,
369
368
  defineField: c,
370
- getField: v,
371
- getFields: i,
369
+ getField: h,
370
+ getFields: n,
372
371
  isDirty: f,
373
- isTouched: p,
374
- isValid: R,
375
- isValidated: M,
376
- errors: $,
377
- defineValidator: (n) => {
378
- const h = _(n) ? n : b(n), g = u(
379
- () => new ir(r, d(h))
372
+ isTouched: v,
373
+ isValid: P,
374
+ isValidated: I,
375
+ errors: J,
376
+ defineValidator: (i) => {
377
+ const p = z(i) ? i : b(i), V = u(
378
+ () => new dr(r, d(p))
380
379
  );
381
- return e.defineValidator(g), h;
380
+ return e.defineValidator(V), p;
382
381
  },
383
- reset: () => l().forEach((n) => n.reset()),
382
+ reset: () => l().forEach((i) => i.reset()),
384
383
  validateForm: () => e.validateForm(),
385
- getSubForm: (n, h) => {
386
- const g = F(r, n);
384
+ getSubForm: (i, p) => {
385
+ const V = F(r, i);
387
386
  return e.getSubForm(
388
- g,
389
- h
387
+ V,
388
+ p
390
389
  );
391
390
  }
392
391
  };
393
392
  }
394
- function pr(e) {
395
- const r = u(() => Object.freeze(y(e.initialData))), t = A(y(r)), s = m({
393
+ function Fr(e) {
394
+ const r = u(() => Object.freeze(g(e.initialData))), t = W(g(r)), s = E({
396
395
  initialData: r,
397
396
  data: t
398
397
  });
399
- E(r, (f) => {
400
- s.data = y(f);
398
+ y(r, (f) => {
399
+ s.data = g(f);
401
400
  });
402
- const a = ar(s, e), o = H(s, a), v = Q(o), c = () => {
403
- t.value = y(r), o.getFields().forEach(
401
+ const a = ur(s, e), o = tr(s, a), h = sr(o), c = () => {
402
+ t.value = g(r), o.getFields().forEach(
404
403
  (f) => f.reset()
405
404
  );
406
405
  };
407
- function i(f, p) {
408
- return nr(l, f);
406
+ function n(f, v) {
407
+ return fr(l, f);
409
408
  }
410
409
  const l = {
411
410
  ...o,
412
411
  ...a,
413
- ...v,
412
+ ...h,
414
413
  reset: c,
415
- getSubForm: i,
416
- initialData: S(s, "initialData"),
417
- data: S(s, "data")
414
+ getSubForm: n,
415
+ initialData: D(s, "initialData"),
416
+ data: D(s, "data")
418
417
  };
419
418
  return l;
420
419
  }
421
- const Vr = /* @__PURE__ */ B({
420
+ const Er = /* @__PURE__ */ S({
422
421
  __name: "Field",
423
422
  props: {
424
423
  form: {},
@@ -430,11 +429,56 @@ const Vr = /* @__PURE__ */ B({
430
429
  setup(e) {
431
430
  const r = e, t = r.form.defineField({
432
431
  path: r.path
433
- }), s = m(t);
434
- return (a, o) => G(a.$slots, "default", L(U(s)));
432
+ }), s = E(t);
433
+ return (a, o) => j(a.$slots, "default", M(B(s)));
434
+ }
435
+ }), wr = /* @__PURE__ */ S({
436
+ inheritAttrs: !1,
437
+ __name: "FormFieldWrapper",
438
+ props: {
439
+ component: {},
440
+ componentProps: {},
441
+ form: {},
442
+ path: {}
443
+ },
444
+ setup(e) {
445
+ return (r, t) => {
446
+ const s = Q("Field");
447
+ return $(), O(s, {
448
+ form: r.form,
449
+ path: r.path
450
+ }, {
451
+ default: A(({ errors: a, data: o, setData: h }) => [
452
+ ($(), O(X(r.component), Y({ ...r.componentProps, ...r.$attrs }, {
453
+ "model-value": o,
454
+ errors: a,
455
+ name: r.path,
456
+ "onUpdate:modelValue": h
457
+ }), {
458
+ default: A(() => [
459
+ j(r.$slots, "default")
460
+ ]),
461
+ _: 2
462
+ }, 1040, ["model-value", "errors", "name", "onUpdate:modelValue"]))
463
+ ]),
464
+ _: 3
465
+ }, 8, ["form", "path"]);
466
+ };
467
+ }
468
+ }), Pr = /* @__PURE__ */ S({
469
+ __name: "FormPart",
470
+ props: {
471
+ form: {},
472
+ path: {}
473
+ },
474
+ setup(e) {
475
+ const r = e, t = u(() => r.form.getSubForm(r.path));
476
+ return (s, a) => j(s.$slots, "default", M(B({ subform: t.value })));
435
477
  }
436
478
  });
437
479
  export {
438
- Vr as Field,
439
- pr as useForm
480
+ Er as Field,
481
+ wr as FormFieldWrapper,
482
+ Pr as FormPart,
483
+ Fr as useForm
440
484
  };
@@ -0,0 +1,8 @@
1
+ import { ObjectOf, Paths } from './util';
2
+ import { Form } from './form';
3
+ import { DeepPartial } from '../utils/type-helpers';
4
+ export interface FormComponentProps<TData extends object, TPath extends Paths<TData>, TModelValueType> {
5
+ form: Form<TData & DeepPartial<ObjectOf<TPath, TModelValueType>>>;
6
+ path: TPath;
7
+ }
8
+ export type ExcludedFieldProps = 'modelValue' | 'onUpdate:modelValue' | 'errors';
@@ -15,7 +15,7 @@ type CleanupAntiCollapse<T extends string> = T extends `${infer Left}${AntiColla
15
15
  type RecursePaths<T, Seen = never> = T extends Seen ? never : T extends Array<infer ArrayType> ? `${number}` | `${number}.${RecursePaths<ArrayType, Seen | T>}` : T extends object ? {
16
16
  [K in keyof T]-?: `${AntiCollapse}${Exclude<K, symbol>}${'' | `.${RecursePaths<T[K], Seen | T>}`}`;
17
17
  }[keyof T] : never;
18
- export type Paths<T, Seen = never> = CleanupAntiCollapse<RecursePaths<T, Seen>>;
18
+ export type Paths<T, Seen = never> = CleanupAntiCollapse<RecursePaths<T, Seen>> | '';
19
19
  /**
20
20
  * Removes the last part of a dot-connected path.
21
21
  */
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@teamnovu/kit-vue-forms",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "",
5
5
  "main": "index.js",
6
+ "type": "module",
6
7
  "exports": {
7
8
  ".": {
8
9
  "types": "./dist/index.d.ts",
@@ -29,6 +30,7 @@
29
30
  },
30
31
  "dependencies": {
31
32
  "@vueuse/core": "^13.5.0",
33
+ "vue-component-type-helpers": "^3.0.5",
32
34
  "zod": "^4"
33
35
  },
34
36
  "scripts": {
@@ -0,0 +1,42 @@
1
+ <template>
2
+ <Field
3
+ v-slot="{ errors, data, setData }"
4
+ :form="form"
5
+ :path="path"
6
+ >
7
+ <component
8
+ :is="component"
9
+ v-bind="{...componentProps, ...$attrs}"
10
+ :model-value="data"
11
+ :errors="errors"
12
+ :name="path"
13
+ @update:model-value="setData"
14
+ >
15
+ <slot />
16
+ </component>
17
+ </Field>
18
+ </template>
19
+
20
+ <script
21
+ setup
22
+ lang="ts"
23
+ generic="TData extends object, TPath extends Paths<TData>, TComponent extends Component"
24
+ >
25
+ import { type Component } from 'vue'
26
+ import type { ComponentProps } from 'vue-component-type-helpers'
27
+ import type { Paths } from '../types/util.ts'
28
+ import type { Form } from '../types/form.ts'
29
+
30
+ export interface FormFieldWrapperProps<TData extends object, TPath extends string, TComponent> {
31
+ component: TComponent
32
+ componentProps: Omit<ComponentProps<TComponent>, 'modelValue' | 'update:modelValue' | 'errors'>
33
+ form: Form<TData>
34
+ path: TPath
35
+ }
36
+
37
+ defineOptions({
38
+ inheritAttrs: false,
39
+ })
40
+
41
+ defineProps<FormFieldWrapperProps<TData, TPath, TComponent>>()
42
+ </script>
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <slot v-bind="{ subform }" />
3
+ </template>
4
+
5
+ <script
6
+ setup
7
+ lang="ts"
8
+ generic="TData extends object, TPath extends EntityPaths<TData>"
9
+ >
10
+ import { computed } from 'vue'
11
+ import type { Form } from '../types/form.ts'
12
+ import type { EntityPaths } from '../types/util.ts'
13
+
14
+ export interface FormPartProps<TData extends object, TPath> {
15
+ form: Form<TData>
16
+ path: TPath
17
+ }
18
+
19
+ const props = defineProps<FormPartProps<TData, TPath>>()
20
+
21
+ const subform = computed(() => props.form.getSubForm(props.path))
22
+ </script>
@@ -7,7 +7,6 @@ import { useFieldRegistry } from './useFieldRegistry'
7
7
  import { useFormState } from './useFormState'
8
8
  import { createSubformInterface, type SubformOptions } from './useSubform'
9
9
  import { useValidation, type ValidationOptions } from './useValidation'
10
- import { syncRef } from '@vueuse/core'
11
10
 
12
11
  // TODO @Elias implement validation strategy handling
13
12
 
package/src/index.ts CHANGED
@@ -12,5 +12,14 @@ export type { DeepPartial } from './utils/type-helpers'
12
12
  export type { Form, FormField } from './types/form'
13
13
  export type { SplitPath, Paths, PickProps, ObjectOf, EntityPaths } from './types/util'
14
14
 
15
+ export type { FormComponentProps, ExcludedFieldProps } from './types/FormComponentProps'
16
+
17
+ // Vue Components
15
18
  export { default as Field } from './components/Field.vue'
16
19
  export type { FieldProps } from './components/Field.vue'
20
+
21
+ export { default as FormFieldWrapper } from './components/FormFieldWrapper.vue'
22
+ export type { FormFieldWrapperProps } from './components/FormFieldWrapper.vue'
23
+
24
+ export { default as FormPart } from './components/FormPart.vue'
25
+ export type { FormPartProps } from './components/FormPart.vue'
@@ -0,0 +1,14 @@
1
+ import type { ObjectOf, Paths } from './util'
2
+ import type { Form } from './form'
3
+ import type { DeepPartial } from '../utils/type-helpers'
4
+
5
+ export interface FormComponentProps<
6
+ TData extends object,
7
+ TPath extends Paths<TData>,
8
+ TModelValueType,
9
+ > {
10
+ form: Form<TData & DeepPartial<ObjectOf<TPath, TModelValueType>>>
11
+ path: TPath
12
+ }
13
+
14
+ export type ExcludedFieldProps = 'modelValue' | 'onUpdate:modelValue' | 'errors'
package/src/types/util.ts CHANGED
@@ -59,7 +59,7 @@ type RecursePaths<T, Seen = never> =
59
59
  }[keyof T]
60
60
  : never
61
61
 
62
- export type Paths<T, Seen = never> = CleanupAntiCollapse<RecursePaths<T, Seen>>
62
+ export type Paths<T, Seen = never> = CleanupAntiCollapse<RecursePaths<T, Seen>> | ''
63
63
 
64
64
  /**
65
65
  * Removes the last part of a dot-connected path.
@@ -3,6 +3,7 @@ import { reactive } from 'vue'
3
3
  import { useFieldRegistry } from '../src/composables/useFieldRegistry'
4
4
  import { useFormState } from '../src/composables/useFormState'
5
5
  import { useForm } from '../src'
6
+ import { useValidation } from '../src/composables/useValidation'
6
7
 
7
8
  describe('useFormState', () => {
8
9
  it('should detect dirty state when form data changes', () => {
@@ -45,10 +46,12 @@ describe('useFormState', () => {
45
46
  email: 'john@example.com',
46
47
  }
47
48
  const data = reactive(initialData)
48
- const fields = useFieldRegistry({
49
+ const state = reactive({
49
50
  data,
50
51
  initialData,
51
52
  })
53
+ const validationState = useValidation(state, {})
54
+ const fields = useFieldRegistry(state, validationState)
52
55
 
53
56
  const nameField = fields.defineField({ path: 'name' })
54
57
  fields.defineField({ path: 'email' })
@@ -62,12 +65,16 @@ describe('useFormState', () => {
62
65
  })
63
66
 
64
67
  it('should handle empty fields map', () => {
65
- const initialData = { name: 'John' }
68
+ const initialData = {
69
+ name: 'John',
70
+ }
66
71
  const data = reactive(initialData)
67
- const fields = useFieldRegistry({
72
+ const state = reactive({
68
73
  data,
69
74
  initialData,
70
75
  })
76
+ const validationState = useValidation(state, {})
77
+ const fields = useFieldRegistry(state, validationState)
71
78
 
72
79
  const formState = useFormState(fields)
73
80
 
@@ -1345,4 +1345,23 @@ describe('Subform Implementation', () => {
1345
1345
  })
1346
1346
  })
1347
1347
  })
1348
+
1349
+ describe('Toplevel array subform', () => {
1350
+ it('can handle arrays on top level', async () => {
1351
+ const schema = z.array(z.string())
1352
+
1353
+ const form = useForm({
1354
+ initialData: { test: ['item1', 'item2'] },
1355
+ schema,
1356
+ })
1357
+
1358
+ const subform = form.getSubForm('test')
1359
+
1360
+ const rootField = subform.defineField({ path: '' })
1361
+ const itemField = subform.defineField({ path: '0' })
1362
+
1363
+ expect(rootField.data.value).toEqual(['item1', 'item2'])
1364
+ expect(itemField.data.value).toEqual('item1')
1365
+ })
1366
+ })
1348
1367
  })
@@ -193,4 +193,26 @@ describe('useForm', () => {
193
193
  expect(form.errors.value.general).toEqual([])
194
194
  expect(form.errors.value.propertyErrors).toEqual({})
195
195
  })
196
+
197
+ it('can handle arrays on top level', async () => {
198
+ const schema = z.array(z.string())
199
+
200
+ const form = useForm({
201
+ initialData: ['item1', 'item2'],
202
+ schema,
203
+ })
204
+
205
+ const result = await form.validateForm()
206
+
207
+ expect(result.isValid).toBe(true)
208
+ expect(form.isValidated.value).toBe(true)
209
+ expect(form.errors.value.general).toEqual([])
210
+ expect(form.errors.value.propertyErrors).toEqual({})
211
+
212
+ const rootField = form.defineField({ path: '' })
213
+ const itemField = form.defineField({ path: '1' })
214
+
215
+ expect(rootField.data.value).toEqual(['item1', 'item2'])
216
+ expect(itemField.data.value).toBe('item2')
217
+ })
196
218
  })
package/vitest.config.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { defineConfig } from 'vitest/config'
2
2
  import { resolve } from 'path'
3
+ import vue from '@vitejs/plugin-vue'
3
4
 
4
5
  export default defineConfig({
6
+ plugins: [vue()],
5
7
  test: {
6
8
  environment: 'happy-dom',
7
9
  globals: true,