diff --git a/res/css/views/elements/_InteractiveTooltip.scss b/res/css/views/elements/_InteractiveTooltip.scss index 11f548fa18..3ec20be928 100644 --- a/res/css/views/elements/_InteractiveTooltip.scss +++ b/res/css/views/elements/_InteractiveTooltip.scss @@ -39,79 +39,13 @@ limitations under the License. z-index: 5001; } -.mx_InteractiveTooltip_right { - right: 0; -} - -.mx_InteractiveTooltip.mx_InteractiveTooltip_withChevron_right { - right: 8px; -} - -.mx_InteractiveTooltip_chevron_right { - position: absolute; - right: -8px; - top: 0px; - width: 0; - height: 0; - border-top: 8px solid transparent; - border-left: 8px solid $menu-bg-color; - border-bottom: 8px solid transparent; -} - -.mx_InteractiveTooltip_chevron_right::after { - content: ''; - width: 0; - height: 0; - border-top: 7px solid transparent; - border-left: 7px solid $menu-bg-color; - border-bottom: 7px solid transparent; - position: absolute; - top: -7px; - right: 1px; -} - -.mx_InteractiveTooltip_left { - left: 0; -} - -.mx_InteractiveTooltip.mx_InteractiveTooltip_withChevron_left { - left: 8px; -} - -.mx_InteractiveTooltip_chevron_left { - position: absolute; - left: -8px; - top: 0px; - width: 0; - height: 0; - border-top: 8px solid transparent; - border-right: 8px solid $menu-bg-color; - border-bottom: 8px solid transparent; -} - -.mx_InteractiveTooltip_chevron_left::after { - content: ''; - width: 0; - height: 0; - border-top: 7px solid transparent; - border-right: 7px solid $menu-bg-color; - border-bottom: 7px solid transparent; - position: absolute; - top: -7px; - left: 1px; -} - -.mx_InteractiveTooltip_top { - top: 0; -} - .mx_InteractiveTooltip.mx_InteractiveTooltip_withChevron_top { top: 8px; } .mx_InteractiveTooltip_chevron_top { position: absolute; - left: 0px; + left: calc(50% - 8px); top: -8px; width: 0; height: 0; @@ -132,17 +66,13 @@ limitations under the License. top: 1px; } -.mx_InteractiveTooltip_bottom { - bottom: 0; -} - .mx_InteractiveTooltip.mx_InteractiveTooltip_withChevron_bottom { bottom: 8px; } .mx_InteractiveTooltip_chevron_bottom { position: absolute; - left: 0px; + left: calc(50% - 8px); bottom: -8px; width: 0; height: 0; diff --git a/src/components/views/elements/InteractiveTooltip.js b/src/components/views/elements/InteractiveTooltip.js index 7c582a2b71..b3d0b32fa7 100644 --- a/src/components/views/elements/InteractiveTooltip.js +++ b/src/components/views/elements/InteractiveTooltip.js @@ -33,21 +33,19 @@ function getOrCreateContainer() { return container; } +/* + * This style of tooltip takes a `target` element's rect and centers the tooltip + * along one edge of the target. + */ export default class InteractiveTooltip extends React.Component { propTypes: { - top: PropTypes.number, - bottom: PropTypes.number, - left: PropTypes.number, - right: PropTypes.number, - chevronOffset: PropTypes.number, - chevronFace: PropTypes.string, // top, bottom, left, right or none + // A DOMRect from the target element + targetRect: PropTypes.object.isRequired, // Function to be called on menu close onFinished: PropTypes.func, - // If true, insert an invisible screen-sized element behind the // menu that when clicked will close it. hasBackground: PropTypes.bool, - // The component to render as the context menu elementClass: PropTypes.element.isRequired, // on resize callback @@ -56,58 +54,64 @@ export default class InteractiveTooltip extends React.Component { closeTooltip: PropTypes.func, }; + constructor() { + super(); + + this.state = { + contentRect: null, + }; + } + + collectContentRect = (element) => { + // We don't need to clean up when unmounting, so ignore + if (!element) return; + + this.setState({ + contentRect: element.getBoundingClientRect(), + }); + } + render() { + const props = this.props; + const { targetRect } = props; + + // The window X and Y offsets are to adjust position when zoomed in to page + const targetLeft = targetRect.left + window.pageXOffset; + const targetBottom = targetRect.bottom + window.pageYOffset; + const targetTop = targetRect.top + window.pageYOffset; + + // Align the tooltip vertically on whichever side of the target has more + // space available. const position = {}; let chevronFace = null; - const props = this.props; - - if (props.top) { - position.top = props.top; + if (targetBottom < window.innerHeight / 2) { + position.top = targetBottom; + chevronFace = "top"; } else { - position.bottom = props.bottom; + position.bottom = window.innerHeight - targetTop; + chevronFace = "bottom"; } - if (props.left) { - position.left = props.left; - chevronFace = 'left'; - } else { - position.right = props.right; - chevronFace = 'right'; - } + // Center the tooltip horizontally with the target's center. + position.left = targetLeft + targetRect.width / 2; - const chevronOffset = {}; - if (props.chevronFace) { - chevronFace = props.chevronFace; - } - const hasChevron = chevronFace && chevronFace !== "none"; - - if (chevronFace === 'top' || chevronFace === 'bottom') { - chevronOffset.left = props.chevronOffset; - } else { - chevronOffset.top = props.chevronOffset; - } - - const chevron = hasChevron ? -
: - undefined; - const className = 'mx_InteractiveTooltip_wrapper'; + const chevron = ; const menuClasses = classNames({ 'mx_InteractiveTooltip': true, - 'mx_InteractiveTooltip_left': !hasChevron && position.left, - 'mx_InteractiveTooltip_right': !hasChevron && position.right, - 'mx_InteractiveTooltip_top': !hasChevron && position.top, - 'mx_InteractiveTooltip_bottom': !hasChevron && position.bottom, - 'mx_InteractiveTooltip_withChevron_left': chevronFace === 'left', - 'mx_InteractiveTooltip_withChevron_right': chevronFace === 'right', 'mx_InteractiveTooltip_withChevron_top': chevronFace === 'top', 'mx_InteractiveTooltip_withChevron_bottom': chevronFace === 'bottom', }); + const menuStyle = {}; + if (this.state.contentRect) { + menuStyle.left = `-${this.state.contentRect.width / 2}px`; + } + const ElementClass = props.elementClass; - return