cogsbox-state 0.5.317 → 0.5.319
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 +764 -811
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +42 -118
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1814,24 +1814,16 @@ 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);
|
|
1818
|
-
const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
|
|
1819
1819
|
|
|
1820
|
-
//
|
|
1821
|
-
const
|
|
1822
|
-
const lastTotalCountRef = useRef(0);
|
|
1820
|
+
// This state triggers a re-render when item heights change.
|
|
1821
|
+
const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
|
|
1823
1822
|
|
|
1824
1823
|
useEffect(() => {
|
|
1825
|
-
let updateCount = 0;
|
|
1826
1824
|
const unsubscribe = getGlobalStore
|
|
1827
1825
|
.getState()
|
|
1828
1826
|
.subscribeToShadowState(stateKey, () => {
|
|
1829
|
-
updateCount++;
|
|
1830
|
-
if (updateCount <= 5) {
|
|
1831
|
-
console.log(
|
|
1832
|
-
`[VirtualView] Shadow update #${updateCount}`
|
|
1833
|
-
);
|
|
1834
|
-
}
|
|
1835
1827
|
setShadowUpdateTrigger((prev) => prev + 1);
|
|
1836
1828
|
});
|
|
1837
1829
|
return unsubscribe;
|
|
@@ -1843,77 +1835,32 @@ function createProxyHandler<T>(
|
|
|
1843
1835
|
) as any[];
|
|
1844
1836
|
const totalCount = sourceArray.length;
|
|
1845
1837
|
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
const pos: number[] = [];
|
|
1868
|
-
let measuredCount = 0;
|
|
1869
|
-
let visibleMeasuredCount = 0;
|
|
1870
|
-
|
|
1871
|
-
for (let i = 0; i < totalCount; i++) {
|
|
1872
|
-
pos[i] = height;
|
|
1873
|
-
const measuredHeight =
|
|
1874
|
-
shadowArray[i]?.virtualizer?.itemHeight;
|
|
1875
|
-
if (measuredHeight) {
|
|
1876
|
-
measuredCount++;
|
|
1877
|
-
// Count measured items in current visible range
|
|
1878
|
-
if (i >= range.startIndex && i < range.endIndex) {
|
|
1879
|
-
visibleMeasuredCount++;
|
|
1880
|
-
}
|
|
1881
|
-
}
|
|
1882
|
-
height += measuredHeight || itemHeight;
|
|
1883
|
-
}
|
|
1884
|
-
|
|
1885
|
-
// Check if all VISIBLE items are measured
|
|
1886
|
-
const visibleCount = range.endIndex - range.startIndex;
|
|
1887
|
-
const allVisibleMeasured =
|
|
1888
|
-
visibleMeasuredCount === visibleCount && visibleCount > 0;
|
|
1889
|
-
|
|
1890
|
-
console.log(
|
|
1891
|
-
`[VirtualView] Heights calc - measured: ${measuredCount}/${totalCount}, visible measured: ${visibleMeasuredCount}/${visibleCount}, totalHeight: ${height}`
|
|
1892
|
-
);
|
|
1893
|
-
|
|
1894
|
-
return {
|
|
1895
|
-
totalHeight: height,
|
|
1896
|
-
positions: pos,
|
|
1897
|
-
visibleMeasured: allVisibleMeasured,
|
|
1898
|
-
};
|
|
1899
|
-
}, [
|
|
1900
|
-
totalCount,
|
|
1901
|
-
stateKey,
|
|
1902
|
-
path.join("."),
|
|
1903
|
-
itemHeight,
|
|
1904
|
-
shadowUpdateTrigger,
|
|
1905
|
-
range, // Add range dependency
|
|
1906
|
-
]);
|
|
1838
|
+
// Calculate heights from shadow state. This runs when data or measurements change.
|
|
1839
|
+
const { totalHeight, positions } = useMemo(() => {
|
|
1840
|
+
const shadowArray =
|
|
1841
|
+
getGlobalStore.getState().getShadowMetadata(stateKey, path) ||
|
|
1842
|
+
[];
|
|
1843
|
+
let height = 0;
|
|
1844
|
+
const pos: number[] = [];
|
|
1845
|
+
for (let i = 0; i < totalCount; i++) {
|
|
1846
|
+
pos[i] = height;
|
|
1847
|
+
const measuredHeight =
|
|
1848
|
+
shadowArray[i]?.virtualizer?.itemHeight;
|
|
1849
|
+
height += measuredHeight || itemHeight;
|
|
1850
|
+
}
|
|
1851
|
+
return { totalHeight: height, positions: pos };
|
|
1852
|
+
}, [
|
|
1853
|
+
totalCount,
|
|
1854
|
+
stateKey,
|
|
1855
|
+
path.join("."),
|
|
1856
|
+
itemHeight,
|
|
1857
|
+
shadowUpdateTrigger,
|
|
1858
|
+
]);
|
|
1907
1859
|
|
|
1908
|
-
// Memoize the virtualized slice
|
|
1860
|
+
// Memoize the virtualized slice of data.
|
|
1909
1861
|
const virtualState = useMemo(() => {
|
|
1910
1862
|
const start = Math.max(0, range.startIndex);
|
|
1911
1863
|
const end = Math.min(totalCount, range.endIndex);
|
|
1912
|
-
|
|
1913
|
-
console.log(
|
|
1914
|
-
`[VirtualView] Creating virtual slice - range: ${start}-${end} (${end - start} items)`
|
|
1915
|
-
);
|
|
1916
|
-
|
|
1917
1864
|
const validIndices = Array.from(
|
|
1918
1865
|
{ length: end - start },
|
|
1919
1866
|
(_, i) => start + i
|
|
@@ -1925,11 +1872,14 @@ function createProxyHandler<T>(
|
|
|
1925
1872
|
});
|
|
1926
1873
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1927
1874
|
|
|
1928
|
-
//
|
|
1875
|
+
// This is the main effect that handles all scrolling and updates.
|
|
1929
1876
|
useLayoutEffect(() => {
|
|
1930
1877
|
const container = containerRef.current;
|
|
1931
1878
|
if (!container) return;
|
|
1932
1879
|
|
|
1880
|
+
let scrollTimeoutId: NodeJS.Timeout;
|
|
1881
|
+
|
|
1882
|
+
// This function determines what's visible in the viewport.
|
|
1933
1883
|
const updateVirtualRange = () => {
|
|
1934
1884
|
if (!container) return;
|
|
1935
1885
|
const { scrollTop } = container;
|
|
@@ -1953,6 +1903,7 @@ function createProxyHandler<T>(
|
|
|
1953
1903
|
setRange({ startIndex, endIndex });
|
|
1954
1904
|
};
|
|
1955
1905
|
|
|
1906
|
+
// This function handles ONLY user-initiated scrolls.
|
|
1956
1907
|
const handleUserScroll = () => {
|
|
1957
1908
|
isLockedToBottomRef.current =
|
|
1958
1909
|
container.scrollHeight -
|
|
@@ -1966,55 +1917,28 @@ function createProxyHandler<T>(
|
|
|
1966
1917
|
passive: true,
|
|
1967
1918
|
});
|
|
1968
1919
|
|
|
1969
|
-
//
|
|
1970
|
-
if (
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
totalCount > 0
|
|
1974
|
-
) {
|
|
1975
|
-
if (visibleMeasured || range.endIndex === totalCount) {
|
|
1976
|
-
// If we're showing the last items OR current visible items are measured
|
|
1977
|
-
console.log(
|
|
1978
|
-
`[VirtualView] Scrolling to bottom - visible measured: ${visibleMeasured}, at end: ${range.endIndex === totalCount}`
|
|
1979
|
-
);
|
|
1980
|
-
hasScrolledToBottomRef.current = true;
|
|
1981
|
-
|
|
1982
|
-
// Use setTimeout to ensure DOM updates are complete
|
|
1983
|
-
setTimeout(() => {
|
|
1920
|
+
// In useLayoutEffect, replace this section:
|
|
1921
|
+
if (stickToBottom) {
|
|
1922
|
+
scrollTimeoutId = setTimeout(() => {
|
|
1923
|
+
if (isLockedToBottomRef.current) {
|
|
1984
1924
|
container.scrollTo({
|
|
1985
|
-
top: container.scrollHeight + 1000,
|
|
1925
|
+
top: container.scrollHeight + 1000, // ADD A BUFFER HERE
|
|
1986
1926
|
behavior: "auto",
|
|
1987
1927
|
});
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
} else {
|
|
1991
|
-
// Jump close to the bottom to trigger rendering of bottom items
|
|
1992
|
-
console.log(
|
|
1993
|
-
`[VirtualView] Jumping near bottom to trigger measurements`
|
|
1994
|
-
);
|
|
1995
|
-
const estimatedScrollPosition = Math.max(
|
|
1996
|
-
0,
|
|
1997
|
-
(totalCount - 20) * itemHeight
|
|
1998
|
-
);
|
|
1999
|
-
container.scrollTo({
|
|
2000
|
-
top: estimatedScrollPosition,
|
|
2001
|
-
behavior: "auto",
|
|
2002
|
-
});
|
|
2003
|
-
}
|
|
1928
|
+
}
|
|
1929
|
+
}, 200);
|
|
2004
1930
|
}
|
|
2005
1931
|
|
|
1932
|
+
// Update the visible range on initial load.
|
|
2006
1933
|
updateVirtualRange();
|
|
2007
1934
|
|
|
1935
|
+
// Cleanup function is vital to prevent memory leaks.
|
|
2008
1936
|
return () => {
|
|
1937
|
+
clearTimeout(scrollTimeoutId);
|
|
2009
1938
|
container.removeEventListener("scroll", handleUserScroll);
|
|
2010
1939
|
};
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
positions,
|
|
2014
|
-
stickToBottom,
|
|
2015
|
-
visibleMeasured,
|
|
2016
|
-
range.endIndex,
|
|
2017
|
-
]);
|
|
1940
|
+
// This effect re-runs whenever the list size or item heights change.
|
|
1941
|
+
}, [totalCount, positions, stickToBottom]);
|
|
2018
1942
|
|
|
2019
1943
|
const scrollToBottom = useCallback(
|
|
2020
1944
|
(behavior: ScrollBehavior = "smooth") => {
|