cogsbox-state 0.5.296 → 0.5.297

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogsbox-state",
3
- "version": "0.5.296",
3
+ "version": "0.5.297",
4
4
  "description": "React state management library with form controls and server sync",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/CogsState.tsx CHANGED
@@ -1814,16 +1814,26 @@ function createProxyHandler<T>(
1814
1814
  endIndex: 10,
1815
1815
  });
1816
1816
 
1817
- const getItemHeight = useCallback(
1818
- (index: number): number => {
1819
- const metadata = getGlobalStore
1820
- .getState()
1821
- .getShadowMetadata(stateKey, [...path, index.toString()]);
1822
- return metadata?.virtualizer?.itemHeight || itemHeight;
1823
- },
1824
- [itemHeight, stateKey, path]
1817
+ // --- STATE AND CALLBACKS FOR HEIGHTS ---
1818
+ // This state value is the key. We increment it to force a re-calculation.
1819
+ const [heightsVersion, setHeightsVersion] = useState(0);
1820
+ // This callback is stable and won't cause re-renders itself.
1821
+ const forceRecalculate = useCallback(
1822
+ () => setHeightsVersion((v) => v + 1),
1823
+ []
1825
1824
  );
1826
1825
 
1826
+ // --- ON MOUNT: SCHEDULE A RECALCULATION ---
1827
+ // This solves the "initial load" problem. It ensures that after the first
1828
+ // items render and measure themselves, we run the calculations again
1829
+ // with the new, correct height data.
1830
+ useEffect(() => {
1831
+ const timer = setTimeout(() => {
1832
+ forceRecalculate();
1833
+ }, 50); // A small delay helps batch initial measurements.
1834
+ return () => clearTimeout(timer);
1835
+ }, [forceRecalculate]);
1836
+
1827
1837
  const isAtBottomRef = useRef(stickToBottom);
1828
1838
  const previousTotalCountRef = useRef(0);
1829
1839
  const isInitialMountRef = useRef(true);
@@ -1834,18 +1844,27 @@ function createProxyHandler<T>(
1834
1844
  ) as any[];
1835
1845
  const totalCount = sourceArray.length;
1836
1846
 
1847
+ // --- EFFICIENT HEIGHT & POSITION CALCULATION ---
1837
1848
  const { totalHeight, positions } = useMemo(() => {
1849
+ // Get the shadow object for the whole array ONCE. This is fast.
1850
+ const shadowArray =
1851
+ getGlobalStore.getState().getShadowMetadata(stateKey, path) ||
1852
+ [];
1853
+
1838
1854
  let height = 0;
1839
1855
  const pos: number[] = [];
1840
1856
  for (let i = 0; i < totalCount; i++) {
1841
1857
  pos[i] = height;
1842
- height += getItemHeight(i);
1843
- console.log("height", getItemHeight(i), height);
1858
+ // Access the height from the local shadowArray. No repeated deep lookups.
1859
+ const measuredHeight =
1860
+ shadowArray[i]?.virtualizer?.itemHeight;
1861
+ height += measuredHeight || itemHeight;
1844
1862
  }
1845
1863
  return { totalHeight: height, positions: pos };
1846
- }, [totalCount, getItemHeight]);
1864
+ // This now depends on `heightsVersion`, so it re-runs when we force it.
1865
+ }, [totalCount, stateKey, path, itemHeight, heightsVersion]);
1847
1866
 
1848
- // This logic is IDENTICAL to your original code.
1867
+ // This logic is from your original working code.
1849
1868
  const virtualState = useMemo(() => {
1850
1869
  const start = Math.max(0, range.startIndex);
1851
1870
  const end = Math.min(totalCount, range.endIndex);
@@ -1860,7 +1879,7 @@ function createProxyHandler<T>(
1860
1879
  });
1861
1880
  }, [range.startIndex, range.endIndex, sourceArray, totalCount]);
1862
1881
 
1863
- // This useLayoutEffect is from your original code.
1882
+ // This is your original useLayoutEffect with the robust index calculation.
1864
1883
  useLayoutEffect(() => {
1865
1884
  const container = containerRef.current;
1866
1885
  if (!container) return;
@@ -1874,15 +1893,13 @@ function createProxyHandler<T>(
1874
1893
  isAtBottomRef.current =
1875
1894
  scrollHeight - scrollTop - clientHeight < 10;
1876
1895
 
1877
- // --- THE ROBUST FIX: Binary search to find the start index ---
1878
- // This is extremely fast and correctly handles all scroll positions.
1896
+ // ROBUST: Binary search to find the start index. Prevents errors.
1879
1897
  let search = (list: number[], value: number) => {
1880
1898
  let low = 0;
1881
1899
  let high = list.length - 1;
1882
1900
  while (low <= high) {
1883
1901
  const mid = Math.floor((low + high) / 2);
1884
- const midValue = list[mid]!;
1885
- if (midValue < value) {
1902
+ if (list[mid]! < value) {
1886
1903
  low = mid + 1;
1887
1904
  } else {
1888
1905
  high = mid - 1;
@@ -1903,7 +1920,7 @@ function createProxyHandler<T>(
1903
1920
 
1904
1921
  startIndex = Math.max(0, startIndex - overscan);
1905
1922
  endIndex = Math.min(totalCount, endIndex + overscan);
1906
- console.log("startIndex", startIndex, "endIndex", endIndex);
1923
+
1907
1924
  setRange((prevRange) => {
1908
1925
  if (
1909
1926
  prevRange.startIndex !== startIndex ||
@@ -1919,7 +1936,7 @@ function createProxyHandler<T>(
1919
1936
  passive: true,
1920
1937
  });
1921
1938
 
1922
- // This stickToBottom logic is IDENTICAL to your original.
1939
+ // This stickToBottom logic is from your original working code.
1923
1940
  if (stickToBottom) {
1924
1941
  if (isInitialMountRef.current) {
1925
1942
  container.scrollTo({