react-f0rm 0.10.0 → 0.11.1

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
@@ -58,6 +58,7 @@
58
58
  pathCache.set(path, value);
59
59
  return value;
60
60
  }
61
+ const isIndex = (segment) => /^-?\d+$/.test(segment);
61
62
  function parsePath(path) {
62
63
  const result = [];
63
64
  let identifier = "";
@@ -90,7 +91,7 @@
90
91
  throw new TypeError(`Unterminated bracket in path: ${path}`);
91
92
  }
92
93
  const content = path.slice(i + 1, close);
93
- result.push(/^-?\d+$/.test(content) ? Number(content) : content);
94
+ result.push(isIndex(content) ? Number(content) : content);
94
95
  i = close;
95
96
  }
96
97
  } else {
@@ -127,9 +128,10 @@
127
128
  function set(values, path, value) {
128
129
  if (!path.length) return value;
129
130
  const [prop, ...props] = path;
130
- if (typeof prop === "number") {
131
+ const index = typeof prop === "number" ? prop : Array.isArray(values) && typeof prop === "string" && isIndex(prop) ? Number(prop) : void 0;
132
+ if (index !== void 0) {
131
133
  const arr = Array.isArray(values) ? values.slice() : [];
132
- arr[prop] = set(arr[prop], props, value);
134
+ arr[index] = set(arr[index], props, value);
133
135
  return arr;
134
136
  }
135
137
  return { ...values, [prop]: set(values && values[prop], props, value) };
@@ -143,7 +145,7 @@
143
145
  const prop = path[i];
144
146
  if (!owned.has(container)) {
145
147
  let copy;
146
- if (typeof prop === "number") {
148
+ if (typeof prop === "number" || Array.isArray(container) && typeof prop === "string" && isIndex(prop)) {
147
149
  copy = Array.isArray(container) ? container.slice() : [];
148
150
  } else {
149
151
  copy = { ...container };
@@ -253,9 +255,16 @@
253
255
  return getValueByPath(form, create$1(name));
254
256
  }
255
257
  function getValueByPath({ initialValues, parsedValues, values, deleted }, path) {
256
- if (values.has(path.key)) return values.get(path.key);
257
- if (deleted.has(path.key)) return void 0;
258
- return get(parsedValues ?? initialValues, path.value);
258
+ const { key, value: segments } = path;
259
+ if (values.has(key)) return values.get(key);
260
+ if (deleted.has(key)) return void 0;
261
+ for (let i = segments.length - 1; i > 0; i--) {
262
+ const ancestorKey = JSON.stringify(segments.slice(0, i));
263
+ if (values.has(ancestorKey)) {
264
+ return get(values.get(ancestorKey), segments.slice(i));
265
+ }
266
+ }
267
+ return get(parsedValues ?? initialValues, segments);
259
268
  }
260
269
  function setValue(form, name, value, options) {
261
270
  setValueByPath(form, create$1(name), value, options);
@@ -263,6 +272,7 @@
263
272
  function setValueByPath(form, path, value, options) {
264
273
  const { emitter, values, deleted } = form;
265
274
  values.set(path.key, value);
275
+ pruneDescendantKeys(values, path);
266
276
  reviveBranch(deleted, path);
267
277
  pruneDirtyBaselines(form, path);
268
278
  if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);
@@ -496,6 +506,13 @@
496
506
  }
497
507
  return false;
498
508
  }
509
+ function pruneDescendantKeys(values, { key }) {
510
+ if (!values.size) return;
511
+ const stem = `${key.slice(0, -1)},`;
512
+ for (const k of values.keys()) {
513
+ if (k.startsWith(stem)) values.delete(k);
514
+ }
515
+ }
499
516
  function reviveBranch(deleted, { key }) {
500
517
  if (!deleted.size) return;
501
518
  for (const tombstone of deleted) {
@@ -1557,6 +1574,21 @@
1557
1574
  function generateId() {
1558
1575
  return `_${++idCounter}`;
1559
1576
  }
1577
+ const arrayIdsRegistry = /* @__PURE__ */ new WeakMap();
1578
+ function getArrayIds(form, key) {
1579
+ return arrayIdsRegistry.get(form)?.get(key);
1580
+ }
1581
+ function registerArrayIds(form, key, ids) {
1582
+ let registry = arrayIdsRegistry.get(form);
1583
+ if (!registry) {
1584
+ registry = /* @__PURE__ */ new Map();
1585
+ arrayIdsRegistry.set(form, registry);
1586
+ }
1587
+ registry.set(key, ids);
1588
+ }
1589
+ function bumpReducer(count) {
1590
+ return count + 1;
1591
+ }
1560
1592
  function useFieldArrayCore(options, Context) {
1561
1593
  const contextForm = React.useContext(Context);
1562
1594
  const form = options.form || contextForm;
@@ -1592,6 +1624,14 @@
1592
1624
  () => onPathEvent(form.emitter, "change", path, "branch", syncFields),
1593
1625
  [form.emitter, path]
1594
1626
  );
1627
+ registerArrayIds(form, path.key, idsRef.current);
1628
+ React.useEffect(() => {
1629
+ registerArrayIds(form, path.key, idsRef.current);
1630
+ return () => {
1631
+ const registry = arrayIdsRegistry.get(form);
1632
+ if (registry?.get(path.key) === idsRef.current) registry.delete(path.key);
1633
+ };
1634
+ }, [form, path.key]);
1595
1635
  const append = useStageFn((value) => {
1596
1636
  const arr = getArray();
1597
1637
  idsRef.current.push(generateId());
@@ -1649,6 +1689,60 @@
1649
1689
  function useFieldArray(options) {
1650
1690
  return useFieldArrayCore(options, FormContext);
1651
1691
  }
1692
+ function useFieldArrayItemCore(options, Context) {
1693
+ const contextForm = React.useContext(Context);
1694
+ const form = options.form || contextForm;
1695
+ if (!form) throw new Error("no form provided");
1696
+ const { id } = options;
1697
+ const arrayPath = usePath(options.name);
1698
+ const computeIndex = React.useCallback(
1699
+ () => getArrayIds(form, arrayPath.key)?.indexOf(id) ?? -1,
1700
+ [form, arrayPath.key, id]
1701
+ );
1702
+ const computeValue = React.useCallback(
1703
+ (index2) => {
1704
+ const arr = getValueByPath(form, arrayPath);
1705
+ return Array.isArray(arr) ? arr[index2] : void 0;
1706
+ },
1707
+ [form, arrayPath]
1708
+ );
1709
+ const index = computeIndex();
1710
+ const value = computeValue(index);
1711
+ const lastRef = React.useRef({ index, value });
1712
+ lastRef.current = { index, value };
1713
+ const computeRef = useStage({ computeIndex, computeValue });
1714
+ const [, bump] = React.useReducer(bumpReducer, 0);
1715
+ React.useEffect(
1716
+ () => onPathEvent(form.emitter, "change", arrayPath, "leaf", () => {
1717
+ const { computeIndex: idx, computeValue: val } = computeRef.current;
1718
+ const nextIndex = idx();
1719
+ if (nextIndex !== lastRef.current.index || !Object.is(val(nextIndex), lastRef.current.value))
1720
+ bump();
1721
+ }),
1722
+ [form.emitter, arrayPath, computeRef]
1723
+ );
1724
+ const itemPath = usePath([...arrayPath.value, index]);
1725
+ const errors = useFieldErrorsByPath(form, itemPath);
1726
+ const setValue = useStageFn((v) => {
1727
+ if (index < 0) return;
1728
+ const arr = [...getValueByPath(form, arrayPath) ?? []];
1729
+ if (index >= arr.length) return;
1730
+ arr[index] = v;
1731
+ setValueByPath(form, arrayPath, arr);
1732
+ });
1733
+ return {
1734
+ value,
1735
+ setValue,
1736
+ errors,
1737
+ error: errors[0]?.message,
1738
+ name: itemPath.key,
1739
+ index,
1740
+ form
1741
+ };
1742
+ }
1743
+ function useFieldArrayItem(options) {
1744
+ return useFieldArrayItemCore(options, FormContext);
1745
+ }
1652
1746
 
1653
1747
  const FormContext = React.createContext(null);
1654
1748
  const FormProvider = FormContext.Provider;
@@ -1676,12 +1770,16 @@
1676
1770
  function useFieldArray(options) {
1677
1771
  return useFieldArrayCore(options, Context);
1678
1772
  }
1773
+ function useFieldArrayItem(options) {
1774
+ return useFieldArrayItemCore(options, Context);
1775
+ }
1679
1776
  return {
1680
1777
  context: Context,
1681
1778
  FormProvider: FormProvider2,
1682
1779
  useFormContext: useFormContext2,
1683
1780
  useField,
1684
- useFieldArray
1781
+ useFieldArray,
1782
+ useFieldArrayItem
1685
1783
  };
1686
1784
  }
1687
1785
  const CheckboxGroupContext = React.createContext(null);
@@ -2012,6 +2110,7 @@
2012
2110
  exports.useErrorByPath = useErrorByPath;
2013
2111
  exports.useField = useField;
2014
2112
  exports.useFieldArray = useFieldArray;
2113
+ exports.useFieldArrayItem = useFieldArrayItem;
2015
2114
  exports.useFieldErrors = useFieldErrors;
2016
2115
  exports.useFieldErrorsByPath = useFieldErrorsByPath;
2017
2116
  exports.useForm = useForm;