cogsbox-state 0.5.385 → 0.5.387

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.385",
3
+ "version": "0.5.387",
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
@@ -1829,7 +1829,10 @@ function createProxyHandler<T>(
1829
1829
  const prevDepsRef = useRef(dependencies);
1830
1830
  const lastUpdateAtScrollTop = useRef(0);
1831
1831
  const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
1832
-
1832
+ const scrollAnchorRef = useRef<{
1833
+ top: number;
1834
+ height: number;
1835
+ } | null>(null);
1833
1836
  useEffect(() => {
1834
1837
  const unsubscribe = getGlobalStore
1835
1838
  .getState()
@@ -1884,27 +1887,46 @@ function createProxyHandler<T>(
1884
1887
  // --- 1. STATE CONTROLLER ---
1885
1888
  // This effect decides which state to transition TO.
1886
1889
  useLayoutEffect(() => {
1887
- const depsChanged = !isDeepEqual(
1888
- dependencies,
1889
- prevDepsRef.current
1890
- );
1890
+ // THIS `IF` BLOCK IS THE NEW LOGIC
1891
+ const container = containerRef.current;
1891
1892
  const hasNewItems = totalCount > prevTotalCountRef.current;
1892
1893
 
1893
- if (depsChanged) {
1894
- console.log("TRANSITION: Deps changed -> IDLE_AT_TOP");
1895
- setStatus("IDLE_AT_TOP");
1896
- return; // Stop here, let the next effect handle the action for the new state.
1897
- }
1894
+ if (hasNewItems && scrollAnchorRef.current && container) {
1895
+ // User was scrolled up, new items arrived. Restore the scroll position.
1896
+ const { top: prevScrollTop, height: prevScrollHeight } =
1897
+ scrollAnchorRef.current;
1898
+ container.scrollTop =
1899
+ prevScrollTop + (container.scrollHeight - prevScrollHeight);
1898
1900
 
1899
- if (
1900
- hasNewItems &&
1901
- status === "LOCKED_AT_BOTTOM" &&
1902
- stickToBottom
1903
- ) {
1901
+ // IMPORTANT: Clear the anchor after using it.
1902
+ scrollAnchorRef.current = null;
1904
1903
  console.log(
1905
- "TRANSITION: New items arrived while locked -> GETTING_HEIGHTS"
1904
+ `ANCHOR RESTORED to scrollTop: ${container.scrollTop}`
1906
1905
  );
1907
- setStatus("GETTING_HEIGHTS");
1906
+ }
1907
+ // YOUR EXISTING LOGIC CONTINUES BELOW in an else-if structure
1908
+ else {
1909
+ const depsChanged = !isDeepEqual(
1910
+ dependencies,
1911
+ prevDepsRef.current
1912
+ );
1913
+
1914
+ if (depsChanged) {
1915
+ console.log("TRANSITION: Deps changed -> IDLE_AT_TOP");
1916
+ setStatus("IDLE_AT_TOP");
1917
+ return; // Stop here, let the next effect handle the action for the new state.
1918
+ }
1919
+
1920
+ if (
1921
+ hasNewItems &&
1922
+ status === "LOCKED_AT_BOTTOM" &&
1923
+ stickToBottom
1924
+ ) {
1925
+ console.log(
1926
+ "TRANSITION: New items arrived while locked -> GETTING_HEIGHTS"
1927
+ );
1928
+ setStatus("GETTING_HEIGHTS");
1929
+ }
1908
1930
  }
1909
1931
 
1910
1932
  prevTotalCountRef.current = totalCount;
@@ -2004,28 +2026,25 @@ function createProxyHandler<T>(
2004
2026
  }
2005
2027
 
2006
2028
  const { scrollTop, scrollHeight, clientHeight } = container;
2007
- console.log(
2008
- "scrollTop, scrollHeight, clientHeight ",
2009
- scrollTop,
2010
- scrollHeight,
2011
- clientHeight
2012
- );
2013
- // --- START OF MINIMAL FIX ---
2014
- // This block is the only thing added. It updates the master 'status'.
2015
- // This is the critical missing piece that tells the component if it should auto-scroll.
2016
- // A 10px buffer prevents jittering when you are scrolled to the very bottom.
2029
+
2017
2030
  const isAtBottom =
2018
2031
  scrollHeight - scrollTop - clientHeight < 10;
2019
- console.log("isAtBottom", isAtBottom);
2032
+
2020
2033
  if (isAtBottom) {
2021
- // If we scroll back to the bottom, re-lock.
2022
2034
  if (status !== "LOCKED_AT_BOTTOM") {
2023
2035
  setStatus("LOCKED_AT_BOTTOM");
2036
+ // We are at the bottom, so we don't need an anchor.
2037
+ scrollAnchorRef.current = null;
2024
2038
  }
2025
2039
  } else {
2026
- // If we have scrolled up, unlock from the bottom.
2027
2040
  if (status !== "IDLE_NOT_AT_BOTTOM") {
2028
2041
  setStatus("IDLE_NOT_AT_BOTTOM");
2042
+ // THE USER SCROLLED UP. SET THE ANCHOR.
2043
+ scrollAnchorRef.current = {
2044
+ top: scrollTop,
2045
+ height: scrollHeight,
2046
+ };
2047
+ console.log(`ANCHOR SET at scrollTop: ${scrollTop}`);
2029
2048
  }
2030
2049
  }
2031
2050
  // --- END OF MINIMAL FIX ---