cogsbox-state 0.5.294 → 0.5.296
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 +418 -412
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +62 -72
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1814,20 +1814,6 @@ function createProxyHandler<T>(
|
|
|
1814
1814
|
endIndex: 10,
|
|
1815
1815
|
});
|
|
1816
1816
|
|
|
1817
|
-
// --- State Tracking Refs for Stability ---
|
|
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
|
|
1831
1817
|
const getItemHeight = useCallback(
|
|
1832
1818
|
(index: number): number => {
|
|
1833
1819
|
const metadata = getGlobalStore
|
|
@@ -1838,20 +1824,28 @@ function createProxyHandler<T>(
|
|
|
1838
1824
|
[itemHeight, stateKey, path]
|
|
1839
1825
|
);
|
|
1840
1826
|
|
|
1841
|
-
|
|
1827
|
+
const isAtBottomRef = useRef(stickToBottom);
|
|
1828
|
+
const previousTotalCountRef = useRef(0);
|
|
1829
|
+
const isInitialMountRef = useRef(true);
|
|
1830
|
+
|
|
1831
|
+
const sourceArray = getGlobalStore().getNestedState(
|
|
1832
|
+
stateKey,
|
|
1833
|
+
path
|
|
1834
|
+
) as any[];
|
|
1835
|
+
const totalCount = sourceArray.length;
|
|
1836
|
+
|
|
1842
1837
|
const { totalHeight, positions } = useMemo(() => {
|
|
1843
|
-
let
|
|
1838
|
+
let height = 0;
|
|
1844
1839
|
const pos: number[] = [];
|
|
1845
1840
|
for (let i = 0; i < totalCount; i++) {
|
|
1846
|
-
pos[i] =
|
|
1847
|
-
|
|
1841
|
+
pos[i] = height;
|
|
1842
|
+
height += getItemHeight(i);
|
|
1843
|
+
console.log("height", getItemHeight(i), height);
|
|
1848
1844
|
}
|
|
1849
|
-
|
|
1850
|
-
console.log("totalHeight", currentHeight);
|
|
1851
|
-
return { totalHeight: currentHeight, positions: pos };
|
|
1845
|
+
return { totalHeight: height, positions: pos };
|
|
1852
1846
|
}, [totalCount, getItemHeight]);
|
|
1853
1847
|
|
|
1854
|
-
// This is
|
|
1848
|
+
// This logic is IDENTICAL to your original code.
|
|
1855
1849
|
const virtualState = useMemo(() => {
|
|
1856
1850
|
const start = Math.max(0, range.startIndex);
|
|
1857
1851
|
const end = Math.min(totalCount, range.endIndex);
|
|
@@ -1866,55 +1860,38 @@ function createProxyHandler<T>(
|
|
|
1866
1860
|
});
|
|
1867
1861
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1868
1862
|
|
|
1869
|
-
//
|
|
1870
|
-
// useLayoutEffect runs after DOM mutations but before the browser paints.
|
|
1871
|
-
// This is the perfect place to correct scroll positions.
|
|
1863
|
+
// This useLayoutEffect is from your original code.
|
|
1872
1864
|
useLayoutEffect(() => {
|
|
1873
1865
|
const container = containerRef.current;
|
|
1874
1866
|
if (!container) return;
|
|
1875
1867
|
|
|
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;
|
|
1868
|
+
const wasAtBottom = isAtBottomRef.current;
|
|
1869
|
+
const listGrew = totalCount > previousTotalCountRef.current;
|
|
1870
|
+
previousTotalCountRef.current = totalCount;
|
|
1901
1871
|
|
|
1902
1872
|
const handleScroll = () => {
|
|
1903
|
-
if (!container) return;
|
|
1904
1873
|
const { scrollTop, clientHeight, scrollHeight } = container;
|
|
1905
|
-
// Update "is at bottom" status on every scroll
|
|
1906
1874
|
isAtBottomRef.current =
|
|
1907
1875
|
scrollHeight - scrollTop - clientHeight < 10;
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
//
|
|
1911
|
-
let
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1876
|
+
|
|
1877
|
+
// --- THE ROBUST FIX: Binary search to find the start index ---
|
|
1878
|
+
// This is extremely fast and correctly handles all scroll positions.
|
|
1879
|
+
let search = (list: number[], value: number) => {
|
|
1880
|
+
let low = 0;
|
|
1881
|
+
let high = list.length - 1;
|
|
1882
|
+
while (low <= high) {
|
|
1883
|
+
const mid = Math.floor((low + high) / 2);
|
|
1884
|
+
const midValue = list[mid]!;
|
|
1885
|
+
if (midValue < value) {
|
|
1886
|
+
low = mid + 1;
|
|
1887
|
+
} else {
|
|
1888
|
+
high = mid - 1;
|
|
1889
|
+
}
|
|
1916
1890
|
}
|
|
1917
|
-
|
|
1891
|
+
return low;
|
|
1892
|
+
};
|
|
1893
|
+
|
|
1894
|
+
let startIndex = search(positions, scrollTop);
|
|
1918
1895
|
|
|
1919
1896
|
let endIndex = startIndex;
|
|
1920
1897
|
while (
|
|
@@ -1926,7 +1903,7 @@ function createProxyHandler<T>(
|
|
|
1926
1903
|
|
|
1927
1904
|
startIndex = Math.max(0, startIndex - overscan);
|
|
1928
1905
|
endIndex = Math.min(totalCount, endIndex + overscan);
|
|
1929
|
-
|
|
1906
|
+
console.log("startIndex", startIndex, "endIndex", endIndex);
|
|
1930
1907
|
setRange((prevRange) => {
|
|
1931
1908
|
if (
|
|
1932
1909
|
prevRange.startIndex !== startIndex ||
|
|
@@ -1938,20 +1915,33 @@ function createProxyHandler<T>(
|
|
|
1938
1915
|
});
|
|
1939
1916
|
};
|
|
1940
1917
|
|
|
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
1918
|
container.addEventListener("scroll", handleScroll, {
|
|
1948
1919
|
passive: true,
|
|
1949
1920
|
});
|
|
1950
|
-
|
|
1921
|
+
|
|
1922
|
+
// This stickToBottom logic is IDENTICAL to your original.
|
|
1923
|
+
if (stickToBottom) {
|
|
1924
|
+
if (isInitialMountRef.current) {
|
|
1925
|
+
container.scrollTo({
|
|
1926
|
+
top: container.scrollHeight,
|
|
1927
|
+
behavior: "auto",
|
|
1928
|
+
});
|
|
1929
|
+
} else if (wasAtBottom && listGrew) {
|
|
1930
|
+
requestAnimationFrame(() => {
|
|
1931
|
+
container.scrollTo({
|
|
1932
|
+
top: container.scrollHeight,
|
|
1933
|
+
behavior: "smooth",
|
|
1934
|
+
});
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
isInitialMountRef.current = false;
|
|
1940
|
+
handleScroll();
|
|
1951
1941
|
|
|
1952
1942
|
return () =>
|
|
1953
1943
|
container.removeEventListener("scroll", handleScroll);
|
|
1954
|
-
}, [totalCount, overscan, positions]);
|
|
1944
|
+
}, [totalCount, overscan, stickToBottom, positions]);
|
|
1955
1945
|
|
|
1956
1946
|
const scrollToBottom = useCallback(
|
|
1957
1947
|
(behavior: ScrollBehavior = "smooth") => {
|
|
@@ -1967,9 +1957,9 @@ function createProxyHandler<T>(
|
|
|
1967
1957
|
|
|
1968
1958
|
const scrollToIndex = useCallback(
|
|
1969
1959
|
(index: number, behavior: ScrollBehavior = "smooth") => {
|
|
1970
|
-
if (containerRef.current
|
|
1960
|
+
if (containerRef.current) {
|
|
1971
1961
|
containerRef.current.scrollTo({
|
|
1972
|
-
top: positions[index],
|
|
1962
|
+
top: positions[index] || 0,
|
|
1973
1963
|
behavior,
|
|
1974
1964
|
});
|
|
1975
1965
|
}
|