react-f0rm 0.9.0 → 0.11.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.umd.js CHANGED
@@ -222,6 +222,7 @@
222
222
  mode: options?.mode ?? "onSubmit",
223
223
  reValidateMode: options?.reValidateMode ?? "onChange",
224
224
  disabled: options?.disabled ?? false,
225
+ validateDeps: options?.validateDeps ? new Set(options.validateDeps.map((dep) => create$1(dep).key)) : void 0,
225
226
  initialValues: options?.initialValues ?? {},
226
227
  values: /* @__PURE__ */ new Map(),
227
228
  deleted: /* @__PURE__ */ new Set(),
@@ -594,34 +595,75 @@
594
595
  function isFieldError(value) {
595
596
  return !!value && typeof value === "object" && typeof value.type === "string" && typeof value.message === "string";
596
597
  }
597
- function setFormErrors(form, result, segments = []) {
598
+ function setFormErrors(form, result, segments = [], footprint) {
598
599
  Object.entries(result).forEach(([key, value]) => {
599
600
  const path = [...segments, ...normalizePath(key)];
600
601
  if (typeof value === "string") {
601
- if (value) setError(form, path, value);
602
+ if (value) {
603
+ setError(form, path, value);
604
+ recordFootprint(form, path, footprint);
605
+ }
602
606
  } else if (Array.isArray(value)) {
603
607
  setError(form, path, value);
608
+ recordFootprint(form, path, footprint);
604
609
  } else if (isFieldError(value)) {
605
610
  setError(form, path, value);
611
+ recordFootprint(form, path, footprint);
606
612
  } else if (value && typeof value === "object") {
607
- setFormErrors(form, value, path);
613
+ setFormErrors(form, value, path, footprint);
608
614
  }
609
615
  });
610
616
  }
617
+ function recordFootprint(form, segments, footprint) {
618
+ if (!footprint) return;
619
+ const path = create$1(segments);
620
+ const stored = form.errors.get(path.key);
621
+ if (stored) footprint.set(path.key, stored);
622
+ }
611
623
  function setParsedValues(form, values) {
612
624
  if (values === void 0 || values === form.parsedValues) return;
613
625
  form.parsedValues = values;
614
626
  emit(form.emitter, "change");
615
627
  }
616
628
  function applyValidateResult(form, result) {
629
+ const footprint = form.validateDeps ? getFormErrorFootprint(form) : void 0;
630
+ if (footprint) {
631
+ clearFormValidateErrors(form, footprint);
632
+ footprint.clear();
633
+ }
617
634
  if (!result) return;
618
635
  if (typeof result === "object" && VALIDATION_OUTCOME in result) {
619
636
  const outcome = result;
620
- if (outcome.errors) setFormErrors(form, outcome.errors);
637
+ if (outcome.errors) setFormErrors(form, outcome.errors, [], footprint);
621
638
  setParsedValues(form, outcome.values);
622
639
  return;
623
640
  }
624
- setFormErrors(form, result);
641
+ setFormErrors(form, result, [], footprint);
642
+ }
643
+ const formErrorFootprints = /* @__PURE__ */ new WeakMap();
644
+ function getFormErrorFootprint(form) {
645
+ let footprint = formErrorFootprints.get(form);
646
+ if (!footprint) {
647
+ footprint = /* @__PURE__ */ new Map();
648
+ formErrorFootprints.set(form, footprint);
649
+ }
650
+ return footprint;
651
+ }
652
+ function hasFormValidateErrors(form) {
653
+ const footprint = formErrorFootprints.get(form);
654
+ if (!footprint) return false;
655
+ for (const [key, written] of footprint) {
656
+ if (form.errors.get(key) === written) return true;
657
+ }
658
+ return false;
659
+ }
660
+ function clearFormValidateErrors(form, footprint) {
661
+ for (const [key, written] of footprint) {
662
+ const stored = form.errors.get(key);
663
+ if (stored !== written) continue;
664
+ form.errors.delete(key);
665
+ emit(form.emitter, "errors", create$1(JSON.parse(key)));
666
+ }
625
667
  }
626
668
  const FORM_VALIDATING_KEY = "__form_validate__";
627
669
  function fieldsSettled(form) {
@@ -715,6 +757,13 @@
715
757
  else waiter.reject(outcome);
716
758
  }
717
759
  }
