cogsbox-state 0.5.312 → 0.5.314
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 +537 -535
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +50 -61
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1814,22 +1814,18 @@ function createProxyHandler<T>(
|
|
|
1814
1814
|
endIndex: 10,
|
|
1815
1815
|
});
|
|
1816
1816
|
|
|
1817
|
-
//
|
|
1817
|
+
// This ref tracks if the user is locked to the bottom.
|
|
1818
1818
|
const isLockedToBottomRef = useRef(stickToBottom);
|
|
1819
|
-
const isInitialMountRef = useRef(true);
|
|
1820
1819
|
|
|
1821
|
-
//
|
|
1820
|
+
// This state triggers a re-render when item heights change.
|
|
1822
1821
|
const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
|
|
1823
1822
|
|
|
1824
1823
|
useEffect(() => {
|
|
1825
|
-
// Subscribe to shadow state updates for this stateKey
|
|
1826
1824
|
const unsubscribe = getGlobalStore
|
|
1827
1825
|
.getState()
|
|
1828
1826
|
.subscribeToShadowState(stateKey, () => {
|
|
1829
|
-
// Force recalculation when shadow state updates
|
|
1830
1827
|
setShadowUpdateTrigger((prev) => prev + 1);
|
|
1831
1828
|
});
|
|
1832
|
-
|
|
1833
1829
|
return unsubscribe;
|
|
1834
1830
|
}, [stateKey]);
|
|
1835
1831
|
|
|
@@ -1839,21 +1835,19 @@ function createProxyHandler<T>(
|
|
|
1839
1835
|
) as any[];
|
|
1840
1836
|
const totalCount = sourceArray.length;
|
|
1841
1837
|
|
|
1842
|
-
// Calculate heights from shadow state
|
|
1838
|
+
// Calculate heights from shadow state. This runs when data or measurements change.
|
|
1843
1839
|
const { totalHeight, positions } = useMemo(() => {
|
|
1844
1840
|
const shadowArray =
|
|
1845
1841
|
getGlobalStore.getState().getShadowMetadata(stateKey, path) ||
|
|
1846
1842
|
[];
|
|
1847
1843
|
let height = 0;
|
|
1848
1844
|
const pos: number[] = [];
|
|
1849
|
-
|
|
1850
1845
|
for (let i = 0; i < totalCount; i++) {
|
|
1851
1846
|
pos[i] = height;
|
|
1852
1847
|
const measuredHeight =
|
|
1853
1848
|
shadowArray[i]?.virtualizer?.itemHeight;
|
|
1854
1849
|
height += measuredHeight || itemHeight;
|
|
1855
1850
|
}
|
|
1856
|
-
|
|
1857
1851
|
return { totalHeight: height, positions: pos };
|
|
1858
1852
|
}, [
|
|
1859
1853
|
totalCount,
|
|
@@ -1863,6 +1857,7 @@ function createProxyHandler<T>(
|
|
|
1863
1857
|
shadowUpdateTrigger,
|
|
1864
1858
|
]);
|
|
1865
1859
|
|
|
1860
|
+
// Memoize the virtualized slice of data.
|
|
1866
1861
|
const virtualState = useMemo(() => {
|
|
1867
1862
|
const start = Math.max(0, range.startIndex);
|
|
1868
1863
|
const end = Math.min(totalCount, range.endIndex);
|
|
@@ -1877,36 +1872,27 @@ function createProxyHandler<T>(
|
|
|
1877
1872
|
});
|
|
1878
1873
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1879
1874
|
|
|
1875
|
+
// This is the main effect that handles all scrolling and updates.
|
|
1880
1876
|
useLayoutEffect(() => {
|
|
1881
1877
|
const container = containerRef.current;
|
|
1882
1878
|
if (!container) return;
|
|
1883
1879
|
|
|
1884
|
-
|
|
1885
|
-
const { scrollTop, clientHeight, scrollHeight } = container;
|
|
1886
|
-
|
|
1887
|
-
// Determine if the user is at the bottom
|
|
1888
|
-
const isNowAtBottom =
|
|
1889
|
-
scrollHeight - scrollTop - clientHeight < 1;
|
|
1890
|
-
|
|
1891
|
-
// If the user scrolls up, unlock. If they scroll back down, re-lock.
|
|
1892
|
-
isLockedToBottomRef.current = isNowAtBottom;
|
|
1880
|
+
let scrollTimeoutId: NodeJS.Timeout;
|
|
1893
1881
|
|
|
1894
|
-
|
|
1882
|
+
// This function determines what's visible in the viewport.
|
|
1883
|
+
const updateVirtualRange = () => {
|
|
1884
|
+
if (!container) return;
|
|
1885
|
+
const { scrollTop } = container;
|
|
1895
1886
|
let low = 0,
|
|
1896
1887
|
high = totalCount - 1;
|
|
1897
1888
|
while (low <= high) {
|
|
1898
1889
|
const mid = Math.floor((low + high) / 2);
|
|
1899
|
-
if (positions[mid]! < scrollTop)
|
|
1900
|
-
|
|
1901
|
-
} else {
|
|
1902
|
-
high = mid - 1;
|
|
1903
|
-
}
|
|
1890
|
+
if (positions[mid]! < scrollTop) low = mid + 1;
|
|
1891
|
+
else high = mid - 1;
|
|
1904
1892
|
}
|
|
1905
1893
|
const startIndex = Math.max(0, high - overscan);
|
|
1906
|
-
|
|
1907
|
-
// Find end index
|
|
1908
1894
|
let endIndex = startIndex;
|
|
1909
|
-
const visibleEnd = scrollTop + clientHeight;
|
|
1895
|
+
const visibleEnd = scrollTop + container.clientHeight;
|
|
1910
1896
|
while (
|
|
1911
1897
|
endIndex < totalCount &&
|
|
1912
1898
|
positions[endIndex]! < visibleEnd
|
|
@@ -1914,53 +1900,54 @@ function createProxyHandler<T>(
|
|
|
1914
1900
|
endIndex++;
|
|
1915
1901
|
}
|
|
1916
1902
|
endIndex = Math.min(totalCount, endIndex + overscan);
|
|
1903
|
+
setRange({ startIndex, endIndex });
|
|
1904
|
+
};
|
|
1917
1905
|
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
});
|
|
1906
|
+
// This function handles ONLY user-initiated scrolls.
|
|
1907
|
+
const handleUserScroll = () => {
|
|
1908
|
+
isLockedToBottomRef.current =
|
|
1909
|
+
container.scrollHeight -
|
|
1910
|
+
container.scrollTop -
|
|
1911
|
+
container.clientHeight <
|
|
1912
|
+
1;
|
|
1913
|
+
updateVirtualRange();
|
|
1927
1914
|
};
|
|
1928
1915
|
|
|
1929
|
-
container.addEventListener("scroll",
|
|
1916
|
+
container.addEventListener("scroll", handleUserScroll, {
|
|
1930
1917
|
passive: true,
|
|
1931
1918
|
});
|
|
1932
1919
|
|
|
1933
|
-
// ---
|
|
1934
|
-
if (stickToBottom
|
|
1935
|
-
//
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
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
|
+
}, 50); // A small 50ms delay is a robust buffer.
|
|
1944
1934
|
}
|
|
1945
1935
|
|
|
1946
|
-
//
|
|
1947
|
-
|
|
1948
|
-
queueMicrotask(() => {
|
|
1949
|
-
if (isInitialMountRef.current) {
|
|
1950
|
-
isInitialMountRef.current = false;
|
|
1951
|
-
}
|
|
1952
|
-
});
|
|
1953
|
-
|
|
1954
|
-
// Run handleScroll once on setup to set initial range and lock status
|
|
1955
|
-
handleScroll();
|
|
1936
|
+
// Update the visible range on initial load.
|
|
1937
|
+
updateVirtualRange();
|
|
1956
1938
|
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1939
|
+
// Cleanup function is vital to prevent memory leaks.
|
|
1940
|
+
return () => {
|
|
1941
|
+
clearTimeout(scrollTimeoutId);
|
|
1942
|
+
container.removeEventListener("scroll", handleUserScroll);
|
|
1943
|
+
};
|
|
1944
|
+
// This effect re-runs whenever the list size or item heights change.
|
|
1945
|
+
}, [totalCount, positions, stickToBottom]);
|
|
1960
1946
|
|
|
1961
1947
|
const scrollToBottom = useCallback(
|
|
1962
1948
|
(behavior: ScrollBehavior = "smooth") => {
|
|
1963
1949
|
if (containerRef.current) {
|
|
1950
|
+
isLockedToBottomRef.current = true;
|
|
1964
1951
|
containerRef.current.scrollTo({
|
|
1965
1952
|
top: containerRef.current.scrollHeight,
|
|
1966
1953
|
behavior,
|
|
@@ -1973,6 +1960,7 @@ function createProxyHandler<T>(
|
|
|
1973
1960
|
const scrollToIndex = useCallback(
|
|
1974
1961
|
(index: number, behavior: ScrollBehavior = "smooth") => {
|
|
1975
1962
|
if (containerRef.current && positions[index] !== undefined) {
|
|
1963
|
+
isLockedToBottomRef.current = false;
|
|
1976
1964
|
containerRef.current.scrollTo({
|
|
1977
1965
|
top: positions[index],
|
|
1978
1966
|
behavior,
|
|
@@ -1999,6 +1987,7 @@ function createProxyHandler<T>(
|
|
|
1999
1987
|
},
|
|
2000
1988
|
},
|
|
2001
1989
|
};
|
|
1990
|
+
|
|
2002
1991
|
return {
|
|
2003
1992
|
virtualState,
|
|
2004
1993
|
virtualizerProps,
|