@teamnovu/kit-vue-forms 0.1.24 → 0.1.26

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 CHANGED
@@ -571,7 +571,7 @@ function Ae(r, t, e, a) {
571
571
  );
572
572
  },
573
573
  submitHandler: (u) => L(M, e ?? {})(u),
574
- useFieldArray: (u, V) => G(M, u, V)
574
+ getFieldArray: (u, V) => G(M, u, V)
575
575
  };
576
576
  return M;
577
577
  }
@@ -607,7 +607,7 @@ function Ce(r) {
607
607
  data: j(a, "data"),
608
608
  submitHandler: (c) => L(F, r)(c),
609
609
  getSubForm: (c, i) => Ae(F, c, r),
610
- useFieldArray: (c, i) => G(F, c, i)
610
+ getFieldArray: (c, i) => G(F, c, i)
611
611
  };
612
612
  return F;
613
613
  }
@@ -61,5 +61,5 @@ export interface Form<T extends FormDataDefault> {
61
61
  validateForm: () => Promise<ValidationResult>;
62
62
  submitHandler: (onSubmit: (data: T) => Awaitable<void>) => (event: SubmitEvent) => Promise<void>;
63
63
  getSubForm: <P extends EntityPaths<T>>(path: P, options?: SubformOptions<PickEntity<T, P>>) => Form<PickEntity<T, P>>;
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>;
64
+ getFieldArray: <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>;
65
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamnovu/kit-vue-forms",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -80,7 +80,7 @@ export function useForm<T extends FormDataDefault>(
80
80
  getSubForm: (path, subformOptions) => {
81
81
  return createSubformInterface(form, path, options, subformOptions)
82
82
  },
83
- useFieldArray: (path, fieldArrayOptions) => {
83
+ getFieldArray: (path, fieldArrayOptions) => {
84
84
  return useFieldArray(form, path, fieldArrayOptions)
85
85
  },
86
86
  }
@@ -203,7 +203,7 @@ export function createSubformInterface<
203
203
  validateForm,
204
204
  getSubForm,
205
205
  submitHandler: onSubmit => makeSubmitHandler(subForm, formOptions ?? {})(onSubmit),
206
- useFieldArray: (fieldArrayPath, fieldArrayOptions) => {
206
+ getFieldArray: (fieldArrayPath, fieldArrayOptions) => {
207
207
  return useFieldArray(subForm, fieldArrayPath, fieldArrayOptions)
208
208
  },
209
209
  }
package/src/types/form.ts CHANGED
@@ -103,10 +103,10 @@ export interface Form<T extends FormDataDefault> {
103
103
  ) => Form<PickEntity<T, P>>
104
104
 
105
105
  // Field arrays
106
- useFieldArray: <K extends Paths<T>>(
106
+ getFieldArray: <K extends Paths<T>>(
107
107
  path: PickProps<T, K> extends unknown[] ? K : never,
108
108
  options?: FieldArrayOptions<
109
109
  PickProps<T, K> extends (infer U)[] ? U : never
110
110
  >,
111
- ) => FieldArray<PickProps<T, K> extends (infer U)[] ? U : never>
111
+ ) => FieldArray<PickProps<T, K> extends (infer U)[] ? U : never, typeof path>
112
112
  }
@@ -1,185 +1,185 @@
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[]>();
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: "A",
25
- };
24
+ name: 'A',
25
+ }
26
26
  const obj2 = {
27
27
  id: 1,
28
- name: "A",
29
- }; // Same content, different reference
28
+ name: 'A',
29
+ } // Same content, different reference
30
30
 
31
- store.set(obj1, ["uuid-1"]);
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(["uuid-1"]);
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("should use custom hash function for semantic equality", () => {
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: "A",
44
- };
43
+ name: 'A',
44
+ }
45
45
  const obj2 = {
46
46
  id: 1,
47
- name: "B",
48
- }; // Same ID, different name
47
+ name: 'B',
48
+ } // Same ID, different name
49
49
  const obj3 = {
50
50
  id: 2,
51
- name: "A",
52
- };
51
+ name: 'A',
52
+ }
53
53
 
