mirror of https://github.com/vector-im/riot-web
				
				
				
			Center tooltip along top or bottom of target
This adjusts the positioning to work more the way we want: * Tooltip is position on the top or bottom edge of the target depending on where space is available * Tooltip and chevron are centered In addition, more bits borrowed from `ContextualMenu` are not needed, so they have been removed for simplicity. Part of https://github.com/vector-im/riot-web/issues/9753 Part of https://github.com/vector-im/riot-web/issues/9716pull/21833/head
							parent
							
								
									6dcdad028e
								
							
						
					
					
						commit
						32bf4588dd
					
				| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 ?
 | 
			
		||||
            <div style={chevronOffset} className={"mx_InteractiveTooltip_chevron_" + chevronFace} /> :
 | 
			
		||||
            undefined;
 | 
			
		||||
        const className = 'mx_InteractiveTooltip_wrapper';
 | 
			
		||||
        const chevron = <div className={"mx_InteractiveTooltip_chevron_" + chevronFace} />;
 | 
			
		||||
 | 
			
		||||
        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 <div className={className} style={{...position}}>
 | 
			
		||||
            <div className={menuClasses}>
 | 
			
		||||
        return <div className="mx_InteractiveTooltip_wrapper" style={{...position}}>
 | 
			
		||||
            <div className={menuClasses} style={menuStyle} ref={this.collectContentRect}>
 | 
			
		||||
                { chevron }
 | 
			
		||||
                <ElementClass {...props} onFinished={props.closeTooltip} onResize={props.windowResize} />
 | 
			
		||||
            </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue