@rowsncolumns/spreadsheet 10.5.0 → 10.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/cjs/components/canvas-grid/index.js +19 -2
- package/dist/cjs/components/formula-input/index.d.ts.map +1 -1
- package/dist/cjs/components/formula-input/index.js +28 -1
- package/dist/cjs/components/range-selector/index.d.ts.map +1 -1
- package/dist/cjs/components/range-selector/index.js +54 -34
- package/dist/cjs/hooks/use-fonts.d.ts +23 -2
- package/dist/cjs/hooks/use-fonts.d.ts.map +1 -1
- package/dist/cjs/hooks/use-fonts.js +33 -3
- package/dist/cjs/hooks/use-selection.d.ts.map +1 -1
- package/dist/cjs/hooks/use-selection.js +62 -16
- package/dist/esm/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/esm/components/canvas-grid/index.js +18 -2
- package/dist/esm/components/formula-input/index.d.ts.map +1 -1
- package/dist/esm/components/formula-input/index.js +26 -1
- package/dist/esm/components/range-selector/index.d.ts.map +1 -1
- package/dist/esm/components/range-selector/index.js +56 -36
- package/dist/esm/hooks/use-fonts.d.ts +23 -2
- package/dist/esm/hooks/use-fonts.d.ts.map +1 -1
- package/dist/esm/hooks/use-fonts.js +28 -2
- package/dist/esm/hooks/use-selection.d.ts.map +1 -1
- package/dist/esm/hooks/use-selection.js +62 -17
- package/package.json +1 -1
|
@@ -171,6 +171,52 @@ var useSelection = function (_a) {
|
|
|
171
171
|
(0, grid_3.useIsomorphicLayoutEffect)(function () {
|
|
172
172
|
activeCellRef.current = activeCell;
|
|
173
173
|
}, [activeCell]);
|
|
174
|
+
/**
|
|
175
|
+
* The last active cell this hook itself emitted. When the `activeCell` prop
|
|
176
|
+
* moves without a grid gesture — undo/redo restoring the cursor, a host
|
|
177
|
+
* navigating — the arrow-key anchor (`selectionStart`/`selectionEnd`) must
|
|
178
|
+
* follow it, or the next arrow key moves from where the cursor was before
|
|
179
|
+
* (rowsncolumns/spreadsheet#1124). Cells the hook emitted itself are skipped:
|
|
180
|
+
* Cmd+click anchors at the range's `start`, not the top-left it emits, and
|
|
181
|
+
* Shift+Arrow never emits at all, so its fixed anchor survives.
|
|
182
|
+
*/
|
|
183
|
+
var lastEmittedActiveCellRef = (0, react_1.useRef)(null);
|
|
184
|
+
var emitActiveCell = (0, react_1.useCallback)(function (sheetId, cell) {
|
|
185
|
+
lastEmittedActiveCellRef.current = cell;
|
|
186
|
+
onChangeActiveCell === null || onChangeActiveCell === void 0 ? void 0 : onChangeActiveCell(sheetId, cell);
|
|
187
|
+
}, [onChangeActiveCell]);
|
|
188
|
+
(0, grid_3.useIsomorphicLayoutEffect)(function () {
|
|
189
|
+
var _a;
|
|
190
|
+
if (!activeCell)
|
|
191
|
+
return;
|
|
192
|
+
var emitted = lastEmittedActiveCellRef.current;
|
|
193
|
+
if (emitted &&
|
|
194
|
+
emitted.rowIndex === activeCell.rowIndex &&
|
|
195
|
+
emitted.columnIndex === activeCell.columnIndex) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (isSelecting.current || isFilling.current || isDragging.current)
|
|
199
|
+
return;
|
|
200
|
+
var range = (_a = selections[selections.length - 1]) === null || _a === void 0 ? void 0 : _a.range;
|
|
201
|
+
if (range && (0, grid_1.isCellWithinBounds)(activeCell, range)) {
|
|
202
|
+
// Keep the selection; anchor at the active cell and let the far corner
|
|
203
|
+
// be the moving end so Shift+Arrow extends from here.
|
|
204
|
+
selectionStart.current = activeCell;
|
|
205
|
+
selectionEnd.current = {
|
|
206
|
+
rowIndex: activeCell.rowIndex === range.startRowIndex
|
|
207
|
+
? range.endRowIndex
|
|
208
|
+
: range.startRowIndex,
|
|
209
|
+
columnIndex: activeCell.columnIndex === range.startColumnIndex
|
|
210
|
+
? range.endColumnIndex
|
|
211
|
+
: range.startColumnIndex,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
selectionStart.current = activeCell;
|
|
216
|
+
selectionEnd.current = activeCell;
|
|
217
|
+
}
|
|
218
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
219
|
+
}, [sheetId, activeCell === null || activeCell === void 0 ? void 0 : activeCell.rowIndex, activeCell === null || activeCell === void 0 ? void 0 : activeCell.columnIndex]);
|
|
174
220
|
(0, grid_3.useIsomorphicLayoutEffect)(function () {
|
|
175
221
|
mergedCellsRef.current = mergedCells;
|
|
176
222
|
}, [mergedCells]);
|
|
@@ -280,7 +326,7 @@ var useSelection = function (_a) {
|
|
|
280
326
|
if (!range) {
|
|
281
327
|
return;
|
|
282
328
|
}
|
|
283
|
-
|
|
329
|
+
emitActiveCell(sheetId, {
|
|
284
330
|
rowIndex: range.startRowIndex,
|
|
285
331
|
columnIndex: range.startColumnIndex,
|
|
286
332
|
});
|
|
@@ -290,7 +336,7 @@ var useSelection = function (_a) {
|
|
|
290
336
|
}, [
|
|
291
337
|
sheetId,
|
|
292
338
|
onChangeSelections,
|
|
293
|
-
|
|
339
|
+
emitActiveCell,
|
|
294
340
|
selectionFromStartEnd,
|
|
295
341
|
isCellOutOfBounds,
|
|
296
342
|
selectionPolicy,
|
|
@@ -317,7 +363,7 @@ var useSelection = function (_a) {
|
|
|
317
363
|
columnIndex: bounds.startColumnIndex,
|
|
318
364
|
};
|
|
319
365
|
/* Keep track of first cell that was selected by user */
|
|
320
|
-
|
|
366
|
+
emitActiveCell(sheetId, coords);
|
|
321
367
|
if (newSelectionMode === "clear") {
|
|
322
368
|
clearSelections();
|
|
323
369
|
}
|
|
@@ -333,7 +379,7 @@ var useSelection = function (_a) {
|
|
|
333
379
|
appendSelection,
|
|
334
380
|
newSelectionMode,
|
|
335
381
|
clearSelections,
|
|
336
|
-
|
|
382
|
+
emitActiveCell,
|
|
337
383
|
selectionFromStartEnd,
|
|
338
384
|
isCellOutOfBounds,
|
|
339
385
|
]);
|
|
@@ -411,12 +457,12 @@ var useSelection = function (_a) {
|
|
|
411
457
|
if (shouldScroll === void 0) { shouldScroll = true; }
|
|
412
458
|
selectionStart.current = coords;
|
|
413
459
|
selectionEnd.current = coords;
|
|
414
|
-
|
|
460
|
+
emitActiveCell(sheetId, coords);
|
|
415
461
|
/* Scroll to the cell */
|
|
416
462
|
if (shouldScroll && coords) {
|
|
417
463
|
scrollToCell(coords, align);
|
|
418
464
|
}
|
|
419
|
-
}, [
|
|
465
|
+
}, [emitActiveCell, scrollToCell]));
|
|
420
466
|
/**
|
|
421
467
|
* Navigate selection using keyboard
|
|
422
468
|
* @param direction
|
|
@@ -1036,7 +1082,7 @@ var useSelection = function (_a) {
|
|
|
1036
1082
|
var newSelection_1 = removeSelectionByIndex(cellIndex);
|
|
1037
1083
|
var nextActiveCell = getPossibleActiveCellFromSelections(newSelection_1);
|
|
1038
1084
|
if (nextActiveCell !== null) {
|
|
1039
|
-
|
|
1085
|
+
emitActiveCell(sheetId, nextActiveCell);
|
|
1040
1086
|
}
|
|
1041
1087
|
if (newSelection_1.length === 1 &&
|
|
1042
1088
|
cellEqualsSelection(nextActiveCell, newSelection_1)) {
|
|
@@ -1090,7 +1136,7 @@ var useSelection = function (_a) {
|
|
|
1090
1136
|
modifySelection,
|
|
1091
1137
|
mouseDownInterceptor,
|
|
1092
1138
|
newSelection,
|
|
1093
|
-
|
|
1139
|
+
emitActiveCell,
|
|
1094
1140
|
removeSelectionByIndex,
|
|
1095
1141
|
selectionLeftBound,
|
|
1096
1142
|
selectionTopBound,
|
|
@@ -1174,7 +1220,7 @@ var useSelection = function (_a) {
|
|
|
1174
1220
|
var direction = isShiftKey ? grid_4.Direction.Up : grid_4.Direction.Down;
|
|
1175
1221
|
var nextCell = (0, grid_4.findNextCellWithinBounds)(activeCellBounds, range, direction);
|
|
1176
1222
|
if (nextCell) {
|
|
1177
|
-
|
|
1223
|
+
emitActiveCell(sheetId, nextCell);
|
|
1178
1224
|
scrollToCell(nextCell);
|
|
1179
1225
|
}
|
|
1180
1226
|
}
|
|
@@ -1195,7 +1241,7 @@ var useSelection = function (_a) {
|
|
|
1195
1241
|
var direction = isShiftKey ? grid_4.Direction.Left : grid_4.Direction.Right;
|
|
1196
1242
|
var nextCell = (0, grid_4.findNextCellWithinBounds)(activeCellBounds, range, direction);
|
|
1197
1243
|
if (nextCell) {
|
|
1198
|
-
|
|
1244
|
+
emitActiveCell(sheetId, nextCell);
|
|
1199
1245
|
scrollToCell(nextCell);
|
|
1200
1246
|
}
|
|
1201
1247
|
}
|
|
@@ -1246,7 +1292,7 @@ var useSelection = function (_a) {
|
|
|
1246
1292
|
activeCell,
|
|
1247
1293
|
selections,
|
|
1248
1294
|
keyNavigate,
|
|
1249
|
-
|
|
1295
|
+
emitActiveCell,
|
|
1250
1296
|
pageDown,
|
|
1251
1297
|
pageLeft,
|
|
1252
1298
|
pageRight,
|
|
@@ -1394,7 +1440,7 @@ var useSelection = function (_a) {
|
|
|
1394
1440
|
// The active cell normally anchors the surviving corner; if it fell in the cleared band,
|
|
1395
1441
|
// move it to the remaining range's top-left so it never points at a deselected cell.
|
|
1396
1442
|
if (!cellInRange(activeCell, shrink.remaining)) {
|
|
1397
|
-
|
|
1443
|
+
emitActiveCell(sheetId, {
|
|
1398
1444
|
rowIndex: shrink.remaining.startRowIndex,
|
|
1399
1445
|
columnIndex: shrink.remaining.startColumnIndex,
|
|
1400
1446
|
});
|
|
@@ -1442,7 +1488,7 @@ var useSelection = function (_a) {
|
|
|
1442
1488
|
focus,
|
|
1443
1489
|
handleFillMouseMove,
|
|
1444
1490
|
onChangeSelections,
|
|
1445
|
-
|
|
1491
|
+
emitActiveCell,
|
|
1446
1492
|
onFill,
|
|
1447
1493
|
onFillHandleRelease,
|
|
1448
1494
|
onFillShrink,
|
|
@@ -1543,7 +1589,7 @@ var useSelection = function (_a) {
|
|
|
1543
1589
|
columnIndex: toSelection.range.startColumnIndex,
|
|
1544
1590
|
};
|
|
1545
1591
|
/* Pick the first cell as activee cell */
|
|
1546
|
-
|
|
1592
|
+
emitActiveCell(sheetId, coords);
|
|
1547
1593
|
/* Update selection start */
|
|
1548
1594
|
selectionStart.current = coords;
|
|
1549
1595
|
if ((0, grid_4.selectionSpansCells)(toSelection === null || toSelection === void 0 ? void 0 : toSelection.range)) {
|
|
@@ -1581,7 +1627,7 @@ var useSelection = function (_a) {
|
|
|
1581
1627
|
onMoveSelection === null || onMoveSelection === void 0 ? void 0 : onMoveSelection(sheetId, fromSelection, toSelection);
|
|
1582
1628
|
return;
|
|
1583
1629
|
}
|
|
1584
|
-
|
|
1630
|
+
emitActiveCell(sheetId, {
|
|
1585
1631
|
rowIndex: fromSelection.range.startRowIndex,
|
|
1586
1632
|
columnIndex: fromSelection.range.startColumnIndex,
|
|
1587
1633
|
});
|
|
@@ -1621,7 +1667,7 @@ var useSelection = function (_a) {
|
|
|
1621
1667
|
getValue,
|
|
1622
1668
|
handleSelectionMouseMove,
|
|
1623
1669
|
focus,
|
|
1624
|
-
|
|
1670
|
+
emitActiveCell,
|
|
1625
1671
|
onChangeSelections,
|
|
1626
1672
|
]);
|
|
1627
1673
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/canvas-grid/index.tsx"],"names":[],"mappings":"AAoDA,OAAO,KASN,MAAM,OAAO,CAAC;AAqNf,OAAO,KAAK,EAGV,QAAQ,EAWT,MAAM,aAAa,CAAC;AAoBrB,OAAO,KAAK,EACV,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EAEhB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/canvas-grid/index.tsx"],"names":[],"mappings":"AAoDA,OAAO,KASN,MAAM,OAAO,CAAC;AAqNf,OAAO,KAAK,EAGV,QAAQ,EAWT,MAAM,aAAa,CAAC;AAoBrB,OAAO,KAAK,EACV,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EAEhB,MAAM,WAAW,CAAC;AAm4LnB,eAAO,MAAM,UAAU,EAA4C,CACjE,CAAC,SAAS,QAAQ,CAAC,GAAG,CAAC,EAEvB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GACvB,wBAAwB,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;CAAE,KAChE,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC"}
|
|
@@ -116,6 +116,16 @@ import { getNestedPropertyNames } from "./helpers";
|
|
|
116
116
|
*/
|
|
117
117
|
/** Where the grid focuses when no cell is given (A1); mirrors spreadsheet-state's `defaultCellCoords` without importing it — that package depends on this one. */
|
|
118
118
|
const defaultCellCoords = { rowIndex: 1, columnIndex: 1 };
|
|
119
|
+
/**
|
|
120
|
+
* Whether a registered editor's DOM has been removed: an input that unmounted while focused
|
|
121
|
+
* gets no blur from Chromium, so its `activeEditor` registration can outlive it. Such an editor
|
|
122
|
+
* cannot hold focus, so the grid treats it as absent (rowsncolumns/spreadsheet#1131). An editor
|
|
123
|
+
* that exposes no element is trusted as live.
|
|
124
|
+
*/
|
|
125
|
+
function isDetachedEditor(editor) {
|
|
126
|
+
const element = editor.el?.();
|
|
127
|
+
return element != null && !element.isConnected;
|
|
128
|
+
}
|
|
119
129
|
function CanvasGridComponent(props, forwardedRef) {
|
|
120
130
|
const { licenseKey, sheetId, sheetType, ChartSheetView: ChartSheetViewComponent = ChartSheetView, instanceId, activeCell = DEFAULT_ACTIVE_CELL, selections = DEFAULT_ARRAY, frozenColumnCount = 0, frozenRowCount = 0, rowCount = DEFAULT_ROW_COUNT, columnCount = DEFAULT_COLUMN_COUNT, autoFocus, basicFilter, tables, namedRanges, bandedRanges = DEFAULT_ARRAY, tableBorderColor = "#008000", filterIconColor = "#008000", conditionalFormats, showFilterIcon = true, showCalculatedColumnIcon = true, showHeaders = true, defaultColumnWidth = DEFAULT_COLUMN_WIDTH, defaultRowHeight = DEFAULT_ROW_HEIGHT, columnHeaderHeight = COLUMN_HEADER_HEIGHT, rowHeaderWidth = ROW_HEADER_WIDTH, gridLineColor = GRIDLINE_COLOR, cellBackgroundColor = DEFAULT_CELL_BACKGROUND_COLOR, cellColor = DEFAULT_CELL_COLOR, headerBorderColor = DEFAULT_HEADER_BORDER_COLOR, headerBackgroundColor = DEFAULT_HEADER_BG, headerColor = DEFAULT_HEADER_COLOR, headerActiveBackgroundColor = DEFAULT_HEADER_ACTIVE_BG, headerSelectedBackgroundColor = DEFAULT_HEADER_SELECTED_BG, headerSelectedColor = "white", headerTableBackgroundColor = DEFAULT_HEADER_TABLE_BG, headerTableActiveBackgroundColor = DEFAULT_HEADER_ACTIVE_TABLE_BG, headerTableSelectedBackgroundColor = DEFAULT_HEADER_SELECTED_TABLE_BG, frozenShadowColor = headerBorderColor, frozenShadowThickness = 1, selectionBackgroundColor = SELECTION_BG, selectionBorderColor = SELECTION_BORDER, selectionDragBorderColor = SELECTION_BORDER, chartSelectedColor, chipBackgroundColor = CHIP_COLOR, merges = DEFAULT_ARRAY, borderStyles, protectedRanges, isWorkbookProtected: isWorkbookProtectedProp = false, getSheetProtection, pivotTables, charts, onUpdateChart, onChangeZOrder, embeds, slicers, rowMetadata = DEFAULT_ARRAY, columnMetadata = DEFAULT_ARRAY, scale = 1, zoomSpeed = 1, overscanCount = 3, enableEditOnEnterKey = true, enableTextOverflow = true, showGridLines: _showGridLines, showFormulas = false, protectFormulas = false, showFrozenSeparator = true, HeaderCell = DefaultHeaderCell, Cell = DefaultCell, SelectionTitleComponent = SelectionTitle, CellEditor = DefaultCellEditor, CellTooltip, ContextMenu = DefaultContextMenu, ContextMenuWrapper = DefaultContextMenuWrapper, FilterBox = DefaultFilterBox, HeaderMoveDragComponent = DefaultHeaderMoveDragComponent, SuggestionsDropdown, stickyEditor = true, scrollSnap = false, scrollThrottleTimeout, preventBrowserDefaultScroll, mouseMoveThrottleTimeout = 60, tooltipIntentInterval = 60, selectionPolicy, users, userId, renderCollaboratorCursor, functionDescriptions, showHeaderMenuButton = true, scrollbarSize = SCROLLBAR_SIZE, theme = defaultSpreadsheetTheme, showNotes = true, showComments = true, locale, formulaLocale: formulaLocaleProp, agentSuggestions, onReviewSuggestion, onShowFormulas, getDataRowCount, getDataRegion, getChartComponent, getEmbedComponent, getSlicerComponent, onChange, onChangeBatch, onExpandCollapse, onPivotDrillDown, onDelete, onChangeEditorValue, onFill, onRequestCalculate, onChangeActiveCell, onChangeActiveSheet, onChangeSelections, getSheetName, getSheetId, getCellData, getNote: getNoteProp, getTextFormatRuns, getDataValidation, getEffectiveFormat, getFormattedValue: getFormattedValueProp, getTooltipContent, errorResolutionComponent, getEffectiveValue: getEffectiveValueProp, getSparklineGroup, getUserEnteredExtendedValue, getEffectiveExtendedValue, getSheetRowCount, getSheetColumnCount, getCellPopoverContent, onScroll, onViewPortChange, windowConditionalFormats = false, engineConditionalFormatResults = false, onResize, onAutoResize, onHideColumn, onHideRow, onShowColumn, onShowRow, onGroupRows, onUngroupRows, onCollapseRowGroup, onExpandRowGroup, onGroupColumns, onUngroupColumns, onCollapseColumnGroup, onExpandColumnGroup, onSetOutlineDepth, onDeleteRow, onDeleteColumn, onInsertRow, onInsertColumn, onChangeAutoExpandTables, onChangeAutoCalculatedColumns, onInsertCellsShiftDown, onInsertCellsShiftRight, onDeleteCellsShiftLeft, onDeleteCellsShiftUp, onClearContents, onMoveColumns, onMoveRows, onSelectNextSheet, onSelectPreviousSheet, onCreateNewSheet, onKeyDown, onChangeFormatting, onRepeatFormatting, onClearFormatting, onRequestEditProtectedRange, onMoveChart, onResizeChart, onDeleteChart, onCutChart, onCopyChart, onMoveEmbed, onResizeEmbed, onDeleteEmbed, onRequestEditEmbed, onMoveSlicer, onResizeSlicer, onDeleteSlicer, onRequestEditSlicer, onFillRange, disableFillRightShortcut, keymap, onCalculateNow, onRequestGoTo, onMoveSelection, onInsertCutCells, onInsertTime, onInsertDate, onInsertDateTime, onInsertAutoSum, onUndo, onRedo, onPaste, onCopy, onCut, onSortColumn, onSortTable, onFilterTable, onFilterPivot, onResizeTable, onUndoTableAutoExpansion, onUndoCalculatedColumn, onOverwriteCalculatedColumn, onRequestEditTable, onRequestEditPivotTable, onRequestDeletePivotTable, onRequestCreatePivotChart, onRemoveTable, onCreateTable, onRequestCreateTable, onInsertTableColumn, onDeleteTableColumn, onInsertTableRow, onDeleteTableRow, onUpdateTable, onRequestResizeTable, onRequestInsertSlicer, onCreateBasicFilter, onRequestDefineNamedRange, onRequestFormatCells, onRequestConditionalFormat, onRequestDataValidation, onRequestInsertComment, onRequestInsertLink, onFreezeColumn, onFreezeRow, onUpdateNote, onProtectRange, onUnProtectRange, onSortRange, onRequestSearch, onRequestReplace, onRequestResize, getRowHeaderText = getDefaultRowHeader, getColumnHeaderText = getDefaultColumnHeader, getAdditionalCellSpacing, tokenizer: defaultTokenizer = defaultFormulaTokenizer, onChangeScale, onRemoveLink, onSelectLink, onSplitTextToColumns, onRemoveDuplicates, onGoalSeek, onSubtotal, onAdvancedFilter, onRequestExpand, cellExpandPredicate, StructuredResult = DefaultStructuredResult, getMentions, getStructuredProperties: getStructuredPropertiesProp, getFunctionParameterSuggestions, shouldFetchFunctionParameters, MentionTagComponent, MentionDropdownItemComponent, showSelectionResizeHandles = true, enableMagicFill, enableQuickEdit = true, MagicFillComponent, MagicFillContent, QuickEdit, PasteMenu, SuggestionAction, HiddenRowMarkers, HiddenColumnMarkers, enableResizerOverflow, className, defaultFontSizePoint = DEFAULT_FONT_SIZE_PT, defaultCellFormat, readonly, isComparing = false, getCellDiff, enableDataBoundaryNavigation, footerHeight, footerComponent, arrowComponents,
|
|
121
131
|
// Host-driven filter props. `hostHiddenRowRanges`
|
|
@@ -129,7 +139,7 @@ function CanvasGridComponent(props, forwardedRef) {
|
|
|
129
139
|
// defaults to `getFormattedValue` inside `useGetValuesFromRange`.
|
|
130
140
|
getValidationSourceValue,
|
|
131
141
|
// Unused props only for api
|
|
132
|
-
onChangeBorder, onDuplicateSheet, onCreateConditionalFormattingRule, onDeleteConditionalFormattingRule, onUpdateConditionalFormattingRule, onCreateDataValidationRule, onUpdateDataValidationRule, onDeleteDataValidationRule, onCreateChart, onDeleteSheet, onCreateEmbed, onDecreaseIndent, onIncreaseIndent, onChangeTheme, onChangeDecimals, onUpdateSheet, onChangeBatchStream, onBlur, ...rest } = props;
|
|
142
|
+
getDataColumnCount, onDeleteNamedRange, onChangeBorder, onDuplicateSheet, onCreateConditionalFormattingRule, onDeleteConditionalFormattingRule, onUpdateConditionalFormattingRule, onCreateDataValidationRule, onUpdateDataValidationRule, onDeleteDataValidationRule, onCreateChart, onDeleteSheet, onCreateEmbed, onDecreaseIndent, onIncreaseIndent, onChangeTheme, onChangeDecimals, onUpdateSheet, onChangeBatchStream, onBlur, ...rest } = props;
|
|
133
143
|
/**
|
|
134
144
|
* Fire onBlur only when the cell editor blurs to an element outside the
|
|
135
145
|
* spreadsheet. Routed through `useEditable` so we hook the always-focused
|
|
@@ -696,7 +706,13 @@ function CanvasGridComponent(props, forwardedRef) {
|
|
|
696
706
|
!gridContainer.contains(activeElement)) {
|
|
697
707
|
return;
|
|
698
708
|
}
|
|
699
|
-
|
|
709
|
+
// An editor whose DOM is gone cannot be the one holding focus: a host that unmounted an
|
|
710
|
+
// input without a blur (Chromium fires none for a removed focused element) can leave a
|
|
711
|
+
// stale `activeEditor` behind, and honouring it would lock the grid out of keyboard input
|
|
712
|
+
// (rowsncolumns/spreadsheet#1131).
|
|
713
|
+
if (!activeEditor ||
|
|
714
|
+
activeEditor === cellEditorInstance ||
|
|
715
|
+
isDetachedEditor(activeEditor)) {
|
|
700
716
|
cellEditorInstance?.focus();
|
|
701
717
|
}
|
|
702
718
|
}, [activeEditor, cellEditorInstance]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/formula-input/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,kBAAkB,CAAC;AAGrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,KAAiB,MAAM,OAAO,CAAC;AActC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,KAAK,iBAAiB,GAAG,cAAc,CAAC,OAAO,SAAS,CAAC,GAAG;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/formula-input/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,kBAAkB,CAAC;AAGrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,KAAiB,MAAM,OAAO,CAAC;AActC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,KAAK,iBAAiB,GAAG,cAAc,CAAC,OAAO,SAAS,CAAC,GAAG;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,sGAuLxB,CAAC"}
|
|
@@ -28,6 +28,9 @@ export const FormulaInput = forwardRef(({ value = "", onChange, onCancel, onBlur
|
|
|
28
28
|
// to be the one to turn it off; otherwise the picked range keeps rendering and the
|
|
29
29
|
// grid stays in formula-selection mode with no editor left to receive the picks.
|
|
30
30
|
const ownsFormulaModeRef = useRef(false);
|
|
31
|
+
// Whether this input holds focus (set on focus, cleared on blur): an input that unmounts
|
|
32
|
+
// while focused got no blur, and is the editor `activeEditor` still points at.
|
|
33
|
+
const focusedRef = useRef(false);
|
|
31
34
|
const enterFormulaMode = useCallback((nextValue) => {
|
|
32
35
|
const formulaMode = isAFormula(nextValue);
|
|
33
36
|
setFormulaMode(formulaMode);
|
|
@@ -41,13 +44,33 @@ export const FormulaInput = forwardRef(({ value = "", onChange, onCancel, onBlur
|
|
|
41
44
|
}, [onCancel, setFormulaMode, setFormulaSelections]);
|
|
42
45
|
// Layout (not passive) cleanup: when a host re-keys its range inputs ("Add range"),
|
|
43
46
|
// the departing input must release the mode before the new one's autoFocus claims it.
|
|
47
|
+
// The same cleanup releases `activeEditor` when it still points at this input: Chromium
|
|
48
|
+
// fires no blur for a focused element that is removed (a dialog closed with Escape while
|
|
49
|
+
// the input had focus), so `handleBlur` never ran, and CanvasGrid skips focusing its own
|
|
50
|
+
// cell editor for as long as another editor is registered — the grid stops taking
|
|
51
|
+
// keyboard input until something else clears it (rowsncolumns/spreadsheet#1131).
|
|
44
52
|
useIsomorphicLayoutEffect(() => () => {
|
|
45
53
|
if (ownsFormulaModeRef.current) {
|
|
46
54
|
ownsFormulaModeRef.current = false;
|
|
47
55
|
setFormulaMode(false);
|
|
48
56
|
setFormulaSelections([]);
|
|
49
57
|
}
|
|
50
|
-
|
|
58
|
+
// The registration is CellInput's live handle, which can be a newer object than the one
|
|
59
|
+
// forwarded here at mount, so match on the element too; and an input removed while it
|
|
60
|
+
// still had focus (`focusedRef`) is the registered one even if neither identity matches.
|
|
61
|
+
const editor = editorRef.current;
|
|
62
|
+
const element = editor?.el?.() ?? null;
|
|
63
|
+
const wasFocused = focusedRef.current;
|
|
64
|
+
focusedRef.current = false;
|
|
65
|
+
setActiveEditor((current) => {
|
|
66
|
+
if (current === undefined)
|
|
67
|
+
return current;
|
|
68
|
+
const ours = current === editor ||
|
|
69
|
+
(element !== null && current.el?.() === element) ||
|
|
70
|
+
wasFocused;
|
|
71
|
+
return ours ? undefined : current;
|
|
72
|
+
});
|
|
73
|
+
}, [setActiveEditor, setFormulaMode, setFormulaSelections]);
|
|
51
74
|
const handleFocus = useCallback((e) => {
|
|
52
75
|
// Set formula mode
|
|
53
76
|
enterFormulaMode(value);
|
|
@@ -58,6 +81,7 @@ export const FormulaInput = forwardRef(({ value = "", onChange, onCancel, onBlur
|
|
|
58
81
|
}
|
|
59
82
|
// Move to end of input
|
|
60
83
|
editorRef.current?.moveCursorToEnd();
|
|
84
|
+
focusedRef.current = true;
|
|
61
85
|
// Trigger change
|
|
62
86
|
onChange?.(value);
|
|
63
87
|
onFocus?.(e);
|
|
@@ -75,6 +99,7 @@ export const FormulaInput = forwardRef(({ value = "", onChange, onCancel, onBlur
|
|
|
75
99
|
return;
|
|
76
100
|
}
|
|
77
101
|
closeFormulaMode();
|
|
102
|
+
focusedRef.current = false;
|
|
78
103
|
// Resetting active editor will
|
|
79
104
|
// cause the dropdown to close if open
|
|
80
105
|
setActiveEditor(undefined);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/range-selector/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,aAAa,EACd,MAAM,oBAAoB,CAAC;AAiC5B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/range-selector/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,aAAa,EACd,MAAM,oBAAoB,CAAC;AAiC5B,OAAO,KAON,MAAM,OAAO,CAAC;AAef,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAc,SAAS,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,MAAM,wBAAwB,CAAC;AAEhC,KAAK,uBAAuB,CAAC,CAAC,SAAS,KAAK,IAAI;IAC9C,UAAU,EAAE,aAAa,CAAC;IAC1B,UAAU,CAAC,EAAE,aAAa,CAAC,mBAAmB,CAAC,EAAE,CAAC;IAClD,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IACb,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC5B,kBAAkB,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAC;IAC1D,kBAAkB,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAC;IAC1D,mBAAmB,CAAC,EAAE,eAAe,CAAC,qBAAqB,CAAC,CAAC;IAC7D,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IAC9D,yBAAyB,CAAC,EAAE,eAAe,CAAC,2BAA2B,CAAC,CAAC;IACzE,yBAAyB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAkRF,iBAAS,sBAAsB,CAAC,CAAC,SAAS,KAAK,EAAE,EAC/C,WAA2B,EAC3B,MAAsB,EACtB,MAAsB,EACtB,MAAM,EACN,UAA0B,EAC1B,UAAU,EACV,QAAwB,EACxB,WAA8B,EAC9B,OAAO,EACP,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,EAClB,yBAAyB,GAC1B,EAAE,uBAAuB,CAAC,CAAC,CAAC,qBAiN5B;AAED,eAAO,MAAM,aAAa,EAErB,OAAO,sBAAsB,CAAC"}
|
|
@@ -3,11 +3,11 @@ import { ChevronRightIcon, FileIcon, GroupIcon, PlusIcon, TableIcon, TriangleDow
|
|
|
3
3
|
import { DropdownLeftSlot, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, DropdownRightSlot, IconButton, Input, } from "@rowsncolumns/ui";
|
|
4
4
|
import { addressToCell, cellToAddress, convertCellToRange, sortSheetsByIndex, } from "@rowsncolumns/utils";
|
|
5
5
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
6
|
-
import React, { memo, useCallback, useEffect, useRef, useState } from "react";
|
|
6
|
+
import React, { memo, useCallback, useEffect, useMemo, useRef, useState, } from "react";
|
|
7
7
|
import { addressToSelection, selectionToRxC } from "../../api/selections";
|
|
8
8
|
import { useFocusSheet, useFormulaModeValue, useGetAddressFromSelection, } from "../../atoms";
|
|
9
9
|
import { useRangeContext } from "../../atoms/range-selector.atom";
|
|
10
|
-
import { DEFAULT_ARRAY, MAX_COLUMN_COUNT, MAX_ROW_COUNT } from "../../config";
|
|
10
|
+
import { DEFAULT_ACTIVE_CELL, DEFAULT_ARRAY, MAX_COLUMN_COUNT, MAX_ROW_COUNT, } from "../../config";
|
|
11
11
|
const SELECTOR_WIDTH = 90;
|
|
12
12
|
const ICON_WIDTH = 23;
|
|
13
13
|
const ITEM_HEIGHT = 28;
|
|
@@ -84,20 +84,43 @@ function RangeSelectorContent({ namedRanges, tables, sheets, sheetId, activeCell
|
|
|
84
84
|
React.createElement(FileIcon, null)),
|
|
85
85
|
title)))));
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* The Name Box's dropdown, memoised so a cursor move does not re-render it: the
|
|
89
|
+
* Radix `DropdownMenu` root schedules a nested state update on every render even
|
|
90
|
+
* while closed, which — on top of the input-sync re-render — is what pushed a held
|
|
91
|
+
* arrow key past React's nested-update limit (rowsncolumns/spreadsheet#1127).
|
|
92
|
+
*/
|
|
93
|
+
function NameBoxMenuComponent({ isOpen, onOpenChange, namedRanges, tables, sheets, sheetId, activeCell, selections, shouldFocusSheetRef, focusSheet, onSelectNamedRange, onSelectTable, onRequestUpdateNamedRange, onDeleteNamedRange, onRequestDefineNamedRange, onChangeActiveSheet, }) {
|
|
94
|
+
return (React.createElement(DropdownMenu, { modal: false, open: isOpen, onOpenChange: onOpenChange },
|
|
95
|
+
React.createElement(DropdownMenuTrigger, { asChild: true },
|
|
96
|
+
React.createElement(IconButton, { className: "rounded-none mr-0", shrink: true },
|
|
97
|
+
React.createElement(TriangleDownIcon, null))),
|
|
98
|
+
React.createElement(DropdownMenuPortal, null,
|
|
99
|
+
React.createElement(DropdownMenuContent, { align: "start", alignOffset: -SELECTOR_WIDTH + ICON_WIDTH, sideOffset: 1, className: "max-h-[300px] overflow-auto", onCloseAutoFocus: (event) => {
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
if (shouldFocusSheetRef.current && !isOpen) {
|
|
102
|
+
focusSheet?.();
|
|
103
|
+
}
|
|
104
|
+
} },
|
|
105
|
+
React.createElement(RangeSelectorContent, { namedRanges: namedRanges, tables: tables, sheets: sheets, sheetId: sheetId, activeCell: activeCell, selections: selections, isOpen: isOpen, shouldFocusSheetRef: shouldFocusSheetRef, onSelectNamedRange: onSelectNamedRange, onSelectTable: onSelectTable, onRequestUpdateNamedRange: onRequestUpdateNamedRange, onDeleteNamedRange: onDeleteNamedRange, onRequestDefineNamedRange: onRequestDefineNamedRange, onChangeActiveSheet: onChangeActiveSheet })))));
|
|
106
|
+
}
|
|
107
|
+
const NameBoxMenu = memo(NameBoxMenuComponent);
|
|
87
108
|
function RangeSelectorComponent({ namedRanges = DEFAULT_ARRAY, tables = DEFAULT_ARRAY, sheets = DEFAULT_ARRAY, merges, selections = DEFAULT_ARRAY, activeCell, rowCount = MAX_ROW_COUNT, columnCount = MAX_COLUMN_COUNT, sheetId, onChangeActiveSheet, onChangeActiveCell, onChangeSelections, onRequestUpdateNamedRange, onDeleteNamedRange, onRequestDefineNamedRange, }) {
|
|
88
109
|
const [isOpen, setIsOpen] = useState(false);
|
|
89
|
-
|
|
110
|
+
// Only what the user is typing into the Name Box; `null` while it just mirrors the
|
|
111
|
+
// selection. The mirrored address is DERIVED during render below — syncing it into
|
|
112
|
+
// state from an effect scheduled one extra synchronous re-render per cursor move,
|
|
113
|
+
// which React's nested-update counter turned into "Maximum update depth exceeded"
|
|
114
|
+
// (#185) under a held arrow key in production builds (rowsncolumns/spreadsheet#1127).
|
|
115
|
+
const [draft, setDraft] = useState(null);
|
|
90
116
|
const inputRef = useRef(null);
|
|
91
117
|
const { navigateToSheetRange } = useRangeContext();
|
|
92
118
|
const isFormulaMode = useFormulaModeValue();
|
|
93
119
|
const shouldFocusSheetRef = useRef(false);
|
|
94
120
|
const focusSheet = useFocusSheet();
|
|
95
121
|
const getAddressFromSelection = useGetAddressFromSelection();
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
if (isFormulaMode) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
122
|
+
// The address (or named-range name) of the current selection / active cell.
|
|
123
|
+
const derivedAddress = useMemo(() => {
|
|
101
124
|
const lastSelection = selections[selections.length - 1];
|
|
102
125
|
const isSelectionInProgress = lastSelection?.inProgress;
|
|
103
126
|
const inputValue = lastSelection
|
|
@@ -110,19 +133,32 @@ function RangeSelectorComponent({ namedRanges = DEFAULT_ARRAY, tables = DEFAULT_
|
|
|
110
133
|
return (range &&
|
|
111
134
|
isAreasEqual(range, lastSelection ? lastSelection.range : convertCellToRange(activeCell)));
|
|
112
135
|
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
136
|
+
return inputValue ? (range ? range.name : inputValue) : undefined;
|
|
137
|
+
// `rowCount` / `columnCount` / `merges` shape the address text through
|
|
138
|
+
// `getAddressFromSelection` without being read here directly.
|
|
139
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
116
140
|
}, [
|
|
117
141
|
rowCount,
|
|
118
142
|
columnCount,
|
|
119
143
|
activeCell,
|
|
120
144
|
selections,
|
|
121
145
|
namedRanges,
|
|
122
|
-
isFormulaMode,
|
|
123
146
|
merges,
|
|
124
147
|
getAddressFromSelection,
|
|
125
148
|
]);
|
|
149
|
+
// Formula mode freezes the box at the address it showed when the formula started
|
|
150
|
+
// (a range picked for the formula must not rename the box); remember the last
|
|
151
|
+
// address shown outside formula mode without scheduling a render.
|
|
152
|
+
const frozenAddressRef = useRef("");
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
if (!isFormulaMode && derivedAddress !== undefined) {
|
|
155
|
+
frozenAddressRef.current = derivedAddress;
|
|
156
|
+
}
|
|
157
|
+
}, [isFormulaMode, derivedAddress]);
|
|
158
|
+
const value = draft ??
|
|
159
|
+
(isFormulaMode
|
|
160
|
+
? frozenAddressRef.current
|
|
161
|
+
: derivedAddress ?? frozenAddressRef.current);
|
|
126
162
|
// Select a range of cells in a sheet
|
|
127
163
|
const onSelectRange = useCallback((range) => {
|
|
128
164
|
navigateToSheetRange?.(range, { enableFlash: false });
|
|
@@ -142,6 +178,7 @@ function RangeSelectorComponent({ namedRanges = DEFAULT_ARRAY, tables = DEFAULT_
|
|
|
142
178
|
const handleKeyDown = useCallback((e) => {
|
|
143
179
|
if (e.key === "Enter") {
|
|
144
180
|
const value = inputRef.current?.value;
|
|
181
|
+
setDraft(null);
|
|
145
182
|
if (value) {
|
|
146
183
|
// Check for named range
|
|
147
184
|
const namedRange = namedRanges.find((range) => range.name === value);
|
|
@@ -191,13 +228,7 @@ function RangeSelectorComponent({ namedRanges = DEFAULT_ARRAY, tables = DEFAULT_
|
|
|
191
228
|
else if (e.key === "Escape" && !isFormulaMode) {
|
|
192
229
|
// Revert the Name Box to the current selection/active cell on
|
|
193
230
|
// Escape, then return focus to the grid.
|
|
194
|
-
|
|
195
|
-
const reverted = lastSelection
|
|
196
|
-
? getAddressFromSelection?.(lastSelection, undefined)
|
|
197
|
-
: cellToAddress(activeCell);
|
|
198
|
-
if (reverted) {
|
|
199
|
-
setValue(reverted);
|
|
200
|
-
}
|
|
231
|
+
setDraft(null);
|
|
201
232
|
inputRef.current?.blur();
|
|
202
233
|
focusSheet?.();
|
|
203
234
|
return;
|
|
@@ -212,25 +243,14 @@ function RangeSelectorComponent({ namedRanges = DEFAULT_ARRAY, tables = DEFAULT_
|
|
|
212
243
|
onChangeSelections,
|
|
213
244
|
isFormulaMode,
|
|
214
245
|
focusSheet,
|
|
215
|
-
selections,
|
|
216
|
-
activeCell,
|
|
217
|
-
getAddressFromSelection,
|
|
218
|
-
setValue,
|
|
219
246
|
navigateToSheetRange,
|
|
220
247
|
]);
|
|
221
248
|
return (React.createElement("div", { className: "flex h-full w-[90px]" },
|
|
222
|
-
React.createElement(Input, { value: value, type: "text", ref: inputRef, onChange: (e) =>
|
|
223
|
-
React.createElement(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
React.createElement(DropdownMenuContent, { align: "start", alignOffset: -SELECTOR_WIDTH + ICON_WIDTH, sideOffset: 1, className: "max-h-[300px] overflow-auto", onCloseAutoFocus: (event) => {
|
|
229
|
-
event.preventDefault();
|
|
230
|
-
if (shouldFocusSheetRef.current && !isOpen) {
|
|
231
|
-
focusSheet?.();
|
|
232
|
-
}
|
|
233
|
-
} },
|
|
234
|
-
React.createElement(RangeSelectorContent, { namedRanges: namedRanges, tables: tables, sheets: sheets, sheetId: sheetId, activeCell: activeCell, selections: selections, isOpen: isOpen, shouldFocusSheetRef: shouldFocusSheetRef, onSelectNamedRange: onSelectNamedRange, onSelectTable: onSelectTable, onRequestUpdateNamedRange: onRequestUpdateNamedRange, onDeleteNamedRange: onDeleteNamedRange, onRequestDefineNamedRange: onRequestDefineNamedRange, onChangeActiveSheet: onChangeActiveSheet }))))));
|
|
249
|
+
React.createElement(Input, { value: value, type: "text", ref: inputRef, onChange: (e) => setDraft(e.target.value), onKeyDown: handleKeyDown, onFocus: (e) => e.target.select(), onBlur: () => setDraft(null), className: "text-xs border-none rounded-none outline-none m-0 bg-rnc-background text-rnc-foreground w-full" }),
|
|
250
|
+
React.createElement(NameBoxMenu, { isOpen: isOpen, onOpenChange: setIsOpen, namedRanges: namedRanges, tables: tables, sheets: sheets, sheetId: sheetId,
|
|
251
|
+
// Only an open menu reads the selection; while closed the props stay
|
|
252
|
+
// put so a cursor move never re-renders the Radix root (it schedules a
|
|
253
|
+
// nested update on every render, see the component note).
|
|
254
|
+
activeCell: isOpen ? activeCell : DEFAULT_ACTIVE_CELL, selections: isOpen ? selections : DEFAULT_ARRAY, shouldFocusSheetRef: shouldFocusSheetRef, focusSheet: focusSheet, onSelectNamedRange: onSelectNamedRange, onSelectTable: onSelectTable, onRequestUpdateNamedRange: onRequestUpdateNamedRange, onDeleteNamedRange: onDeleteNamedRange, onRequestDefineNamedRange: onRequestDefineNamedRange, onChangeActiveSheet: onChangeActiveSheet })));
|
|
235
255
|
}
|
|
236
256
|
export const RangeSelector = memo(RangeSelectorComponent);
|
|
@@ -2,7 +2,28 @@ import { FontLoader } from "../api/font-loader";
|
|
|
2
2
|
export type UseFontResults = {
|
|
3
3
|
isFontActive: boolean;
|
|
4
4
|
};
|
|
5
|
+
/** A host-provided font loader: receives `Family:weight` specs, resolves when they are usable. */
|
|
6
|
+
export type WebFontLoader = (fonts: string[]) => Promise<void | undefined>;
|
|
7
|
+
export type WebFontOptions = {
|
|
8
|
+
/**
|
|
9
|
+
* Whether the grid may fetch web fonts (a Google Fonts stylesheet appended to
|
|
10
|
+
* `document.head`). Hosts with a strict Content-Security-Policy or that must
|
|
11
|
+
* work offline turn this off; the formula editor then uses whatever
|
|
12
|
+
* `--rnc-formula-font-family` resolves to (rowsncolumns/spreadsheet#1125).
|
|
13
|
+
* Default `true`.
|
|
14
|
+
*/
|
|
15
|
+
loadWebFonts?: boolean;
|
|
16
|
+
/** Load fonts through the host instead of Google Fonts (`onRequestFonts`). */
|
|
17
|
+
onRequestFonts?: WebFontLoader;
|
|
18
|
+
};
|
|
19
|
+
/** Configure web-font loading for every grid on the page (what `useFonts` applies from its options). */
|
|
20
|
+
export declare const configureWebFonts: (options: WebFontOptions | undefined) => void;
|
|
21
|
+
export declare const isWebFontLoadingEnabled: () => boolean;
|
|
5
22
|
export declare const getWebFont: () => FontLoader | undefined;
|
|
6
|
-
|
|
7
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Load `Family:weight` specs — through the host's loader when one is configured,
|
|
25
|
+
* otherwise from Google Fonts; a no-op when web fonts are turned off.
|
|
26
|
+
*/
|
|
27
|
+
export declare const loadWebFont: (fonts: string[]) => Promise<void>;
|
|
28
|
+
export declare const useFonts: (fonts?: string, options?: WebFontOptions) => void;
|
|
8
29
|
//# sourceMappingURL=use-fonts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-fonts.d.ts","sourceRoot":"","sources":["../../../hooks/use-fonts.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,UAAU,8BAAmD,CAAC;AAE3E,eAAO,MAAM,WAAW,GAAU,OAAO,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"use-fonts.d.ts","sourceRoot":"","sources":["../../../hooks/use-fonts.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,kGAAkG;AAClG,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC;AAE3E,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,aAAa,CAAC;CAChC,CAAC;AAQF,wGAAwG;AACxG,eAAO,MAAM,iBAAiB,GAAI,SAAS,cAAc,GAAG,SAAS,SAKpE,CAAC;AAEF,eAAO,MAAM,uBAAuB,eAA8B,CAAC;AAEnE,eAAO,MAAM,UAAU,8BAAmD,CAAC;AAE3E;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAU,OAAO,MAAM,EAAE,kBAIhD,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,QAAQ,MAAM,EAAE,UAAU,cAAc,SAYhE,CAAC"}
|
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
import { canUseDOM } from "@rowsncolumns/grid";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
3
|
import { FontLoader } from "../api/font-loader";
|
|
4
|
+
// Page-level: every grid on the page shares one policy, and the paste / import /
|
|
5
|
+
// font-picker fallbacks consult it too, so "off" means no third-party request at all.
|
|
6
|
+
let webFontConfig = { enabled: true };
|
|
7
|
+
/** Configure web-font loading for every grid on the page (what `useFonts` applies from its options). */
|
|
8
|
+
export const configureWebFonts = (options) => {
|
|
9
|
+
webFontConfig = {
|
|
10
|
+
enabled: options?.loadWebFonts ?? true,
|
|
11
|
+
loader: options?.onRequestFonts,
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export const isWebFontLoadingEnabled = () => webFontConfig.enabled;
|
|
4
15
|
export const getWebFont = () => (canUseDOM ? new FontLoader() : undefined);
|
|
16
|
+
/**
|
|
17
|
+
* Load `Family:weight` specs — through the host's loader when one is configured,
|
|
18
|
+
* otherwise from Google Fonts; a no-op when web fonts are turned off.
|
|
19
|
+
*/
|
|
5
20
|
export const loadWebFont = async (fonts) => {
|
|
21
|
+
if (!webFontConfig.enabled)
|
|
22
|
+
return;
|
|
23
|
+
if (webFontConfig.loader)
|
|
24
|
+
return webFontConfig.loader(fonts);
|
|
6
25
|
return getWebFont()?.loadFonts(fonts);
|
|
7
26
|
};
|
|
8
|
-
export const useFonts = (fonts) => {
|
|
27
|
+
export const useFonts = (fonts, options) => {
|
|
28
|
+
const loadWebFonts = options?.loadWebFonts ?? true;
|
|
29
|
+
const onRequestFonts = options?.onRequestFonts;
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
configureWebFonts({ loadWebFonts, onRequestFonts });
|
|
32
|
+
}, [loadWebFonts, onRequestFonts]);
|
|
9
33
|
useEffect(() => {
|
|
34
|
+
if (!loadWebFonts)
|
|
35
|
+
return;
|
|
10
36
|
const defaultFonts = ["Inconsolata:400"];
|
|
11
37
|
const finalFonts = fonts?.split(",").map((value) => String(value).trim());
|
|
12
38
|
loadWebFont(defaultFonts.concat(finalFonts ? finalFonts : []));
|
|
13
|
-
}, [fonts]);
|
|
39
|
+
}, [fonts, loadWebFonts]);
|
|
14
40
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-selection.d.ts","sourceRoot":"","sources":["../../../hooks/use-selection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,UAAU,EACV,qBAAqB,EACrB,aAAa,EACb,eAAe,EACf,aAAa,EACd,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"use-selection.d.ts","sourceRoot":"","sources":["../../../hooks/use-selection.ts"],"names":[],"mappings":"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"}
|