cogsbox-state 0.5.315 → 0.5.317
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 +811 -764
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +119 -47
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1814,16 +1814,24 @@ function createProxyHandler<T>(
|
|
|
1814
1814
|
endIndex: 10,
|
|
1815
1815
|
});
|
|
1816
1816
|
|
|
1817
|
-
// This ref tracks if the user is locked to the bottom.
|
|
1818
1817
|
const isLockedToBottomRef = useRef(stickToBottom);
|
|
1819
|
-
|
|
1820
|
-
// This state triggers a re-render when item heights change.
|
|
1821
1818
|
const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
|
|
1822
1819
|
|
|
1820
|
+
// Track if we've scrolled to bottom after initial load
|
|
1821
|
+
const hasScrolledToBottomRef = useRef(false);
|
|
1822
|
+
const lastTotalCountRef = useRef(0);
|
|
1823
|
+
|
|
1823
1824
|
useEffect(() => {
|
|
1825
|
+
let updateCount = 0;
|
|
1824
1826
|
const unsubscribe = getGlobalStore
|
|
1825
1827
|
.getState()
|
|
1826
1828
|
.subscribeToShadowState(stateKey, () => {
|
|
1829
|
+
updateCount++;
|
|
1830
|
+
if (updateCount <= 5) {
|
|
1831
|
+
console.log(
|
|
1832
|
+
`[VirtualView] Shadow update #${updateCount}`
|
|
1833
|
+
);
|
|
1834
|
+
}
|
|
1827
1835
|
setShadowUpdateTrigger((prev) => prev + 1);
|
|
1828
1836
|
});
|
|
1829
1837
|
return unsubscribe;
|
|
@@ -1835,32 +1843,77 @@ function createProxyHandler<T>(
|
|
|
1835
1843
|
) as any[];
|
|
1836
1844
|
const totalCount = sourceArray.length;
|
|
1837
1845
|
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1846
|
+
console.log(
|
|
1847
|
+
`[VirtualView] Initial setup - totalCount: ${totalCount}, itemHeight: ${itemHeight}, stickToBottom: ${stickToBottom}`
|
|
1848
|
+
);
|
|
1849
|
+
|
|
1850
|
+
// Reset when array size changes significantly
|
|
1851
|
+
if (totalCount !== lastTotalCountRef.current) {
|
|
1852
|
+
console.log(
|
|
1853
|
+
`[VirtualView] Array size changed from ${lastTotalCountRef.current} to ${totalCount}`
|
|
1854
|
+
);
|
|
1855
|
+
hasScrolledToBottomRef.current = false;
|
|
1856
|
+
lastTotalCountRef.current = totalCount;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
// Calculate heights from shadow state
|
|
1860
|
+
const { totalHeight, positions, visibleMeasured } =
|
|
1861
|
+
useMemo(() => {
|
|
1862
|
+
const shadowArray =
|
|
1863
|
+
getGlobalStore
|
|
1864
|
+
.getState()
|
|
1865
|
+
.getShadowMetadata(stateKey, path) || [];
|
|
1866
|
+
let height = 0;
|
|
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
|
+
);
|
|
1859
1893
|
|
|
1860
|
-
|
|
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
|
+
]);
|
|
1907
|
+
|
|
1908
|
+
// Memoize the virtualized slice
|
|
1861
1909
|
const virtualState = useMemo(() => {
|
|
1862
1910
|
const start = Math.max(0, range.startIndex);
|
|
1863
1911
|
const end = Math.min(totalCount, range.endIndex);
|
|
1912
|
+
|
|
1913
|
+
console.log(
|
|
1914
|
+
`[VirtualView] Creating virtual slice - range: ${start}-${end} (${end - start} items)`
|
|
1915
|
+
);
|
|
1916
|
+
|
|
1864
1917
|
const validIndices = Array.from(
|
|
1865
1918
|
{ length: end - start },
|
|
1866
1919
|
(_, i) => start + i
|
|
@@ -1872,14 +1925,11 @@ function createProxyHandler<T>(
|
|
|
1872
1925
|
});
|
|
1873
1926
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1874
1927
|
|
|
1875
|
-
//
|
|
1928
|
+
// Main layout effect
|
|
1876
1929
|
useLayoutEffect(() => {
|
|
1877
1930
|
const container = containerRef.current;
|
|
1878
1931
|
if (!container) return;
|
|
1879
1932
|
|
|
1880
|
-
let scrollTimeoutId: NodeJS.Timeout;
|
|
1881
|
-
|
|
1882
|
-
// This function determines what's visible in the viewport.
|
|
1883
1933
|
const updateVirtualRange = () => {
|
|
1884
1934
|
if (!container) return;
|
|
1885
1935
|
const { scrollTop } = container;
|
|
@@ -1903,7 +1953,6 @@ function createProxyHandler<T>(
|
|
|
1903
1953
|
setRange({ startIndex, endIndex });
|
|
1904
1954
|
};
|
|
1905
1955
|
|
|
1906
|
-
// This function handles ONLY user-initiated scrolls.
|
|
1907
1956
|
const handleUserScroll = () => {
|
|
1908
1957
|
isLockedToBottomRef.current =
|
|
1909
1958
|
container.scrollHeight -
|
|
@@ -1917,32 +1966,55 @@ function createProxyHandler<T>(
|
|
|
1917
1966
|
passive: true,
|
|
1918
1967
|
});
|
|
1919
1968
|
|
|
1920
|
-
//
|
|
1921
|
-
if (
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1969
|
+
// For stick to bottom: first jump to approximate bottom position
|
|
1970
|
+
if (
|
|
1971
|
+
stickToBottom &&
|
|
1972
|
+
!hasScrolledToBottomRef.current &&
|
|
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(() => {
|
|
1928
1984
|
container.scrollTo({
|
|
1929
|
-
top: container.scrollHeight,
|
|
1930
|
-
behavior: "auto",
|
|
1985
|
+
top: container.scrollHeight + 1000,
|
|
1986
|
+
behavior: "auto",
|
|
1931
1987
|
});
|
|
1932
|
-
|
|
1933
|
-
|
|
1988
|
+
isLockedToBottomRef.current = true;
|
|
1989
|
+
}, 0);
|
|
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
|
+
}
|
|
1934
2004
|
}
|
|
1935
2005
|
|
|
1936
|
-
// Update the visible range on initial load.
|
|
1937
2006
|
updateVirtualRange();
|
|
1938
2007
|
|
|
1939
|
-
// Cleanup function is vital to prevent memory leaks.
|
|
1940
2008
|
return () => {
|
|
1941
|
-
clearTimeout(scrollTimeoutId);
|
|
1942
2009
|
container.removeEventListener("scroll", handleUserScroll);
|
|
1943
2010
|
};
|
|
1944
|
-
|
|
1945
|
-
|
|
2011
|
+
}, [
|
|
2012
|
+
totalCount,
|
|
2013
|
+
positions,
|
|
2014
|
+
stickToBottom,
|
|
2015
|
+
visibleMeasured,
|
|
2016
|
+
range.endIndex,
|
|
2017
|
+
]);
|
|
1946
2018
|
|
|
1947
2019
|
const scrollToBottom = useCallback(
|
|
1948
2020
|
(behavior: ScrollBehavior = "smooth") => {
|