54
- store.set(obj1, ["uuid-1"]);
54
+ store.set(obj1, ['uuid-1'])
55
55
 
56
- expect(store.has(obj2)).toBe(true);
57
- expect(store.get(obj2)).toEqual(["uuid-1"]);
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("should overwrite value on subsequent set calls", () => {
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, ["id-1"]);
66
- expect(store.get(item)).toEqual(["id-1"]);
65
+ store.set(item, ['id-1'])
66
+ expect(store.get(item)).toEqual(['id-1'])
67
67
 
68
- store.set(item, ["id-1", "id-2"]);
69
- expect(store.get(item)).toEqual(["id-1", "id-2"]);
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("ID Persistence", () => {
74
- it("should maintain IDs when items are reordered", async () => {
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: "First",
80
+ name: 'First',
81
81
  },
82
82
  {
83
83
  id: 2,
84
- name: "Second",
84
+ name: 'Second',
85
85
  },
86
86
  {
87
87
  id: 3,
88
- name: "Third",
88
+ name: 'Third',
89
89
  },
90
90
  ],
91
91
  },
92
- });
93
- const fieldArray = useFieldArray(form, "items", {
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("items");
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("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 () => {
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: "ABC",
125
- name: "Widget",
124
+ sku: 'ABC',
125
+ name: 'Widget',
126
126
  price: 10,
127
127
  },
128
128
  {
129
- sku: "DEF",
130
- name: "Gadget",
129
+ sku: 'DEF',
130
+ name: 'Gadget',
131
131
  price: 20,
132
132
  },
133
133
  ],
134
134
  },
