Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Improve tooltip positioning
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Weimann <[email protected]>
  • Loading branch information
weeman1337 committed May 12, 2022
1 parent 1e73184 commit 7ed3089
Show file tree
Hide file tree
Showing 16 changed files with 112 additions and 68 deletions.
4 changes: 3 additions & 1 deletion res/css/views/elements/_MiniAvatarUploader.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ limitations under the License.
z-index: unset;
width: max-content;
left: 72px;
top: 0;
// top edge starting at 50 % of parent - 50 % of itself -> centered vertically
top: 50%;
transform: translateY(-50%);
}

.mx_MiniAvatarUploader_indicator {
Expand Down
2 changes: 0 additions & 2 deletions res/css/views/elements/_Tooltip.scss
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ limitations under the License.
font-weight: 500;
max-width: 300px;
word-break: break-word;
margin-left: 6px;
margin-right: 6px;

background-color: #21262C; // Same on both themes
color: $accent-fg-color;
Expand Down
4 changes: 0 additions & 4 deletions res/css/views/rooms/_RoomSublist.scss
Original file line number Diff line number Diff line change
Expand Up @@ -406,10 +406,6 @@ limitations under the License.
}
}

.mx_RoomSublist_addRoomTooltip {
margin-top: -3px;
}

.mx_RoomSublist_skeletonUI {
position: relative;
margin-left: 4px;
Expand Down
3 changes: 2 additions & 1 deletion src/components/structures/SpaceHierarchy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
import { JoinRoomReadyPayload } from "../../dispatcher/payloads/JoinRoomReadyPayload";
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
import { getKeyBindingsManager } from "../../KeyBindingsManager";
import { Alignment } from "../views/elements/Tooltip";

interface IProps {
space: Room;
Expand Down Expand Up @@ -583,7 +584,7 @@ const ManageButtons = ({ hierarchy, selected, setSelected, setError }: IManageBu
Button = AccessibleTooltipButton;
props = {
tooltip: _t("Select a room below first"),
yOffset: -40,
alignment: Alignment.Top,
};
}

Expand Down
1 change: 0 additions & 1 deletion src/components/views/beta/BetaCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export const BetaPill = ({
</div>
</div>}
onClick={onClick}
yOffset={-10}
>
{ _t("Beta") }
</AccessibleTooltipButton>;
Expand Down
2 changes: 0 additions & 2 deletions src/components/views/dialogs/ForwardDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ const Entry: React.FC<IEntryProps> = ({ room, type, content, matrixClient: cli,
className="mx_ForwardList_roomButton"
onClick={jumpToRoom}
title={_t("Open room")}
yOffset={-20}
alignment={Alignment.Top}
>
<DecoratedRoomAvatar room={room} avatarSize={32} />
Expand All @@ -151,7 +150,6 @@ const Entry: React.FC<IEntryProps> = ({ room, type, content, matrixClient: cli,
onClick={send}
disabled={disabled}
title={title}
yOffset={-20}
alignment={Alignment.Top}
>
<div className="mx_ForwardList_sendLabel">{ _t("Send") }</div>
Expand Down
4 changes: 1 addition & 3 deletions src/components/views/elements/AccessibleTooltipButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ interface IProps extends React.ComponentProps<typeof AccessibleButton> {
label?: string;
tooltipClassName?: string;
forceHide?: boolean;
yOffset?: number;
alignment?: Alignment;
onHover?: (hovering: boolean) => void;
onHideTooltip?(ev: SyntheticEvent): void;
Expand Down Expand Up @@ -76,13 +75,12 @@ export default class AccessibleTooltipButton extends React.PureComponent<IProps,

render() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { title, tooltip, children, tooltipClassName, forceHide, yOffset, alignment, onHideTooltip,
const { title, tooltip, children, tooltipClassName, forceHide, alignment, onHideTooltip,
...props } = this.props;

const tip = this.state.hover && <Tooltip
tooltipClassName={tooltipClassName}
label={tooltip || title}
yOffset={yOffset}
alignment={alignment}
/>;
return (
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/elements/FacePile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const FacePile: FC<IProps> = ({ members, faceSize, overflow, tooltip, children,

return <div {...props} className="mx_FacePile">
{ tooltip ? (
<TextWithTooltip class="mx_FacePile_faces" tooltip={tooltip} tooltipProps={{ yOffset: 32 }}>
<TextWithTooltip class="mx_FacePile_faces" tooltip={tooltip}>
{ pileContents }
</TextWithTooltip>
) : (
Expand Down
48 changes: 19 additions & 29 deletions src/components/views/elements/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import classNames from 'classnames';

import UIStore from "../../../stores/UIStore";

const MIN_TOOLTIP_HEIGHT = 25;

export enum Alignment {
Natural, // Pick left or right
Left,
Expand All @@ -33,7 +31,6 @@ export enum Alignment {
Bottom, // Centered
InnerBottom, // Inside the target, at the bottom
TopRight, // On top of the target, right aligned
TopCenter, // On top of the target, center aligned
}

export interface ITooltipProps {
Expand All @@ -48,7 +45,6 @@ export interface ITooltipProps {
// the react element to put into the tooltip
label: React.ReactNode;
alignment?: Alignment; // defaults to Natural
yOffset?: number;
// id describing tooltip
// used to associate tooltip with target for a11y
id?: string;
Expand All @@ -66,7 +62,6 @@ export default class Tooltip extends React.Component<ITooltipProps> {

public static readonly defaultProps = {
visible: true,
yOffset: 0,
alignment: Alignment.Natural,
};

Expand Down Expand Up @@ -102,65 +97,60 @@ export default class Tooltip extends React.Component<ITooltipProps> {
// positioned, also taking into account any window zoom
private updatePosition(style: CSSProperties) {
const parentBox = this.parent.getBoundingClientRect();
let offset = 0;
if (parentBox.height > MIN_TOOLTIP_HEIGHT) {
offset = Math.floor((parentBox.height - MIN_TOOLTIP_HEIGHT) / 2);
} else {
// The tooltip is larger than the parent height: figure out what offset
// we need so that we're still centered.
offset = Math.floor(parentBox.height - MIN_TOOLTIP_HEIGHT);
}
const width = UIStore.instance.windowWidth;
const spacing = 6;
const parentWidth = (
this.props.maxParentWidth
? Math.min(parentBox.width, this.props.maxParentWidth)
: parentBox.width
);
const baseTop = (parentBox.top - 2 + this.props.yOffset) + window.scrollY;
const top = baseTop + offset;
const baseTop = parentBox.top + window.scrollY;
const centerTop = parentBox.top + window.scrollY + (parentBox.height / 2);
const right = width - parentBox.left - window.scrollX;
const left = parentBox.right + window.scrollX;
const horizontalCenter = (
parentBox.left - window.scrollX + (parentWidth / 2)
);

switch (this.props.alignment) {
case Alignment.Natural:
if (parentBox.right > width / 2) {
style.right = right;
style.top = top;
style.right = right + spacing;
style.top = centerTop;
style.transform = "translateY(-50%)";
break;
}
// fall through to Right
case Alignment.Right:
style.left = left;
style.top = top;
style.left = left + spacing;
style.top = centerTop;
style.transform = "translateY(-50%)";
break;
case Alignment.Left:
style.right = right;
style.top = top;
style.right = right + spacing;
style.top = centerTop;
style.transform = "translateY(-50%)";
break;
case Alignment.Top:
style.top = baseTop - 16;
style.top = baseTop - spacing;
style.left = horizontalCenter;
style.transform = "translate(-50%, -100%)";
break;
case Alignment.Bottom:
style.top = baseTop + parentBox.height;
style.top = baseTop + parentBox.height + spacing;
style.left = horizontalCenter;
style.transform = "translate(-50%)";
break;
case Alignment.InnerBottom:
style.top = baseTop + parentBox.height - 50;
style.left = horizontalCenter;
style.transform = "translate(-50%)";
break;
case Alignment.TopRight:
style.top = baseTop - 5;
style.top = baseTop - spacing;
style.right = width - parentBox.right - window.scrollX;
style.transform = "translate(5px, -100%)";
style.transform = "translateY(-100%)";
break;
case Alignment.TopCenter:
style.top = baseTop - 5;
style.left = horizontalCenter;
style.transform = "translate(-50%, -100%)";
}

return style;
Expand Down
2 changes: 0 additions & 2 deletions src/components/views/elements/TooltipTarget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const TooltipTarget: React.FC<IProps> = ({
id,
label,
alignment,
yOffset,
tooltipClassName,
maxParentWidth,
...rest
Expand All @@ -51,7 +50,6 @@ const TooltipTarget: React.FC<IProps> = ({
className={className}
tooltipClassName={tooltipClassName}
label={label}
yOffset={yOffset}
alignment={alignment}
visible={isVisible}
maxParentWidth={maxParentWidth}
Expand Down
4 changes: 0 additions & 4 deletions src/components/views/right_panel/RoomSummaryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
title={openTitle}
forceHide={!(isPinned || isMaximised)}
disabled={isPinned || isMaximised}
yOffset={-48}
>
<WidgetAvatar app={app} />
<span>{ name }</span>
Expand All @@ -178,21 +177,18 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
isExpanded={menuDisplayed}
onClick={openMenu}
title={_t("Options")}
yOffset={-24}
/> }

<AccessibleTooltipButton
className="mx_RoomSummaryCard_app_pinToggle"
onClick={togglePin}
title={pinTitle}
disabled={cannotPin}
yOffset={-24}
/>
<AccessibleTooltipButton
className="mx_RoomSummaryCard_app_maximiseToggle"
onClick={toggleMaximised}
title={maximiseTitle}
yOffset={-24}
/>

{ contextMenu }
Expand Down
1 change: 0 additions & 1 deletion src/components/views/rooms/MessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,6 @@ export default class MessageComposer extends React.Component<IProps, IState> {
recordingTooltip = <Tooltip
label={_t("%(seconds)ss left", { seconds: secondsLeft })}
alignment={Alignment.Top}
yOffset={-50}
/>;
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/views/rooms/ReadReceiptGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ interface ReadReceiptPersonProps extends IReadReceiptProps {

function ReadReceiptPerson({ userId, roomMember, ts, isTwelveHour, onAfterClick }: ReadReceiptPersonProps) {
const [{ showTooltip, hideTooltip }, tooltip] = useTooltip({
alignment: Alignment.TopCenter,
alignment: Alignment.Top,
tooltipClassName: "mx_ReadReceiptGroup_person--tooltip",
label: (
<>
Expand Down
6 changes: 0 additions & 6 deletions src/components/views/voip/CallView/CallViewButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ import { MediaDeviceKindEnum } from "../../../../MediaDeviceHandler";
// height to get the max height of the video
const CONTEXT_MENU_VPADDING = 8; // How far the context menu sits above the button (px)

const TOOLTIP_Y_OFFSET = -24;

const CONTROLS_HIDE_DELAY = 2000;

interface IButtonProps extends Omit<React.ComponentProps<typeof AccessibleTooltipButton>, "title"> {
Expand Down Expand Up @@ -69,7 +67,6 @@ const CallViewToggleButton: React.FC<IButtonProps> = ({
className={classes}
title={isOn ? onLabel : offLabel}
alignment={Alignment.Top}
yOffset={TOOLTIP_Y_OFFSET}
{...props}
>
{ children }
Expand Down Expand Up @@ -267,7 +264,6 @@ export default class CallViewButtons extends React.Component<IProps, IState> {
isExpanded={this.state.showDialpad}
title={_t("Dialpad")}
alignment={Alignment.Top}
yOffset={TOOLTIP_Y_OFFSET}
/> }
<CallViewDropdownButton
state={!this.props.buttonsState.micMuted}
Expand Down Expand Up @@ -306,14 +302,12 @@ export default class CallViewButtons extends React.Component<IProps, IState> {
isExpanded={this.state.showMoreMenu}
title={_t("More")}
alignment={Alignment.Top}
yOffset={TOOLTIP_Y_OFFSET}
/> }
<AccessibleTooltipButton
className="mx_CallViewButtons_button mx_CallViewButtons_button_hangup"
onClick={this.props.handlers.onHangupClick}
title={_t("Hangup")}
alignment={Alignment.Top}
yOffset={TOOLTIP_Y_OFFSET}
/>
</div>
);
Expand Down
19 changes: 11 additions & 8 deletions test/components/views/elements/TooltipTarget-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ describe('<TooltipTarget />', () => {
"className": 'test className',
"tooltipClassName": 'test tooltipClassName',
"label": 'test label',
"yOffset": 1,
"alignment": Alignment.Left,
"id": 'test id',
'data-test-id': 'test',
Expand Down Expand Up @@ -64,13 +63,17 @@ describe('<TooltipTarget />', () => {
expect(getVisibleTooltip()).toBeFalsy();
});

it('displays tooltip on mouseover', () => {
const wrapper = getComponent();
act(() => {
Simulate.mouseOver(wrapper);
});
expect(getVisibleTooltip()).toMatchSnapshot();
});
for (const alignment in Alignment) {
if (isNaN(Number(alignment))) {
it(`displays ${alignment} aligned tooltip on mouseover`, () => {
const wrapper = getComponent({ alignment: Alignment[alignment] });
act(() => {
Simulate.mouseOver(wrapper);
});
expect(getVisibleTooltip()).toMatchSnapshot();
});
}
}

it('hides tooltip on mouseleave', () => {
const wrapper = getComponent();
Expand Down
Loading

0 comments on commit 7ed3089

Please sign in to comment.