cogsbox-state 0.5.322 → 0.5.324
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 +311 -306
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +30 -9
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1814,7 +1814,10 @@ function createProxyHandler<T>(
|
|
|
1814
1814
|
endIndex: 10,
|
|
1815
1815
|
});
|
|
1816
1816
|
|
|
1817
|
+
// This ref tracks if the user is locked to the bottom.
|
|
1817
1818
|
const isLockedToBottomRef = useRef(stickToBottom);
|
|
1819
|
+
|
|
1820
|
+
// This state triggers a re-render when item heights change.
|
|
1818
1821
|
const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
|
|
1819
1822
|
|
|
1820
1823
|
useEffect(() => {
|
|
@@ -1832,6 +1835,7 @@ function createProxyHandler<T>(
|
|
|
1832
1835
|
) as any[];
|
|
1833
1836
|
const totalCount = sourceArray.length;
|
|
1834
1837
|
|
|
1838
|
+
// Calculate heights from shadow state. This runs when data or measurements change.
|
|
1835
1839
|
const { totalHeight, positions } = useMemo(() => {
|
|
1836
1840
|
const shadowArray =
|
|
1837
1841
|
getGlobalStore.getState().getShadowMetadata(stateKey, path) ||
|
|
@@ -1853,6 +1857,7 @@ function createProxyHandler<T>(
|
|
|
1853
1857
|
shadowUpdateTrigger,
|
|
1854
1858
|
]);
|
|
1855
1859
|
|
|
1860
|
+
// Memoize the virtualized slice of data.
|
|
1856
1861
|
const virtualState = useMemo(() => {
|
|
1857
1862
|
const start = Math.max(0, range.startIndex);
|
|
1858
1863
|
const end = Math.min(totalCount, range.endIndex);
|
|
@@ -1867,10 +1872,14 @@ function createProxyHandler<T>(
|
|
|
1867
1872
|
});
|
|
1868
1873
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1869
1874
|
|
|
1875
|
+
// This is the main effect that handles all scrolling and updates.
|
|
1870
1876
|
useLayoutEffect(() => {
|
|
1871
1877
|
const container = containerRef.current;
|
|
1872
1878
|
if (!container) return;
|
|
1873
1879
|
|
|
1880
|
+
let scrollTimeoutId: NodeJS.Timeout;
|
|
1881
|
+
|
|
1882
|
+
// This function determines what's visible in the viewport.
|
|
1874
1883
|
const updateVirtualRange = () => {
|
|
1875
1884
|
if (!container) return;
|
|
1876
1885
|
const { scrollTop } = container;
|
|
@@ -1894,6 +1903,7 @@ function createProxyHandler<T>(
|
|
|
1894
1903
|
setRange({ startIndex, endIndex });
|
|
1895
1904
|
};
|
|
1896
1905
|
|
|
1906
|
+
// This function handles ONLY user-initiated scrolls.
|
|
1897
1907
|
const handleUserScroll = () => {
|
|
1898
1908
|
isLockedToBottomRef.current =
|
|
1899
1909
|
container.scrollHeight -
|
|
@@ -1907,20 +1917,31 @@ function createProxyHandler<T>(
|
|
|
1907
1917
|
passive: true,
|
|
1908
1918
|
});
|
|
1909
1919
|
|
|
1920
|
+
// --- THE CORE FIX ---
|
|
1921
|
+
if (stickToBottom) {
|
|
1922
|
+
// We use a timeout to wait for React to render AND for useMeasure to update heights.
|
|
1923
|
+
// This is the CRUCIAL part that fixes the race condition.
|
|
1924
|
+
scrollTimeoutId = setTimeout(() => {
|
|
1925
|
+
// By the time this runs, `container.scrollHeight` is accurate.
|
|
1926
|
+
// We only scroll if the user hasn't manually scrolled up in the meantime.
|
|
1927
|
+
if (isLockedToBottomRef.current) {
|
|
1928
|
+
container.scrollTo({
|
|
1929
|
+
top: container.scrollHeight,
|
|
1930
|
+
behavior: "auto", // ALWAYS 'auto' for an instant, correct jump.
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
}, 1000); // A small 50ms delay is a robust buffer.
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1910
1936
|
updateVirtualRange();
|
|
1911
1937
|
|
|
1938
|
+
// Cleanup function is vital to prevent memory leaks.
|
|
1912
1939
|
return () => {
|
|
1940
|
+
clearTimeout(scrollTimeoutId);
|
|
1913
1941
|
container.removeEventListener("scroll", handleUserScroll);
|
|
1914
1942
|
};
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
// SEPARATE EFFECT JUST FOR SCROLLING TO BOTTOM
|
|
1918
|
-
useEffect(() => {
|
|
1919
|
-
if (stickToBottom && containerRef.current && totalCount > 0) {
|
|
1920
|
-
// Just scroll to a massive number every time count changes
|
|
1921
|
-
containerRef.current.scrollTop = 999999999;
|
|
1922
|
-
}
|
|
1923
|
-
}, [totalCount, stickToBottom]);
|
|
1943
|
+
// This effect re-runs whenever the list size or item heights change.
|
|
1944
|
+
}, [totalCount, positions, stickToBottom]);
|
|
1924
1945
|
|
|
1925
1946
|
const scrollToBottom = useCallback(
|
|
1926
1947
|
(behavior: ScrollBehavior = "smooth") => {
|