@ricsam/react-spreadsheets 0.0.7 → 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.
- package/README.md +64 -1
- package/dist/cjs/lib.cjs +302 -46
- package/dist/cjs/lib.cjs.map +4 -4
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/lib.mjs +303 -47
- package/dist/mjs/lib.mjs.map +4 -4
- package/dist/mjs/package.json +1 -1
- package/dist/styles.css +36 -0
- package/dist/types/lib.d.ts +2 -1
- package/dist/types/spreadsheet/spreadsheet.d.ts +13 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -138,6 +138,68 @@ keyboard and edit selection state independently. For `FormulaWorkbook`, pass a
|
|
|
138
138
|
controlled `activeSheet` if the editor should reveal a reference on another
|
|
139
139
|
sheet; the same `customCellStyle` prop is forwarded to its active sheet.
|
|
140
140
|
|
|
141
|
+
For the range currently being inserted into a formula, use selection-manager's
|
|
142
|
+
reference-picking mode instead. It preserves the primary/editing selection and
|
|
143
|
+
the grid renders the picked range with an independent animated border:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
<Spreadsheet
|
|
147
|
+
rowCount={1_000}
|
|
148
|
+
columnCount={50}
|
|
149
|
+
selection={{
|
|
150
|
+
effects(manager) {
|
|
151
|
+
const stop = manager.listenToReferenceSelection((event) => {
|
|
152
|
+
if (event.phase === "start" || event.phase === "change") {
|
|
153
|
+
updateFormulaReference(event.range);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
manager.beginReferenceSelection({
|
|
158
|
+
editedRange: {
|
|
159
|
+
start: { row: 1, col: 3 },
|
|
160
|
+
end: {
|
|
161
|
+
row: { type: "number", value: 1 },
|
|
162
|
+
col: { type: "number", value: 3 },
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
return () => {
|
|
167
|
+
stop();
|
|
168
|
+
manager.endReferenceSelection();
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
}}
|
|
172
|
+
/>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Data-aware keyboard navigation
|
|
176
|
+
|
|
177
|
+
`Cmd/Ctrl + Arrow` jumps through populated cells and reveals the destination;
|
|
178
|
+
adding Shift extends the primary selection. Plain `Spreadsheet` derives its
|
|
179
|
+
used range and jump targets from non-empty `cellData`. `FormulaSheet` also
|
|
180
|
+
supplies table bounds from the formula engine, so navigation inside a table
|
|
181
|
+
respects its data body.
|
|
182
|
+
|
|
183
|
+
Override any part of that behavior through `selection.navigation`:
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
<Spreadsheet
|
|
187
|
+
rowCount={100_000}
|
|
188
|
+
columnCount={200}
|
|
189
|
+
selection={{
|
|
190
|
+
navigation: {
|
|
191
|
+
getUsedRange: () => model.usedRange,
|
|
192
|
+
getTableAt: (cell) => model.tableAt(cell),
|
|
193
|
+
resolveTarget: (request) => model.resolveJump(request),
|
|
194
|
+
},
|
|
195
|
+
}}
|
|
196
|
+
/>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The imperative `SpreadsheetRef.scrollToCell({ row, col }, { align })` reveals a
|
|
200
|
+
cell without changing selection. Alignment is `"nearest"` by default, with
|
|
201
|
+
`"start"` and `"end"` available for explicit positioning.
|
|
202
|
+
|
|
141
203
|
## Theming
|
|
142
204
|
|
|
143
205
|
All colors, fonts and sizes are CSS custom properties. The package only ever
|
|
@@ -227,11 +289,12 @@ Per-cell styling is done in JS:
|
|
|
227
289
|
| --- | --- |
|
|
228
290
|
| `cellData` | `Map<string, SerializedCellValue>` keyed by A1 reference. Omit for uncontrolled mode. |
|
|
229
291
|
| `onCellDataChange` | Called with the next map after an edit, paste or fill. |
|
|
292
|
+
| `rowCount` / `columnCount` | Finite grid dimensions. Omit either dimension for an unbounded axis. |
|
|
230
293
|
| `columnWidths` / `rowHeights` | Controlled sizing, keyed by column letter / 1-based row. |
|
|
231
294
|
| `customCellStyle` | Per-cell `CSSProperties`. |
|
|
232
295
|
| `customCellRenderer` | Per-cell React node. |
|
|
233
296
|
| `parseValue` | Coerce raw editor strings (e.g. text → number) before storing. |
|
|
234
|
-
| `selection` | Selection state,
|
|
297
|
+
| `selection` | Selection state, navigation model, and `effects(selectionManager)` for copy/paste/fill/reference hooks. |
|
|
235
298
|
| `components` + `overlayChildren` | Floating overlays anchored to the grid, optionally snapped to cell edges. |
|
|
236
299
|
|
|
237
300
|
## Development
|
package/dist/cjs/lib.cjs
CHANGED
|
@@ -1457,6 +1457,99 @@ var withReadableTextColor = (style) => {
|
|
|
1457
1457
|
return { ...style, color };
|
|
1458
1458
|
};
|
|
1459
1459
|
var hasBorderStyle = (style) => Boolean(style?.borderColor || style?.borderTopColor || style?.borderRightColor || style?.borderBottomColor || style?.borderLeftColor);
|
|
1460
|
+
var normalizeFiniteCount = (count) => {
|
|
1461
|
+
if (count === undefined || count === Infinity)
|
|
1462
|
+
return;
|
|
1463
|
+
if (!Number.isFinite(count))
|
|
1464
|
+
return 0;
|
|
1465
|
+
return Math.max(0, Math.floor(count));
|
|
1466
|
+
};
|
|
1467
|
+
var countToSelectionBound = (count) => count === undefined ? { type: "infinity" } : { type: "number", value: count };
|
|
1468
|
+
var alignScrollOffset = (current, itemStart, itemEnd, viewportSize, align) => {
|
|
1469
|
+
if (viewportSize <= 0)
|
|
1470
|
+
return current;
|
|
1471
|
+
if (align === "start")
|
|
1472
|
+
return itemStart;
|
|
1473
|
+
if (align === "end")
|
|
1474
|
+
return itemEnd - viewportSize;
|
|
1475
|
+
if (itemStart < current)
|
|
1476
|
+
return itemStart;
|
|
1477
|
+
if (itemEnd > current + viewportSize)
|
|
1478
|
+
return itemEnd - viewportSize;
|
|
1479
|
+
return current;
|
|
1480
|
+
};
|
|
1481
|
+
var hasCellData = (value) => value !== undefined && value !== "";
|
|
1482
|
+
var getSparseUsedRange = (cellData, rowCount, columnCount) => {
|
|
1483
|
+
let minRow = Infinity;
|
|
1484
|
+
let maxRow = -1;
|
|
1485
|
+
let minCol = Infinity;
|
|
1486
|
+
let maxCol = -1;
|
|
1487
|
+
for (const [reference, value] of cellData) {
|
|
1488
|
+
if (!hasCellData(value))
|
|
1489
|
+
continue;
|
|
1490
|
+
let rowIndex;
|
|
1491
|
+
let columnIndex;
|
|
1492
|
+
try {
|
|
1493
|
+
({ rowIndex, columnIndex } = parseCellReference(reference));
|
|
1494
|
+
} catch {
|
|
1495
|
+
continue;
|
|
1496
|
+
}
|
|
1497
|
+
if (rowIndex < 0 || columnIndex < 0 || rowCount !== undefined && rowIndex >= rowCount || columnCount !== undefined && columnIndex >= columnCount) {
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1500
|
+
minRow = Math.min(minRow, rowIndex);
|
|
1501
|
+
maxRow = Math.max(maxRow, rowIndex);
|
|
1502
|
+
minCol = Math.min(minCol, columnIndex);
|
|
1503
|
+
maxCol = Math.max(maxCol, columnIndex);
|
|
1504
|
+
}
|
|
1505
|
+
if (maxRow < 0 || maxCol < 0)
|
|
1506
|
+
return;
|
|
1507
|
+
return {
|
|
1508
|
+
start: { row: minRow, col: minCol },
|
|
1509
|
+
end: {
|
|
1510
|
+
row: { type: "number", value: maxRow },
|
|
1511
|
+
col: { type: "number", value: maxCol }
|
|
1512
|
+
}
|
|
1513
|
+
};
|
|
1514
|
+
};
|
|
1515
|
+
var resolveSparseNavigationTarget = (request, cellData) => {
|
|
1516
|
+
if (request.kind !== "jump" || request.table)
|
|
1517
|
+
return;
|
|
1518
|
+
const isVertical = request.direction === "up" || request.direction === "down";
|
|
1519
|
+
const movingForward = request.direction === "down" || request.direction === "right";
|
|
1520
|
+
const originAxis = isVertical ? request.origin.row : request.origin.col;
|
|
1521
|
+
const fixedAxis = isVertical ? request.origin.col : request.origin.row;
|
|
1522
|
+
const occupied = new Set;
|
|
1523
|
+
for (const [reference, value] of cellData) {
|
|
1524
|
+
if (!hasCellData(value))
|
|
1525
|
+
continue;
|
|
1526
|
+
let rowIndex;
|
|
1527
|
+
let columnIndex;
|
|
1528
|
+
try {
|
|
1529
|
+
({ rowIndex, columnIndex } = parseCellReference(reference));
|
|
1530
|
+
} catch {
|
|
1531
|
+
continue;
|
|
1532
|
+
}
|
|
1533
|
+
if ((isVertical ? columnIndex : rowIndex) === fixedAxis) {
|
|
1534
|
+
occupied.add(isVertical ? rowIndex : columnIndex);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
const step = movingForward ? 1 : -1;
|
|
1538
|
+
let targetAxis;
|
|
1539
|
+
if (occupied.has(originAxis + step)) {
|
|
1540
|
+
targetAxis = originAxis + step;
|
|
1541
|
+
while (occupied.has(targetAxis + step))
|
|
1542
|
+
targetAxis += step;
|
|
1543
|
+
} else {
|
|
1544
|
+
const candidates = [...occupied].filter((index) => movingForward ? index > originAxis : index < originAxis);
|
|
1545
|
+
if (candidates.length > 0) {
|
|
1546
|
+
targetAxis = movingForward ? Math.min(...candidates) : Math.max(...candidates);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
if (targetAxis === undefined)
|
|
1550
|
+
return;
|
|
1551
|
+
return isVertical ? { row: targetAxis, col: request.origin.col } : { row: request.origin.row, col: targetAxis };
|
|
1552
|
+
};
|
|
1460
1553
|
var CellComponent = import_react4.default.memo(({
|
|
1461
1554
|
value,
|
|
1462
1555
|
row,
|
|
@@ -1760,6 +1853,8 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
1760
1853
|
cellData: controlledCellData,
|
|
1761
1854
|
onCellDataChange,
|
|
1762
1855
|
onCellDataChangeError,
|
|
1856
|
+
rowCount,
|
|
1857
|
+
columnCount,
|
|
1763
1858
|
columnWidths: controlledColumnWidths,
|
|
1764
1859
|
onColumnWidthsChange,
|
|
1765
1860
|
rowHeights: controlledRowHeights,
|
|
@@ -1781,14 +1876,35 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
1781
1876
|
const [containerDivRef, setContainerDivRef] = import_react4.useState(null);
|
|
1782
1877
|
const colorScheme = useColorScheme(containerDivRef);
|
|
1783
1878
|
const gridLineProbeRef = import_react4.useRef(null);
|
|
1879
|
+
const [internalCellData, setInternalCellData] = import_react4.useState(new Map);
|
|
1880
|
+
const [retryingEdit, setRetryingEdit] = import_react4.useState(null);
|
|
1881
|
+
const retryingEditVersionRef = import_react4.useRef(0);
|
|
1882
|
+
const cellData = controlledCellData ?? internalCellData;
|
|
1883
|
+
const cellDataRef = import_react4.useRef(cellData);
|
|
1884
|
+
cellDataRef.current = cellData;
|
|
1885
|
+
const finiteRowCount = normalizeFiniteCount(rowCount);
|
|
1886
|
+
const finiteColumnCount = normalizeFiniteCount(columnCount);
|
|
1887
|
+
const gridCountsRef = import_react4.useRef({ rows: finiteRowCount, columns: finiteColumnCount });
|
|
1888
|
+
gridCountsRef.current = { rows: finiteRowCount, columns: finiteColumnCount };
|
|
1889
|
+
const getNumRows = import_react4.useCallback(() => countToSelectionBound(gridCountsRef.current.rows), []);
|
|
1890
|
+
const getNumCols = import_react4.useCallback(() => countToSelectionBound(gridCountsRef.current.columns), []);
|
|
1891
|
+
const getDefaultUsedRange = import_react4.useCallback(() => getSparseUsedRange(cellDataRef.current, gridCountsRef.current.rows, gridCountsRef.current.columns), []);
|
|
1892
|
+
const defaultResolveTarget = import_react4.useCallback((request) => resolveSparseNavigationTarget(request, cellDataRef.current), []);
|
|
1893
|
+
const consumerNavigation = selection?.navigation;
|
|
1894
|
+
const navigation = import_react4.useMemo(() => ({
|
|
1895
|
+
getUsedRange: consumerNavigation?.getUsedRange ?? getDefaultUsedRange,
|
|
1896
|
+
...consumerNavigation?.getTableAt ? { getTableAt: consumerNavigation.getTableAt } : {},
|
|
1897
|
+
resolveTarget: consumerNavigation?.resolveTarget ?? defaultResolveTarget
|
|
1898
|
+
}), [consumerNavigation, defaultResolveTarget, getDefaultUsedRange]);
|
|
1784
1899
|
const selectionManager = import_selection_manager.useInitializeSelectionManager({
|
|
1785
|
-
getNumCols
|
|
1786
|
-
getNumRows
|
|
1900
|
+
getNumCols,
|
|
1901
|
+
getNumRows,
|
|
1787
1902
|
initialState: selection?.initialState,
|
|
1788
1903
|
state: selection?.state,
|
|
1789
1904
|
onStateChange: selection?.onStateChange,
|
|
1790
1905
|
containerElement: containerDivRef,
|
|
1791
1906
|
formats: selection?.formats,
|
|
1907
|
+
navigation,
|
|
1792
1908
|
disableAutoClipboard: true
|
|
1793
1909
|
});
|
|
1794
1910
|
const effects = selection?.effects;
|
|
@@ -1797,12 +1913,6 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
1797
1913
|
return effects(selectionManager);
|
|
1798
1914
|
}
|
|
1799
1915
|
}, [selectionManager, effects]);
|
|
1800
|
-
const [internalCellData, setInternalCellData] = import_react4.useState(new Map);
|
|
1801
|
-
const [retryingEdit, setRetryingEdit] = import_react4.useState(null);
|
|
1802
|
-
const retryingEditVersionRef = import_react4.useRef(0);
|
|
1803
|
-
const cellData = controlledCellData ?? internalCellData;
|
|
1804
|
-
const cellDataRef = import_react4.useRef(cellData);
|
|
1805
|
-
cellDataRef.current = cellData;
|
|
1806
1916
|
const isControlled = controlledCellData !== undefined;
|
|
1807
1917
|
const prevIsControlledRef = import_react4.useRef(isControlled);
|
|
1808
1918
|
selectionManager.getCellsWithData = (area) => {
|
|
@@ -1978,7 +2088,7 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
1978
2088
|
ctx.lineWidth = 1;
|
|
1979
2089
|
let x = gridDims.headerWidth;
|
|
1980
2090
|
let colIndex = 0;
|
|
1981
|
-
while (x < viewport.scrollX + gridDims.headerWidth) {
|
|
2091
|
+
while (x < viewport.scrollX + gridDims.headerWidth && (finiteColumnCount === undefined || colIndex < finiteColumnCount)) {
|
|
1982
2092
|
const colWidth = (columnWidths.get(indexToColumn(colIndex)) || DEFAULT_CELL_WIDTH) * viewport.zoom;
|
|
1983
2093
|
if (x + colWidth > viewport.scrollX + gridDims.headerWidth) {
|
|
1984
2094
|
break;
|
|
@@ -1986,7 +2096,7 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
1986
2096
|
x += colWidth;
|
|
1987
2097
|
colIndex++;
|
|
1988
2098
|
}
|
|
1989
|
-
while (x - viewport.scrollX < width) {
|
|
2099
|
+
while (x - viewport.scrollX < width && (finiteColumnCount === undefined || colIndex < finiteColumnCount)) {
|
|
1990
2100
|
const drawX = x - viewport.scrollX;
|
|
1991
2101
|
if (drawX >= gridDims.headerWidth && drawX <= width) {
|
|
1992
2102
|
ctx.beginPath();
|
|
@@ -2000,7 +2110,7 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2000
2110
|
}
|
|
2001
2111
|
let y = gridDims.headerHeight;
|
|
2002
2112
|
let rowNum = 1;
|
|
2003
|
-
while (y < viewport.scrollY + gridDims.headerHeight) {
|
|
2113
|
+
while (y < viewport.scrollY + gridDims.headerHeight && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
|
|
2004
2114
|
const rowHeight = (rowHeights.get(rowNum) || DEFAULT_CELL_HEIGHT) * viewport.zoom;
|
|
2005
2115
|
if (y + rowHeight > viewport.scrollY + gridDims.headerHeight) {
|
|
2006
2116
|
break;
|
|
@@ -2008,7 +2118,7 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2008
2118
|
y += rowHeight;
|
|
2009
2119
|
rowNum++;
|
|
2010
2120
|
}
|
|
2011
|
-
while (y - viewport.scrollY < height) {
|
|
2121
|
+
while (y - viewport.scrollY < height && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
|
|
2012
2122
|
const drawY = y - viewport.scrollY;
|
|
2013
2123
|
if (drawY >= gridDims.headerHeight && drawY <= height) {
|
|
2014
2124
|
ctx.beginPath();
|
|
@@ -2022,6 +2132,8 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2022
2132
|
}
|
|
2023
2133
|
}, [
|
|
2024
2134
|
columnWidths,
|
|
2135
|
+
finiteColumnCount,
|
|
2136
|
+
finiteRowCount,
|
|
2025
2137
|
gridDims.headerHeight,
|
|
2026
2138
|
gridDims.headerWidth,
|
|
2027
2139
|
height,
|
|
@@ -2039,7 +2151,7 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2039
2151
|
const cells = [];
|
|
2040
2152
|
let x = 0;
|
|
2041
2153
|
let startCol = 0;
|
|
2042
|
-
while (x < viewport.scrollX) {
|
|
2154
|
+
while (x < viewport.scrollX && (finiteColumnCount === undefined || startCol < finiteColumnCount)) {
|
|
2043
2155
|
const colWidth = (columnWidths.get(indexToColumn(startCol)) || DEFAULT_CELL_WIDTH) * viewport.zoom;
|
|
2044
2156
|
if (x + colWidth > viewport.scrollX) {
|
|
2045
2157
|
break;
|
|
@@ -2049,7 +2161,7 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2049
2161
|
}
|
|
2050
2162
|
let y = 0;
|
|
2051
2163
|
let startRow = 0;
|
|
2052
|
-
while (y < viewport.scrollY) {
|
|
2164
|
+
while (y < viewport.scrollY && (finiteRowCount === undefined || startRow < finiteRowCount)) {
|
|
2053
2165
|
const rowHeight = (rowHeights.get(startRow + 1) || DEFAULT_CELL_HEIGHT) * viewport.zoom;
|
|
2054
2166
|
if (y + rowHeight > viewport.scrollY) {
|
|
2055
2167
|
break;
|
|
@@ -2059,10 +2171,10 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2059
2171
|
}
|
|
2060
2172
|
let rowY = y;
|
|
2061
2173
|
let row = startRow;
|
|
2062
|
-
while (rowY < viewport.scrollY + height) {
|
|
2174
|
+
while (rowY < viewport.scrollY + height && (finiteRowCount === undefined || row < finiteRowCount)) {
|
|
2063
2175
|
let colX = x;
|
|
2064
2176
|
let col = startCol;
|
|
2065
|
-
while (colX < viewport.scrollX + width) {
|
|
2177
|
+
while (colX < viewport.scrollX + width && (finiteColumnCount === undefined || col < finiteColumnCount)) {
|
|
2066
2178
|
const colKey = indexToColumn(col);
|
|
2067
2179
|
const rowNum = getRowNumber(row);
|
|
2068
2180
|
const cellKey = `${colKey}${rowNum}`;
|
|
@@ -2083,6 +2195,8 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2083
2195
|
}, [
|
|
2084
2196
|
cellData,
|
|
2085
2197
|
columnWidths,
|
|
2198
|
+
finiteColumnCount,
|
|
2199
|
+
finiteRowCount,
|
|
2086
2200
|
height,
|
|
2087
2201
|
rowHeights,
|
|
2088
2202
|
viewport.scrollX,
|
|
@@ -2110,6 +2224,64 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2110
2224
|
}
|
|
2111
2225
|
return { x, y };
|
|
2112
2226
|
}, [columnWidths, rowHeights]);
|
|
2227
|
+
const clampViewportOffsets = import_react4.useCallback((scrollX, scrollY, zoom) => {
|
|
2228
|
+
const bodyWidth = Math.max(0, width - HEADER_WIDTH * zoom);
|
|
2229
|
+
const bodyHeight = Math.max(0, height - HEADER_HEIGHT * zoom);
|
|
2230
|
+
const maxScrollX = finiteColumnCount === undefined ? Infinity : Math.max(0, getColumnEdgePosition(finiteColumnCount, columnWidths) * zoom - bodyWidth);
|
|
2231
|
+
const maxScrollY = finiteRowCount === undefined ? Infinity : Math.max(0, getRowEdgePosition(finiteRowCount, rowHeights) * zoom - bodyHeight);
|
|
2232
|
+
return {
|
|
2233
|
+
scrollX: Math.min(Math.max(0, scrollX), maxScrollX),
|
|
2234
|
+
scrollY: Math.min(Math.max(0, scrollY), maxScrollY)
|
|
2235
|
+
};
|
|
2236
|
+
}, [columnWidths, finiteColumnCount, finiteRowCount, height, rowHeights, width]);
|
|
2237
|
+
const revealCell = import_react4.useCallback((cell, options = {}) => {
|
|
2238
|
+
if (finiteRowCount === 0 || finiteColumnCount === 0)
|
|
2239
|
+
return;
|
|
2240
|
+
if (!Number.isFinite(cell.row) || !Number.isFinite(cell.col))
|
|
2241
|
+
return;
|
|
2242
|
+
const requestedRow = Math.max(0, Math.floor(cell.row));
|
|
2243
|
+
const requestedCol = Math.max(0, Math.floor(cell.col));
|
|
2244
|
+
const row = finiteRowCount === undefined ? requestedRow : Math.min(requestedRow, finiteRowCount - 1);
|
|
2245
|
+
const col = finiteColumnCount === undefined ? requestedCol : Math.min(requestedCol, finiteColumnCount - 1);
|
|
2246
|
+
setViewport((previous) => {
|
|
2247
|
+
const bodyWidth = Math.max(0, width - HEADER_WIDTH * previous.zoom);
|
|
2248
|
+
const bodyHeight = Math.max(0, height - HEADER_HEIGHT * previous.zoom);
|
|
2249
|
+
const left = getColumnEdgePosition(col, columnWidths) * previous.zoom;
|
|
2250
|
+
const right = getColumnEdgePosition(col + 1, columnWidths) * previous.zoom;
|
|
2251
|
+
const top = getRowEdgePosition(row, rowHeights) * previous.zoom;
|
|
2252
|
+
const bottom = getRowEdgePosition(row + 1, rowHeights) * previous.zoom;
|
|
2253
|
+
const desired = clampViewportOffsets(alignScrollOffset(previous.scrollX, left, right, bodyWidth, options.columnAlign ?? "nearest"), alignScrollOffset(previous.scrollY, top, bottom, bodyHeight, options.rowAlign ?? "nearest"), previous.zoom);
|
|
2254
|
+
if (desired.scrollX === previous.scrollX && desired.scrollY === previous.scrollY) {
|
|
2255
|
+
return previous;
|
|
2256
|
+
}
|
|
2257
|
+
return { ...previous, ...desired };
|
|
2258
|
+
});
|
|
2259
|
+
}, [
|
|
2260
|
+
clampViewportOffsets,
|
|
2261
|
+
columnWidths,
|
|
2262
|
+
finiteColumnCount,
|
|
2263
|
+
finiteRowCount,
|
|
2264
|
+
height,
|
|
2265
|
+
rowHeights,
|
|
2266
|
+
width
|
|
2267
|
+
]);
|
|
2268
|
+
const scrollToCell = import_react4.useCallback((cell, options = {}) => {
|
|
2269
|
+
const align = options.align ?? "nearest";
|
|
2270
|
+
revealCell(cell, { rowAlign: align, columnAlign: align });
|
|
2271
|
+
}, [revealCell]);
|
|
2272
|
+
import_react4.useEffect(() => selectionManager.listenToViewportRequest((request) => {
|
|
2273
|
+
const isVertical = request.direction === "up" || request.direction === "down";
|
|
2274
|
+
revealCell(request.cell, {
|
|
2275
|
+
rowAlign: isVertical ? request.align : "nearest",
|
|
2276
|
+
columnAlign: isVertical ? "nearest" : request.align
|
|
2277
|
+
});
|
|
2278
|
+
}), [revealCell, selectionManager]);
|
|
2279
|
+
import_react4.useEffect(() => {
|
|
2280
|
+
setViewport((previous) => {
|
|
2281
|
+
const next = clampViewportOffsets(previous.scrollX, previous.scrollY, previous.zoom);
|
|
2282
|
+
return next.scrollX === previous.scrollX && next.scrollY === previous.scrollY ? previous : { ...previous, ...next };
|
|
2283
|
+
});
|
|
2284
|
+
}, [clampViewportOffsets]);
|
|
2113
2285
|
import_react4.default.useEffect(() => {
|
|
2114
2286
|
return selectionManager.listenToUpdateData((updates) => {
|
|
2115
2287
|
const editingState = selectionManager.isEditing;
|
|
@@ -2184,12 +2356,11 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2184
2356
|
const handleWheel = import_react4.useCallback((e) => {
|
|
2185
2357
|
e.preventDefault();
|
|
2186
2358
|
e.stopPropagation();
|
|
2187
|
-
setViewport((
|
|
2188
|
-
...
|
|
2189
|
-
scrollX
|
|
2190
|
-
scrollY: Math.max(0, prev.scrollY + e.deltaY)
|
|
2359
|
+
setViewport((previous) => ({
|
|
2360
|
+
...previous,
|
|
2361
|
+
...clampViewportOffsets(previous.scrollX + e.deltaX, previous.scrollY + e.deltaY, previous.zoom)
|
|
2191
2362
|
}));
|
|
2192
|
-
}, []);
|
|
2363
|
+
}, [clampViewportOffsets]);
|
|
2193
2364
|
const commitOverlayChildren = import_react4.useCallback((children) => {
|
|
2194
2365
|
overlayChildrenRef.current = children;
|
|
2195
2366
|
if (onOverlayChildrenChange) {
|
|
@@ -2608,7 +2779,7 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2608
2779
|
const headers = [];
|
|
2609
2780
|
let x = 0;
|
|
2610
2781
|
let startCol = 0;
|
|
2611
|
-
while (x < viewport.scrollX) {
|
|
2782
|
+
while (x < viewport.scrollX && (finiteColumnCount === undefined || startCol < finiteColumnCount)) {
|
|
2612
2783
|
const col = indexToColumn(startCol);
|
|
2613
2784
|
const colWidth = getColumnWidth(col) * viewport.zoom;
|
|
2614
2785
|
if (x + colWidth > viewport.scrollX) {
|
|
@@ -2617,7 +2788,7 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2617
2788
|
x += colWidth;
|
|
2618
2789
|
startCol++;
|
|
2619
2790
|
}
|
|
2620
|
-
while (x < viewport.scrollX + width) {
|
|
2791
|
+
while (x < viewport.scrollX + width && (finiteColumnCount === undefined || startCol < finiteColumnCount)) {
|
|
2621
2792
|
const col = indexToColumn(startCol);
|
|
2622
2793
|
const colWidth = getColumnWidth(col) * viewport.zoom;
|
|
2623
2794
|
headers.push({
|
|
@@ -2628,14 +2799,14 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2628
2799
|
startCol++;
|
|
2629
2800
|
}
|
|
2630
2801
|
return headers;
|
|
2631
|
-
}, [width, viewport, getColumnWidth]);
|
|
2802
|
+
}, [finiteColumnCount, width, viewport, getColumnWidth]);
|
|
2632
2803
|
const getVisibleRowHeaders = import_react4.useCallback(() => {
|
|
2633
2804
|
if (!height)
|
|
2634
2805
|
return [];
|
|
2635
2806
|
const headers = [];
|
|
2636
2807
|
let y = 0;
|
|
2637
2808
|
let rowNum = 1;
|
|
2638
|
-
while (y < viewport.scrollY) {
|
|
2809
|
+
while (y < viewport.scrollY && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
|
|
2639
2810
|
const rowHeight = getRowHeight(rowNum) * viewport.zoom;
|
|
2640
2811
|
if (y + rowHeight > viewport.scrollY) {
|
|
2641
2812
|
break;
|
|
@@ -2643,7 +2814,7 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2643
2814
|
y += rowHeight;
|
|
2644
2815
|
rowNum++;
|
|
2645
2816
|
}
|
|
2646
|
-
while (y < viewport.scrollY + height) {
|
|
2817
|
+
while (y < viewport.scrollY + height && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
|
|
2647
2818
|
const rowHeight = getRowHeight(rowNum) * viewport.zoom;
|
|
2648
2819
|
headers.push({
|
|
2649
2820
|
row: rowNum,
|
|
@@ -2653,7 +2824,7 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2653
2824
|
rowNum++;
|
|
2654
2825
|
}
|
|
2655
2826
|
return headers;
|
|
2656
|
-
}, [height, viewport, getRowHeight]);
|
|
2827
|
+
}, [finiteRowCount, height, viewport, getRowHeight]);
|
|
2657
2828
|
const { visibleOverlays, overlaysToRender } = import_react4.useMemo(() => {
|
|
2658
2829
|
if (!width || !height || !overlayChildren) {
|
|
2659
2830
|
return { visibleOverlays: [], overlaysToRender: [] };
|
|
@@ -2701,6 +2872,43 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2701
2872
|
const visibleColumnHeaders = import_react4.default.useMemo(() => getVisibleColumnHeaders(), [getVisibleColumnHeaders]);
|
|
2702
2873
|
const visibleRowHeaders = import_react4.default.useMemo(() => getVisibleRowHeaders(), [getVisibleRowHeaders]);
|
|
2703
2874
|
const hasFocus = import_selection_manager.useSelectionManager(selectionManager, () => selectionManager.hasFocus);
|
|
2875
|
+
const referenceSelection = import_selection_manager.useSelectionManager(selectionManager, () => selectionManager.referenceSelection);
|
|
2876
|
+
const referenceSelectionRect = import_react4.useMemo(() => {
|
|
2877
|
+
if (referenceSelection.type === "none")
|
|
2878
|
+
return;
|
|
2879
|
+
if (finiteRowCount === 0 || finiteColumnCount === 0)
|
|
2880
|
+
return;
|
|
2881
|
+
const range = referenceSelection.range;
|
|
2882
|
+
const lastVisibleRow = visibleCells.reduce((maximum, cell) => Math.max(maximum, cell.row - 1), range.start.row);
|
|
2883
|
+
const lastVisibleCol = visibleCells.reduce((maximum, cell) => Math.max(maximum, columnToIndex(cell.col)), range.start.col);
|
|
2884
|
+
const rawEndRow = range.end.row.type === "number" ? range.end.row.value : finiteRowCount === undefined ? lastVisibleRow : finiteRowCount - 1;
|
|
2885
|
+
const rawEndCol = range.end.col.type === "number" ? range.end.col.value : finiteColumnCount === undefined ? lastVisibleCol : finiteColumnCount - 1;
|
|
2886
|
+
const maxRow = finiteRowCount === undefined ? Infinity : finiteRowCount - 1;
|
|
2887
|
+
const maxCol = finiteColumnCount === undefined ? Infinity : finiteColumnCount - 1;
|
|
2888
|
+
const startRow = Math.min(Math.max(0, Math.min(range.start.row, rawEndRow)), maxRow);
|
|
2889
|
+
const endRow = Math.min(Math.max(0, Math.max(range.start.row, rawEndRow)), maxRow);
|
|
2890
|
+
const startCol = Math.min(Math.max(0, Math.min(range.start.col, rawEndCol)), maxCol);
|
|
2891
|
+
const endCol = Math.min(Math.max(0, Math.max(range.start.col, rawEndCol)), maxCol);
|
|
2892
|
+
const rect = getRectFromCellSnapAnchor({
|
|
2893
|
+
startCol,
|
|
2894
|
+
startRow,
|
|
2895
|
+
endCol: endCol + 1,
|
|
2896
|
+
endRow: endRow + 1
|
|
2897
|
+
}, columnWidths, rowHeights);
|
|
2898
|
+
return {
|
|
2899
|
+
left: HEADER_WIDTH + rect.x,
|
|
2900
|
+
top: HEADER_HEIGHT + rect.y,
|
|
2901
|
+
width: rect.width,
|
|
2902
|
+
height: rect.height
|
|
2903
|
+
};
|
|
2904
|
+
}, [
|
|
2905
|
+
columnWidths,
|
|
2906
|
+
finiteColumnCount,
|
|
2907
|
+
finiteRowCount,
|
|
2908
|
+
referenceSelection,
|
|
2909
|
+
rowHeights,
|
|
2910
|
+
visibleCells
|
|
2911
|
+
]);
|
|
2704
2912
|
import_react4.default.useEffect(() => {
|
|
2705
2913
|
if (!containerDivRef || !hasFocus || !parentSelected) {
|
|
2706
2914
|
return;
|
|
@@ -2718,8 +2926,9 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2718
2926
|
},
|
|
2719
2927
|
blur: () => {
|
|
2720
2928
|
selectionManager.blur();
|
|
2721
|
-
}
|
|
2722
|
-
|
|
2929
|
+
},
|
|
2930
|
+
scrollToCell
|
|
2931
|
+
}), [scrollToCell, selectionManager]);
|
|
2723
2932
|
return /* @__PURE__ */ jsx_runtime3.jsx("div", {
|
|
2724
2933
|
className: cn("rsp-root", className),
|
|
2725
2934
|
"data-rsp-scheme": colorScheme,
|
|
@@ -2823,28 +3032,38 @@ var Spreadsheet = import_react4.forwardRef(({
|
|
|
2823
3032
|
zIndex: 40
|
|
2824
3033
|
}
|
|
2825
3034
|
}),
|
|
2826
|
-
/* @__PURE__ */ jsx_runtime3.
|
|
3035
|
+
/* @__PURE__ */ jsx_runtime3.jsxs("div", {
|
|
2827
3036
|
className: "rsp-cells",
|
|
2828
3037
|
"data-testid": "spreadsheet-cells",
|
|
2829
3038
|
style: {
|
|
2830
3039
|
transform: `translate(${-viewport.scrollX}px, ${-viewport.scrollY}px) scale(${viewport.zoom})`,
|
|
2831
3040
|
transformOrigin: "0 0"
|
|
2832
3041
|
},
|
|
2833
|
-
children:
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
3042
|
+
children: [
|
|
3043
|
+
visibleCells.map((cell) => /* @__PURE__ */ jsx_runtime3.jsx(CellComponent, {
|
|
3044
|
+
value: cellData.get(`${cell.col}${cell.row}`) ?? "",
|
|
3045
|
+
row: cell.row,
|
|
3046
|
+
col: cell.col,
|
|
3047
|
+
selectionManager,
|
|
3048
|
+
cellToPixelBase,
|
|
3049
|
+
getColumnWidth,
|
|
3050
|
+
getRowHeight,
|
|
3051
|
+
customCellStyle,
|
|
3052
|
+
customCellRenderer,
|
|
3053
|
+
inputValue: cellData.get(`${cell.col}${cell.row}`) ?? "",
|
|
3054
|
+
editInputVersion: retryingEdit?.rowIndex === cell.row - 1 && retryingEdit?.colIndex === columnToIndex(cell.col) ? retryingEdit.version : 0,
|
|
3055
|
+
width,
|
|
3056
|
+
height
|
|
3057
|
+
}, `${cell.col}${cell.row}`)),
|
|
3058
|
+
referenceSelectionRect && referenceSelection.type !== "none" && /* @__PURE__ */ jsx_runtime3.jsx("div", {
|
|
3059
|
+
"aria-hidden": "true",
|
|
3060
|
+
className: cn("rsp-reference-selection", `rsp-reference-selection-${referenceSelection.type}`),
|
|
3061
|
+
"data-testid": "spreadsheet-reference-selection",
|
|
3062
|
+
"data-reference-id": referenceSelection.id,
|
|
3063
|
+
"data-reference-phase": referenceSelection.type,
|
|
3064
|
+
style: referenceSelectionRect
|
|
3065
|
+
})
|
|
3066
|
+
]
|
|
2848
3067
|
}),
|
|
2849
3068
|
components && overlayChildren && overlayChildren.length > 0 && /* @__PURE__ */ jsx_runtime3.jsxs("div", {
|
|
2850
3069
|
ref: overlayContainerRef,
|
|
@@ -3450,6 +3669,42 @@ function FormulaSheet({
|
|
|
3450
3669
|
const clipboardManager = import_react6.default.useMemo(() => controlledClipboardManager ?? new WorkbookClipboardManager(engine), [controlledClipboardManager, engine]);
|
|
3451
3670
|
const state = useEngine(engine);
|
|
3452
3671
|
const sheet = state.workbooks.get(workbookName)?.sheets.get(sheetName);
|
|
3672
|
+
const getEngineTableAt = import_react6.useCallback((cell) => {
|
|
3673
|
+
const table = engine.isCellInTable({
|
|
3674
|
+
workbookName,
|
|
3675
|
+
sheetName,
|
|
3676
|
+
rowIndex: cell.row,
|
|
3677
|
+
colIndex: cell.col
|
|
3678
|
+
});
|
|
3679
|
+
if (!table)
|
|
3680
|
+
return;
|
|
3681
|
+
const endRow = table.endRow.type === "number" ? { type: "number", value: table.endRow.value } : { type: "infinity" };
|
|
3682
|
+
const endCol = {
|
|
3683
|
+
type: "number",
|
|
3684
|
+
value: table.start.colIndex + table.headers.size - 1
|
|
3685
|
+
};
|
|
3686
|
+
const hasDataRows = endRow.type === "infinity" || endRow.value > table.start.rowIndex;
|
|
3687
|
+
return {
|
|
3688
|
+
id: `${table.workbookName}/${table.name}`,
|
|
3689
|
+
bounds: {
|
|
3690
|
+
start: { row: table.start.rowIndex, col: table.start.colIndex },
|
|
3691
|
+
end: { row: endRow, col: endCol }
|
|
3692
|
+
},
|
|
3693
|
+
...hasDataRows ? {
|
|
3694
|
+
dataBounds: {
|
|
3695
|
+
start: {
|
|
3696
|
+
row: table.start.rowIndex + 1,
|
|
3697
|
+
col: table.start.colIndex
|
|
3698
|
+
},
|
|
3699
|
+
end: { row: endRow, col: endCol }
|
|
3700
|
+
}
|
|
3701
|
+
} : {}
|
|
3702
|
+
};
|
|
3703
|
+
}, [engine, sheetName, workbookName]);
|
|
3704
|
+
const formulaNavigation = import_react6.useMemo(() => ({
|
|
3705
|
+
...selection?.navigation,
|
|
3706
|
+
getTableAt: selection?.navigation?.getTableAt ?? getEngineTableAt
|
|
3707
|
+
}), [getEngineTableAt, selection?.navigation]);
|
|
3453
3708
|
const onCellDataChange = import_react6.useCallback((updatedSpreadsheet) => {
|
|
3454
3709
|
const data = new Map(updatedSpreadsheet);
|
|
3455
3710
|
data.forEach((value, key) => {
|
|
@@ -3623,6 +3878,7 @@ function FormulaSheet({
|
|
|
3623
3878
|
parentSelected: isSelected,
|
|
3624
3879
|
selection: {
|
|
3625
3880
|
...selection,
|
|
3881
|
+
navigation: formulaNavigation,
|
|
3626
3882
|
effects: selectionManagerEffects
|
|
3627
3883
|
},
|
|
3628
3884
|
containerProps: isSelected && selectedArea ? {
|
|
@@ -4161,4 +4417,4 @@ var useClickAway = (ref, callback) => {
|
|
|
4161
4417
|
}, [ref, callback]);
|
|
4162
4418
|
};
|
|
4163
4419
|
|
|
4164
|
-
//# debugId=
|
|
4420
|
+
//# debugId=5EB66D4C826F4D2464756E2164756E21
|