Implement ctrl-shift click to select multiple documents (#1851)

* Initial implementation of shift and ctrl click to select

* Implement shift-ctrl selection

* Fix snapshot, update selectionHelper comment

* Fix missing type

* Properly disable cursor selection

* Update snapshots

* Do not enable (multiselect) if readonly

* Consider meta key for mac and ctrl for everything else
This commit is contained in:
Laurent Nguyen
2024-06-05 17:47:27 +02:00
committed by GitHub
parent 7c5fb1b697
commit 7002da0b51
7 changed files with 304 additions and 61 deletions

View File

@@ -22,6 +22,9 @@ import {
useTableFeatures,
useTableSelection,
} from "@fluentui/react-components";
import { NormalizedEventKey } from "Common/Constants";
import { selectionHelper } from "Explorer/Tabs/DocumentsTabV2/SelectionHelper";
import { isEnvironmentCtrlPressed, isEnvironmentShiftPressed } from "Utils/KeyboardUtils";
import React, { useCallback, useEffect, useMemo } from "react";
import { FixedSizeList as List, ListChildComponentProps } from "react-window";
@@ -123,17 +126,63 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
[columnHeaders],
);
const onIdClicked = useCallback((index: number) => onSelectedRowsChange(new Set([index])), [onSelectedRowsChange]);
const [selectionStartIndex, setSelectionStartIndex] = React.useState<number>(undefined);
const onTableCellClicked = useCallback(
(e: React.MouseEvent, index: number) => {
if (isSelectionDisabled) {
// Only allow click
onSelectedRowsChange(new Set<TableRowId>([index]));
setSelectionStartIndex(index);
return;
}
const result = selectionHelper(
selectedRows as Set<number>,
index,
isEnvironmentShiftPressed(e),
isEnvironmentCtrlPressed(e),
selectionStartIndex,
);
onSelectedRowsChange(result.selection);
if (result.selectionStartIndex !== undefined) {
setSelectionStartIndex(result.selectionStartIndex);
}
},
[isSelectionDisabled, selectedRows, selectionStartIndex, onSelectedRowsChange],
);
/**
* Callback for when:
* - a key has been pressed on the cell
* - a key is down and the cell is clicked by the mouse
*/
const onIdClicked = useCallback(
(e: React.KeyboardEvent<Element>, index: number) => {
if (e.key === NormalizedEventKey.Enter || e.key === NormalizedEventKey.Space) {
onSelectedRowsChange(new Set<TableRowId>([index]));
}
},
[onSelectedRowsChange],
);
const RenderRow = ({ index, style, data }: ReactWindowRenderFnProps) => {
const { item, selected, appearance, onClick, onKeyDown } = data[index];
return (
<TableRow aria-rowindex={index + 2} style={style} key={item.id} aria-selected={selected} appearance={appearance}>
<TableRow
aria-rowindex={index + 2}
style={{ ...style, cursor: "pointer", userSelect: "none" }}
key={item.id}
aria-selected={selected}
appearance={appearance}
>
{!isSelectionDisabled && (
<TableSelectionCell
checked={selected}
checkboxIndicator={{ "aria-label": "Select row" }}
onClick={onClick}
onClick={(e: React.MouseEvent) => {
setSelectionStartIndex(index);
onClick(e);
}}
onKeyDown={onKeyDown}
/>
)}
@@ -141,8 +190,9 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
<TableCell
key={column.columnId}
className="documentsTableCell"
onClick={(/* e */) => onSelectedRowsChange(new Set<TableRowId>([index]))}
onKeyDown={() => onIdClicked(index)}
// When clicking on a cell with shift/ctrl key, onKeyDown is called instead of onClick.
onClick={(e: React.MouseEvent<Element, MouseEvent>) => onTableCellClicked(e, index)}
onKeyPress={(e: React.KeyboardEvent<Element>) => onIdClicked(e, index)}
{...columnSizing.getTableCellProps(column.columnId)}
tabIndex={column.columnId === "id" ? 0 : -1}
>
@@ -166,7 +216,7 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
[
useTableColumnSizing_unstable({ columnSizingOptions, onColumnResize }),
useTableSelection({
selectionMode: "multiselect",
selectionMode: isSelectionDisabled ? "single" : "multiselect",
selectedItems: selectedRows,
// eslint-disable-next-line react/prop-types
onSelectionChange: (e, data) => onSelectedRowsChange(data.selectedItems),
@@ -207,9 +257,10 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
if (newActiveItemIndex !== activeItemIndex) {
onItemClicked(newActiveItemIndex);
setActiveItemIndex(newActiveItemIndex);
setSelectionStartIndex(newActiveItemIndex);
}
}
}, [selectedRows, items]);
}, [selectedRows, items, activeItemIndex, onItemClicked]);
// Cell keyboard navigation
const keyboardNavAttr = useArrowNavigationGroup({ axis: "grid" });