From 38dc3c8bd1f2443f64c525ef4d8a9438c0a0ed09 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 17 Jul 2020 18:43:42 +0100
Subject: [PATCH] Replace non-functional Interactive Tooltip

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
 res/css/_components.scss                      |   1 -
 .../views/elements/_InteractiveTooltip.scss   |  91 -----
 .../views/elements/InteractiveTooltip.js      | 336 ------------------
 .../views/rooms/MessageComposerFormatBar.js   |  29 +-
 4 files changed, 14 insertions(+), 443 deletions(-)
 delete mode 100644 res/css/views/elements/_InteractiveTooltip.scss
 delete mode 100644 src/components/views/elements/InteractiveTooltip.js

diff --git a/res/css/_components.scss b/res/css/_components.scss
index d0432b2f23..27eebf0ea9 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -108,7 +108,6 @@
 @import "./views/elements/_IconButton.scss";
 @import "./views/elements/_ImageView.scss";
 @import "./views/elements/_InlineSpinner.scss";
-@import "./views/elements/_InteractiveTooltip.scss";
 @import "./views/elements/_ManageIntegsButton.scss";
 @import "./views/elements/_PowerSelector.scss";
 @import "./views/elements/_ProgressBar.scss";
diff --git a/res/css/views/elements/_InteractiveTooltip.scss b/res/css/views/elements/_InteractiveTooltip.scss
deleted file mode 100644
index db98d95709..0000000000
--- a/res/css/views/elements/_InteractiveTooltip.scss
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
-Copyright 2019 The Matrix.org Foundation C.I.C.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-.mx_InteractiveTooltip_wrapper {
-    position: fixed;
-    z-index: 5000;
-}
-
-.mx_InteractiveTooltip {
-    border-radius: 3px;
-    background-color: $interactive-tooltip-bg-color;
-    color: $interactive-tooltip-fg-color;
-    position: absolute;
-    font-size: $font-10px;
-    font-weight: 600;
-    padding: 6px;
-    z-index: 5001;
-}
-
-.mx_InteractiveTooltip.mx_InteractiveTooltip_withChevron_top {
-    top: 10px; // 8px chevron + 2px spacing
-}
-
-.mx_InteractiveTooltip_chevron_top {
-    position: absolute;
-    left: calc(50% - 8px);
-    top: -8px;
-    width: 0;
-    height: 0;
-    border-left: 8px solid transparent;
-    border-bottom: 8px solid $interactive-tooltip-bg-color;
-    border-right: 8px solid transparent;
-}
-
-// Adapted from https://codyhouse.co/blog/post/css-rounded-triangles-with-clip-path
-// by Sebastiano Guerriero (@guerriero_se)
-@supports (clip-path: polygon(0% 0%, 100% 100%, 0% 100%)) {
-    .mx_InteractiveTooltip_chevron_top {
-        height: 16px;
-        width: 16px;
-        background-color: inherit;
-        border: none;
-        clip-path: polygon(0% 0%, 100% 100%, 0% 100%);
-        transform: rotate(135deg);
-        border-radius: 0 0 0 3px;
-        top: calc(-8px / 1.414); // sqrt(2) because of rotation
-    }
-}
-
-.mx_InteractiveTooltip.mx_InteractiveTooltip_withChevron_bottom {
-    bottom: 10px; // 8px chevron + 2px spacing
-}
-
-.mx_InteractiveTooltip_chevron_bottom {
-    position: absolute;
-    left: calc(50% - 8px);
-    bottom: -8px;
-    width: 0;
-    height: 0;
-    border-left: 8px solid transparent;
-    border-top: 8px solid $interactive-tooltip-bg-color;
-    border-right: 8px solid transparent;
-}
-
-// Adapted from https://codyhouse.co/blog/post/css-rounded-triangles-with-clip-path
-// by Sebastiano Guerriero (@guerriero_se)
-@supports (clip-path: polygon(0% 0%, 100% 100%, 0% 100%)) {
-    .mx_InteractiveTooltip_chevron_bottom {
-        height: 16px;
-        width: 16px;
-        background-color: inherit;
-        border: none;
-        clip-path: polygon(0% 0%, 100% 100%, 0% 100%);
-        transform: rotate(-45deg);
-        border-radius: 0 0 0 3px;
-        bottom: calc(-8px / 1.414); // sqrt(2) because of rotation
-    }
-}
diff --git a/src/components/views/elements/InteractiveTooltip.js b/src/components/views/elements/InteractiveTooltip.js
deleted file mode 100644
index 7f5f24a094..0000000000
--- a/src/components/views/elements/InteractiveTooltip.js
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
-Copyright 2019 The Matrix.org Foundation C.I.C.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import ReactDOM from 'react-dom';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-
-const InteractiveTooltipContainerId = "mx_InteractiveTooltip_Container";
-
-// If the distance from tooltip to window edge is below this value, the tooltip
-// will flip around to the other side of the target.
-const MIN_SAFE_DISTANCE_TO_WINDOW_EDGE = 20;
-
-function getOrCreateContainer() {
-    let container = document.getElementById(InteractiveTooltipContainerId);
-
-    if (!container) {
-        container = document.createElement("div");
-        container.id = InteractiveTooltipContainerId;
-        document.body.appendChild(container);
-    }
-
-    return container;
-}
-
-function isInRect(x, y, rect) {
-    const { top, right, bottom, left } = rect;
-    return x >= left && x <= right && y >= top && y <= bottom;
-}
-
-/**
- * Returns the positive slope of the diagonal of the rect.
- *
- * @param {DOMRect} rect
- * @return {integer}
- */
-function getDiagonalSlope(rect) {
-    const { top, right, bottom, left } = rect;
-    return (bottom - top) / (right - left);
-}
-
-function isInUpperLeftHalf(x, y, rect) {
-    const { bottom, left } = rect;
-    // Negative slope because Y values grow downwards and for this case, the
-    // diagonal goes from larger to smaller Y values.
-    const diagonalSlope = getDiagonalSlope(rect) * -1;
-    return isInRect(x, y, rect) && (y <= bottom + diagonalSlope * (x - left));
-}
-
-function isInLowerRightHalf(x, y, rect) {
-    const { bottom, left } = rect;
-    // Negative slope because Y values grow downwards and for this case, the
-    // diagonal goes from larger to smaller Y values.
-    const diagonalSlope = getDiagonalSlope(rect) * -1;
-    return isInRect(x, y, rect) && (y >= bottom + diagonalSlope * (x - left));
-}
-
-function isInUpperRightHalf(x, y, rect) {
-    const { top, left } = rect;
-    // Positive slope because Y values grow downwards and for this case, the
-    // diagonal goes from smaller to larger Y values.
-    const diagonalSlope = getDiagonalSlope(rect) * 1;
-    return isInRect(x, y, rect) && (y <= top + diagonalSlope * (x - left));
-}
-
-function isInLowerLeftHalf(x, y, rect) {
-    const { top, left } = rect;
-    // Positive slope because Y values grow downwards and for this case, the
-    // diagonal goes from smaller to larger Y values.
-    const diagonalSlope = getDiagonalSlope(rect) * 1;
-    return isInRect(x, y, rect) && (y >= top + diagonalSlope * (x - left));
-}
-
-/*
- * This style of tooltip takes a "target" element as its child and centers the
- * tooltip along one edge of the target.
- */
-export default class InteractiveTooltip extends React.Component {
-    static propTypes = {
-        // Content to show in the tooltip
-        content: PropTypes.node.isRequired,
-        // Function to call when visibility of the tooltip changes
-        onVisibilityChange: PropTypes.func,
-        // flag to forcefully hide this tooltip
-        forceHidden: PropTypes.bool,
-    };
-
-    constructor() {
-        super();
-
-        this.state = {
-            contentRect: null,
-            visible: false,
-        };
-    }
-
-    componentDidUpdate() {
-        // Whenever this passthrough component updates, also render the tooltip
-        // in a separate DOM tree. This allows the tooltip content to participate
-        // the normal React rendering cycle: when this component re-renders, the
-        // tooltip content re-renders.
-        // Once we upgrade to React 16, this could be done a bit more naturally
-        // using the portals feature instead.
-        this.renderTooltip();
-    }
-
-    componentWillUnmount() {
-        document.removeEventListener("mousemove", this.onMouseMove);
-    }
-
-    collectContentRect = (element) => {
-        // We don't need to clean up when unmounting, so ignore
-        if (!element) return;
-
-        this.setState({
-            contentRect: element.getBoundingClientRect(),
-        });
-    }
-
-    collectTarget = (element) => {
-        this.target = element;
-    }
-
-    canTooltipFitAboveTarget() {
-        const { contentRect } = this.state;
-        const targetRect = this.target.getBoundingClientRect();
-        const targetTop = targetRect.top + window.pageYOffset;
-        return (
-            !contentRect ||
-            (targetTop - contentRect.height > MIN_SAFE_DISTANCE_TO_WINDOW_EDGE)
-        );
-    }
-
-    onMouseMove = (ev) => {
-        const { clientX: x, clientY: y } = ev;
-        const { contentRect } = this.state;
-        const targetRect = this.target.getBoundingClientRect();
-
-        // When moving the mouse from the target to the tooltip, we create a
-        // safe area that includes the tooltip, the target, and the trapezoid
-        // ABCD between them:
-        //                            ┌───────────┐
-        //                            │           │
-        //                            │           │
-        //                          A └───E───F───┘ B
-        //                                  V
-        //                                 ┌─┐
-        //                                 │ │
-        //                                C└─┘D
-        //
-        // As long as the mouse remains inside the safe area, the tooltip will
-        // stay open.
-        const buffer = 50;
-        if (isInRect(x, y, targetRect)) {
-            return;
-        }
-        if (this.canTooltipFitAboveTarget()) {
-            const contentRectWithBuffer = {
-                top: contentRect.top - buffer,
-                right: contentRect.right + buffer,
-                bottom: contentRect.bottom,
-                left: contentRect.left - buffer,
-            };
-            const trapezoidLeft = {
-                top: contentRect.bottom,
-                right: targetRect.left,
-                bottom: targetRect.bottom,
-                left: contentRect.left - buffer,
-            };
-            const trapezoidCenter = {
-                top: contentRect.bottom,
-                right: targetRect.right,
-                bottom: targetRect.bottom,
-                left: targetRect.left,
-            };
-            const trapezoidRight = {
-                top: contentRect.bottom,
-                right: contentRect.right + buffer,
-                bottom: targetRect.bottom,
-                left: targetRect.right,
-            };
-
-            if (
-                isInRect(x, y, contentRectWithBuffer) ||
-                isInUpperRightHalf(x, y, trapezoidLeft) ||
-                isInRect(x, y, trapezoidCenter) ||
-                isInUpperLeftHalf(x, y, trapezoidRight)
-            ) {
-                return;
-            }
-        } else {
-            const contentRectWithBuffer = {
-                top: contentRect.top,
-                right: contentRect.right + buffer,
-                bottom: contentRect.bottom + buffer,
-                left: contentRect.left - buffer,
-            };
-            const trapezoidLeft = {
-                top: targetRect.top,
-                right: targetRect.left,
-                bottom: contentRect.top,
-                left: contentRect.left - buffer,
-            };
-            const trapezoidCenter = {
-                top: targetRect.top,
-                right: targetRect.right,
-                bottom: contentRect.top,
-                left: targetRect.left,
-            };
-            const trapezoidRight = {
-                top: targetRect.top,
-                right: contentRect.right + buffer,
-                bottom: contentRect.top,
-                left: targetRect.right,
-            };
-
-            if (
-                isInRect(x, y, contentRectWithBuffer) ||
-                isInLowerRightHalf(x, y, trapezoidLeft) ||
-                isInRect(x, y, trapezoidCenter) ||
-                isInLowerLeftHalf(x, y, trapezoidRight)
-            ) {
-                return;
-            }
-        }
-
-        this.hideTooltip();
-    }
-
-    onTargetMouseOver = (ev) => {
-        this.showTooltip();
-    }
-
-    showTooltip() {
-        // Don't enter visible state if we haven't collected the target yet
-        if (!this.target) {
-            return;
-        }
-        this.setState({
-            visible: true,
-        });
-        if (this.props.onVisibilityChange) {
-            this.props.onVisibilityChange(true);
-        }
-        document.addEventListener("mousemove", this.onMouseMove);
-    }
-
-    hideTooltip() {
-        this.setState({
-            visible: false,
-        });
-        if (this.props.onVisibilityChange) {
-            this.props.onVisibilityChange(false);
-        }
-        document.removeEventListener("mousemove", this.onMouseMove);
-    }
-
-    renderTooltip() {
-        const { contentRect, visible } = this.state;
-        if (this.props.forceHidden === true || !visible) {
-            ReactDOM.render(null, getOrCreateContainer());
-            return null;
-        }
-
-        const targetRect = this.target.getBoundingClientRect();
-
-        // 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;
-
-        // Place the tooltip above the target by default. If we find that the
-        // tooltip content would extend past the safe area towards the window
-        // edge, flip around to below the target.
-        const position = {};
-        let chevronFace = null;
-        if (this.canTooltipFitAboveTarget()) {
-            position.bottom = window.innerHeight - targetTop;
-            chevronFace = "bottom";
-        } else {
-            position.top = targetBottom;
-            chevronFace = "top";
-        }
-
-        // Center the tooltip horizontally with the target's center.
-        position.left = targetLeft + targetRect.width / 2;
-
-        const chevron = <div className={"mx_InteractiveTooltip_chevron_" + chevronFace} />;
-
-        const menuClasses = classNames({
-            'mx_InteractiveTooltip': true,
-            'mx_InteractiveTooltip_withChevron_top': chevronFace === 'top',
-            'mx_InteractiveTooltip_withChevron_bottom': chevronFace === 'bottom',
-        });
-
-        const menuStyle = {};
-        if (contentRect) {
-            menuStyle.left = `-${contentRect.width / 2}px`;
-        }
-
-        const tooltip = <div className="mx_InteractiveTooltip_wrapper" style={{...position}}>
-            <div className={menuClasses}
-                style={menuStyle}
-                ref={this.collectContentRect}
-            >
-                {chevron}
-                {this.props.content}
-            </div>
-        </div>;
-
-        ReactDOM.render(tooltip, getOrCreateContainer());
-    }
-
-    render() {
-        // We use `cloneElement` here to append some props to the child content
-        // without using a wrapper element which could disrupt layout.
-        return React.cloneElement(this.props.children, {
-            ref: this.collectTarget,
-            onMouseOver: this.onTargetMouseOver,
-        });
-    }
-}
diff --git a/src/components/views/rooms/MessageComposerFormatBar.js b/src/components/views/rooms/MessageComposerFormatBar.js
index 42d54f5987..71aef1e833 100644
--- a/src/components/views/rooms/MessageComposerFormatBar.js
+++ b/src/components/views/rooms/MessageComposerFormatBar.js
@@ -17,9 +17,8 @@ limitations under the License.
 import React from 'react';
 import PropTypes from 'prop-types';
 import { _t } from '../../../languageHandler';
