Skip to content

Commit 903bd9e

Browse files
authored
Optimize additive selection mode (#1114)
* Optimize additive selection mode * Added more tests * Add coumn selection mode * Fix * Fix issue with dragging on row will cause the cell rect to resize * Add support for auto touch mode
1 parent 0d90b8e commit 903bd9e

File tree

5 files changed

+216
-29
lines changed

5 files changed

+216
-29
lines changed

packages/core/API.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ Most data grids will want to set the majority of these props one way or another.
192192
| [onRowMoved](#onrowmoved) | Emitted when a row has been dragged to a new location. |
193193
| [preventDiagonalScrolling](#preventdiagonalscrolling) | Prevents diagonal scrolling |
194194
| [rowSelectionMode](#rowselectionmode) | Determines if row selection requires a modifier key to enable multi-selection or not. |
195+
| [columnSelectionMode](#columnselectionmode) | Determines if column selection requires a modifier key to enable multi-selection or not. |
195196
| [scrollToEnd](#scrolltoend) | When set to true, the grid will scroll to the end. The ref has a better method to do this and this prop should not be used but it will remain supported for the foreseeable future. |
196197
| [showMinimap](#showminimap) | Shows the interactive minimap of the grid. |
197198
| [validateCell](#validatecell) | When returns false indicates to the user the value will not be accepted. When returns a new GridCell the value is coerced to match. |
@@ -1407,6 +1408,16 @@ rowSelectionMode?: "auto" | "multi";
14071408

14081409
---
14091410

1411+
## columnSelectionMode
1412+
1413+
```ts
1414+
columnSelectionMode?: "auto" | "multi";
1415+
```
1416+
1417+
`columnSelectionMode` changes how selecting columns behaves. In auto mode it adapts to touch or mouse environments automatically, in multi-mode it always acts as if the multi key (Ctrl) is pressed.
1418+
1419+
---
1420+
14101421
## showMinimap
14111422

14121423
```ts

packages/core/src/data-editor/data-editor.tsx

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ export interface DataEditorProps extends Props, Pick<DataGridSearchProps, "image
544544
* @returns A valid GridCell to be rendered by the Grid.
545545
*/
546546
readonly getCellContent: (cell: Item) => GridCell;
547+
547548
/**
548549
* Determines if row selection requires a modifier key to enable multi-selection or not. In auto mode it adapts to
549550
* touch or mouse environments automatically, in multi-mode it always acts as if the multi key (Ctrl) is pressed.
@@ -552,6 +553,14 @@ export interface DataEditorProps extends Props, Pick<DataGridSearchProps, "image
552553
*/
553554
readonly rowSelectionMode?: "auto" | "multi";
554555

556+
/**
557+
* Determines if column selection requires a modifier key to enable multi-selection or not. In auto mode it adapts to
558+
* touch or mouse environments automatically, in multi-mode it always acts as if the multi key (Ctrl) is pressed.
559+
* @group Editing
560+
* @defaultValue `auto`
561+
*/
562+
readonly columnSelectionMode?: "auto" | "multi";
563+
555564
/**
556565
* Add table headers to copied data.
557566
* @group Editing
@@ -838,6 +847,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
838847
freezeColumns = 0,
839848
cellActivationBehavior = "second-click",
840849
rowSelectionMode = "auto",
850+
columnSelectionMode = "auto",
841851
onHeaderMenuClick,
842852
onHeaderIndicatorClick,
843853
getGroupDetails,
@@ -1320,7 +1330,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
13201330
//If the grid is empty, we will return text
13211331
const isFirst = col === rowMarkerOffset;
13221332

1323-
const maybeFirstColumnHint = isFirst ? trailingRowOptions?.hint ?? "" : "";
1333+
const maybeFirstColumnHint = isFirst ? (trailingRowOptions?.hint ?? "") : "";
13241334
const c = mangledColsRef.current[col];
13251335

13261336
if (c?.trailingRowOptions?.disabled === true) {
@@ -1829,7 +1839,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
18291839
(args: GridMouseEventArgs) => {
18301840
const isMultiKey = browserIsOSX.value ? args.metaKey : args.ctrlKey;
18311841
const isMultiRow = isMultiKey && rowSelect === "multi";
1832-
const isMultiCol = isMultiKey && columnSelect === "multi";
1842+
18331843
const [col, row] = args.location;
18341844
const selectedColumns = gridSelection.columns;
18351845
const selectedRows = gridSelection.rows;
@@ -2003,14 +2013,19 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
20032013
lastCol !== undefined &&
20042014
selectedColumns.hasIndex(lastCol)
20052015
) {
2016+
// Support for selecting a slice of columns:
20062017
const newSlice: Slice = [Math.min(lastCol, col), Math.max(lastCol, col) + 1];
20072018

2008-
if (isMultiCol) {
2019+
if (isMultiKey || args.isTouch || columnSelectionMode === "multi") {
20092020
setSelectedColumns(undefined, newSlice, isMultiKey);
20102021
} else {
20112022
setSelectedColumns(CompactSelection.fromSingleSelection(newSlice), undefined, isMultiKey);
20122023
}
2013-
} else if (isMultiCol) {
2024+
} else if (
2025+
columnSelect === "multi" &&
2026+
(isMultiKey || args.isTouch || columnSelectionMode === "multi")
2027+
) {
2028+
// Support for selecting a single columns additively:
20142029
if (selectedColumns.hasIndex(col)) {
20152030
// If the column is already selected, deselect that column:
20162031
setSelectedColumns(selectedColumns.remove(col), undefined, isMultiKey);
@@ -2020,6 +2035,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
20202035
lastSelectedColRef.current = col;
20212036
} else if (columnSelect !== "none") {
20222037
if (selectedColumns.hasIndex(col)) {
2038+
// If the column is already selected, deselect that column:
20232039
setSelectedColumns(selectedColumns.remove(col), undefined, isMultiKey);
20242040
} else {
20252041
setSelectedColumns(CompactSelection.fromSingleSelection(col), undefined, isMultiKey);
@@ -2053,6 +2069,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
20532069
onRowMoved,
20542070
focus,
20552071
rowSelectionMode,
2072+
columnSelectionMode,
20562073
getCellRenderer,
20572074
themeForCell,
20582075
setSelectedRows,
@@ -2146,7 +2163,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
21462163

21472164
focus();
21482165

2149-
if (isMultiKey) {
2166+
if (isMultiKey || args.isTouch || columnSelectionMode === "multi") {
21502167
if (selectedColumns.hasAll([start, end + 1])) {
21512168
let newVal = selectedColumns;
21522169
for (let index = start; index <= end; index++) {
@@ -2160,7 +2177,15 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
21602177
setSelectedColumns(CompactSelection.fromSingleSelection([start, end + 1]), undefined, isMultiKey);
21612178
}
21622179
},
2163-
[columnSelect, focus, gridSelection.columns, mangledCols, rowMarkerOffset, setSelectedColumns]
2180+
[
2181+
columnSelect,
2182+
focus,
2183+
gridSelection.columns,
2184+
mangledCols,
2185+
rowMarkerOffset,
2186+
setSelectedColumns,
2187+
columnSelectionMode,
2188+
]
21642189
);
21652190

21662191
const isPrevented = React.useRef(false);
@@ -2392,9 +2417,10 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
23922417
}
23932418
}
23942419
if (shouldActivate) {
2395-
const act = a.isDoubleClick === true
2396-
? "double-click"
2397-
: (c.activationBehaviorOverride ?? cellActivationBehavior);
2420+
const act =
2421+
a.isDoubleClick === true
2422+
? "double-click"
2423+
: (c.activationBehaviorOverride ?? cellActivationBehavior);
23982424
const activationEvent: CellActivatedEventArgs = {
23992425
inputType: "pointer",
24002426
pointerActivation: act,
@@ -2709,7 +2735,6 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
27092735
args.buttons !== 0 &&
27102736
mouseState !== undefined &&
27112737
mouseDownData.current?.location[0] === 0 &&
2712-
args.location[0] === 0 &&
27132738
rowMarkerOffset === 1 &&
27142739
rowSelect === "multi" &&
27152740
mouseState.previousSelection &&
@@ -2720,7 +2745,8 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
27202745
const end = Math.max(mouseDownData.current.location[1], args.location[1]) + 1;
27212746
setSelectedRows(CompactSelection.fromSingleSelection([start, end]), undefined, false);
27222747
}
2723-
if (
2748+
// Only handle rect selection if not already processed by row selection:
2749+
else if (
27242750
args.buttons !== 0 &&
27252751
mouseState !== undefined &&
27262752
gridSelection.current !== undefined &&
@@ -3942,7 +3968,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
39423968
(col: number) => {
39433969
return typeof verticalBorder === "boolean"
39443970
? verticalBorder
3945-
: verticalBorder?.(col - rowMarkerOffset) ?? true;
3971+
: (verticalBorder?.(col - rowMarkerOffset) ?? true);
39463972
},
39473973
[rowMarkerOffset, verticalBorder]
39483974
);

packages/core/src/docs/examples/input-blending.stories.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ interface InputBlendingGridProps {
3131
rangeMultiSelect: "none" | "cell" | "rect" | "multi-cell" | "multi-rect";
3232
columnMultiSelect: "none" | "single" | "multi";
3333
rowMultiSelect: "none" | "single" | "multi";
34+
rowSelectionMode: "auto" | "multi";
35+
columnSelectionMode: "auto" | "multi";
3436
}
3537

3638
export const InputBlending: React.FC<InputBlendingGridProps> = p => {
@@ -60,6 +62,8 @@ export const InputBlending: React.FC<InputBlendingGridProps> = p => {
6062
rangeSelectionBlending={p.rangeBlending}
6163
columnSelectionBlending={p.columnBlending}
6264
rowSelectionBlending={p.rowBlending}
65+
rowSelectionMode={p.rowSelectionMode}
66+
columnSelectionMode={p.columnSelectionMode}
6367
getCellContent={getCellContent}
6468
columns={cols}
6569
rows={10_000}
@@ -73,6 +77,8 @@ export const InputBlending: React.FC<InputBlendingGridProps> = p => {
7377
rangeMultiSelect: "rect",
7478
columnMultiSelect: "multi",
7579
rowMultiSelect: "multi",
80+
rowSelectionMode: "auto",
81+
columnSelectionMode: "auto",
7682
};
7783
(InputBlending as any).argTypes = {
7884
rangeBlending: {
@@ -99,4 +105,12 @@ export const InputBlending: React.FC<InputBlendingGridProps> = p => {
99105
control: { type: "select" },
100106
options: ["none", "single", "multi"],
101107
},
108+
rowSelectionMode: {
109+
control: { type: "select" },
110+
options: ["auto", "multi"],
111+
},
112+
columnSelectionMode: {
113+
control: { type: "select" },
114+
options: ["auto", "multi"],
115+
},
102116
};

packages/core/src/internal/data-grid/use-selection-behavior.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function useSelectionBehavior(
5454
}
5555

5656
const rangeMixable =
57-
(rangeBehavior === "mixed" || rangeBehavior === "additive") && (append || trigger === "drag");
57+
(rangeBehavior === "mixed" && (append || trigger === "drag")) || rangeBehavior === "additive";
5858
const allowColumnCoSelect = (columnBehavior === "mixed" || columnBehavior === "additive") && rangeMixable;
5959
const allowRowCoSelect = (rowBehavior === "mixed" || rowBehavior === "additive") && rangeMixable;
6060
let newVal: GridSelection = {

0 commit comments

Comments
 (0)