cogsbox-state 0.5.302 → 0.5.303

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.302",
3
+ "version": "0.5.303",
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
@@ -1824,6 +1824,7 @@ function createProxyHandler<T>(
1824
1824
  const isAtBottomRef = useRef(stickToBottom);
1825
1825
  const previousTotalCountRef = useRef(0);
1826
1826
  const isInitialMountRef = useRef(true);
1827
+ const hasScrolledToBottomRef = useRef(false); // Track if we've done initial scroll
1827
1828
 
1828
1829
  useEffect(() => {
1829
1830
  const unsubscribe = getGlobalStore
@@ -1842,20 +1843,32 @@ function createProxyHandler<T>(
1842
1843
  ) as any[];
1843
1844
  const totalCount = sourceArray.length;
1844
1845
 
1845
- const { totalHeight, positions } = useMemo(() => {
1846
- const shadowArray =
1847
- getGlobalStore.getState().getShadowMetadata(stateKey, path) ||
1848
- [];
1849
- let height = 0;
1850
- const pos: number[] = [];
1851
- for (let i = 0; i < totalCount; i++) {
1852
- pos[i] = height;
1853
- const measuredHeight =
1854
- shadowArray[i]?.virtualizer?.itemHeight;
1855
- height += measuredHeight || itemHeight;
1856
- }
1857
- return { totalHeight: height, positions: pos };
1858
- }, [totalCount, stateKey, path, itemHeight, heightsVersion]);
1846
+ const { totalHeight, positions, allItemsMeasured } =
1847
+ useMemo(() => {
1848
+ const shadowArray =
1849
+ getGlobalStore
1850
+ .getState()
1851
+ .getShadowMetadata(stateKey, path) || [];
1852
+ let height = 0;
1853
+ const pos: number[] = [];
1854
+ let measured = true;
1855
+
1856
+ for (let i = 0; i < totalCount; i++) {
1857
+ pos[i] = height;
1858
+ const measuredHeight =
1859
+ shadowArray[i]?.virtualizer?.itemHeight;
1860
+ if (!measuredHeight && totalCount > 0) {
1861
+ measured = false;
1862
+ }
1863
+ height += measuredHeight || itemHeight;
1864
+ }
1865
+
1866
+ return {
1867
+ totalHeight: height,
1868
+ positions: pos,
1869
+ allItemsMeasured: measured,
1870
+ };
1871
+ }, [totalCount, stateKey, path, itemHeight, heightsVersion]);
1859
1872
 
1860
1873
  const virtualState = useMemo(() => {
1861
1874
  const start = Math.max(0, range.startIndex);
@@ -1926,13 +1939,37 @@ function createProxyHandler<T>(
1926
1939
 
1927
1940
  // Handle stick to bottom
1928
1941
  if (stickToBottom) {
1929
- if (isInitialMountRef.current) {
1930
- // First render - go to bottom instantly
1931
- container.scrollTo({
1932
- top: container.scrollHeight,
1933
- behavior: "auto",
1934
- });
1935
- } else if (wasAtBottom && listGrew) {
1942
+ if (
1943
+ isInitialMountRef.current &&
1944
+ !hasScrolledToBottomRef.current
1945
+ ) {
1946
+ // For initial mount, wait for items to be measured
1947
+ if (allItemsMeasured && totalCount > 0) {
1948
+ container.scrollTo({
1949
+ top: container.scrollHeight,
1950
+ behavior: "auto",
1951
+ });
1952
+ hasScrolledToBottomRef.current = true;
1953
+ isInitialMountRef.current = false;
1954
+ } else if (totalCount > 0) {
1955
+ // If not all measured yet, try again soon
1956
+ const retryTimer = setTimeout(() => {
1957
+ if (containerRef.current && isInitialMountRef.current) {
1958
+ containerRef.current.scrollTo({
1959
+ top: containerRef.current.scrollHeight,
1960
+ behavior: "auto",
1961
+ });
1962
+ hasScrolledToBottomRef.current = true;
1963
+ isInitialMountRef.current = false;
1964
+ }
1965
+ }, 100);
1966
+ return () => clearTimeout(retryTimer);
1967
+ }
1968
+ } else if (
1969
+ !isInitialMountRef.current &&
1970
+ wasAtBottom &&
1971
+ listGrew
1972
+ ) {
1936
1973
  // New items added and we were at bottom - stay at bottom
1937
1974
  requestAnimationFrame(() => {
1938
1975
  container.scrollTo({
@@ -1941,17 +1978,22 @@ function createProxyHandler<T>(
1941
1978
  });
1942
1979
  });
1943
1980
  }
1981
+ } else {
1982
+ isInitialMountRef.current = false;
1944
1983
  }
1945
1984
 
1946
- // Mark as no longer initial mount after first render
1947
- isInitialMountRef.current = false;
1948
-
1949
1985
  // Run handleScroll once to set initial range
1950
1986
  handleScroll();
1951
1987
 
1952
1988
  return () =>
1953
1989
  container.removeEventListener("scroll", handleScroll);
1954
- }, [totalCount, positions, overscan, stickToBottom]);
1990
+ }, [
1991
+ totalCount,
1992
+ positions,
1993
+ overscan,
1994
+ stickToBottom,
1995
+ allItemsMeasured,
1996
+ ]);
1955
1997
 
1956
1998
  const scrollToBottom = useCallback(
1957
1999
  (behavior: ScrollBehavior = "smooth") => {