cogsbox-state 0.5.301 → 0.5.302

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.301",
3
+ "version": "0.5.302",
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
@@ -1803,7 +1803,7 @@ function createProxyHandler<T>(
1803
1803
  options: VirtualViewOptions
1804
1804
  ): VirtualStateObjectResult<any[]> => {
1805
1805
  const {
1806
- itemHeight = 50, // Default/estimated height
1806
+ itemHeight = 50,
1807
1807
  overscan = 5,
1808
1808
  stickToBottom = false,
1809
1809
  } = options;
@@ -1820,6 +1820,11 @@ function createProxyHandler<T>(
1820
1820
  []
1821
1821
  );
1822
1822
 
1823
+ // Track scroll position
1824
+ const isAtBottomRef = useRef(stickToBottom);
1825
+ const previousTotalCountRef = useRef(0);
1826
+ const isInitialMountRef = useRef(true);
1827
+
1823
1828
  useEffect(() => {
1824
1829
  const unsubscribe = getGlobalStore
1825
1830
  .getState()
@@ -1831,10 +1836,6 @@ function createProxyHandler<T>(
1831
1836
  };
1832
1837
  }, [stateKey, forceRecalculate]);
1833
1838
 
1834
- const isAtBottomRef = useRef(stickToBottom);
1835
- const isInitialMountRef = useRef(true);
1836
- const previousTotalCountRef = useRef(0);
1837
-
1838
1839
  const sourceArray = getGlobalStore().getNestedState(
1839
1840
  stateKey,
1840
1841
  path
@@ -1868,46 +1869,46 @@ function createProxyHandler<T>(
1868
1869
  ...meta,
1869
1870
  validIndices,
1870
1871
  });
1871
- }, [range.startIndex, range.endIndex, sourceArray]);
1872
+ }, [range.startIndex, range.endIndex, sourceArray, totalCount]);
1872
1873
 
