cogsbox-state 0.5.319 → 0.5.320

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.319",
3
+ "version": "0.5.320",
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
@@ -1809,16 +1809,22 @@ function createProxyHandler<T>(
1809
1809
  } = options;
1810
1810
 
1811
1811
  const containerRef = useRef<HTMLDivElement | null>(null);
1812
+
1813
+ const sourceArray = getGlobalStore().getNestedState(
1814
+ stateKey,
1815
+ path
1816
+ ) as any[];
1817
+ const totalCount = sourceArray.length;
1818
+
1819
+ // Start at top, will adjust once container is measured
1812
1820
  const [range, setRange] = useState({
1813
1821
  startIndex: 0,
1814
1822
  endIndex: 10,
1815
1823
  });
1816
1824
 
1817
- // This ref tracks if the user is locked to the bottom.
1818
1825
  const isLockedToBottomRef = useRef(stickToBottom);
1819
-
1820
- // This state triggers a re-render when item heights change.
1821
1826
  const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
1827
+ const hasInitializedRef = useRef(false);
1822
1828
 
1823
1829
  useEffect(() => {
1824
1830
  const unsubscribe = getGlobalStore
@@ -1829,13 +1835,6 @@ function createProxyHandler<T>(
1829
1835
  return unsubscribe;
1830
1836
  }, [stateKey]);
1831
1837
 
1832
- const sourceArray = getGlobalStore().getNestedState(
1833
- stateKey,
1834
- path
1835
- ) as any[];
1836
- const totalCount = sourceArray.length;
1837
-
1838
- // Calculate heights from shadow state. This runs when data or measurements change.
1839
1838
  const { totalHeight, positions } = useMemo(() => {
1840
1839
  const shadowArray =
1841
1840
  getGlobalStore.getState().getShadowMetadata(stateKey, path) ||
@@ -1857,7 +1856,6 @@ function createProxyHandler<T>(
1857
1856
  shadowUpdateTrigger,
1858
1857
  ]);
1859
1858
 
1860
- // Memoize the virtualized slice of data.
1861
1859
  const virtualState = useMemo(() => {
1862
1860
  const start = Math.max(0, range.startIndex);
1863
1861
  const end = Math.min(totalCount, range.endIndex);
@@ -1872,14 +1870,10 @@ function createProxyHandler<T>(
1872
1870
  });
1873
1871
  }, [range.startIndex, range.endIndex, sourceArray, totalCount]);
1874
1872
 
1875
- // This is the main effect that handles all scrolling and updates.
1876
1873
  useLayoutEffect(() => {
1877
1874
  const container = containerRef.current;
1878
1875
  if (!container) return;
1879
1876
 
1880
- let scrollTimeoutId: NodeJS.Timeout;
1881
-
1882
- // This function determines what's visible in the viewport.
1883
1877
  const updateVirtualRange = () => {
1884
1878
  if (!container) return;
1885
1879
  const { scrollTop } = container;
@@ -1903,7 +1897,6 @@ function createProxyHandler<T>(
1903
1897
  setRange({ startIndex, endIndex });
1904
1898
  };
1905
1899
 
1906
- // This function handles ONLY user-initiated scrolls.
1907
1900
  const handleUserScroll = () => {
1908
1901
  isLockedToBottomRef.current =
1909
1902
  container.scrollHeight -
@@ -1917,28 +1910,40 @@ function createProxyHandler<T>(
1917
1910
  passive: true,
1918
1911
  });
1919
1912
 
1920
- // In useLayoutEffect, replace this section:
1921
- if (stickToBottom) {
1922
- scrollTimeoutId = setTimeout(() => {
1923
- if (isLockedToBottomRef.current) {
1924
- container.scrollTo({
1925
- top: container.scrollHeight + 1000, // ADD A BUFFER HERE
1926
- behavior: "auto",
1927
- });
1928
- }
1929
- }, 200);
1913
+ // Initialize at bottom if needed
1914
+ if (
1915
+ stickToBottom &&
1916
+ !hasInitializedRef.current &&
1917
+ totalCount > 0 &&
1918
+ container.clientHeight > 0
1919
+ ) {
1920
+ hasInitializedRef.current = true;
1921
+
1922
+ // Now we have the actual container height, calculate visible items
1923
+ const visibleCount = Math.ceil(
1924
+ container.clientHeight / itemHeight
1925
+ );
1926
+ const startIdx = Math.max(
1927
+ 0,
1928
+ totalCount - visibleCount - overscan
1929
+ );
1930
+
1931
+ // Set range to show bottom items
1932
+ setRange({
1933
+ startIndex: startIdx,
1934
+ endIndex: totalCount,
1935
+ });
1936
+
1937
+ // Scroll to bottom
1938
+ container.scrollTop = container.scrollHeight;
1930
1939
  }
1931
1940
 
1932
- // Update the visible range on initial load.
1933
1941
  updateVirtualRange();
1934
1942
 
1935
- // Cleanup function is vital to prevent memory leaks.
1936
1943
  return () => {
1937
- clearTimeout(scrollTimeoutId);
1938
1944
  container.removeEventListener("scroll", handleUserScroll);
1939
1945
  };
1940
- // This effect re-runs whenever the list size or item heights change.
1941
- }, [totalCount, positions, stickToBottom]);
1946
+ }, [totalCount, positions, stickToBottom, itemHeight, overscan]);
1942
1947
 
1943
1948
  const scrollToBottom = useCallback(
1944
1949
  (behavior: ScrollBehavior = "smooth") => {