Files
jitsi-meet/react/features/base/tooltip/components/Tooltip.tsx
Robert Pintilii 00780929e5 feat(tooltip) Create and move to our component (#13061)
Create Tooltip component
Fix Popover positioning calculations
Add margins to popover
Remove @atlaskit/tooltip
Update all components to use the new Tooltip component

Added tooltip actions and reducers for the following functionality: when a user hovers over an element is sees the tooltip for that element and then hovers another element that has a tooltip, instead of using the delay and animations we just unmount the current tooltip and mount the next one immediately
2023-03-17 12:23:51 +02:00

151 lines
4.3 KiB
TypeScript

import React, { ReactElement, useCallback, useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { keyframes } from 'tss-react';
import { makeStyles } from 'tss-react/mui';
import { IReduxState } from '../../../app/types';
import { isMobileBrowser } from '../../environment/utils';
import Popover from '../../popover/components/Popover.web';
import { withPixelLineHeight } from '../../styles/functions.web';
import { hideTooltip, showTooltip } from '../actions';
const TOOLTIP_DELAY = 300;
const ANIMATION_DURATION = 0.2;
interface IProps {
children: ReactElement;
containerClassName?: string;
content: string;
position?: 'top' | 'bottom' | 'left' | 'right';
}
const useStyles = makeStyles()(theme => {
return {
container: {
backgroundColor: theme.palette.uiBackground,
borderRadius: '3px',
padding: theme.spacing(2),
...withPixelLineHeight(theme.typography.labelRegular),
color: theme.palette.text01,
position: 'relative',
'&.mounting-animation': {
animation: `${keyframes`
0% {
opacity: 0;
}
100% {
opacity: 1;
}
`} ${ANIMATION_DURATION}s forwards ease-in`
},
'&.unmounting': {
animation: `${keyframes`
0% {
opacity: 1;
}
100% {
opacity: 0;
}
`} ${ANIMATION_DURATION}s forwards ease-out`
}
}
};
});
const Tooltip = ({ containerClassName, content, children, position = 'top' }: IProps) => {
const dispatch = useDispatch();
const [ visible, setVisible ] = useState(false);
const [ isUnmounting, setIsUnmounting ] = useState(false);
const { classes, cx } = useStyles();
const timeoutID = useRef({
open: 0,
close: 0
});
const {
content: storeContent,
previousContent,
visible: isVisible
} = useSelector((state: IReduxState) => state['features/base/tooltip']);
if (isMobileBrowser()) {
return children;
}
const contentComponent = (
<div
className = { cx(classes.container, previousContent === '' && 'mounting-animation',
isUnmounting && 'unmounting') }>
{content}
</div>
);
const openPopover = () => {
setVisible(true);
dispatch(showTooltip(content));
};
const closePopover = () => {
setVisible(false);
dispatch(hideTooltip(content));
setIsUnmounting(false);
};
const onPopoverOpen = useCallback(() => {
clearTimeout(timeoutID.current.close);
timeoutID.current.close = 0;
if (!visible) {
if (isVisible) {
openPopover();
} else {
timeoutID.current.open = window.setTimeout(() => {
openPopover();
}, TOOLTIP_DELAY);
}
}
}, [ visible, isVisible ]);
const onPopoverClose = useCallback(() => {
clearTimeout(timeoutID.current.open);
if (visible) {
timeoutID.current.close = window.setTimeout(() => {
setIsUnmounting(true);
}, TOOLTIP_DELAY);
}
}, [ visible ]);
useEffect(() => {
if (isUnmounting) {
setTimeout(() => {
if (timeoutID.current.close !== 0) {
closePopover();
}
}, (ANIMATION_DURATION * 1000) + 10);
}
}, [ isUnmounting ]);
useEffect(() => {
if (storeContent !== content) {
closePopover();
clearTimeout(timeoutID.current.close);
timeoutID.current.close = 0;
}
}, [ storeContent ]);
return (
<Popover
allowClick = { true }
className = { containerClassName }
content = { contentComponent }
onPopoverClose = { onPopoverClose }
onPopoverOpen = { onPopoverOpen }
position = { position }
visible = { visible }>
{children}
</Popover>
);
};
export default Tooltip;