From 0781900109439be8e0bca55f02665d2005df2136 Mon Sep 17 00:00:00 2001 From: Don Date: Wed, 7 Feb 2024 14:31:11 -0500 Subject: [PATCH] fix: show copy cursor in grid on key down and not just mouse move (#1735) - Adds copy cell key handler to set cursor on key down. Grid was only showing copy cursor on mouse move, not just key down. Unfortunately we still require focus to get the key down, and I don't think it's worth hacking around that. - Moved cursor styles to linker and irisgrid css respectively, so that it is shared to DHE. AppMainContainer.css isn't used in DHE. - Moved cursors and logos in to asset folder (folders named "assets" are bundled by Vite - Tweaked copy icon svg (aliasing is improved) - default cursor to "copy" in iris grid so that it shows in embed widget and DHE BREAKING CHANGE: linker and iris grid custom cursor styling and assets are now provided by components directly. DHE css and svg files containing linker cursors should be removed/de-duplicated. --- .../src/assets/svg/cursor-copy.svg | 15 ------- .../src/main/AppMainContainer.scss | 28 ------------ .../logos/community-wordmark-dark.svg | 0 .../logos/community-wordmark-light.svg | 0 packages/components/package.json | 2 +- packages/components/src/theme/Logo.css | 4 +- .../assets/svg/cursor-unlinker.svg | 0 packages/dashboard-core-plugins/package.json | 3 +- .../src/linker/LinkerLink.scss | 6 +++ .../src/panels/IrisGridPanel.scss | 8 ---- packages/grid/src/Grid.tsx | 5 +++ packages/grid/src/KeyHandler.ts | 3 ++ packages/iris-grid/assets/svg/cursor-copy.svg | 14 ++++++ .../assets/svg/cursor-linker-not-allowed.svg | 0 .../assets/svg/cursor-linker.svg | 0 packages/iris-grid/package.json | 3 +- packages/iris-grid/src/IrisGrid.scss | 18 ++++++++ packages/iris-grid/src/IrisGrid.tsx | 4 +- .../src/key-handlers/CopyCellKeyHandler.ts | 45 +++++++++++++++++++ packages/iris-grid/src/key-handlers/index.ts | 1 + 20 files changed, 102 insertions(+), 57 deletions(-) delete mode 100644 packages/code-studio/src/assets/svg/cursor-copy.svg rename packages/components/{ => assets}/logos/community-wordmark-dark.svg (100%) rename packages/components/{ => assets}/logos/community-wordmark-light.svg (100%) rename packages/{code-studio/src => dashboard-core-plugins}/assets/svg/cursor-unlinker.svg (100%) create mode 100644 packages/iris-grid/assets/svg/cursor-copy.svg rename packages/{code-studio/src => iris-grid}/assets/svg/cursor-linker-not-allowed.svg (100%) rename packages/{code-studio/src => iris-grid}/assets/svg/cursor-linker.svg (100%) create mode 100644 packages/iris-grid/src/key-handlers/CopyCellKeyHandler.ts diff --git a/packages/code-studio/src/assets/svg/cursor-copy.svg b/packages/code-studio/src/assets/svg/cursor-copy.svg deleted file mode 100644 index 28421e86b4..0000000000 --- a/packages/code-studio/src/assets/svg/cursor-copy.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/packages/code-studio/src/main/AppMainContainer.scss b/packages/code-studio/src/main/AppMainContainer.scss index de2ea3b468..86cce5cf87 100644 --- a/packages/code-studio/src/main/AppMainContainer.scss +++ b/packages/code-studio/src/main/AppMainContainer.scss @@ -9,34 +9,6 @@ $tab-link-disabled-color: $gray-600; $nav-space: 4px; // give a gap around some buttons for focus area that are in nav bar -.grid-cursor-copy { - cursor: - url('../assets/svg/cursor-copy.svg') 8 8, - copy; -} - -.grid-cursor-linker { - cursor: - url('../assets/svg/cursor-linker.svg') 8 8, - crosshair; -} - -.grid-cursor-linker-not-allowed { - cursor: - url('../assets/svg/cursor-linker-not-allowed.svg') 8 8, - not-allowed; -} - -.linker-overlay path.link-select { - cursor: pointer; -} - -.linker-overlay.danger-delete path.link-select { - cursor: - url('../assets/svg/cursor-unlinker.svg') 8 8, - pointer; -} - .app-main-top-nav-menus { display: flex; width: 100%; diff --git a/packages/components/logos/community-wordmark-dark.svg b/packages/components/assets/logos/community-wordmark-dark.svg similarity index 100% rename from packages/components/logos/community-wordmark-dark.svg rename to packages/components/assets/logos/community-wordmark-dark.svg diff --git a/packages/components/logos/community-wordmark-light.svg b/packages/components/assets/logos/community-wordmark-light.svg similarity index 100% rename from packages/components/logos/community-wordmark-light.svg rename to packages/components/assets/logos/community-wordmark-light.svg diff --git a/packages/components/package.json b/packages/components/package.json index 7bfdf0fc12..5992fb7817 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -58,7 +58,7 @@ "dist", "scss", "css", - "logos" + "assets" ], "sideEffects": [ "*.css" diff --git a/packages/components/src/theme/Logo.css b/packages/components/src/theme/Logo.css index 64f6c791f4..6396d2d9e4 100644 --- a/packages/components/src/theme/Logo.css +++ b/packages/components/src/theme/Logo.css @@ -1,6 +1,6 @@ :root { - --dh-logo-dark-img: url('../../logos/community-wordmark-dark.svg'); - --dh-logo-light-img: url('../../logos/community-wordmark-light.svg'); + --dh-logo-dark-img: url('../../assets/logos/community-wordmark-dark.svg'); + --dh-logo-light-img: url('../../assets/logos/community-wordmark-light.svg'); } .dh-logo { diff --git a/packages/code-studio/src/assets/svg/cursor-unlinker.svg b/packages/dashboard-core-plugins/assets/svg/cursor-unlinker.svg similarity index 100% rename from packages/code-studio/src/assets/svg/cursor-unlinker.svg rename to packages/dashboard-core-plugins/assets/svg/cursor-unlinker.svg diff --git a/packages/dashboard-core-plugins/package.json b/packages/dashboard-core-plugins/package.json index a12b64bbdb..e4c98e6081 100644 --- a/packages/dashboard-core-plugins/package.json +++ b/packages/dashboard-core-plugins/package.json @@ -68,7 +68,8 @@ "@deephaven/mocks": "file:../mocks" }, "files": [ - "dist" + "dist", + "assets" ], "sideEffects": [ "*.css" diff --git a/packages/dashboard-core-plugins/src/linker/LinkerLink.scss b/packages/dashboard-core-plugins/src/linker/LinkerLink.scss index cdb0c85e87..afba9f3ad5 100644 --- a/packages/dashboard-core-plugins/src/linker/LinkerLink.scss +++ b/packages/dashboard-core-plugins/src/linker/LinkerLink.scss @@ -128,6 +128,12 @@ $dash-size: 10; } &.danger-delete { + path.link-select { + cursor: + url('../assets/svg/cursor-unlinker.svg') 8 8, + pointer; + } + path.link-select:hover ~ path.link-background { stroke: $dash-delete-color-2; } diff --git a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.scss b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.scss index e5235adc1d..f798b35bfd 100644 --- a/packages/dashboard-core-plugins/src/panels/IrisGridPanel.scss +++ b/packages/dashboard-core-plugins/src/panels/IrisGridPanel.scss @@ -27,11 +27,3 @@ $panel-message-overlay-top: 30px; color: $danger; } } - -.grid-cursor-linker { - cursor: crosshair; -} - -.grid-cursor-copy { - cursor: copy; -} diff --git a/packages/grid/src/Grid.tsx b/packages/grid/src/Grid.tsx index 84157d5264..b29b4332f1 100644 --- a/packages/grid/src/Grid.tsx +++ b/packages/grid/src/Grid.tsx @@ -1736,18 +1736,23 @@ class Grid extends PureComponent { event: GridKeyboardEvent ): void { const keyHandlers = this.getKeyHandlers(); + let cursor = null; for (let i = 0; i < keyHandlers.length; i += 1) { const keyHandler = keyHandlers[i]; const result = keyHandler[functionName] != null && keyHandler[functionName](event, this); if (result !== false) { + if (keyHandler.cursor != null) { + ({ cursor } = keyHandler); + } const options = result as EventHandlerResultOptions; if (options?.stopPropagation ?? true) event.stopPropagation(); if (options?.preventDefault ?? true) event.preventDefault(); break; } } + this.setState({ cursor }); } handleKeyDown(event: GridKeyboardEvent): void { diff --git a/packages/grid/src/KeyHandler.ts b/packages/grid/src/KeyHandler.ts index f6216f5adf..776b533383 100644 --- a/packages/grid/src/KeyHandler.ts +++ b/packages/grid/src/KeyHandler.ts @@ -26,6 +26,9 @@ export class KeyHandler { this.order = order; } + // Cursor to use if this returns any truthy value including { stopPropagation: false, preventDefault: false } + cursor: string | null = null; + /** * Handle a keydown event on the grid. * @param event The keyboard event diff --git a/packages/iris-grid/assets/svg/cursor-copy.svg b/packages/iris-grid/assets/svg/cursor-copy.svg new file mode 100644 index 0000000000..893bd23cc1 --- /dev/null +++ b/packages/iris-grid/assets/svg/cursor-copy.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/packages/code-studio/src/assets/svg/cursor-linker-not-allowed.svg b/packages/iris-grid/assets/svg/cursor-linker-not-allowed.svg similarity index 100% rename from packages/code-studio/src/assets/svg/cursor-linker-not-allowed.svg rename to packages/iris-grid/assets/svg/cursor-linker-not-allowed.svg diff --git a/packages/code-studio/src/assets/svg/cursor-linker.svg b/packages/iris-grid/assets/svg/cursor-linker.svg similarity index 100% rename from packages/code-studio/src/assets/svg/cursor-linker.svg rename to packages/iris-grid/assets/svg/cursor-linker.svg diff --git a/packages/iris-grid/package.json b/packages/iris-grid/package.json index ceef79a330..531d8aad60 100644 --- a/packages/iris-grid/package.json +++ b/packages/iris-grid/package.json @@ -69,7 +69,8 @@ "@deephaven/mocks": "file:../mocks" }, "files": [ - "dist" + "dist", + "assets" ], "sideEffects": [ "*.css" diff --git a/packages/iris-grid/src/IrisGrid.scss b/packages/iris-grid/src/IrisGrid.scss index e978240b5f..e8eb16f2d5 100644 --- a/packages/iris-grid/src/IrisGrid.scss +++ b/packages/iris-grid/src/IrisGrid.scss @@ -40,6 +40,24 @@ $cell-invalid-box-shadow: position: relative; } + .grid-cursor-copy { + cursor: + url('../assets/svg/cursor-copy.svg') 8 8, + copy; + } + + .grid-cursor-linker { + cursor: + url('../assets/svg/cursor-linker.svg') 8 8, + crosshair; + } + + .grid-cursor-linker-not-allowed { + cursor: + url('../assets/svg/cursor-linker-not-allowed.svg') 8 8, + not-allowed; + } + .table-sidebar { height: 100%; flex: 0 0 $table-sidebar-max-width; diff --git a/packages/iris-grid/src/IrisGrid.tsx b/packages/iris-grid/src/IrisGrid.tsx index 7ce3dc9cee..b2daddbc57 100644 --- a/packages/iris-grid/src/IrisGrid.tsx +++ b/packages/iris-grid/src/IrisGrid.tsx @@ -112,6 +112,7 @@ import PendingDataBottomBar from './PendingDataBottomBar'; import IrisGridCopyHandler, { CopyOperation } from './IrisGridCopyHandler'; import FilterInputField from './FilterInputField'; import { + CopyCellKeyHandler, ClearFilterKeyHandler, CopyKeyHandler, ReverseKeyHandler, @@ -497,7 +498,7 @@ export class IrisGrid extends Component { columnSelectionValidator: null, columnAllowedCursor: null, columnNotAllowedCursor: null, - copyCursor: null, + copyCursor: 'copy', name: 'table', onlyFetchVisibleColumns: true, showSearchBar: false, @@ -709,6 +710,7 @@ export class IrisGrid extends Component { const { dh } = model; const keyHandlers: KeyHandler[] = [ + new CopyCellKeyHandler(this), new ReverseKeyHandler(this), new ClearFilterKeyHandler(this), ]; diff --git a/packages/iris-grid/src/key-handlers/CopyCellKeyHandler.ts b/packages/iris-grid/src/key-handlers/CopyCellKeyHandler.ts new file mode 100644 index 0000000000..8bcea06008 --- /dev/null +++ b/packages/iris-grid/src/key-handlers/CopyCellKeyHandler.ts @@ -0,0 +1,45 @@ +import { KeyboardEvent } from 'react'; +import { KeyHandler } from '@deephaven/grid'; +import { ContextActionUtils } from '@deephaven/components'; +import type { Grid } from '@deephaven/grid'; +import { IrisGrid } from '../IrisGrid'; + +class CopyCellKeyHandler extends KeyHandler { + private irisGrid: IrisGrid; + + constructor(irisGrid: IrisGrid) { + super(); + + this.irisGrid = irisGrid; + this.cursor = null; + } + + onDown(event: KeyboardEvent, grid: Grid): boolean { + if ( + event.altKey && + !ContextActionUtils.isModifierKeyDown(event) && + !event.shiftKey + ) { + const { mouseX, mouseY } = grid.state; + if (mouseX == null || mouseY == null) { + return false; + } + const gridPoint = grid.getGridPointFromXY(mouseX, mouseY); + if (gridPoint.column != null && gridPoint.row != null) { + this.cursor = this.irisGrid.props.copyCursor; + return true; + } + } + return false; + } + + onUp(event: KeyboardEvent, grid: Grid): boolean { + if (this.cursor === this.irisGrid.props.copyCursor) { + this.cursor = null; + return true; + } + return false; + } +} + +export default CopyCellKeyHandler; diff --git a/packages/iris-grid/src/key-handlers/index.ts b/packages/iris-grid/src/key-handlers/index.ts index 2ddf3e7624..4570b91e76 100644 --- a/packages/iris-grid/src/key-handlers/index.ts +++ b/packages/iris-grid/src/key-handlers/index.ts @@ -1,3 +1,4 @@ +export { default as CopyCellKeyHandler } from './CopyCellKeyHandler'; export { default as CopyKeyHandler } from './CopyKeyHandler'; export { default as ReverseKeyHandler } from './ReverseKeyHandler'; export { default as ClearFilterKeyHandler } from './ClearFilterKeyHandler';