cogsbox-state 0.5.403 → 0.5.404
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/dist/CogsState.jsx +547 -536
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +83 -53
package/package.json
CHANGED
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,71 @@ function createProxyHandler<T>(
|
|
|
1877
1881
|
});
|
|
1878
1882
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1879
1883
|
|
|
1880
|
-
//
|
|
1884
|
+
// Handle auto-scroll to bottom
|
|
1881
1885
|
useEffect(() => {
|
|
1882
1886
|
if (!stickToBottom || !containerRef.current || totalCount === 0)
|
|
1883
1887
|
return;
|
|
1888
|
+
if (!shouldStickToBottomRef.current) return;
|
|
1884
1889
|
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
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
|
-
}
|
|
1890
|
+
// Clear any existing interval
|
|
1891
|
+
if (scrollToBottomIntervalRef.current) {
|
|
1892
|
+
clearInterval(scrollToBottomIntervalRef.current);
|
|
1893
|
+
}
|
|
1898
1894
|
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1895
|
+
// For initial load or big jumps, show the end immediately
|
|
1896
|
+
const jumpThreshold = 50;
|
|
1897
|
+
const isInitialLoad = range.endIndex < jumpThreshold;
|
|
1898
|
+
const isBigJump = totalCount > range.endIndex + jumpThreshold;
|
|
1899
|
+
|
|
1900
|
+
if (isInitialLoad || isBigJump) {
|
|
1901
|
+
// Jump to show the last items immediately
|
|
1902
|
+
setRange({
|
|
1903
|
+
startIndex: Math.max(0, totalCount - 20),
|
|
1904
|
+
endIndex: totalCount,
|
|
1905
|
+
});
|
|
1908
1906
|
}
|
|
1907
|
+
|
|
1908
|
+
// Keep scrolling to bottom until we're actually there
|
|
1909
|
+
let attempts = 0;
|
|
1910
|
+
const maxAttempts = 50; // 5 seconds max
|
|
1911
|
+
|
|
1912
|
+
scrollToBottomIntervalRef.current = setInterval(() => {
|
|
1913
|
+
const container = containerRef.current;
|
|
1914
|
+
if (!container) return;
|
|
1915
|
+
|
|
1916
|
+
attempts++;
|
|
1917
|
+
|
|
1918
|
+
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
1919
|
+
const currentBottom = scrollTop + clientHeight;
|
|
1920
|
+
const actualBottom = scrollHeight;
|
|
1921
|
+
const isAtBottom = actualBottom - currentBottom < 5;
|
|
1922
|
+
|
|
1923
|
+
console.log(
|
|
1924
|
+
`Scroll attempt ${attempts}: currentBottom=${currentBottom}, actualBottom=${actualBottom}, isAtBottom=${isAtBottom}`
|
|
1925
|
+
);
|
|
1926
|
+
|
|
1927
|
+
if (isAtBottom || attempts >= maxAttempts) {
|
|
1928
|
+
clearInterval(scrollToBottomIntervalRef.current!);
|
|
1929
|
+
scrollToBottomIntervalRef.current = null;
|
|
1930
|
+
console.log(
|
|
1931
|
+
isAtBottom ? "Reached bottom!" : "Timeout - giving up"
|
|
1932
|
+
);
|
|
1933
|
+
} else {
|
|
1934
|
+
// Use instant scroll, not smooth
|
|
1935
|
+
container.scrollTop = container.scrollHeight;
|
|
1936
|
+
}
|
|
1937
|
+
}, 100);
|
|
1938
|
+
|
|
1939
|
+
// Cleanup
|
|
1940
|
+
return () => {
|
|
1941
|
+
if (scrollToBottomIntervalRef.current) {
|
|
1942
|
+
clearInterval(scrollToBottomIntervalRef.current);
|
|
1943
|
+
scrollToBottomIntervalRef.current = null;
|
|
1944
|
+
}
|
|
1945
|
+
};
|
|
1909
1946
|
}, [totalCount, stickToBottom]);
|
|
1910
1947
|
|
|
1911
|
-
// Handle scroll
|
|
1948
|
+
// Handle user scroll
|
|
1912
1949
|
useEffect(() => {
|
|
1913
1950
|
const container = containerRef.current;
|
|
1914
1951
|
if (!container) return;
|
|
@@ -1916,21 +1953,27 @@ function createProxyHandler<T>(
|
|
|
1916
1953
|
let scrollTimeout: NodeJS.Timeout;
|
|
1917
1954
|
|
|
1918
1955
|
const handleScroll = () => {
|
|
1919
|
-
|
|
1920
|
-
|
|
1956
|
+
if (scrollToBottomIntervalRef.current) {
|
|
1957
|
+
// Stop auto-scrolling if user scrolls
|
|
1958
|
+
clearInterval(scrollToBottomIntervalRef.current);
|
|
1959
|
+
scrollToBottomIntervalRef.current = null;
|
|
1960
|
+
}
|
|
1921
1961
|
|
|
1922
|
-
|
|
1923
|
-
|
|
1962
|
+
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
1963
|
+
const isAtBottom =
|
|
1964
|
+
scrollHeight - scrollTop - clientHeight < 10;
|
|
1965
|
+
|
|
1966
|
+
// Update whether we should stick to bottom
|
|
1967
|
+
shouldStickToBottomRef.current = isAtBottom;
|
|
1924
1968
|
|
|
1925
|
-
//
|
|
1969
|
+
// Mark as user scrolling
|
|
1970
|
+
clearTimeout(scrollTimeout);
|
|
1971
|
+
isUserScrollingRef.current = true;
|
|
1926
1972
|
scrollTimeout = setTimeout(() => {
|
|
1927
|
-
|
|
1973
|
+
isUserScrollingRef.current = false;
|
|
1928
1974
|
}, 150);
|
|
1929
1975
|
|
|
1930
1976
|
// Update visible range
|
|
1931
|
-
const { scrollTop, clientHeight } = container;
|
|
1932
|
-
|
|
1933
|
-
// Find first visible item
|
|
1934
1977
|
let startIndex = 0;
|
|
1935
1978
|
for (let i = 0; i < positions.length; i++) {
|
|
1936
1979
|
if (positions[i]! > scrollTop - itemHeight * overscan) {
|
|
@@ -1939,7 +1982,6 @@ function createProxyHandler<T>(
|
|
|
1939
1982
|
}
|
|
1940
1983
|
}
|
|
1941
1984
|
|
|
1942
|
-
// Find last visible item
|
|
1943
1985
|
let endIndex = startIndex;
|
|
1944
1986
|
const viewportEnd = scrollTop + clientHeight;
|
|
1945
1987
|
for (let i = startIndex; i < positions.length; i++) {
|
|
@@ -1958,9 +2000,7 @@ function createProxyHandler<T>(
|
|
|
1958
2000
|
container.addEventListener("scroll", handleScroll, {
|
|
1959
2001
|
passive: true,
|
|
1960
2002
|
});
|
|
1961
|
-
|
|
1962
|
-
// Initial range calculation
|
|
1963
|
-
handleScroll();
|
|
2003
|
+
handleScroll(); // Initial calculation
|
|
1964
2004
|
|
|
1965
2005
|
return () => {
|
|
1966
2006
|
container.removeEventListener("scroll", handleScroll);
|
|
@@ -1968,22 +2008,12 @@ function createProxyHandler<T>(
|
|
|
1968
2008
|
};
|
|
1969
2009
|
}, [positions, totalCount, itemHeight, overscan]);
|
|
1970
2010
|
|
|
1971
|
-
// Cleanup scroll timeout on unmount
|
|
1972
|
-
useEffect(() => {
|
|
1973
|
-
return () => {
|
|
1974
|
-
if (scrollTimeoutRef.current) {
|
|
1975
|
-
clearTimeout(scrollTimeoutRef.current);
|
|
1976
|
-
}
|
|
1977
|
-
};
|
|
1978
|
-
}, []);
|
|
1979
|
-
|
|
1980
2011
|
const scrollToBottom = useCallback(
|
|
1981
|
-
(behavior: ScrollBehavior = "
|
|
2012
|
+
(behavior: ScrollBehavior = "auto") => {
|
|
2013
|
+
shouldStickToBottomRef.current = true;
|
|
1982
2014
|
if (containerRef.current) {
|
|
1983
|
-
containerRef.current.
|
|
1984
|
-
|
|
1985
|
-
behavior,
|
|
1986
|
-
});
|
|
2015
|
+
containerRef.current.scrollTop =
|
|
2016
|
+
containerRef.current.scrollHeight;
|
|
1987
2017
|
}
|
|
1988
2018
|
},
|
|
1989
2019
|
[]
|