cogsbox-state 0.5.283 → 0.5.284
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 +473 -452
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +77 -39
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1802,7 +1802,7 @@ function createProxyHandler<T>(
|
|
|
1802
1802
|
options: VirtualViewOptions
|
|
1803
1803
|
): VirtualStateObjectResult<any[]> => {
|
|
1804
1804
|
const {
|
|
1805
|
-
itemHeight,
|
|
1805
|
+
itemHeight = 50, // Now optional with default
|
|
1806
1806
|
overscan = 5,
|
|
1807
1807
|
stickToBottom = false,
|
|
1808
1808
|
} = options;
|
|
@@ -1812,16 +1812,38 @@ function createProxyHandler<T>(
|
|
|
1812
1812
|
startIndex: 0,
|
|
1813
1813
|
endIndex: 10,
|
|
1814
1814
|
});
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1815
|
+
|
|
1816
|
+
// Get item height from shadow state or fall back to default
|
|
1817
|
+
const getItemHeight = useCallback(
|
|
1818
|
+
(index: number) => {
|
|
1819
|
+
const metadata = getGlobalStore
|
|
1820
|
+
.getState()
|
|
1821
|
+
.getShadowMetadata(stateKey, [...path, index.toString()]);
|
|
1822
|
+
return metadata?.virtualizer?.itemHeight || itemHeight;
|
|
1823
|
+
},
|
|
1824
|
+
[itemHeight]
|
|
1825
|
+
);
|
|
1826
|
+
|
|
1827
|
+
// Calculate total height and item positions
|
|
1828
|
+
const calculateHeights = useCallback(() => {
|
|
1829
|
+
const sourceArray = getGlobalStore().getNestedState(
|
|
1830
|
+
stateKey,
|
|
1831
|
+
path
|
|
1832
|
+
) as any[];
|
|
1833
|
+
|
|
1834
|
+
let totalHeight = 0;
|
|
1835
|
+
const positions: number[] = [];
|
|
1836
|
+
|
|
1837
|
+
for (let i = 0; i < sourceArray.length; i++) {
|
|
1838
|
+
positions[i] = totalHeight;
|
|
1839
|
+
totalHeight += getItemHeight(i);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
return { totalHeight, positions };
|
|
1843
|
+
}, [getItemHeight]);
|
|
1844
|
+
|
|
1822
1845
|
const isAtBottomRef = useRef(stickToBottom);
|
|
1823
1846
|
const previousTotalCountRef = useRef(0);
|
|
1824
|
-
// NEW: Ref to explicitly track if this is the component's first render cycle.
|
|
1825
1847
|
const isInitialMountRef = useRef(true);
|
|
1826
1848
|
|
|
1827
1849
|
const sourceArray = getGlobalStore().getNestedState(
|
|
@@ -1852,25 +1874,43 @@ function createProxyHandler<T>(
|
|
|
1852
1874
|
const listGrew = totalCount > previousTotalCountRef.current;
|
|
1853
1875
|
previousTotalCountRef.current = totalCount;
|
|
1854
1876
|
|
|
1877
|
+
const { totalHeight, positions } = calculateHeights();
|
|
1878
|
+
|
|
1855
1879
|
const handleScroll = () => {
|
|
1856
1880
|
const { scrollTop, clientHeight, scrollHeight } = container;
|
|
1857
1881
|
isAtBottomRef.current =
|
|
1858
1882
|
scrollHeight - scrollTop - clientHeight < 10;
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1883
|
+
|
|
1884
|
+
// Find start index using binary search
|
|
1885
|
+
let start = 0;
|
|
1886
|
+
let end = positions.length - 1;
|
|
1887
|
+
while (start < end) {
|
|
1888
|
+
const mid = Math.floor((start + end) / 2);
|
|
1889
|
+
if (positions[mid]! < scrollTop) {
|
|
1890
|
+
start = mid + 1;
|
|
1891
|
+
} else {
|
|
1892
|
+
end = mid;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
start = Math.max(0, start - overscan);
|
|
1896
|
+
|
|
1897
|
+
// Find end index
|
|
1898
|
+
const visibleEnd = scrollTop + clientHeight;
|
|
1899
|
+
let endIndex = start;
|
|
1900
|
+
while (
|
|
1901
|
+
endIndex < positions.length &&
|
|
1902
|
+
positions[endIndex]! < visibleEnd
|
|
1903
|
+
) {
|
|
1904
|
+
endIndex++;
|
|
1905
|
+
}
|
|
1906
|
+
endIndex = Math.min(totalCount, endIndex + overscan);
|
|
1907
|
+
|
|
1868
1908
|
setRange((prevRange) => {
|
|
1869
1909
|
if (
|
|
1870
1910
|
prevRange.startIndex !== start ||
|
|
1871
|
-
prevRange.endIndex !==
|
|
1911
|
+
prevRange.endIndex !== endIndex
|
|
1872
1912
|
) {
|
|
1873
|
-
return { startIndex: start, endIndex:
|
|
1913
|
+
return { startIndex: start, endIndex: endIndex };
|
|
1874
1914
|
}
|
|
1875
1915
|
return prevRange;
|
|
1876
1916
|
});
|
|
@@ -1880,19 +1920,13 @@ function createProxyHandler<T>(
|
|
|
1880
1920
|
passive: true,
|
|
1881
1921
|
});
|
|
1882
1922
|
|
|
1883
|
-
// --- THE CORRECTED DECISION LOGIC ---
|
|
1884
1923
|
if (stickToBottom) {
|
|
1885
1924
|
if (isInitialMountRef.current) {
|
|
1886
|
-
// SCENARIO 1: First render of the component.
|
|
1887
|
-
// Go to the bottom unconditionally. Use `auto` scroll for an instant jump.
|
|
1888
1925
|
container.scrollTo({
|
|
1889
1926
|
top: container.scrollHeight,
|
|
1890
1927
|
behavior: "auto",
|
|
1891
1928
|
});
|
|
1892
1929
|
} else if (wasAtBottom && listGrew) {
|
|
1893
|
-
// SCENARIO 2: Subsequent renders (new messages arrive).
|
|
1894
|
-
// Only scroll if the user was already at the bottom.
|
|
1895
|
-
// Use `smooth` for a nice animated scroll for new messages.
|
|
1896
1930
|
requestAnimationFrame(() => {
|
|
1897
1931
|
container.scrollTo({
|
|
1898
1932
|
top: container.scrollHeight,
|
|
@@ -1902,15 +1936,12 @@ function createProxyHandler<T>(
|
|
|
1902
1936
|
}
|
|
1903
1937
|
}
|
|
1904
1938
|
|
|
1905
|
-
// After the logic runs, it's no longer the initial mount.
|
|
1906
1939
|
isInitialMountRef.current = false;
|
|
1907
|
-
|
|
1908
|
-
// Always run handleScroll once to set the initial visible window.
|
|
1909
1940
|
handleScroll();
|
|
1910
1941
|
|
|
1911
1942
|
return () =>
|
|
1912
1943
|
container.removeEventListener("scroll", handleScroll);
|
|
1913
|
-
}, [totalCount,
|
|
1944
|
+
}, [totalCount, calculateHeights, overscan, stickToBottom]);
|
|
1914
1945
|
|
|
1915
1946
|
const scrollToBottom = useCallback(
|
|
1916
1947
|
(behavior: ScrollBehavior = "smooth") => {
|
|
@@ -1927,37 +1958,44 @@ function createProxyHandler<T>(
|
|
|
1927
1958
|
const scrollToIndex = useCallback(
|
|
1928
1959
|
(index: number, behavior: ScrollBehavior = "smooth") => {
|
|
1929
1960
|
if (containerRef.current) {
|
|
1961
|
+
const { positions } = calculateHeights();
|
|
1930
1962
|
containerRef.current.scrollTo({
|
|
1931
|
-
top: index
|
|
1963
|
+
top: positions[index] || 0,
|
|
1932
1964
|
behavior,
|
|
1933
1965
|
});
|
|
1934
1966
|
}
|
|
1935
1967
|
},
|
|
1936
|
-
[
|
|
1968
|
+
[calculateHeights]
|
|
1937
1969
|
);
|
|
1938
1970
|
|
|
1939
|
-
//
|
|
1971
|
+
// Calculate actual heights for rendering
|
|
1972
|
+
const {
|
|
1973
|
+
totalHeight: totalHeightForRender,
|
|
1974
|
+
positions: positionsForRender,
|
|
1975
|
+
} = calculateHeights();
|
|
1976
|
+
const offsetY = positionsForRender[range.startIndex] || 0;
|
|
1977
|
+
|
|
1940
1978
|
const virtualizerProps = {
|
|
1941
1979
|
outer: {
|
|
1942
1980
|
ref: containerRef,
|
|
1943
|
-
style: { overflowY: "auto", height: "100%" },
|
|
1981
|
+
style: { overflowY: "auto", height: "100%" } as CSSProperties,
|
|
1944
1982
|
},
|
|
1945
1983
|
inner: {
|
|
1946
1984
|
style: {
|
|
1947
|
-
height: `${
|
|
1985
|
+
height: `${totalHeightForRender}px`,
|
|
1948
1986
|
position: "relative",
|
|
1949
|
-
},
|
|
1987
|
+
} as CSSProperties,
|
|
1950
1988
|
},
|
|
1951
1989
|
list: {
|
|
1952
1990
|
style: {
|
|
1953
|
-
transform: `translateY(${
|
|
1954
|
-
},
|
|
1991
|
+
transform: `translateY(${offsetY}px)`,
|
|
1992
|
+
} as CSSProperties,
|
|
1955
1993
|
},
|
|
1956
1994
|
};
|
|
1957
1995
|
|
|
1958
1996
|
return {
|
|
1959
1997
|
virtualState,
|
|
1960
|
-
virtualizerProps
|
|
1998
|
+
virtualizerProps,
|
|
1961
1999
|
scrollToBottom,
|
|
1962
2000
|
scrollToIndex,
|
|
1963
2001
|
};
|