cogsbox-state 0.5.376 → 0.5.377

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.376",
3
+ "version": "0.5.377",
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
@@ -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 lastScrollTopRef = useRef(0);
1831
-
1830
+ const lastUpdateAtScrollTop = useRef(0);
1832
1831
  const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
1833
1832
 
1834
1833
  useEffect(() => {
@@ -2001,71 +2000,50 @@ function createProxyHandler<T>(
2001
2000
  const container = containerRef.current;
2002
2001
  if (!container) return;
2003
2002
 
2004
- // We no longer need a manual threshold. The `positions` array is our threshold.
2003
+ // The scroll distance threshold. One item's height is a great default.
2004
+ const scrollThreshold = itemHeight;
2005
2005
 
2006
2006
  const handleUserScroll = () => {
2007
- // Essential check to ignore our own programmatic scrolls.
2007
+ // Essential guard for our own programmatic scrolls.
2008
2008
  if (isProgrammaticScroll.current) {
2009
2009
  return;
2010
2010
  }
2011
2011
 
2012
- const { scrollTop, clientHeight } = container;
2013
-
2014
- // --- Part 1: Quick state update logic (still important) ---
2015
- const isAtBottom =
2016
- container.scrollHeight -
2017
- scrollTop -
2018
- container.clientHeight <
2019
- 1;
2012
+ const scrollTop = container.scrollTop;
2020
2013
 
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
- }
2014
+ // --- THE CORE LOGIC YOU REQUESTED ---
2015
+ // Is the user just wiggling the scrollbar? If so, exit.
2016
+ // This is a very cheap check that runs on every scroll event.
2017
+ if (
2018
+ Math.abs(scrollTop - lastUpdateAtScrollTop.current) <
2019
+ scrollThreshold
2020
+ ) {
2021
+ return;
2029
2022
  }
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
2023
 
2034
- // --- Part 2: Index-Based Invalidation (The real optimization) ---
2024
+ // --- IF WE ARE HERE, WE HAVE SCROLLED A "DECENT AMOUNT" ---
2025
+
2026
+ console.log(
2027
+ `Threshold passed at ${scrollTop}px. Recalculating range...`
2028
+ );
2035
2029
 
2036
- // Find the index of the item at the top of the viewport.
2037
- // This binary search is extremely fast.
2030
+ // NOW we do the expensive work.
2031
+ const { clientHeight } = container;
2038
2032
  let high = totalCount - 1;
2039
2033
  let low = 0;
2040
- let potentialTopIndex = 0;
2034
+ let topItemIndex = 0;
2041
2035
  while (low <= high) {
2042
2036
  const mid = Math.floor((low + high) / 2);
2043
2037
  if (positions[mid]! < scrollTop) {
2044
- potentialTopIndex = mid;
2038
+ topItemIndex = mid;
2045
2039
  low = mid + 1;
2046
2040
  } else {
2047
2041
  high = mid - 1;
2048
2042
  }
2049
2043
  }
2050
2044
 
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
- }
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;
2045
+ const startIndex = Math.max(0, topItemIndex - overscan);
2046
+ let endIndex = startIndex;
2069
2047
  const visibleEnd = scrollTop + clientHeight;
2070
2048
  while (
2071
2049
  endIndex < totalCount &&
@@ -2075,9 +2053,12 @@ function createProxyHandler<T>(
2075
2053
  }
2076
2054
 
2077
2055
  setRange({
2078
- startIndex: potentialStartIndex,
2056
+ startIndex,
2079
2057
  endIndex: Math.min(totalCount, endIndex + overscan),
2080
2058
  });
2059
+
2060
+ // Finally, we record that we did the work at THIS scroll position.
2061
+ lastUpdateAtScrollTop.current = scrollTop;
2081
2062
  };
2082
2063
 
2083
2064
  container.addEventListener("scroll", handleUserScroll, {
@@ -2085,7 +2066,7 @@ function createProxyHandler<T>(
2085
2066
  });
2086
2067
  return () =>
2087
2068
  container.removeEventListener("scroll", handleUserScroll);
2088
- }, [totalCount, positions, status, range.startIndex]); // Add range.startIndex!
2069
+ }, [totalCount, positions, itemHeight, overscan, status]);
2089
2070
 
2090
2071
  const scrollToBottom = useCallback(() => {
2091
2072
  console.log(