cogsbox-state 0.5.375 → 0.5.376
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 +647 -644
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +40 -40
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -2001,19 +2001,17 @@ function createProxyHandler<T>(
|
|
|
2001
2001
|
const container = containerRef.current;
|
|
2002
2002
|
if (!container) return;
|
|
2003
2003
|
|
|
2004
|
-
//
|
|
2005
|
-
// You can adjust this value. Using a full itemHeight also works well.
|
|
2006
|
-
const scrollThreshold = itemHeight / 2;
|
|
2004
|
+
// We no longer need a manual threshold. The `positions` array is our threshold.
|
|
2007
2005
|
|
|
2008
2006
|
const handleUserScroll = () => {
|
|
2009
|
-
//
|
|
2007
|
+
// Essential check to ignore our own programmatic scrolls.
|
|
2010
2008
|
if (isProgrammaticScroll.current) {
|
|
2011
2009
|
return;
|
|
2012
2010
|
}
|
|
2013
2011
|
|
|
2014
|
-
const scrollTop = container
|
|
2012
|
+
const { scrollTop, clientHeight } = container;
|
|
2015
2013
|
|
|
2016
|
-
// --- Part 1:
|
|
2014
|
+
// --- Part 1: Quick state update logic (still important) ---
|
|
2017
2015
|
const isAtBottom =
|
|
2018
2016
|
container.scrollHeight -
|
|
2019
2017
|
scrollTop -
|
|
@@ -2022,51 +2020,52 @@ function createProxyHandler<T>(
|
|
|
2022
2020
|
|
|
2023
2021
|
if (!isAtBottom) {
|
|
2024
2022
|
if (status !== "IDLE_NOT_AT_BOTTOM") {
|
|
2025
|
-
console.log(
|
|
2026
|
-
"USER ACTION: Scrolled up -> IDLE_NOT_AT_BOTTOM"
|
|
2027
|
-
);
|
|
2028
2023
|
setStatus("IDLE_NOT_AT_BOTTOM");
|
|
2029
2024
|
}
|
|
2030
|
-
shouldNotScroll.current = true;
|
|
2031
2025
|
} else {
|
|
2032
2026
|
if (status === "IDLE_NOT_AT_BOTTOM") {
|
|
2033
|
-
console.log(
|
|
2034
|
-
"USER ACTION: Scrolled back to bottom -> LOCKED_AT_BOTTOM"
|
|
2035
|
-
);
|
|
2036
2027
|
setStatus("LOCKED_AT_BOTTOM");
|
|
2037
2028
|
}
|
|
2038
|
-
shouldNotScroll.current = false;
|
|
2039
2029
|
}
|
|
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.
|
|
2040
2033
|
|
|
2041
|
-
// --- Part 2: The
|
|
2034
|
+
// --- Part 2: Index-Based Invalidation (The real optimization) ---
|
|
2042
2035
|
|
|
2043
|
-
//
|
|
2044
|
-
//
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
) {
|
|
2049
|
-
|
|
2050
|
-
|
|
2036
|
+
// Find the index of the item at the top of the viewport.
|
|
2037
|
+
// This binary search is extremely fast.
|
|
2038
|
+
let high = totalCount - 1;
|
|
2039
|
+
let low = 0;
|
|
2040
|
+
let potentialTopIndex = 0;
|
|
2041
|
+
while (low <= high) {
|
|
2042
|
+
const mid = Math.floor((low + high) / 2);
|
|
2043
|
+
if (positions[mid]! < scrollTop) {
|
|
2044
|
+
potentialTopIndex = mid;
|
|
2045
|
+
low = mid + 1;
|
|
2046
|
+
} else {
|
|
2047
|
+
high = mid - 1;
|
|
2048
|
+
}
|
|
2051
2049
|
}
|
|
2052
2050
|
|
|
2053
|
-
//
|
|
2054
|
-
|
|
2051
|
+
// Compare the potential new start index with the current one.
|
|
2052
|
+
// Remember to account for the overscan!
|
|
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
|
+
}
|
|
2055
2062
|
|
|
2056
|
-
|
|
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
|
+
);
|
|
2057
2067
|
|
|
2058
|
-
|
|
2059
|
-
// This code now only runs when it's actually needed.
|
|
2060
|
-
const { clientHeight } = container;
|
|
2061
|
-
let low = 0,
|
|
2062
|
-
high = totalCount - 1;
|
|
2063
|
-
while (low <= high) {
|
|
2064
|
-
const mid = Math.floor((low + high) / 2);
|
|
2065
|
-
if (positions[mid]! < scrollTop) low = mid + 1;
|
|
2066
|
-
else high = mid - 1;
|
|
2067
|
-
}
|
|
2068
|
-
const startIndex = Math.max(0, high - overscan);
|
|
2069
|
-
let endIndex = startIndex;
|
|
2068
|
+
let endIndex = potentialStartIndex;
|
|
2070
2069
|
const visibleEnd = scrollTop + clientHeight;
|
|
2071
2070
|
while (
|
|
2072
2071
|
endIndex < totalCount &&
|
|
@@ -2074,8 +2073,9 @@ function createProxyHandler<T>(
|
|
|
2074
2073
|
) {
|
|
2075
2074
|
endIndex++;
|
|
2076
2075
|
}
|
|
2076
|
+
|
|
2077
2077
|
setRange({
|
|
2078
|
-
startIndex,
|
|
2078
|
+
startIndex: potentialStartIndex,
|
|
2079
2079
|
endIndex: Math.min(totalCount, endIndex + overscan),
|
|
2080
2080
|
});
|
|
2081
2081
|
};
|
|
@@ -2085,7 +2085,7 @@ function createProxyHandler<T>(
|
|
|
2085
2085
|
});
|
|
2086
2086
|
return () =>
|
|
2087
2087
|
container.removeEventListener("scroll", handleUserScroll);
|
|
2088
|
-
}, [totalCount, positions, status,
|
|
2088
|
+
}, [totalCount, positions, status, range.startIndex]); // Add range.startIndex!
|
|
2089
2089
|
|
|
2090
2090
|
const scrollToBottom = useCallback(() => {
|
|
2091
2091
|
console.log(
|