diff --git a/web/chat/composed-message.react.js b/web/chat/composed-message.react.js --- a/web/chat/composed-message.react.js +++ b/web/chat/composed-message.react.js @@ -26,11 +26,11 @@ import { tooltipPositions } from './tooltip-utils'; const availableTooltipPositionsForViewerMessage = [ - tooltipPositions.TOP_RIGHT, + tooltipPositions.RIGHT_TOP, tooltipPositions.LEFT, ]; const availableTooltipPositionsForNonViewerMessage = [ - tooltipPositions.TOP_LEFT, + tooltipPositions.LEFT_TOP, tooltipPositions.RIGHT, ]; diff --git a/web/chat/message-timestamp-tooltip.react.js b/web/chat/message-timestamp-tooltip.react.js --- a/web/chat/message-timestamp-tooltip.react.js +++ b/web/chat/message-timestamp-tooltip.react.js @@ -21,7 +21,7 @@ const availablePositionsForComposedViewerMessage = [tooltipPositions.LEFT]; const availablePositionsForNonComposedOrNonViewerMessage = [ - tooltipPositions.BOTTOM_RIGHT, + tooltipPositions.RIGHT_BOTTOM, ]; type Props = { @@ -99,7 +99,7 @@ top: tooltipPointing, }; className = css.messageRightTooltip; - } else if (tooltipPosition === tooltipPositions.TOP_LEFT) { + } else if (tooltipPosition === tooltipPositions.LEFT_TOP) { const tooltipPointing = Math.min( containerHeight - messagePosition.top, containerHeight, @@ -109,7 +109,7 @@ bottom: tooltipPointing + sizeOfTooltipArrow, }; className = css.messageTopLeftTooltip; - } else if (tooltipPosition === tooltipPositions.TOP_RIGHT) { + } else if (tooltipPosition === tooltipPositions.RIGHT_TOP) { const tooltipPointing = Math.min( containerHeight - messagePosition.top, containerHeight, @@ -119,14 +119,14 @@ bottom: tooltipPointing + sizeOfTooltipArrow, }; className = css.messageTopRightTooltip; - } else if (tooltipPosition === tooltipPositions.BOTTOM_LEFT) { + } else if (tooltipPosition === tooltipPositions.LEFT_BOTTOM) { const tooltipPointing = Math.min(messagePosition.bottom, containerHeight); style = { left: messagePosition.left, top: tooltipPointing + sizeOfTooltipArrow, }; className = css.messageBottomLeftTooltip; - } else if (tooltipPosition === tooltipPositions.BOTTOM_RIGHT) { + } else if (tooltipPosition === tooltipPositions.RIGHT_BOTTOM) { const centerOfMessage = messagePosition.top + messagePosition.height / 2; const tooltipPointing = Math.max( Math.min(centerOfMessage, containerHeight), diff --git a/web/chat/robotext-message.react.js b/web/chat/robotext-message.react.js --- a/web/chat/robotext-message.react.js +++ b/web/chat/robotext-message.react.js @@ -26,7 +26,7 @@ // eslint-disable-next-line no-unused-vars const availableTooltipPositionsForRobotext = [ - tooltipPositions.TOP_RIGHT, + tooltipPositions.RIGHT_TOP, tooltipPositions.RIGHT, tooltipPositions.LEFT, ]; diff --git a/web/chat/tooltip-utils.js b/web/chat/tooltip-utils.js --- a/web/chat/tooltip-utils.js +++ b/web/chat/tooltip-utils.js @@ -1,20 +1,25 @@ // @flow -import invariant from 'invariant'; import * as React from 'react'; -import { calculateMaxTextWidth } from '../utils/text-utils'; -import type { ItemAndContainerPositionInfo } from './position-types'; +import type { PositionInfo } from './position-types'; export const tooltipPositions = Object.freeze({ LEFT: 'left', RIGHT: 'right', - BOTTOM_LEFT: 'bottom-left', - BOTTOM_RIGHT: 'bottom-right', - TOP_LEFT: 'top-left', - TOP_RIGHT: 'top-right', + LEFT_BOTTOM: 'left-bottom', + RIGHT_BOTTOM: 'right-bottom', + LEFT_TOP: 'left-top', + RIGHT_TOP: 'right-top', + TOP: 'top', + BOTTOM: 'bottom', }); +type TooltipSize = { + +height: number, + +width: number, +}; + export type TooltipPositionStyle = { xCoord: number, yCoord: number, @@ -32,122 +37,139 @@ }; const sizeOfTooltipArrow = 10; // 7px arrow + 3px extra -const tooltipMenuItemHeight = 22; // 17px line-height + 5px padding bottom -const tooltipInnerTopPadding = 5; // 5px bottom is included in last item -const tooltipInnerPadding = 10; +const appTopBarHeight = 65; +// eslint-disable-next-line no-unused-vars const font = '14px -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", ' + '"Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", ui-sans-serif'; type FindTooltipPositionArgs = { - +pointingToInfo: ItemAndContainerPositionInfo, - +tooltipTexts: $ReadOnlyArray, + +sourcePositionInfo: PositionInfo, + +tooltipSize: TooltipSize, +availablePositions: $ReadOnlyArray, - +layoutPosition: 'relative' | 'absolute', + +preventDisplayingBelowSource?: boolean, }; + function findTooltipPosition({ - pointingToInfo, - tooltipTexts, + sourcePositionInfo, + tooltipSize, availablePositions, - layoutPosition, -}: FindTooltipPositionArgs): TooltipPosition { - const { itemPosition: pointingTo, containerPosition } = pointingToInfo; + preventDisplayingBelowSource, +}: FindTooltipPositionArgs): ?TooltipPosition { + if (!window) { + return null; + } + const appContainerPositionInfo: PositionInfo = { + height: window.innerHeight - appTopBarHeight, + width: window.innerWidth, + top: appTopBarHeight, + bottom: window.innerHeight, + left: 0, + right: window.innerWidth, + }; + + const pointingTo = sourcePositionInfo; const { - height: containerHeight, top: containerTop, - width: containerWidth, left: containerLeft, - } = containerPosition; - - const textWidth = calculateMaxTextWidth(tooltipTexts, font); - const width = textWidth + tooltipInnerPadding + sizeOfTooltipArrow; - const numberOfTooltipItems = tooltipTexts.length; - const tooltipHeight = - numberOfTooltipItems * tooltipMenuItemHeight + tooltipInnerTopPadding; - const heightWithArrow = tooltipHeight + sizeOfTooltipArrow; - - const absolutePositionedTooltip = layoutPosition === 'absolute'; - - let canBeDisplayedInLeftPosition, - canBeDisplayedInRightPosition, - canBeDisplayedInTopPosition, - canBeDisplayedInBottomPosition; - if (absolutePositionedTooltip) { - const pointingCenter = pointingTo.top + pointingTo.height / 2; - const currentTop = Math.max(pointingTo.top, 0); - const currentBottom = Math.min(pointingTo.bottom, containerHeight); - const currentPointing = Math.max( - Math.min(pointingCenter, containerHeight), - 0, - ); - const canBeDisplayedSideways = - currentPointing - tooltipHeight / 2 + containerTop >= 0 && - currentPointing + tooltipHeight / 2 + containerTop <= window.innerHeight; - - canBeDisplayedInLeftPosition = - pointingTo.left - width + containerLeft >= 0 && canBeDisplayedSideways; - canBeDisplayedInRightPosition = - pointingTo.right + width + containerLeft <= window.innerWidth && - canBeDisplayedSideways; - canBeDisplayedInTopPosition = - currentTop - heightWithArrow + containerTop >= 0; - canBeDisplayedInBottomPosition = - currentBottom + heightWithArrow + containerTop <= window.innerHeight; - } else { - const canBeDisplayedSideways = - pointingTo.top - (tooltipHeight - pointingTo.height) / 2 >= 0 && - pointingTo.bottom + (tooltipHeight - pointingTo.height) / 2 <= - containerHeight; - canBeDisplayedInLeftPosition = - pointingTo.left - width >= 0 && canBeDisplayedSideways; - canBeDisplayedInRightPosition = - pointingTo.right + width <= containerWidth && canBeDisplayedSideways; - canBeDisplayedInTopPosition = pointingTo.top - heightWithArrow >= 0; - canBeDisplayedInBottomPosition = - pointingTo.bottom + heightWithArrow <= containerHeight; - } + right: containerRight, + bottom: containerBottom, + } = appContainerPositionInfo; + + const tooltipWidth = tooltipSize.width; + const tooltipHeight = tooltipSize.height; + + const canBeDisplayedOnLeft = containerLeft + tooltipWidth <= pointingTo.left; + const canBeDisplayedOnRight = + tooltipWidth + pointingTo.right <= containerRight; + + const willCoverSidebarOnTopSideways = + preventDisplayingBelowSource && + pointingTo.top + tooltipHeight > pointingTo.bottom; + + const canBeDisplayedOnTopSideways = + pointingTo.top >= containerTop && + pointingTo.top + tooltipHeight <= containerBottom && + !willCoverSidebarOnTopSideways; + + const canBeDisplayedOnBottomSideways = + pointingTo.bottom <= containerBottom && + pointingTo.bottom - tooltipHeight >= containerTop; + + const verticalCenterOfPointingTo = pointingTo.top + pointingTo.height / 2; + const horizontalCenterOfPointingTo = pointingTo.left + pointingTo.width / 2; + + const willCoverSidebarInTheMiddleSideways = + preventDisplayingBelowSource && + verticalCenterOfPointingTo + tooltipHeight / 2 > pointingTo.bottom; + + const canBeDisplayedInTheMiddleSideways = + verticalCenterOfPointingTo - tooltipHeight / 2 >= containerTop && + verticalCenterOfPointingTo + tooltipHeight / 2 <= containerBottom && + !willCoverSidebarInTheMiddleSideways; + + const canBeDisplayedOnTop = + pointingTo.top - tooltipHeight >= containerTop && + horizontalCenterOfPointingTo - tooltipWidth / 2 >= containerLeft && + horizontalCenterOfPointingTo + tooltipWidth / 2 <= containerRight; + + const canBeDisplayedOnBottom = + pointingTo.bottom + tooltipHeight <= containerBottom && + horizontalCenterOfPointingTo - tooltipWidth / 2 >= containerLeft && + horizontalCenterOfPointingTo + tooltipWidth / 2 <= containerRight && + !preventDisplayingBelowSource; for (const tooltipPosition of availablePositions) { - invariant( - numberOfTooltipItems === 1 || - (tooltipPosition !== tooltipPositions.RIGHT && - tooltipPosition !== tooltipPositions.LEFT), - `${tooltipPosition} position can be used only for single element tooltip`, - ); if ( tooltipPosition === tooltipPositions.RIGHT && - canBeDisplayedInRightPosition + canBeDisplayedOnRight && + canBeDisplayedInTheMiddleSideways ) { return tooltipPosition; } else if ( - tooltipPosition === tooltipPositions.BOTTOM_RIGHT && - canBeDisplayedInBottomPosition + tooltipPosition === tooltipPositions.RIGHT_BOTTOM && + canBeDisplayedOnRight && + canBeDisplayedOnBottomSideways ) { return tooltipPosition; } else if ( tooltipPosition === tooltipPositions.LEFT && - canBeDisplayedInLeftPosition + canBeDisplayedOnLeft && + canBeDisplayedInTheMiddleSideways + ) { + return tooltipPosition; + } else if ( + tooltipPosition === tooltipPositions.LEFT_BOTTOM && + canBeDisplayedOnLeft && + canBeDisplayedOnBottomSideways + ) { + return tooltipPosition; + } else if ( + tooltipPosition === tooltipPositions.LEFT_TOP && + canBeDisplayedOnLeft && + canBeDisplayedOnTopSideways ) { return tooltipPosition; } else if ( - tooltipPosition === tooltipPositions.BOTTOM_LEFT && - canBeDisplayedInBottomPosition + tooltipPosition === tooltipPositions.RIGHT_TOP && + canBeDisplayedOnRight && + canBeDisplayedOnTopSideways ) { return tooltipPosition; } else if ( - tooltipPosition === tooltipPositions.TOP_LEFT && - canBeDisplayedInTopPosition + tooltipPosition === tooltipPositions.TOP && + canBeDisplayedOnTop ) { return tooltipPosition; } else if ( - tooltipPosition === tooltipPositions.TOP_RIGHT && - canBeDisplayedInTopPosition + tooltipPosition === tooltipPositions.BOTTOM && + canBeDisplayedOnBottom ) { return tooltipPosition; } } - return availablePositions[availablePositions.length - 1]; + return null; } export { findTooltipPosition, sizeOfTooltipArrow }; diff --git a/web/chat/tooltip.react.js b/web/chat/tooltip.react.js --- a/web/chat/tooltip.react.js +++ b/web/chat/tooltip.react.js @@ -4,7 +4,7 @@ import * as React from 'react'; import type { ItemAndContainerPositionInfo } from './position-types'; -import { findTooltipPosition, type TooltipPosition } from './tooltip-utils'; +import { type TooltipPosition, tooltipPositions } from './tooltip-utils'; import css from './tooltip.css'; type Style = { @@ -25,34 +25,15 @@ >, }; function TooltipMenu(props: TooltipMenuProps): React.Node { - const { - availableTooltipPositions, - targetPositionInfo, - layoutPosition, - getStyle, - children, - } = props; + const { getStyle, children } = props; + // eslint-disable-next-line no-unused-vars const tooltipTexts = React.useMemo( () => React.Children.map(children, item => item.props.text), [children], ); - const tooltipPosition = React.useMemo( - () => - findTooltipPosition({ - pointingToInfo: targetPositionInfo, - tooltipTexts, - availablePositions: availableTooltipPositions, - layoutPosition, - }), - [ - tooltipTexts, - targetPositionInfo, - availableTooltipPositions, - layoutPosition, - ], - ); + const tooltipPosition = React.useMemo(() => tooltipPositions.LEFT, []); const tooltipStyle = React.useMemo(() => getStyle(tooltipPosition), [ getStyle,