1873
1874
  useLayoutEffect(() => {
1874
1875
  const container = containerRef.current;
1875
1876
  if (!container) return;
1876
1877
 
1878
+ const wasAtBottom = isAtBottomRef.current;
1877
1879
  const listGrew = totalCount > previousTotalCountRef.current;
1878
1880
  previousTotalCountRef.current = totalCount;
1879
1881
 
1880
- const wasAtBottom = isAtBottomRef.current;
1881
-
1882
1882
  const handleScroll = () => {
1883
1883
  const { scrollTop, clientHeight, scrollHeight } = container;
1884
+ // Consider "at bottom" if within 10px
1884
1885
  isAtBottomRef.current =
1885
- scrollHeight - scrollTop - clientHeight < 5;
1886
-
1887
- let search = (list: number[], value: number) => {
1888
- let low = 0,
1889
- high = list.length - 1;
1890
- while (low <= high) {
1891
- const mid = Math.floor((low + high) / 2);
1892
- const midValue = list[mid]!;
1893
- if (midValue < value) {
1894
- low = mid + 1;
1895
- } else {
1896
- high = mid - 1;
1897
- }
1886
+ scrollHeight - scrollTop - clientHeight < 10;
1887
+
1888
+ // Binary search for start index
1889
+ let low = 0,
1890
+ high = totalCount - 1;
1891
+ while (low <= high) {
1892
+ const mid = Math.floor((low + high) / 2);
1893
+ if (positions[mid]! < scrollTop) {
1894
+ low = mid + 1;
1895
+ } else {
1896
+ high = mid - 1;
1898
1897
  }
1899
- return low;
1900
- };
1901
- let startIndex = search(positions, scrollTop);
1898
+ }
1899
+ const startIndex = Math.max(0, high - overscan);
1900
+
1901
+ // Find end index
1902
1902
  let endIndex = startIndex;
1903
+ const visibleEnd = scrollTop + clientHeight;
1903
1904
  while (
1904
1905
  endIndex < totalCount &&
1905
- positions[endIndex]! < scrollTop + clientHeight
1906
+ positions[endIndex]! < visibleEnd
1906
1907
  ) {
1907
1908
  endIndex++;
1908
1909
  }
1909
- startIndex = Math.max(0, startIndex - overscan);
1910
1910
  endIndex = Math.min(totalCount, endIndex + overscan);
1911
+
1911
1912
  setRange((prevRange) => {
1912
1913
  if (
1913
1914
  prevRange.startIndex !== startIndex ||
@@ -1922,29 +1923,35 @@ function createProxyHandler<T>(
1922
1923
  container.addEventListener("scroll", handleScroll, {
1923
1924
  passive: true,
1924
1925
  });
1925
- handleScroll();
1926
1926
 
1927
+ // Handle stick to bottom
1927
1928
  if (stickToBottom) {
1928
1929
  if (isInitialMountRef.current) {
1930
+ // First render - go to bottom instantly
1929
1931
  container.scrollTo({
1930
1932
  top: container.scrollHeight,
1931
1933
  behavior: "auto",
1932
1934
  });
1933
- } else if (listGrew && wasAtBottom) {
1934
- container.scrollTo({
1935
- top: container.scrollHeight,
1936
- behavior: "auto",
1935
+ } else if (wasAtBottom && listGrew) {
1936
+ // New items added and we were at bottom - stay at bottom
1937
+ requestAnimationFrame(() => {
1938
+ container.scrollTo({
1939
+ top: container.scrollHeight,
1940
+ behavior: "smooth",
1941
+ });
1937
1942
  });
1938
1943
  }
1939
1944
  }
1940
1945
 
1941
- if (totalCount > 0) {
1942
- isInitialMountRef.current = false;
1943
- }
1946
+ // Mark as no longer initial mount after first render
1947
+ isInitialMountRef.current = false;
1948
+
1949
+ // Run handleScroll once to set initial range
1950
+ handleScroll();
1944
1951
 
1945
1952
  return () =>
1946
1953
  container.removeEventListener("scroll", handleScroll);
1947
- }, [totalCount, overscan, stickToBottom, positions]);
1954
+ }, [totalCount, positions, overscan, stickToBottom]);
1948
1955
 
1949
1956
  const scrollToBottom = useCallback(
1950
1957
  (behavior: ScrollBehavior = "smooth") => {
@@ -1960,9 +1967,9 @@ function createProxyHandler<T>(
1960
1967
 
1961
1968
  const scrollToIndex = useCallback(
1962
1969
  (index: number, behavior: ScrollBehavior = "smooth") => {
1963
- if (containerRef.current) {
1970
+ if (containerRef.current && positions[index] !== undefined) {
1964
1971
  containerRef.current.scrollTo({
1965
- top: positions[index] || 0,
1972
+ top: positions[index],
1966
1973
  behavior,
1967
1974
  });
1968
1975
  }
@@ -1973,10 +1980,13 @@ function createProxyHandler<T>(
1973
1980
  const virtualizerProps = {
1974
1981
  outer: {
1975
1982
  ref: containerRef,
1976
- style: { overflowY: "auto", height: "100%" },
1983
+ style: { overflowY: "auto" as const, height: "100%" },
1977
1984
  },
1978
1985
  inner: {
1979
- style: { height: `${totalHeight}px`, position: "relative" },
1986
+ style: {
1987
+ height: `${totalHeight}px`,
1988
+ position: "relative" as const,
1989
+ },
1980
1990
  },
1981
1991
  list: {
1982
1992
  style: {
@@ -1987,7 +1997,7 @@ function createProxyHandler<T>(
1987
1997
 
1988
1998
  return {
1989
1999
  virtualState,
1990
- virtualizerProps: virtualizerProps as any,
2000
+ virtualizerProps,
1991
2001
  scrollToBottom,
1992
2002
  scrollToIndex,
1993
2003
  };