cogsbox-state 0.5.403 → 0.5.405

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.403",
3
+ "version": "0.5.405",
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
@@ -1804,6 +1804,7 @@ function createProxyHandler<T>(
1804
1804
  };
1805
1805
  }
1806
1806
  // Simplified useVirtualView approach
1807
+ // Optimal approach - replace the useVirtualView implementation
1807
1808
  if (prop === "useVirtualView") {
1808
1809
  return (
1809
1810
  options: VirtualViewOptions
@@ -1820,9 +1821,12 @@ function createProxyHandler<T>(
1820
1821
  startIndex: 0,
1821
1822
  endIndex: 10,
1822
1823
  });
1823
- const isUserScrolling = useRef(false);
1824
- const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
1825
1824
  const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
1825
+ const isUserScrollingRef = useRef(false);
1826
+ const shouldStickToBottomRef = useRef(true);
1827
+ const scrollToBottomIntervalRef = useRef<NodeJS.Timeout | null>(
1828
+ null
1829
+ );
1826
1830
 
1827
1831
  // Subscribe to shadow state updates
1828
1832
  useEffect(() => {
@@ -1877,38 +1881,79 @@ function createProxyHandler<T>(
1877
1881
  });
1878
1882
  }, [range.startIndex, range.endIndex, sourceArray, totalCount]);
1879
1883
 
