Skip to content

Commit 4a8a81a

Browse files
authored
feat: Add alt+click shortcut to copy cell and column headers (#1694)
Adds a alt/opt+click shortcut to copy the value of a cell or column header. Resolves #1585.
1 parent 73e0b65 commit 4a8a81a

16 files changed

Lines changed: 321 additions & 71 deletions

File tree

Lines changed: 15 additions & 0 deletions
Loading

packages/code-studio/src/main/AppMainContainer.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,12 @@ $nav-space: 4px; // give a gap around some buttons for focus area that are in na
248248
}
249249
}
250250

251+
.grid-cursor-copy {
252+
cursor:
253+
url('../assets/svg/cursor-copy.svg') 8 8,
254+
copy;
255+
}
256+
251257
.grid-cursor-linker {
252258
cursor:
253259
url('../assets/svg/cursor-linker.svg') 8 8,

packages/components/src/context-actions/ContextActionUtils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ export interface ContextAction {
1717
icon?: IconDefinition | React.ReactElement;
1818
iconColor?: string;
1919
shortcut?: Shortcut;
20+
21+
/* Display text for the shortcut if the shortcut is not wired up through the Shortcut class */
22+
shortcutText?: string;
2023
isGlobal?: boolean;
2124
group?: number;
2225
order?: number;

packages/components/src/context-actions/ContextMenuItem.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ const ContextMenuItem = React.forwardRef<HTMLDivElement, ContextMenuItemProps>(
7171
'data-testid': dataTestId,
7272
} = props;
7373

74-
const displayShortcut = menuItem.shortcut?.getDisplayText();
74+
const displayShortcut =
75+
menuItem.shortcutText ?? menuItem.shortcut?.getDisplayText();
7576
let icon: IconDefinition | React.ReactElement | null = null;
7677
if (menuItem.icon) {
7778
const menuItemIcon = menuItem.icon;

packages/dashboard-core-plugins/src/panels/IrisGridPanel.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,7 @@ $panel-message-overlay-top: 30px;
3131
.grid-cursor-linker {
3232
cursor: crosshair;
3333
}
34+
35+
.grid-cursor-copy {
36+
cursor: copy;
37+
}

packages/dashboard-core-plugins/src/panels/IrisGridPanel.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,7 @@ export class IrisGridPanel extends PureComponent<
13081308
)}
13091309
columnAllowedCursor="linker"
13101310
columnNotAllowedCursor="linker-not-allowed"
1311+
copyCursor="copy"
13111312
customColumns={customColumns}
13121313
customColumnFormatMap={customColumnFormatMap}
13131314
columnSelectionValidator={this.isColumnSelectionValid}

packages/grid/src/Grid.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ function mouseDoubleClick(
220220

221221
function keyDown(key: string, component: Grid, extraArgs?: KeyboardEventInit) {
222222
const args = { key, ...extraArgs };
223-
component.handleKeyDown(
223+
component.notifyKeyboardHandlers(
224+
'onDown',
224225
new KeyboardEvent('keydown', args) as unknown as React.KeyboardEvent
225226
);
226227
}

packages/grid/src/Grid.tsx

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ import {
3434
GridTokenMouseHandler,
3535
} from './mouse-handlers';
3636
import './Grid.scss';
37-
import KeyHandler, { GridKeyboardEvent } from './KeyHandler';
37+
import KeyHandler, {
38+
GridKeyHandlerFunctionName,
39+
GridKeyboardEvent,
40+
} from './KeyHandler';
3841
import {
3942
EditKeyHandler,
4043
PasteKeyHandler,
@@ -342,7 +345,9 @@ class Grid extends PureComponent<GridProps, GridState> {
342345
this.handleEditCellChange = this.handleEditCellChange.bind(this);
343346
this.handleEditCellCommit = this.handleEditCellCommit.bind(this);
344347
this.handleDoubleClick = this.handleDoubleClick.bind(this);
348+
this.notifyKeyboardHandlers = this.notifyKeyboardHandlers.bind(this);
345349
this.handleKeyDown = this.handleKeyDown.bind(this);
350+
this.handleKeyUp = this.handleKeyUp.bind(this);
346351
this.handleMouseDown = this.handleMouseDown.bind(this);
347352
this.handleMouseDrag = this.handleMouseDrag.bind(this);
348353
this.handleMouseMove = this.handleMouseMove.bind(this);
@@ -1715,14 +1720,20 @@ class Grid extends PureComponent<GridProps, GridState> {
17151720
}
17161721

17171722
/**
1718-
* Handle a key down event from the keyboard. Pass the event to the registered keyboard handlers until one handles it.
1719-
* @param event Keyboard event
1723+
* Notify all of the keyboard handlers for this grid of a keyboard event.
1724+
* @param functionName The name of the function in the keyboard handler to call
1725+
* @param event The keyboard event to notify
17201726
*/
1721-
handleKeyDown(event: GridKeyboardEvent): void {
1727+
notifyKeyboardHandlers(
1728+
functionName: GridKeyHandlerFunctionName,
1729+
event: GridKeyboardEvent
1730+
): void {
17221731
const keyHandlers = this.getKeyHandlers();
17231732
for (let i = 0; i < keyHandlers.length; i += 1) {
17241733
const keyHandler = keyHandlers[i];
1725-
const result = keyHandler.onDown(event, this);
1734+
const result =
1735+
keyHandler[functionName] != null &&
1736+
keyHandler[functionName](event, this);
17261737
if (result !== false) {
17271738
const options = result as EventHandlerResultOptions;
17281739
if (options?.stopPropagation ?? true) event.stopPropagation();
@@ -1732,6 +1743,14 @@ class Grid extends PureComponent<GridProps, GridState> {
17321743
}
17331744
}
17341745

1746+
handleKeyDown(event: GridKeyboardEvent): void {
1747+
this.notifyKeyboardHandlers('onDown', event);
1748+
}
1749+
1750+
handleKeyUp(event: GridKeyboardEvent): void {
1751+
this.notifyKeyboardHandlers('onUp', event);
1752+
}
1753+
17351754
/**
17361755
* Notify all of the mouse handlers for this grid of a mouse event.
17371756
* @param functionName The name of the function in the mouse handler to call
@@ -2229,6 +2248,7 @@ class Grid extends PureComponent<GridProps, GridState> {
22292248
onContextMenu={this.handleContextMenu}
22302249
onDoubleClick={this.handleDoubleClick}
22312250
onKeyDown={this.handleKeyDown}
2251+
onKeyUp={this.handleKeyUp}
22322252
onMouseDown={this.handleMouseDown}
22332253
onMouseMove={this.handleMouseMove}
22342254
onMouseLeave={this.handleMouseLeave}

packages/grid/src/KeyHandler.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import type Grid from './Grid';
1515
*/
1616
export type GridKeyboardEvent = KeyboardEvent | React.KeyboardEvent;
1717

18+
export type GridKeyHandlerFunctionName = 'onDown' | 'onUp';
19+
1820
export class KeyHandler {
1921
order: number;
2022

@@ -33,6 +35,16 @@ export class KeyHandler {
3335
onDown(event: GridKeyboardEvent, grid: Grid): EventHandlerResult {
3436
return false;
3537
}
38+
39+
/**
40+
* Handle a keyup event on the grid.
41+
* @param event The keyboard event
42+
* @param grid The grid component the key press is on
43+
* @returns Response indicating if the key was consumed
44+
*/
45+
onUp(event: GridKeyboardEvent, grid: Grid): EventHandlerResult {
46+
return false;
47+
}
3648
}
3749

3850
export default KeyHandler;

packages/iris-grid/src/IrisGrid.test.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ function makeComponent(
8080

8181
function keyDown(key, component, extraArgs?) {
8282
const args = { key, ...extraArgs };
83-
component.grid.handleKeyDown(new KeyboardEvent('keydown', args));
83+
component.grid.notifyKeyboardHandlers(
84+
'onDown',
85+
new KeyboardEvent('keydown', args)
86+
);
8487
}
8588

8689
it('renders without crashing', () => {

0 commit comments

Comments
 (0)