cogsbox-state 0.5.346 → 0.5.349
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 +512 -506
- package/dist/CogsState.jsx.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +70 -79
package/package.json
CHANGED
package/src/CogsState.tsx
CHANGED
|
@@ -1813,7 +1813,12 @@ function createProxyHandler<T>(
|
|
|
1813
1813
|
startIndex: 0,
|
|
1814
1814
|
endIndex: 10,
|
|
1815
1815
|
});
|
|
1816
|
+
const sourceArray = getGlobalStore().getNestedState(
|
|
1817
|
+
stateKey,
|
|
1818
|
+
path
|
|
1819
|
+
) as any[];
|
|
1816
1820
|
const isLockedToBottomRef = useRef(stickToBottom);
|
|
1821
|
+
const prevTotalCountRef = useRef(sourceArray.length);
|
|
1817
1822
|
|
|
1818
1823
|
const [shadowUpdateTrigger, setShadowUpdateTrigger] = useState(0);
|
|
1819
1824
|
|
|
@@ -1826,10 +1831,6 @@ function createProxyHandler<T>(
|
|
|
1826
1831
|
return unsubscribe;
|
|
1827
1832
|
}, [stateKey]);
|
|
1828
1833
|
|
|
1829
|
-
const sourceArray = getGlobalStore().getNestedState(
|
|
1830
|
-
stateKey,
|
|
1831
|
-
path
|
|
1832
|
-
) as any[];
|
|
1833
1834
|
const totalCount = sourceArray.length;
|
|
1834
1835
|
|
|
1835
1836
|
const { totalHeight, positions } = useMemo(() => {
|
|
@@ -1867,85 +1868,71 @@ function createProxyHandler<T>(
|
|
|
1867
1868
|
});
|
|
1868
1869
|
}, [range.startIndex, range.endIndex, sourceArray, totalCount]);
|
|
1869
1870
|
|
|
1870
|
-
//
|
|
1871
|
-
// This effect is the entry point. It triggers when new items are added.
|
|
1871
|
+
// The one and only layout effect.
|
|
1872
1872
|
useLayoutEffect(() => {
|
|
1873
1873
|
const container = containerRef.current;
|
|
1874
|
-
|
|
1875
|
-
if (
|
|
1876
|
-
!container ||
|
|
1877
|
-
!isLockedToBottomRef.current ||
|
|
1878
|
-
totalCount === 0
|
|
1879
|
-
) {
|
|
1880
|
-
return;
|
|
1881
|
-
}
|
|
1874
|
+
if (!container) return;
|
|
1882
1875
|
|
|
1883
|
-
|
|
1884
|
-
console.log("ALGORITHM: Starting...");
|
|
1885
|
-
const visibleCount = 10;
|
|
1886
|
-
setRange({
|
|
1887
|
-
startIndex: Math.max(0, totalCount - visibleCount - overscan),
|
|
1888
|
-
endIndex: totalCount,
|
|
1889
|
-
});
|
|
1876
|
+
const hasNewItems = totalCount > prevTotalCountRef.current;
|
|
1890
1877
|
|
|
1891
|
-
//
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
const
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
clearInterval(intervalId); // Stop the loop.
|
|
1878
|
+
// This function is now ALWAYS fresh.
|
|
1879
|
+
const updateVirtualRange = () => {
|
|
1880
|
+
const { scrollTop, clientHeight } = container;
|
|
1881
|
+
let low = 0,
|
|
1882
|
+
high = totalCount - 1;
|
|
1883
|
+
while (low <= high) {
|
|
1884
|
+
const mid = Math.floor((low + high) / 2);
|
|
1885
|
+
if (positions[mid]! < scrollTop) low = mid + 1;
|
|
1886
|
+
else high = mid - 1;
|
|
1887
|
+
}
|
|
1888
|
+
const startIndex = Math.max(0, high - overscan);
|
|
1889
|
+
let endIndex = startIndex;
|
|
1890
|
+
const visibleEnd = scrollTop + clientHeight;
|
|
1891
|
+
while (
|
|
1892
|
+
endIndex < totalCount &&
|
|
1893
|
+
positions[endIndex]! < visibleEnd
|
|
1894
|
+
) {
|
|
1895
|
+
endIndex++;
|
|
1896
|
+
}
|
|
1897
|
+
setRange({
|
|
1898
|
+
startIndex,
|
|
1899
|
+
endIndex: Math.min(totalCount, endIndex + overscan),
|
|
1900
|
+
});
|
|
1901
|
+
};
|
|
1916
1902
|
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1903
|
+
// --- YOUR SCROLLING LOGIC ---
|
|
1904
|
+
// It only runs if we have new items and are locked to the bottom.
|
|
1905
|
+
if (hasNewItems && isLockedToBottomRef.current) {
|
|
1906
|
+
// STEP 1: Set range to the end to start measuring.
|
|
1907
|
+
setRange({
|
|
1908
|
+
startIndex: Math.max(0, totalCount - 10 - overscan),
|
|
1909
|
+
endIndex: totalCount,
|
|
1910
|
+
});
|
|
1911
|
+
|
|
1912
|
+
// STEP 2: Start the LOOP.
|
|
1913
|
+
const intervalId = setInterval(() => {
|
|
1914
|
+
const lastItemIndex = totalCount - 1;
|
|
1915
|
+
const shadowArray =
|
|
1916
|
+
getGlobalStore
|
|
1917
|
+
.getState()
|
|
1918
|
+
.getShadowMetadata(stateKey, path) || [];
|
|
1919
|
+
const lastItemHeight =
|
|
1920
|
+
shadowArray[lastItemIndex]?.virtualizer?.itemHeight || 0;
|
|
1921
|
+
|
|
1922
|
+
if (lastItemHeight > 0) {
|
|
1929
1923
|
clearInterval(intervalId);
|
|
1924
|
+
container.scrollTo({
|
|
1925
|
+
top: container.scrollHeight,
|
|
1926
|
+
behavior: "smooth",
|
|
1927
|
+
});
|
|
1930
1928
|
}
|
|
1931
|
-
}
|
|
1932
|
-
}, 100); // Check every 100ms.
|
|
1933
|
-
|
|
1934
|
-
// Cleanup: Stop the loop if the component unmounts.
|
|
1935
|
-
return () => {
|
|
1936
|
-
console.log("ALGORITHM: Cleaning up loop.");
|
|
1937
|
-
clearInterval(intervalId);
|
|
1938
|
-
};
|
|
1939
|
-
}, [totalCount]); // This whole process triggers ONLY when totalCount changes.
|
|
1929
|
+
}, 100);
|
|
1940
1930
|
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
if (!container) return;
|
|
1931
|
+
// This return is the cleanup for the if-block.
|
|
1932
|
+
return () => clearInterval(intervalId);
|
|
1933
|
+
}
|
|
1945
1934
|
|
|
1946
|
-
|
|
1947
|
-
/* ... same as before ... */
|
|
1948
|
-
};
|
|
1935
|
+
// --- USER SCROLL HANDLING ---
|
|
1949
1936
|
const handleUserScroll = () => {
|
|
1950
1937
|
const isAtBottom =
|
|
1951
1938
|
container.scrollHeight -
|
|
@@ -1954,23 +1941,28 @@ function createProxyHandler<T>(
|
|
|
1954
1941
|
1;
|
|
1955
1942
|
if (!isAtBottom) {
|
|
1956
1943
|
isLockedToBottomRef.current = false;
|
|
1957
|
-
console.log("USER ACTION: Scroll lock DISABLED.");
|
|
1958
1944
|
}
|
|
1959
1945
|
updateVirtualRange();
|
|
1960
1946
|
};
|
|
1947
|
+
|
|
1961
1948
|
container.addEventListener("scroll", handleUserScroll, {
|
|
1962
1949
|
passive: true,
|
|
1963
1950
|
});
|
|
1951
|
+
updateVirtualRange(); // Always update range for current view.
|
|
1952
|
+
|
|
1953
|
+
// This return is the cleanup for the whole effect.
|
|
1964
1954
|
return () =>
|
|
1965
1955
|
container.removeEventListener("scroll", handleUserScroll);
|
|
1966
|
-
}, []);
|
|
1956
|
+
}, [totalCount, positions]); // Re-run when layout-related data changes.
|
|
1967
1957
|
|
|
1958
|
+
// This simple effect tracks the item count for the next render.
|
|
1959
|
+
useEffect(() => {
|
|
1960
|
+
prevTotalCountRef.current = totalCount;
|
|
1961
|
+
});
|
|
1968
1962
|
const scrollToBottom = useCallback(
|
|
1969
1963
|
(behavior: ScrollBehavior = "smooth") => {
|
|
1970
1964
|
if (containerRef.current) {
|
|
1971
1965
|
isLockedToBottomRef.current = true;
|
|
1972
|
-
console.log("USER ACTION: Scroll lock ENABLED.");
|
|
1973
|
-
// This is a manual trigger, so we don't need the loop. Just scroll.
|
|
1974
1966
|
containerRef.current.scrollTo({
|
|
1975
1967
|
top: containerRef.current.scrollHeight,
|
|
1976
1968
|
behavior,
|
|
@@ -1984,7 +1976,6 @@ function createProxyHandler<T>(
|
|
|
1984
1976
|
(index: number, behavior: ScrollBehavior = "smooth") => {
|
|
1985
1977
|
if (containerRef.current && positions[index] !== undefined) {
|
|
1986
1978
|
isLockedToBottomRef.current = false;
|
|
1987
|
-
console.log("USER ACTION: Scroll lock DISABLED.");
|
|
1988
1979
|
containerRef.current.scrollTo({
|
|
1989
1980
|
top: positions[index],
|
|
1990
1981
|
behavior,
|