Merge pull request #5012 from matrix-org/t3chguy/tooltips
Sprinkle and consolidate some tooltips
This commit is contained in:
commit
618ecbd261
30 changed files with 197 additions and 660 deletions
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -51,32 +51,18 @@ limitations under the License.
|
|||
.mx_Tooltip {
|
||||
display: none;
|
||||
position: fixed;
|
||||
border: 1px solid $menu-border-color;
|
||||
border-radius: 8px;
|
||||
box-shadow: 4px 4px 12px 0 $menu-box-shadow-color;
|
||||
background-color: $menu-bg-color;
|
||||
z-index: 6000; // Higher than context menu so tooltips can be used everywhere
|
||||
padding: 10px;
|
||||
pointer-events: none;
|
||||
line-height: $font-14px;
|
||||
font-size: $font-12px;
|
||||
font-weight: 500;
|
||||
color: $primary-fg-color;
|
||||
max-width: 200px;
|
||||
word-break: break-word;
|
||||
margin-right: 50px;
|
||||
|
||||
&.mx_Tooltip_visible {
|
||||
animation: mx_fadein 0.2s forwards;
|
||||
}
|
||||
|
||||
&.mx_Tooltip_invisible {
|
||||
animation: mx_fadeout 0.1s forwards;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_Tooltip_timeline {
|
||||
&.mx_Tooltip {
|
||||
background-color: $inverted-bg-color;
|
||||
color: $accent-fg-color;
|
||||
border: 0;
|
||||
|
@ -85,6 +71,13 @@ limitations under the License.
|
|||
.mx_Tooltip_chevron {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.mx_Tooltip_visible {
|
||||
animation: mx_fadein 0.2s forwards;
|
||||
}
|
||||
|
||||
&.mx_Tooltip_invisible {
|
||||
animation: mx_fadeout 0.1s forwards;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,14 +55,4 @@ limitations under the License.
|
|||
.mx_RoomBreadcrumbs2_Tooltip {
|
||||
margin-left: -42px;
|
||||
margin-top: -42px;
|
||||
|
||||
&.mx_Tooltip {
|
||||
background-color: $inverted-bg-color;
|
||||
color: $accent-fg-color;
|
||||
border: 0;
|
||||
|
||||
.mx_Tooltip_chevron {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,9 +18,9 @@ limitations under the License.
|
|||
|
||||
import React from "react";
|
||||
|
||||
import AccessibleButton, {IProps as IAccessibleButtonProps} from "../../components/views/elements/AccessibleButton";
|
||||
import AccessibleButton from "../../components/views/elements/AccessibleButton";
|
||||
|
||||
interface IProps extends IAccessibleButtonProps {
|
||||
interface IProps extends React.ComponentProps<typeof AccessibleButton> {
|
||||
label?: string;
|
||||
// whether or not the context menu is currently open
|
||||
isExpanded: boolean;
|
||||
|
|
47
src/accessibility/context_menu/ContextMenuTooltipButton.tsx
Normal file
47
src/accessibility/context_menu/ContextMenuTooltipButton.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
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 AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton";
|
||||
|
||||
interface IProps extends React.ComponentProps<typeof AccessibleTooltipButton> {
|
||||
// whether or not the context menu is currently open
|
||||
isExpanded: boolean;
|
||||
}
|
||||
|
||||
// Semantic component for representing the AccessibleButton which launches a <ContextMenu />
|
||||
export const ContextMenuTooltipButton: React.FC<IProps> = ({
|
||||
isExpanded,
|
||||
children,
|
||||
onClick,
|
||||
onContextMenu,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<AccessibleTooltipButton
|
||||
{...props}
|
||||
onClick={onClick}
|
||||
onContextMenu={onContextMenu || onClick}
|
||||
aria-haspopup={true}
|
||||
aria-expanded={isExpanded}
|
||||
>
|
||||
{ children }
|
||||
</AccessibleTooltipButton>
|
||||
);
|
||||
};
|
|
@ -461,6 +461,7 @@ export function createMenu(ElementClass, props) {
|
|||
|
||||
// re-export the semantic helper components for simplicity
|
||||
export {ContextMenuButton} from "../../accessibility/context_menu/ContextMenuButton";
|
||||
export {ContextMenuTooltipButton} from "../../accessibility/context_menu/ContextMenuTooltipButton";
|
||||
export {MenuGroup} from "../../accessibility/context_menu/MenuGroup";
|
||||
export {MenuItem} from "../../accessibility/context_menu/MenuItem";
|
||||
export {MenuItemCheckbox} from "../../accessibility/context_menu/MenuItemCheckbox";
|
||||
|
|
|
@ -34,6 +34,7 @@ import SettingsStore from "../../settings/SettingsStore";
|
|||
import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomListStore2";
|
||||
import {Key} from "../../Keyboard";
|
||||
import IndicatorScrollbar from "../structures/IndicatorScrollbar";
|
||||
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
||||
|
||||
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367
|
||||
|
||||
|
@ -347,7 +348,7 @@ export default class LeftPanel2 extends React.Component<IProps, IState> {
|
|||
onVerticalArrow={this.onKeyDown}
|
||||
onEnter={this.onEnter}
|
||||
/>
|
||||
<AccessibleButton
|
||||
<AccessibleTooltipButton
|
||||
className="mx_LeftPanel2_exploreButton"
|
||||
onClick={this.onExplore}
|
||||
title={_t("Explore rooms")}
|
||||
|
|
|
@ -27,7 +27,7 @@ export type ButtonEvent = React.MouseEvent<Element> | React.KeyboardEvent<Elemen
|
|||
* onClick: (required) Event handler for button activation. Should be
|
||||
* implemented exactly like a normal onClick handler.
|
||||
*/
|
||||
export interface IProps extends React.InputHTMLAttributes<Element> {
|
||||
interface IProps extends React.InputHTMLAttributes<Element> {
|
||||
inputRef?: React.Ref<Element>;
|
||||
element?: string;
|
||||
// The kind of button, similar to how Bootstrap works.
|
||||
|
@ -118,7 +118,7 @@ export default function AccessibleButton({
|
|||
AccessibleButton.defaultProps = {
|
||||
element: 'div',
|
||||
role: 'button',
|
||||
tabIndex: "0",
|
||||
tabIndex: 0,
|
||||
};
|
||||
|
||||
AccessibleButton.displayName = "AccessibleButton";
|
||||
|
|
|
@ -19,10 +19,9 @@ import React from 'react';
|
|||
import classNames from 'classnames';
|
||||
|
||||
import AccessibleButton from "./AccessibleButton";
|
||||
import {IProps} from "./AccessibleButton";
|
||||
import Tooltip from './Tooltip';
|
||||
|
||||
interface ITooltipProps extends IProps {
|
||||
interface ITooltipProps extends React.ComponentProps<typeof AccessibleButton> {
|
||||
title: string;
|
||||
tooltip?: React.ReactNode;
|
||||
tooltipClassName?: string;
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -17,10 +17,10 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import AccessibleTooltipButton from "./AccessibleTooltipButton";
|
||||
|
||||
export default class ManageIntegsButton extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -45,9 +45,8 @@ export default class ManageIntegsButton extends React.Component {
|
|||
render() {
|
||||
let integrationsButton = <div />;
|
||||
if (IntegrationManagers.sharedInstance().hasManager()) {
|
||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||
integrationsButton = (
|
||||
<AccessibleButton
|
||||
<AccessibleTooltipButton
|
||||
className='mx_RoomHeader_button mx_RoomHeader_manageIntegsButton'
|
||||
title={_t("Manage Integrations")}
|
||||
onClick={this.onManageIntegrations}
|
||||
|
|
|
@ -18,12 +18,9 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import React, {Component, CSSProperties} from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import classNames from 'classnames';
|
||||
import { ViewTooltipPayload } from '../../../dispatcher/payloads/ViewTooltipPayload';
|
||||
import { Action } from '../../../dispatcher/actions';
|
||||
|
||||
const MIN_TOOLTIP_HEIGHT = 25;
|
||||
|
||||
|
@ -68,18 +65,12 @@ export default class Tooltip extends React.Component<IProps> {
|
|||
|
||||
// Remove the wrapper element, as the tooltip has finished using it
|
||||
public componentWillUnmount() {
|
||||
dis.dispatch<ViewTooltipPayload>({
|
||||
action: Action.ViewTooltip,
|
||||
tooltip: null,
|
||||
parent: null,
|
||||
});
|
||||
|
||||
ReactDOM.unmountComponentAtNode(this.tooltipContainer);
|
||||
document.body.removeChild(this.tooltipContainer);
|
||||
window.removeEventListener('scroll', this.renderTooltip, true);
|
||||
}
|
||||
|
||||
private updatePosition(style: {[key: string]: any}) {
|
||||
private updatePosition(style: CSSProperties) {
|
||||
const parentBox = this.parent.getBoundingClientRect();
|
||||
let offset = 0;
|
||||
if (parentBox.height > MIN_TOOLTIP_HEIGHT) {
|
||||
|
@ -89,8 +80,14 @@ export default class Tooltip extends React.Component<IProps> {
|
|||
// we need so that we're still centered.
|
||||
offset = Math.floor(parentBox.height - MIN_TOOLTIP_HEIGHT);
|
||||
}
|
||||
|
||||
style.top = (parentBox.top - 2) + window.pageYOffset + offset;
|
||||
style.left = 6 + parentBox.right + window.pageXOffset;
|
||||
if (parentBox.right > window.innerWidth / 2) {
|
||||
style.right = window.innerWidth - parentBox.right - window.pageXOffset - 8;
|
||||
} else {
|
||||
style.left = parentBox.right + window.pageXOffset + 6;
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
|
@ -99,7 +96,6 @@ export default class Tooltip extends React.Component<IProps> {
|
|||
// positioned, also taking into account any window zoom
|
||||
// NOTE: The additional 6 pixels for the left position, is to take account of the
|
||||
// tooltips chevron
|
||||
const parent = ReactDOM.findDOMNode(this).parentNode as Element;
|
||||
const style = this.updatePosition({});
|
||||
// Hide the entire container when not visible. This prevents flashing of the tooltip
|
||||
// if it is not meant to be visible on first mount.
|
||||
|
@ -119,13 +115,6 @@ export default class Tooltip extends React.Component<IProps> {
|
|||
|
||||
// Render the tooltip manually, as we wish it not to be rendered within the parent
|
||||
this.tooltip = ReactDOM.render<Element>(tooltip, this.tooltipContainer);
|
||||
|
||||
// Tell the roomlist about us so it can manipulate us if it wishes
|
||||
dis.dispatch<ViewTooltipPayload>({
|
||||
action: Action.ViewTooltip,
|
||||
tooltip: this.tooltip,
|
||||
parent: parent,
|
||||
});
|
||||
};
|
||||
|
||||
public render() {
|
||||
|
|
|
@ -22,11 +22,11 @@ import PropTypes from 'prop-types';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import * as sdk from '../../../index';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import {aboveLeftOf, ContextMenu, ContextMenuButton, useContextMenu} from '../../structures/ContextMenu';
|
||||
import {aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu} from '../../structures/ContextMenu';
|
||||
import { isContentActionable, canEditContent } from '../../../utils/EventUtils';
|
||||
import RoomContext from "../../../contexts/RoomContext";
|
||||
import Toolbar from "../../../accessibility/Toolbar";
|
||||
import {RovingAccessibleButton, useRovingTabIndex} from "../../../accessibility/RovingTabIndex";
|
||||
import {RovingAccessibleTooltipButton, useRovingTabIndex} from "../../../accessibility/RovingTabIndex";
|
||||
|
||||
const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFocusChange}) => {
|
||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
||||
|
@ -55,9 +55,9 @@ const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFo
|
|||
}
|
||||
|
||||
return <React.Fragment>
|
||||
<ContextMenuButton
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_MessageActionBar_maskButton mx_MessageActionBar_optionsButton"
|
||||
label={_t("Options")}
|
||||
title={_t("Options")}
|
||||
onClick={openMenu}
|
||||
isExpanded={menuDisplayed}
|
||||
inputRef={ref}
|
||||
|
@ -86,9 +86,9 @@ const ReactButton = ({mxEvent, reactions, onFocusChange}) => {
|
|||
}
|
||||
|
||||
return <React.Fragment>
|
||||
<ContextMenuButton
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_MessageActionBar_maskButton mx_MessageActionBar_reactButton"
|
||||
label={_t("React")}
|
||||
title={_t("React")}
|
||||
onClick={openMenu}
|
||||
isExpanded={menuDisplayed}
|
||||
inputRef={ref}
|
||||
|
@ -167,7 +167,7 @@ export default class MessageActionBar extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
if (this.context.canReply) {
|
||||
replyButton = <RovingAccessibleButton
|
||||
replyButton = <RovingAccessibleTooltipButton
|
||||
className="mx_MessageActionBar_maskButton mx_MessageActionBar_replyButton"
|
||||
title={_t("Reply")}
|
||||
onClick={this.onReplyClick}
|
||||
|
@ -175,7 +175,7 @@ export default class MessageActionBar extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
if (canEditContent(this.props.mxEvent)) {
|
||||
editButton = <RovingAccessibleButton
|
||||
editButton = <RovingAccessibleTooltipButton
|
||||
className="mx_MessageActionBar_maskButton mx_MessageActionBar_editButton"
|
||||
title={_t("Edit")}
|
||||
onClick={this.onEditClick}
|
||||
|
|
|
@ -73,11 +73,7 @@ export default class ReactionsRowButtonTooltip extends React.PureComponent {
|
|||
|
||||
let tooltip;
|
||||
if (tooltipLabel) {
|
||||
tooltip = <Tooltip
|
||||
tooltipClassName="mx_Tooltip_timeline"
|
||||
visible={visible}
|
||||
label={tooltipLabel}
|
||||
/>;
|
||||
tooltip = <Tooltip visible={visible} label={tooltipLabel} />;
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
|
|
|
@ -391,7 +391,6 @@ export default createReactClass({
|
|||
onClick={this._openHistoryDialog}
|
||||
title={_t("Edited at %(date)s. Click to view edits.", {date: dateString})}
|
||||
tooltip={tooltip}
|
||||
tooltipClassName="mx_Tooltip_timeline"
|
||||
>
|
||||
<span>{`(${_t("edited")})`}</span>
|
||||
</AccessibleTooltipButton>
|
||||
|
|
|
@ -22,7 +22,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import Analytics from '../../../Analytics';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
|
||||
export default class HeaderButton extends React.Component {
|
||||
constructor() {
|
||||
|
@ -42,13 +42,13 @@ export default class HeaderButton extends React.Component {
|
|||
[`mx_RightPanel_${this.props.name}`]: true,
|
||||
});
|
||||
|
||||
return <AccessibleButton
|
||||
return <AccessibleTooltipButton
|
||||
aria-selected={this.props.isHighlighted}
|
||||
role="tab"
|
||||
title={this.props.title}
|
||||
className={classes}
|
||||
onClick={this.onClick}>
|
||||
</AccessibleButton>;
|
||||
onClick={this.onClick}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@ import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks';
|
|||
import ContentMessages from '../../../ContentMessages';
|
||||
import E2EIcon from './E2EIcon';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {aboveLeftOf, ContextMenu, ContextMenuButton, useContextMenu} from "../../structures/ContextMenu";
|
||||
import {aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu} from "../../structures/ContextMenu";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
|
||||
function ComposerAvatar(props) {
|
||||
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
|
||||
|
@ -41,7 +42,6 @@ ComposerAvatar.propTypes = {
|
|||
};
|
||||
|
||||
function CallButton(props) {
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
const onVoiceCallClick = (ev) => {
|
||||
dis.dispatch({
|
||||
action: 'place_call',
|
||||
|
@ -50,7 +50,8 @@ function CallButton(props) {
|
|||
});
|
||||
};
|
||||
|
||||
return (<AccessibleButton className="mx_MessageComposer_button mx_MessageComposer_voicecall"
|
||||
return (<AccessibleTooltipButton
|
||||
className="mx_MessageComposer_button mx_MessageComposer_voicecall"
|
||||
onClick={onVoiceCallClick}
|
||||
title={_t('Voice call')}
|
||||
/>);
|
||||
|
@ -61,7 +62,6 @@ CallButton.propTypes = {
|
|||
};
|
||||
|
||||
function VideoCallButton(props) {
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
const onCallClick = (ev) => {
|
||||
dis.dispatch({
|
||||
action: 'place_call',
|
||||
|
@ -70,7 +70,8 @@ function VideoCallButton(props) {
|
|||
});
|
||||
};
|
||||
|
||||
return <AccessibleButton className="mx_MessageComposer_button mx_MessageComposer_videocall"
|
||||
return <AccessibleTooltipButton
|
||||
className="mx_MessageComposer_button mx_MessageComposer_videocall"
|
||||
onClick={onCallClick}
|
||||
title={_t('Video call')}
|
||||
/>;
|
||||
|
@ -117,14 +118,15 @@ const EmojiButton = ({addEmoji}) => {
|
|||
}
|
||||
|
||||
return <React.Fragment>
|
||||
<ContextMenuButton className="mx_MessageComposer_button mx_MessageComposer_emoji"
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_MessageComposer_button mx_MessageComposer_emoji"
|
||||
onClick={openMenu}
|
||||
isExpanded={menuDisplayed}
|
||||
label={_t('Emoji picker')}
|
||||
title={_t('Emoji picker')}
|
||||
inputRef={button}
|
||||
>
|
||||
|
||||
</ContextMenuButton>
|
||||
</ContextMenuTooltipButton>
|
||||
|
||||
{ contextMenu }
|
||||
</React.Fragment>;
|
||||
|
@ -185,9 +187,9 @@ class UploadButton extends React.Component {
|
|||
|
||||
render() {
|
||||
const uploadInputStyle = {display: 'none'};
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
return (
|
||||
<AccessibleButton className="mx_MessageComposer_button mx_MessageComposer_upload"
|
||||
<AccessibleTooltipButton
|
||||
className="mx_MessageComposer_button mx_MessageComposer_upload"
|
||||
onClick={this.onUploadClick}
|
||||
title={_t('Upload file')}
|
||||
>
|
||||
|
@ -198,7 +200,7 @@ class UploadButton extends React.Component {
|
|||
multiple
|
||||
onChange={this.onUploadFileInputChange}
|
||||
/>
|
||||
</AccessibleButton>
|
||||
</AccessibleTooltipButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
<AccessibleTooltipButton
|
||||
as="span"
|
||||
role="button"
|
||||
onClick={this.props.onClick}
|
||||
aria-label={this.props.label}
|
||||
title={this.props.label}
|
||||
tooltip={tooltip}
|
||||
className={className} />
|
||||
</InteractiveTooltip>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
|
|||
onClick={() => this.viewRoom(r, i)}
|
||||
aria-label={_t("Room %(name)s", {name: r.name})}
|
||||
title={r.name}
|
||||
tooltipClassName={"mx_RoomBreadcrumbs2_Tooltip"}
|
||||
tooltipClassName="mx_RoomBreadcrumbs2_Tooltip"
|
||||
>
|
||||
<DecoratedRoomAvatar
|
||||
room={r}
|
||||
|
|
|
@ -26,7 +26,6 @@ import Modal from "../../../Modal";
|
|||
import RateLimitedFunc from '../../../ratelimitedfunc';
|
||||
|
||||
import { linkifyElement } from '../../../HtmlUtils';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import ManageIntegsButton from '../elements/ManageIntegsButton';
|
||||
import {CancelButton} from './SimpleRoomHeader';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
@ -34,6 +33,7 @@ import RoomHeaderButtons from '../right_panel/RoomHeaderButtons';
|
|||
import E2EIcon from './E2EIcon';
|
||||
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
|
||||
import {DefaultTagID} from "../../../stores/room-list/models";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'RoomHeader',
|
||||
|
@ -220,11 +220,10 @@ export default createReactClass({
|
|||
|
||||
if (this.props.onSettingsClick) {
|
||||
settingsButton =
|
||||
<AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_settingsButton"
|
||||
<AccessibleTooltipButton
|
||||
className="mx_RoomHeader_button mx_RoomHeader_settingsButton"
|
||||
onClick={this.props.onSettingsClick}
|
||||
title={_t("Settings")}
|
||||
>
|
||||
</AccessibleButton>;
|
||||
title={_t("Settings")} />;
|
||||
}
|
||||
|
||||
if (this.props.onPinnedClick && SettingsStore.isFeatureEnabled('feature_pinning')) {
|
||||
|
@ -236,55 +235,45 @@ export default createReactClass({
|
|||
}
|
||||
|
||||
pinnedEventsButton =
|
||||
<AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_pinnedButton"
|
||||
onClick={this.props.onPinnedClick} title={_t("Pinned Messages")}>
|
||||
<AccessibleTooltipButton
|
||||
className="mx_RoomHeader_button mx_RoomHeader_pinnedButton"
|
||||
onClick={this.props.onPinnedClick}
|
||||
title={_t("Pinned Messages")}
|
||||
>
|
||||
{ pinsIndicator }
|
||||
</AccessibleButton>;
|
||||
</AccessibleTooltipButton>;
|
||||
}
|
||||
|
||||
// var leave_button;
|
||||
// if (this.props.onLeaveClick) {
|
||||
// leave_button =
|
||||
// <div className="mx_RoomHeader_button" onClick={this.props.onLeaveClick} title="Leave room">
|
||||
// <TintableSvg src={require("../../../../res/img/leave.svg")} width="26" height="20"/>
|
||||
// </div>;
|
||||
// }
|
||||
|
||||
let forgetButton;
|
||||
if (this.props.onForgetClick) {
|
||||
forgetButton =
|
||||
<AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_forgetButton"
|
||||
<AccessibleTooltipButton
|
||||
className="mx_RoomHeader_button mx_RoomHeader_forgetButton"
|
||||
onClick={this.props.onForgetClick}
|
||||
title={_t("Forget room")}
|
||||
>
|
||||
</AccessibleButton>;
|
||||
title={_t("Forget room")} />;
|
||||
}
|
||||
|
||||
let searchButton;
|
||||
if (this.props.onSearchClick && this.props.inRoom) {
|
||||
searchButton =
|
||||
<AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_searchButton"
|
||||
<AccessibleTooltipButton
|
||||
className="mx_RoomHeader_button mx_RoomHeader_searchButton"
|
||||
onClick={this.props.onSearchClick}
|
||||
title={_t("Search")}
|
||||
>
|
||||
</AccessibleButton>;
|
||||
title={_t("Search")} />;
|
||||
}
|
||||
|
||||
let shareRoomButton;
|
||||
if (this.props.inRoom) {
|
||||
shareRoomButton =
|
||||
<AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_shareButton"
|
||||
<AccessibleTooltipButton
|
||||
className="mx_RoomHeader_button mx_RoomHeader_shareButton"
|
||||
onClick={this.onShareRoomClick}
|
||||
title={_t('Share room')}
|
||||
>
|
||||
</AccessibleButton>;
|
||||
title={_t('Share room')} />;
|
||||
}
|
||||
|
||||
let manageIntegsButton;
|
||||
if (this.props.room && this.props.room.roomId && this.props.inRoom) {
|
||||
manageIntegsButton = <ManageIntegsButton
|
||||
room={this.props.room}
|
||||
/>;
|
||||
manageIntegsButton = <ManageIntegsButton room={this.props.room} />;
|
||||
}
|
||||
|
||||
const rightRow =
|
||||
|
|
|
@ -230,9 +230,6 @@ export default createReactClass({
|
|||
|
||||
onAction: function(payload) {
|
||||
switch (payload.action) {
|
||||
case 'view_tooltip':
|
||||
this.tooltip = payload.tooltip;
|
||||
break;
|
||||
case 'call_state':
|
||||
var call = CallHandler.getCall(payload.room_id);
|
||||
if (call && call.call_state === 'ringing') {
|
||||
|
@ -589,18 +586,6 @@ export default createReactClass({
|
|||
}
|
||||
},
|
||||
|
||||
_whenScrolling: function(e) {
|
||||
this._hideTooltip(e);
|
||||
this._repositionIncomingCallBox(e, false);
|
||||
},
|
||||
|
||||
_hideTooltip: function(e) {
|
||||
// Hide tooltip when scrolling, as we'll no longer be over the one we were on
|
||||
if (this.tooltip && this.tooltip.style.display !== "none") {
|
||||
this.tooltip.style.display = "none";
|
||||
}
|
||||
},
|
||||
|
||||
_repositionIncomingCallBox: function(e, firstTime) {
|
||||
const incomingCallBox = document.getElementById("incomingCallBox");
|
||||
if (incomingCallBox && incomingCallBox.parentElement) {
|
||||
|
|
|
@ -28,7 +28,7 @@ import { ListLayout } from "../../../stores/room-list/ListLayout";
|
|||
import {
|
||||
ChevronFace,
|
||||
ContextMenu,
|
||||
ContextMenuButton,
|
||||
ContextMenuTooltipButton,
|
||||
StyledMenuItemCheckbox,
|
||||
StyledMenuItemRadio,
|
||||
} from "../../structures/ContextMenu";
|
||||
|
@ -499,10 +499,10 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
|||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ContextMenuButton
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_RoomSublist2_menuButton"
|
||||
onClick={this.onOpenMenuClick}
|
||||
label={_t("List options")}
|
||||
title={_t("List options")}
|
||||
isExpanded={!!this.state.contextMenuPosition}
|
||||
/>
|
||||
{contextMenu}
|
||||
|
@ -561,6 +561,11 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
|||
</div>
|
||||
);
|
||||
|
||||
let Button: React.ComponentType<React.ComponentProps<typeof AccessibleButton>> = AccessibleButton;
|
||||
if (this.props.isMinimized) {
|
||||
Button = AccessibleTooltipButton;
|
||||
}
|
||||
|
||||
// Note: the addRoomButton conditionally gets moved around
|
||||
// the DOM depending on whether or not the list is minimized.
|
||||
// If we're minimized, we want it below the header so it
|
||||
|
@ -569,7 +574,7 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
|||
return (
|
||||
<div className={classes} onKeyDown={this.onHeaderKeyDown} onFocus={onFocus} aria-label={this.props.label}>
|
||||
<div className="mx_RoomSublist2_stickable">
|
||||
<AccessibleButton
|
||||
<Button
|
||||
onFocus={onFocus}
|
||||
inputRef={ref}
|
||||
tabIndex={tabIndex}
|
||||
|
@ -579,10 +584,11 @@ export default class RoomSublist2 extends React.Component<IProps, IState> {
|
|||
aria-level={1}
|
||||
onClick={this.onHeaderClick}
|
||||
onContextMenu={this.onContextMenu}
|
||||
title={this.props.isMinimized ? this.props.label : undefined}
|
||||
>
|
||||
<span className={collapseClasses} />
|
||||
<span>{this.props.label}</span>
|
||||
</AccessibleButton>
|
||||
</Button>
|
||||
{this.renderMenu()}
|
||||
{this.props.isMinimized ? null : badgeContainer}
|
||||
{this.props.isMinimized ? null : addRoomButton}
|
||||
|
|
|
@ -29,7 +29,7 @@ import { _t } from "../../../languageHandler";
|
|||
import {
|
||||
ChevronFace,
|
||||
ContextMenu,
|
||||
ContextMenuButton,
|
||||
ContextMenuTooltipButton,
|
||||
MenuItemRadio,
|
||||
MenuItemCheckbox,
|
||||
MenuItem,
|
||||
|
@ -54,6 +54,7 @@ import defaultDispatcher from "../../../dispatcher/dispatcher";
|
|||
import {ActionPayload} from "../../../dispatcher/payloads";
|
||||
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
|
||||
import { NotificationState } from "../../../stores/notifications/NotificationState";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
|
||||
// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367
|
||||
|
||||
|
@ -373,10 +374,10 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
|
|||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ContextMenuButton
|
||||
<ContextMenuTooltipButton
|
||||
className={classes}
|
||||
onClick={this.onNotificationsMenuOpenClick}
|
||||
label={_t("Notification options")}
|
||||
title={_t("Notification options")}
|
||||
isExpanded={!!this.state.notificationsMenuPosition}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
/>
|
||||
|
@ -441,10 +442,10 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
|
|||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ContextMenuButton
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_RoomTile2_menuButton"
|
||||
onClick={this.onGeneralMenuOpenClick}
|
||||
label={_t("Room options")}
|
||||
title={_t("Room options")}
|
||||
isExpanded={!!this.state.generalMenuPosition}
|
||||
/>
|
||||
{contextMenu}
|
||||
|
@ -537,11 +538,16 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
|
|||
ariaDescribedBy = messagePreviewId(this.props.room.roomId);
|
||||
}
|
||||
|
||||
let Button: React.ComponentType<React.ComponentProps<typeof AccessibleButton>> = AccessibleButton;
|
||||
if (this.props.isMinimized) {
|
||||
Button = AccessibleTooltipButton;
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<RovingTabIndexWrapper inputRef={this.roomTileRef}>
|
||||
{({onFocus, isActive, ref}) =>
|
||||
<AccessibleButton
|
||||
<Button
|
||||
onFocus={onFocus}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
inputRef={ref}
|
||||
|
@ -554,13 +560,14 @@ export default class RoomTile2 extends React.Component<IProps, IState> {
|
|||
aria-label={ariaLabel}
|
||||
aria-selected={this.state.selected}
|
||||
aria-describedby={ariaDescribedBy}
|
||||
title={this.props.isMinimized ? name : undefined}
|
||||
>
|
||||
{roomAvatar}
|
||||
{nameContainer}
|
||||
{badge}
|
||||
{this.renderGeneralMenu()}
|
||||
{this.renderNotificationsMenu(isActive)}
|
||||
</AccessibleButton>
|
||||
</Button>
|
||||
}
|
||||
</RovingTabIndexWrapper>
|
||||
</React.Fragment>
|
||||
|
|
|
@ -162,7 +162,6 @@ export default class RoomTileIcon extends React.Component<IProps, IState> {
|
|||
|
||||
return <TextWithTooltip
|
||||
tooltip={tooltipText(this.state.icon)}
|
||||
tooltipClass="mx_Tooltip_timeline"
|
||||
class={`mx_RoomTileIcon mx_RoomTileIcon_${this.state.icon.toLowerCase()}`}
|
||||
/>;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
|||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {ContextMenu} from "../../structures/ContextMenu";
|
||||
import {WidgetType} from "../../../widgets/WidgetType";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
|
||||
// This should be below the dialog level (4000), but above the rest of the UI (1000-2000).
|
||||
// We sit in a context menu, so this should be given to the context menu.
|
||||
|
@ -409,14 +410,14 @@ export default class Stickerpicker extends React.Component {
|
|||
} else {
|
||||
// Show show-stickers button
|
||||
stickersButton =
|
||||
<AccessibleButton
|
||||
<AccessibleTooltipButton
|
||||
id='stickersButton'
|
||||
key="controls_show_stickers"
|
||||
className="mx_MessageComposer_button mx_MessageComposer_stickers"
|
||||
onClick={this._onShowStickersClick}
|
||||
title={_t("Show Stickers")}
|
||||
>
|
||||
</AccessibleButton>;
|
||||
</AccessibleTooltipButton>;
|
||||
}
|
||||
return <React.Fragment>
|
||||
{ stickersButton }
|
||||
|
|
|
@ -16,7 +16,11 @@ limitations under the License.
|
|||
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import { RovingTabIndexWrapper } from "../../../accessibility/RovingTabIndex";
|
||||
import {
|
||||
RovingAccessibleButton,
|
||||
RovingAccessibleTooltipButton,
|
||||
RovingTabIndexWrapper
|
||||
} from "../../../accessibility/RovingTabIndex";
|
||||
import AccessibleButton from "../../views/elements/AccessibleButton";
|
||||
import NotificationBadge from "./NotificationBadge";
|
||||
import { NotificationState } from "../../../stores/notifications/NotificationState";
|
||||
|
@ -86,19 +90,20 @@ export default class TemporaryTile extends React.Component<IProps, IState> {
|
|||
);
|
||||
if (this.props.isMinimized) nameContainer = null;
|
||||
|
||||
let Button = RovingAccessibleButton;
|
||||
if (this.props.isMinimized) {
|
||||
Button = RovingAccessibleTooltipButton;
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<RovingTabIndexWrapper>
|
||||
{({onFocus, isActive, ref}) =>
|
||||
<AccessibleButton
|
||||
onFocus={onFocus}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
inputRef={ref}
|
||||
<Button
|
||||
className={classes}
|
||||
onMouseEnter={this.onTileMouseEnter}
|
||||
onMouseLeave={this.onTileMouseLeave}
|
||||
onClick={this.props.onClick}
|
||||
role="treeitem"
|
||||
title={this.props.isMinimized ? name : undefined}
|
||||
>
|
||||
<div className="mx_RoomTile2_avatarContainer">
|
||||
{this.props.avatar}
|
||||
|
@ -107,9 +112,7 @@ export default class TemporaryTile extends React.Component<IProps, IState> {
|
|||
<div className="mx_RoomTile2_badgeContainer">
|
||||
{badge}
|
||||
</div>
|
||||
</AccessibleButton>
|
||||
}
|
||||
</RovingTabIndexWrapper>
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -45,11 +45,6 @@ export enum Action {
|
|||
*/
|
||||
ViewRoomDirectory = "view_room_directory",
|
||||
|
||||
/**
|
||||
* Sets the current tooltip. Should be use with ViewTooltipPayload.
|
||||
*/
|
||||
ViewTooltip = "view_tooltip",
|
||||
|
||||
/**
|
||||
* Forces the theme to reload. No additional payload information required.
|
||||
*/
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 { ActionPayload } from "../payloads";
|
||||
import { Action } from "../actions";
|
||||
import { Component } from "react";
|
||||
|
||||
export interface ViewTooltipPayload extends ActionPayload {
|
||||
action: Action.ViewTooltip;
|
||||
|
||||
/*
|
||||
* The tooltip to render. If it's null the tooltip will not be rendered
|
||||
* We need the void type because of typescript headaches.
|
||||
*/
|
||||
tooltip: null | void | Element | Component<Element, any, any>;
|
||||
|
||||
/*
|
||||
* The parent under which to render the tooltip. Can be null to remove
|
||||
* the parent type.
|
||||
*/
|
||||
parent: null | Element;
|
||||
}
|
|
@ -45,7 +45,7 @@ async function findTabs(session) {
|
|||
/// XXX delay is needed here, possibly because the header is being rerendered
|
||||
/// click doesn't do anything otherwise
|
||||
await session.delay(1000);
|
||||
const settingsButton = await session.query(".mx_RoomHeader .mx_AccessibleButton[title=Settings]");
|
||||
const settingsButton = await session.query(".mx_RoomHeader .mx_AccessibleButton[aria-label=Settings]");
|
||||
await settingsButton.click();
|
||||
|
||||
//find tabs
|
||||
|
|
Loading…
Reference in a new issue