cogsbox-state 0.5.376 → 0.5.378
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 +689 -681
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +50 -58
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1827,8 +1827,7 @@ function createProxyHandler<T>(
|
|
|
1827
1827
|
const isProgrammaticScroll = useRef(false);
|
|
1828
1828
|
const prevTotalCountRef = useRef(0);
|
|
1829
1829
|
const prevDepsRef = useRef(dependencies);
|
|
1830
|
-
const
|
|
1831
|
-
|
|
1830
|
+
const lastUpdateAtScrollTop = useRef(0);
|
|
1832
1831
|
const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
|
|
1833
1832
|
|
|
1834
1833
|
useEffect(() => {
|
|
@@ -1934,11 +1933,13 @@ function createProxyHandler<T>(
|
|
|
1934
1933
|
console.log(
|
|
1935
1934
|
"ACTION (GETTING_HEIGHTS): Setting range to end and starting loop."
|
|
1936
1935
|
);
|
|
1936
|
+
|
|
1937
1937
|
setRange({
|
|
1938
1938
|
startIndex: Math.max(0, totalCount - 10 - overscan),
|
|
1939
1939
|
endIndex: totalCount,
|
|
1940
1940
|
});
|
|
1941
1941
|
|
|
1942
|
+
let intervalId: NodeJS.Timeout;
|
|
1942
1943
|
intervalId = setInterval(() => {
|
|
1943
1944
|
const lastItemIndex = totalCount - 1;
|
|
1944
1945
|
const shadowArray =
|
|
@@ -1950,15 +1951,36 @@ function createProxyHandler<T>(
|
|
|
1950
1951
|
|
|
1951
1952
|
if (lastItemHeight > 0) {
|
|
1952
1953
|
clearInterval(intervalId);
|
|
1953
|
-
if (!shouldNotScroll.current) {
|
|
1954
|
-
console.log(
|
|
1955
|
-
"ACTION (GETTING_HEIGHTS): Measurement success -> SCROLLING_TO_BOTTOM"
|
|
1956
|
-
);
|
|
1957
1954
|
|
|
1958
|
-
|
|
1955
|
+
if (!shouldNotScroll.current) {
|
|
1956
|
+
const prevCount = prevTotalCountRef.current;
|
|
1957
|
+
const addedItems = totalCount - prevCount;
|
|
1958
|
+
const smallAddition = addedItems > 0 && addedItems <= 3;
|
|
1959
|
+
|
|
1960
|
+
if (smallAddition) {
|
|
1961
|
+
const addedHeight =
|
|
1962
|
+
positions[totalCount - 1]! -
|
|
1963
|
+
(positions[prevCount] ?? 0);
|
|
1964
|
+
container.scrollBy({
|
|
1965
|
+
top: addedHeight,
|
|
1966
|
+
behavior: "smooth",
|
|
1967
|
+
});
|
|
1968
|
+
|
|
1969
|
+
console.log(
|
|
1970
|
+
"ACTION (GETTING_HEIGHTS): Small addition -> LOCKED_AT_BOTTOM"
|
|
1971
|
+
);
|
|
1972
|
+
setStatus("LOCKED_AT_BOTTOM");
|
|
1973
|
+
} else {
|
|
1974
|
+
console.log(
|
|
1975
|
+
"ACTION (GETTING_HEIGHTS): Large change -> SCROLLING_TO_BOTTOM"
|
|
1976
|
+
);
|
|
1977
|
+
setStatus("SCROLLING_TO_BOTTOM");
|
|
1978
|
+
}
|
|
1959
1979
|
}
|
|
1960
1980
|
}
|
|
1961
1981
|
}, 100);
|
|
1982
|
+
|
|
1983
|
+
return () => clearInterval(intervalId);
|
|
1962
1984
|
} else if (status === "SCROLLING_TO_BOTTOM") {
|
|
1963
1985
|
console.log(
|
|
1964
1986
|
"ACTION (SCROLLING_TO_BOTTOM): Executing scroll."
|
|
@@ -1988,84 +2010,51 @@ function createProxyHandler<T>(
|
|
|
1988
2010
|
return () => clearTimeout(timeoutId);
|
|
1989
2011
|
}
|
|
1990
2012
|
|
|
1991
|
-
// If status is IDLE_NOT_AT_BOTTOM or LOCKED_AT_BOTTOM, we do nothing here.
|
|
1992
|
-
// The scroll has either finished or been disabled by the user.
|
|
1993
|
-
|
|
1994
2013
|
return () => {
|
|
1995
2014
|
if (intervalId) clearInterval(intervalId);
|
|
1996
2015
|
};
|
|
1997
2016
|
}, [status, totalCount, positions]);
|
|
1998
2017
|
|
|
1999
|
-
// --- 3. USER INTERACTION & RANGE UPDATER ---
|
|
2000
2018
|
useEffect(() => {
|
|
2001
2019
|
const container = containerRef.current;
|
|
2002
2020
|
if (!container) return;
|
|
2003
2021
|
|
|
2004
|
-
|
|
2022
|
+
const scrollThreshold = itemHeight;
|
|
2005
2023
|
|
|
2006
2024
|
const handleUserScroll = () => {
|
|
2007
|
-
// Essential check to ignore our own programmatic scrolls.
|
|
2008
2025
|
if (isProgrammaticScroll.current) {
|
|
2009
2026
|
return;
|
|
2010
2027
|
}
|
|
2011
2028
|
|
|
2012
|
-
const
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
container.clientHeight <
|
|
2019
|
-
1;
|
|
2020
|
-
|
|
2021
|
-
if (!isAtBottom) {
|
|
2022
|
-
if (status !== "IDLE_NOT_AT_BOTTOM") {
|
|
2023
|
-
setStatus("IDLE_NOT_AT_BOTTOM");
|
|
2024
|
-
}
|
|
2025
|
-
} else {
|
|
2026
|
-
if (status === "IDLE_NOT_AT_BOTTOM") {
|
|
2027
|
-
setStatus("LOCKED_AT_BOTTOM");
|
|
2028
|
-
}
|
|
2029
|
+
const scrollTop = container.scrollTop;
|
|
2030
|
+
if (
|
|
2031
|
+
Math.abs(scrollTop - lastUpdateAtScrollTop.current) <
|
|
2032
|
+
scrollThreshold
|
|
2033
|
+
) {
|
|
2034
|
+
return;
|
|
2029
2035
|
}
|
|
2030
|
-
// NOTE: We've removed `shouldNotScroll` as its purpose is now better
|
|
2031
|
-
// served by the state machine itself. The state `IDLE_NOT_AT_BOTTOM`
|
|
2032
|
-
// already tells us the user has scrolled up.
|
|
2033
2036
|
|
|
2034
|
-
|
|
2037
|
+
console.log(
|
|
2038
|
+
`Threshold passed at ${scrollTop}px. Recalculating range...`
|
|
2039
|
+
);
|
|
2035
2040
|
|
|
2036
|
-
//
|
|
2037
|
-
|
|
2041
|
+
// NOW we do the expensive work.
|
|
2042
|
+
const { clientHeight } = container;
|
|
2038
2043
|
let high = totalCount - 1;
|
|
2039
2044
|
let low = 0;
|
|
2040
|
-
let
|
|
2045
|
+
let topItemIndex = 0;
|
|
2041
2046
|
while (low <= high) {
|
|
2042
2047
|
const mid = Math.floor((low + high) / 2);
|
|
2043
2048
|
if (positions[mid]! < scrollTop) {
|
|
2044
|
-
|
|
2049
|
+
topItemIndex = mid;
|
|
2045
2050
|
low = mid + 1;
|
|
2046
2051
|
} else {
|
|
2047
2052
|
high = mid - 1;
|
|
2048
2053
|
}
|
|
2049
2054
|
}
|
|
2050
2055
|
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
const potentialStartIndex = Math.max(
|
|
2054
|
-
0,
|
|
2055
|
-
potentialTopIndex - overscan
|
|
2056
|
-
);
|
|
2057
|
-
if (potentialStartIndex === range.startIndex) {
|
|
2058
|
-
// The visible items haven't changed, so we do nothing.
|
|
2059
|
-
// This is the core of the optimization.
|
|
2060
|
-
return;
|
|
2061
|
-
}
|
|
2062
|
-
|
|
2063
|
-
// --- Part 3: If we're here, we MUST update the range ---
|
|
2064
|
-
console.log(
|
|
2065
|
-
`Index changed from ${range.startIndex} to ${potentialStartIndex}. Updating range.`
|
|
2066
|
-
);
|
|
2067
|
-
|
|
2068
|
-
let endIndex = potentialStartIndex;
|
|
2056
|
+
const startIndex = Math.max(0, topItemIndex - overscan);
|
|
2057
|
+
let endIndex = startIndex;
|
|
2069
2058
|
const visibleEnd = scrollTop + clientHeight;
|
|
2070
2059
|
while (
|
|
2071
2060
|
endIndex < totalCount &&
|
|
@@ -2075,9 +2064,12 @@ function createProxyHandler<T>(
|
|
|
2075
2064
|
}
|
|
2076
2065
|
|
|
2077
2066
|
setRange({
|
|
2078
|
-
startIndex
|
|
2067
|
+
startIndex,
|
|
2079
2068
|
endIndex: Math.min(totalCount, endIndex + overscan),
|
|
2080
2069
|
});
|
|
2070
|
+
|
|
2071
|
+
// Finally, we record that we did the work at THIS scroll position.
|
|
2072
|
+
lastUpdateAtScrollTop.current = scrollTop;
|
|
2081
2073
|
};
|
|
2082
2074
|
|
|
2083
2075
|
container.addEventListener("scroll", handleUserScroll, {
|
|
@@ -2085,7 +2077,7 @@ function createProxyHandler<T>(
|
|
|
2085
2077
|
});
|
|
2086
2078
|
return () =>
|
|
2087
2079
|
container.removeEventListener("scroll", handleUserScroll);
|
|
2088
|
-
}, [totalCount, positions,
|
|
2080
|
+
}, [totalCount, positions, itemHeight, overscan, status]);
|
|
2089
2081
|
|
|
2090
2082
|
const scrollToBottom = useCallback(() => {
|
|
2091
2083
|
console.log(
|