@rowsncolumns/spreadsheet 10.2.77 → 10.3.0
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/api/insert-options.d.ts +67 -0
- package/dist/cjs/api/insert-options.d.ts.map +1 -0
- package/dist/cjs/api/insert-options.js +104 -0
- package/dist/cjs/api/move-overwrite.d.ts +14 -0
- package/dist/cjs/api/move-overwrite.d.ts.map +1 -0
- package/dist/cjs/api/move-overwrite.js +29 -0
- package/dist/cjs/api/multi-selection.d.ts +59 -0
- package/dist/cjs/api/multi-selection.d.ts.map +1 -0
- package/dist/cjs/api/multi-selection.js +166 -0
- package/dist/cjs/atoms/index.d.ts +3 -0
- package/dist/cjs/atoms/index.d.ts.map +1 -1
- package/dist/cjs/atoms/index.js +3 -0
- package/dist/cjs/atoms/insert-cells-dialog.atom.d.ts +36 -0
- package/dist/cjs/atoms/insert-cells-dialog.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/insert-cells-dialog.atom.js +34 -0
- package/dist/cjs/atoms/insert-options.atom.d.ts +21 -0
- package/dist/cjs/atoms/insert-options.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/insert-options.atom.js +11 -0
- package/dist/cjs/atoms/move-overwrite-confirm.atom.d.ts +24 -0
- package/dist/cjs/atoms/move-overwrite-confirm.atom.d.ts.map +1 -0
- package/dist/cjs/atoms/move-overwrite-confirm.atom.js +42 -0
- package/dist/cjs/atoms/sheet-context.atom.d.ts +2 -0
- package/dist/cjs/atoms/sheet-context.atom.d.ts.map +1 -1
- package/dist/cjs/atoms/sheet-context.atom.js +15 -2
- package/dist/cjs/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/cjs/components/canvas-grid/index.js +284 -63
- package/dist/cjs/components/canvas-grid/typings.d.ts +47 -5
- package/dist/cjs/components/canvas-grid/typings.d.ts.map +1 -1
- package/dist/cjs/components/cell-editor/formula-result-preview.d.ts +4 -1
- package/dist/cjs/components/cell-editor/formula-result-preview.d.ts.map +1 -1
- package/dist/cjs/components/cell-editor/formula-result-preview.js +2 -2
- package/dist/cjs/components/cell-editor/index.d.ts +7 -1
- package/dist/cjs/components/cell-editor/index.d.ts.map +1 -1
- package/dist/cjs/components/cell-editor/index.js +3 -3
- package/dist/cjs/components/cell-input/cell-input.d.ts +13 -0
- package/dist/cjs/components/cell-input/cell-input.d.ts.map +1 -1
- package/dist/cjs/components/cell-input/cell-input.js +8 -3
- package/dist/cjs/components/cell-input/formula-bar-cell-input.d.ts.map +1 -1
- package/dist/cjs/components/cell-input/formula-bar-cell-input.js +3 -1
- package/dist/cjs/components/context-menu/index.d.ts +16 -1
- package/dist/cjs/components/context-menu/index.d.ts.map +1 -1
- package/dist/cjs/components/context-menu/index.js +245 -24
- package/dist/cjs/components/floating-cell-editor/index.d.ts.map +1 -1
- package/dist/cjs/components/floating-cell-editor/index.js +13 -5
- package/dist/cjs/components/insert-cells-dialog/index.d.ts +35 -0
- package/dist/cjs/components/insert-cells-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/insert-cells-dialog/index.js +84 -0
- package/dist/cjs/components/insert-options-smart-tag/index.d.ts +25 -0
- package/dist/cjs/components/insert-options-smart-tag/index.d.ts.map +1 -0
- package/dist/cjs/components/insert-options-smart-tag/index.js +41 -0
- package/dist/cjs/components/move-overwrite-confirm-dialog/index.d.ts +8 -0
- package/dist/cjs/components/move-overwrite-confirm-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/move-overwrite-confirm-dialog/index.js +34 -0
- package/dist/cjs/components/selection/index.d.ts +1 -1
- package/dist/cjs/components/selection/index.d.ts.map +1 -1
- package/dist/cjs/components/selection/index.js +4 -2
- package/dist/cjs/components/selection/types.d.ts +8 -0
- package/dist/cjs/components/selection/types.d.ts.map +1 -1
- package/dist/cjs/config.d.ts +6 -0
- package/dist/cjs/config.d.ts.map +1 -1
- package/dist/cjs/config.js +7 -1
- package/dist/cjs/hooks/use-cell-renderer.d.ts +7 -1
- package/dist/cjs/hooks/use-cell-renderer.d.ts.map +1 -1
- package/dist/cjs/hooks/use-cell-renderer.js +8 -3
- package/dist/cjs/hooks/use-context-setter.d.ts.map +1 -1
- package/dist/cjs/hooks/use-context-setter.js +5 -1
- package/dist/cjs/hooks/use-copy-paste-cut.d.ts +5 -0
- package/dist/cjs/hooks/use-copy-paste-cut.d.ts.map +1 -1
- package/dist/cjs/hooks/use-copy-paste-cut.js +19 -0
- package/dist/cjs/hooks/use-editor-change.d.ts +4 -1
- package/dist/cjs/hooks/use-editor-change.d.ts.map +1 -1
- package/dist/cjs/hooks/use-editor-change.js +4 -3
- package/dist/cjs/hooks/use-editor-submit.d.ts +8 -1
- package/dist/cjs/hooks/use-editor-submit.d.ts.map +1 -1
- package/dist/cjs/hooks/use-editor-submit.js +9 -1
- package/dist/cjs/hooks/use-move-columns-rows.d.ts.map +1 -1
- package/dist/cjs/hooks/use-move-columns-rows.js +6 -0
- package/dist/cjs/hooks/use-selection-renderer.d.ts +9 -1
- package/dist/cjs/hooks/use-selection-renderer.d.ts.map +1 -1
- package/dist/cjs/hooks/use-selection-renderer.js +26 -2
- package/dist/cjs/hooks/use-selection.d.ts +13 -5
- package/dist/cjs/hooks/use-selection.d.ts.map +1 -1
- package/dist/cjs/hooks/use-selection.js +48 -6
- package/dist/cjs/index.d.ts +7 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +7 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/api/insert-options.d.ts +67 -0
- package/dist/esm/api/insert-options.d.ts.map +1 -0
- package/dist/esm/api/insert-options.js +91 -0
- package/dist/esm/api/move-overwrite.d.ts +14 -0
- package/dist/esm/api/move-overwrite.d.ts.map +1 -0
- package/dist/esm/api/move-overwrite.js +25 -0
- package/dist/esm/api/multi-selection.d.ts +59 -0
- package/dist/esm/api/multi-selection.d.ts.map +1 -0
- package/dist/esm/api/multi-selection.js +92 -0
- package/dist/esm/atoms/index.d.ts +3 -0
- package/dist/esm/atoms/index.d.ts.map +1 -1
- package/dist/esm/atoms/index.js +3 -0
- package/dist/esm/atoms/insert-cells-dialog.atom.d.ts +36 -0
- package/dist/esm/atoms/insert-cells-dialog.atom.d.ts.map +1 -0
- package/dist/esm/atoms/insert-cells-dialog.atom.js +25 -0
- package/dist/esm/atoms/insert-options.atom.d.ts +21 -0
- package/dist/esm/atoms/insert-options.atom.d.ts.map +1 -0
- package/dist/esm/atoms/insert-options.atom.js +5 -0
- package/dist/esm/atoms/move-overwrite-confirm.atom.d.ts +24 -0
- package/dist/esm/atoms/move-overwrite-confirm.atom.d.ts.map +1 -0
- package/dist/esm/atoms/move-overwrite-confirm.atom.js +29 -0
- package/dist/esm/atoms/sheet-context.atom.d.ts +2 -0
- package/dist/esm/atoms/sheet-context.atom.d.ts.map +1 -1
- package/dist/esm/atoms/sheet-context.atom.js +10 -0
- package/dist/esm/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/esm/components/canvas-grid/index.js +236 -19
- package/dist/esm/components/canvas-grid/typings.d.ts +47 -5
- package/dist/esm/components/canvas-grid/typings.d.ts.map +1 -1
- package/dist/esm/components/cell-editor/formula-result-preview.d.ts +4 -1
- package/dist/esm/components/cell-editor/formula-result-preview.d.ts.map +1 -1
- package/dist/esm/components/cell-editor/formula-result-preview.js +3 -3
- package/dist/esm/components/cell-editor/index.d.ts +7 -1
- package/dist/esm/components/cell-editor/index.d.ts.map +1 -1
- package/dist/esm/components/cell-editor/index.js +3 -3
- package/dist/esm/components/cell-input/cell-input.d.ts +13 -0
- package/dist/esm/components/cell-input/cell-input.d.ts.map +1 -1
- package/dist/esm/components/cell-input/cell-input.js +10 -5
- package/dist/esm/components/cell-input/formula-bar-cell-input.d.ts.map +1 -1
- package/dist/esm/components/cell-input/formula-bar-cell-input.js +3 -1
- package/dist/esm/components/context-menu/index.d.ts +16 -1
- package/dist/esm/components/context-menu/index.d.ts.map +1 -1
- package/dist/esm/components/context-menu/index.js +193 -16
- package/dist/esm/components/floating-cell-editor/index.d.ts.map +1 -1
- package/dist/esm/components/floating-cell-editor/index.js +12 -6
- package/dist/esm/components/insert-cells-dialog/index.d.ts +35 -0
- package/dist/esm/components/insert-cells-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/insert-cells-dialog/index.js +79 -0
- package/dist/esm/components/insert-options-smart-tag/index.d.ts +25 -0
- package/dist/esm/components/insert-options-smart-tag/index.d.ts.map +1 -0
- package/dist/esm/components/insert-options-smart-tag/index.js +35 -0
- package/dist/esm/components/move-overwrite-confirm-dialog/index.d.ts +8 -0
- package/dist/esm/components/move-overwrite-confirm-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/move-overwrite-confirm-dialog/index.js +30 -0
- package/dist/esm/components/selection/index.d.ts +1 -1
- package/dist/esm/components/selection/index.d.ts.map +1 -1
- package/dist/esm/components/selection/index.js +4 -2
- package/dist/esm/components/selection/types.d.ts +8 -0
- package/dist/esm/components/selection/types.d.ts.map +1 -1
- package/dist/esm/config.d.ts +6 -0
- package/dist/esm/config.d.ts.map +1 -1
- package/dist/esm/config.js +6 -0
- package/dist/esm/hooks/use-cell-renderer.d.ts +7 -1
- package/dist/esm/hooks/use-cell-renderer.d.ts.map +1 -1
- package/dist/esm/hooks/use-cell-renderer.js +9 -4
- package/dist/esm/hooks/use-context-setter.d.ts.map +1 -1
- package/dist/esm/hooks/use-context-setter.js +5 -1
- package/dist/esm/hooks/use-copy-paste-cut.d.ts +5 -0
- package/dist/esm/hooks/use-copy-paste-cut.d.ts.map +1 -1
- package/dist/esm/hooks/use-copy-paste-cut.js +19 -0
- package/dist/esm/hooks/use-editor-change.d.ts +4 -1
- package/dist/esm/hooks/use-editor-change.d.ts.map +1 -1
- package/dist/esm/hooks/use-editor-change.js +5 -4
- package/dist/esm/hooks/use-editor-submit.d.ts +8 -1
- package/dist/esm/hooks/use-editor-submit.d.ts.map +1 -1
- package/dist/esm/hooks/use-editor-submit.js +10 -2
- package/dist/esm/hooks/use-move-columns-rows.d.ts.map +1 -1
- package/dist/esm/hooks/use-move-columns-rows.js +6 -0
- package/dist/esm/hooks/use-selection-renderer.d.ts +9 -1
- package/dist/esm/hooks/use-selection-renderer.d.ts.map +1 -1
- package/dist/esm/hooks/use-selection-renderer.js +26 -2
- package/dist/esm/hooks/use-selection.d.ts +13 -5
- package/dist/esm/hooks/use-selection.d.ts.map +1 -1
- package/dist/esm/hooks/use-selection.js +39 -3
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +7 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/spreadsheet.min.css +1 -1
- package/package.json +6 -6
|
@@ -3,26 +3,193 @@ import { ArrowLeftIcon, ArrowRightIcon, CheckIcon, ChevronRightIcon, ClipboardIc
|
|
|
3
3
|
import { DropdownLeftSlot, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuItemIndicator, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownRightSlot, } from "@rowsncolumns/ui";
|
|
4
4
|
import { addressToCell, cellToAddress, number2Alpha } from "@rowsncolumns/utils";
|
|
5
5
|
import React, { useCallback, useMemo, useRef } from "react";
|
|
6
|
+
import { bandsForAreas, bandsFromIndexes, classifyMultiSelection, defaultDeleteCellsChoice, defaultInsertCellsChoice, indexesForAreas, wholeColumnPredicate, wholeColumnPredicateFromHeaderIds, wholeRowPredicate, wholeRowPredicateFromHeaderIds, } from "../../api/multi-selection";
|
|
6
7
|
import { getProtectedRange, isProtectedRange } from "../../api/validation";
|
|
7
8
|
import { useGetContentfulRangeAroundCell } from "../../atoms";
|
|
8
9
|
import { useIsAdvancedFilterDialogMounted, useOpenAdvancedFilterDialog, } from "../../atoms/advanced-filter-dialog.atom";
|
|
9
10
|
import { useIsGoalSeekDialogMounted, useOpenGoalSeekDialog, } from "../../atoms/goal-seek-dialog.atom";
|
|
11
|
+
import { useIsInsertCellsDialogMounted, useOpenInsertCellsDialog, } from "../../atoms/insert-cells-dialog.atom";
|
|
10
12
|
import { useIsRemoveDuplicatesDialogMounted, useOpenRemoveDuplicatesDialog, } from "../../atoms/remove-duplicates-dialog.atom";
|
|
11
13
|
import { useIsSubtotalDialogMounted, useOpenSubtotalDialog, } from "../../atoms/subtotal-dialog.atom";
|
|
12
14
|
import { useIsTextToColumnsDialogMounted, useOpenTextToColumnsDialog, } from "../../atoms/text-to-columns-dialog.atom";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
+
import { useShowValidationAlert } from "../../atoms/validation-alert.atom";
|
|
16
|
+
import { BLANK_LABEL, MULTIPLE_SELECTIONS_ERROR } from "../../config";
|
|
17
|
+
import { generateMultiDimTitle } from "../../helpers";
|
|
15
18
|
import { getTotalRowFunctionFormula, getTotalRowFunctionFromFormula, TOTAL_ROW_FUNCTIONS, } from "./total-row-functions";
|
|
16
|
-
export const ContextMenu = ({ isRowHeader, isColumnHeader, activeCell, selections, basicFilter, isTableHeader, onClearContents, onHideColumn, onHideRow, onGroupRows, onUngroupRows, onGroupColumns, onUngroupColumns, onDeleteRow, onDeleteColumn, onInsertColumn, onInsertRow, onInsertCellsShiftDown, onInsertCellsShiftRight, onDeleteCellsShiftLeft, onDeleteCellsShiftUp, onFreezeRow, onFreezeColumn, onCopy, onCut, onPaste, onRequestPasteSpecial, onRequestEvaluateFormula, onRequestAddWatch, isActiveCellFormula, onInsertNote, onProtectRange, onUnProtectRange, onSortRange, onSortColumn, onRequestDefineNamedRange, onRequestResize, onRequestEditTable, onRequestEditPivotTable, onRequestDeletePivotTable, onPivotDrillDown, onRequestFormatCells, onRequestConditionalFormat, onRequestDataValidation, onRemoveTable, onClearFormatting, onCreateTable, onCreateBasicFilter, onFilterTable, onSortTable, getFormattedValuesFromRange, focusSheet, isSheetFocused, getFormattedValue, onInsertTableColumn, onDeleteTableColumn, onInsertTableRow, onDeleteTableRow, onSplitTextToColumns, onRemoveDuplicates, onGoalSeek, onSubtotal, onAdvancedFilter, onChange, getCellData, isHiddenColumn, isHiddenRow, onShowColumn, onShowRow, tables, pivotTables, protectedRanges, sheetId, frozenRows, frozenColumns, selectedRowHeadersIds, selectedColumnHeadersIds, readonly = false, onAddQuickEdit, enableMagicFill, }) => {
|
|
19
|
+
export const ContextMenu = ({ isRowHeader, isColumnHeader, activeCell, selections, basicFilter, isTableHeader, onClearContents, onHideColumn, onHideRow, onGroupRows, onUngroupRows, onGroupColumns, onUngroupColumns, onDeleteRow, onDeleteColumn, onInsertColumn, onInsertRow, onInsertCellsShiftDown, onInsertCellsShiftRight, onDeleteCellsShiftLeft, onDeleteCellsShiftUp, onFreezeRow, onFreezeColumn, onCopy, onCut, hasCutSelection, onInsertCutCells, onPaste, onRequestPasteSpecial, onRequestEvaluateFormula, onRequestAddWatch, isActiveCellFormula, onInsertNote, onProtectRange, onUnProtectRange, onSortRange, onSortColumn, onRequestDefineNamedRange, onRequestResize, onRequestEditTable, onRequestEditPivotTable, onRequestDeletePivotTable, onPivotDrillDown, onRequestFormatCells, onRequestConditionalFormat, onRequestDataValidation, onRemoveTable, onClearFormatting, onCreateTable, onCreateBasicFilter, onFilterTable, onSortTable, getFormattedValuesFromRange, focusSheet, isSheetFocused, getFormattedValue, onInsertTableColumn, onDeleteTableColumn, onInsertTableRow, onDeleteTableRow, onSplitTextToColumns, onRemoveDuplicates, onGoalSeek, onSubtotal, onAdvancedFilter, onChange, getCellData, isHiddenColumn, isHiddenRow, onShowColumn, onShowRow, tables, pivotTables, protectedRanges, sheetId, frozenRows, frozenColumns, selectedRowHeadersIds, selectedColumnHeadersIds, rowCount, columnCount, readonly = false, onAddQuickEdit, enableMagicFill, }) => {
|
|
17
20
|
const isHeader = isColumnHeader || isRowHeader;
|
|
18
21
|
const shouldFocusSheetRef = useRef(true);
|
|
19
22
|
const multiColumnTitle = generateMultiDimTitle(selectedColumnHeadersIds, "y");
|
|
20
|
-
const
|
|
21
|
-
? selections
|
|
22
|
-
: selectionFromActiveCell(activeCell)
|
|
23
|
+
const selectionAreas = selections.length
|
|
24
|
+
? selections
|
|
25
|
+
: selectionFromActiveCell(activeCell);
|
|
26
|
+
const selection = selectionAreas[0];
|
|
23
27
|
const multiRowTitle = generateMultiDimTitle(selectedRowHeadersIds, "x");
|
|
24
|
-
|
|
25
|
-
const
|
|
28
|
+
// Every Ctrl-click area counts, not just the first (Excel INS-08 / UI-07).
|
|
29
|
+
const selectedRowIndexes = indexesForAreas(selectionAreas, "row");
|
|
30
|
+
const selectedColumnIndexes = indexesForAreas(selectionAreas, "column");
|
|
31
|
+
const isWholeRow = columnCount != null
|
|
32
|
+
? wholeRowPredicate(columnCount)
|
|
33
|
+
: wholeRowPredicateFromHeaderIds(selectedRowHeadersIds);
|
|
34
|
+
const isWholeColumn = rowCount != null
|
|
35
|
+
? wholeColumnPredicate(rowCount)
|
|
36
|
+
: wholeColumnPredicateFromHeaderIds(selectedColumnHeadersIds);
|
|
37
|
+
const multiSelectionKind = classifyMultiSelection(selectionAreas, {
|
|
38
|
+
isWholeRow,
|
|
39
|
+
isWholeColumn,
|
|
40
|
+
});
|
|
41
|
+
const showValidationAlert = useShowValidationAlert();
|
|
42
|
+
const isInsertCellsDialogMounted = useIsInsertCellsDialogMounted();
|
|
43
|
+
const openInsertCellsDialog = useOpenInsertCellsDialog();
|
|
44
|
+
/** Excel: "That command cannot be used on multiple selections." */
|
|
45
|
+
const refuseMultipleSelections = (title) => {
|
|
46
|
+
shouldFocusSheetRef.current = false;
|
|
47
|
+
void showValidationAlert({
|
|
48
|
+
style: "stop",
|
|
49
|
+
title,
|
|
50
|
+
message: MULTIPLE_SELECTIONS_ERROR,
|
|
51
|
+
confirm: false,
|
|
52
|
+
}).then(() => focusSheet());
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* The entire-row bands a row insert / delete acts on — one per selected
|
|
56
|
+
* area, bottom-up, so inserting above each band never shifts a band still
|
|
57
|
+
* to be processed (Excel INS-08). Falls back to the header bookkeeping, then
|
|
58
|
+
* to the active row, when no area is a whole row.
|
|
59
|
+
*/
|
|
60
|
+
const rowBands = () => {
|
|
61
|
+
const areas = selectionAreas.filter(({ range }) => isWholeRow(range));
|
|
62
|
+
const bands = areas.length
|
|
63
|
+
? bandsForAreas(areas, "row")
|
|
64
|
+
: bandsFromIndexes(selectedRowHeadersIds);
|
|
65
|
+
return bands.length
|
|
66
|
+
? bands
|
|
67
|
+
: [{ start: activeCell.rowIndex, end: activeCell.rowIndex }];
|
|
68
|
+
};
|
|
69
|
+
const columnBands = () => {
|
|
70
|
+
const areas = selectionAreas.filter(({ range }) => isWholeColumn(range));
|
|
71
|
+
const bands = areas.length
|
|
72
|
+
? bandsForAreas(areas, "column")
|
|
73
|
+
: bandsFromIndexes(selectedColumnHeadersIds);
|
|
74
|
+
return bands.length
|
|
75
|
+
? bands
|
|
76
|
+
: [{ start: activeCell.columnIndex, end: activeCell.columnIndex }];
|
|
77
|
+
};
|
|
78
|
+
const bandSize = (band) => band.end - band.start + 1;
|
|
79
|
+
const insertRowsAbove = () => {
|
|
80
|
+
for (const band of rowBands()) {
|
|
81
|
+
onInsertRow?.(sheetId, band.start, bandSize(band));
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const insertRowsBelow = () => {
|
|
85
|
+
for (const band of rowBands()) {
|
|
86
|
+
onInsertRow?.(sheetId, band.end + 1, bandSize(band));
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const insertColumnsLeft = () => {
|
|
90
|
+
for (const band of columnBands()) {
|
|
91
|
+
onInsertColumn?.(sheetId, band.start, bandSize(band));
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const insertColumnsRight = () => {
|
|
95
|
+
for (const band of columnBands()) {
|
|
96
|
+
onInsertColumn?.(sheetId, band.end + 1, bandSize(band));
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const isSingleWholeRow = multiSelectionKind === "single" && isWholeRow(selection.range);
|
|
100
|
+
const isSingleWholeColumn = multiSelectionKind === "single" && isWholeColumn(selection.range);
|
|
101
|
+
/**
|
|
102
|
+
* Excel's cell-menu Insert: entire rows / columns insert directly, a
|
|
103
|
+
* multi-area selection inserts per area or is refused, and a plain cell
|
|
104
|
+
* range opens the Insert dialog (UI-06 / UI-07). The direct submenu remains
|
|
105
|
+
* only while no `InsertCellsDialogHost` is mounted.
|
|
106
|
+
*/
|
|
107
|
+
const usesDirectInsertSubmenu = multiSelectionKind === "single" &&
|
|
108
|
+
!isSingleWholeRow &&
|
|
109
|
+
!isSingleWholeColumn &&
|
|
110
|
+
!isInsertCellsDialogMounted;
|
|
111
|
+
const handleInsertCells = () => {
|
|
112
|
+
if (multiSelectionKind === "mixed") {
|
|
113
|
+
refuseMultipleSelections("Insert");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (multiSelectionKind === "all-rows" || isSingleWholeRow) {
|
|
117
|
+
insertRowsAbove();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (multiSelectionKind === "all-columns" || isSingleWholeColumn) {
|
|
121
|
+
insertColumnsLeft();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
shouldFocusSheetRef.current = false;
|
|
125
|
+
openInsertCellsDialog({
|
|
126
|
+
mode: "insert",
|
|
127
|
+
defaultChoice: defaultInsertCellsChoice(selection.range),
|
|
128
|
+
onApply: (choice) => {
|
|
129
|
+
switch (choice) {
|
|
130
|
+
case "shift-right":
|
|
131
|
+
onInsertCellsShiftRight?.(sheetId, activeCell, selections);
|
|
132
|
+
break;
|
|
133
|
+
case "shift-down":
|
|
134
|
+
onInsertCellsShiftDown?.(sheetId, activeCell, selections);
|
|
135
|
+
break;
|
|
136
|
+
case "entire-row":
|
|
137
|
+
onInsertRow?.(sheetId, selection.range.startRowIndex, selectedRowIndexes.length);
|
|
138
|
+
break;
|
|
139
|
+
case "entire-column":
|
|
140
|
+
onInsertColumn?.(sheetId, selection.range.startColumnIndex, selectedColumnIndexes.length);
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
focusSheet();
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
const handleDeleteCells = () => {
|
|
148
|
+
if (multiSelectionKind === "mixed") {
|
|
149
|
+
refuseMultipleSelections("Delete");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (multiSelectionKind === "all-rows" || isSingleWholeRow) {
|
|
153
|
+
onDeleteRow?.(sheetId, selectedRowIndexes);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (multiSelectionKind === "all-columns" || isSingleWholeColumn) {
|
|
157
|
+
onDeleteColumn?.(sheetId, selectedColumnIndexes);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
shouldFocusSheetRef.current = false;
|
|
161
|
+
openInsertCellsDialog({
|
|
162
|
+
mode: "delete",
|
|
163
|
+
defaultChoice: defaultDeleteCellsChoice(selection.range),
|
|
164
|
+
onApply: (choice) => {
|
|
165
|
+
switch (choice) {
|
|
166
|
+
case "shift-left":
|
|
167
|
+
onDeleteCellsShiftLeft?.(sheetId, activeCell, selections);
|
|
168
|
+
break;
|
|
169
|
+
case "shift-up":
|
|
170
|
+
onDeleteCellsShiftUp?.(sheetId, activeCell, selections);
|
|
171
|
+
break;
|
|
172
|
+
case "entire-row":
|
|
173
|
+
onDeleteRow?.(sheetId, selectedRowIndexes);
|
|
174
|
+
break;
|
|
175
|
+
case "entire-column":
|
|
176
|
+
onDeleteColumn?.(sheetId, selectedColumnIndexes);
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
focusSheet();
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
const insertCellsLabel = multiSelectionKind === "all-rows" || isSingleWholeRow
|
|
184
|
+
? `Insert ${selectedRowIndexes.length} row above`
|
|
185
|
+
: multiSelectionKind === "all-columns" || isSingleWholeColumn
|
|
186
|
+
? `Insert ${selectedColumnIndexes.length} column left`
|
|
187
|
+
: "Insert…";
|
|
188
|
+
const deleteCellsLabel = multiSelectionKind === "all-rows" || isSingleWholeRow
|
|
189
|
+
? `Delete row ${generateMultiDimTitle(selectedRowIndexes, "x") ?? ""}`
|
|
190
|
+
: multiSelectionKind === "all-columns" || isSingleWholeColumn
|
|
191
|
+
? `Delete column ${generateMultiDimTitle(selectedColumnIndexes, "y") ?? ""}`
|
|
192
|
+
: "Delete…";
|
|
26
193
|
const hiddenColumns = selectedColumnIndexes.filter((index) => isHiddenColumn(index));
|
|
27
194
|
const hiddenRows = selectedRowIndexes.filter((index) => isHiddenRow(index));
|
|
28
195
|
// Check for active table
|
|
@@ -329,6 +496,10 @@ export const ContextMenu = ({ isRowHeader, isColumnHeader, activeCell, selection
|
|
|
329
496
|
React.createElement(ClipboardIcon, null)),
|
|
330
497
|
"Paste ",
|
|
331
498
|
React.createElement(DropdownRightSlot, null, "\u2318V")),
|
|
499
|
+
onInsertCutCells ? (React.createElement(DropdownMenuItem, { onClick: () => onInsertCutCells(isRowHeader ? "rows" : isColumnHeader ? "columns" : "cells"), disabled: readonly || !hasCutSelection },
|
|
500
|
+
React.createElement(DropdownLeftSlot, null,
|
|
501
|
+
React.createElement(ClipboardIcon, null)),
|
|
502
|
+
"Insert Cut Cells")) : null,
|
|
332
503
|
React.createElement(DropdownMenuSub, null,
|
|
333
504
|
React.createElement(DropdownMenuSubTrigger, { disabled: readonly },
|
|
334
505
|
React.createElement(DropdownLeftSlot, null,
|
|
@@ -373,13 +544,13 @@ export const ContextMenu = ({ isRowHeader, isColumnHeader, activeCell, selection
|
|
|
373
544
|
React.createElement(DropdownMenuItem, { onClick: () => onClearContents?.(sheetId, activeCell, selections), disabled: readonly }, "Clear contents"))),
|
|
374
545
|
React.createElement(DropdownMenuSeparator, null),
|
|
375
546
|
isColumnHeader ? (React.createElement(React.Fragment, null,
|
|
376
|
-
React.createElement(DropdownMenuItem, { onClick:
|
|
547
|
+
React.createElement(DropdownMenuItem, { onClick: insertColumnsLeft, disabled: readonly },
|
|
377
548
|
React.createElement(DropdownLeftSlot, null,
|
|
378
549
|
React.createElement(PlusIcon, null)),
|
|
379
550
|
"Insert ",
|
|
380
551
|
selectedColumnHeadersIds.length,
|
|
381
552
|
" column left"),
|
|
382
|
-
React.createElement(DropdownMenuItem, { onClick:
|
|
553
|
+
React.createElement(DropdownMenuItem, { onClick: insertColumnsRight, disabled: readonly },
|
|
383
554
|
React.createElement(DropdownLeftSlot, null,
|
|
384
555
|
React.createElement(PlusIcon, null)),
|
|
385
556
|
"Insert ",
|
|
@@ -433,13 +604,13 @@ export const ContextMenu = ({ isRowHeader, isColumnHeader, activeCell, selection
|
|
|
433
604
|
React.createElement(SortUpIcon, null)),
|
|
434
605
|
"Sort sheet Z to A"))) : null,
|
|
435
606
|
isRowHeader ? (React.createElement(React.Fragment, null,
|
|
436
|
-
React.createElement(DropdownMenuItem, { onClick:
|
|
607
|
+
React.createElement(DropdownMenuItem, { onClick: insertRowsAbove, disabled: readonly },
|
|
437
608
|
React.createElement(DropdownLeftSlot, null,
|
|
438
609
|
React.createElement(PlusIcon, null)),
|
|
439
610
|
"Insert ",
|
|
440
611
|
selectedRowHeadersIds.length,
|
|
441
612
|
" row above"),
|
|
442
|
-
React.createElement(DropdownMenuItem, { onClick:
|
|
613
|
+
React.createElement(DropdownMenuItem, { onClick: insertRowsBelow, disabled: readonly },
|
|
443
614
|
React.createElement(DropdownLeftSlot, null,
|
|
444
615
|
React.createElement(PlusIcon, null)),
|
|
445
616
|
"Insert ",
|
|
@@ -508,7 +679,7 @@ export const ContextMenu = ({ isRowHeader, isColumnHeader, activeCell, selection
|
|
|
508
679
|
React.createElement(DropdownLeftSlot, null,
|
|
509
680
|
React.createElement(MdOutlineFactCheck, null)),
|
|
510
681
|
"Data validation")) : null)) : (React.createElement(React.Fragment, null,
|
|
511
|
-
React.createElement(DropdownMenuSub, null,
|
|
682
|
+
activeTable || usesDirectInsertSubmenu ? (React.createElement(DropdownMenuSub, null,
|
|
512
683
|
React.createElement(DropdownMenuSubTrigger, { disabled: readonly },
|
|
513
684
|
React.createElement(DropdownLeftSlot, null,
|
|
514
685
|
React.createElement(PlusIcon, null)),
|
|
@@ -556,8 +727,11 @@ export const ContextMenu = ({ isRowHeader, isColumnHeader, activeCell, selection
|
|
|
556
727
|
React.createElement(DropdownMenuItem, { onClick: () => onInsertCellsShiftDown?.(sheetId, activeCell, selections), disabled: readonly },
|
|
557
728
|
React.createElement(DropdownLeftSlot, null,
|
|
558
729
|
React.createElement(PlusIcon, null)),
|
|
559
|
-
"Insert cells and shift cells down"))))),
|
|
560
|
-
|
|
730
|
+
"Insert cells and shift cells down")))))) : (React.createElement(DropdownMenuItem, { onClick: handleInsertCells, disabled: readonly },
|
|
731
|
+
React.createElement(DropdownLeftSlot, null,
|
|
732
|
+
React.createElement(PlusIcon, null)),
|
|
733
|
+
insertCellsLabel)),
|
|
734
|
+
activeTable || usesDirectInsertSubmenu ? (React.createElement(DropdownMenuSub, null,
|
|
561
735
|
React.createElement(DropdownMenuSubTrigger, { disabled: readonly },
|
|
562
736
|
React.createElement(DropdownLeftSlot, null,
|
|
563
737
|
React.createElement(TrashIcon, null)),
|
|
@@ -593,7 +767,10 @@ export const ContextMenu = ({ isRowHeader, isColumnHeader, activeCell, selection
|
|
|
593
767
|
React.createElement(DropdownMenuItem, { onClick: () => onDeleteCellsShiftUp?.(sheetId, activeCell, selections), disabled: readonly },
|
|
594
768
|
React.createElement(DropdownLeftSlot, null,
|
|
595
769
|
React.createElement(TrashIcon, null)),
|
|
596
|
-
"Delete cells and shift cells up"))))),
|
|
770
|
+
"Delete cells and shift cells up")))))) : (React.createElement(DropdownMenuItem, { onClick: handleDeleteCells, disabled: readonly },
|
|
771
|
+
React.createElement(DropdownLeftSlot, null,
|
|
772
|
+
React.createElement(TrashIcon, null)),
|
|
773
|
+
deleteCellsLabel)),
|
|
597
774
|
React.createElement(DropdownMenuSeparator, null),
|
|
598
775
|
React.createElement(DropdownMenuSub, null,
|
|
599
776
|
React.createElement(DropdownMenuSubTrigger, { disabled: readonly },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/floating-cell-editor/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGvE,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/floating-cell-editor/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGvE,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAU3C,OAAO,KAAK,MAAM,OAAO,CAAC;AAc1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAgBrD,MAAM,MAAM,uBAAuB,GAAG;IACpC;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,qBAAqB,CAAC,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;IAC/C;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,UAAU,EAAE,aAAa,CAAC;IAC1B;;OAEG;IACH,UAAU,CAAC,EAAE,aAAa,CAAC,mBAAmB,CAAC,EAAE,CAAC;IAClD;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B;;OAEG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,oBAAoB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;IACtC;;OAEG;IACH,kBAAkB,CAAC,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAC;IAC3D;;OAEG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC,aAAa,CAAC,CAAC;IAC7C;;OAEG;IACH,cAAc,CAAC,EAAE,eAAe,CAAC,gBAAgB,CAAC,CAAC;IAEnD;;OAEG;IACH,+BAA+B,CAAC,EAAE,eAAe,CAAC,iCAAiC,CAAC,CAAC;IACrF;;OAEG;IACH,6BAA6B,CAAC,EAAE,eAAe,CAAC,+BAA+B,CAAC,CAAC;IACjF;;OAEG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;CACnD,CAAC;AAEF,eAAO,MAAM,kBAAkB,mRAkB1B,uBAAuB,uBAwW3B,CAAC"}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { isEqualCells } from "@rowsncolumns/grid";
|
|
2
2
|
import { CheckIcon, Cross1Icon, Pencil1Icon } from "@rowsncolumns/icons";
|
|
3
3
|
import { cn, IconButton } from "@rowsncolumns/ui";
|
|
4
|
+
import { canonicalizeCellInput, localizeCellInput } from "@rowsncolumns/utils";
|
|
4
5
|
import { useSelectionAwareChangeFormatting } from "../../hooks/use-selection-aware-formatting";
|
|
5
6
|
import { memo, useCallback, useDeferredValue, useMemo, useRef, useState, } from "react";
|
|
6
7
|
import React from "react";
|
|
7
|
-
import { useEditingCell, useFocusSheet, useFormulaModeValue, useSetActiveEditor, useSetFormulaMode, useSetFormulaSelections, useSpreadsheet, } from "../../atoms";
|
|
8
|
+
import { useEditingCell, useFocusSheet, useFormulaLocale, useFormulaModeValue, useSetActiveEditor, useSetFormulaMode, useSetFormulaSelections, useSpreadsheet, } from "../../atoms";
|
|
8
9
|
import { DEFAULT_ARRAY } from "../../config";
|
|
9
10
|
import { useIsomorphicLayoutEffect } from "../../hooks";
|
|
10
11
|
import { FormulaInput } from "../formula-input";
|
|
11
12
|
import { BackgroundColorSelector, ButtonBold, ButtonInsertColumnRight, ButtonInsertRowBelow, ButtonItalic, ButtonStrikethrough, ButtonSubscript, ButtonSuperscript, ButtonUnderline, TextColorSelector, TextHorizontalAlignSelector, ToolbarSeparator, } from "../toolbar";
|
|
12
13
|
export const FloatingCellEditor = memo(({ initialValue, initialTextFormatRuns, className, activeCell, sheetId, cellFormat, theme, isDarkMode, functionDescriptions, selections = DEFAULT_ARRAY, onChange, onChangeFormatting: onChangeFormattingProp, onInsertRow, onInsertColumn, ColorSelector, ...props }) => {
|
|
13
14
|
const formulaInputRef = useRef(null);
|
|
14
|
-
|
|
15
|
+
// The stored (canonical) entry is shown and edited in the grid's formula locale; the commit
|
|
16
|
+
// below hands the canonical text back to the host.
|
|
17
|
+
const formulaLocale = useFormulaLocale();
|
|
18
|
+
const [value, setValue] = useState(() => localizeCellInput(initialValue ?? "", formulaLocale));
|
|
15
19
|
const [textFormatRuns, setTextFormatRuns] = useState(initialTextFormatRuns);
|
|
16
20
|
const setFormulaMode = useSetFormulaMode();
|
|
17
21
|
const formulaMode = useFormulaModeValue();
|
|
@@ -54,15 +58,16 @@ export const FloatingCellEditor = memo(({ initialValue, initialTextFormatRuns, c
|
|
|
54
58
|
// Set value when not editing, or when editing but not in formula mode
|
|
55
59
|
if (!isEditing || !formulaMode) {
|
|
56
60
|
const currentValue = formulaInputRef.current?.getValue();
|
|
57
|
-
const newValue = initialValue ?? "";
|
|
61
|
+
const newValue = localizeCellInput(initialValue ?? "", formulaLocale);
|
|
58
62
|
if (currentValue !== newValue && (currentValue || newValue)) {
|
|
59
63
|
setValue(newValue);
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
|
-
}, [initialValue, isEditing, formulaMode]);
|
|
66
|
+
}, [initialValue, isEditing, formulaMode, formulaLocale]);
|
|
63
67
|
const handleSubmit = useCallback((value, textFormatRuns) => {
|
|
64
68
|
if (editingCellCoordinate) {
|
|
65
|
-
|
|
69
|
+
const canonicalValue = canonicalizeCellInput(value, formulaLocale);
|
|
70
|
+
onChange?.(editingCellCoordinate.sheetId, editingCellCoordinate, canonicalValue, textFormatRuns, initialValue, initialValue !== canonicalValue, initialTextFormatRuns);
|
|
66
71
|
if (!isEqualCells(editingCellCoordinate, activeCell)) {
|
|
67
72
|
setActiveCell?.(editingCellCoordinate.sheetId, editingCellCoordinate, false);
|
|
68
73
|
}
|
|
@@ -76,6 +81,7 @@ export const FloatingCellEditor = memo(({ initialValue, initialTextFormatRuns, c
|
|
|
76
81
|
stopEditing,
|
|
77
82
|
focusSheet,
|
|
78
83
|
onChange,
|
|
84
|
+
formulaLocale,
|
|
79
85
|
]);
|
|
80
86
|
const onFocus = useCallback(() => {
|
|
81
87
|
if (!isEditing) {
|
|
@@ -97,7 +103,7 @@ export const FloatingCellEditor = memo(({ initialValue, initialTextFormatRuns, c
|
|
|
97
103
|
lineHeight: "normal",
|
|
98
104
|
fontSize: "1rem",
|
|
99
105
|
minHeight: 44,
|
|
100
|
-
}, functionDescriptions: functionDescriptions, getMentions: getMentions, ...props })),
|
|
106
|
+
}, functionDescriptions: functionDescriptions, getMentions: getMentions, formulaLocale: formulaLocale, ...props })),
|
|
101
107
|
isEditing ? (React.createElement(React.Fragment, null,
|
|
102
108
|
React.createElement(IconButton, { tooltip: "Save", variant: "primary", type: "submit", className: "min-w-8 min-h-8" },
|
|
103
109
|
React.createElement(CheckIcon, null)),
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { DeleteCellsChoice, InsertCellsChoice } from "../../api/multi-selection";
|
|
3
|
+
export type InsertCellsDialogProps = {
|
|
4
|
+
mode: "insert";
|
|
5
|
+
/** Controls visibility. */
|
|
6
|
+
open: boolean;
|
|
7
|
+
onOpenChange(open: boolean): void;
|
|
8
|
+
/** Pre-selected entry — Excel derives it from the selection shape (UI-06). */
|
|
9
|
+
defaultChoice: InsertCellsChoice;
|
|
10
|
+
onApply(choice: InsertCellsChoice): void;
|
|
11
|
+
} | {
|
|
12
|
+
mode: "delete";
|
|
13
|
+
open: boolean;
|
|
14
|
+
onOpenChange(open: boolean): void;
|
|
15
|
+
defaultChoice: DeleteCellsChoice;
|
|
16
|
+
onApply(choice: DeleteCellsChoice): void;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Excel's Insert / Delete cells dialog (right-click a cell → "Insert…" /
|
|
20
|
+
* "Delete…"): Shift cells right|down (left|up for Delete), Entire row, Entire
|
|
21
|
+
* column. UI-only — the opener turns the choice into the matching structural
|
|
22
|
+
* command. Header menus never show it: entire rows / columns insert directly.
|
|
23
|
+
*/
|
|
24
|
+
export declare const InsertCellsDialog: React.MemoExoticComponent<(props: InsertCellsDialogProps) => React.JSX.Element>;
|
|
25
|
+
/**
|
|
26
|
+
* Host-mounted, atom-driven wrapper around {@link InsertCellsDialog}. Mount it
|
|
27
|
+
* once in the host alongside the other editor dialogs; the grid opens it from
|
|
28
|
+
* the cell context menu. Remounted per request so each opening starts from the
|
|
29
|
+
* request's default.
|
|
30
|
+
*/
|
|
31
|
+
export declare const InsertCellsDialogHost: {
|
|
32
|
+
(): React.JSX.Element | null;
|
|
33
|
+
displayName: string;
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/insert-cells-dialog/index.tsx"],"names":[],"mappings":"AASA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAEtE,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,2BAA2B,CAAC;AAoBnC,MAAM,MAAM,sBAAsB,GAC9B;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,2BAA2B;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,8EAA8E;IAC9E,aAAa,EAAE,iBAAiB,CAAC;IACjC,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC1C,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,aAAa,EAAE,iBAAiB,CAAC;IACjC,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC1C,CAAC;AAEN;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,oCAAgB,sBAAsB,uBA0ElE,CAAC;AAIH;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;CAyBjC,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Button, Dialog, DialogBody, DialogCloseButton, DialogContent, DialogTitle, Separator, } from "@rowsncolumns/ui";
|
|
2
|
+
import React, { memo, useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { useInsertCellsDialogState, useRegisterInsertCellsDialogHost, } from "../../atoms/insert-cells-dialog.atom";
|
|
4
|
+
const INSERT_CHOICES = [
|
|
5
|
+
{ value: "shift-right", label: "Shift cells right" },
|
|
6
|
+
{ value: "shift-down", label: "Shift cells down" },
|
|
7
|
+
{ value: "entire-row", label: "Entire row" },
|
|
8
|
+
{ value: "entire-column", label: "Entire column" },
|
|
9
|
+
];
|
|
10
|
+
const DELETE_CHOICES = [
|
|
11
|
+
{ value: "shift-left", label: "Shift cells left" },
|
|
12
|
+
{ value: "shift-up", label: "Shift cells up" },
|
|
13
|
+
{ value: "entire-row", label: "Entire row" },
|
|
14
|
+
{ value: "entire-column", label: "Entire column" },
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* Excel's Insert / Delete cells dialog (right-click a cell → "Insert…" /
|
|
18
|
+
* "Delete…"): Shift cells right|down (left|up for Delete), Entire row, Entire
|
|
19
|
+
* column. UI-only — the opener turns the choice into the matching structural
|
|
20
|
+
* command. Header menus never show it: entire rows / columns insert directly.
|
|
21
|
+
*/
|
|
22
|
+
export const InsertCellsDialog = memo((props) => {
|
|
23
|
+
const { mode, open, onOpenChange, defaultChoice } = props;
|
|
24
|
+
const [choice, setChoice] = useState(defaultChoice);
|
|
25
|
+
// Re-seed on every open (the request decides the default).
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (open)
|
|
28
|
+
setChoice(defaultChoice);
|
|
29
|
+
}, [open, defaultChoice]);
|
|
30
|
+
const commit = useCallback(() => {
|
|
31
|
+
if (props.mode === "insert") {
|
|
32
|
+
props.onApply(choice);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
props.onApply(choice);
|
|
36
|
+
}
|
|
37
|
+
onOpenChange(false);
|
|
38
|
+
}, [props, choice, onOpenChange]);
|
|
39
|
+
const title = mode === "insert" ? "Insert" : "Delete";
|
|
40
|
+
const choices = mode === "insert" ? INSERT_CHOICES : DELETE_CHOICES;
|
|
41
|
+
return (React.createElement(Dialog, { open: open, onOpenChange: onOpenChange, modal: true },
|
|
42
|
+
React.createElement(DialogContent, { className: "w-[300px] max-w-[92vw] pointer-events-auto" },
|
|
43
|
+
React.createElement(DialogTitle, null, title),
|
|
44
|
+
React.createElement(DialogCloseButton, null),
|
|
45
|
+
React.createElement(DialogBody, { className: "flex flex-col gap-4" },
|
|
46
|
+
React.createElement("form", { className: "flex flex-col gap-4", onSubmit: (e) => {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
commit();
|
|
49
|
+
} },
|
|
50
|
+
React.createElement("fieldset", { className: "flex flex-col gap-1 min-w-0 border-0 p-0 m-0" },
|
|
51
|
+
React.createElement("legend", { className: "text-xs font-medium mb-1" }, title),
|
|
52
|
+
choices.map((entry) => (React.createElement("label", { key: entry.value, className: "flex items-center gap-2 text-xs py-0.5 cursor-pointer" },
|
|
53
|
+
React.createElement("input", { type: "radio", name: `${mode}-cells-choice`, value: entry.value, checked: choice === entry.value, onChange: () => setChoice(entry.value) }),
|
|
54
|
+
entry.label)))),
|
|
55
|
+
React.createElement(Separator, null),
|
|
56
|
+
React.createElement("div", { className: "flex justify-end gap-2" },
|
|
57
|
+
React.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onOpenChange(false) }, "Cancel"),
|
|
58
|
+
React.createElement(Button, { type: "submit", size: "sm", autoFocus: true }, "OK")))))));
|
|
59
|
+
});
|
|
60
|
+
InsertCellsDialog.displayName = "InsertCellsDialog";
|
|
61
|
+
/**
|
|
62
|
+
* Host-mounted, atom-driven wrapper around {@link InsertCellsDialog}. Mount it
|
|
63
|
+
* once in the host alongside the other editor dialogs; the grid opens it from
|
|
64
|
+
* the cell context menu. Remounted per request so each opening starts from the
|
|
65
|
+
* request's default.
|
|
66
|
+
*/
|
|
67
|
+
export const InsertCellsDialogHost = () => {
|
|
68
|
+
useRegisterInsertCellsDialogHost();
|
|
69
|
+
const [request, setRequest] = useInsertCellsDialogState();
|
|
70
|
+
const close = useCallback(() => setRequest(null), [setRequest]);
|
|
71
|
+
if (!request)
|
|
72
|
+
return null;
|
|
73
|
+
const onOpenChange = (open) => {
|
|
74
|
+
if (!open)
|
|
75
|
+
close();
|
|
76
|
+
};
|
|
77
|
+
return request.mode === "insert" ? (React.createElement(InsertCellsDialog, { mode: "insert", open: true, onOpenChange: onOpenChange, defaultChoice: request.defaultChoice, onApply: request.onApply })) : (React.createElement(InsertCellsDialog, { mode: "delete", open: true, onOpenChange: onOpenChange, defaultChoice: request.defaultChoice, onApply: request.onApply }));
|
|
78
|
+
};
|
|
79
|
+
InsertCellsDialogHost.displayName = "InsertCellsDialogHost";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { InsertFormatOption, InsertOptionsAxis } from "../../api/insert-options";
|
|
3
|
+
export type InsertOptionsSmartTagProps = {
|
|
4
|
+
/** Position/size of the selection the tag is anchored to (bottom-left). */
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
axis: InsertOptionsAxis;
|
|
10
|
+
/** The entry currently applied to the inserted band. */
|
|
11
|
+
option: InsertFormatOption;
|
|
12
|
+
onChooseOption(option: InsertFormatOption): void;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Excel's Insert Options smart tag: the small paintbrush button that appears at
|
|
16
|
+
* the inserted rows / columns right after an insert and offers "Format Same As
|
|
17
|
+
* Above / Below / Clear Formatting" (Left / Right for columns). The insert
|
|
18
|
+
* already applied "Same As Above / Left"; picking another entry re-formats the
|
|
19
|
+
* band. Same skin as the Auto Fill Options tag.
|
|
20
|
+
*/
|
|
21
|
+
export declare const InsertOptionsSmartTag: {
|
|
22
|
+
({ x, y, width: _width, height, axis, option, onChooseOption, }: InsertOptionsSmartTagProps): React.JSX.Element;
|
|
23
|
+
displayName: string;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/insert-options-smart-tag/index.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,0BAA0B,CAAC;AAElC,MAAM,MAAM,0BAA0B,GAAG;IACvC,2EAA2E;IAC3E,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,iBAAiB,CAAC;IACxB,wDAAwD;IACxD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;CAClD,CAAC;AAgBF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;qEAQ/B,0BAA0B;;CA0C5B,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CheckIcon, PaintBrush } from "@rowsncolumns/icons";
|
|
2
|
+
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItemIndicator, DropdownMenuPortal, DropdownMenuTrigger, IconButton, } from "@rowsncolumns/ui";
|
|
3
|
+
import React, { useState } from "react";
|
|
4
|
+
const ROW_ENTRIES = [
|
|
5
|
+
{ option: "before", label: "Format Same As Above" },
|
|
6
|
+
{ option: "after", label: "Format Same As Below" },
|
|
7
|
+
{ option: "clear", label: "Clear Formatting" },
|
|
8
|
+
];
|
|
9
|
+
const COLUMN_ENTRIES = [
|
|
10
|
+
{ option: "before", label: "Format Same As Left" },
|
|
11
|
+
{ option: "after", label: "Format Same As Right" },
|
|
12
|
+
{ option: "clear", label: "Clear Formatting" },
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* Excel's Insert Options smart tag: the small paintbrush button that appears at
|
|
16
|
+
* the inserted rows / columns right after an insert and offers "Format Same As
|
|
17
|
+
* Above / Below / Clear Formatting" (Left / Right for columns). The insert
|
|
18
|
+
* already applied "Same As Above / Left"; picking another entry re-formats the
|
|
19
|
+
* band. Same skin as the Auto Fill Options tag.
|
|
20
|
+
*/
|
|
21
|
+
export const InsertOptionsSmartTag = ({ x, y, width: _width, height, axis, option, onChooseOption, }) => {
|
|
22
|
+
const [open, setOpen] = useState(false);
|
|
23
|
+
const entries = axis === "row" ? ROW_ENTRIES : COLUMN_ENTRIES;
|
|
24
|
+
return (React.createElement("div", { className: "absolute pointer-events-auto flex items-start justify-start", style: { left: x, top: y + height }, onMouseDown: (e) => e.stopPropagation(), "data-testid": "insert-options" },
|
|
25
|
+
React.createElement(DropdownMenu, { modal: false, open: open, onOpenChange: setOpen },
|
|
26
|
+
React.createElement(DropdownMenuTrigger, { asChild: true },
|
|
27
|
+
React.createElement(IconButton, { tooltip: "Insert Options", "aria-label": "Insert Options", className: "shadow-sm bg-rnc-background border-rnc-border border border-solid rounded-md" },
|
|
28
|
+
React.createElement(PaintBrush, null))),
|
|
29
|
+
React.createElement(DropdownMenuPortal, null,
|
|
30
|
+
React.createElement(DropdownMenuContent, { align: "start", onCloseAutoFocus: (event) => event.preventDefault() }, entries.map((entry) => (React.createElement(DropdownMenuCheckboxItem, { key: entry.option, checked: option === entry.option, onSelect: () => onChooseOption(entry.option) },
|
|
31
|
+
React.createElement(DropdownMenuItemIndicator, null,
|
|
32
|
+
React.createElement(CheckIcon, null)),
|
|
33
|
+
entry.label))))))));
|
|
34
|
+
};
|
|
35
|
+
InsertOptionsSmartTag.displayName = "InsertOptionsSmartTag";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Excel's drag-move overwrite prompt ("There's already data here. Do you want to
|
|
4
|
+
* replace it?"). Mount once next to the grid; while mounted, a plain drag-move
|
|
5
|
+
* onto non-empty cells waits for OK / Cancel here instead of `window.confirm`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MoveOverwriteConfirmDialogHost: React.MemoExoticComponent<() => React.JSX.Element>;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/move-overwrite-confirm-dialog/index.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAe,MAAM,OAAO,CAAC;AAOpC;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,oDAgCzC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Cross2Icon } from "@rowsncolumns/icons";
|
|
2
|
+
import { Button, Dialog, DialogBody, DialogClose, DialogCloseButton, DialogContent, DialogDescription, DialogTitle, Stack, } from "@rowsncolumns/ui";
|
|
3
|
+
import React, { memo } from "react";
|
|
4
|
+
import { useMoveOverwriteConfirmState, useRegisterMoveOverwriteConfirmHost, } from "../../atoms/move-overwrite-confirm.atom";
|
|
5
|
+
/**
|
|
6
|
+
* Excel's drag-move overwrite prompt ("There's already data here. Do you want to
|
|
7
|
+
* replace it?"). Mount once next to the grid; while mounted, a plain drag-move
|
|
8
|
+
* onto non-empty cells waits for OK / Cancel here instead of `window.confirm`.
|
|
9
|
+
*/
|
|
10
|
+
export const MoveOverwriteConfirmDialogHost = memo(() => {
|
|
11
|
+
useRegisterMoveOverwriteConfirmHost();
|
|
12
|
+
const [request] = useMoveOverwriteConfirmState();
|
|
13
|
+
const isOpen = Boolean(request);
|
|
14
|
+
const answer = (replace) => request?.resolve(replace);
|
|
15
|
+
return (React.createElement(Dialog, { modal: true, open: isOpen, onOpenChange: (open) => {
|
|
16
|
+
if (!open)
|
|
17
|
+
answer(false);
|
|
18
|
+
} },
|
|
19
|
+
React.createElement(DialogContent, null,
|
|
20
|
+
React.createElement(DialogTitle, null, "Replace destination cells?"),
|
|
21
|
+
React.createElement(DialogDescription, null, request?.message),
|
|
22
|
+
React.createElement(DialogBody, null,
|
|
23
|
+
React.createElement(Stack, { className: "gap-2" },
|
|
24
|
+
React.createElement(Button, { variant: "primary", onClick: () => answer(true) }, "OK"),
|
|
25
|
+
React.createElement(Button, { onClick: () => answer(false) }, "Cancel"))),
|
|
26
|
+
React.createElement(DialogClose, { asChild: true },
|
|
27
|
+
React.createElement(DialogCloseButton, null,
|
|
28
|
+
React.createElement(Cross2Icon, null))))));
|
|
29
|
+
});
|
|
30
|
+
MoveOverwriteConfirmDialogHost.displayName = "MoveOverwriteConfirmDialogHost";
|
|
@@ -5,5 +5,5 @@ import type { SelectionNewProps } from "./types";
|
|
|
5
5
|
* @param param0
|
|
6
6
|
* @returns
|
|
7
7
|
*/
|
|
8
|
-
export declare const Selection: React.MemoExoticComponent<({ x, y, width, height, fillHandleSize, showFillHandle, offset, stroke, strokeLeftColor, strokeTopColor, strokeRightColor, strokeBottomColor, strokeWidth, strokeTopWidth, strokeRightWidth, strokeBottomWidth, strokeLeftWidth, strokeStyle, fillOpacity, draggable, isDragging, borderCoverWidth, type, range, title, keepTitleWithinBounds, keepTitleWithinVerticalBounds, flipTitleToBottom, table, pivotTable, onMouseDownTitle, onMouseDownResizeHandle, onMouseDownMoveHandle, TitleComponent, onMouseDown, onRequestEditTable, onRequestEditPivotTable, onFlashEnd, zIndex, index, id, showResizeHandles, resizeHandleBorderColor, resizeHandleSize, fill, duration, selectionCount, enableMagicFill, sheetId, activeCell, selection, MagicFillComponent, metadata, readonly, showPasteMenu, onSplitTextToColumns, agentSuggestion, isHovered, MagicFillContent, QuickEdit, PasteMenu, FillOptionsMenu, fillOptions, onChooseFillType, onOpenFillSeriesDialog, onCloseFillOptions, SuggestionAction, onRemoveQuickEdit, onAddQuickEdit, }: SelectionNewProps) => React.JSX.Element>;
|
|
8
|
+
export declare const Selection: React.MemoExoticComponent<({ x, y, width, height, fillHandleSize, showFillHandle, offset, stroke, strokeLeftColor, strokeTopColor, strokeRightColor, strokeBottomColor, strokeWidth, strokeTopWidth, strokeRightWidth, strokeBottomWidth, strokeLeftWidth, strokeStyle, fillOpacity, draggable, isDragging, borderCoverWidth, type, range, title, keepTitleWithinBounds, keepTitleWithinVerticalBounds, flipTitleToBottom, table, pivotTable, onMouseDownTitle, onMouseDownResizeHandle, onMouseDownMoveHandle, TitleComponent, onMouseDown, onRequestEditTable, onRequestEditPivotTable, onFlashEnd, zIndex, index, id, showResizeHandles, resizeHandleBorderColor, resizeHandleSize, fill, duration, selectionCount, enableMagicFill, sheetId, activeCell, selection, MagicFillComponent, metadata, readonly, showPasteMenu, onSplitTextToColumns, agentSuggestion, isHovered, MagicFillContent, QuickEdit, PasteMenu, FillOptionsMenu, fillOptions, onChooseFillType, onOpenFillSeriesDialog, onCloseFillOptions, InsertOptionsSmartTag, insertOptions, onChooseInsertOption, SuggestionAction, onRemoveQuickEdit, onAddQuickEdit, }: SelectionNewProps) => React.JSX.Element>;
|
|
9
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/selection/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAe,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../components/selection/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAe,MAAM,OAAO,CAAC;AAepC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,SAAS,2kCA0EjB,iBAAiB,uBAqUrB,CAAC"}
|