cogsbox-state 0.5.358 → 0.5.360
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 +559 -549
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +98 -95
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1816,8 +1816,11 @@ function createProxyHandler<T>(
|
|
|
1816
1816
|
});
|
|
1817
1817
|
const isLockedToBottomRef = useRef(stickToBottom);
|
|
1818
1818
|
|
|
1819
|
-
// This
|
|
1820
|
-
const
|
|
1819
|
+
// This flag prevents our own scroll animation from breaking the lock.
|
|
1820
|
+
const isAutoScrolling = useRef(false);
|
|
1821
|
+
|
|
1822
|
+
const prevDepsRef = useRef(dependencies);
|
|
1823
|
+
const prevTotalCountRef = useRef(0);
|
|
1821
1824
|
|
|
1822
1825
|
const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
|
|
1823
1826
|
|
|
@@ -1837,6 +1840,7 @@ function createProxyHandler<T>(
|
|
|
1837
1840
|
const totalCount = sourceArray.length;
|
|
1838
1841
|
|
|
1839
1842
|
const { totalHeight, positions } = useMemo(() => {
|
|
1843
|
+
// ... same as before ...
|
|
1840
1844
|
const shadowArray =
|
|
1841
1845
|
getGlobalStore.getState().getShadowMetadata(stateKey, path) ||
|
|
1842
1846
|
[];
|
|
@@ -1872,116 +1876,110 @@ function createProxyHandler<T>(
|
|
|
1872
1876
|
});
|
|
1873
1877
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1874
1878
|
|
|
1875
|
-
//
|
|
1876
|
-
// This runs when the data or dependencies change.
|
|
1879
|
+
// The single, authoritative effect for ALL layout logic.
|
|
1877
1880
|
useLayoutEffect(() => {
|
|
1878
1881
|
const container = containerRef.current;
|
|
1879
|
-
if (
|
|
1880
|
-
!container ||
|
|
1881
|
-
!stickToBottom ||
|
|
1882
|
-
!isLockedToBottomRef.current
|
|
1883
|
-
) {
|
|
1884
|
-
return;
|
|
1885
|
-
}
|
|
1886
|
-
|
|
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
|
-
});
|
|
1882
|
+
if (!container) return;
|
|
1899
1883
|
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1884
|
+
const depsChanged = !isDeepEqual(
|
|
1885
|
+
dependencies,
|
|
1886
|
+
prevDepsRef.current
|
|
1903
1887
|
);
|
|
1904
|
-
|
|
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;
|
|
1913
|
-
}
|
|
1888
|
+
const hasNewItems = totalCount > prevTotalCountRef.current;
|
|
1914
1889
|
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
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;
|
|
1890
|
+
if (depsChanged) {
|
|
1891
|
+
console.log("DEPENDENCY CHANGE: Resetting scroll lock.");
|
|
1892
|
+
isLockedToBottomRef.current = stickToBottom;
|
|
1893
|
+
}
|
|
1929
1894
|
|
|
1930
|
-
|
|
1931
|
-
|
|
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
|
-
}
|
|
1941
|
-
}
|
|
1942
|
-
}, 100);
|
|
1895
|
+
const shouldStartLoop =
|
|
1896
|
+
isLockedToBottomRef.current && (hasNewItems || depsChanged);
|
|
1943
1897
|
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1898
|
+
const updateVirtualRange = () => {
|
|
1899
|
+
// This is the full, non-placeholder function.
|
|
1900
|
+
const { scrollTop, clientHeight } = container;
|
|
1901
|
+
let low = 0,
|
|
1902
|
+
high = totalCount - 1;
|
|
1903
|
+
while (low <= high) {
|
|
1904
|
+
const mid = Math.floor((low + high) / 2);
|
|
1905
|
+
if (positions[mid]! < scrollTop) low = mid + 1;
|
|
1906
|
+
else high = mid - 1;
|
|
1907
|
+
}
|
|
1908
|
+
const startIndex = Math.max(0, high - overscan);
|
|
1909
|
+
let endIndex = startIndex;
|
|
1910
|
+
const visibleEnd = scrollTop + clientHeight;
|
|
1911
|
+
while (
|
|
1912
|
+
endIndex < totalCount &&
|
|
1913
|
+
positions[endIndex]! < visibleEnd
|
|
1914
|
+
) {
|
|
1915
|
+
endIndex++;
|
|
1947
1916
|
}
|
|
1917
|
+
setRange({
|
|
1918
|
+
startIndex,
|
|
1919
|
+
endIndex: Math.min(totalCount, endIndex + overscan),
|
|
1920
|
+
});
|
|
1948
1921
|
};
|
|
1949
|
-
}, [totalCount, ...dependencies]);
|
|
1950
1922
|
|
|
1951
|
-
|
|
1952
|
-
// This handles manual scrolling and resetting when the chat changes.
|
|
1953
|
-
useEffect(() => {
|
|
1954
|
-
const container = containerRef.current;
|
|
1955
|
-
if (!container) return;
|
|
1923
|
+
let intervalId: NodeJS.Timeout | undefined;
|
|
1956
1924
|
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
"
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1925
|
+
if (shouldStartLoop) {
|
|
1926
|
+
// --- YOUR ALGORITHM ---
|
|
1927
|
+
console.log("ALGORITHM: Starting...");
|
|
1928
|
+
setRange({
|
|
1929
|
+
startIndex: Math.max(0, totalCount - 10 - overscan),
|
|
1930
|
+
endIndex: totalCount,
|
|
1931
|
+
});
|
|
1963
1932
|
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1933
|
+
intervalId = setInterval(() => {
|
|
1934
|
+
const lastItemIndex = totalCount - 1;
|
|
1935
|
+
if (lastItemIndex < 0) {
|
|
1936
|
+
clearInterval(intervalId);
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
const shadowArray =
|
|
1941
|
+
getGlobalStore
|
|
1942
|
+
.getState()
|
|
1943
|
+
.getShadowMetadata(stateKey, path) || [];
|
|
1944
|
+
const lastItemHeight =
|
|
1945
|
+
shadowArray[lastItemIndex]?.virtualizer?.itemHeight || 0;
|
|
1946
|
+
|
|
1947
|
+
if (lastItemHeight > 0) {
|
|
1948
|
+
clearInterval(intervalId);
|
|
1949
|
+
console.log("%cSUCCESS: Scrolling now.", "color: green;");
|
|
1950
|
+
|
|
1951
|
+
// Set the flag to true before we start our animation.
|
|
1952
|
+
isAutoScrolling.current = true;
|
|
1953
|
+
|
|
1954
|
+
container.scrollTo({
|
|
1955
|
+
top: container.scrollHeight,
|
|
1956
|
+
behavior: "smooth",
|
|
1957
|
+
});
|
|
1958
|
+
|
|
1959
|
+
// After 1 second, assume animation is done and unset the flag.
|
|
1960
|
+
setTimeout(() => {
|
|
1961
|
+
isAutoScrolling.current = false;
|
|
1962
|
+
}, 1000);
|
|
1963
|
+
}
|
|
1964
|
+
}, 100);
|
|
1965
|
+
} else {
|
|
1966
|
+
updateVirtualRange();
|
|
1967
|
+
}
|
|
1967
1968
|
|
|
1968
1969
|
const handleUserScroll = () => {
|
|
1970
|
+
// If our code is scrolling, ignore this event.
|
|
1971
|
+
if (isAutoScrolling.current) return;
|
|
1972
|
+
|
|
1969
1973
|
const isAtBottom =
|
|
1970
1974
|
container.scrollHeight -
|
|
1971
1975
|
container.scrollTop -
|
|
1972
1976
|
container.clientHeight <
|
|
1973
1977
|
1;
|
|
1974
|
-
if (!isAtBottom) {
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
if (scrollLoopId.current) {
|
|
1980
|
-
clearInterval(scrollLoopId.current);
|
|
1981
|
-
scrollLoopId.current = null;
|
|
1982
|
-
console.log("...Auto-scroll loop terminated by user.");
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1978
|
+
if (!isAtBottom && isLockedToBottomRef.current) {
|
|
1979
|
+
console.log("USER SCROLL: Lock broken.");
|
|
1980
|
+
isLockedToBottomRef.current = false;
|
|
1981
|
+
// If a loop was somehow running, kill it.
|
|
1982
|
+
if (intervalId) clearInterval(intervalId);
|
|
1985
1983
|
}
|
|
1986
1984
|
updateVirtualRange();
|
|
1987
1985
|
};
|
|
@@ -1989,10 +1987,15 @@ function createProxyHandler<T>(
|
|
|
1989
1987
|
container.addEventListener("scroll", handleUserScroll, {
|
|
1990
1988
|
passive: true,
|
|
1991
1989
|
});
|
|
1992
|
-
updateVirtualRange();
|
|
1993
1990
|
|
|
1994
|
-
|
|
1991
|
+
// Update refs for the next render.
|
|
1992
|
+
prevDepsRef.current = dependencies;
|
|
1993
|
+
prevTotalCountRef.current = totalCount;
|
|
1994
|
+
|
|
1995
|
+
return () => {
|
|
1995
1996
|
container.removeEventListener("scroll", handleUserScroll);
|
|
1997
|
+
if (intervalId) clearInterval(intervalId);
|
|
1998
|
+
};
|
|
1996
1999
|
}, [totalCount, positions, ...dependencies]);
|
|
1997
2000
|
|
|
1998
2001
|
const scrollToBottom = useCallback(
|