cogsbox-state 0.5.357 → 0.5.358
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/dist/CogsState.jsx +551 -556
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +100 -87
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1798,7 +1798,6 @@ function createProxyHandler<T>(
|
|
|
1798
1798
|
return selectedIndex ?? -1;
|
|
1799
1799
|
};
|
|
1800
1800
|
}
|
|
1801
|
-
|
|
1802
1801
|
if (prop === "useVirtualView") {
|
|
1803
1802
|
return (
|
|
1804
1803
|
options: VirtualViewOptions
|
|
@@ -1816,7 +1815,9 @@ function createProxyHandler<T>(
|
|
|
1816
1815
|
endIndex: 10,
|
|
1817
1816
|
});
|
|
1818
1817
|
const isLockedToBottomRef = useRef(stickToBottom);
|
|
1819
|
-
|
|
1818
|
+
|
|
1819
|
+
// This ref will hold the ID of our loop so we can stop it.
|
|
1820
|
+
const scrollLoopId = useRef<NodeJS.Timeout | null>(null);
|
|
1820
1821
|
|
|
1821
1822
|
const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
|
|
1822
1823
|
|
|
@@ -1857,6 +1858,7 @@ function createProxyHandler<T>(
|
|
|
1857
1858
|
]);
|
|
1858
1859
|
|
|
1859
1860
|
const virtualState = useMemo(() => {
|
|
1861
|
+
// ... same as before ...
|
|
1860
1862
|
const start = Math.max(0, range.startIndex);
|
|
1861
1863
|
const end = Math.min(totalCount, range.endIndex);
|
|
1862
1864
|
const validIndices = Array.from(
|
|
@@ -1870,118 +1872,129 @@ function createProxyHandler<T>(
|
|
|
1870
1872
|
});
|
|
1871
1873
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1872
1874
|
|
|
1873
|
-
//
|
|
1875
|
+
// --- EFFECT #1: THE SCROLL ALGORITHM ---
|
|
1876
|
+
// This runs when the data or dependencies change.
|
|
1874
1877
|
useLayoutEffect(() => {
|
|
1875
1878
|
const container = containerRef.current;
|
|
1876
|
-
if (
|
|
1879
|
+
if (
|
|
1880
|
+
!container ||
|
|
1881
|
+
!stickToBottom ||
|
|
1882
|
+
!isLockedToBottomRef.current
|
|
1883
|
+
) {
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1877
1886
|
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1887
|
+
// If a loop is already running, stop it before starting a new one.
|
|
1888
|
+
if (scrollLoopId.current) {
|
|
1889
|
+
clearInterval(scrollLoopId.current);
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
console.log("ALGORITHM: Starting...");
|
|
1893
|
+
|
|
1894
|
+
// STEP 1: Set range to render the last item.
|
|
1895
|
+
setRange({
|
|
1896
|
+
startIndex: Math.max(0, totalCount - 10 - overscan),
|
|
1897
|
+
endIndex: totalCount,
|
|
1898
|
+
});
|
|
1899
|
+
|
|
1900
|
+
// STEP 2: Start the LOOP.
|
|
1901
|
+
console.log(
|
|
1902
|
+
"ALGORITHM: Starting LOOP to wait for measurement."
|
|
1903
|
+
);
|
|
1904
|
+
let loopCount = 0;
|
|
1905
|
+
scrollLoopId.current = setInterval(() => {
|
|
1906
|
+
loopCount++;
|
|
1907
|
+
console.log(`LOOP ${loopCount}: Checking last item...`);
|
|
1908
|
+
|
|
1909
|
+
const lastItemIndex = totalCount - 1;
|
|
1910
|
+
if (lastItemIndex < 0) {
|
|
1911
|
+
clearInterval(scrollLoopId.current!);
|
|
1912
|
+
return;
|
|
1891
1913
|
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1914
|
+
|
|
1915
|
+
const shadowArray =
|
|
1916
|
+
getGlobalStore
|
|
1917
|
+
.getState()
|
|
1918
|
+
.getShadowMetadata(stateKey, path) || [];
|
|
1919
|
+
const lastItemHeight =
|
|
1920
|
+
shadowArray[lastItemIndex]?.virtualizer?.itemHeight || 0;
|
|
1921
|
+
|
|
1922
|
+
if (lastItemHeight > 0) {
|
|
1923
|
+
console.log(
|
|
1924
|
+
`%cSUCCESS: Last item is measured. Scrolling.`,
|
|
1925
|
+
"color: green; font-weight: bold;"
|
|
1926
|
+
);
|
|
1927
|
+
clearInterval(scrollLoopId.current!);
|
|
1928
|
+
scrollLoopId.current = null;
|
|
1929
|
+
|
|
1930
|
+
container.scrollTo({
|
|
1931
|
+
top: container.scrollHeight,
|
|
1932
|
+
behavior: "smooth",
|
|
1933
|
+
});
|
|
1934
|
+
} else {
|
|
1935
|
+
console.log("...WAITING. Height is not ready.");
|
|
1936
|
+
if (loopCount > 30) {
|
|
1937
|
+
console.error("LOOP TIMEOUT. Stopping.");
|
|
1938
|
+
clearInterval(scrollLoopId.current!);
|
|
1939
|
+
scrollLoopId.current = null;
|
|
1940
|
+
}
|
|
1900
1941
|
}
|
|
1901
|
-
|
|
1902
|
-
startIndex,
|
|
1903
|
-
endIndex: Math.min(totalCount, endIndex + overscan),
|
|
1904
|
-
});
|
|
1905
|
-
};
|
|
1942
|
+
}, 100);
|
|
1906
1943
|
|
|
1907
|
-
|
|
1944
|
+
return () => {
|
|
1945
|
+
if (scrollLoopId.current) {
|
|
1946
|
+
clearInterval(scrollLoopId.current);
|
|
1947
|
+
}
|
|
1948
|
+
};
|
|
1949
|
+
}, [totalCount, ...dependencies]);
|
|
1908
1950
|
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1951
|
+
// --- EFFECT #2: USER INTERACTION & RESET ---
|
|
1952
|
+
// This handles manual scrolling and resetting when the chat changes.
|
|
1953
|
+
useEffect(() => {
|
|
1954
|
+
const container = containerRef.current;
|
|
1955
|
+
if (!container) return;
|
|
1912
1956
|
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
endIndex: totalCount,
|
|
1920
|
-
});
|
|
1957
|
+
// When the chat changes (dependencies change), reset the lock.
|
|
1958
|
+
console.log(
|
|
1959
|
+
"DEPENDENCY CHANGE: Resetting scroll lock to:",
|
|
1960
|
+
stickToBottom
|
|
1961
|
+
);
|
|
1962
|
+
isLockedToBottomRef.current = stickToBottom;
|
|
1921
1963
|
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
const lastItemIndex = totalCount - 1;
|
|
1926
|
-
const shadowArray =
|
|
1927
|
-
getGlobalStore
|
|
1928
|
-
.getState()
|
|
1929
|
-
.getShadowMetadata(stateKey, path) || [];
|
|
1930
|
-
const lastItemHeight =
|
|
1931
|
-
shadowArray[lastItemIndex]?.virtualizer?.itemHeight || 0;
|
|
1932
|
-
|
|
1933
|
-
if (lastItemHeight > 0) {
|
|
1934
|
-
console.log(
|
|
1935
|
-
"%c...SUCCESS: Last item measured. Scrolling.",
|
|
1936
|
-
"color: green; font-weight: bold;"
|
|
1937
|
-
);
|
|
1938
|
-
clearInterval(intervalId);
|
|
1939
|
-
container.scrollTo({
|
|
1940
|
-
top: container.scrollHeight,
|
|
1941
|
-
behavior: "smooth",
|
|
1942
|
-
});
|
|
1943
|
-
} else {
|
|
1944
|
-
console.log("...WAITING for measurement.");
|
|
1945
|
-
}
|
|
1946
|
-
}, 100);
|
|
1947
|
-
} else {
|
|
1948
|
-
// --- MANUAL SCROLL PATH ---
|
|
1949
|
-
// If we are not auto-scrolling, just update the view for the user's position.
|
|
1950
|
-
updateVirtualRangeForUser();
|
|
1951
|
-
}
|
|
1964
|
+
const updateVirtualRange = () => {
|
|
1965
|
+
/* ... same as before ... */
|
|
1966
|
+
};
|
|
1952
1967
|
|
|
1953
|
-
// --- USER INTERACTION ---
|
|
1954
1968
|
const handleUserScroll = () => {
|
|
1955
|
-
// If the user scrolls up, break the lock.
|
|
1956
1969
|
const isAtBottom =
|
|
1957
1970
|
container.scrollHeight -
|
|
1958
1971
|
container.scrollTop -
|
|
1959
1972
|
container.clientHeight <
|
|
1960
1973
|
1;
|
|
1961
1974
|
if (!isAtBottom) {
|
|
1962
|
-
isLockedToBottomRef.current
|
|
1975
|
+
if (isLockedToBottomRef.current) {
|
|
1976
|
+
console.log("USER SCROLL: Lock broken.");
|
|
1977
|
+
isLockedToBottomRef.current = false;
|
|
1978
|
+
// If a scroll loop was running, kill it.
|
|
1979
|
+
if (scrollLoopId.current) {
|
|
1980
|
+
clearInterval(scrollLoopId.current);
|
|
1981
|
+
scrollLoopId.current = null;
|
|
1982
|
+
console.log("...Auto-scroll loop terminated by user.");
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1963
1985
|
}
|
|
1964
|
-
|
|
1965
|
-
updateVirtualRangeForUser();
|
|
1986
|
+
updateVirtualRange();
|
|
1966
1987
|
};
|
|
1967
1988
|
|
|
1968
1989
|
container.addEventListener("scroll", handleUserScroll, {
|
|
1969
1990
|
passive: true,
|
|
1970
1991
|
});
|
|
1992
|
+
updateVirtualRange();
|
|
1971
1993
|
|
|
1972
|
-
return () =>
|
|
1994
|
+
return () =>
|
|
1973
1995
|
container.removeEventListener("scroll", handleUserScroll);
|
|
1974
|
-
if (intervalId) {
|
|
1975
|
-
clearInterval(intervalId);
|
|
1976
|
-
}
|
|
1977
|
-
};
|
|
1978
1996
|
}, [totalCount, positions, ...dependencies]);
|
|
1979
1997
|
|
|
1980
|
-
// This simple effect tracks the item count for the next render.
|
|
1981
|
-
useEffect(() => {
|
|
1982
|
-
prevTotalCountRef.current = totalCount;
|
|
1983
|
-
});
|
|
1984
|
-
|
|
1985
1998
|
const scrollToBottom = useCallback(
|
|
1986
1999
|
(behavior: ScrollBehavior = "smooth") => {
|
|
1987
2000
|
if (containerRef.current) {
|