cogsbox-state 0.5.347 → 0.5.350

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.347",
3
+ "version": "0.5.350",
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,6 +1814,7 @@ function createProxyHandler<T>(
1814
1814
  endIndex: 10,
1815
1815
  });
1816
1816
  const isLockedToBottomRef = useRef(stickToBottom);
1817
+ const prevTotalCountRef = useRef(0);
1817
1818
 
1818
1819
  const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
1819
1820
 
@@ -1867,31 +1868,28 @@ function createProxyHandler<T>(
1867
1868
  });
1868
1869
  }, [range.startIndex, range.endIndex, sourceArray, totalCount]);
1869
1870
 
1870
- // --- YOUR SCROLLING ALGORITHM (UNCHANGED and WORKING) ---
1871
- // This effect is the entry point. It triggers when new items are added.
1871
+ // --- YOUR WORKING SCROLL ALGORITHM - UNTOUCHED ---
1872
1872
  useLayoutEffect(() => {
1873
1873
  const container = containerRef.current;
1874
- // Only run if we are supposed to be at the bottom.
1874
+ const hasNewItems = totalCount > prevTotalCountRef.current;
1875
+
1875
1876
  if (
1876
1877
  !container ||
1878
+ !stickToBottom ||
1877
1879
  !isLockedToBottomRef.current ||
1878
- totalCount === 0
1880
+ !hasNewItems
1879
1881
  ) {
1880
1882
  return;
1881
1883
  }
1882
1884
 
1883
- // STEP 1: Set the range to the end so the last items are rendered.
1884
- const visibleCount = 10;
1885
+ // STEP 1: Set range to the end to render the items we need to measure.
1885
1886
  setRange({
1886
- startIndex: Math.max(0, totalCount - visibleCount - overscan),
1887
+ startIndex: Math.max(0, totalCount - 10 - overscan),
1887
1888
  endIndex: totalCount,
1888
1889
  });
1889
1890
 
1890
- // STEP 2: Start the LOOP.
1891
- let loopCount = 0;
1891
+ // STEP 2: The LOOP.
1892
1892
  const intervalId = setInterval(() => {
1893
- loopCount++;
1894
- // The Check: Get the last item's height FROM THE SHADOW OBJECT.
1895
1893
  const lastItemIndex = totalCount - 1;
1896
1894
  const shadowArray =
1897
1895
  getGlobalStore
@@ -1901,33 +1899,24 @@ function createProxyHandler<T>(
1901
1899
  shadowArray[lastItemIndex]?.virtualizer?.itemHeight || 0;
1902
1900
 
1903
1901
  if (lastItemHeight > 0) {
1904
- // EXIT CONDITION MET
1905
- clearInterval(intervalId); // Stop the loop.
1906
-
1902
+ clearInterval(intervalId);
1907
1903
  // STEP 3: Scroll.
1908
1904
  container.scrollTo({
1909
1905
  top: container.scrollHeight,
1910
1906
  behavior: "smooth",
1911
1907
  });
1912
- } else {
1913
- if (loopCount > 20) {
1914
- // Safety break
1915
- clearInterval(intervalId);
1916
- }
1917
1908
  }
1918
1909
  }, 100);
1919
1910
 
1920
- // Cleanup: Stop the loop if the component unmounts.
1921
1911
  return () => clearInterval(intervalId);
1922
- }, [totalCount]); // This whole process triggers ONLY when totalCount changes.
1912
+ }, [totalCount]);
1923
1913
 
1924
1914
  // --- THE FIX IS HERE ---
1925
- // This effect now correctly handles user scrolling AND updates the view.
1915
+ // This effect now correctly updates when data changes.
1926
1916
  useEffect(() => {
1927
1917
  const container = containerRef.current;
1928
1918
  if (!container) return;
1929
1919
 
1930
- // This function now always has the LATEST totalCount and positions.
1931
1920
  const updateVirtualRange = () => {
1932
1921
  const { scrollTop, clientHeight } = container;
1933
1922
  let low = 0,
@@ -1961,24 +1950,28 @@ function createProxyHandler<T>(
1961
1950
  if (!isAtBottom) {
1962
1951
  isLockedToBottomRef.current = false;
1963
1952
  }
1964
- // This always calls the fresh version of updateVirtualRange.
1965
1953
  updateVirtualRange();
1966
1954
  };
1967
1955
 
1968
1956
  container.addEventListener("scroll", handleUserScroll, {
1969
1957
  passive: true,
1970
1958
  });
1971
- updateVirtualRange(); // Update range on initial load and when data changes.
1959
+ updateVirtualRange(); // Always update range on render.
1972
1960
 
1973
- // This cleanup is crucial. It removes the old listener before adding a new one.
1974
1961
  return () =>
1975
1962
  container.removeEventListener("scroll", handleUserScroll);
1976
- }, [totalCount, positions]); // Its dependency array now includes totalCount and positions.
1963
+ }, [totalCount, positions]); // FIX: This now has dependencies.
1977
1964
 
1965
+ // Simple effect to track previous item count for the scroll algorithm.
1966
+ useEffect(() => {
1967
+ prevTotalCountRef.current = totalCount;
1968
+ });
1978
1969
  const scrollToBottom = useCallback(
1979
1970
  (behavior: ScrollBehavior = "smooth") => {
1980
1971
  if (containerRef.current) {
1981
1972
  isLockedToBottomRef.current = true;
1973
+ console.log("USER ACTION: Scroll lock ENABLED.");
1974
+ // This is a manual trigger, so we don't need the loop. Just scroll.
1982
1975
  containerRef.current.scrollTo({
1983
1976
  top: containerRef.current.scrollHeight,
1984
1977
  behavior,
@@ -1992,6 +1985,7 @@ function createProxyHandler<T>(
1992
1985
  (index: number, behavior: ScrollBehavior = "smooth") => {
1993
1986
  if (containerRef.current && positions[index] !== undefined) {
1994
1987
  isLockedToBottomRef.current = false;
1988
+ console.log("USER ACTION: Scroll lock DISABLED.");
1995
1989
  containerRef.current.scrollTo({
1996
1990
  top: positions[index],
1997
1991
  behavior,
@@ -2018,6 +2012,7 @@ function createProxyHandler<T>(
2018
2012
  },
2019
2013
  },
2020
2014
  };
2015
+
2021
2016
  return {
2022
2017
  virtualState,
2023
2018
  virtualizerProps,