-import * as sdk from '../../../index';
 import classNames from 'classnames';
-import AccessibleButton from "../elements/AccessibleButton";
+import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
 
 export default class MessageComposerFormatBar extends React.PureComponent {
     static propTypes = {
@@ -68,28 +67,28 @@ class FormatButton extends React.PureComponent {
     };
 
     render() {
-        const InteractiveTooltip = sdk.getComponent('elements.InteractiveTooltip');
         const className = `mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIcon${this.props.icon}`;
         let shortcut;
         if (this.props.shortcut) {
             shortcut = <div className="mx_MessageComposerFormatBar_tooltipShortcut">{this.props.shortcut}</div>;
         }
-        const tooltipContent = (
-            <div className="mx_MessageComposerFormatBar_buttonTooltip">
-                <div>{this.props.label}</div>
+        const tooltip = <div>
+            <div className="mx_Tooltip_title">
+                {this.props.label}
+            </div>
+            <div className="mx_Tooltip_sub">
                 {shortcut}
             </div>
-        );
+        </div>;
 
         return (
-            <InteractiveTooltip content={tooltipContent} forceHidden={!this.props.visible}>
-                <AccessibleButton
-                    as="span"
-                    role="button"
-                    onClick={this.props.onClick}
-                    aria-label={this.props.label}
-                    className={className} />
-            </InteractiveTooltip>
+            <AccessibleTooltipButton
+                as="span"
+                role="button"
+                onClick={this.props.onClick}
+                title={this.props.label}
+                tooltip={tooltip}
+                className={className} />
         );
     }
 }