@ricsam/react-spreadsheets 0.0.7 → 0.0.9
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 +86 -4
- package/dist/cjs/lib.cjs +411 -62
- package/dist/cjs/lib.cjs.map +5 -5
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/lib.mjs +412 -63
- package/dist/mjs/lib.mjs.map +5 -5
- package/dist/mjs/package.json +1 -1
- package/dist/styles.css +36 -0
- package/dist/types/lib.d.ts +3 -1
- package/dist/types/spreadsheet/spreadsheet.d.ts +18 -1
- package/dist/types/workbook/workbook-selection-manager.d.ts +23 -3
- package/package.json +3 -3
package/dist/mjs/lib.mjs
CHANGED
|
@@ -1349,6 +1349,124 @@ 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 (itemEnd - itemStart > viewportSize) {
|
|
1368
|
+
if (itemEnd <= current)
|
|
1369
|
+
return itemEnd - viewportSize;
|
|
1370
|
+
if (itemStart >= current + viewportSize)
|
|
1371
|
+
return itemStart;
|
|
1372
|
+
return current;
|
|
1373
|
+
}
|
|
1374
|
+
if (itemStart < current)
|
|
1375
|
+
return itemStart;
|
|
1376
|
+
if (itemEnd > current + viewportSize)
|
|
1377
|
+
return itemEnd - viewportSize;
|
|
1378
|
+
return current;
|
|
1379
|
+
};
|
|
1380
|
+
var normalizeScrollRangeAxis = (start, end, finiteCount) => {
|
|
1381
|
+
if (!Number.isFinite(start) || end.type === "number" && !Number.isFinite(end.value)) {
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
const normalizedStart = Math.max(0, Math.floor(start));
|
|
1385
|
+
const normalizedEnd = end.type === "infinity" ? finiteCount === undefined ? normalizedStart : Math.max(0, finiteCount - 1) : Math.max(0, Math.floor(end.value));
|
|
1386
|
+
const lower = Math.min(normalizedStart, normalizedEnd);
|
|
1387
|
+
const upper = Math.max(normalizedStart, normalizedEnd);
|
|
1388
|
+
if (finiteCount === undefined)
|
|
1389
|
+
return { start: lower, end: upper };
|
|
1390
|
+
if (finiteCount === 0)
|
|
1391
|
+
return;
|
|
1392
|
+
const lastIndex = finiteCount - 1;
|
|
1393
|
+
return {
|
|
1394
|
+
start: Math.min(lower, lastIndex),
|
|
1395
|
+
end: Math.min(upper, lastIndex)
|
|
1396
|
+
};
|
|
1397
|
+
};
|
|
1398
|
+
var hasCellData = (value) => value !== undefined && value !== "";
|
|
1399
|
+
var getSparseUsedRange = (cellData, rowCount, columnCount) => {
|
|
1400
|
+
let minRow = Infinity;
|
|
1401
|
+
let maxRow = -1;
|
|
1402
|
+
let minCol = Infinity;
|
|
1403
|
+
let maxCol = -1;
|
|
1404
|
+
for (const [reference, value] of cellData) {
|
|
1405
|
+
if (!hasCellData(value))
|
|
1406
|
+
continue;
|
|
1407
|
+
let rowIndex;
|
|
1408
|
+
let columnIndex;
|
|
1409
|
+
try {
|
|
1410
|
+
({ rowIndex, columnIndex } = parseCellReference(reference));
|
|
1411
|
+
} catch {
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
if (rowIndex < 0 || columnIndex < 0 || rowCount !== undefined && rowIndex >= rowCount || columnCount !== undefined && columnIndex >= columnCount) {
|
|
1415
|
+
continue;
|
|
1416
|
+
}
|
|
1417
|
+
minRow = Math.min(minRow, rowIndex);
|
|
1418
|
+
maxRow = Math.max(maxRow, rowIndex);
|
|
1419
|
+
minCol = Math.min(minCol, columnIndex);
|
|
1420
|
+
maxCol = Math.max(maxCol, columnIndex);
|
|
1421
|
+
}
|
|
1422
|
+
if (maxRow < 0 || maxCol < 0)
|
|
1423
|
+
return;
|
|
1424
|
+
return {
|
|
1425
|
+
start: { row: minRow, col: minCol },
|
|
1426
|
+
end: {
|
|
1427
|
+
row: { type: "number", value: maxRow },
|
|
1428
|
+
col: { type: "number", value: maxCol }
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
};
|
|
1432
|
+
var resolveSparseNavigationTarget = (request, cellData) => {
|
|
1433
|
+
if (request.kind !== "jump" || request.table)
|
|
1434
|
+
return;
|
|
1435
|
+
const isVertical = request.direction === "up" || request.direction === "down";
|
|
1436
|
+
const movingForward = request.direction === "down" || request.direction === "right";
|
|
1437
|
+
const originAxis = isVertical ? request.origin.row : request.origin.col;
|
|
1438
|
+
const fixedAxis = isVertical ? request.origin.col : request.origin.row;
|
|
1439
|
+
const occupied = new Set;
|
|
1440
|
+
for (const [reference, value] of cellData) {
|
|
1441
|
+
if (!hasCellData(value))
|
|
1442
|
+
continue;
|
|
1443
|
+
let rowIndex;
|
|
1444
|
+
let columnIndex;
|
|
1445
|
+
try {
|
|
1446
|
+
({ rowIndex, columnIndex } = parseCellReference(reference));
|
|
1447
|
+
} catch {
|
|
1448
|
+
continue;
|
|
1449
|
+
}
|
|
1450
|
+
if ((isVertical ? columnIndex : rowIndex) === fixedAxis) {
|
|
1451
|
+
occupied.add(isVertical ? rowIndex : columnIndex);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
const step = movingForward ? 1 : -1;
|
|
1455
|
+
let targetAxis;
|
|
1456
|
+
if (occupied.has(originAxis + step)) {
|
|
1457
|
+
targetAxis = originAxis + step;
|
|
1458
|
+
while (occupied.has(targetAxis + step))
|
|
1459
|
+
targetAxis += step;
|
|
1460
|
+
} else {
|
|
1461
|
+
const candidates = [...occupied].filter((index) => movingForward ? index > originAxis : index < originAxis);
|
|
1462
|
+
if (candidates.length > 0) {
|
|
1463
|
+
targetAxis = movingForward ? Math.min(...candidates) : Math.max(...candidates);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
if (targetAxis === undefined)
|
|
1467
|
+
return;
|
|
1468
|
+
return isVertical ? { row: targetAxis, col: request.origin.col } : { row: request.origin.row, col: targetAxis };
|
|
1469
|
+
};
|
|
1352
1470
|
var CellComponent = React2.memo(({
|
|
1353
1471
|
value,
|
|
1354
1472
|
row,
|
|
@@ -1652,6 +1770,8 @@ var Spreadsheet = forwardRef(({
|
|
|
1652
1770
|
cellData: controlledCellData,
|
|
1653
1771
|
onCellDataChange,
|
|
1654
1772
|
onCellDataChangeError,
|
|
1773
|
+
rowCount,
|
|
1774
|
+
columnCount,
|
|
1655
1775
|
columnWidths: controlledColumnWidths,
|
|
1656
1776
|
onColumnWidthsChange,
|
|
1657
1777
|
rowHeights: controlledRowHeights,
|
|
@@ -1673,14 +1793,35 @@ var Spreadsheet = forwardRef(({
|
|
|
1673
1793
|
const [containerDivRef, setContainerDivRef] = useState4(null);
|
|
1674
1794
|
const colorScheme = useColorScheme(containerDivRef);
|
|
1675
1795
|
const gridLineProbeRef = useRef3(null);
|
|
1796
|
+
const [internalCellData, setInternalCellData] = useState4(new Map);
|
|
1797
|
+
const [retryingEdit, setRetryingEdit] = useState4(null);
|
|
1798
|
+
const retryingEditVersionRef = useRef3(0);
|
|
1799
|
+
const cellData = controlledCellData ?? internalCellData;
|
|
1800
|
+
const cellDataRef = useRef3(cellData);
|
|
1801
|
+
cellDataRef.current = cellData;
|
|
1802
|
+
const finiteRowCount = normalizeFiniteCount(rowCount);
|
|
1803
|
+
const finiteColumnCount = normalizeFiniteCount(columnCount);
|
|
1804
|
+
const gridCountsRef = useRef3({ rows: finiteRowCount, columns: finiteColumnCount });
|
|
1805
|
+
gridCountsRef.current = { rows: finiteRowCount, columns: finiteColumnCount };
|
|
1806
|
+
const getNumRows = useCallback3(() => countToSelectionBound(gridCountsRef.current.rows), []);
|
|
1807
|
+
const getNumCols = useCallback3(() => countToSelectionBound(gridCountsRef.current.columns), []);
|
|
1808
|
+
const getDefaultUsedRange = useCallback3(() => getSparseUsedRange(cellDataRef.current, gridCountsRef.current.rows, gridCountsRef.current.columns), []);
|
|
1809
|
+
const defaultResolveTarget = useCallback3((request) => resolveSparseNavigationTarget(request, cellDataRef.current), []);
|
|
1810
|
+
const consumerNavigation = selection?.navigation;
|
|
1811
|
+
const navigation = useMemo(() => ({
|
|
1812
|
+
getUsedRange: consumerNavigation?.getUsedRange ?? getDefaultUsedRange,
|
|
1813
|
+
...consumerNavigation?.getTableAt ? { getTableAt: consumerNavigation.getTableAt } : {},
|
|
1814
|
+
resolveTarget: consumerNavigation?.resolveTarget ?? defaultResolveTarget
|
|
1815
|
+
}), [consumerNavigation, defaultResolveTarget, getDefaultUsedRange]);
|
|
1676
1816
|
const selectionManager = useInitializeSelectionManager({
|
|
1677
|
-
getNumCols
|
|
1678
|
-
getNumRows
|
|
1817
|
+
getNumCols,
|
|
1818
|
+
getNumRows,
|
|
1679
1819
|
initialState: selection?.initialState,
|
|
1680
1820
|
state: selection?.state,
|
|
1681
1821
|
onStateChange: selection?.onStateChange,
|
|
1682
1822
|
containerElement: containerDivRef,
|
|
1683
1823
|
formats: selection?.formats,
|
|
1824
|
+
navigation,
|
|
1684
1825
|
disableAutoClipboard: true
|
|
1685
1826
|
});
|
|
1686
1827
|
const effects = selection?.effects;
|
|
@@ -1689,12 +1830,6 @@ var Spreadsheet = forwardRef(({
|
|
|
1689
1830
|
return effects(selectionManager);
|
|
1690
1831
|
}
|
|
1691
1832
|
}, [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
1833
|
const isControlled = controlledCellData !== undefined;
|
|
1699
1834
|
const prevIsControlledRef = useRef3(isControlled);
|
|
1700
1835
|
selectionManager.getCellsWithData = (area) => {
|
|
@@ -1870,7 +2005,7 @@ var Spreadsheet = forwardRef(({
|
|
|
1870
2005
|
ctx.lineWidth = 1;
|
|
1871
2006
|
let x = gridDims.headerWidth;
|
|
1872
2007
|
let colIndex = 0;
|
|
1873
|
-
while (x < viewport.scrollX + gridDims.headerWidth) {
|
|
2008
|
+
while (x < viewport.scrollX + gridDims.headerWidth && (finiteColumnCount === undefined || colIndex < finiteColumnCount)) {
|
|
1874
2009
|
const colWidth = (columnWidths.get(indexToColumn(colIndex)) || DEFAULT_CELL_WIDTH) * viewport.zoom;
|
|
1875
2010
|
if (x + colWidth > viewport.scrollX + gridDims.headerWidth) {
|
|
1876
2011
|
break;
|
|
@@ -1878,7 +2013,7 @@ var Spreadsheet = forwardRef(({
|
|
|
1878
2013
|
x += colWidth;
|
|
1879
2014
|
colIndex++;
|
|
1880
2015
|
}
|
|
1881
|
-
while (x - viewport.scrollX < width) {
|
|
2016
|
+
while (x - viewport.scrollX < width && (finiteColumnCount === undefined || colIndex < finiteColumnCount)) {
|
|
1882
2017
|
const drawX = x - viewport.scrollX;
|
|
1883
2018
|
if (drawX >= gridDims.headerWidth && drawX <= width) {
|
|
1884
2019
|
ctx.beginPath();
|
|
@@ -1892,7 +2027,7 @@ var Spreadsheet = forwardRef(({
|
|
|
1892
2027
|
}
|
|
1893
2028
|
let y = gridDims.headerHeight;
|
|
1894
2029
|
let rowNum = 1;
|
|
1895
|
-
while (y < viewport.scrollY + gridDims.headerHeight) {
|
|
2030
|
+
while (y < viewport.scrollY + gridDims.headerHeight && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
|
|
1896
2031
|
const rowHeight = (rowHeights.get(rowNum) || DEFAULT_CELL_HEIGHT) * viewport.zoom;
|
|
1897
2032
|
if (y + rowHeight > viewport.scrollY + gridDims.headerHeight) {
|
|
1898
2033
|
break;
|
|
@@ -1900,7 +2035,7 @@ var Spreadsheet = forwardRef(({
|
|
|
1900
2035
|
y += rowHeight;
|
|
1901
2036
|
rowNum++;
|
|
1902
2037
|
}
|
|
1903
|
-
while (y - viewport.scrollY < height) {
|
|
2038
|
+
while (y - viewport.scrollY < height && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
|
|
1904
2039
|
const drawY = y - viewport.scrollY;
|
|
1905
2040
|
if (drawY >= gridDims.headerHeight && drawY <= height) {
|
|
1906
2041
|
ctx.beginPath();
|
|
@@ -1914,6 +2049,8 @@ var Spreadsheet = forwardRef(({
|
|
|
1914
2049
|
}
|
|
1915
2050
|
}, [
|
|
1916
2051
|
columnWidths,
|
|
2052
|
+
finiteColumnCount,
|
|
2053
|
+
finiteRowCount,
|
|
1917
2054
|
gridDims.headerHeight,
|
|
1918
2055
|
gridDims.headerWidth,
|
|
1919
2056
|
height,
|
|
@@ -1931,7 +2068,7 @@ var Spreadsheet = forwardRef(({
|
|
|
1931
2068
|
const cells = [];
|
|
1932
2069
|
let x = 0;
|
|
1933
2070
|
let startCol = 0;
|
|
1934
|
-
while (x < viewport.scrollX) {
|
|
2071
|
+
while (x < viewport.scrollX && (finiteColumnCount === undefined || startCol < finiteColumnCount)) {
|
|
1935
2072
|
const colWidth = (columnWidths.get(indexToColumn(startCol)) || DEFAULT_CELL_WIDTH) * viewport.zoom;
|
|
1936
2073
|
if (x + colWidth > viewport.scrollX) {
|
|
1937
2074
|
break;
|
|
@@ -1941,7 +2078,7 @@ var Spreadsheet = forwardRef(({
|
|
|
1941
2078
|
}
|
|
1942
2079
|
let y = 0;
|
|
1943
2080
|
let startRow = 0;
|
|
1944
|
-
while (y < viewport.scrollY) {
|
|
2081
|
+
while (y < viewport.scrollY && (finiteRowCount === undefined || startRow < finiteRowCount)) {
|
|
1945
2082
|
const rowHeight = (rowHeights.get(startRow + 1) || DEFAULT_CELL_HEIGHT) * viewport.zoom;
|
|
1946
2083
|
if (y + rowHeight > viewport.scrollY) {
|
|
1947
2084
|
break;
|
|
@@ -1951,10 +2088,10 @@ var Spreadsheet = forwardRef(({
|
|
|
1951
2088
|
}
|
|
1952
2089
|
let rowY = y;
|
|
1953
2090
|
let row = startRow;
|
|
1954
|
-
while (rowY < viewport.scrollY + height) {
|
|
2091
|
+
while (rowY < viewport.scrollY + height && (finiteRowCount === undefined || row < finiteRowCount)) {
|
|
1955
2092
|
let colX = x;
|
|
1956
2093
|
let col = startCol;
|
|
1957
|
-
while (colX < viewport.scrollX + width) {
|
|
2094
|
+
while (colX < viewport.scrollX + width && (finiteColumnCount === undefined || col < finiteColumnCount)) {
|
|
1958
2095
|
const colKey = indexToColumn(col);
|
|
1959
2096
|
const rowNum = getRowNumber(row);
|
|
1960
2097
|
const cellKey = `${colKey}${rowNum}`;
|
|
@@ -1975,6 +2112,8 @@ var Spreadsheet = forwardRef(({
|
|
|
1975
2112
|
}, [
|
|
1976
2113
|
cellData,
|
|
1977
2114
|
columnWidths,
|
|
2115
|
+
finiteColumnCount,
|
|
2116
|
+
finiteRowCount,
|
|
1978
2117
|
height,
|
|
1979
2118
|
rowHeights,
|
|
1980
2119
|
viewport.scrollX,
|
|
@@ -2002,6 +2141,79 @@ var Spreadsheet = forwardRef(({
|
|
|
2002
2141
|
}
|
|
2003
2142
|
return { x, y };
|
|
2004
2143
|
}, [columnWidths, rowHeights]);
|
|
2144
|
+
const clampViewportOffsets = useCallback3((scrollX, scrollY, zoom) => {
|
|
2145
|
+
const bodyWidth = Math.max(0, width - HEADER_WIDTH * zoom);
|
|
2146
|
+
const bodyHeight = Math.max(0, height - HEADER_HEIGHT * zoom);
|
|
2147
|
+
const maxScrollX = finiteColumnCount === undefined ? Infinity : Math.max(0, getColumnEdgePosition(finiteColumnCount, columnWidths) * zoom - bodyWidth);
|
|
2148
|
+
const maxScrollY = finiteRowCount === undefined ? Infinity : Math.max(0, getRowEdgePosition(finiteRowCount, rowHeights) * zoom - bodyHeight);
|
|
2149
|
+
return {
|
|
2150
|
+
scrollX: Math.min(Math.max(0, scrollX), maxScrollX),
|
|
2151
|
+
scrollY: Math.min(Math.max(0, scrollY), maxScrollY)
|
|
2152
|
+
};
|
|
2153
|
+
}, [columnWidths, finiteColumnCount, finiteRowCount, height, rowHeights, width]);
|
|
2154
|
+
const revealRange = useCallback3((range, options = {}) => {
|
|
2155
|
+
if (finiteRowCount === 0 || finiteColumnCount === 0)
|
|
2156
|
+
return;
|
|
2157
|
+
const rows = normalizeScrollRangeAxis(range.start.row, range.end.row, finiteRowCount);
|
|
2158
|
+
const columns = normalizeScrollRangeAxis(range.start.col, range.end.col, finiteColumnCount);
|
|
2159
|
+
if (!rows || !columns)
|
|
2160
|
+
return;
|
|
2161
|
+
setViewport((previous) => {
|
|
2162
|
+
const bodyWidth = Math.max(0, width - HEADER_WIDTH * previous.zoom);
|
|
2163
|
+
const bodyHeight = Math.max(0, height - HEADER_HEIGHT * previous.zoom);
|
|
2164
|
+
const left = getColumnEdgePosition(columns.start, columnWidths) * previous.zoom;
|
|
2165
|
+
const right = getColumnEdgePosition(columns.end + 1, columnWidths) * previous.zoom;
|
|
2166
|
+
const top = getRowEdgePosition(rows.start, rowHeights) * previous.zoom;
|
|
2167
|
+
const bottom = getRowEdgePosition(rows.end + 1, rowHeights) * previous.zoom;
|
|
2168
|
+
const desired = clampViewportOffsets(alignScrollOffset(previous.scrollX, left, right, bodyWidth, options.columnAlign ?? "nearest"), alignScrollOffset(previous.scrollY, top, bottom, bodyHeight, options.rowAlign ?? "nearest"), previous.zoom);
|
|
2169
|
+
if (desired.scrollX === previous.scrollX && desired.scrollY === previous.scrollY) {
|
|
2170
|
+
return previous;
|
|
2171
|
+
}
|
|
2172
|
+
return { ...previous, ...desired };
|
|
2173
|
+
});
|
|
2174
|
+
}, [
|
|
2175
|
+
clampViewportOffsets,
|
|
2176
|
+
columnWidths,
|
|
2177
|
+
finiteColumnCount,
|
|
2178
|
+
finiteRowCount,
|
|
2179
|
+
height,
|
|
2180
|
+
rowHeights,
|
|
2181
|
+
width
|
|
2182
|
+
]);
|
|
2183
|
+
const revealCell = useCallback3((cell, options = {}) => {
|
|
2184
|
+
revealRange({
|
|
2185
|
+
start: { row: cell.row, col: cell.col },
|
|
2186
|
+
end: {
|
|
2187
|
+
row: { type: "number", value: cell.row },
|
|
2188
|
+
col: { type: "number", value: cell.col }
|
|
2189
|
+
}
|
|
2190
|
+
}, options);
|
|
2191
|
+
}, [revealRange]);
|
|
2192
|
+
const scrollToCell = useCallback3((cell, options = {}) => {
|
|
2193
|
+
const align = options.align ?? "nearest";
|
|
2194
|
+
revealCell(cell, { rowAlign: align, columnAlign: align });
|
|
2195
|
+
}, [revealCell]);
|
|
2196
|
+
const scrollToRange = useCallback3((range, options = {}) => {
|
|
2197
|
+
const align = options.align ?? "nearest";
|
|
2198
|
+
revealRange(range, { rowAlign: align, columnAlign: align });
|
|
2199
|
+
}, [revealRange]);
|
|
2200
|
+
useEffect3(() => selectionManager.listenToViewportRequest((request) => {
|
|
2201
|
+
if (request.type === "reveal-range") {
|
|
2202
|
+
scrollToRange(request.range, { align: request.align });
|
|
2203
|
+
return;
|
|
2204
|
+
}
|
|
2205
|
+
const isVertical = request.direction === "up" || request.direction === "down";
|
|
2206
|
+
revealCell(request.cell, {
|
|
2207
|
+
rowAlign: isVertical ? request.align : "nearest",
|
|
2208
|
+
columnAlign: isVertical ? "nearest" : request.align
|
|
2209
|
+
});
|
|
2210
|
+
}), [revealCell, scrollToRange, selectionManager]);
|
|
2211
|
+
useEffect3(() => {
|
|
2212
|
+
setViewport((previous) => {
|
|
2213
|
+
const next = clampViewportOffsets(previous.scrollX, previous.scrollY, previous.zoom);
|
|
2214
|
+
return next.scrollX === previous.scrollX && next.scrollY === previous.scrollY ? previous : { ...previous, ...next };
|
|
2215
|
+
});
|
|
2216
|
+
}, [clampViewportOffsets]);
|
|
2005
2217
|
React2.useEffect(() => {
|
|
2006
2218
|
return selectionManager.listenToUpdateData((updates) => {
|
|
2007
2219
|
const editingState = selectionManager.isEditing;
|
|
@@ -2076,12 +2288,11 @@ var Spreadsheet = forwardRef(({
|
|
|
2076
2288
|
const handleWheel = useCallback3((e) => {
|
|
2077
2289
|
e.preventDefault();
|
|
2078
2290
|
e.stopPropagation();
|
|
2079
|
-
setViewport((
|
|
2080
|
-
...
|
|
2081
|
-
scrollX
|
|
2082
|
-
scrollY: Math.max(0, prev.scrollY + e.deltaY)
|
|
2291
|
+
setViewport((previous) => ({
|
|
2292
|
+
...previous,
|
|
2293
|
+
...clampViewportOffsets(previous.scrollX + e.deltaX, previous.scrollY + e.deltaY, previous.zoom)
|
|
2083
2294
|
}));
|
|
2084
|
-
}, []);
|
|
2295
|
+
}, [clampViewportOffsets]);
|
|
2085
2296
|
const commitOverlayChildren = useCallback3((children) => {
|
|
2086
2297
|
overlayChildrenRef.current = children;
|
|
2087
2298
|
if (onOverlayChildrenChange) {
|
|
@@ -2500,7 +2711,7 @@ var Spreadsheet = forwardRef(({
|
|
|
2500
2711
|
const headers = [];
|
|
2501
2712
|
let x = 0;
|
|
2502
2713
|
let startCol = 0;
|
|
2503
|
-
while (x < viewport.scrollX) {
|
|
2714
|
+
while (x < viewport.scrollX && (finiteColumnCount === undefined || startCol < finiteColumnCount)) {
|
|
2504
2715
|
const col = indexToColumn(startCol);
|
|
2505
2716
|
const colWidth = getColumnWidth(col) * viewport.zoom;
|
|
2506
2717
|
if (x + colWidth > viewport.scrollX) {
|
|
@@ -2509,7 +2720,7 @@ var Spreadsheet = forwardRef(({
|
|
|
2509
2720
|
x += colWidth;
|
|
2510
2721
|
startCol++;
|
|
2511
2722
|
}
|
|
2512
|
-
while (x < viewport.scrollX + width) {
|
|
2723
|
+
while (x < viewport.scrollX + width && (finiteColumnCount === undefined || startCol < finiteColumnCount)) {
|
|
2513
2724
|
const col = indexToColumn(startCol);
|
|
2514
2725
|
const colWidth = getColumnWidth(col) * viewport.zoom;
|
|
2515
2726
|
headers.push({
|
|
@@ -2520,14 +2731,14 @@ var Spreadsheet = forwardRef(({
|
|
|
2520
2731
|
startCol++;
|
|
2521
2732
|
}
|
|
2522
2733
|
return headers;
|
|
2523
|
-
}, [width, viewport, getColumnWidth]);
|
|
2734
|
+
}, [finiteColumnCount, width, viewport, getColumnWidth]);
|
|
2524
2735
|
const getVisibleRowHeaders = useCallback3(() => {
|
|
2525
2736
|
if (!height)
|
|
2526
2737
|
return [];
|
|
2527
2738
|
const headers = [];
|
|
2528
2739
|
let y = 0;
|
|
2529
2740
|
let rowNum = 1;
|
|
2530
|
-
while (y < viewport.scrollY) {
|
|
2741
|
+
while (y < viewport.scrollY && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
|
|
2531
2742
|
const rowHeight = getRowHeight(rowNum) * viewport.zoom;
|
|
2532
2743
|
if (y + rowHeight > viewport.scrollY) {
|
|
2533
2744
|
break;
|
|
@@ -2535,7 +2746,7 @@ var Spreadsheet = forwardRef(({
|
|
|
2535
2746
|
y += rowHeight;
|
|
2536
2747
|
rowNum++;
|
|
2537
2748
|
}
|
|
2538
|
-
while (y < viewport.scrollY + height) {
|
|
2749
|
+
while (y < viewport.scrollY + height && (finiteRowCount === undefined || rowNum <= finiteRowCount)) {
|
|
2539
2750
|
const rowHeight = getRowHeight(rowNum) * viewport.zoom;
|
|
2540
2751
|
headers.push({
|
|
2541
2752
|
row: rowNum,
|
|
@@ -2545,7 +2756,7 @@ var Spreadsheet = forwardRef(({
|
|
|
2545
2756
|
rowNum++;
|
|
2546
2757
|
}
|
|
2547
2758
|
return headers;
|
|
2548
|
-
}, [height, viewport, getRowHeight]);
|
|
2759
|
+
}, [finiteRowCount, height, viewport, getRowHeight]);
|
|
2549
2760
|
const { visibleOverlays, overlaysToRender } = useMemo(() => {
|
|
2550
2761
|
if (!width || !height || !overlayChildren) {
|
|
2551
2762
|
return { visibleOverlays: [], overlaysToRender: [] };
|
|
@@ -2593,8 +2804,45 @@ var Spreadsheet = forwardRef(({
|
|
|
2593
2804
|
const visibleColumnHeaders = React2.useMemo(() => getVisibleColumnHeaders(), [getVisibleColumnHeaders]);
|
|
2594
2805
|
const visibleRowHeaders = React2.useMemo(() => getVisibleRowHeaders(), [getVisibleRowHeaders]);
|
|
2595
2806
|
const hasFocus = useSelectionManager(selectionManager, () => selectionManager.hasFocus);
|
|
2807
|
+
const referenceSelection = useSelectionManager(selectionManager, () => selectionManager.referenceSelection);
|
|
2808
|
+
const referenceSelectionRect = useMemo(() => {
|
|
2809
|
+
if (referenceSelection.type === "none")
|
|
2810
|
+
return;
|
|
2811
|
+
if (finiteRowCount === 0 || finiteColumnCount === 0)
|
|
2812
|
+
return;
|
|
2813
|
+
const range = referenceSelection.range;
|
|
2814
|
+
const lastVisibleRow = visibleCells.reduce((maximum, cell) => Math.max(maximum, cell.row - 1), range.start.row);
|
|
2815
|
+
const lastVisibleCol = visibleCells.reduce((maximum, cell) => Math.max(maximum, columnToIndex(cell.col)), range.start.col);
|
|
2816
|
+
const rawEndRow = range.end.row.type === "number" ? range.end.row.value : finiteRowCount === undefined ? lastVisibleRow : finiteRowCount - 1;
|
|
2817
|
+
const rawEndCol = range.end.col.type === "number" ? range.end.col.value : finiteColumnCount === undefined ? lastVisibleCol : finiteColumnCount - 1;
|
|
2818
|
+
const maxRow = finiteRowCount === undefined ? Infinity : finiteRowCount - 1;
|
|
2819
|
+
const maxCol = finiteColumnCount === undefined ? Infinity : finiteColumnCount - 1;
|
|
2820
|
+
const startRow = Math.min(Math.max(0, Math.min(range.start.row, rawEndRow)), maxRow);
|
|
2821
|
+
const endRow = Math.min(Math.max(0, Math.max(range.start.row, rawEndRow)), maxRow);
|
|
2822
|
+
const startCol = Math.min(Math.max(0, Math.min(range.start.col, rawEndCol)), maxCol);
|
|
2823
|
+
const endCol = Math.min(Math.max(0, Math.max(range.start.col, rawEndCol)), maxCol);
|
|
2824
|
+
const rect = getRectFromCellSnapAnchor({
|
|
2825
|
+
startCol,
|
|
2826
|
+
startRow,
|
|
2827
|
+
endCol: endCol + 1,
|
|
2828
|
+
endRow: endRow + 1
|
|
2829
|
+
}, columnWidths, rowHeights);
|
|
2830
|
+
return {
|
|
2831
|
+
left: HEADER_WIDTH + rect.x,
|
|
2832
|
+
top: HEADER_HEIGHT + rect.y,
|
|
2833
|
+
width: rect.width,
|
|
2834
|
+
height: rect.height
|
|
2835
|
+
};
|
|
2836
|
+
}, [
|
|
2837
|
+
columnWidths,
|
|
2838
|
+
finiteColumnCount,
|
|
2839
|
+
finiteRowCount,
|
|
2840
|
+
referenceSelection,
|
|
2841
|
+
rowHeights,
|
|
2842
|
+
visibleCells
|
|
2843
|
+
]);
|
|
2596
2844
|
React2.useEffect(() => {
|
|
2597
|
-
if (!containerDivRef || !
|
|
2845
|
+
if (!containerDivRef || !parentSelected) {
|
|
2598
2846
|
return;
|
|
2599
2847
|
}
|
|
2600
2848
|
containerDivRef.addEventListener("wheel", handleWheel, {
|
|
@@ -2603,15 +2851,17 @@ var Spreadsheet = forwardRef(({
|
|
|
2603
2851
|
return () => {
|
|
2604
2852
|
containerDivRef.removeEventListener("wheel", handleWheel);
|
|
2605
2853
|
};
|
|
2606
|
-
}, [containerDivRef, handleWheel,
|
|
2854
|
+
}, [containerDivRef, handleWheel, parentSelected]);
|
|
2607
2855
|
useImperativeHandle(ref, () => ({
|
|
2608
2856
|
focus: () => {
|
|
2609
2857
|
selectionManager.focus();
|
|
2610
2858
|
},
|
|
2611
2859
|
blur: () => {
|
|
2612
2860
|
selectionManager.blur();
|
|
2613
|
-
}
|
|
2614
|
-
|
|
2861
|
+
},
|
|
2862
|
+
scrollToCell,
|
|
2863
|
+
scrollToRange
|
|
2864
|
+
}), [scrollToCell, scrollToRange, selectionManager]);
|
|
2615
2865
|
return /* @__PURE__ */ jsx3("div", {
|
|
2616
2866
|
className: cn("rsp-root", className),
|
|
2617
2867
|
"data-rsp-scheme": colorScheme,
|
|
@@ -2715,28 +2965,38 @@ var Spreadsheet = forwardRef(({
|
|
|
2715
2965
|
zIndex: 40
|
|
2716
2966
|
}
|
|
2717
2967
|
}),
|
|
2718
|
-
/* @__PURE__ */
|
|
2968
|
+
/* @__PURE__ */ jsxs2("div", {
|
|
2719
2969
|
className: "rsp-cells",
|
|
2720
2970
|
"data-testid": "spreadsheet-cells",
|
|
2721
2971
|
style: {
|
|
2722
2972
|
transform: `translate(${-viewport.scrollX}px, ${-viewport.scrollY}px) scale(${viewport.zoom})`,
|
|
2723
2973
|
transformOrigin: "0 0"
|
|
2724
2974
|
},
|
|
2725
|
-
children:
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2975
|
+
children: [
|
|
2976
|
+
visibleCells.map((cell) => /* @__PURE__ */ jsx3(CellComponent, {
|
|
2977
|
+
value: cellData.get(`${cell.col}${cell.row}`) ?? "",
|
|
2978
|
+
row: cell.row,
|
|
2979
|
+
col: cell.col,
|
|
2980
|
+
selectionManager,
|
|
2981
|
+
cellToPixelBase,
|
|
2982
|
+
getColumnWidth,
|
|
2983
|
+
getRowHeight,
|
|
2984
|
+
customCellStyle,
|
|
2985
|
+
customCellRenderer,
|
|
2986
|
+
inputValue: cellData.get(`${cell.col}${cell.row}`) ?? "",
|
|
2987
|
+
editInputVersion: retryingEdit?.rowIndex === cell.row - 1 && retryingEdit?.colIndex === columnToIndex(cell.col) ? retryingEdit.version : 0,
|
|
2988
|
+
width,
|
|
2989
|
+
height
|
|
2990
|
+
}, `${cell.col}${cell.row}`)),
|
|
2991
|
+
referenceSelectionRect && referenceSelection.type !== "none" && /* @__PURE__ */ jsx3("div", {
|
|
2992
|
+
"aria-hidden": "true",
|
|
2993
|
+
className: cn("rsp-reference-selection", `rsp-reference-selection-${referenceSelection.type}`),
|
|
2994
|
+
"data-testid": "spreadsheet-reference-selection",
|
|
2995
|
+
"data-reference-id": referenceSelection.id,
|
|
2996
|
+
"data-reference-phase": referenceSelection.type,
|
|
2997
|
+
style: referenceSelectionRect
|
|
2998
|
+
})
|
|
2999
|
+
]
|
|
2740
3000
|
}),
|
|
2741
3001
|
components && overlayChildren && overlayChildren.length > 0 && /* @__PURE__ */ jsxs2("div", {
|
|
2742
3002
|
ref: overlayContainerRef,
|
|
@@ -2821,7 +3081,7 @@ import {
|
|
|
2821
3081
|
parseCellReference as parseCellReference2,
|
|
2822
3082
|
getCellReference as getCellReference3
|
|
2823
3083
|
} from "@ricsam/formula-engine";
|
|
2824
|
-
import React3, { useCallback as useCallback4, useRef as useRef4, useState as useState6 } from "react";
|
|
3084
|
+
import React3, { useCallback as useCallback4, useMemo as useMemo2, useRef as useRef4, useState as useState6 } from "react";
|
|
2825
3085
|
|
|
2826
3086
|
// src/utils/use-scheme-root.ts
|
|
2827
3087
|
import { useState as useState5 } from "react";
|
|
@@ -2949,6 +3209,7 @@ class WorkbookSelectionManager {
|
|
|
2949
3209
|
lastFocusedManager = null;
|
|
2950
3210
|
listeners = new Set;
|
|
2951
3211
|
cleanupFunctions = new Map;
|
|
3212
|
+
pendingRangeFocus = null;
|
|
2952
3213
|
smAreaToSpreadsheetRange(area) {
|
|
2953
3214
|
const rowEnd = area.end.row.type === "infinity" ? { type: "infinity", sign: "positive" } : { type: "number", value: area.end.row.value };
|
|
2954
3215
|
const colEnd = area.end.col.type === "infinity" ? { type: "infinity", sign: "positive" } : { type: "number", value: area.end.col.value };
|
|
@@ -2963,6 +3224,47 @@ class WorkbookSelectionManager {
|
|
|
2963
3224
|
}
|
|
2964
3225
|
};
|
|
2965
3226
|
}
|
|
3227
|
+
spreadsheetRangeToSmArea(range) {
|
|
3228
|
+
return {
|
|
3229
|
+
start: { row: range.start.row, col: range.start.col },
|
|
3230
|
+
end: {
|
|
3231
|
+
row: range.end.row.type === "infinity" ? { type: "infinity" } : { type: "number", value: range.end.row.value },
|
|
3232
|
+
col: range.end.col.type === "infinity" ? { type: "infinity" } : { type: "number", value: range.end.col.value }
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
}
|
|
3236
|
+
cloneRangeAddress(address) {
|
|
3237
|
+
return {
|
|
3238
|
+
workbookName: address.workbookName,
|
|
3239
|
+
sheetName: address.sheetName,
|
|
3240
|
+
range: this.smAreaToSpreadsheetRange(this.spreadsheetRangeToSmArea(address.range))
|
|
3241
|
+
};
|
|
3242
|
+
}
|
|
3243
|
+
matchesContext(address, context) {
|
|
3244
|
+
return address.workbookName === context.workbookName && address.sheetName === context.sheetName;
|
|
3245
|
+
}
|
|
3246
|
+
applyRangeFocus(manager, context, request) {
|
|
3247
|
+
const selection = this.spreadsheetRangeToSmArea(request.address.range);
|
|
3248
|
+
manager.setState({ selections: [selection] });
|
|
3249
|
+
manager.focus();
|
|
3250
|
+
manager.revealRange(selection, request.options);
|
|
3251
|
+
this.lastFocusedManager = { sm: manager, context };
|
|
3252
|
+
this.emitChange();
|
|
3253
|
+
}
|
|
3254
|
+
applyPendingRangeFocusOnMount(manager, context) {
|
|
3255
|
+
const pending = this.pendingRangeFocus;
|
|
3256
|
+
if (!pending || !this.matchesContext(pending.address, context))
|
|
3257
|
+
return;
|
|
3258
|
+
queueMicrotask(() => {
|
|
3259
|
+
if (this.pendingRangeFocus !== pending)
|
|
3260
|
+
return;
|
|
3261
|
+
const mountedContext = this.managers.get(manager);
|
|
3262
|
+
if (!mountedContext || !this.matchesContext(pending.address, mountedContext))
|
|
3263
|
+
return;
|
|
3264
|
+
this.pendingRangeFocus = null;
|
|
3265
|
+
this.applyRangeFocus(manager, mountedContext, pending);
|
|
3266
|
+
});
|
|
3267
|
+
}
|
|
2966
3268
|
add(manager, context) {
|
|
2967
3269
|
this.managers.set(manager, context);
|
|
2968
3270
|
const focusCleanup = manager.observeStateChange((state) => state.hasFocus, (hasFocus) => {
|
|
@@ -2988,6 +3290,7 @@ class WorkbookSelectionManager {
|
|
|
2988
3290
|
}
|
|
2989
3291
|
};
|
|
2990
3292
|
this.cleanupFunctions.set(manager, cleanup);
|
|
3293
|
+
this.applyPendingRangeFocusOnMount(manager, context);
|
|
2991
3294
|
return cleanup;
|
|
2992
3295
|
}
|
|
2993
3296
|
getLastFocusedSelectionManager() {
|
|
@@ -3081,22 +3384,31 @@ class WorkbookSelectionManager {
|
|
|
3081
3384
|
return false;
|
|
3082
3385
|
}
|
|
3083
3386
|
focusCell(workbookName, sheetName, row, col) {
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
}
|
|
3092
|
-
}
|
|
3093
|
-
manager.setState({ selections: [selection] });
|
|
3094
|
-
manager.focus();
|
|
3095
|
-
this.lastFocusedManager = { sm: manager, context };
|
|
3096
|
-
this.emitChange();
|
|
3097
|
-
return true;
|
|
3387
|
+
return this.focusRange({
|
|
3388
|
+
workbookName,
|
|
3389
|
+
sheetName,
|
|
3390
|
+
range: {
|
|
3391
|
+
start: { row, col },
|
|
3392
|
+
end: {
|
|
3393
|
+
row: { type: "number", value: row },
|
|
3394
|
+
col: { type: "number", value: col }
|
|
3395
|
+
}
|
|
3098
3396
|
}
|
|
3397
|
+
});
|
|
3398
|
+
}
|
|
3399
|
+
focusRange(address, options = {}) {
|
|
3400
|
+
const request = {
|
|
3401
|
+
address: this.cloneRangeAddress(address),
|
|
3402
|
+
options: { ...options }
|
|
3403
|
+
};
|
|
3404
|
+
for (const [manager, context] of this.managers) {
|
|
3405
|
+
if (!this.matchesContext(request.address, context))
|
|
3406
|
+
continue;
|
|
3407
|
+
this.pendingRangeFocus = null;
|
|
3408
|
+
this.applyRangeFocus(manager, context, request);
|
|
3409
|
+
return true;
|
|
3099
3410
|
}
|
|
3411
|
+
this.pendingRangeFocus = request;
|
|
3100
3412
|
return false;
|
|
3101
3413
|
}
|
|
3102
3414
|
emitChange() {
|
|
@@ -3345,6 +3657,42 @@ function FormulaSheet({
|
|
|
3345
3657
|
const clipboardManager = React3.useMemo(() => controlledClipboardManager ?? new WorkbookClipboardManager(engine), [controlledClipboardManager, engine]);
|
|
3346
3658
|
const state = useEngine(engine);
|
|
3347
3659
|
const sheet = state.workbooks.get(workbookName)?.sheets.get(sheetName);
|
|
3660
|
+
const getEngineTableAt = useCallback4((cell) => {
|
|
3661
|
+
const table = engine.isCellInTable({
|
|
3662
|
+
workbookName,
|
|
3663
|
+
sheetName,
|
|
3664
|
+
rowIndex: cell.row,
|
|
3665
|
+
colIndex: cell.col
|
|
3666
|
+
});
|
|
3667
|
+
if (!table)
|
|
3668
|
+
return;
|
|
3669
|
+
const endRow = table.endRow.type === "number" ? { type: "number", value: table.endRow.value } : { type: "infinity" };
|
|
3670
|
+
const endCol = {
|
|
3671
|
+
type: "number",
|
|
3672
|
+
value: table.start.colIndex + table.headers.size - 1
|
|
3673
|
+
};
|
|
3674
|
+
const hasDataRows = endRow.type === "infinity" || endRow.value > table.start.rowIndex;
|
|
3675
|
+
return {
|
|
3676
|
+
id: `${table.workbookName}/${table.name}`,
|
|
3677
|
+
bounds: {
|
|
3678
|
+
start: { row: table.start.rowIndex, col: table.start.colIndex },
|
|
3679
|
+
end: { row: endRow, col: endCol }
|
|
3680
|
+
},
|
|
3681
|
+
...hasDataRows ? {
|
|
3682
|
+
dataBounds: {
|
|
3683
|
+
start: {
|
|
3684
|
+
row: table.start.rowIndex + 1,
|
|
3685
|
+
col: table.start.colIndex
|
|
3686
|
+
},
|
|
3687
|
+
end: { row: endRow, col: endCol }
|
|
3688
|
+
}
|
|
3689
|
+
} : {}
|
|
3690
|
+
};
|
|
3691
|
+
}, [engine, sheetName, workbookName]);
|
|
3692
|
+
const formulaNavigation = useMemo2(() => ({
|
|
3693
|
+
...selection?.navigation,
|
|
3694
|
+
getTableAt: selection?.navigation?.getTableAt ?? getEngineTableAt
|
|
3695
|
+
}), [getEngineTableAt, selection?.navigation]);
|
|
3348
3696
|
const onCellDataChange = useCallback4((updatedSpreadsheet) => {
|
|
3349
3697
|
const data = new Map(updatedSpreadsheet);
|
|
3350
3698
|
data.forEach((value, key) => {
|
|
@@ -3518,6 +3866,7 @@ function FormulaSheet({
|
|
|
3518
3866
|
parentSelected: isSelected,
|
|
3519
3867
|
selection: {
|
|
3520
3868
|
...selection,
|
|
3869
|
+
navigation: formulaNavigation,
|
|
3521
3870
|
effects: selectionManagerEffects
|
|
3522
3871
|
},
|
|
3523
3872
|
containerProps: isSelected && selectedArea ? {
|
|
@@ -4107,4 +4456,4 @@ export {
|
|
|
4107
4456
|
useResizeObserver
|
|
4108
4457
|
};
|
|
4109
4458
|
|
|
4110
|
-
//# debugId=
|
|
4459
|
+
//# debugId=58E97FA1EA7F33CF64756E2164756E21
|