@juantroconisf/lib 11.6.0 → 11.7.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.
Files changed (4) hide show
  1. package/README.md +180 -437
  2. package/dist/index.js +150 -151
  3. package/dist/index.mjs +150 -151
  4. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -18,11 +18,13 @@ function handleNestedChange({
18
18
  value,
19
19
  hasNestedValues = false
20
20
  }) {
21
- if (!hasNestedValues) return { ...state, [id]: value };
22
- const propertyDepth = String(id).split("."), newValues = { ...state };
23
- let current = newValues;
24
- for (let i = 0; i < propertyDepth.length - 1; i++) {
25
- const key = propertyDepth[i];
21
+ const isNested = hasNestedValues || String(id).includes(".");
22
+ if (!isNested) return { ...state, [id]: value };
23
+ const keys = String(id).split(".");
24
+ const newState = { ...state };
25
+ let current = newState;
26
+ for (let i = 0; i < keys.length - 1; i++) {
27
+ const key = keys[i];
26
28
  if (Array.isArray(current[key])) {
27
29
  current[key] = [...current[key]];
28
30
  } else {
@@ -30,8 +32,8 @@ function handleNestedChange({
30
32
  }
31
33
  current = current[key];
32
34
  }
33
- current[propertyDepth[propertyDepth.length - 1]] = value;
34
- return { ...newValues };
35
+ current[keys[keys.length - 1]] = value;
36
+ return newState;
35
37
  }
36
38
  function handleArrayItemChange({
37
39
  state,
@@ -72,21 +74,6 @@ function removeCompositeKeysByPrefix(map, prefix) {
72
74
  }
73
75
  return result;
74
76
  }
75
- function setNestedValue(state, dotPath, value) {
76
- const keys = dotPath.split(".");
77
- const copy = { ...state };
78
- let current = copy;
79
- for (let i = 0; i < keys.length - 1; i++) {
80
- if (Array.isArray(current[keys[i]])) {
81
- current[keys[i]] = [...current[keys[i]]];
82
- } else {
83
- current[keys[i]] = { ...current[keys[i]] };
84
- }
85
- current = current[keys[i]];
86
- }
87
- current[keys[keys.length - 1]] = value;
88
- return copy;
89
- }
90
77
 
91
78
  // src/hooks/useForm.tsx
92
79
  import { Form } from "@heroui/react";
@@ -381,7 +368,7 @@ function useForm(schema, {
381
368
  }, [schema]);
382
369
  const [state, setState] = useState(initialState), [metadata, setMetadata] = useState(/* @__PURE__ */ new Map());
383
370
  useComponentLanguage();
384
- const stateRef = useRef(state), metadataRef = useRef(metadata);
371
+ const stateRef = useRef(state), metadataRef = useRef(metadata), propsCache = useRef(/* @__PURE__ */ new Map());
385
372
  stateRef.current = state;
386
373
  metadataRef.current = metadata;
387
374
  const indexMap = useMemo(() => {
@@ -442,14 +429,7 @@ function useForm(schema, {
442
429
  const runValidation = useCallback(
443
430
  (ruleDef, value, compositeKey, realPath, fullState) => {
444
431
  if (isSchema(ruleDef)) {
445
- try {
446
- if (validationSchema && realPath && fullState !== void 0) {
447
- validationSchema.validateSyncAt(realPath, fullState);
448
- } else {
449
- ruleDef.validateSync(value);
450
- }
451
- return false;
452
- } catch (err) {
432
+ const updateMetadata = (err) => {
453
433
  const error = {
454
434
  isInvalid: true,
455
435
  errorMessage: err.message || "Invalid"
@@ -464,7 +444,32 @@ function useForm(schema, {
464
444
  newMap.set(compositeKey, { ...currentMeta, ...error });
465
445
  return newMap;
466
446
  });
467
- return true;
447
+ };
448
+ try {
449
+ if (validationSchema && realPath && fullState !== void 0) {
450
+ validationSchema.validateSyncAt(realPath, fullState);
451
+ } else {
452
+ ruleDef.validateSync(value);
453
+ }
454
+ return false;
455
+ } catch (err) {
456
+ if (err.name === "ValidationError") {
457
+ updateMetadata(err);
458
+ return true;
459
+ }
460
+ return (async () => {
461
+ try {
462
+ if (validationSchema && realPath && fullState !== void 0) {
463
+ await validationSchema.validateAt(realPath, fullState);
464
+ } else {
465
+ await ruleDef.validate(value);
466
+ }
467
+ return false;
468
+ } catch (asyncErr) {
469
+ updateMetadata(asyncErr);
470
+ return true;
471
+ }
472
+ })();
468
473
  }
469
474
  }
470
475
  return false;
@@ -475,35 +480,46 @@ function useForm(schema, {
475
480
  (compositeKey, fieldPath, realPath, value, fullState) => {
476
481
  let schemaRule = getRule(fieldPath, validationSchema);
477
482
  if (schemaRule) {
478
- if (runValidation(schemaRule, value, compositeKey, realPath, fullState))
479
- return true;
483
+ const result = runValidation(
484
+ schemaRule,
485
+ value,
486
+ compositeKey,
487
+ realPath,
488
+ fullState
489
+ );
490
+ if (result === true) return true;
491
+ if (result instanceof Promise) {
492
+ return result.then((res) => {
493
+ if (!res) clearError();
494
+ return res;
495
+ });
496
+ }
480
497
  }
481
- setMetadata((prev) => {
482
- const newMap = new Map(prev);
483
- const currentMeta = newMap.get(compositeKey) || {
484
- isTouched: false,
485
- isInvalid: false,
486
- errorMessage: ""
487
- };
488
- newMap.set(compositeKey, {
489
- ...currentMeta,
490
- isInvalid: false,
491
- errorMessage: ""
498
+ function clearError() {
499
+ setMetadata((prev) => {
500
+ const newMap = new Map(prev);
501
+ const currentMeta = newMap.get(compositeKey) || {
502
+ isTouched: false,
503
+ isInvalid: false,
504
+ errorMessage: ""
505
+ };
506
+ newMap.set(compositeKey, {
507
+ ...currentMeta,
508
+ isInvalid: false,
509
+ errorMessage: ""
510
+ });
511
+ return newMap;
492
512
  });
493
- return newMap;
494
- });
513
+ }
514
+ clearError();
495
515
  return false;
496
516
  },
497
517
  [getRule, runValidation, validationSchema]
498
518
  );
499
519
  const validateAll = useCallback(() => {
500
520
  if (!validationSchema) return false;
501
- let hasError = false;
502
521
  const newMetadata = new Map(metadataRef.current);
503
- try {
504
- validationSchema.validateSync(state, { abortEarly: false });
505
- } catch (err) {
506
- hasError = true;
522
+ const handleErrors = (err) => {
507
523
  if (err.inner) {
508
524
  err.inner.forEach((validationError) => {
509
525
  const yupPath = validationError.path;
@@ -544,9 +560,26 @@ function useForm(schema, {
544
560
  });
545
561
  });
546
562
  }
563
+ setMetadata(newMetadata);
564
+ return true;
565
+ };
566
+ try {
567
+ validationSchema.validateSync(state, { abortEarly: false });
568
+ } catch (err) {
569
+ if (err.name === "ValidationError") {
570
+ return handleErrors(err);
571
+ }
572
+ return (async () => {
573
+ try {
574
+ await validationSchema.validate(state, { abortEarly: false });
575
+ return false;
576
+ } catch (asyncErr) {
577
+ return handleErrors(asyncErr);
578
+ }
579
+ })();
547
580
  }
548
581
  setMetadata(newMetadata);
549
- return hasError;
582
+ return false;
550
583
  }, [validationSchema, state, arrayIdentifiers]);
551
584
  const handleFieldChange = useCallback(
552
585
  (resolution, newValue) => {
@@ -631,7 +664,12 @@ function useForm(schema, {
631
664
  nextState = { ...nextState, [parentKey]: parentArr };
632
665
  }
633
666
  } else if (type === "deep" /* Deep */) {
634
- nextState = setNestedValue(nextState, resolution.realPath, finalValue);
667
+ nextState = handleNestedChange({
668
+ state: nextState,
669
+ id: resolution.realPath,
670
+ value: finalValue,
671
+ hasNestedValues: true
672
+ });
635
673
  }
636
674
  setState(nextState);
637
675
  validateField(
@@ -687,8 +725,8 @@ function useForm(schema, {
687
725
  [validateField, getRule, validationSchema]
688
726
  );
689
727
  const on = useMemo(
690
- () => ({
691
- input: (...args) => {
728
+ () => {
729
+ const getCachedProps = (method, args, factory) => {
692
730
  const data = resolveFieldData(
693
731
  args,
694
732
  stateRef.current,
@@ -698,117 +736,78 @@ function useForm(schema, {
698
736
  validationSchema
699
737
  );
700
738
  if (!data) return {};
701
- return {
739
+ const meta = metadataRef.current.get(data.compositeKey);
740
+ const deps = JSON.stringify([
741
+ data.value,
742
+ meta?.isInvalid,
743
+ meta?.errorMessage,
744
+ meta?.isTouched
745
+ ]);
746
+ const cacheKey = `${method}:${JSON.stringify(args)}`;
747
+ const cached = propsCache.current.get(cacheKey);
748
+ if (cached && cached.deps === deps) {
749
+ return cached.props;
750
+ }
751
+ const props = factory(data);
752
+ propsCache.current.set(cacheKey, { props, deps });
753
+ return props;
754
+ };
755
+ return {
756
+ input: (...args) => getCachedProps("input", args, (data) => ({
702
757
  ...createHandlers(data),
703
758
  value: data.value === null || data.value === void 0 ? "" : typeof data.value === "boolean" ? data.value : String(data.value),
704
759
  onValueChange: (v) => handleFieldChange(data, v)
705
- };
706
- },
707
- select: (...args) => {
708
- const data = resolveFieldData(
709
- args,
710
- stateRef.current,
711
- getIndex,
712
- getNestedValue,
713
- getRule,
714
- validationSchema
715
- );
716
- if (!data) return {};
717
- const isArray = Array.isArray(data.value);
718
- return {
719
- ...createHandlers(data),
720
- selectedKeys: data.value === null || data.value === void 0 ? [] : isArray ? data.value.map(String) : [String(data.value)],
721
- onSelectionChange: (v) => {
722
- const fixed = typeof v === "string" || v === null ? v : isArray ? Array.from(v) : Array.from(v)[0] ?? null;
723
- handleFieldChange(data, fixed);
724
- }
725
- };
726
- },
727
- autocomplete: (...args) => {
728
- const data = resolveFieldData(
729
- args,
730
- stateRef.current,
731
- getIndex,
732
- getNestedValue,
733
- getRule,
734
- validationSchema
735
- );
736
- if (!data) return {};
737
- return {
760
+ })),
761
+ select: (...args) => getCachedProps("select", args, (data) => {
762
+ const isArray = Array.isArray(data.value);
763
+ return {
764
+ ...createHandlers(data),
765
+ selectedKeys: data.value === null || data.value === void 0 ? [] : isArray ? data.value.map(String) : [String(data.value)],
766
+ onSelectionChange: (v) => {
767
+ const fixed = typeof v === "string" || v === null ? v : isArray ? Array.from(v) : Array.from(v)[0] ?? null;
768
+ handleFieldChange(data, fixed);
769
+ }
770
+ };
771
+ }),
772
+ autocomplete: (...args) => getCachedProps("autocomplete", args, (data) => ({
738
773
  ...createHandlers(data),
739
774
  selectedKey: data.value === null || data.value === void 0 ? null : String(data.value),
740
775
  onSelectionChange: (v) => {
741
776
  const fixed = typeof v === "string" || v === null || v === void 0 ? v : String(v);
742
777
  handleFieldChange(data, fixed);
743
778
  }
744
- };
745
- },
746
- numberInput: (...args) => {
747
- const data = resolveFieldData(
748
- args,
749
- stateRef.current,
750
- getIndex,
751
- getNestedValue,
752
- getRule,
753
- validationSchema
754
- );
755
- if (!data) return {};
756
- return {
779
+ })),
780
+ numberInput: (...args) => getCachedProps("numberInput", args, (data) => ({
757
781
  ...createHandlers(data),
758
782
  value: data.value === null || data.value === void 0 ? "" : String(data.value),
759
783
  onValueChange: (v) => handleFieldChange(data, v)
760
- };
761
- },
762
- checkbox: (...args) => {
763
- const data = resolveFieldData(
764
- args,
765
- stateRef.current,
766
- getIndex,
767
- getNestedValue,
768
- getRule,
769
- validationSchema
770
- );
771
- if (!data) return {};
772
- return {
784
+ })),
785
+ checkbox: (...args) => getCachedProps("checkbox", args, (data) => ({
773
786
  ...createHandlers(data),
774
787
  isSelected: Boolean(data.value),
775
788
  onValueChange: (v) => handleFieldChange(data, v)
776
- };
777
- },
778
- switch: (...args) => {
779
- const data = resolveFieldData(
780
- args,
781
- stateRef.current,
782
- getIndex,
783
- getNestedValue,
784
- getRule,
785
- validationSchema
786
- );
787
- if (!data) return {};
788
- return {
789
+ })),
790
+ switch: (...args) => getCachedProps("switch", args, (data) => ({
789
791
  ...createHandlers(data),
790
792
  isSelected: Boolean(data.value),
791
793
  onValueChange: (v) => handleFieldChange(data, v)
792
- };
793
- },
794
- radio: (...args) => {
795
- const data = resolveFieldData(
796
- args,
797
- stateRef.current,
798
- getIndex,
799
- getNestedValue,
800
- getRule,
801
- validationSchema
802
- );
803
- if (!data) return {};
804
- return {
794
+ })),
795
+ radio: (...args) => getCachedProps("radio", args, (data) => ({
805
796
  ...createHandlers(data),
806
797
  value: data.value === null || data.value === void 0 ? "" : String(data.value),
807
798
  onValueChange: (v) => handleFieldChange(data, v)
808
- };
809
- }
810
- }),
811
- [createHandlers, getIndex, handleFieldChange, getRule, validationSchema]
799
+ }))
800
+ };
801
+ },
802
+ [
803
+ createHandlers,
804
+ getIndex,
805
+ handleFieldChange,
806
+ getRule,
807
+ validationSchema,
808
+ state,
809
+ metadata
810
+ ]
812
811
  );
813
812
  const helpers = useMemo(
814
813
  () => ({
@@ -925,7 +924,7 @@ function useForm(schema, {
925
924
  return getNestedValue(stateRef.current, arrayKey)[index];
926
925
  }
927
926
  }),
928
- [getIndex, arrayIdentifiers]
927
+ [getIndex, arrayIdentifiers, state, metadata]
929
928
  );
930
929
  const onBlur = useCallback(
931
930
  (id) => {
@@ -1009,9 +1008,9 @@ function useForm(schema, {
1009
1008
  [getIndex, handleFieldChange, getRule, validationSchema]
1010
1009
  );
1011
1010
  const onFormSubmit = useCallback(
1012
- (fn) => (e) => {
1011
+ (fn) => async (e) => {
1013
1012
  e.preventDefault();
1014
- if (validateAll()) return;
1013
+ if (await validateAll()) return;
1015
1014
  fn(stateRef.current, e);
1016
1015
  },
1017
1016
  [validateAll]
@@ -1044,9 +1043,9 @@ function useForm(schema, {
1044
1043
  const ControlledForm = useMemo(() => {
1045
1044
  return (props) => {
1046
1045
  const { onSubmit, ...rest } = props;
1047
- const handleSubmit = (e) => {
1046
+ const handleSubmit = async (e) => {
1048
1047
  e.preventDefault();
1049
- if (validateAllRef.current()) {
1048
+ if (await validateAllRef.current()) {
1050
1049
  return;
1051
1050
  }
1052
1051
  onFormSubmitPropRef.current?.(stateRef.current, e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juantroconisf/lib",
3
- "version": "11.6.0",
3
+ "version": "11.7.0",
4
4
  "description": "A form validation library for HeroUI.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",