diff --git a/packages/grid/src/mouse-handlers/GridTokenMouseHandler.ts b/packages/grid/src/mouse-handlers/GridTokenMouseHandler.ts index 08612bcf6d..468ad0a491 100644 --- a/packages/grid/src/mouse-handlers/GridTokenMouseHandler.ts +++ b/packages/grid/src/mouse-handlers/GridTokenMouseHandler.ts @@ -13,6 +13,7 @@ import GridUtils, { } from '../GridUtils'; import { isTokenBoxCellRenderer } from '../TokenBoxCellRenderer'; +// Handler also helps with other tooltips class GridTokenMouseHandler extends GridMouseHandler { timeoutId?: ReturnType; @@ -21,7 +22,7 @@ class GridTokenMouseHandler extends GridMouseHandler { private isDown = false; // Stores the current hovered token box if it exists with translated coordinates - private currentLinkBox?: TokenBox; + protected currentLinkBox?: TokenBox; private static HOLD_LENGTH = 1000; @@ -86,7 +87,7 @@ class GridTokenMouseHandler extends GridMouseHandler { * @param grid The grid * @returns False */ - private setCursor(gridPoint: GridPoint, grid: Grid): EventHandlerResult { + protected setCursor(gridPoint: GridPoint, grid: Grid): EventHandlerResult { if (this.isHoveringLink(gridPoint, grid)) { this.cursor = 'pointer'; } else { diff --git a/packages/iris-grid/src/IrisGrid.tsx b/packages/iris-grid/src/IrisGrid.tsx index f8b29c48bf..c63f98ffa2 100644 --- a/packages/iris-grid/src/IrisGrid.tsx +++ b/packages/iris-grid/src/IrisGrid.tsx @@ -446,8 +446,8 @@ export interface IrisGridState { overflowButtonTooltipProps: CSSProperties | null; expandCellTooltipProps: CSSProperties | null; expandTooltipDisplayValue: string; - linkHoverTooltipProps: CSSProperties | null; - linkHoverDisplayValue: string; + hoverTooltipProps: CSSProperties | null; + hoverDisplayValue: ReactNode; gotoRow: string; gotoRowError: string; @@ -874,8 +874,8 @@ class IrisGrid extends Component { overflowButtonTooltipProps: null, expandCellTooltipProps: null, expandTooltipDisplayValue: 'expand', - linkHoverTooltipProps: null, - linkHoverDisplayValue: '', + hoverTooltipProps: null, + hoverDisplayValue: '', isGotoShown: false, gotoRow: '', gotoRowError: '', @@ -3976,37 +3976,31 @@ class IrisGrid extends Component { } ); - getLinkHoverTooltip = memoize( - (linkHoverTooltipProps: CSSProperties): ReactNode => { - if (linkHoverTooltipProps == null) { - return null; - } + getHoverTooltip = memoize((hoverTooltipProps: CSSProperties): ReactNode => { + if (hoverTooltipProps == null) { + return null; + } - const { linkHoverDisplayValue } = this.state; + const { hoverDisplayValue } = this.state; - const wrapperStyle: CSSProperties = { - position: 'absolute', - ...linkHoverTooltipProps, - pointerEvents: 'none', - }; + const wrapperStyle: CSSProperties = { + position: 'absolute', + ...hoverTooltipProps, + pointerEvents: 'none', + }; - const popperOptions: PopperOptions = { - placement: 'bottom', - }; + const popperOptions: PopperOptions = { + placement: 'bottom', + }; - return ( -
- -
- {linkHoverDisplayValue} - Click once to follow. -
- Click and hold to select this cell. -
-
-
- ); - } - ); + return ( +
+ +
{hoverDisplayValue}
+
+
+ ); + }); handleGotoRowSelectedRowNumberSubmit(): void { const { gotoRow: rowNumber } = this.state; @@ -4282,7 +4276,7 @@ class IrisGrid extends Component { overflowText, overflowButtonTooltipProps, expandCellTooltipProps, - linkHoverTooltipProps, + hoverTooltipProps, isGotoShown, gotoRow, gotoRowError, @@ -4902,8 +4896,7 @@ class IrisGrid extends Component { this.getOverflowButtonTooltip(overflowButtonTooltipProps)} {expandCellTooltipProps && this.getExpandCellTooltip(expandCellTooltipProps)} - {linkHoverTooltipProps && - this.getLinkHoverTooltip(linkHoverTooltipProps)} + {hoverTooltipProps && this.getHoverTooltip(hoverTooltipProps)} = left && x <= right && y >= top && y <= bottom) { - return true; - } - } - - const renderState = grid.updateRenderState(); - const tokensInCell = cellRenderer.getTokenBoxesForVisibleCell( - column, - row, - renderState - ); - - // Loop through each link and check if cursor is in bounds - for (let i = 0; i < tokensInCell.length; i += 1) { - if (isLinkToken(tokensInCell[i].token)) { - const translatedTokenBox = GridUtils.translateTokenBox( - tokensInCell[i], - metrics - ); - const { x1: left, x2: right, y1: top, y2: bottom } = translatedTokenBox; - if (x >= left && x <= right && y >= top && y <= bottom) { - this.currentLinkBox = translatedTokenBox; - return true; - } - } - } - - // If this point is reached, that means the cursor was not hovering any of the links or there are no links - this.currentLinkBox = undefined; - return false; - } - - private setCursor(gridPoint: GridPoint, grid: Grid): EventHandlerResult { - if (this.isHoveringLink(gridPoint, grid)) { - this.cursor = 'pointer'; - return { stopPropagation: false, preventDefault: false }; - } - this.cursor = null; - return false; - } - - onMove(gridPoint: GridPoint, grid: Grid): EventHandlerResult { - const isUserHoveringLink = this.isHoveringLink(gridPoint, grid); - if ( - isUserHoveringLink && - this.currentLinkBox != null && - isLinkToken(this.currentLinkBox.token) - ) { - const { linkHoverTooltipProps } = this.irisGrid.state; - if (this.currentLinkBox == null) { - return false; - } - const { x1: left, y1: top, x2: right, y2: bottom } = this.currentLinkBox; - const { href } = this.currentLinkBox.token; - const width = right - left; - const height = bottom - top; - const newProps = { left, top: top + 1, width, height }; - if (!deepEqual(linkHoverTooltipProps, newProps)) { - this.irisGrid.setState({ - linkHoverTooltipProps: newProps, - linkHoverDisplayValue: href, - }); - } - } else { - this.destroyTooltip(); - } - - return this.setCursor(gridPoint, grid); - } - - onDown(): EventHandlerResult { - this.destroyTooltip(); - return false; - } - - onContextMenu(): EventHandlerResult { - this.destroyTooltip(); - return false; - } - - onWheel(): EventHandlerResult { - this.destroyTooltip(); - return false; - } - - onLeave(): EventHandlerResult { - this.destroyTooltip(); - return false; - } -} - -export default IrisGridTokenMouseHandler; diff --git a/packages/iris-grid/src/mousehandlers/IrisGridTokenMouseHandler.tsx b/packages/iris-grid/src/mousehandlers/IrisGridTokenMouseHandler.tsx new file mode 100644 index 0000000000..b7b2ac76a3 --- /dev/null +++ b/packages/iris-grid/src/mousehandlers/IrisGridTokenMouseHandler.tsx @@ -0,0 +1,131 @@ +import { + type EventHandlerResult, + type Grid, + type GridPoint, + isLinkToken, + GridTokenMouseHandler, + type GridRangeIndex, +} from '@deephaven/grid'; +import deepEqual from 'fast-deep-equal'; +import type IrisGrid from '../IrisGrid'; + +// Handler also helps with other tooltips +class IrisGridTokenMouseHandler extends GridTokenMouseHandler { + private irisGrid: IrisGrid; + + private lastColumn: GridRangeIndex; + + private lastRow: GridRangeIndex; + + constructor(irisGrid: IrisGrid) { + super(); + + this.irisGrid = irisGrid; + this.lastColumn = null; + this.lastRow = null; + } + + private destroyTooltip(): void { + this.irisGrid.setState({ hoverTooltipProps: null }); + } + + protected setCursor(gridPoint: GridPoint, grid: Grid): EventHandlerResult { + if (this.isHoveringLink(gridPoint, grid)) { + this.cursor = 'pointer'; + return { stopPropagation: false, preventDefault: false }; + } + this.cursor = null; + return false; + } + + onMove(gridPoint: GridPoint, grid: Grid): EventHandlerResult { + const { model } = this.irisGrid.props; + const isUserHoveringLink = this.isHoveringLink(gridPoint, grid); + const tooltip = model.tooltipForCell(gridPoint.column, gridPoint.row); + + if ( + isUserHoveringLink && + this.currentLinkBox != null && + isLinkToken(this.currentLinkBox.token) + ) { + const { hoverTooltipProps } = this.irisGrid.state; + const { x1: left, y1: top, x2: right, y2: bottom } = this.currentLinkBox; + const { href } = this.currentLinkBox.token; + const width = right - left; + const height = bottom - top; + const newProps = { left, top: top + 1, width, height }; + + if (!deepEqual(hoverTooltipProps, newProps)) { + this.irisGrid.setState({ + hoverTooltipProps: newProps, + hoverDisplayValue: ( + <> + {href} - Click once to follow. +
+ Click and hold to select this cell. + + ), + }); + } + } else if (tooltip !== null) { + const { hoverTooltipProps } = this.irisGrid.state; + const newProps = { + left: gridPoint.x, + top: gridPoint.y + 1, + width: 1, + height: 1, + }; + if (!deepEqual(hoverTooltipProps, newProps)) { + if (hoverTooltipProps == null) { + this.irisGrid.setState({ + hoverTooltipProps: newProps, + hoverDisplayValue: tooltip, + }); + } else if ( + this.lastColumn !== gridPoint.column || + this.lastRow !== gridPoint.row + ) { + this.irisGrid.setState( + { + hoverTooltipProps: null, + }, + () => + this.irisGrid.setState({ + hoverTooltipProps: newProps, + hoverDisplayValue: tooltip, + }) + ); + } + } + } else { + this.destroyTooltip(); + } + + this.lastColumn = gridPoint.column; + this.lastRow = gridPoint.row; + + return this.setCursor(gridPoint, grid); + } + + onDown(): EventHandlerResult { + this.destroyTooltip(); + return false; + } + + onContextMenu(): EventHandlerResult { + this.destroyTooltip(); + return false; + } + + onWheel(): EventHandlerResult { + this.destroyTooltip(); + return false; + } + + onLeave(): EventHandlerResult { + this.destroyTooltip(); + return false; + } +} + +export default IrisGridTokenMouseHandler;