cogsbox-state 0.5.294 → 0.5.295
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 +427 -408
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +71 -69
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1802,6 +1802,7 @@ function createProxyHandler<T>(
|
|
|
1802
1802
|
return (
|
|
1803
1803
|
options: VirtualViewOptions
|
|
1804
1804
|
): VirtualStateObjectResult<any[]> => {
|
|
1805
|
+
// --- CHANGE 1: itemHeight is now optional, with a default fallback.
|
|
1805
1806
|
const {
|
|
1806
1807
|
itemHeight = 50, // Default/estimated height
|
|
1807
1808
|
overscan = 5,
|
|
@@ -1814,20 +1815,7 @@ function createProxyHandler<T>(
|
|
|
1814
1815
|
endIndex: 10,
|
|
1815
1816
|
});
|
|
1816
1817
|
|
|
1817
|
-
// ---
|
|
1818
|
-
const isAtBottomRef = useRef(stickToBottom);
|
|
1819
|
-
// Store the scroll position before a new item is added
|
|
1820
|
-
const scrollOffsetRef = useRef(0);
|
|
1821
|
-
// Ref to track if the list has grown, to trigger scroll correction
|
|
1822
|
-
const listGrewRef = useRef(false);
|
|
1823
|
-
|
|
1824
|
-
const sourceArray = getGlobalStore().getNestedState(
|
|
1825
|
-
stateKey,
|
|
1826
|
-
path
|
|
1827
|
-
) as any[];
|
|
1828
|
-
const totalCount = sourceArray.length;
|
|
1829
|
-
|
|
1830
|
-
// Helper to get measured heights or the default
|
|
1818
|
+
// --- CHANGE 2: Add a helper to get the real height of each item. ---
|
|
1831
1819
|
const getItemHeight = useCallback(
|
|
1832
1820
|
(index: number): number => {
|
|
1833
1821
|
const metadata = getGlobalStore
|
|
@@ -1838,20 +1826,30 @@ function createProxyHandler<T>(
|
|
|
1838
1826
|
[itemHeight, stateKey, path]
|
|
1839
1827
|
);
|
|
1840
1828
|
|
|
1841
|
-
//
|
|
1829
|
+
// --- These refs are from your original code. NO CHANGE. ---
|
|
1830
|
+
const isAtBottomRef = useRef(stickToBottom);
|
|
1831
|
+
const previousTotalCountRef = useRef(0);
|
|
1832
|
+
const isInitialMountRef = useRef(true);
|
|
1833
|
+
|
|
1834
|
+
const sourceArray = getGlobalStore().getNestedState(
|
|
1835
|
+
stateKey,
|
|
1836
|
+
path
|
|
1837
|
+
) as any[];
|
|
1838
|
+
const totalCount = sourceArray.length;
|
|
1839
|
+
|
|
1840
|
+
// --- CHANGE 3: Calculate the total height and position of each item. ---
|
|
1841
|
+
// This is the only new block of logic required.
|
|
1842
1842
|
const { totalHeight, positions } = useMemo(() => {
|
|
1843
|
-
let
|
|
1843
|
+
let height = 0;
|
|
1844
1844
|
const pos: number[] = [];
|
|
1845
1845
|
for (let i = 0; i < totalCount; i++) {
|
|
1846
|
-
pos[i] =
|
|
1847
|
-
|
|
1846
|
+
pos[i] = height;
|
|
1847
|
+
height += getItemHeight(i);
|
|
1848
1848
|
}
|
|
1849
|
-
|
|
1850
|
-
console.log("totalHeight", currentHeight);
|
|
1851
|
-
return { totalHeight: currentHeight, positions: pos };
|
|
1849
|
+
return { totalHeight: height, positions: pos };
|
|
1852
1850
|
}, [totalCount, getItemHeight]);
|
|
1853
1851
|
|
|
1854
|
-
//
|
|
1852
|
+
// --- The virtualState logic is IDENTICAL to your original. NO CHANGE. ---
|
|
1855
1853
|
const virtualState = useMemo(() => {
|
|
1856
1854
|
const start = Math.max(0, range.startIndex);
|
|
1857
1855
|
const end = Math.min(totalCount, range.endIndex);
|
|
@@ -1866,49 +1864,25 @@ function createProxyHandler<T>(
|
|
|
1866
1864
|
});
|
|
1867
1865
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1868
1866
|
|
|
1869
|
-
// ---
|
|
1870
|
-
//
|
|
1871
|
-
// This is the perfect place to correct scroll positions.
|
|
1867
|
+
// --- This useLayoutEffect is from your original code. ---
|
|
1868
|
+
// --- We only change the math inside handleScroll. ---
|
|
1872
1869
|
useLayoutEffect(() => {
|
|
1873
1870
|
const container = containerRef.current;
|
|
1874
1871
|
if (!container) return;
|
|
1875
1872
|
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
listGrewRef.current = false; // Reset the flag
|
|
1880
|
-
|
|
1881
|
-
if (isAtBottomRef.current) {
|
|
1882
|
-
// If we were at the bottom, stay at the bottom.
|
|
1883
|
-
// This is the fix for the auto-scroll issue.
|
|
1884
|
-
container.scrollTop = container.scrollHeight;
|
|
1885
|
-
} else {
|
|
1886
|
-
// If we were in the middle, restore the previous scroll position
|
|
1887
|
-
// plus the height of the content that was added above us.
|
|
1888
|
-
// This is an advanced case, but for now, let's keep it simple
|
|
1889
|
-
// as most use-cases are for chat-like views. For a simple list,
|
|
1890
|
-
// just staying at the bottom is the main goal.
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
}, [totalHeight]); // This effect runs whenever the total height changes
|
|
1894
|
-
|
|
1895
|
-
useEffect(() => {
|
|
1896
|
-
const container = containerRef.current;
|
|
1897
|
-
if (!container) return;
|
|
1898
|
-
|
|
1899
|
-
// Track the previous total count to detect when new items are added
|
|
1900
|
-
let previousTotalCount = totalCount;
|
|
1873
|
+
const wasAtBottom = isAtBottomRef.current;
|
|
1874
|
+
const listGrew = totalCount > previousTotalCountRef.current;
|
|
1875
|
+
previousTotalCountRef.current = totalCount;
|
|
1901
1876
|
|
|
1902
1877
|
const handleScroll = () => {
|
|
1903
|
-
if (!container) return;
|
|
1904
1878
|
const { scrollTop, clientHeight, scrollHeight } = container;
|
|
1905
|
-
// Update "is at bottom" status on every scroll
|
|
1906
1879
|
isAtBottomRef.current =
|
|
1907
1880
|
scrollHeight - scrollTop - clientHeight < 10;
|
|
1908
|
-
scrollOffsetRef.current = scrollTop;
|
|
1909
1881
|
|
|
1910
|
-
//
|
|
1882
|
+
// --- CHANGE 4: The math to find the start and end index. ---
|
|
1883
|
+
// This replaces `scrollTop / itemHeight` with a more accurate search.
|
|
1911
1884
|
let startIndex = 0;
|
|
1885
|
+
// Find the first item whose top position is past the scroll top.
|
|
1912
1886
|
for (let i = 0; i < positions.length; i++) {
|
|
1913
1887
|
if (positions[i]! >= scrollTop) {
|
|
1914
1888
|
startIndex = i;
|
|
@@ -1917,6 +1891,7 @@ function createProxyHandler<T>(
|
|
|
1917
1891
|
}
|
|
1918
1892
|
|
|
1919
1893
|
let endIndex = startIndex;
|
|
1894
|
+
// Find the first item whose top position is past the bottom of the viewport.
|
|
1920
1895
|
while (
|
|
1921
1896
|
endIndex < totalCount &&
|
|
1922
1897
|
positions[endIndex]! < scrollTop + clientHeight
|
|
@@ -1924,34 +1899,56 @@ function createProxyHandler<T>(
|
|
|
1924
1899
|
endIndex++;
|
|
1925
1900
|
}
|
|
1926
1901
|
|
|
1902
|
+
// Apply overscan, identical to your original code.
|
|
1927
1903
|
startIndex = Math.max(0, startIndex - overscan);
|
|
1928
1904
|
endIndex = Math.min(totalCount, endIndex + overscan);
|
|
1929
|
-
|
|
1905
|
+
console.log(
|
|
1906
|
+
"startIndex",
|
|
1907
|
+
startIndex,
|
|
1908
|
+
"endIndex",
|
|
1909
|
+
endIndex,
|
|
1910
|
+
"totalHeight",
|
|
1911
|
+
totalHeight
|
|
1912
|
+
);
|
|
1930
1913
|
setRange((prevRange) => {
|
|
1931
1914
|
if (
|
|
1932
1915
|
prevRange.startIndex !== startIndex ||
|
|
1933
1916
|
prevRange.endIndex !== endIndex
|
|
1934
1917
|
) {
|
|
1935
|
-
return { startIndex, endIndex };
|
|
1918
|
+
return { startIndex: startIndex, endIndex: endIndex };
|
|
1936
1919
|
}
|
|
1937
1920
|
return prevRange;
|
|
1938
1921
|
});
|
|
1939
1922
|
};
|
|
1940
1923
|
|
|
1941
|
-
// Check if the list has grown *before* the next render cycle
|
|
1942
|
-
if (totalCount > previousTotalCount) {
|
|
1943
|
-
listGrewRef.current = true;
|
|
1944
|
-
}
|
|
1945
|
-
previousTotalCount = totalCount;
|
|
1946
|
-
|
|
1947
1924
|
container.addEventListener("scroll", handleScroll, {
|
|
1948
1925
|
passive: true,
|
|
1949
1926
|
});
|
|
1950
|
-
|
|
1927
|
+
|
|
1928
|
+
// --- This stickToBottom logic is IDENTICAL to your original. NO CHANGE. ---
|
|
1929
|
+
if (stickToBottom) {
|
|
1930
|
+
if (isInitialMountRef.current) {
|
|
1931
|
+
container.scrollTo({
|
|
1932
|
+
top: container.scrollHeight,
|
|
1933
|
+
behavior: "auto",
|
|
1934
|
+
});
|
|
1935
|
+
} else if (wasAtBottom && listGrew) {
|
|
1936
|
+
requestAnimationFrame(() => {
|
|
1937
|
+
container.scrollTo({
|
|
1938
|
+
top: container.scrollHeight,
|
|
1939
|
+
behavior: "smooth",
|
|
1940
|
+
});
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
isInitialMountRef.current = false;
|
|
1946
|
+
handleScroll();
|
|
1951
1947
|
|
|
1952
1948
|
return () =>
|
|
1953
1949
|
container.removeEventListener("scroll", handleScroll);
|
|
1954
|
-
|
|
1950
|
+
// --- We swap `itemHeight` for `positions` in the dependency array. ---
|
|
1951
|
+
}, [totalCount, overscan, stickToBottom, positions]);
|
|
1955
1952
|
|
|
1956
1953
|
const scrollToBottom = useCallback(
|
|
1957
1954
|
(behavior: ScrollBehavior = "smooth") => {
|
|
@@ -1965,29 +1962,34 @@ function createProxyHandler<T>(
|
|
|
1965
1962
|
[]
|
|
1966
1963
|
);
|
|
1967
1964
|
|
|
1965
|
+
// --- CHANGE 5: Update scrollToIndex to use the positions array. ---
|
|
1968
1966
|
const scrollToIndex = useCallback(
|
|
1969
1967
|
(index: number, behavior: ScrollBehavior = "smooth") => {
|
|
1970
|
-
if (containerRef.current
|
|
1968
|
+
if (containerRef.current) {
|
|
1971
1969
|
containerRef.current.scrollTo({
|
|
1972
|
-
top: positions[index],
|
|
1970
|
+
top: positions[index] || 0, // Use the calculated position
|
|
1973
1971
|
behavior,
|
|
1974
1972
|
});
|
|
1975
1973
|
}
|
|
1976
1974
|
},
|
|
1977
|
-
[positions]
|
|
1975
|
+
[positions] // Dependency is now `positions`
|
|
1978
1976
|
);
|
|
1979
1977
|
|
|
1978
|
+
// --- CHANGE 6: Update virtualizer props to use dynamic values. ---
|
|
1980
1979
|
const virtualizerProps = {
|
|
1981
1980
|
outer: {
|
|
1982
1981
|
ref: containerRef,
|
|
1983
1982
|
style: { overflowY: "auto", height: "100%" },
|
|
1984
1983
|
},
|
|
1985
1984
|
inner: {
|
|
1986
|
-
style: {
|
|
1985
|
+
style: {
|
|
1986
|
+
height: `${totalHeight}px`, // Use calculated dynamic height
|
|
1987
|
+
position: "relative",
|
|
1988
|
+
},
|
|
1987
1989
|
},
|
|
1988
1990
|
list: {
|
|
1989
1991
|
style: {
|
|
1990
|
-
transform: `translateY(${positions[range.startIndex] || 0}px)`,
|
|
1992
|
+
transform: `translateY(${positions[range.startIndex] || 0}px)`, // Use calculated position
|
|
1991
1993
|
},
|
|
1992
1994
|
},
|
|
1993
1995
|
};
|