@juantroconisf/lib 5.2.0 → 6.0.0

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.d.mts CHANGED
@@ -81,8 +81,8 @@ interface FormOptions<O extends StateType> {
81
81
  */
82
82
  arrayIdentifiers?: Partial<Record<ArrayKeys<O>, string>>;
83
83
  }
84
- type TouchedType<O extends StateType> = Record<keyof O, boolean>;
85
- type ErrorsType<O extends StateType> = Record<keyof O, NextUIError>;
84
+ type TouchedType = Map<string, boolean>;
85
+ type ErrorsType = Map<string, NextUIError>;
86
86
  type ValueChangeFunc<O extends StateType, K extends keyof O> = (id: K, value: O[K]) => void;
87
87
  type BlurFunc<O extends StateType> = (id: keyof O) => void;
88
88
  interface ComponentInputProps<O extends StateType> {
@@ -130,6 +130,10 @@ interface OnMethods<O extends StateType> {
130
130
  type ArrayKeys<O extends StateType> = {
131
131
  [K in keyof O]: O[K] extends any[] ? K : never;
132
132
  }[keyof O];
133
+ /** Keys whose values are arrays of objects (not primitives). */
134
+ type ObjectArrayKeys<O extends StateType> = {
135
+ [K in keyof O]: O[K] extends Record<string, any>[] ? K : never;
136
+ }[keyof O];
133
137
  type ArrayElement<T> = T extends (infer E)[] ? E : never;
134
138
  /** Resolves the type of the identifier field for an array element (defaults to "id"). */
135
139
  type ItemIdType<O extends StateType, K extends keyof O> = "id" extends keyof ArrayElement<O[K]> ? ArrayElement<O[K]>["id"] : string | number;
@@ -170,12 +174,15 @@ interface HelpersFunc<O extends StateType> {
170
174
  */
171
175
  interface UseFormResponse<O extends StateType> {
172
176
  onBlur: BlurFunc<O>;
173
- onValueChange: ValueChangeFunc<O, keyof O>;
177
+ /** Updates an object array element's field by ID. */
178
+ onValueChange<K extends ObjectArrayKeys<O>, F extends keyof ArrayElement<O[K]> & string>(arrayKey: K, itemId: ItemIdType<O, K>, field: F, value: ArrayElement<O[K]>[F]): void;
179
+ /** Updates a scalar or nested object field. */
180
+ onValueChange<P extends AllPaths<O>>(id: P, value: NestedFieldValue<O, P & string>): void;
174
181
  onSelectionChange: ValueChangeFunc<O, keyof O>;
175
182
  state: O;
176
183
  setState: React.Dispatch<React.SetStateAction<O>>;
177
- touched: TouchedType<O>;
178
- errors: ErrorsType<O>;
184
+ touched: TouchedType;
185
+ errors: ErrorsType;
179
186
  /** Main object to bind form elements to the state. */
180
187
  on: OnMethods<O>;
181
188
  /** Array manipulation helpers. */
package/dist/index.d.ts CHANGED
@@ -81,8 +81,8 @@ interface FormOptions<O extends StateType> {
81
81
  */
82
82
  arrayIdentifiers?: Partial<Record<ArrayKeys<O>, string>>;
83
83
  }
84
- type TouchedType<O extends StateType> = Record<keyof O, boolean>;
85
- type ErrorsType<O extends StateType> = Record<keyof O, NextUIError>;
84
+ type TouchedType = Map<string, boolean>;
85
+ type ErrorsType = Map<string, NextUIError>;
86
86
  type ValueChangeFunc<O extends StateType, K extends keyof O> = (id: K, value: O[K]) => void;
87
87
  type BlurFunc<O extends StateType> = (id: keyof O) => void;
88
88
  interface ComponentInputProps<O extends StateType> {
@@ -130,6 +130,10 @@ interface OnMethods<O extends StateType> {
130
130
  type ArrayKeys<O extends StateType> = {
131
131
  [K in keyof O]: O[K] extends any[] ? K : never;
132
132
  }[keyof O];
133
+ /** Keys whose values are arrays of objects (not primitives). */
134
+ type ObjectArrayKeys<O extends StateType> = {
135
+ [K in keyof O]: O[K] extends Record<string, any>[] ? K : never;
136
+ }[keyof O];
133
137
  type ArrayElement<T> = T extends (infer E)[] ? E : never;
134
138
  /** Resolves the type of the identifier field for an array element (defaults to "id"). */
135
139
  type ItemIdType<O extends StateType, K extends keyof O> = "id" extends keyof ArrayElement<O[K]> ? ArrayElement<O[K]>["id"] : string | number;
@@ -170,12 +174,15 @@ interface HelpersFunc<O extends StateType> {
170
174
  */
171
175
  interface UseFormResponse<O extends StateType> {
172
176
  onBlur: BlurFunc<O>;
173
- onValueChange: ValueChangeFunc<O, keyof O>;
177
+ /** Updates an object array element's field by ID. */
178
+ onValueChange<K extends ObjectArrayKeys<O>, F extends keyof ArrayElement<O[K]> & string>(arrayKey: K, itemId: ItemIdType<O, K>, field: F, value: ArrayElement<O[K]>[F]): void;
179
+ /** Updates a scalar or nested object field. */
180
+ onValueChange<P extends AllPaths<O>>(id: P, value: NestedFieldValue<O, P & string>): void;
174
181
  onSelectionChange: ValueChangeFunc<O, keyof O>;
175
182
  state: O;
176
183
  setState: React.Dispatch<React.SetStateAction<O>>;
177
- touched: TouchedType<O>;
178
- errors: ErrorsType<O>;
184
+ touched: TouchedType;
185
+ errors: ErrorsType;
179
186
  /** Main object to bind form elements to the state. */
180
187
  on: OnMethods<O>;
181
188
  /** Array manipulation helpers. */
package/dist/index.js CHANGED
@@ -82,10 +82,10 @@ function getNestedValue(obj, path) {
82
82
  return path.split(".").reduce((acc, key) => acc?.[key], obj);
83
83
  }
84
84
  function removeCompositeKeysByPrefix(map2, prefix) {
85
- const result = {};
86
- for (const [key, value] of Object.entries(map2)) {
87
- if (!key.startsWith(prefix)) {
88
- result[key] = value;
85
+ const result = new Map(map2);
86
+ for (const key of map2.keys()) {
87
+ if (key.startsWith(prefix)) {
88
+ result.delete(key);
89
89
  }
90
90
  }
91
91
  return result;
@@ -101,13 +101,6 @@ function setNestedValue(state, dotPath, value) {
101
101
  current[keys[keys.length - 1]] = value;
102
102
  return copy;
103
103
  }
104
- var allToValue = (obj, value) => Object.keys(obj).reduce(
105
- (acc, key) => {
106
- acc[key] = value;
107
- return acc;
108
- },
109
- {}
110
- );
111
104
  var regex = {
112
105
  email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
113
106
  password: /^(?!.*\s)(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[~`!@#$%^&*()--+={}[\]|:;"'<>,.?/_₹]).{8,24}$/
@@ -233,14 +226,7 @@ function useForm(initialState, {
233
226
  arrayMessages,
234
227
  arrayIdentifiers
235
228
  } = {}) {
236
- const initial = {
237
- touched: allToValue(initialState, false),
238
- errors: allToValue(initialState, {
239
- isInvalid: false,
240
- errorMessage: ""
241
- })
242
- };
243
- const [state, setState] = (0, import_react.useState)(initialState), [touched, setTouched] = (0, import_react.useState)(initial.touched), [errors2, setErrors] = (0, import_react.useState)(initial.errors), [itemTouched, setItemTouched] = (0, import_react.useState)({}), [itemErrors, setItemErrors] = (0, import_react.useState)({}), [nestedTouched, setNestedTouched] = (0, import_react.useState)({}), [nestedErrors, setNestedErrors] = (0, import_react.useState)({}), { performValidations } = useValidate();
229
+ const [state, setState] = (0, import_react.useState)(initialState), [touched, setTouched] = (0, import_react.useState)(/* @__PURE__ */ new Map()), [errors2, setErrors] = (0, import_react.useState)(/* @__PURE__ */ new Map()), { performValidations } = useValidate();
244
230
  const indexMap = (0, import_react.useMemo)(() => {
245
231
  const map2 = /* @__PURE__ */ new Map();
246
232
  for (const key in state) {
@@ -266,38 +252,71 @@ function useForm(initialState, {
266
252
  },
267
253
  [indexMap]
268
254
  );
269
- const validateInput = (id, value) => setErrors(
270
- (prev) => handleNestedChange({
271
- state: prev,
272
- id,
273
- value: performValidations(value, rules?.[id], messages?.[id])
274
- })
275
- );
255
+ const validateInput = (compositeKey, value, ruleKey) => {
256
+ const error = performValidations(
257
+ value,
258
+ rules?.[ruleKey],
259
+ messages?.[ruleKey]
260
+ );
261
+ setErrors((prev) => new Map(prev).set(compositeKey, error));
262
+ };
276
263
  const getUXProps = (0, import_react.useCallback)(
277
- (id) => {
278
- const inputError = errors2[id] || nestedErrors[id];
279
- const isTouched = touched[id] || nestedTouched[id];
264
+ (compositeKey) => {
265
+ const inputError = errors2.get(compositeKey);
266
+ const isTouched = touched.get(compositeKey);
280
267
  return {
281
268
  isInvalid: Boolean(isTouched && inputError?.isInvalid),
282
269
  errorMessage: isTouched ? inputError?.errorMessage || "" : ""
283
270
  };
284
271
  },
285
- [errors2, touched, nestedErrors, nestedTouched]
272
+ [errors2, touched]
286
273
  );
287
274
  const onBlur = (id) => {
288
- validateInput(id, state[id]);
289
- if (touched[id]) return;
290
- setTouched(
291
- (prev) => handleNestedChange({
292
- state: prev,
293
- id,
294
- value: true
295
- })
296
- );
297
- }, onValueChange = (id, value) => {
298
- setState((prev) => handleNestedChange({ state: prev, id, value }));
299
- validateInput(id, value);
300
- }, onSelectionChange = (id, value) => {
275
+ validateInput(String(id), state[id], id);
276
+ if (touched.get(String(id))) return;
277
+ setTouched((prev) => new Map(prev).set(String(id), true));
278
+ }, onValueChange = ((...args) => {
279
+ if (args.length === 2) {
280
+ const [id, value] = args;
281
+ setState((prev) => handleNestedChange({ state: prev, id, value }));
282
+ validateInput(String(id), value, id);
283
+ return;
284
+ }
285
+ if (args.length === 3) {
286
+ const [arrayKey, index, value] = args;
287
+ setState((prev) => {
288
+ const arr = [...prev[arrayKey]];
289
+ arr[index] = value;
290
+ return { ...prev, [arrayKey]: arr };
291
+ });
292
+ return;
293
+ }
294
+ if (args.length === 4) {
295
+ const [arrayKey, itemId, field, value] = args;
296
+ const index = getIndex(String(arrayKey), itemId);
297
+ if (index === void 0) return;
298
+ setState(
299
+ (prev) => handleArrayItemChange({ state: prev, arrayKey, index, field, value })
300
+ );
301
+ validateItemInput(arrayKey, itemId, field, value);
302
+ return;
303
+ }
304
+ if (args.length === 5) {
305
+ const [parentKey, parentId, field, index, value] = args;
306
+ const parentIndex = getIndex(String(parentKey), parentId);
307
+ if (parentIndex === void 0) return;
308
+ setState((prev) => {
309
+ const parentArr = [...prev[parentKey]];
310
+ const item = { ...parentArr[parentIndex] };
311
+ const nestedArr = [...getNestedValue(item, field) || []];
312
+ nestedArr[index] = value;
313
+ const updatedItem = setNestedValue(item, field, nestedArr);
314
+ parentArr[parentIndex] = updatedItem;
315
+ return { ...prev, [parentKey]: parentArr };
316
+ });
317
+ return;
318
+ }
319
+ }), onSelectionChange = (id, value) => {
301
320
  const fixedValue = typeof value === "string" || value === null ? value : Array.from(value);
302
321
  setState(
303
322
  (prev) => handleNestedChange({
@@ -306,7 +325,7 @@ function useForm(initialState, {
306
325
  value: fixedValue
307
326
  })
308
327
  );
309
- validateInput(id, fixedValue);
328
+ validateInput(String(id), fixedValue, id);
310
329
  };
311
330
  const getItemCompositeKey = (arrayKey, itemId, field) => `${arrayKey}.${itemId}.${field}`;
312
331
  const getPrimitiveCompositeKey = (arrayKey, index) => `${arrayKey}.@${index}`;
@@ -319,22 +338,24 @@ function useForm(initialState, {
319
338
  const fieldRules = typeof arrayRuleDef === "function" ? arrayRuleDef(item, index) : arrayRuleDef;
320
339
  const fieldMessages = arrayMessages?.[arrayKey]?.[field];
321
340
  const compositeKey = getItemCompositeKey(String(arrayKey), itemId, field);
322
- setItemErrors((prev) => ({
323
- ...prev,
324
- [compositeKey]: performValidations(value, fieldRules, fieldMessages)
325
- }));
341
+ setErrors(
342
+ (prev) => new Map(prev).set(
343
+ compositeKey,
344
+ performValidations(value, fieldRules, fieldMessages)
345
+ )
346
+ );
326
347
  };
327
348
  const getItemUXProps = (0, import_react.useCallback)(
328
349
  (arrayKey, itemId, field) => {
329
350
  const compositeKey = getItemCompositeKey(arrayKey, itemId, field);
330
- const inputError = itemErrors[compositeKey];
331
- const isTouched = itemTouched[compositeKey];
351
+ const inputError = errors2.get(compositeKey);
352
+ const isTouched = touched.get(compositeKey);
332
353
  return {
333
354
  isInvalid: Boolean(isTouched && inputError?.isInvalid),
334
355
  errorMessage: isTouched ? inputError?.errorMessage || "" : ""
335
356
  };
336
357
  },
337
- [itemErrors, itemTouched]
358
+ [errors2, touched]
338
359
  );
339
360
  const onItemBlur = (arrayKey, itemId, field) => {
340
361
  const index = getIndex(String(arrayKey), itemId);
@@ -343,8 +364,8 @@ function useForm(initialState, {
343
364
  const value = getNestedValue(arr[index], field);
344
365
  validateItemInput(arrayKey, itemId, field, value);
345
366
  const compositeKey = getItemCompositeKey(String(arrayKey), itemId, field);
346
- if (itemTouched[compositeKey]) return;
347
- setItemTouched((prev) => ({ ...prev, [compositeKey]: true }));
367
+ if (touched.get(compositeKey)) return;
368
+ setTouched((prev) => new Map(prev).set(compositeKey, true));
348
369
  };
349
370
  const onItemValueChange = (arrayKey, itemId, field, value) => {
350
371
  const index = getIndex(String(arrayKey), itemId);
@@ -374,16 +395,18 @@ function useForm(initialState, {
374
395
  };
375
396
  const validateNestedInput = (dotPath, value) => {
376
397
  const topKey = dotPath.split(".")[0];
377
- setNestedErrors((prev) => ({
378
- ...prev,
379
- [dotPath]: performValidations(value, rules?.[topKey], messages?.[topKey])
380
- }));
398
+ setErrors(
399
+ (prev) => new Map(prev).set(
400
+ dotPath,
401
+ performValidations(value, rules?.[topKey], messages?.[topKey])
402
+ )
403
+ );
381
404
  };
382
405
  const onNestedBlur = (dotPath) => {
383
406
  const value = getNestedValue(state, dotPath);
384
407
  validateNestedInput(dotPath, value);
385
- if (nestedTouched[dotPath]) return;
386
- setNestedTouched((prev) => ({ ...prev, [dotPath]: true }));
408
+ if (touched.get(dotPath)) return;
409
+ setTouched((prev) => new Map(prev).set(dotPath, true));
387
410
  };
388
411
  const onNestedValueChange = (dotPath, value) => {
389
412
  setState((prev) => setNestedValue(prev, dotPath, value));
@@ -428,12 +451,8 @@ function useForm(initialState, {
428
451
  ...getItemUXProps(String(arrayKey2), `@${index2}`, ""),
429
452
  id: compositeKey2,
430
453
  onBlur: () => {
431
- const compositeKey3 = getPrimitiveCompositeKey(
432
- String(arrayKey2),
433
- index2
434
- );
435
- if (itemTouched[compositeKey3]) return;
436
- setItemTouched((prev) => ({ ...prev, [compositeKey3]: true }));
454
+ if (touched.get(compositeKey2)) return;
455
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
437
456
  },
438
457
  onValueChange: (v) => {
439
458
  setState((prev) => {
@@ -441,6 +460,7 @@ function useForm(initialState, {
441
460
  arr3[index2] = v;
442
461
  return { ...prev, [arrayKey2]: arr3 };
443
462
  });
463
+ validateItemInput(arrayKey2, `@${index2}`, "", v);
444
464
  },
445
465
  value: value2
446
466
  };
@@ -461,8 +481,8 @@ function useForm(initialState, {
461
481
  ...getItemUXProps(String(parentKey), parentId, `${field2}.@${index2}`),
462
482
  id: compositeKey2,
463
483
  onBlur: () => {
464
- if (itemTouched[compositeKey2]) return;
465
- setItemTouched((prev) => ({ ...prev, [compositeKey2]: true }));
484
+ if (touched.get(compositeKey2)) return;
485
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
466
486
  },
467
487
  onValueChange: (v) => {
468
488
  if (parentIndex === void 0) return;
@@ -474,6 +494,8 @@ function useForm(initialState, {
474
494
  parentArr[parentIndex] = setNestedValue(item, field2, nestedArr2);
475
495
  return { ...prev, [parentKey]: parentArr };
476
496
  });
497
+ const fieldPath = `${field2}.@${index2}`;
498
+ validateItemInput(parentKey, parentId, fieldPath, v);
477
499
  },
478
500
  value: value2
479
501
  };
@@ -528,8 +550,8 @@ function useForm(initialState, {
528
550
  ...getItemUXProps(String(arrayKey2), `@${index2}`, ""),
529
551
  id: compositeKey2,
530
552
  onBlur: () => {
531
- if (itemTouched[compositeKey2]) return;
532
- setItemTouched((prev) => ({ ...prev, [compositeKey2]: true }));
553
+ if (touched.get(compositeKey2)) return;
554
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
533
555
  },
534
556
  onSelectionChange: (v) => {
535
557
  const fixedValue = typeof v === "string" || v === null ? v : Array.from(v)[0] || null;
@@ -538,6 +560,7 @@ function useForm(initialState, {
538
560
  arr3[index2] = fixedValue;
539
561
  return { ...prev, [arrayKey2]: arr3 };
540
562
  });
563
+ validateItemInput(arrayKey2, `@${index2}`, "", fixedValue);
541
564
  },
542
565
  selectedKeys: value2 === null ? [] : [value2]
543
566
  };
@@ -558,8 +581,8 @@ function useForm(initialState, {
558
581
  ...getItemUXProps(String(parentKey), parentId, `${field2}.@${index2}`),
559
582
  id: compositeKey2,
560
583
  onBlur: () => {
561
- if (itemTouched[compositeKey2]) return;
562
- setItemTouched((prev) => ({ ...prev, [compositeKey2]: true }));
584
+ if (touched.get(compositeKey2)) return;
585
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
563
586
  },
564
587
  onSelectionChange: (v) => {
565
588
  if (parentIndex === void 0) return;
@@ -572,6 +595,8 @@ function useForm(initialState, {
572
595
  parentArr[parentIndex] = setNestedValue(item, field2, nestedArr2);
573
596
  return { ...prev, [parentKey]: parentArr };
574
597
  });
598
+ const fieldPath = `${field2}.@${index2}`;
599
+ validateItemInput(parentKey, parentId, fieldPath, fixedValue);
575
600
  },
576
601
  selectedKeys: value2 === null ? [] : [value2]
577
602
  };
@@ -621,15 +646,17 @@ function useForm(initialState, {
621
646
  ...getItemUXProps(String(arrayKey2), `@${index2}`, ""),
622
647
  id: compositeKey2,
623
648
  onBlur: () => {
624
- if (itemTouched[compositeKey2]) return;
625
- setItemTouched((prev) => ({ ...prev, [compositeKey2]: true }));
649
+ if (touched.get(compositeKey2)) return;
650
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
626
651
  },
627
652
  onSelectionChange: (v) => {
653
+ const fixedValue = typeof v === "string" || v === null ? v : String(v);
628
654
  setState((prev) => {
629
655
  const arr3 = [...prev[arrayKey2]];
630
- arr3[index2] = typeof v === "string" || v === null ? v : String(v);
656
+ arr3[index2] = fixedValue;
631
657
  return { ...prev, [arrayKey2]: arr3 };
632
658
  });
659
+ validateItemInput(arrayKey2, `@${index2}`, "", fixedValue);
633
660
  },
634
661
  selectedKey: value2
635
662
  };
@@ -650,19 +677,22 @@ function useForm(initialState, {
650
677
  ...getItemUXProps(String(parentKey), parentId, `${field2}.@${index2}`),
651
678
  id: compositeKey2,
652
679
  onBlur: () => {
653
- if (itemTouched[compositeKey2]) return;
654
- setItemTouched((prev) => ({ ...prev, [compositeKey2]: true }));
680
+ if (touched.get(compositeKey2)) return;
681
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
655
682
  },
656
683
  onSelectionChange: (v) => {
657
684
  if (parentIndex === void 0) return;
685
+ const fixedValue = typeof v === "string" || v === null ? v : String(v);
658
686
  setState((prev) => {
659
687
  const parentArr = [...prev[parentKey]];
660
688
  const item = { ...parentArr[parentIndex] };
661
689
  const nestedArr2 = [...getNestedValue(item, field2) || []];
662
- nestedArr2[index2] = typeof v === "string" || v === null ? v : String(v);
690
+ nestedArr2[index2] = fixedValue;
663
691
  parentArr[parentIndex] = setNestedValue(item, field2, nestedArr2);
664
692
  return { ...prev, [parentKey]: parentArr };
665
693
  });
694
+ const fieldPath = `${field2}.@${index2}`;
695
+ validateItemInput(parentKey, parentId, fieldPath, fixedValue);
666
696
  },
667
697
  selectedKey: value2
668
698
  };
@@ -713,8 +743,8 @@ function useForm(initialState, {
713
743
  });
714
744
  if (itemId !== void 0) {
715
745
  const prefix = `${String(arrayKey)}.${itemId}.`;
716
- setItemTouched((prev) => removeCompositeKeysByPrefix(prev, prefix));
717
- setItemErrors((prev) => removeCompositeKeysByPrefix(prev, prefix));
746
+ setTouched((prev) => removeCompositeKeysByPrefix(prev, prefix));
747
+ setErrors((prev) => removeCompositeKeysByPrefix(prev, prefix));
718
748
  }
719
749
  },
720
750
  removeById: (arrayKey, itemId) => {
@@ -724,8 +754,8 @@ function useForm(initialState, {
724
754
  arr.splice(index, 1);
725
755
  setState((prev) => ({ ...prev, [arrayKey]: arr }));
726
756
  const prefix = `${String(arrayKey)}.${itemId}.`;
727
- setItemTouched((prev) => removeCompositeKeysByPrefix(prev, prefix));
728
- setItemErrors((prev) => removeCompositeKeysByPrefix(prev, prefix));
757
+ setTouched((prev) => removeCompositeKeysByPrefix(prev, prefix));
758
+ setErrors((prev) => removeCompositeKeysByPrefix(prev, prefix));
729
759
  }
730
760
  },
731
761
  moveItem: (arrayKey, from, to) => {
@@ -765,35 +795,12 @@ function useForm(initialState, {
765
795
  },
766
796
  isDirty: JSON.stringify(state) !== JSON.stringify(initialState),
767
797
  hasInvalidValues: () => {
768
- setTouched((prev) => allToValue(prev, true));
769
- setItemTouched(
770
- (prev) => Object.keys(prev).reduce(
771
- (acc, key) => ({ ...acc, [key]: true }),
772
- {}
773
- )
774
- );
775
- setNestedTouched(
776
- (prev) => Object.keys(prev).reduce(
777
- (acc, key) => ({ ...acc, [key]: true }),
778
- {}
779
- )
780
- );
781
- const topLevelInvalid = JSON.stringify(errors2).includes(":true");
782
- const itemLevelInvalid = Object.values(itemErrors).some(
783
- (e) => e?.isInvalid
784
- );
785
- const nestedLevelInvalid = Object.values(nestedErrors).some(
786
- (e) => e?.isInvalid
787
- );
788
- return topLevelInvalid || itemLevelInvalid || nestedLevelInvalid;
798
+ const isInvalid = Array.from(errors2.values()).some((e) => e?.isInvalid);
799
+ return isInvalid;
789
800
  },
790
801
  resetForm: (preservedKeys) => {
791
- setTouched(initial.touched);
792
- setErrors(initial.errors);
793
- setItemTouched({});
794
- setItemErrors({});
795
- setNestedTouched({});
796
- setNestedErrors({});
802
+ setTouched(/* @__PURE__ */ new Map());
803
+ setErrors(/* @__PURE__ */ new Map());
797
804
  setState(
798
805
  (prev) => preservedKeys === void 0 ? initialState : preservedKeys.reduce(
799
806
  (acc, key) => ({
@@ -805,16 +812,13 @@ function useForm(initialState, {
805
812
  );
806
813
  },
807
814
  resetTouched: (preservedKeys) => {
808
- setItemTouched({});
809
- setNestedTouched({});
810
815
  setTouched(
811
- (prev) => preservedKeys === void 0 ? initialState : preservedKeys.reduce(
812
- (acc, key) => ({
813
- ...acc,
814
- [key]: prev[key]
815
- }),
816
- initial.touched
817
- )
816
+ (prev) => preservedKeys === void 0 ? /* @__PURE__ */ new Map() : preservedKeys.reduce((acc, key) => {
817
+ if (prev.has(String(key))) {
818
+ acc.set(String(key), prev.get(String(key)));
819
+ }
820
+ return acc;
821
+ }, /* @__PURE__ */ new Map())
818
822
  );
819
823
  }
820
824
  };
package/dist/index.mjs CHANGED
@@ -63,10 +63,10 @@ function getNestedValue(obj, path) {
63
63
  return path.split(".").reduce((acc, key) => acc?.[key], obj);
64
64
  }
65
65
  function removeCompositeKeysByPrefix(map2, prefix) {
66
- const result = {};
67
- for (const [key, value] of Object.entries(map2)) {
68
- if (!key.startsWith(prefix)) {
69
- result[key] = value;
66
+ const result = new Map(map2);
67
+ for (const key of map2.keys()) {
68
+ if (key.startsWith(prefix)) {
69
+ result.delete(key);
70
70
  }
71
71
  }
72
72
  return result;
@@ -82,13 +82,6 @@ function setNestedValue(state, dotPath, value) {
82
82
  current[keys[keys.length - 1]] = value;
83
83
  return copy;
84
84
  }
85
- var allToValue = (obj, value) => Object.keys(obj).reduce(
86
- (acc, key) => {
87
- acc[key] = value;
88
- return acc;
89
- },
90
- {}
91
- );
92
85
  var regex = {
93
86
  email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
94
87
  password: /^(?!.*\s)(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[~`!@#$%^&*()--+={}[\]|:;"'<>,.?/_₹]).{8,24}$/
@@ -214,14 +207,7 @@ function useForm(initialState, {
214
207
  arrayMessages,
215
208
  arrayIdentifiers
216
209
  } = {}) {
217
- const initial = {
218
- touched: allToValue(initialState, false),
219
- errors: allToValue(initialState, {
220
- isInvalid: false,
221
- errorMessage: ""
222
- })
223
- };
224
- const [state, setState] = useState(initialState), [touched, setTouched] = useState(initial.touched), [errors2, setErrors] = useState(initial.errors), [itemTouched, setItemTouched] = useState({}), [itemErrors, setItemErrors] = useState({}), [nestedTouched, setNestedTouched] = useState({}), [nestedErrors, setNestedErrors] = useState({}), { performValidations } = useValidate();
210
+ const [state, setState] = useState(initialState), [touched, setTouched] = useState(/* @__PURE__ */ new Map()), [errors2, setErrors] = useState(/* @__PURE__ */ new Map()), { performValidations } = useValidate();
225
211
  const indexMap = useMemo(() => {
226
212
  const map2 = /* @__PURE__ */ new Map();
227
213
  for (const key in state) {
@@ -247,38 +233,71 @@ function useForm(initialState, {
247
233
  },
248
234
  [indexMap]
249
235
  );
250
- const validateInput = (id, value) => setErrors(
251
- (prev) => handleNestedChange({
252
- state: prev,
253
- id,
254
- value: performValidations(value, rules?.[id], messages?.[id])
255
- })
256
- );
236
+ const validateInput = (compositeKey, value, ruleKey) => {
237
+ const error = performValidations(
238
+ value,
239
+ rules?.[ruleKey],
240
+ messages?.[ruleKey]
241
+ );
242
+ setErrors((prev) => new Map(prev).set(compositeKey, error));
243
+ };
257
244
  const getUXProps = useCallback(
258
- (id) => {
259
- const inputError = errors2[id] || nestedErrors[id];
260
- const isTouched = touched[id] || nestedTouched[id];
245
+ (compositeKey) => {
246
+ const inputError = errors2.get(compositeKey);
247
+ const isTouched = touched.get(compositeKey);
261
248
  return {
262
249
  isInvalid: Boolean(isTouched && inputError?.isInvalid),
263
250
  errorMessage: isTouched ? inputError?.errorMessage || "" : ""
264
251
  };
265
252
  },
266
- [errors2, touched, nestedErrors, nestedTouched]
253
+ [errors2, touched]
267
254
  );
268
255
  const onBlur = (id) => {
269
- validateInput(id, state[id]);
270
- if (touched[id]) return;
271
- setTouched(
272
- (prev) => handleNestedChange({
273
- state: prev,
274
- id,
275
- value: true
276
- })
277
- );
278
- }, onValueChange = (id, value) => {
279
- setState((prev) => handleNestedChange({ state: prev, id, value }));
280
- validateInput(id, value);
281
- }, onSelectionChange = (id, value) => {
256
+ validateInput(String(id), state[id], id);
257
+ if (touched.get(String(id))) return;
258
+ setTouched((prev) => new Map(prev).set(String(id), true));
259
+ }, onValueChange = ((...args) => {
260
+ if (args.length === 2) {
261
+ const [id, value] = args;
262
+ setState((prev) => handleNestedChange({ state: prev, id, value }));
263
+ validateInput(String(id), value, id);
264
+ return;
265
+ }
266
+ if (args.length === 3) {
267
+ const [arrayKey, index, value] = args;
268
+ setState((prev) => {
269
+ const arr = [...prev[arrayKey]];
270
+ arr[index] = value;
271
+ return { ...prev, [arrayKey]: arr };
272
+ });
273
+ return;
274
+ }
275
+ if (args.length === 4) {
276
+ const [arrayKey, itemId, field, value] = args;
277
+ const index = getIndex(String(arrayKey), itemId);
278
+ if (index === void 0) return;
279
+ setState(
280
+ (prev) => handleArrayItemChange({ state: prev, arrayKey, index, field, value })
281
+ );
282
+ validateItemInput(arrayKey, itemId, field, value);
283
+ return;
284
+ }
285
+ if (args.length === 5) {
286
+ const [parentKey, parentId, field, index, value] = args;
287
+ const parentIndex = getIndex(String(parentKey), parentId);
288
+ if (parentIndex === void 0) return;
289
+ setState((prev) => {
290
+ const parentArr = [...prev[parentKey]];
291
+ const item = { ...parentArr[parentIndex] };
292
+ const nestedArr = [...getNestedValue(item, field) || []];
293
+ nestedArr[index] = value;
294
+ const updatedItem = setNestedValue(item, field, nestedArr);
295
+ parentArr[parentIndex] = updatedItem;
296
+ return { ...prev, [parentKey]: parentArr };
297
+ });
298
+ return;
299
+ }
300
+ }), onSelectionChange = (id, value) => {
282
301
  const fixedValue = typeof value === "string" || value === null ? value : Array.from(value);
283
302
  setState(
284
303
  (prev) => handleNestedChange({
@@ -287,7 +306,7 @@ function useForm(initialState, {
287
306
  value: fixedValue
288
307
  })
289
308
  );
290
- validateInput(id, fixedValue);
309
+ validateInput(String(id), fixedValue, id);
291
310
  };
292
311
  const getItemCompositeKey = (arrayKey, itemId, field) => `${arrayKey}.${itemId}.${field}`;
293
312
  const getPrimitiveCompositeKey = (arrayKey, index) => `${arrayKey}.@${index}`;
@@ -300,22 +319,24 @@ function useForm(initialState, {
300
319
  const fieldRules = typeof arrayRuleDef === "function" ? arrayRuleDef(item, index) : arrayRuleDef;
301
320
  const fieldMessages = arrayMessages?.[arrayKey]?.[field];
302
321
  const compositeKey = getItemCompositeKey(String(arrayKey), itemId, field);
303
- setItemErrors((prev) => ({
304
- ...prev,
305
- [compositeKey]: performValidations(value, fieldRules, fieldMessages)
306
- }));
322
+ setErrors(
323
+ (prev) => new Map(prev).set(
324
+ compositeKey,
325
+ performValidations(value, fieldRules, fieldMessages)
326
+ )
327
+ );
307
328
  };
308
329
  const getItemUXProps = useCallback(
309
330
  (arrayKey, itemId, field) => {
310
331
  const compositeKey = getItemCompositeKey(arrayKey, itemId, field);
311
- const inputError = itemErrors[compositeKey];
312
- const isTouched = itemTouched[compositeKey];
332
+ const inputError = errors2.get(compositeKey);
333
+ const isTouched = touched.get(compositeKey);
313
334
  return {
314
335
  isInvalid: Boolean(isTouched && inputError?.isInvalid),
315
336
  errorMessage: isTouched ? inputError?.errorMessage || "" : ""
316
337
  };
317
338
  },
318
- [itemErrors, itemTouched]
339
+ [errors2, touched]
319
340
  );
320
341
  const onItemBlur = (arrayKey, itemId, field) => {
321
342
  const index = getIndex(String(arrayKey), itemId);
@@ -324,8 +345,8 @@ function useForm(initialState, {
324
345
  const value = getNestedValue(arr[index], field);
325
346
  validateItemInput(arrayKey, itemId, field, value);
326
347
  const compositeKey = getItemCompositeKey(String(arrayKey), itemId, field);
327
- if (itemTouched[compositeKey]) return;
328
- setItemTouched((prev) => ({ ...prev, [compositeKey]: true }));
348
+ if (touched.get(compositeKey)) return;
349
+ setTouched((prev) => new Map(prev).set(compositeKey, true));
329
350
  };
330
351
  const onItemValueChange = (arrayKey, itemId, field, value) => {
331
352
  const index = getIndex(String(arrayKey), itemId);
@@ -355,16 +376,18 @@ function useForm(initialState, {
355
376
  };
356
377
  const validateNestedInput = (dotPath, value) => {
357
378
  const topKey = dotPath.split(".")[0];
358
- setNestedErrors((prev) => ({
359
- ...prev,
360
- [dotPath]: performValidations(value, rules?.[topKey], messages?.[topKey])
361
- }));
379
+ setErrors(
380
+ (prev) => new Map(prev).set(
381
+ dotPath,
382
+ performValidations(value, rules?.[topKey], messages?.[topKey])
383
+ )
384
+ );
362
385
  };
363
386
  const onNestedBlur = (dotPath) => {
364
387
  const value = getNestedValue(state, dotPath);
365
388
  validateNestedInput(dotPath, value);
366
- if (nestedTouched[dotPath]) return;
367
- setNestedTouched((prev) => ({ ...prev, [dotPath]: true }));
389
+ if (touched.get(dotPath)) return;
390
+ setTouched((prev) => new Map(prev).set(dotPath, true));
368
391
  };
369
392
  const onNestedValueChange = (dotPath, value) => {
370
393
  setState((prev) => setNestedValue(prev, dotPath, value));
@@ -409,12 +432,8 @@ function useForm(initialState, {
409
432
  ...getItemUXProps(String(arrayKey2), `@${index2}`, ""),
410
433
  id: compositeKey2,
411
434
  onBlur: () => {
412
- const compositeKey3 = getPrimitiveCompositeKey(
413
- String(arrayKey2),
414
- index2
415
- );
416
- if (itemTouched[compositeKey3]) return;
417
- setItemTouched((prev) => ({ ...prev, [compositeKey3]: true }));
435
+ if (touched.get(compositeKey2)) return;
436
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
418
437
  },
419
438
  onValueChange: (v) => {
420
439
  setState((prev) => {
@@ -422,6 +441,7 @@ function useForm(initialState, {
422
441
  arr3[index2] = v;
423
442
  return { ...prev, [arrayKey2]: arr3 };
424
443
  });
444
+ validateItemInput(arrayKey2, `@${index2}`, "", v);
425
445
  },
426
446
  value: value2
427
447
  };
@@ -442,8 +462,8 @@ function useForm(initialState, {
442
462
  ...getItemUXProps(String(parentKey), parentId, `${field2}.@${index2}`),
443
463
  id: compositeKey2,
444
464
  onBlur: () => {
445
- if (itemTouched[compositeKey2]) return;
446
- setItemTouched((prev) => ({ ...prev, [compositeKey2]: true }));
465
+ if (touched.get(compositeKey2)) return;
466
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
447
467
  },
448
468
  onValueChange: (v) => {
449
469
  if (parentIndex === void 0) return;
@@ -455,6 +475,8 @@ function useForm(initialState, {
455
475
  parentArr[parentIndex] = setNestedValue(item, field2, nestedArr2);
456
476
  return { ...prev, [parentKey]: parentArr };
457
477
  });
478
+ const fieldPath = `${field2}.@${index2}`;
479
+ validateItemInput(parentKey, parentId, fieldPath, v);
458
480
  },
459
481
  value: value2
460
482
  };
@@ -509,8 +531,8 @@ function useForm(initialState, {
509
531
  ...getItemUXProps(String(arrayKey2), `@${index2}`, ""),
510
532
  id: compositeKey2,
511
533
  onBlur: () => {
512
- if (itemTouched[compositeKey2]) return;
513
- setItemTouched((prev) => ({ ...prev, [compositeKey2]: true }));
534
+ if (touched.get(compositeKey2)) return;
535
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
514
536
  },
515
537
  onSelectionChange: (v) => {
516
538
  const fixedValue = typeof v === "string" || v === null ? v : Array.from(v)[0] || null;
@@ -519,6 +541,7 @@ function useForm(initialState, {
519
541
  arr3[index2] = fixedValue;
520
542
  return { ...prev, [arrayKey2]: arr3 };
521
543
  });
544
+ validateItemInput(arrayKey2, `@${index2}`, "", fixedValue);
522
545
  },
523
546
  selectedKeys: value2 === null ? [] : [value2]
524
547
  };
@@ -539,8 +562,8 @@ function useForm(initialState, {
539
562
  ...getItemUXProps(String(parentKey), parentId, `${field2}.@${index2}`),
540
563
  id: compositeKey2,
541
564
  onBlur: () => {
542
- if (itemTouched[compositeKey2]) return;
543
- setItemTouched((prev) => ({ ...prev, [compositeKey2]: true }));
565
+ if (touched.get(compositeKey2)) return;
566
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
544
567
  },
545
568
  onSelectionChange: (v) => {
546
569
  if (parentIndex === void 0) return;
@@ -553,6 +576,8 @@ function useForm(initialState, {
553
576
  parentArr[parentIndex] = setNestedValue(item, field2, nestedArr2);
554
577
  return { ...prev, [parentKey]: parentArr };
555
578
  });
579
+ const fieldPath = `${field2}.@${index2}`;
580
+ validateItemInput(parentKey, parentId, fieldPath, fixedValue);
556
581
  },
557
582
  selectedKeys: value2 === null ? [] : [value2]
558
583
  };
@@ -602,15 +627,17 @@ function useForm(initialState, {
602
627
  ...getItemUXProps(String(arrayKey2), `@${index2}`, ""),
603
628
  id: compositeKey2,
604
629
  onBlur: () => {
605
- if (itemTouched[compositeKey2]) return;
606
- setItemTouched((prev) => ({ ...prev, [compositeKey2]: true }));
630
+ if (touched.get(compositeKey2)) return;
631
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
607
632
  },
608
633
  onSelectionChange: (v) => {
634
+ const fixedValue = typeof v === "string" || v === null ? v : String(v);
609
635
  setState((prev) => {
610
636
  const arr3 = [...prev[arrayKey2]];
611
- arr3[index2] = typeof v === "string" || v === null ? v : String(v);
637
+ arr3[index2] = fixedValue;
612
638
  return { ...prev, [arrayKey2]: arr3 };
613
639
  });
640
+ validateItemInput(arrayKey2, `@${index2}`, "", fixedValue);
614
641
  },
615
642
  selectedKey: value2
616
643
  };
@@ -631,19 +658,22 @@ function useForm(initialState, {
631
658
  ...getItemUXProps(String(parentKey), parentId, `${field2}.@${index2}`),
632
659
  id: compositeKey2,
633
660
  onBlur: () => {
634
- if (itemTouched[compositeKey2]) return;
635
- setItemTouched((prev) => ({ ...prev, [compositeKey2]: true }));
661
+ if (touched.get(compositeKey2)) return;
662
+ setTouched((prev) => new Map(prev).set(compositeKey2, true));
636
663
  },
637
664
  onSelectionChange: (v) => {
638
665
  if (parentIndex === void 0) return;
666
+ const fixedValue = typeof v === "string" || v === null ? v : String(v);
639
667
  setState((prev) => {
640
668
  const parentArr = [...prev[parentKey]];
641
669
  const item = { ...parentArr[parentIndex] };
642
670
  const nestedArr2 = [...getNestedValue(item, field2) || []];
643
- nestedArr2[index2] = typeof v === "string" || v === null ? v : String(v);
671
+ nestedArr2[index2] = fixedValue;
644
672
  parentArr[parentIndex] = setNestedValue(item, field2, nestedArr2);
645
673
  return { ...prev, [parentKey]: parentArr };
646
674
  });
675
+ const fieldPath = `${field2}.@${index2}`;
676
+ validateItemInput(parentKey, parentId, fieldPath, fixedValue);
647
677
  },
648
678
  selectedKey: value2
649
679
  };
@@ -694,8 +724,8 @@ function useForm(initialState, {
694
724
  });
695
725
  if (itemId !== void 0) {
696
726
  const prefix = `${String(arrayKey)}.${itemId}.`;
697
- setItemTouched((prev) => removeCompositeKeysByPrefix(prev, prefix));
698
- setItemErrors((prev) => removeCompositeKeysByPrefix(prev, prefix));
727
+ setTouched((prev) => removeCompositeKeysByPrefix(prev, prefix));
728
+ setErrors((prev) => removeCompositeKeysByPrefix(prev, prefix));
699
729
  }
700
730
  },
701
731
  removeById: (arrayKey, itemId) => {
@@ -705,8 +735,8 @@ function useForm(initialState, {
705
735
  arr.splice(index, 1);
706
736
  setState((prev) => ({ ...prev, [arrayKey]: arr }));
707
737
  const prefix = `${String(arrayKey)}.${itemId}.`;
708
- setItemTouched((prev) => removeCompositeKeysByPrefix(prev, prefix));
709
- setItemErrors((prev) => removeCompositeKeysByPrefix(prev, prefix));
738
+ setTouched((prev) => removeCompositeKeysByPrefix(prev, prefix));
739
+ setErrors((prev) => removeCompositeKeysByPrefix(prev, prefix));
710
740
  }
711
741
  },
712
742
  moveItem: (arrayKey, from, to) => {
@@ -746,35 +776,12 @@ function useForm(initialState, {
746
776
  },
747
777
  isDirty: JSON.stringify(state) !== JSON.stringify(initialState),
748
778
  hasInvalidValues: () => {
749
- setTouched((prev) => allToValue(prev, true));
750
- setItemTouched(
751
- (prev) => Object.keys(prev).reduce(
752
- (acc, key) => ({ ...acc, [key]: true }),
753
- {}
754
- )
755
- );
756
- setNestedTouched(
757
- (prev) => Object.keys(prev).reduce(
758
- (acc, key) => ({ ...acc, [key]: true }),
759
- {}
760
- )
761
- );
762
- const topLevelInvalid = JSON.stringify(errors2).includes(":true");
763
- const itemLevelInvalid = Object.values(itemErrors).some(
764
- (e) => e?.isInvalid
765
- );
766
- const nestedLevelInvalid = Object.values(nestedErrors).some(
767
- (e) => e?.isInvalid
768
- );
769
- return topLevelInvalid || itemLevelInvalid || nestedLevelInvalid;
779
+ const isInvalid = Array.from(errors2.values()).some((e) => e?.isInvalid);
780
+ return isInvalid;
770
781
  },
771
782
  resetForm: (preservedKeys) => {
772
- setTouched(initial.touched);
773
- setErrors(initial.errors);
774
- setItemTouched({});
775
- setItemErrors({});
776
- setNestedTouched({});
777
- setNestedErrors({});
783
+ setTouched(/* @__PURE__ */ new Map());
784
+ setErrors(/* @__PURE__ */ new Map());
778
785
  setState(
779
786
  (prev) => preservedKeys === void 0 ? initialState : preservedKeys.reduce(
780
787
  (acc, key) => ({
@@ -786,16 +793,13 @@ function useForm(initialState, {
786
793
  );
787
794
  },
788
795
  resetTouched: (preservedKeys) => {
789
- setItemTouched({});
790
- setNestedTouched({});
791
796
  setTouched(
792
- (prev) => preservedKeys === void 0 ? initialState : preservedKeys.reduce(
793
- (acc, key) => ({
794
- ...acc,
795
- [key]: prev[key]
796
- }),
797
- initial.touched
798
- )
797
+ (prev) => preservedKeys === void 0 ? /* @__PURE__ */ new Map() : preservedKeys.reduce((acc, key) => {
798
+ if (prev.has(String(key))) {
799
+ acc.set(String(key), prev.get(String(key)));
800
+ }
801
+ return acc;
802
+ }, /* @__PURE__ */ new Map())
799
803
  );
800
804
  }
801
805
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juantroconisf/lib",
3
- "version": "5.2.0",
3
+ "version": "6.0.0",
4
4
  "description": "A form validation library for HeroUI.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",