1880
- // Simple scroll to bottom when items are added
1884
+ // Handle auto-scroll to bottom
1885
+ const [hasMounted, setHasMounted] = useState(false);
1886
+
1887
+ // Add this effect to track mounting:
1888
+ useEffect(() => {
1889
+ setHasMounted(true);
1890
+ }, []);
1891
+
1892
+ // Replace the auto-scroll effect with this version:
1881
1893
  useEffect(() => {
1882
1894
  if (!stickToBottom || !containerRef.current || totalCount === 0)
1883
1895
  return;
1896
+ if (!shouldStickToBottomRef.current && hasMounted) return; // Only check shouldStickToBottom after mount
1884
1897
 
1885
- const container = containerRef.current;
1886
- const isNearBottom =
1887
- container.scrollHeight -
1888
- container.scrollTop -
1889
- container.clientHeight <
1890
- 100;
1891
-
1892
- // If user is near bottom or we're auto-scrolling, scroll to bottom
1893
- if (isNearBottom || !isUserScrolling.current) {
1894
- // Clear any pending scroll
1895
- if (scrollTimeoutRef.current) {
1896
- clearTimeout(scrollTimeoutRef.current);
1897
- }
1898
+ // Clear any existing interval
1899
+ if (scrollToBottomIntervalRef.current) {
1900
+ clearInterval(scrollToBottomIntervalRef.current);
1901
+ }
1898
1902
 
1899
- // Delay scroll to allow items to render and measure
1900
- scrollTimeoutRef.current = setTimeout(() => {
1901
- if (containerRef.current) {
1902
- containerRef.current.scrollTo({
1903
- top: containerRef.current.scrollHeight,
1904
- behavior: "smooth",
1905
- });
1906
- }
1907
- }, 100);
1903
+ // For initial load or big jumps, show the end immediately
1904
+ const jumpThreshold = 50;
1905
+ const isInitialLoad = range.endIndex < jumpThreshold;
1906
+ const isBigJump = totalCount > range.endIndex + jumpThreshold;
1907
+
1908
+ if (isInitialLoad || isBigJump) {
1909
+ // Jump to show the last items immediately
1910
+ setRange({
1911
+ startIndex: Math.max(0, totalCount - 20),
1912
+ endIndex: totalCount,
1913
+ });
1908
1914
  }
1909
- }, [totalCount, stickToBottom]);
1910
1915
 
1911
- // Handle scroll events
1916
+ // Keep scrolling to bottom until we're actually there
1917
+ let attempts = 0;
1918
+ const maxAttempts = 50; // 5 seconds max
1919
+
1920
+ scrollToBottomIntervalRef.current = setInterval(() => {
1921
+ const container = containerRef.current;
1922
+ if (!container) return;
1923
+
1924
+ attempts++;
1925
+
1926
+ const { scrollTop, scrollHeight, clientHeight } = container;
1927
+ const currentBottom = scrollTop + clientHeight;
1928
+ const actualBottom = scrollHeight;
1929
+ const isAtBottom = actualBottom - currentBottom < 5;
1930
+
1931
+ console.log(
1932
+ `Scroll attempt ${attempts}: currentBottom=${currentBottom}, actualBottom=${actualBottom}, isAtBottom=${isAtBottom}, hasMounted=${hasMounted}`
1933
+ );
1934
+
1935
+ if (isAtBottom || attempts >= maxAttempts) {
1936
+ clearInterval(scrollToBottomIntervalRef.current!);
1937
+ scrollToBottomIntervalRef.current = null;
1938
+ console.log(
1939
+ isAtBottom ? "Reached bottom!" : "Timeout - giving up"
1940
+ );
1941
+ } else {
1942
+ // Use instant scroll, not smooth
1943
+ container.scrollTop = container.scrollHeight;
1944
+ }
1945
+ }, 100);
1946
+
1947
+ // Cleanup
1948
+ return () => {
1949
+ if (scrollToBottomIntervalRef.current) {
1950
+ clearInterval(scrollToBottomIntervalRef.current);
1951
+ scrollToBottomIntervalRef.current = null;
1952
+ }
1953
+ };
1954
+ }, [totalCount, stickToBottom, hasMounted]);
1955
+
1956
+ // Handle user scroll
1912
1957
  useEffect(() => {
1913
1958
  const container = containerRef.current;
1914
1959
  if (!container) return;
@@ -1916,21 +1961,27 @@ function createProxyHandler<T>(
1916
1961
  let scrollTimeout: NodeJS.Timeout;
1917
1962
 
1918
1963
  const handleScroll = () => {
1919
- // Clear existing timeout
1920
- clearTimeout(scrollTimeout);
1964
+ if (scrollToBottomIntervalRef.current) {
1965
+ // Stop auto-scrolling if user scrolls
1966
+ clearInterval(scrollToBottomIntervalRef.current);
1967
+ scrollToBottomIntervalRef.current = null;
1968
+ }
1921
1969
 
1922
- // Mark as user scrolling
1923
- isUserScrolling.current = true;
1970
+ const { scrollTop, scrollHeight, clientHeight } = container;
1971
+ const isAtBottom =
1972
+ scrollHeight - scrollTop - clientHeight < 10;
1973
+
1974
+ // Update whether we should stick to bottom
1975
+ shouldStickToBottomRef.current = isAtBottom;
1924
1976
 
1925
- // Reset after scrolling stops
1977
+ // Mark as user scrolling
1978
+ clearTimeout(scrollTimeout);
1979
+ isUserScrollingRef.current = true;
1926
1980
  scrollTimeout = setTimeout(() => {
1927
- isUserScrolling.current = false;
1981
+ isUserScrollingRef.current = false;
1928
1982
  }, 150);
1929
1983
 
1930
1984
  // Update visible range
1931
- const { scrollTop, clientHeight } = container;
1932
-
1933
- // Find first visible item
1934
1985
  let startIndex = 0;
1935
1986
  for (let i = 0; i < positions.length; i++) {
1936
1987
  if (positions[i]! > scrollTop - itemHeight * overscan) {
@@ -1939,7 +1990,6 @@ function createProxyHandler<T>(
1939
1990
  }
1940
1991
  }
1941
1992
 
1942
- // Find last visible item
1943
1993
  let endIndex = startIndex;
1944
1994
  const viewportEnd = scrollTop + clientHeight;
1945
1995
  for (let i = startIndex; i < positions.length; i++) {
@@ -1958,9 +2008,7 @@ function createProxyHandler<T>(
1958
2008
  container.addEventListener("scroll", handleScroll, {
1959
2009
  passive: true,
1960
2010
  });
1961
-
1962
- // Initial range calculation
1963
- handleScroll();
2011
+ handleScroll(); // Initial calculation
1964
2012
 
1965
2013
  return () => {
1966
2014
  container.removeEventListener("scroll", handleScroll);
@@ -1968,22 +2016,12 @@ function createProxyHandler<T>(
1968
2016
  };
1969
2017
  }, [positions, totalCount, itemHeight, overscan]);
1970
2018
 
1971
- // Cleanup scroll timeout on unmount
1972
- useEffect(() => {
1973
- return () => {
1974
- if (scrollTimeoutRef.current) {
1975
- clearTimeout(scrollTimeoutRef.current);
1976
- }
1977
- };
1978
- }, []);
1979
-
1980
2019
  const scrollToBottom = useCallback(
1981
- (behavior: ScrollBehavior = "smooth") => {
2020
+ (behavior: ScrollBehavior = "auto") => {
2021
+ shouldStickToBottomRef.current = true;
1982
2022
  if (containerRef.current) {
1983
- containerRef.current.scrollTo({
1984
- top: containerRef.current.scrollHeight,
1985
- behavior,
1986
- });
2023
+ containerRef.current.scrollTop =
2024
+ containerRef.current.scrollHeight;
1987
2025
  }
1988
2026
  },
1989
2027
  []