135
- });
136
- const fieldArray = useFieldArray(form, "products", {
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("products");
144
+ const arrayField = form.getField('products')
145
145
  arrayField.setData([
146
146
  {
147
- sku: "DEF",
148
- name: "Gadget",
147
+ sku: 'DEF',
148
+ name: 'Gadget',
149
149
  price: 25,
150
150
  },
151
151
  {
152
- sku: "ABC",
153
- name: "Widget",
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: "XYZ",
167
- name: "Widget",
166
+ sku: 'XYZ',
167
+ name: 'Widget',
168
168
  price: 15,
169
169
  },
170
170
  {
171
- sku: "DEF",
172
- name: "Gadget",
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("should assign IDs in order for items with same hash", async () => {
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,164 +187,167 @@ describe("useFieldArray", () => {
187
187
  initialData: {
188
188
  items: [
189
189
  {
190
- type: "tag",
191
- value: "vue",
190
+ type: 'tag',
191
+ value: 'vue',
192
192
  },
193
193
  {
194
- type: "tag",
195
- value: "react",
194
+ type: 'tag',
195
+ value: 'react',
196
196
  },
197
197
  {
198
- type: "tag",
199
- value: "svelte",
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, "items", {
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("items");
214
+ const arrayField = form.getField('items')
215
215
  arrayField.setData([
216
216
  {
217
- type: "tag",
218
- value: "svelte",
217
+ type: 'tag',
218
+ value: 'svelte',
219
219
  },
220
220
  {
221
- type: "tag",
222
- value: "vue",
221
+ type: 'tag',
222
+ value: 'vue',
223
223
  },
224
224
  {
225
- type: "tag",
226
- value: "react",
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("svelte");
239
- expect(fieldArray.items.value[1].item.value).toBe("vue");
240
- expect(fieldArray.items.value[2].item.value).toBe("react");
241
- });
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
242
 
243
- it("should push an item to the array", async () => {
243
+ it('should push an item to the array', async () => {
244
244
  const form = useForm({
245
245
  initialData: {
246
246
  items: [
247
247
  {
248
248
  id: 1,
249
- name: "First",
249
+ name: 'First',
250
250
  },
251
251
  ],
252
252
  },
253
- });
254
- const fieldArray = useFieldArray(form, "items", {
253
+ })
254
+ const fieldArray = useFieldArray(form, 'items', {
255
255
  hashFn: (item: { id: number }) => item.id,
256
- });
256
+ })
257
257
 
258
258
  // Capture initial IDs
259
- const id1 = fieldArray.items.value[0].id;
259
+ const id1 = fieldArray.items.value[0].id
260
260
 
261
261
  // Push a new item
262
262
  const newItem = fieldArray.push({
263
263
  id: 2,
264
- name: "Second",
265
- });
264
+ name: 'Second',
265
+ })
266
266
 
267
- expect(newItem.item.name).toBe("Second");
268
- expect(newItem.path).toBe("items.1");
269
- expect(newItem.id).toBeDefined();
267
+ expect(newItem.item.name).toBe('Second')
268
+ expect(newItem.path).toBe('items.1')
269
+ expect(newItem.id).toBeDefined()
270
270
 
271
- await nextTick();
271
+ await nextTick()
272
272
 
273
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
- });
274
+ expect(fieldArray.items.value[0].id).toBe(id1)
275
+ expect(fieldArray.items.value[1].id).toBe(newItem.id)
276
+ })
277
277
 
278
- it("should remove an item from the array", async () => {
278
+ it('should remove an item from the array', async () => {
279
279
  const form = useForm({
280
280
  initialData: {
281
281
  items: [
282
282
  {
283
283
  id: 1,
284
- name: "First",
284
+ name: 'First',
285
285
  },
286
286
  {
287
287
  id: 2,
288
- name: "Second",
288
+ name: 'Second',
289
289
  },
290
290
  ],
291
291
  },
292
- });
293
- const fieldArray = useFieldArray(form, "items", {
292
+ })
293
+ const fieldArray = useFieldArray(form, 'items', {
294
294
  hashFn: (item: { id: number }) => item.id,
295
- });
295
+ })
296
296
 
297
297
  // Capture initial IDs
298
- const id1 = fieldArray.items.value[0].id;
298
+ const id1 = fieldArray.items.value[0].id
299
299
 
300
300
  // Remove an item
301
- fieldArray.remove(id1);
301
+ fieldArray.remove(id1)
302
302
 
303
- await nextTick();
303
+ await nextTick()
304
304
 
305
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
- });
306
+ expect(fieldArray.items.value.length).toBe(1)
307
+ expect(fieldArray.items.value[0].item.name).toBe('Second')
308
+ })
309
309
 
310
- it("should insert an item into the array", async () => {
310
+ it('should insert an item into the array', async () => {
311
311
  const form = useForm({
312
312
  initialData: {
313
313
  items: [
314
314
  {
315
315
  id: 1,
316
- name: "First",
316
+ name: 'First',
317
317
  },
318
318
  {
319
319
  id: 3,
320
- name: "Third",
320
+ name: 'Third',
321
321
  },
322
322
  ],
323
323
  },
324
- });
325
- const fieldArray = useFieldArray(form, "items", {
324
+ })
325
+ const fieldArray = useFieldArray(form, 'items', {
326
326
  hashFn: (item: { id: number }) => item.id,
327
- });
327
+ })
328
328
 
329
329
  // Capture initial IDs
330
- const id1 = fieldArray.items.value[0].id;
331
- const id3 = fieldArray.items.value[1].id;
330
+ const id1 = fieldArray.items.value[0].id
331
+ const id3 = fieldArray.items.value[1].id
332
332
 
333
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();
334
+ const insertedItem = fieldArray.insert({
335
+ id: 2,
336
+ name: 'Second',
337
+ }, 1)
338
+ expect(insertedItem.item.name).toBe('Second')
339
+ expect(insertedItem.path).toBe('items.1')
340
+ expect(insertedItem.id).toBeDefined()
338
341
 
339
- await nextTick();
342
+ await nextTick()
340
343
 
341
344
  // 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
- });
345
+ expect(fieldArray.items.value.length).toBe(3)
346
+ expect(fieldArray.items.value[0].id).toBe(id1)
347
+ expect(fieldArray.items.value[0].item.name).toBe('First')
348
+ expect(fieldArray.items.value[1].item.name).toBe('Second')
349
+ expect(fieldArray.items.value[2].id).toBe(id3)
350
+ expect(fieldArray.items.value[2].item.name).toBe('Third')
351
+ })
352
+ })
353
+ })