cogsbox-state 0.5.319 → 0.5.321
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 +550 -531
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +84 -52
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1813,12 +1813,15 @@ function createProxyHandler<T>(
|
|
|
1813
1813
|
startIndex: 0,
|
|
1814
1814
|
endIndex: 10,
|
|
1815
1815
|
});
|
|
1816
|
-
|
|
1817
|
-
// This ref tracks if the user is locked to the bottom.
|
|
1818
1816
|
const isLockedToBottomRef = useRef(stickToBottom);
|
|
1819
|
-
|
|
1820
|
-
// This state triggers a re-render when item heights change.
|
|
1821
1817
|
const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
|
|
1818
|
+
const hasScrolledToBottomRef = useRef(false);
|
|
1819
|
+
|
|
1820
|
+
const sourceArray = getGlobalStore().getNestedState(
|
|
1821
|
+
stateKey,
|
|
1822
|
+
path
|
|
1823
|
+
) as any[];
|
|
1824
|
+
const totalCount = sourceArray.length;
|
|
1822
1825
|
|
|
1823
1826
|
useEffect(() => {
|
|
1824
1827
|
const unsubscribe = getGlobalStore
|
|
@@ -1829,35 +1832,52 @@ function createProxyHandler<T>(
|
|
|
1829
1832
|
return unsubscribe;
|
|
1830
1833
|
}, [stateKey]);
|
|
1831
1834
|
|
|
1832
|
-
const
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1835
|
+
const { totalHeight, positions, bottomItemsMeasured } =
|
|
1836
|
+
useMemo(() => {
|
|
1837
|
+
const shadowArray =
|
|
1838
|
+
getGlobalStore
|
|
1839
|
+
.getState()
|
|
1840
|
+
.getShadowMetadata(stateKey, path) || [];
|
|
1841
|
+
let height = 0;
|
|
1842
|
+
const pos: number[] = [];
|
|
1843
|
+
let bottomMeasuredCount = 0;
|
|
1844
|
+
|
|
1845
|
+
// Check how many of the last 20 items are measured
|
|
1846
|
+
const checkFromIndex = Math.max(0, totalCount - 20);
|
|
1847
|
+
|
|
1848
|
+
for (let i = 0; i < totalCount; i++) {
|
|
1849
|
+
pos[i] = height;
|
|
1850
|
+
const measuredHeight =
|
|
1851
|
+
shadowArray[i]?.virtualizer?.itemHeight;
|
|
1852
|
+
|
|
1853
|
+
if (measuredHeight) {
|
|
1854
|
+
height += measuredHeight;
|
|
1855
|
+
if (i >= checkFromIndex) {
|
|
1856
|
+
bottomMeasuredCount++;
|
|
1857
|
+
}
|
|
1858
|
+
} else {
|
|
1859
|
+
height += itemHeight;
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1837
1862
|
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
path.join("."),
|
|
1856
|
-
itemHeight,
|
|
1857
|
-
shadowUpdateTrigger,
|
|
1858
|
-
]);
|
|
1863
|
+
// Bottom items are measured if we have measurements for the last 20 items
|
|
1864
|
+
const bottomReady =
|
|
1865
|
+
bottomMeasuredCount >=
|
|
1866
|
+
Math.min(20, totalCount - checkFromIndex);
|
|
1867
|
+
|
|
1868
|
+
return {
|
|
1869
|
+
totalHeight: height,
|
|
1870
|
+
positions: pos,
|
|
1871
|
+
bottomItemsMeasured: bottomReady,
|
|
1872
|
+
};
|
|
1873
|
+
}, [
|
|
1874
|
+
totalCount,
|
|
1875
|
+
stateKey,
|
|
1876
|
+
path.join("."),
|
|
1877
|
+
itemHeight,
|
|
1878
|
+
shadowUpdateTrigger,
|
|
1879
|
+
]);
|
|
1859
1880
|
|
|
1860
|
-
// Memoize the virtualized slice of data.
|
|
1861
1881
|
const virtualState = useMemo(() => {
|
|
1862
1882
|
const start = Math.max(0, range.startIndex);
|
|
1863
1883
|
const end = Math.min(totalCount, range.endIndex);
|
|
@@ -1872,14 +1892,10 @@ function createProxyHandler<T>(
|
|
|
1872
1892
|
});
|
|
1873
1893
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1874
1894
|
|
|
1875
|
-
// This is the main effect that handles all scrolling and updates.
|
|
1876
1895
|
useLayoutEffect(() => {
|
|
1877
1896
|
const container = containerRef.current;
|
|
1878
1897
|
if (!container) return;
|
|
1879
1898
|
|
|
1880
|
-
let scrollTimeoutId: NodeJS.Timeout;
|
|
1881
|
-
|
|
1882
|
-
// This function determines what's visible in the viewport.
|
|
1883
1899
|
const updateVirtualRange = () => {
|
|
1884
1900
|
if (!container) return;
|
|
1885
1901
|
const { scrollTop } = container;
|
|
@@ -1903,7 +1919,6 @@ function createProxyHandler<T>(
|
|
|
1903
1919
|
setRange({ startIndex, endIndex });
|
|
1904
1920
|
};
|
|
1905
1921
|
|
|
1906
|
-
// This function handles ONLY user-initiated scrolls.
|
|
1907
1922
|
const handleUserScroll = () => {
|
|
1908
1923
|
isLockedToBottomRef.current =
|
|
1909
1924
|
container.scrollHeight -
|
|
@@ -1917,28 +1932,39 @@ function createProxyHandler<T>(
|
|
|
1917
1932
|
passive: true,
|
|
1918
1933
|
});
|
|
1919
1934
|
|
|
1920
|
-
//
|
|
1921
|
-
if (
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1935
|
+
// STICK TO BOTTOM LOGIC
|
|
1936
|
+
if (
|
|
1937
|
+
stickToBottom &&
|
|
1938
|
+
!hasScrolledToBottomRef.current &&
|
|
1939
|
+
totalCount > 0
|
|
1940
|
+
) {
|
|
1941
|
+
if (!bottomItemsMeasured) {
|
|
1942
|
+
// Step 1: Jump to near bottom to trigger rendering of bottom items
|
|
1943
|
+
console.log(
|
|
1944
|
+
"[VirtualView] Jumping to near bottom to trigger measurements"
|
|
1945
|
+
);
|
|
1946
|
+
const jumpPosition = Math.max(
|
|
1947
|
+
0,
|
|
1948
|
+
(totalCount - 30) * itemHeight
|
|
1949
|
+
);
|
|
1950
|
+
container.scrollTop = jumpPosition;
|
|
1951
|
+
} else {
|
|
1952
|
+
// Step 2: Bottom items are measured, now scroll to actual bottom
|
|
1953
|
+
console.log(
|
|
1954
|
+
"[VirtualView] Bottom items measured, scrolling to true bottom"
|
|
1955
|
+
);
|
|
1956
|
+
hasScrolledToBottomRef.current = true;
|
|
1957
|
+
container.scrollTop = container.scrollHeight;
|
|
1958
|
+
isLockedToBottomRef.current = true;
|
|
1959
|
+
}
|
|
1930
1960
|
}
|
|
1931
1961
|
|
|
1932
|
-
// Update the visible range on initial load.
|
|
1933
1962
|
updateVirtualRange();
|
|
1934
1963
|
|
|
1935
|
-
// Cleanup function is vital to prevent memory leaks.
|
|
1936
1964
|
return () => {
|
|
1937
|
-
clearTimeout(scrollTimeoutId);
|
|
1938
1965
|
container.removeEventListener("scroll", handleUserScroll);
|
|
1939
1966
|
};
|
|
1940
|
-
|
|
1941
|
-
}, [totalCount, positions, stickToBottom]);
|
|
1967
|
+
}, [totalCount, positions, stickToBottom, bottomItemsMeasured]);
|
|
1942
1968
|
|
|
1943
1969
|
const scrollToBottom = useCallback(
|
|
1944
1970
|
(behavior: ScrollBehavior = "smooth") => {
|
|
@@ -1969,7 +1995,13 @@ function createProxyHandler<T>(
|
|
|
1969
1995
|
const virtualizerProps = {
|
|
1970
1996
|
outer: {
|
|
1971
1997
|
ref: containerRef,
|
|
1972
|
-
style: {
|
|
1998
|
+
style: {
|
|
1999
|
+
overflowY: "auto" as const,
|
|
2000
|
+
height: "100%",
|
|
2001
|
+
overflowAnchor: stickToBottom
|
|
2002
|
+
? ("auto" as const)
|
|
2003
|
+
: ("none" as const),
|
|
2004
|
+
},
|
|
1973
2005
|
},
|
|
1974
2006
|
inner: {
|
|
1975
2007
|
style: {
|