cogsbox-state 0.5.287 → 0.5.288

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogsbox-state",
3
- "version": "0.5.287",
3
+ "version": "0.5.288",
4
4
  "description": "React state management library with form controls and server sync",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/CogsState.tsx CHANGED
@@ -1803,8 +1803,9 @@ function createProxyHandler<T>(
1803
1803
  return (
1804
1804
  options: VirtualViewOptions
1805
1805
  ): VirtualStateObjectResult<any[]> => {
1806
+ // --- CHANGE 1: itemHeight is now optional with a default ---
1806
1807
  const {
1807
- itemHeight = 50, // Default height for unmeasured items
1808
+ itemHeight = 50, // Serves as a fallback for unmeasured items
1808
1809
  overscan = 5,
1809
1810
  stickToBottom = false,
1810
1811
  } = options;
@@ -1815,18 +1816,7 @@ function createProxyHandler<T>(
1815
1816
  endIndex: 10,
1816
1817
  });
1817
1818
 
1818
- // --- State Tracking Refs for stickToBottom ---
1819
- const isAtBottomRef = useRef(stickToBottom);
1820
- const previousTotalCountRef = useRef(0);
1821
- const isInitialMountRef = useRef(true);
1822
-
1823
- const sourceArray = getGlobalStore().getNestedState(
1824
- stateKey,
1825
- path
1826
- ) as any[];
1827
- const totalCount = sourceArray.length;
1828
-
1829
- // Helper to get an item's measured height or the default
1819
+ // --- CHANGE 2: Add a helper to get heights from shadow store ---
1830
1820
  const getItemHeight = useCallback(
1831
1821
  (index: number): number => {
1832
1822
  const metadata = getGlobalStore
@@ -1837,7 +1827,18 @@ function createProxyHandler<T>(
1837
1827
  [itemHeight, stateKey, path]
1838
1828
  );
1839
1829
 
1840
- // Pre-calculate total height and the top offset of each item
1830
+ const isAtBottomRef = useRef(stickToBottom);
1831
+ const previousTotalCountRef = useRef(0);
1832
+ const isInitialMountRef = useRef(true);
1833
+
1834
+ const sourceArray = getGlobalStore().getNestedState(
1835
+ stateKey,
1836
+ path
1837
+ ) as any[];
1838
+ const totalCount = sourceArray.length;
1839
+
1840
+ // --- CHANGE 3: Pre-calculate total height and item positions ---
1841
+ // This replaces all instances of `totalCount * itemHeight`.
1841
1842
  const { totalHeight, positions } = useMemo(() => {
1842
1843
  let currentHeight = 0;
1843
1844
  const pos: number[] = [];
@@ -1848,6 +1849,7 @@ function createProxyHandler<T>(
1848
1849
  return { totalHeight: currentHeight, positions: pos };
1849
1850
  }, [totalCount, getItemHeight]);
1850
1851
 
1852
+ // This part is IDENTICAL to your original code
1851
1853
  const virtualState = useMemo(() => {
1852
1854
  const start = Math.max(0, range.startIndex);
1853
1855
  const end = Math.min(totalCount, range.endIndex);
@@ -1869,13 +1871,16 @@ function createProxyHandler<T>(
1869
1871
  const wasAtBottom = isAtBottomRef.current;
1870
1872
  const listGrew = totalCount > previousTotalCountRef.current;
1871
1873
  previousTotalCountRef.current = totalCount;
1874
+
1872
1875
  const handleScroll = () => {
1873
1876
  const { scrollTop, clientHeight, scrollHeight } = container;
1874
1877
  isAtBottomRef.current =
1875
1878
  scrollHeight - scrollTop - clientHeight < 10;
1876
1879
 
1877
- // Find the start index by searching for the first item in the viewport
1880
+ // --- CHANGE 4: Update scroll logic to use positions array ---
1881
+ // This is the dynamic equivalent of `Math.floor(scrollTop / itemHeight)`.
1878
1882
  let startIndex = 0;
1883
+ // A simple loop is robust and easy to understand.
1879
1884
  for (let i = 0; i < positions.length; i++) {
1880
1885
  if (positions[i]! >= scrollTop) {
1881
1886
  startIndex = i;
@@ -1883,16 +1888,15 @@ function createProxyHandler<T>(
1883
1888
  }
1884
1889
  }
1885
1890
 
1886
- // Find the end index by seeing how many items fit in the viewport
1887
1891
  let endIndex = startIndex;
1888
1892
  while (
1889
1893
  endIndex < totalCount &&
1894
+ positions[endIndex] &&
1890
1895
  positions[endIndex]! < scrollTop + clientHeight
1891
1896
  ) {
1892
1897
  endIndex++;
1893
1898
  }
1894
1899
 
1895
- // Apply overscan
1896
1900
  startIndex = Math.max(0, startIndex - overscan);
1897
1901
  endIndex = Math.min(totalCount, endIndex + overscan);
1898
1902
 
@@ -1901,7 +1905,7 @@ function createProxyHandler<T>(
1901
1905
  prevRange.startIndex !== startIndex ||
1902
1906
  prevRange.endIndex !== endIndex
1903
1907
  ) {
1904
- return { startIndex, endIndex };
1908
+ return { startIndex: startIndex, endIndex: endIndex };
1905
1909
  }
1906
1910
  return prevRange;
1907
1911
  });
@@ -1911,7 +1915,7 @@ function createProxyHandler<T>(
1911
1915
  passive: true,
1912
1916
  });
1913
1917
 
1914
- // Logic to keep the view scrolled to the bottom
1918
+ // This logic is IDENTICAL to your original code
1915
1919
  if (stickToBottom) {
1916
1920
  if (isInitialMountRef.current) {
1917
1921
  container.scrollTo({
@@ -1927,12 +1931,12 @@ function createProxyHandler<T>(
1927
1931
  });
1928
1932
  }
1929
1933
  }
1930
-
1931
1934
  isInitialMountRef.current = false;
1932
- handleScroll(); // Initial calculation
1935
+ handleScroll();
1933
1936
 
1934
1937
  return () =>
1935
1938
  container.removeEventListener("scroll", handleScroll);
1939
+ // The dependencies are almost identical, just swapping itemHeight for `positions`
1936
1940
  }, [totalCount, overscan, stickToBottom, positions]);
1937
1941
 
1938
1942
  const scrollToBottom = useCallback(
@@ -1947,18 +1951,20 @@ function createProxyHandler<T>(
1947
1951
  []
1948
1952
  );
1949
1953
 
1954
+ // --- CHANGE 5: Update scrollToIndex to use positions array ---
1950
1955
  const scrollToIndex = useCallback(
1951
1956
  (index: number, behavior: ScrollBehavior = "smooth") => {
1952
1957
  if (containerRef.current && positions[index] !== undefined) {
1953
1958
  containerRef.current.scrollTo({
1954
- top: positions[index],
1959
+ top: positions[index], // Instead of `index * itemHeight`
1955
1960
  behavior,
1956
1961
  });
1957
1962
  }
1958
1963
  },
1959
- [positions] // Depends on the calculated positions
1964
+ [positions] // Depends on `positions` now instead of `itemHeight`
1960
1965
  );
1961
1966
 
1967
+ // --- CHANGE 6: Update props to use dynamic totalHeight and offsets ---
1962
1968
  const virtualizerProps = {
1963
1969
  outer: {
1964
1970
  ref: containerRef,
@@ -1966,12 +1972,13 @@ function createProxyHandler<T>(
1966
1972
  },
1967
1973
  inner: {
1968
1974
  style: {
1969
- height: `${totalHeight}px`,
1975
+ height: `${totalHeight}px`, // Use calculated total height
1970
1976
  position: "relative",
1971
1977
  },
1972
1978
  },
1973
1979
  list: {
1974
1980
  style: {
1981
+ // Use the pre-calculated position of the first visible item
1975
1982
  transform: `translateY(${positions[range.startIndex] || 0}px)`,
1976
1983
  },
1977
1984
  },