@ricsam/react-spreadsheets 0.0.6 → 0.0.8

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/react-spreadsheets",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/lib.mjs CHANGED
@@ -1349,6 +1349,99 @@ var withReadableTextColor = (style) => {
1349
1349
  return { ...style, color };
1350
1350
  };
1351
1351
  var hasBorderStyle = (style) => Boolean(style?.borderColor || style?.borderTopColor || style?.borderRightColor || style?.borderBottomColor || style?.borderLeftColor);
1352
+ var normalizeFiniteCount = (count) => {
1353
+ if (count === undefined || count === Infinity)
1354
+ return;
1355
+ if (!Number.isFinite(count))
1356
+ return 0;
1357
+ return Math.max(0, Math.floor(count));
1358
+ };
1359
+ var countToSelectionBound = (count) => count === undefined ? { type: "infinity" } : { type: "number", value: count };
1360
+ var alignScrollOffset = (current, itemStart, itemEnd, viewportSize, align) => {
1361
+ if (viewportSize <= 0)
1362
+ return current;
1363
+ if (align === "start")
1364
+ return itemStart;
1365
+ if (align === "end")
1366
+ return itemEnd - viewportSize;
1367
+ if (itemStart < current)
1368
+ return itemStart;
1369
+ if (itemEnd > current + viewportSize)
1370
+ return itemEnd - viewportSize;
1371
+ return current;
1372
+ };
1373
+ var hasCellData = (value) => value !== undefined && value !== "";
1374
+ var getSparseUsedRange = (cellData, rowCount, columnCount) => {
1375
+ let minRow = Infinity;
1376
+ let maxRow = -1;
1377
+ let minCol = Infinity;
1378
+ let maxCol = -1;
1379
+ for (const [reference, value] of cellData) {
1380
+ if (!hasCellData(value))
1381
+ continue;
1382
+ let rowIndex;
1383
+ let columnIndex;
1384
+ try {
1385
+ ({ rowIndex, columnIndex } = parseCellReference(reference));
1386
+ } catch {
1387
+ continue;
1388
+ }
1389
+ if (rowIndex < 0 || columnIndex < 0 || rowCount !== undefined && rowIndex >= rowCount || columnCount !== undefined && columnIndex >= columnCount) {
1390
+ continue;
1391
+ }
1392
+ minRow = Math.min(minRow, rowIndex);
1393
+ maxRow = Math.max(maxRow, rowIndex);
1394
+ minCol = Math.min(minCol, columnIndex);
1395
+ maxCol = Math.max(maxCol, columnIndex);
1396
+ }
1397
+ if (maxRow < 0 || maxCol < 0)
1398
+ return;
1399
+ return {
1400
+ start: { row: minRow, col: minCol },
1401
+ end: {
1402
+ row: { type: "number", value: maxRow },
1403
+ col: { type: "number", value: maxCol }
1404
+ }
1405
+ };
1406
+ };
1407
+ var resolveSparseNavigationTarget = (request, cellData) => {
1408
+ if (request.kind !== "jump" || request.table)
1409
+ return;
1410
+ const isVertical = request.direction === "up" || request.direction === "down";
1411
+ const movingForward = request.direction === "down" || request.direction === "right";
1412
+ const originAxis = isVertical ? request.origin.row : request.origin.col;
1413
+ const fixedAxis = isVertical ? request.origin.col : request.origin.row;
1414
+ const occupied = new Set;
1415
+ for (const [reference, value] of cellData) {
1416
+ if (!hasCellData(value))
1417
+ continue;
1418
+ let rowIndex;
1419
+ let columnIndex;
1420
+ try {
1421
+ ({ rowIndex, columnIndex } = parseCellReference(reference));
1422
+ } catch {
1423
+ continue;
1424
+ }
1425
+ if ((isVertical ? columnIndex : rowIndex) === fixedAxis) {
1426
+ occupied.add(isVertical ? rowIndex : columnIndex);
1427
+ }
1428
+ }
1429
+ const step = movingForward ? 1 : -1;
1430
+ let targetAxis;
1431
+ if (occupied.has(originAxis + step)) {
1432
+ targetAxis = originAxis + step;
1433
+ while (occupied.has(targetAxis + step))
1434
+ targetAxis += step;
1435
+ } else {
1436
+ const candidates = [...occupied].filter((index) => movingForward ? index > originAxis : index < originAxis);
1437
+ if (candidates.length > 0) {
1438
+ targetAxis = movingForward ? Math.min(...candidates) : Math.max(...candidates);
1439
+ }
1440
+ }
1441
+ if (targetAxis === undefined)
1442
+ return;
1443
+ return isVertical ? { row: targetAxis, col: request.origin.col } : { row: request.origin.row, col: targetAxis };
1444
+ };
1352
1445
  var CellComponent = React2.memo(({
1353
1446
  value,
1354
1447
  row,
@@ -1652,6 +1745,8 @@ var Spreadsheet = forwardRef(({
1652
1745
  cellData: controlledCellData,
1653
1746
  onCellDataChange,
1654
1747
  onCellDataChangeError,
1748
+ rowCount,
1749
+ columnCount,
1655
1750
  columnWidths: controlledColumnWidths,
1656
1751
  onColumnWidthsChange,
1657
1752
  rowHeights: controlledRowHeights,
@@ -1673,14 +1768,35 @@ var Spreadsheet = forwardRef(({
1673
1768
  const [containerDivRef, setContainerDivRef] = useState4(null);
1674
1769
  const colorScheme = useColorScheme(containerDivRef);
1675
1770
  const gridLineProbeRef = useRef3(null);
1771
+ const [internalCellData, setInternalCellData] = useState4(new Map);
1772
+ const [retryingEdit, setRetryingEdit] = useState4(null);
1773
+ const retryingEditVersionRef = useRef3(0);
1774
+ const cellData = controlledCellData ?? internalCellData;
1775
+ const cellDataRef = useRef3(cellData);
1776
+ cellDataRef.current = cellData;
1777
+ const finiteRowCount = normalizeFiniteCount(rowCount);
1778
+ const finiteColumnCount = normalizeFiniteCount(columnCount);
1779
+ const gridCountsRef = useRef3({ rows: finiteRowCount, columns: finiteColumnCount });
1780
+ gridCountsRef.current = { rows: finiteRowCount, columns: finiteColumnCount };
1781
+ const getNumRows = useCallback3(() => countToSelectionBound(gridCountsRef.current.rows), []);
1782
+ const getNumCols = useCallback3(() => countToSelectionBound(gridCountsRef.current.columns), []);
1783
+ const getDefaultUsedRange = useCallback3(() => getSparseUsedRange(cellDataRef.current, gridCountsRef.current.rows, gridCountsRef.current.columns), []);
1784
+ const defaultResolveTarget = useCallback3((request) => resolveSparseNavigationTarget(request, cellDataRef.current), []);
1785
+ const consumerNavigation = selection?.navigation;
1786
+ const navigation = useMemo(() => ({
1787
+ getUsedRange: consumerNavigation?.getUsedRange ?? getDefaultUsedRange,
1788
+ ...consumerNavigation?.getTableAt ? { getTableAt: consumerNavigation.getTableAt } : {},
1789
+ resolveTarget: consumerNavigation?.resolveTarget ?? defaultResolveTarget
1790
+ }), [consumerNavigation, defaultResolveTarget, getDefaultUsedRange]);
1676
1791
  const selectionManager = useInitializeSelectionManager({
1677
- getNumCols: () => ({ type: "infinity" }),
1678
- getNumRows: () => ({ type: "infinity" }),
1792
+ getNumCols,
1793
+ getNumRows,
1679
1794
  initialState: selection?.initialState,
1680
1795
  state: selection?.state,
1681
1796
  onStateChange: selection?.onStateChange,
1682
1797
  containerElement: containerDivRef,
1683
1798
  formats: selection?.formats,
1799
+ navigation,
1684
1800
  disableAutoClipboard: true
1685
1801
  });
1686
1802
  const effects = selection?.effects;
@@ -1689,12 +1805,6 @@ var Spreadsheet = forwardRef(({
1689
1805
  return effects(selectionManager);
1690
1806
  }
1691
1807
  }, [selectionManager, effects]);
1692
- const [internalCellData, setInternalCellData] = useState4(new Map);
1693
- const [retryingEdit, setRetryingEdit] = useState4(null);
1694
- const retryingEditVersionRef = useRef3(0);
1695
- const cellData = controlledCellData ?? internalCellData;
1696
- const cellDataRef = useRef3(cellData);
1697
- cellDataRef.current = cellData;
1698
1808
  const isControlled = controlledCellData !== undefined;
1699
1809
  const prevIsControlledRef = useRef3(isControlled);
1700
1810
  selectionManager.getCellsWithData = (area) => {
@@ -1870,7 +1980,7 @@ var Spreadsheet = forwardRef(({
1870
1980
  ctx.lineWidth = 1;
1871
1981
  let x = gridDims.headerWidth;
1872
1982
  let colIndex = 0;
1873
- while (x < viewport.scrollX + gridDims.headerWidth) {
1983
+ while (x < viewport.scrollX + gridDims.headerWidth && (finiteColumnCount === undefined || colIndex < finiteColumnCount)) {
1874
1984
  const colWidth = (columnWidths.get(indexToColumn(colIndex)) || DEFAULT_CELL_WIDTH) * viewport.zoom;
1875
1985
  if (x + colWidth > viewport.scrollX + gridDims.headerWidth) {
1876
1986
  break;
@@ -1878,7 +1988,7 @@ var Spreadsheet = forwardRef(({
1878
1988
  x += colWidth;
1879
1989
  colIndex++;
1880
1990
  }
1881
- while (x - viewport.scrollX < width) {
1991
+ while (x - viewport.scrollX < width && (finiteColumnCount === undefined || colIndex < finiteColumnCount)) {
1882
1992
  const drawX = x - viewport.scrollX;
1883
1993
  if (drawX >= gridDims.headerWidth && drawX <= width) {
1884
1994
  ctx.beginPath();
@@ -1892,7 +2002,7 @@ var Spreadsheet = forwardRef(({
1892
2002
  }
1893
2003
  let y = gridDims.headerHeight;
1894
2004
  let rowNum = 1;
1895
- while (y < viewport.scrollY + gridDims.headerHeight) {
2005
+ while (y < viewport.scrollY + gridDims.headerHeight && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
1896
2006
  const rowHeight = (rowHeights.get(rowNum) || DEFAULT_CELL_HEIGHT) * viewport.zoom;
1897
2007
  if (y + rowHeight > viewport.scrollY + gridDims.headerHeight) {
1898
2008
  break;
@@ -1900,7 +2010,7 @@ var Spreadsheet = forwardRef(({
1900
2010
  y += rowHeight;
1901
2011
  rowNum++;
1902
2012
  }
1903
- while (y - viewport.scrollY < height) {
2013
+ while (y - viewport.scrollY < height && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
1904
2014
  const drawY = y - viewport.scrollY;
1905
2015
  if (drawY >= gridDims.headerHeight && drawY <= height) {
1906
2016
  ctx.beginPath();
@@ -1914,6 +2024,8 @@ var Spreadsheet = forwardRef(({
1914
2024
  }
1915
2025
  }, [
1916
2026
  columnWidths,
2027
+ finiteColumnCount,
2028
+ finiteRowCount,
1917
2029
  gridDims.headerHeight,
1918
2030
  gridDims.headerWidth,
1919
2031
  height,
@@ -1931,7 +2043,7 @@ var Spreadsheet = forwardRef(({
1931
2043
  const cells = [];
1932
2044
  let x = 0;
1933
2045
  let startCol = 0;
1934
- while (x < viewport.scrollX) {
2046
+ while (x < viewport.scrollX && (finiteColumnCount === undefined || startCol < finiteColumnCount)) {
1935
2047
  const colWidth = (columnWidths.get(indexToColumn(startCol)) || DEFAULT_CELL_WIDTH) * viewport.zoom;
1936
2048
  if (x + colWidth > viewport.scrollX) {
1937
2049
  break;
@@ -1941,7 +2053,7 @@ var Spreadsheet = forwardRef(({
1941
2053
  }
1942
2054
  let y = 0;
1943
2055
  let startRow = 0;
1944
- while (y < viewport.scrollY) {
2056
+ while (y < viewport.scrollY && (finiteRowCount === undefined || startRow < finiteRowCount)) {
1945
2057
  const rowHeight = (rowHeights.get(startRow + 1) || DEFAULT_CELL_HEIGHT) * viewport.zoom;
1946
2058
  if (y + rowHeight > viewport.scrollY) {
1947
2059
  break;
@@ -1951,10 +2063,10 @@ var Spreadsheet = forwardRef(({
1951
2063
  }
1952
2064
  let rowY = y;
1953
2065
  let row = startRow;
1954
- while (rowY < viewport.scrollY + height) {
2066
+ while (rowY < viewport.scrollY + height && (finiteRowCount === undefined || row < finiteRowCount)) {
1955
2067
  let colX = x;
1956
2068
  let col = startCol;
1957
- while (colX < viewport.scrollX + width) {
2069
+ while (colX < viewport.scrollX + width && (finiteColumnCount === undefined || col < finiteColumnCount)) {
1958
2070
  const colKey = indexToColumn(col);
1959
2071
  const rowNum = getRowNumber(row);
1960
2072
  const cellKey = `${colKey}${rowNum}`;
@@ -1975,6 +2087,8 @@ var Spreadsheet = forwardRef(({
1975
2087
  }, [
1976
2088
  cellData,
1977
2089
  columnWidths,
2090
+ finiteColumnCount,
2091
+ finiteRowCount,
1978
2092
  height,
1979
2093
  rowHeights,
1980
2094
  viewport.scrollX,
@@ -2002,6 +2116,64 @@ var Spreadsheet = forwardRef(({
2002
2116
  }
2003
2117
  return { x, y };
2004
2118
  }, [columnWidths, rowHeights]);
2119
+ const clampViewportOffsets = useCallback3((scrollX, scrollY, zoom) => {
2120
+ const bodyWidth = Math.max(0, width - HEADER_WIDTH * zoom);
2121
+ const bodyHeight = Math.max(0, height - HEADER_HEIGHT * zoom);
2122
+ const maxScrollX = finiteColumnCount === undefined ? Infinity : Math.max(0, getColumnEdgePosition(finiteColumnCount, columnWidths) * zoom - bodyWidth);
2123
+ const maxScrollY = finiteRowCount === undefined ? Infinity : Math.max(0, getRowEdgePosition(finiteRowCount, rowHeights) * zoom - bodyHeight);
2124
+ return {
2125
+ scrollX: Math.min(Math.max(0, scrollX), maxScrollX),
2126
+ scrollY: Math.min(Math.max(0, scrollY), maxScrollY)
2127
+ };
2128
+ }, [columnWidths, finiteColumnCount, finiteRowCount, height, rowHeights, width]);
2129
+ const revealCell = useCallback3((cell, options = {}) => {
2130
+ if (finiteRowCount === 0 || finiteColumnCount === 0)
2131
+ return;
2132
+ if (!Number.isFinite(cell.row) || !Number.isFinite(cell.col))
2133
+ return;
2134
+ const requestedRow = Math.max(0, Math.floor(cell.row));
2135
+ const requestedCol = Math.max(0, Math.floor(cell.col));
2136
+ const row = finiteRowCount === undefined ? requestedRow : Math.min(requestedRow, finiteRowCount - 1);
2137
+ const col = finiteColumnCount === undefined ? requestedCol : Math.min(requestedCol, finiteColumnCount - 1);
2138
+ setViewport((previous) => {
2139
+ const bodyWidth = Math.max(0, width - HEADER_WIDTH * previous.zoom);
2140
+ const bodyHeight = Math.max(0, height - HEADER_HEIGHT * previous.zoom);
2141
+ const left = getColumnEdgePosition(col, columnWidths) * previous.zoom;
2142
+ const right = getColumnEdgePosition(col + 1, columnWidths) * previous.zoom;
2143
+ const top = getRowEdgePosition(row, rowHeights) * previous.zoom;
2144
+ const bottom = getRowEdgePosition(row + 1, rowHeights) * previous.zoom;
2145
+ const desired = clampViewportOffsets(alignScrollOffset(previous.scrollX, left, right, bodyWidth, options.columnAlign ?? "nearest"), alignScrollOffset(previous.scrollY, top, bottom, bodyHeight, options.rowAlign ?? "nearest"), previous.zoom);
2146
+ if (desired.scrollX === previous.scrollX && desired.scrollY === previous.scrollY) {
2147
+ return previous;
2148
+ }
2149
+ return { ...previous, ...desired };
2150
+ });
2151
+ }, [
2152
+ clampViewportOffsets,
2153
+ columnWidths,
2154
+ finiteColumnCount,
2155
+ finiteRowCount,
2156
+ height,
2157
+ rowHeights,
2158
+ width
2159
+ ]);
2160
+ const scrollToCell = useCallback3((cell, options = {}) => {
2161
+ const align = options.align ?? "nearest";
2162
+ revealCell(cell, { rowAlign: align, columnAlign: align });
2163
+ }, [revealCell]);
2164
+ useEffect3(() => selectionManager.listenToViewportRequest((request) => {
2165
+ const isVertical = request.direction === "up" || request.direction === "down";
2166
+ revealCell(request.cell, {
2167
+ rowAlign: isVertical ? request.align : "nearest",
2168
+ columnAlign: isVertical ? "nearest" : request.align
2169
+ });
2170
+ }), [revealCell, selectionManager]);
2171
+ useEffect3(() => {
2172
+ setViewport((previous) => {
2173
+ const next = clampViewportOffsets(previous.scrollX, previous.scrollY, previous.zoom);
2174
+ return next.scrollX === previous.scrollX && next.scrollY === previous.scrollY ? previous : { ...previous, ...next };
2175
+ });
2176
+ }, [clampViewportOffsets]);
2005
2177
  React2.useEffect(() => {
2006
2178
  return selectionManager.listenToUpdateData((updates) => {
2007
2179
  const editingState = selectionManager.isEditing;
@@ -2076,12 +2248,11 @@ var Spreadsheet = forwardRef(({
2076
2248
  const handleWheel = useCallback3((e) => {
2077
2249
  e.preventDefault();
2078
2250
  e.stopPropagation();
2079
- setViewport((prev) => ({
2080
- ...prev,
2081
- scrollX: Math.max(0, prev.scrollX + e.deltaX),
2082
- scrollY: Math.max(0, prev.scrollY + e.deltaY)
2251
+ setViewport((previous) => ({
2252
+ ...previous,
2253
+ ...clampViewportOffsets(previous.scrollX + e.deltaX, previous.scrollY + e.deltaY, previous.zoom)
2083
2254
  }));
2084
- }, []);
2255
+ }, [clampViewportOffsets]);
2085
2256
  const commitOverlayChildren = useCallback3((children) => {
2086
2257
  overlayChildrenRef.current = children;
2087
2258
  if (onOverlayChildrenChange) {
@@ -2500,7 +2671,7 @@ var Spreadsheet = forwardRef(({
2500
2671
  const headers = [];
2501
2672
  let x = 0;
2502
2673
  let startCol = 0;
2503
- while (x < viewport.scrollX) {
2674
+ while (x < viewport.scrollX && (finiteColumnCount === undefined || startCol < finiteColumnCount)) {
2504
2675
  const col = indexToColumn(startCol);
2505
2676
  const colWidth = getColumnWidth(col) * viewport.zoom;
2506
2677
  if (x + colWidth > viewport.scrollX) {
@@ -2509,7 +2680,7 @@ var Spreadsheet = forwardRef(({
2509
2680
  x += colWidth;
2510
2681
  startCol++;
2511
2682
  }
2512
- while (x < viewport.scrollX + width) {
2683
+ while (x < viewport.scrollX + width && (finiteColumnCount === undefined || startCol < finiteColumnCount)) {
2513
2684
  const col = indexToColumn(startCol);
2514
2685
  const colWidth = getColumnWidth(col) * viewport.zoom;
2515
2686
  headers.push({
@@ -2520,14 +2691,14 @@ var Spreadsheet = forwardRef(({
2520
2691
  startCol++;
2521
2692
  }
2522
2693
  return headers;
2523
- }, [width, viewport, getColumnWidth]);
2694
+ }, [finiteColumnCount, width, viewport, getColumnWidth]);
2524
2695
  const getVisibleRowHeaders = useCallback3(() => {
2525
2696
  if (!height)
2526
2697
  return [];
2527
2698
  const headers = [];
2528
2699
  let y = 0;
2529
2700
  let rowNum = 1;
2530
- while (y < viewport.scrollY) {
2701
+ while (y < viewport.scrollY && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
2531
2702
  const rowHeight = getRowHeight(rowNum) * viewport.zoom;
2532
2703
  if (y + rowHeight > viewport.scrollY) {
2533
2704
  break;
@@ -2535,7 +2706,7 @@ var Spreadsheet = forwardRef(({
2535
2706
  y += rowHeight;
2536
2707
  rowNum++;
2537
2708
  }
2538
- while (y < viewport.scrollY + height) {
2709
+ while (y < viewport.scrollY + height && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
2539
2710
  const rowHeight = getRowHeight(rowNum) * viewport.zoom;
2540
2711
  headers.push({
2541
2712
  row: rowNum,
@@ -2545,7 +2716,7 @@ var Spreadsheet = forwardRef(({
2545
2716
  rowNum++;
2546
2717
  }
2547
2718
  return headers;
2548
- }, [height, viewport, getRowHeight]);
2719
+ }, [finiteRowCount, height, viewport, getRowHeight]);
2549
2720
  const { visibleOverlays, overlaysToRender } = useMemo(() => {
2550
2721
  if (!width || !height || !overlayChildren) {
2551
2722
  return { visibleOverlays: [], overlaysToRender: [] };
@@ -2593,6 +2764,43 @@ var Spreadsheet = forwardRef(({
2593
2764
  const visibleColumnHeaders = React2.useMemo(() => getVisibleColumnHeaders(), [getVisibleColumnHeaders]);
2594
2765
  const visibleRowHeaders = React2.useMemo(() => getVisibleRowHeaders(), [getVisibleRowHeaders]);
2595
2766
  const hasFocus = useSelectionManager(selectionManager, () => selectionManager.hasFocus);
2767
+ const referenceSelection = useSelectionManager(selectionManager, () => selectionManager.referenceSelection);
2768
+ const referenceSelectionRect = useMemo(() => {
2769
+ if (referenceSelection.type === "none")
2770
+ return;
2771
+ if (finiteRowCount === 0 || finiteColumnCount === 0)
2772
+ return;
2773
+ const range = referenceSelection.range;
2774
+ const lastVisibleRow = visibleCells.reduce((maximum, cell) => Math.max(maximum, cell.row - 1), range.start.row);
2775
+ const lastVisibleCol = visibleCells.reduce((maximum, cell) => Math.max(maximum, columnToIndex(cell.col)), range.start.col);
2776
+ const rawEndRow = range.end.row.type === "number" ? range.end.row.value : finiteRowCount === undefined ? lastVisibleRow : finiteRowCount - 1;
2777
+ const rawEndCol = range.end.col.type === "number" ? range.end.col.value : finiteColumnCount === undefined ? lastVisibleCol : finiteColumnCount - 1;
2778
+ const maxRow = finiteRowCount === undefined ? Infinity : finiteRowCount - 1;
2779
+ const maxCol = finiteColumnCount === undefined ? Infinity : finiteColumnCount - 1;
2780
+ const startRow = Math.min(Math.max(0, Math.min(range.start.row, rawEndRow)), maxRow);
2781
+ const endRow = Math.min(Math.max(0, Math.max(range.start.row, rawEndRow)), maxRow);
2782
+ const startCol = Math.min(Math.max(0, Math.min(range.start.col, rawEndCol)), maxCol);
2783
+ const endCol = Math.min(Math.max(0, Math.max(range.start.col, rawEndCol)), maxCol);
2784
+ const rect = getRectFromCellSnapAnchor({
2785
+ startCol,
2786
+ startRow,
2787
+ endCol: endCol + 1,
2788
+ endRow: endRow + 1
2789
+ }, columnWidths, rowHeights);
2790
+ return {
2791
+ left: HEADER_WIDTH + rect.x,
2792
+ top: HEADER_HEIGHT + rect.y,
2793
+ width: rect.width,
2794
+ height: rect.height
2795
+ };
2796
+ }, [
2797
+ columnWidths,
2798
+ finiteColumnCount,
2799
+ finiteRowCount,
2800
+ referenceSelection,
2801
+ rowHeights,
2802
+ visibleCells
2803
+ ]);
2596
2804
  React2.useEffect(() => {
2597
2805
  if (!containerDivRef || !hasFocus || !parentSelected) {
2598
2806
  return;
@@ -2610,8 +2818,9 @@ var Spreadsheet = forwardRef(({
2610
2818
  },
2611
2819
  blur: () => {
2612
2820
  selectionManager.blur();
2613
- }
2614
- }), [selectionManager]);
2821
+ },
2822
+ scrollToCell
2823
+ }), [scrollToCell, selectionManager]);
2615
2824
  return /* @__PURE__ */ jsx3("div", {
2616
2825
  className: cn("rsp-root", className),
2617
2826
  "data-rsp-scheme": colorScheme,
@@ -2715,28 +2924,38 @@ var Spreadsheet = forwardRef(({
2715
2924
  zIndex: 40
2716
2925
  }
2717
2926
  }),
2718
- /* @__PURE__ */ jsx3("div", {
2927
+ /* @__PURE__ */ jsxs2("div", {
2719
2928
  className: "rsp-cells",
2720
2929
  "data-testid": "spreadsheet-cells",
2721
2930
  style: {
2722
2931
  transform: `translate(${-viewport.scrollX}px, ${-viewport.scrollY}px) scale(${viewport.zoom})`,
2723
2932
  transformOrigin: "0 0"
2724
2933
  },
2725
- children: visibleCells.map((cell) => /* @__PURE__ */ jsx3(CellComponent, {
2726
- value: cellData.get(`${cell.col}${cell.row}`) ?? "",
2727
- row: cell.row,
2728
- col: cell.col,
2729
- selectionManager,
2730
- cellToPixelBase,
2731
- getColumnWidth,
2732
- getRowHeight,
2733
- customCellStyle,
2734
- customCellRenderer,
2735
- inputValue: cellData.get(`${cell.col}${cell.row}`) ?? "",
2736
- editInputVersion: retryingEdit?.rowIndex === cell.row - 1 && retryingEdit?.colIndex === columnToIndex(cell.col) ? retryingEdit.version : 0,
2737
- width,
2738
- height
2739
- }, `${cell.col}${cell.row}`))
2934
+ children: [
2935
+ visibleCells.map((cell) => /* @__PURE__ */ jsx3(CellComponent, {
2936
+ value: cellData.get(`${cell.col}${cell.row}`) ?? "",
2937
+ row: cell.row,
2938
+ col: cell.col,
2939
+ selectionManager,
2940
+ cellToPixelBase,
2941
+ getColumnWidth,
2942
+ getRowHeight,
2943
+ customCellStyle,
2944
+ customCellRenderer,
2945
+ inputValue: cellData.get(`${cell.col}${cell.row}`) ?? "",
2946
+ editInputVersion: retryingEdit?.rowIndex === cell.row - 1 && retryingEdit?.colIndex === columnToIndex(cell.col) ? retryingEdit.version : 0,
2947
+ width,
2948
+ height
2949
+ }, `${cell.col}${cell.row}`)),
2950
+ referenceSelectionRect && referenceSelection.type !== "none" && /* @__PURE__ */ jsx3("div", {
2951
+ "aria-hidden": "true",
2952
+ className: cn("rsp-reference-selection", `rsp-reference-selection-${referenceSelection.type}`),
2953
+ "data-testid": "spreadsheet-reference-selection",
2954
+ "data-reference-id": referenceSelection.id,
2955
+ "data-reference-phase": referenceSelection.type,
2956
+ style: referenceSelectionRect
2957
+ })
2958
+ ]
2740
2959
  }),
2741
2960
  components && overlayChildren && overlayChildren.length > 0 && /* @__PURE__ */ jsxs2("div", {
2742
2961
  ref: overlayContainerRef,
@@ -2821,7 +3040,7 @@ import {
2821
3040
  parseCellReference as parseCellReference2,
2822
3041
  getCellReference as getCellReference3
2823
3042
  } from "@ricsam/formula-engine";
2824
- import React3, { useCallback as useCallback4, useRef as useRef4, useState as useState6 } from "react";
3043
+ import React3, { useCallback as useCallback4, useMemo as useMemo2, useRef as useRef4, useState as useState6 } from "react";
2825
3044
 
2826
3045
  // src/utils/use-scheme-root.ts
2827
3046
  import { useState as useState5 } from "react";
@@ -3345,6 +3564,42 @@ function FormulaSheet({
3345
3564
  const clipboardManager = React3.useMemo(() => controlledClipboardManager ?? new WorkbookClipboardManager(engine), [controlledClipboardManager, engine]);
3346
3565
  const state = useEngine(engine);
3347
3566
  const sheet = state.workbooks.get(workbookName)?.sheets.get(sheetName);
3567
+ const getEngineTableAt = useCallback4((cell) => {
3568
+ const table = engine.isCellInTable({
3569
+ workbookName,
3570
+ sheetName,
3571
+ rowIndex: cell.row,
3572
+ colIndex: cell.col
3573
+ });
3574
+ if (!table)
3575
+ return;
3576
+ const endRow = table.endRow.type === "number" ? { type: "number", value: table.endRow.value } : { type: "infinity" };
3577
+ const endCol = {
3578
+ type: "number",
3579
+ value: table.start.colIndex + table.headers.size - 1
3580
+ };
3581
+ const hasDataRows = endRow.type === "infinity" || endRow.value > table.start.rowIndex;
3582
+ return {
3583
+ id: `${table.workbookName}/${table.name}`,
3584
+ bounds: {
3585
+ start: { row: table.start.rowIndex, col: table.start.colIndex },
3586
+ end: { row: endRow, col: endCol }
3587
+ },
3588
+ ...hasDataRows ? {
3589
+ dataBounds: {
3590
+ start: {
3591
+ row: table.start.rowIndex + 1,
3592
+ col: table.start.colIndex
3593
+ },
3594
+ end: { row: endRow, col: endCol }
3595
+ }
3596
+ } : {}
3597
+ };
3598
+ }, [engine, sheetName, workbookName]);
3599
+ const formulaNavigation = useMemo2(() => ({
3600
+ ...selection?.navigation,
3601
+ getTableAt: selection?.navigation?.getTableAt ?? getEngineTableAt
3602
+ }), [getEngineTableAt, selection?.navigation]);
3348
3603
  const onCellDataChange = useCallback4((updatedSpreadsheet) => {
3349
3604
  const data = new Map(updatedSpreadsheet);
3350
3605
  data.forEach((value, key) => {
@@ -3518,6 +3773,7 @@ function FormulaSheet({
3518
3773
  parentSelected: isSelected,
3519
3774
  selection: {
3520
3775
  ...selection,
3776
+ navigation: formulaNavigation,
3521
3777
  effects: selectionManagerEffects
3522
3778
  },
3523
3779
  containerProps: isSelected && selectedArea ? {
@@ -3537,31 +3793,29 @@ function FormulaSheet({
3537
3793
  workbookName
3538
3794
  });
3539
3795
  if (!tableInfo) {
3540
- if (conditionalStyle) {
3541
- return {
3542
- ...conditionalStyle.backgroundColor && {
3543
- backgroundColor: conditionalStyle.backgroundColor
3544
- },
3545
- ...conditionalStyle.color && {
3546
- color: conditionalStyle.color
3547
- },
3548
- ...conditionalStyle.fontSize && {
3549
- fontSize: conditionalStyle.fontSize
3550
- },
3551
- ...conditionalStyle.bold && {
3552
- fontWeight: "bold"
3553
- },
3554
- ...conditionalStyle.italic && {
3555
- fontStyle: "italic"
3556
- },
3557
- ...conditionalStyle.underline && {
3558
- textDecoration: "underline"
3559
- },
3560
- ...getBorderCellStyle(conditionalStyle),
3561
- ...getWrapCellStyle(conditionalStyle)
3562
- };
3563
- }
3564
- return {};
3796
+ const style2 = conditionalStyle ? {
3797
+ ...conditionalStyle.backgroundColor && {
3798
+ backgroundColor: conditionalStyle.backgroundColor
3799
+ },
3800
+ ...conditionalStyle.color && {
3801
+ color: conditionalStyle.color
3802
+ },
3803
+ ...conditionalStyle.fontSize && {
3804
+ fontSize: conditionalStyle.fontSize
3805
+ },
3806
+ ...conditionalStyle.bold && {
3807
+ fontWeight: "bold"
3808
+ },
3809
+ ...conditionalStyle.italic && {
3810
+ fontStyle: "italic"
3811
+ },
3812
+ ...conditionalStyle.underline && {
3813
+ textDecoration: "underline"
3814
+ },
3815
+ ...getBorderCellStyle(conditionalStyle),
3816
+ ...getWrapCellStyle(conditionalStyle)
3817
+ } : {};
3818
+ return customCellStyle ? customCellStyle(cell, style2) : style2;
3565
3819
  }
3566
3820
  const isHeaderRow = cell.rowIndex === tableInfo.start.rowIndex;
3567
3821
  const isFirstColumn = cell.colIndex === tableInfo.start.colIndex;
@@ -4109,4 +4363,4 @@ export {
4109
4363
  useResizeObserver
4110
4364
  };
4111
4365
 
4112
- //# debugId=2CF31D1B5984B38964756E2164756E21
4366
+ //# debugId=22D97CA826E5512F64756E2164756E21