760
+ function revalidateFormOnChange(form, path, mode) {
761
+ if (!form.validateDeps?.has(path.key) || !form.validate) return;
762
+ if (mode === "onChange" || mode === "all" || mode === "onTouched" && hasTouchedByPath(form, path) || form.reValidateMode === "onChange" && hasFormValidateErrors(form)) {
763
+ runFormValidate(form).catch(() => {
764
+ });
765
+ }
766
+ }
718
767
  async function ensureValidate(form) {
719
768
  form.validators.forEach((validator) => validator());
720
769
  await waitUntil(
@@ -1466,6 +1515,7 @@
1466
1515
  setValueByPath(form, path, v);
1467
1516
  if (mode === "onChange" || mode === "all" || mode === "onTouched" && hasTouchedByPath(form, path) || liveErrors.length > 0 && form.reValidateMode === "onChange")
1468
1517
  validator();
1518
+ revalidateFormOnChange(form, path, mode);
1469
1519
  });
1470
1520
  React.useEffect(() => {
1471
1521
  form.changeHandlers.set(path.key, onChange);
@@ -1507,6 +1557,21 @@
1507
1557
  function generateId() {
1508
1558
  return `_${++idCounter}`;
1509
1559
  }
1560
+ const arrayIdsRegistry = /* @__PURE__ */ new WeakMap();
1561
+ function getArrayIds(form, key) {
1562
+ return arrayIdsRegistry.get(form)?.get(key);
1563
+ }
1564
+ function registerArrayIds(form, key, ids) {
1565
+ let registry = arrayIdsRegistry.get(form);
1566
+ if (!registry) {
1567
+ registry = /* @__PURE__ */ new Map();
1568
+ arrayIdsRegistry.set(form, registry);
1569
+ }
1570
+ registry.set(key, ids);
1571
+ }
1572
+ function bumpReducer(count) {
1573
+ return count + 1;
1574
+ }
1510
1575
  function useFieldArrayCore(options, Context) {
1511
1576
  const contextForm = React.useContext(Context);
1512
1577
  const form = options.form || contextForm;
@@ -1542,6 +1607,14 @@
1542
1607
  () => onPathEvent(form.emitter, "change", path, "branch", syncFields),
1543
1608
  [form.emitter, path]
1544
1609
  );
1610
+ registerArrayIds(form, path.key, idsRef.current);
1611
+ React.useEffect(() => {
1612
+ registerArrayIds(form, path.key, idsRef.current);
1613
+ return () => {
1614
+ const registry = arrayIdsRegistry.get(form);
1615
+ if (registry?.get(path.key) === idsRef.current) registry.delete(path.key);
1616
+ };
1617
+ }, [form, path.key]);
1545
1618
  const append = useStageFn((value) => {
1546
1619
  const arr = getArray();
1547
1620
  idsRef.current.push(generateId());
@@ -1599,6 +1672,60 @@
1599
1672
  function useFieldArray(options) {
1600
1673
  return useFieldArrayCore(options, FormContext);
1601
1674
  }
1675
+ function useFieldArrayItemCore(options, Context) {
1676
+ const contextForm = React.useContext(Context);
1677
+ const form = options.form || contextForm;
1678
+ if (!form) throw new Error("no form provided");
1679
+ const { id } = options;
1680
+ const arrayPath = usePath(options.name);
1681
+ const computeIndex = React.useCallback(
1682
+ () => getArrayIds(form, arrayPath.key)?.indexOf(id) ?? -1,
1683
+ [form, arrayPath.key, id]
1684
+ );
1685
+ const computeValue = React.useCallback(
1686
+ (index2) => {
1687
+ const arr = getValueByPath(form, arrayPath);
1688
+ return Array.isArray(arr) ? arr[index2] : void 0;
1689
+ },
1690
+ [form, arrayPath]
1691
+ );
1692
+ const index = computeIndex();
1693
+ const value = computeValue(index);
1694
+ const lastRef = React.useRef({ index, value });
1695
+ lastRef.current = { index, value };
1696
+ const computeRef = useStage({ computeIndex, computeValue });
1697
+ const [, bump] = React.useReducer(bumpReducer, 0);
1698
+ React.useEffect(
1699
+ () => onPathEvent(form.emitter, "change", arrayPath, "leaf", () => {
1700
+ const { computeIndex: idx, computeValue: val } = computeRef.current;
1701
+ const nextIndex = idx();
1702
+ if (nextIndex !== lastRef.current.index || !Object.is(val(nextIndex), lastRef.current.value))
1703
+ bump();
1704
+ }),
1705
+ [form.emitter, arrayPath, computeRef]
1706
+ );
1707
+ const itemPath = usePath([...arrayPath.value, index]);
1708
+ const errors = useFieldErrorsByPath(form, itemPath);
1709
+ const setValue = useStageFn((v) => {
1710
+ if (index < 0) return;
1711
+ const arr = [...getValueByPath(form, arrayPath) ?? []];
1712
+ if (index >= arr.length) return;
1713
+ arr[index] = v;
1714
+ setValueByPath(form, arrayPath, arr);
1715
+ });
1716
+ return {
1717
+ value,
1718
+ setValue,
1719
+ errors,
1720
+ error: errors[0]?.message,
1721
+ name: itemPath.key,
1722
+ index,
1723
+ form
1724
+ };
1725
+ }
1726
+ function useFieldArrayItem(options) {
1727
+ return useFieldArrayItemCore(options, FormContext);
1728
+ }
1602
1729
 
1603
1730
  const FormContext = React.createContext(null);
1604
1731
  const FormProvider = FormContext.Provider;
@@ -1626,12 +1753,16 @@
1626
1753
  function useFieldArray(options) {
1627
1754
  return useFieldArrayCore(options, Context);
1628
1755
  }
1756
+ function useFieldArrayItem(options) {
1757
+ return useFieldArrayItemCore(options, Context);
1758
+ }
1629
1759
  return {
1630
1760
  context: Context,
1631
1761
  FormProvider: FormProvider2,
1632
1762
  useFormContext: useFormContext2,
1633
1763
  useField,
1634
- useFieldArray
1764
+ useFieldArray,
1765
+ useFieldArrayItem
1635
1766
  };
1636
1767
  }
1637
1768
  const CheckboxGroupContext = React.createContext(null);
@@ -1938,6 +2069,7 @@
1938
2069
  exports.removeFieldByPath = removeFieldByPath;
1939
2070
  exports.reset = reset;
1940
2071
  exports.resetField = resetField;
2072
+ exports.revalidateFormOnChange = revalidateFormOnChange;
1941
2073
  exports.setDisabled = setDisabled;
1942
2074
  exports.setError = setError;
1943
2075
  exports.setErrorByPath = setErrorByPath;
@@ -1961,6 +2093,7 @@
1961
2093
  exports.useErrorByPath = useErrorByPath;
1962
2094
  exports.useField = useField;
1963
2095
  exports.useFieldArray = useFieldArray;
2096
+ exports.useFieldArrayItem = useFieldArrayItem;
1964
2097
  exports.useFieldErrors = useFieldErrors;
1965
2098
  exports.useFieldErrorsByPath = useFieldErrorsByPath;
1966
2099
  exports.useForm = useForm;