cogsbox-state 0.5.318 → 0.5.319

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.318",
3
+ "version": "0.5.319",
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
@@ -1814,24 +1814,16 @@ function createProxyHandler<T>(
1814
1814
  endIndex: 10,
1815
1815
  });
1816
1816
 
1817
+ // This ref tracks if the user is locked to the bottom.
1817
1818
  const isLockedToBottomRef = useRef(stickToBottom);
1818
- const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
1819
1819
 
1820
- // Track if we've scrolled to bottom after initial load
1821
- const hasScrolledToBottomRef = useRef(false);
1822
- const lastTotalCountRef = useRef(0);
1820
+ // This state triggers a re-render when item heights change.
1821
+ const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
1823
1822
 
1824
1823
  useEffect(() => {
1825
- let updateCount = 0;
1826
1824
  const unsubscribe = getGlobalStore
1827
1825
  .getState()
1828
1826
  .subscribeToShadowState(stateKey, () => {
1829
- updateCount++;
1830
- if (updateCount <= 5) {
1831
- console.log(
1832
- `[VirtualView] Shadow update #${updateCount}`
1833
- );
1834
- }
1835
1827
  setShadowUpdateTrigger((prev) => prev + 1);
1836
1828
  });
1837
1829
  return unsubscribe;
@@ -1843,77 +1835,32 @@ function createProxyHandler<T>(
1843
1835
  ) as any[];
1844
1836
  const totalCount = sourceArray.length;
1845
1837
 
1846
- console.log(
1847
- `[VirtualView] Initial setup - totalCount: ${totalCount}, itemHeight: ${itemHeight}, stickToBottom: ${stickToBottom}`
1848
- );
1849
-
1850
- // Reset when array size changes significantly
1851
- if (totalCount !== lastTotalCountRef.current) {
1852
- console.log(
1853
- `[VirtualView] Array size changed from ${lastTotalCountRef.current} to ${totalCount}`
1854
- );
1855
- hasScrolledToBottomRef.current = false;
1856
- lastTotalCountRef.current = totalCount;
1857
- }
1858
-
1859
- // Calculate heights from shadow state
1860
- const { totalHeight, positions, visibleMeasured } =
1861
- useMemo(() => {
1862
- const shadowArray =
1863
- getGlobalStore
1864
- .getState()
1865
- .getShadowMetadata(stateKey, path) || [];
1866
- let height = 0;
1867
- const pos: number[] = [];
1868
- let measuredCount = 0;
1869
- let visibleMeasuredCount = 0;
1870
-
1871
- for (let i = 0; i < totalCount; i++) {
1872
- pos[i] = height;
1873
- const measuredHeight =
1874
- shadowArray[i]?.virtualizer?.itemHeight;
1875
- if (measuredHeight) {
1876
- measuredCount++;
1877
- // Count measured items in current visible range
1878
- if (i >= range.startIndex && i < range.endIndex) {
1879
- visibleMeasuredCount++;
1880
- }
1881
- }
1882
- height += measuredHeight || itemHeight;
1883
- }
1884
-
1885
- // Check if all VISIBLE items are measured
1886
- const visibleCount = range.endIndex - range.startIndex;
1887
- const allVisibleMeasured =
1888
- visibleMeasuredCount === visibleCount && visibleCount > 0;
1889
-
1890
- console.log(
1891
- `[VirtualView] Heights calc - measured: ${measuredCount}/${totalCount}, visible measured: ${visibleMeasuredCount}/${visibleCount}, totalHeight: ${height}`
1892
- );
1893
-
1894
- return {
1895
- totalHeight: height,
1896
- positions: pos,
1897
- visibleMeasured: allVisibleMeasured,
1898
- };
1899
- }, [
1900
- totalCount,
1901
- stateKey,
1902
- path.join("."),
1903
- itemHeight,
1904
- shadowUpdateTrigger,
1905
- range, // Add range dependency
1906
- ]);
1838
+ // Calculate heights from shadow state. This runs when data or measurements change.
1839
+ const { totalHeight, positions } = useMemo(() => {
1840
+ const shadowArray =
1841
+ getGlobalStore.getState().getShadowMetadata(stateKey, path) ||
1842
+ [];
1843
+ let height = 0;
1844
+ const pos: number[] = [];
1845
+ for (let i = 0; i < totalCount; i++) {
1846
+ pos[i] = height;
1847
+ const measuredHeight =
1848
+ shadowArray[i]?.virtualizer?.itemHeight;
1849
+ height += measuredHeight || itemHeight;
1850
+ }
1851
+ return { totalHeight: height, positions: pos };
1852
+ }, [
1853
+ totalCount,
1854
+ stateKey,
1855
+ path.join("."),
1856
+ itemHeight,
1857
+ shadowUpdateTrigger,
1858
+ ]);
1907
1859
 
1908
- // Memoize the virtualized slice
1860
+ // Memoize the virtualized slice of data.
1909
1861
  const virtualState = useMemo(() => {
1910
1862
  const start = Math.max(0, range.startIndex);
1911
1863
  const end = Math.min(totalCount, range.endIndex);
1912
-
1913
- console.log(
1914
- `[VirtualView] Creating virtual slice - range: ${start}-${end} (${end - start} items)`
1915
- );
1916
-
1917
1864
  const validIndices = Array.from(
1918
1865
  { length: end - start },
1919
1866
  (_, i) => start + i
@@ -1925,11 +1872,14 @@ function createProxyHandler<T>(
1925
1872
  });
1926
1873
  }, [range.startIndex, range.endIndex, sourceArray, totalCount]);
1927
1874
 
1928
- // Main layout effect
1875
+ // This is the main effect that handles all scrolling and updates.
1929
1876
  useLayoutEffect(() => {
1930
1877
  const container = containerRef.current;
1931
1878
  if (!container) return;
1932
1879
 
1880
+ let scrollTimeoutId: NodeJS.Timeout;
1881
+
1882
+ // This function determines what's visible in the viewport.
1933
1883
  const updateVirtualRange = () => {
1934
1884
  if (!container) return;
1935
1885
  const { scrollTop } = container;
@@ -1953,6 +1903,7 @@ function createProxyHandler<T>(
1953
1903
  setRange({ startIndex, endIndex });
1954
1904
  };
1955
1905
 
1906
+ // This function handles ONLY user-initiated scrolls.
1956
1907
  const handleUserScroll = () => {
1957
1908
  isLockedToBottomRef.current =
1958
1909
  container.scrollHeight -
@@ -1966,52 +1917,28 @@ function createProxyHandler<T>(
1966
1917
  passive: true,
1967
1918
  });
1968
1919
 
1969
- // For stick to bottom: check conditions without triggering re-renders
1970
- if (
1971
- stickToBottom &&
1972
- !hasScrolledToBottomRef.current &&
1973
- totalCount > 0
1974
- ) {
1975
- // Check current range without dependency
1976
- const currentRange = range;
1977
- const atEnd = currentRange.endIndex >= totalCount - 5; // Close to end
1978
-
1979
- if (atEnd) {
1980
- console.log(
1981
- `[VirtualView] At end of list, scrolling to bottom`
1982
- );
1983
- hasScrolledToBottomRef.current = true;
1984
-
1985
- setTimeout(() => {
1986
- const scrollTarget = container.scrollHeight + 1000;
1920
+ // In useLayoutEffect, replace this section:
1921
+ if (stickToBottom) {
1922
+ scrollTimeoutId = setTimeout(() => {
1923
+ if (isLockedToBottomRef.current) {
1987
1924
  container.scrollTo({
1988
- top: scrollTarget,
1925
+ top: container.scrollHeight + 1000, // ADD A BUFFER HERE
1989
1926
  behavior: "auto",
1990
1927
  });
1991
- isLockedToBottomRef.current = true;
1992
- }, 50);
1993
- } else {
1994
- // Jump close to the bottom
1995
- console.log(
1996
- `[VirtualView] Jumping near bottom to trigger measurements`
1997
- );
1998
- const estimatedScrollPosition = Math.max(
1999
- 0,
2000
- (totalCount - 20) * itemHeight
2001
- );
2002
- container.scrollTo({
2003
- top: estimatedScrollPosition,
2004
- behavior: "auto",
2005
- });
2006
- }
1928
+ }
1929
+ }, 200);
2007
1930
  }
2008
1931
 
1932
+ // Update the visible range on initial load.
2009
1933
  updateVirtualRange();
2010
1934
 
1935
+ // Cleanup function is vital to prevent memory leaks.
2011
1936
  return () => {
1937
+ clearTimeout(scrollTimeoutId);
2012
1938
  container.removeEventListener("scroll", handleUserScroll);
2013
1939
  };
2014
- }, [totalCount, positions, stickToBottom]); // Removed visibleMeasured and range.endIndex
1940
+ // This effect re-runs whenever the list size or item heights change.
1941
+ }, [totalCount, positions, stickToBottom]);
2015
1942
 
2016
1943
  const scrollToBottom = useCallback(
2017
1944
  (behavior: ScrollBehavior = "smooth") => {