From 81ded2acca1b0264301fe5fa46366e004b9463c6 Mon Sep 17 00:00:00 2001 From: Don McKenzie Date: Tue, 23 Nov 2021 10:47:08 -0500 Subject: [PATCH] Make label centered --- .../src/styleguide/MockIrisGridTreeModel.js | 4 ++ packages/components/src/popper/Popper.tsx | 11 ++-- packages/components/src/popper/Tooltip.tsx | 13 ++++- packages/iris-grid/package.json | 1 + packages/iris-grid/src/IrisGrid.jsx | 50 +++++++++++++++++-- 5 files changed, 68 insertions(+), 11 deletions(-) diff --git a/packages/code-studio/src/styleguide/MockIrisGridTreeModel.js b/packages/code-studio/src/styleguide/MockIrisGridTreeModel.js index 4a0a7d925c..c36f4475df 100644 --- a/packages/code-studio/src/styleguide/MockIrisGridTreeModel.js +++ b/packages/code-studio/src/styleguide/MockIrisGridTreeModel.js @@ -53,6 +53,10 @@ class MockIrisGridTreeModel extends IrisGridModel { set pendingDataMap(value) {} + getColumnIndexByName(name) { + return 3; + } + textForCell(column, row) { return ( this.editedData[column]?.[row] ?? this.model.textForCell(column, row) diff --git a/packages/components/src/popper/Popper.tsx b/packages/components/src/popper/Popper.tsx index 96dd99139f..95f524bcc5 100644 --- a/packages/components/src/popper/Popper.tsx +++ b/packages/components/src/popper/Popper.tsx @@ -19,7 +19,7 @@ import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import classNames from 'classnames'; import { CSSTransition } from 'react-transition-group'; -import PopperJs, { PopperOptions } from 'popper.js'; +import PopperJs, { PopperOptions, ReferenceObject } from 'popper.js'; import PropTypes from 'prop-types'; import ThemeExport from '../ThemeExport'; import './Popper.scss'; @@ -33,6 +33,7 @@ interface PopperProps { isShown: boolean; closeOnBlur: boolean; interactive: boolean; + referenceObject: ReferenceObject; } interface PopperState { @@ -51,6 +52,7 @@ class Popper extends Component { isShown: PropTypes.bool, closeOnBlur: PropTypes.bool, interactive: PropTypes.bool, + referenceObject: PropTypes.shape({}), }; static defaultProps = { @@ -66,6 +68,7 @@ class Popper extends Component { isShown: false, interactive: false, closeOnBlur: false, + referenceObject: null, }; constructor(props: PopperProps) { @@ -136,7 +139,7 @@ class Popper extends Component { initPopper(): void { let { popper } = this.state; - const { closeOnBlur } = this.props; + const { closeOnBlur, referenceObject } = this.props; if (popper) { return; @@ -158,7 +161,7 @@ class Popper extends Component { parent = this.container.current; } - popper = new PopperJs(parent, this.element, options); + popper = new PopperJs(referenceObject || parent, this.element, options); popper.scheduleUpdate(); // delayed due to scheduleUpdate @@ -293,4 +296,4 @@ class Popper extends Component { } export default Popper; -export type { PopperOptions }; +export type { PopperOptions, ReferenceObject }; diff --git a/packages/components/src/popper/Tooltip.tsx b/packages/components/src/popper/Tooltip.tsx index 131a77f14a..79f3e179eb 100644 --- a/packages/components/src/popper/Tooltip.tsx +++ b/packages/components/src/popper/Tooltip.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import classNames from 'classnames'; import Log from '@deephaven/log'; -import Popper, { PopperOptions } from './Popper'; +import Popper, { PopperOptions, ReferenceObject } from './Popper'; const log = Log.module('Tooltip'); @@ -12,6 +12,7 @@ interface TooltipProps { reshowTimeout: number; children: React.ReactNode; popperClassName: string; + referenceObject: ReferenceObject; } interface TooltipState { @@ -47,6 +48,7 @@ class Tooltip extends Component { popperClassName: '', reshowTimeout: Tooltip.defaultReshowTimeout, timeout: Tooltip.defaultTimeout, + referenceObject: null, }; static handleHidden(): void { @@ -280,7 +282,13 @@ class Tooltip extends Component { } render(): JSX.Element { - const { interactive, children, options, popperClassName } = this.props; + const { + interactive, + children, + options, + referenceObject, + popperClassName, + } = this.props; const { isShown } = this.state; return (
@@ -290,6 +298,7 @@ class Tooltip extends Component { ref={this.popper} onExited={this.handleExited} interactive={interactive} + referenceObject={referenceObject} >
{isShown && children}
diff --git a/packages/iris-grid/package.json b/packages/iris-grid/package.json index 06543028df..3b794ef7d5 100644 --- a/packages/iris-grid/package.json +++ b/packages/iris-grid/package.json @@ -42,6 +42,7 @@ "@fortawesome/react-fontawesome": "^0.1.12", "classnames": "^2.3.1", "deep-equal": "^2.0.4", + "lodash.clamp": "^4.0.3", "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1", "memoize-one": "^5.1.1", diff --git a/packages/iris-grid/src/IrisGrid.jsx b/packages/iris-grid/src/IrisGrid.jsx index ba558fdce2..c0bf4dfa1b 100644 --- a/packages/iris-grid/src/IrisGrid.jsx +++ b/packages/iris-grid/src/IrisGrid.jsx @@ -36,6 +36,7 @@ import dh, { PropTypes as APIPropTypes } from '@deephaven/jsapi-shim'; import { Pending, PromiseUtils, ValidationError } from '@deephaven/utils'; import throttle from 'lodash.throttle'; import debounce from 'lodash.debounce'; +import clamp from 'lodash.clamp'; import PendingDataBottomBar from './PendingDataBottomBar'; import IrisGridCopyHandler from './IrisGridCopyHandler'; import FilterInputField from './FilterInputField'; @@ -2421,7 +2422,7 @@ export class IrisGrid extends Component { } let columnTooltip = null; - if (shownColumnTooltip != null && metrics) { + if (shownColumnTooltip != null && metrics && this.gridWrapper) { const { columnHeaderHeight, visibleColumnXs, @@ -2433,10 +2434,10 @@ export class IrisGrid extends Component { /** * Create a wrapper dom element, the size of the column header. - * The wrapper acts as tooltip parent, for positioning, and the - * tooltip component will trigger hide on mouseleave of wrapper - * or tooltip. The wrapper should be bound to within the - * grid dimensions, so popper doesn't end up outside the panel. + * The wrapper acts as tooltip parent, the tooltip component + * will trigger hide on mouseleave of wrapper or tooltip. + * The wrapper should be bound to within the grid dimensions, + * so popper only remains triggered while mouse is inside the panel. */ const boundedLeft = Math.max(0, columnX); let boundedWidth = columnWidth; @@ -2457,6 +2458,44 @@ export class IrisGrid extends Component { pointerEvents: 'none', }; + /** + * Because the popper parent wrapper center is no longer the same as + * the column label center, we create a popper virtual ref, to handle + * positioning and keep the popper centered on the label. Creates a + * 1px x headerHeight virtual object, placed centered on the column + * label, clamped to 0 + margin to width - margin. We add a margin, + * otherwise the arrow wants to esacpe the boundry. + */ + const gridRect = this.gridWrapper.getBoundingClientRect(); + const popperMargin = 20; + const virtualReference = { + clientWidth: 1, + clientHeight: columnHeaderHeight, + getBoundingClientRect() { + return { + top: gridRect.top, + left: + gridRect.left + + clamp( + columnX + columnWidth / 2, + popperMargin, + width - popperMargin + ), + bottom: gridRect.top + columnHeaderHeight, + right: + gridRect.left + + clamp( + columnX + columnWidth / 2, + popperMargin, + width - popperMargin + ) + + 1, + width: 1, + height: columnHeaderHeight, + }; + }, + }; + const popperOptions = { placement: 'bottom', modifiers: { @@ -2478,6 +2517,7 @@ export class IrisGrid extends Component { interactive options={popperOptions} ref={this.handleTooltipRef} + referenceObject={virtualReference} >