[MM-62390] Combine the floating and transition styles into one in WithTooltip and other related minor improvements (#29730)

Этот коммит содержится в:
M-ZubairAhmed
2025-01-07 18:40:01 +05:30
коммит произвёл GitHub
родитель 06ce7b4a5a
Коммит e0e24aaa6f
19 изменённых файлов: 158 добавлений и 162 удалений

Просмотреть файл

@@ -64,7 +64,6 @@ export function SendPostOptions({disabled, onSelect, channelId}: Props) {
return ( return (
<Menu.Container <Menu.Container
menuButtonTooltip={{ menuButtonTooltip={{
id: 'send_post_option_schedule_post',
text: formatMessage({ text: formatMessage({
id: 'create_post_button.option.schedule_message', id: 'create_post_button.option.schedule_message',
defaultMessage: 'Schedule message', defaultMessage: 'Schedule message',

Просмотреть файл

@@ -195,7 +195,8 @@ export default class AnnouncementBar extends React.PureComponent<Props, State> {
barContent = ( barContent = (
<WithTooltip <WithTooltip
title={this.props.tooltipMsg ? this.props.tooltipMsg : message} title={this.props.tooltipMsg ? this.props.tooltipMsg : message}
tooltipContentContainerClassName='announcementBarTooltip' className='announcementBarTooltip'
delayClose={true}
> >
{barContent} {barContent}

Просмотреть файл

@@ -1,5 +1,5 @@
.announcementBarTooltip { .announcementBarTooltip {
min-width: 50vw; min-width: 50vw;
max-width: 100vw; max-width: 90vw;
pointer-events: auto; pointer-events: auto;
} }

Просмотреть файл

@@ -82,7 +82,6 @@ function BookmarksMenu({
id: 'channelBookmarksPlusMenuDropdown', id: 'channelBookmarksPlusMenuDropdown',
}} }}
menuButtonTooltip={addBookmarkTooltipText ? { menuButtonTooltip={addBookmarkTooltipText ? {
id: 'channelBookmarksPlusMenuButtonTooltip',
text: addBookmarkTooltipText, text: addBookmarkTooltipText,
} : undefined} } : undefined}
> >

Просмотреть файл

@@ -352,7 +352,6 @@ exports[`components/dot_menu/DotMenu should match snapshot, on Center 1`] = `
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "PostDotMenu-ButtonTooltip-post_id_1",
"text": "More", "text": "More",
} }
} }

Просмотреть файл

@@ -507,7 +507,6 @@ export class DotMenuClass extends React.PureComponent<Props, State> {
onToggle: this.handleMenuToggle, onToggle: this.handleMenuToggle,
}} }}
menuButtonTooltip={{ menuButtonTooltip={{
id: `PostDotMenu-ButtonTooltip-${this.props.post.id}`,
text: formatMessage({id: 'post_info.dot_menu.tooltip.more', defaultMessage: 'More'}), text: formatMessage({id: 'post_info.dot_menu.tooltip.more', defaultMessage: 'More'}),
class: 'hidden-xs', class: 'hidden-xs',
}} }}

Просмотреть файл

@@ -44,7 +44,6 @@ type MenuButtonProps = {
} }
type MenuButtonTooltipProps = { type MenuButtonTooltipProps = {
id: string;
isVertical?: boolean; isVertical?: boolean;
class?: string; class?: string;
text: string; text: string;

Просмотреть файл

@@ -85,13 +85,7 @@ export function ProfilePopoverController<TriggerComponentType = HTMLSpanElement>
middleware: [autoPlacement()], middleware: [autoPlacement()],
}); });
const {isMounted, styles: transitionStyles} = useTransitionStyles(floatingContext, { const {isMounted, styles: transitionStyles} = useTransitionStyles(floatingContext, TRANSITION_STYLE_PROPS);
duration: {
open: OverlaysTimings.FADE_IN_DURATION,
close: OverlaysTimings.FADE_OUT_DURATION,
},
initial: OverlayTransitionStyles.START,
});
const clickInteractions = useClick(floatingContext); const clickInteractions = useClick(floatingContext);
const dismissInteraction = useDismiss(floatingContext); const dismissInteraction = useDismiss(floatingContext);
@@ -154,3 +148,11 @@ export function ProfilePopoverController<TriggerComponentType = HTMLSpanElement>
</> </>
); );
} }
const TRANSITION_STYLE_PROPS = {
duration: {
open: OverlaysTimings.FADE_IN_DURATION,
close: OverlaysTimings.FADE_OUT_DURATION,
},
initial: OverlayTransitionStyles.START,
};

Просмотреть файл

@@ -25,7 +25,6 @@ exports[`components/sidebar/sidebar_category/sidebar_category_sorting_menu shoul
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "SidebarCategorySortingMenu-ButtonTooltip-category_id",
"text": "Category options", "text": "Category options",
} }
} }

Просмотреть файл

@@ -47,7 +47,6 @@ const SidebarCategoryGenericMenu = ({
children: <DotsVerticalIcon size={16}/>, children: <DotsVerticalIcon size={16}/>,
}} }}
menuButtonTooltip={{ menuButtonTooltip={{
id: `SidebarCategoryMenu-ButtonTooltip-${id}`,
text: formatMessage({id: 'sidebar_left.sidebar_category_menu.editCategory', defaultMessage: 'Category options'}), text: formatMessage({id: 'sidebar_left.sidebar_category_menu.editCategory', defaultMessage: 'Category options'}),
class: 'hidden-xs', class: 'hidden-xs',
}} }}

Просмотреть файл

@@ -198,7 +198,6 @@ const SidebarCategorySortingMenu = ({
children: <DotsVerticalIcon size={16}/>, children: <DotsVerticalIcon size={16}/>,
}} }}
menuButtonTooltip={{ menuButtonTooltip={{
id: `SidebarCategorySortingMenu-ButtonTooltip-${category.id}`,
text: formatMessage({id: 'sidebar_left.sidebar_category_menu.editCategory', defaultMessage: 'Category options'}), text: formatMessage({id: 'sidebar_left.sidebar_category_menu.editCategory', defaultMessage: 'Category options'}),
class: 'hidden-xs', class: 'hidden-xs',
}} }}

Просмотреть файл

@@ -22,7 +22,6 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should match sn
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "SidebarChannelMenu-ButtonTooltip-channel_id",
"text": "Channel options", "text": "Channel options",
} }
} }
@@ -168,7 +167,6 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should match sn
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "SidebarChannelMenu-ButtonTooltip-channel_id",
"text": "Channel options", "text": "Channel options",
} }
} }
@@ -314,7 +312,6 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should match sn
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "SidebarChannelMenu-ButtonTooltip-channel_id",
"text": "Channel options", "text": "Channel options",
} }
} }
@@ -460,7 +457,6 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "SidebarChannelMenu-ButtonTooltip-channel_id",
"text": "Channel options", "text": "Channel options",
} }
} }
@@ -574,7 +570,6 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "SidebarChannelMenu-ButtonTooltip-channel_id",
"text": "Channel options", "text": "Channel options",
} }
} }
@@ -703,7 +698,6 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "SidebarChannelMenu-ButtonTooltip-channel_id",
"text": "Channel options", "text": "Channel options",
} }
} }
@@ -849,7 +843,6 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "SidebarChannelMenu-ButtonTooltip-channel_id",
"text": "Channel options", "text": "Channel options",
} }
} }
@@ -995,7 +988,6 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "SidebarChannelMenu-ButtonTooltip-channel_id",
"text": "Channel options", "text": "Channel options",
} }
} }
@@ -1141,7 +1133,6 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "SidebarChannelMenu-ButtonTooltip-channel_id",
"text": "Channel options", "text": "Channel options",
} }
} }
@@ -1287,7 +1278,6 @@ exports[`components/sidebar/sidebar_channel/sidebar_channel_menu should show cor
menuButtonTooltip={ menuButtonTooltip={
Object { Object {
"class": "hidden-xs", "class": "hidden-xs",
"id": "SidebarChannelMenu-ButtonTooltip-channel_id",
"text": "Channel options", "text": "Channel options",
} }
} }

Просмотреть файл

@@ -297,7 +297,6 @@ const SidebarChannelMenu = ({
children: <DotsVerticalIcon size={16}/>, children: <DotsVerticalIcon size={16}/>,
}} }}
menuButtonTooltip={{ menuButtonTooltip={{
id: `SidebarChannelMenu-ButtonTooltip-${channel.id}`,
class: 'hidden-xs', class: 'hidden-xs',
text: formatMessage({id: 'sidebar_left.sidebar_channel_menu.editChannel', defaultMessage: 'Channel options'}), text: formatMessage({id: 'sidebar_left.sidebar_channel_menu.editChannel', defaultMessage: 'Channel options'}),
}} }}

Просмотреть файл

@@ -1,8 +1,9 @@
@use 'utils/variables';
#user-group-popover .popover-content { #user-group-popover .popover-content {
padding: 0; padding: 0;
} }
.user-group-popover-floating-overlay { .user-group-popover-floating-overlay {
// 99 being the z-index of the global header z-index: variables.$z-index-user-group-popover;
z-index: 1060;
} }

Просмотреть файл

@@ -24,21 +24,17 @@ import type {ReactElement, ReactNode} from 'react';
import type {MessageDescriptor} from 'react-intl'; import type {MessageDescriptor} from 'react-intl';
import {defineMessage} from 'react-intl'; import {defineMessage} from 'react-intl';
import {Constants} from 'utils/constants'; import {OverlayArrow, OverlaysTimings, OverlayTransitionStyles, RootHtmlPortalId} from 'utils/constants';
import TooltipContent from './tooltip_content'; import TooltipContent from './tooltip_content';
import type {ShortcutDefinition} from './tooltip_shortcut'; import type {ShortcutDefinition} from './tooltip_shortcut';
import './tooltip.scss'; import './with_tooltip.scss';
const ARROW_WIDTH = 10; // in px
const ARROW_HEIGHT = 6; // in px
const ARROW_OFFSET = 8; // in px
const TOOLTIP_REST_TIME_BEFORE_OPEN = 400; // in ms
const TOOLTIP_APPEAR_DURATION = 250; // in ms
const TOOLTIP_DISAPPEAR_DURATION = 200; // in ms
/**
* Shortcut keys map to translations that can be used in the tooltip
* when shortcut definition is provided
*/
export const ShortcutKeys = { export const ShortcutKeys = {
alt: defineMessage({ alt: defineMessage({
id: 'shortcuts.generic.alt', id: 'shortcuts.generic.alt',
@@ -68,7 +64,17 @@ interface Props {
* This doesn't always guarantee the tooltip will be vertical, it just determines the initial placement and fallback placements * This doesn't always guarantee the tooltip will be vertical, it just determines the initial placement and fallback placements
*/ */
isVertical?: boolean; isVertical?: boolean;
tooltipContentContainerClassName?: string;
/**
* If closing of the tooltip should be delayed,
* Useful if tooltips contains links that need to be clicked
*/
delayClose?: boolean;
/**
* Additional class name to be added to the tooltip container
*/
className?: string;
disabled?: boolean; disabled?: boolean;
/** /**
@@ -79,7 +85,7 @@ interface Props {
children: ReactElement; children: ReactElement;
} }
function WithTooltip({ export default function WithTooltip({
children, children,
title, title,
emoji, emoji,
@@ -87,7 +93,8 @@ function WithTooltip({
hint, hint,
shortcut, shortcut,
isVertical = true, isVertical = true,
tooltipContentContainerClassName, delayClose = false,
className,
onOpen, onOpen,
disabled, disabled,
}: Props) { }: Props) {
@@ -116,13 +123,13 @@ function WithTooltip({
return {initial, fallback}; return {initial, fallback};
}, [isVertical]); }, [isVertical]);
const {refs: {setReference, setFloating}, floatingStyles, context} = useFloating({ const {refs: {setReference, setFloating}, floatingStyles, context: floatingContext} = useFloating({
open: disabled ? false : open, open: disabled ? false : open,
onOpenChange: handleChange, onOpenChange: handleChange,
whileElementsMounted: autoUpdate, whileElementsMounted: autoUpdate,
placement: placements.initial, placement: placements.initial,
middleware: [ middleware: [
offset(ARROW_OFFSET), offset(OverlayArrow.OFFSET),
flip({ flip({
fallbackPlacements: placements.fallback, fallbackPlacements: placements.fallback,
}), }),
@@ -132,75 +139,61 @@ function WithTooltip({
], ],
}); });
const hover = useHover(context, { const {isMounted, styles: transitionStyles} = useTransitionStyles(floatingContext, TRANSITION_STYLE_PROPS);
restMs: TOOLTIP_REST_TIME_BEFORE_OPEN,
const hover = useHover(floatingContext, {
restMs: OverlaysTimings.CURSOR_REST_TIME_BEFORE_OPEN,
delay: { delay: {
open: Constants.OVERLAY_TIME_DELAY, open: OverlaysTimings.CURSOR_MOUSEOVER_TO_OPEN,
close: delayClose ? OverlaysTimings.CURSOR_MOUSEOUT_TO_CLOSE_WITH_DELAY : OverlaysTimings.CURSOR_MOUSEOUT_TO_CLOSE,
}, },
}); });
const focus = useFocus(context); const focus = useFocus(floatingContext);
const dismiss = useDismiss(context); const dismiss = useDismiss(floatingContext);
const role = useRole(context, {role: 'tooltip'}); const role = useRole(floatingContext, {role: 'tooltip'});
const {getReferenceProps, getFloatingProps} = useInteractions([hover, focus, dismiss, role]); const {getReferenceProps, getFloatingProps} = useInteractions([hover, focus, dismiss, role]);
const {isMounted, styles: transitionStyles} = useTransitionStyles(context, {
duration: {
open: TOOLTIP_APPEAR_DURATION,
close: TOOLTIP_DISAPPEAR_DURATION,
},
initial: {
opacity: 0,
},
common: {
opacity: 1,
},
});
if (!isValidElement(children)) { if (!isValidElement(children)) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error('Children must be a valid React element for WithTooltip'); console.error('Children must be a valid React element for WithTooltip');
} }
const mergedRefs = useMergeRefs([(children as any)?.ref, setReference]); const mergedRefs = useMergeRefs([setReference, (children as any)?.ref]);
const trigger = cloneElement(children, { const trigger = cloneElement(
...getReferenceProps({ children,
getReferenceProps({
ref: mergedRefs, ref: mergedRefs,
...children.props, ...children.props,
}), }),
}); );
return ( return (
<> <>
{trigger} {trigger}
{isMounted && ( {isMounted && (
<FloatingPortal <FloatingPortal id={RootHtmlPortalId}>
id='root-portal' // This is the global portal container id
>
<div <div
className='tooltipContainer'
ref={setFloating} ref={setFloating}
style={floatingStyles} className={classNames('tooltipContainer', className)}
style={{...floatingStyles, ...transitionStyles}}
{...getFloatingProps()} {...getFloatingProps()}
> >
<div <TooltipContent
className={classNames('tooltipContentContainer', tooltipContentContainerClassName)} title={title}
style={transitionStyles} emoji={emoji}
> isEmojiLarge={isEmojiLarge}
<TooltipContent hint={hint}
title={title} shortcut={shortcut}
emoji={emoji} />
isEmojiLarge={isEmojiLarge} <FloatingArrow
hint={hint} ref={arrowRef}
shortcut={shortcut} context={floatingContext}
/> width={OverlayArrow.WIDTH}
<FloatingArrow height={OverlayArrow.HEIGHT}
ref={arrowRef} />
context={context}
width={ARROW_WIDTH}
height={ARROW_HEIGHT}
/>
</div>
</div> </div>
</FloatingPortal> </FloatingPortal>
)} )}
@@ -208,4 +201,10 @@ function WithTooltip({
); );
} }
export default WithTooltip; const TRANSITION_STYLE_PROPS = {
duration: {
open: OverlaysTimings.FADE_IN_DURATION,
close: OverlaysTimings.FADE_OUT_DURATION,
},
initial: OverlayTransitionStyles.START,
};

Просмотреть файл

@@ -1,70 +0,0 @@
.tooltipContainer {
z-index: 1070;
> .tooltipContentContainer {
z-index: 1070;
max-width: 220px;
padding: 4px 8px;
border-radius: 4px;
background: rgba(0, 0, 0, 1);
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.12);
line-height: 18px;
pointer-events: none;
text-align: center;
word-break: break-word;
> .tooltipContent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: "Open Sans", sans-serif;
> .tooltipContentTitleContainer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 6px;
&.isEmojiLarge {
flex-direction: column;
gap: 2px;
> .tooltipContentEmoji {
padding-top: 1px;
}
}
> .tooltipContentEmoji {
display: flex;
align-items: center;
justify-content: center;
}
> .tooltipContentTitle {
color: #ffffff;
font-size: 12px;
font-weight: 600;
line-height: 15px;
}
}
> .tooltipContentShortcut {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 4px 0;
gap: 2px;
}
> .tooltipContentHint {
color: rgba(255, 255, 255, 0.64);
font-size: 11px;
font-weight: 600;
line-height: 16px;
}
}
}
}

Просмотреть файл

@@ -0,0 +1,68 @@
@use 'utils/variables';
.tooltipContainer {
z-index: variables.$z-index-popover;
max-width: 220px;
padding: 4px 8px;
border-radius: 4px;
background: rgba(0, 0, 0, 1);
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.12);
line-height: 18px;
pointer-events: none;
text-align: center;
word-break: break-word;
> .tooltipContent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: "Open Sans", sans-serif;
> .tooltipContentTitleContainer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 6px;
&.isEmojiLarge {
flex-direction: column;
gap: 2px;
> .tooltipContentEmoji {
padding-top: 1px;
}
}
> .tooltipContentEmoji {
display: flex;
align-items: center;
justify-content: center;
}
> .tooltipContentTitle {
color: #ffffff;
font-size: 12px;
font-weight: 600;
line-height: 15px;
}
}
> .tooltipContentShortcut {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 4px 0;
gap: 2px;
}
> .tooltipContentHint {
color: rgba(255, 255, 255, 0.64);
font-size: 11px;
font-weight: 600;
line-height: 16px;
}
}
}

Просмотреть файл

@@ -36,3 +36,9 @@ $elevation-3: var(--elevation-3);
$elevation-4: var(--elevation-4); $elevation-4: var(--elevation-4);
$elevation-5: var(--elevation-5); $elevation-5: var(--elevation-5);
$elevation-6: var(--elevation-6); $elevation-6: var(--elevation-6);
// Z-Index
// Central place to manage z-index values for the app, providing a consistent way to manage stacking order.
$z-index-popover: 1070;
$z-index-user-group-popover: 1060;
$z-index-global-header: 99;

Просмотреть файл

@@ -2240,6 +2240,9 @@ export const RootHtmlPortalId = 'root-portal';
export const OverlaysTimings = { export const OverlaysTimings = {
CURSOR_REST_TIME_BEFORE_OPEN: 400, // in ms CURSOR_REST_TIME_BEFORE_OPEN: 400, // in ms
CURSOR_MOUSEOVER_TO_OPEN: 400, // in ms
CURSOR_MOUSEOUT_TO_CLOSE: 0,
CURSOR_MOUSEOUT_TO_CLOSE_WITH_DELAY: 200, // in ms
FADE_IN_DURATION: 250, // in ms FADE_IN_DURATION: 250, // in ms
FADE_OUT_DURATION: 150, // in ms FADE_OUT_DURATION: 150, // in ms
}; };
@@ -2250,5 +2253,10 @@ export const OverlayTransitionStyles = {
}, },
}; };
export default Constants; export const OverlayArrow = {
WIDTH: 10, // in px
HEIGHT: 6, // in px
OFFSET: 8, // in px
};
export default Constants;