cogsbox-state 0.5.339 → 0.5.340

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.339",
3
+ "version": "0.5.340",
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
@@ -1895,12 +1895,13 @@ function createProxyHandler<T>(
1895
1895
  // }
1896
1896
  // }, [totalCount]);
1897
1897
  // This is the main effect that handles all scrolling and updates.
1898
+ // This is the main effect that handles all scrolling and updates.
1898
1899
  useLayoutEffect(() => {
1899
1900
  const container = containerRef.current;
1900
1901
  if (!container) return;
1901
1902
 
1902
1903
  // --- STEP 1: Remember if we were scrolled to the bottom BEFORE this render ---
1903
- // We check this now, before the new items might have pushed the scrollbar up.
1904
+ // The check is made more forgiving (< itemHeight) to handle measurement delays.
1904
1905
  const wasScrolledToBottom =
1905
1906
  container.scrollHeight -
1906
1907
  container.scrollTop -
@@ -1911,8 +1912,7 @@ function createProxyHandler<T>(
1911
1912
  const updateVirtualRange = () => {
1912
1913
  if (!container) return;
1913
1914
  const { scrollTop, clientHeight } = container;
1914
-
1915
- // Find the first visible item
1915
+ // ... (rest of the function is the same, no changes needed)
1916
1916
  let low = 0,
1917
1917
  high = totalCount - 1;
1918
1918
  while (low <= high) {
@@ -1921,8 +1921,6 @@ function createProxyHandler<T>(
1921
1921
  else high = mid - 1;
1922
1922
  }
1923
1923
  const startIndex = Math.max(0, high - overscan);
1924
-
1925
- // Find the last visible item
1926
1924
  let endIndex = startIndex;
1927
1925
  const visibleEnd = scrollTop + clientHeight;
1928
1926
  while (
@@ -1932,8 +1930,6 @@ function createProxyHandler<T>(
1932
1930
  endIndex++;
1933
1931
  }
1934
1932
  endIndex = Math.min(totalCount, endIndex + overscan);
1935
-
1936
- // Update the state to render the correct slice
1937
1933
  setRange({ startIndex, endIndex });
1938
1934
  };
1939
1935
 
@@ -1944,7 +1940,7 @@ function createProxyHandler<T>(
1944
1940
  container.scrollHeight -
1945
1941
  container.scrollTop -
1946
1942
  container.clientHeight <
1947
- 1;
1943
+ itemHeight; // Strict check for user action
1948
1944
  // Then, just render what's visible at the new position.
1949
1945
  updateVirtualRange();
1950
1946
  };
@@ -1954,14 +1950,21 @@ function createProxyHandler<T>(
1954
1950
  passive: true,
1955
1951
  });
1956
1952
 
1957
- // --- STEP 2: Apply the scroll AFTER the render, based on what we remembered ---
1953
+ let scrollTimeoutId: NodeJS.Timeout | undefined;
1954
+
1955
+ // --- STEP 2: Conditionally schedule the SMOOTH scroll ---
1958
1956
  if (
1959
1957
  stickToBottom &&
1960
1958
  (isLockedToBottomRef.current || wasScrolledToBottom)
1961
1959
  ) {
1962
- // If we are "locked" OR if we were at the bottom just before this render,
1963
- // then scroll to the new bottom. This handles both initial load and new items.
1964
- container.scrollTop = container.scrollHeight;
1960
+ // A timeout is crucial for smooth scroll. It schedules the scroll
1961
+ // for *after* the browser has painted the new items, preventing jank.
1962
+ scrollTimeoutId = setTimeout(() => {
1963
+ container.scrollTo({
1964
+ top: container.scrollHeight,
1965
+ behavior: "smooth",
1966
+ });
1967
+ }, 0); // 0ms is enough to defer it to the next event loop tick.
1965
1968
  }
1966
1969
 
1967
1970
  // Always calculate the visible range after any potential scroll changes.
@@ -1970,8 +1973,17 @@ function createProxyHandler<T>(
1970
1973
  // Cleanup function is vital to prevent memory leaks.
1971
1974
  return () => {
1972
1975
  container.removeEventListener("scroll", handleUserScroll);
1976
+ if (scrollTimeoutId) {
1977
+ clearTimeout(scrollTimeoutId);
1978
+ }
1973
1979
  };
1974
- }, [totalCount, positions, totalHeight, stickToBottom]);
1980
+ }, [
1981
+ totalCount,
1982
+ positions,
1983
+ totalHeight,
1984
+ stickToBottom,
1985
+ itemHeight,
1986
+ ]); // Add itemHeight to dependencies
1975
1987
 
1976
1988
  const scrollToBottom = useCallback(
1977
1989
  (behavior: ScrollBehavior = "smooth") => {