cogsbox-state 0.5.318 → 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 +612 -654
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +42 -115
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,52 +1917,28 @@ function createProxyHandler<T>(
|
|
|
1966
1917
|
passive: true,
|
|
1967
1918
|
});
|
|
1968
1919
|
|
|
1969
|
-
//
|
|
1970
|
-
if (
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
totalCount > 0
|
|
1974
|
-
) {
|
|
1975
|
-
// Check current range without dependency
|
|
1976
|
-
const currentRange = range;
|
|
1977
|
-
const atEnd = currentRange.endIndex >= totalCount - 5; // Close to end
|
|
1978
|
-
|
|
1979
|
-
if (atEnd) {
|
|
1980
|
-
console.log(
|
|
1981
|
-
`[VirtualView] At end of list, scrolling to bottom`
|
|
1982
|
-
);
|
|
1983
|
-
hasScrolledToBottomRef.current = true;
|
|
1984
|
-
|
|
1985
|
-
setTimeout(() => {
|
|
1986
|
-
const scrollTarget = container.scrollHeight + 1000;
|
|
1920
|
+
// In useLayoutEffect, replace this section:
|
|
1921
|
+
if (stickToBottom) {
|
|
1922
|
+
scrollTimeoutId = setTimeout(() => {
|
|
1923
|
+
if (isLockedToBottomRef.current) {
|
|
1987
1924
|
container.scrollTo({
|
|
1988
|
-
top:
|
|
1925
|
+
top: container.scrollHeight + 1000, // ADD A BUFFER HERE
|
|
1989
1926
|
behavior: "auto",
|
|
1990
1927
|
});
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
} else {
|
|
1994
|
-
// Jump close to the bottom
|
|
1995
|
-
console.log(
|
|
1996
|
-
`[VirtualView] Jumping near bottom to trigger measurements`
|
|
1997
|
-
);
|
|
1998
|
-
const estimatedScrollPosition = Math.max(
|
|
1999
|
-
0,
|
|
2000
|
-
(totalCount - 20) * itemHeight
|
|
2001
|
-
);
|
|
2002
|
-
container.scrollTo({
|
|
2003
|
-
top: estimatedScrollPosition,
|
|
2004
|
-
behavior: "auto",
|
|
2005
|
-
});
|
|
2006
|
-
}
|
|
1928
|
+
}
|
|
1929
|
+
}, 200);
|
|
2007
1930
|
}
|
|
2008
1931
|
|
|
1932
|
+
// Update the visible range on initial load.
|
|
2009
1933
|
updateVirtualRange();
|
|
2010
1934
|
|
|
1935
|
+
// Cleanup function is vital to prevent memory leaks.
|
|
2011
1936
|
return () => {
|
|
1937
|
+
clearTimeout(scrollTimeoutId);
|
|
2012
1938
|
container.removeEventListener("scroll", handleUserScroll);
|
|
2013
1939
|
};
|
|
2014
|
-
|
|
1940
|
+
// This effect re-runs whenever the list size or item heights change.
|
|
1941
|
+
}, [totalCount, positions, stickToBottom]);
|
|
2015
1942
|
|
|
2016
1943
|
const scrollToBottom = useCallback(
|
|
2017
1944
|
(behavior: ScrollBehavior = "smooth") => {
|