cogsbox-state 0.5.301 → 0.5.303
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 +446 -415
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +111 -59
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1803,7 +1803,7 @@ function createProxyHandler<T>(
|
|
|
1803
1803
|
options: VirtualViewOptions
|
|
1804
1804
|
): VirtualStateObjectResult<any[]> => {
|
|
1805
1805
|
const {
|
|
1806
|
-
itemHeight = 50,
|
|
1806
|
+
itemHeight = 50,
|
|
1807
1807
|
overscan = 5,
|
|
1808
1808
|
stickToBottom = false,
|
|
1809
1809
|
} = options;
|
|
@@ -1820,6 +1820,12 @@ function createProxyHandler<T>(
|
|
|
1820
1820
|
[]
|
|
1821
1821
|
);
|
|
1822
1822
|
|
|
1823
|
+
// Track scroll position
|
|
1824
|
+
const isAtBottomRef = useRef(stickToBottom);
|
|
1825
|
+
const previousTotalCountRef = useRef(0);
|
|
1826
|
+
const isInitialMountRef = useRef(true);
|
|
1827
|
+
const hasScrolledToBottomRef = useRef(false); // Track if we've done initial scroll
|
|
1828
|
+
|
|
1823
1829
|
useEffect(() => {
|
|
1824
1830
|
const unsubscribe = getGlobalStore
|
|
1825
1831
|
.getState()
|
|
@@ -1831,30 +1837,38 @@ function createProxyHandler<T>(
|
|
|
1831
1837
|
};
|
|
1832
1838
|
}, [stateKey, forceRecalculate]);
|
|
1833
1839
|
|
|
1834
|
-
const isAtBottomRef = useRef(stickToBottom);
|
|
1835
|
-
const isInitialMountRef = useRef(true);
|
|
1836
|
-
const previousTotalCountRef = useRef(0);
|
|
1837
|
-
|
|
1838
1840
|
const sourceArray = getGlobalStore().getNestedState(
|
|
1839
1841
|
stateKey,
|
|
1840
1842
|
path
|
|
1841
1843
|
) as any[];
|
|
1842
1844
|
const totalCount = sourceArray.length;
|
|
1843
1845
|
|
|
1844
|
-
const { totalHeight, positions } =
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
pos[
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1846
|
+
const { totalHeight, positions, allItemsMeasured } =
|
|
1847
|
+
useMemo(() => {
|
|
1848
|
+
const shadowArray =
|
|
1849
|
+
getGlobalStore
|
|
1850
|
+
.getState()
|
|
1851
|
+
.getShadowMetadata(stateKey, path) || [];
|
|
1852
|
+
let height = 0;
|
|
1853
|
+
const pos: number[] = [];
|
|
1854
|
+
let measured = true;
|
|
1855
|
+
|
|
1856
|
+
for (let i = 0; i < totalCount; i++) {
|
|
1857
|
+
pos[i] = height;
|
|
1858
|
+
const measuredHeight =
|
|
1859
|
+
shadowArray[i]?.virtualizer?.itemHeight;
|
|
1860
|
+
if (!measuredHeight && totalCount > 0) {
|
|
1861
|
+
measured = false;
|
|
1862
|
+
}
|
|
1863
|
+
height += measuredHeight || itemHeight;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
return {
|
|
1867
|
+
totalHeight: height,
|
|
1868
|
+
positions: pos,
|
|
1869
|
+
allItemsMeasured: measured,
|
|
1870
|
+
};
|
|
1871
|
+
}, [totalCount, stateKey, path, itemHeight, heightsVersion]);
|
|
1858
1872
|
|
|
1859
1873
|
const virtualState = useMemo(() => {
|
|
1860
1874
|
const start = Math.max(0, range.startIndex);
|
|
@@ -1868,46 +1882,46 @@ function createProxyHandler<T>(
|
|
|
1868
1882
|
...meta,
|
|
1869
1883
|
validIndices,
|
|
1870
1884
|
});
|
|
1871
|
-
}, [range.startIndex, range.endIndex, sourceArray]);
|
|
1885
|
+
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1872
1886
|
|
|
1873
1887
|
useLayoutEffect(() => {
|
|
1874
1888
|
const container = containerRef.current;
|
|
1875
1889
|
if (!container) return;
|
|
1876
1890
|
|
|
1891
|
+
const wasAtBottom = isAtBottomRef.current;
|
|
1877
1892
|
const listGrew = totalCount > previousTotalCountRef.current;
|
|
1878
1893
|
previousTotalCountRef.current = totalCount;
|
|
1879
1894
|
|
|
1880
|
-
const wasAtBottom = isAtBottomRef.current;
|
|
1881
|
-
|
|
1882
1895
|
const handleScroll = () => {
|
|
1883
1896
|
const { scrollTop, clientHeight, scrollHeight } = container;
|
|
1897
|
+
// Consider "at bottom" if within 10px
|
|
1884
1898
|
isAtBottomRef.current =
|
|
1885
|
-
scrollHeight - scrollTop - clientHeight <
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
high = mid - 1;
|
|
1897
|
-
}
|
|
1899
|
+
scrollHeight - scrollTop - clientHeight < 10;
|
|
1900
|
+
|
|
1901
|
+
// Binary search for start index
|
|
1902
|
+
let low = 0,
|
|
1903
|
+
high = totalCount - 1;
|
|
1904
|
+
while (low <= high) {
|
|
1905
|
+
const mid = Math.floor((low + high) / 2);
|
|
1906
|
+
if (positions[mid]! < scrollTop) {
|
|
1907
|
+
low = mid + 1;
|
|
1908
|
+
} else {
|
|
1909
|
+
high = mid - 1;
|
|
1898
1910
|
}
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1911
|
+
}
|
|
1912
|
+
const startIndex = Math.max(0, high - overscan);
|
|
1913
|
+
|
|
1914
|
+
// Find end index
|
|
1902
1915
|
let endIndex = startIndex;
|
|
1916
|
+
const visibleEnd = scrollTop + clientHeight;
|
|
1903
1917
|
while (
|
|
1904
1918
|
endIndex < totalCount &&
|
|
1905
|
-
positions[endIndex]! <
|
|
1919
|
+
positions[endIndex]! < visibleEnd
|
|
1906
1920
|
) {
|
|
1907
1921
|
endIndex++;
|
|
1908
1922
|
}
|
|
1909
|
-
startIndex = Math.max(0, startIndex - overscan);
|
|
1910
1923
|
endIndex = Math.min(totalCount, endIndex + overscan);
|
|
1924
|
+
|
|
1911
1925
|
setRange((prevRange) => {
|
|
1912
1926
|
if (
|
|
1913
1927
|
prevRange.startIndex !== startIndex ||
|
|
@@ -1922,29 +1936,64 @@ function createProxyHandler<T>(
|
|
|
1922
1936
|
container.addEventListener("scroll", handleScroll, {
|
|
1923
1937
|
passive: true,
|
|
1924
1938
|
});
|
|
1925
|
-
handleScroll();
|
|
1926
1939
|
|
|
1940
|
+
// Handle stick to bottom
|
|
1927
1941
|
if (stickToBottom) {
|
|
1928
|
-
if (
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1942
|
+
if (
|
|
1943
|
+
isInitialMountRef.current &&
|
|
1944
|
+
!hasScrolledToBottomRef.current
|
|
1945
|
+
) {
|
|
1946
|
+
// For initial mount, wait for items to be measured
|
|
1947
|
+
if (allItemsMeasured && totalCount > 0) {
|
|
1948
|
+
container.scrollTo({
|
|
1949
|
+
top: container.scrollHeight,
|
|
1950
|
+
behavior: "auto",
|
|
1951
|
+
});
|
|
1952
|
+
hasScrolledToBottomRef.current = true;
|
|
1953
|
+
isInitialMountRef.current = false;
|
|
1954
|
+
} else if (totalCount > 0) {
|
|
1955
|
+
// If not all measured yet, try again soon
|
|
1956
|
+
const retryTimer = setTimeout(() => {
|
|
1957
|
+
if (containerRef.current && isInitialMountRef.current) {
|
|
1958
|
+
containerRef.current.scrollTo({
|
|
1959
|
+
top: containerRef.current.scrollHeight,
|
|
1960
|
+
behavior: "auto",
|
|
1961
|
+
});
|
|
1962
|
+
hasScrolledToBottomRef.current = true;
|
|
1963
|
+
isInitialMountRef.current = false;
|
|
1964
|
+
}
|
|
1965
|
+
}, 100);
|
|
1966
|
+
return () => clearTimeout(retryTimer);
|
|
1967
|
+
}
|
|
1968
|
+
} else if (
|
|
1969
|
+
!isInitialMountRef.current &&
|
|
1970
|
+
wasAtBottom &&
|
|
1971
|
+
listGrew
|
|
1972
|
+
) {
|
|
1973
|
+
// New items added and we were at bottom - stay at bottom
|
|
1974
|
+
requestAnimationFrame(() => {
|
|
1975
|
+
container.scrollTo({
|
|
1976
|
+
top: container.scrollHeight,
|
|
1977
|
+
behavior: "smooth",
|
|
1978
|
+
});
|
|
1937
1979
|
});
|
|
1938
1980
|
}
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
if (totalCount > 0) {
|
|
1981
|
+
} else {
|
|
1942
1982
|
isInitialMountRef.current = false;
|
|
1943
1983
|
}
|
|
1944
1984
|
|
|
1985
|
+
// Run handleScroll once to set initial range
|
|
1986
|
+
handleScroll();
|
|
1987
|
+
|
|
1945
1988
|
return () =>
|
|
1946
1989
|
container.removeEventListener("scroll", handleScroll);
|
|
1947
|
-
}, [
|
|
1990
|
+
}, [
|
|
1991
|
+
totalCount,
|
|
1992
|
+
positions,
|
|
1993
|
+
overscan,
|
|
1994
|
+
stickToBottom,
|
|
1995
|
+
allItemsMeasured,
|
|
1996
|
+
]);
|
|
1948
1997
|
|
|
1949
1998
|
const scrollToBottom = useCallback(
|
|
1950
1999
|
(behavior: ScrollBehavior = "smooth") => {
|
|
@@ -1960,9 +2009,9 @@ function createProxyHandler<T>(
|
|
|
1960
2009
|
|
|
1961
2010
|
const scrollToIndex = useCallback(
|
|
1962
2011
|
(index: number, behavior: ScrollBehavior = "smooth") => {
|
|
1963
|
-
if (containerRef.current) {
|
|
2012
|
+
if (containerRef.current && positions[index] !== undefined) {
|
|
1964
2013
|
containerRef.current.scrollTo({
|
|
1965
|
-
top: positions[index]
|
|
2014
|
+
top: positions[index],
|
|
1966
2015
|
behavior,
|
|
1967
2016
|
});
|
|
1968
2017
|
}
|
|
@@ -1973,10 +2022,13 @@ function createProxyHandler<T>(
|
|
|
1973
2022
|
const virtualizerProps = {
|
|
1974
2023
|
outer: {
|
|
1975
2024
|
ref: containerRef,
|
|
1976
|
-
style: { overflowY: "auto", height: "100%" },
|
|
2025
|
+
style: { overflowY: "auto" as const, height: "100%" },
|
|
1977
2026
|
},
|
|
1978
2027
|
inner: {
|
|
1979
|
-
style: {
|
|
2028
|
+
style: {
|
|
2029
|
+
height: `${totalHeight}px`,
|
|
2030
|
+
position: "relative" as const,
|
|
2031
|
+
},
|
|
1980
2032
|
},
|
|
1981
2033
|
list: {
|
|
1982
2034
|
style: {
|
|
@@ -1987,7 +2039,7 @@ function createProxyHandler<T>(
|
|
|
1987
2039
|
|
|
1988
2040
|
return {
|
|
1989
2041
|
virtualState,
|
|
1990
|
-
virtualizerProps
|
|
2042
|
+
virtualizerProps,
|
|
1991
2043
|
scrollToBottom,
|
|
1992
2044
|
scrollToIndex,
|
|
1993
2045
|
};
|