tooltip.tsx 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import {Fragment, useEffect} from 'react';
  2. import {createPortal} from 'react-dom';
  3. import {SerializedStyles, useTheme} from '@emotion/react';
  4. import styled from '@emotion/styled';
  5. import {AnimatePresence} from 'framer-motion';
  6. import {Overlay, PositionWrapper} from 'sentry/components/overlay';
  7. import {space} from 'sentry/styles/space';
  8. import {useHoverOverlay, UseHoverOverlayProps} from 'sentry/utils/useHoverOverlay';
  9. interface TooltipProps extends UseHoverOverlayProps {
  10. /**
  11. * The content to show in the tooltip popover
  12. */
  13. title: React.ReactNode;
  14. children?: React.ReactNode;
  15. /**
  16. * Disable the tooltip display entirely
  17. */
  18. disabled?: boolean;
  19. /**
  20. * Additional style rules for the tooltip content.
  21. */
  22. overlayStyle?: React.CSSProperties | SerializedStyles;
  23. }
  24. function Tooltip({
  25. children,
  26. overlayStyle,
  27. title,
  28. disabled = false,
  29. ...hoverOverlayProps
  30. }: TooltipProps) {
  31. const theme = useTheme();
  32. const {wrapTrigger, isOpen, overlayProps, placement, arrowData, arrowProps, reset} =
  33. useHoverOverlay('tooltip', hoverOverlayProps);
  34. // Reset the visibility when the tooltip becomes disabled
  35. useEffect(() => {
  36. if (disabled) {
  37. reset();
  38. }
  39. }, [reset, disabled]);
  40. if (disabled || !title) {
  41. return <Fragment>{children}</Fragment>;
  42. }
  43. const tooltipContent = isOpen && (
  44. <PositionWrapper zIndex={theme.zIndex.tooltip} {...overlayProps}>
  45. <TooltipContent
  46. animated
  47. arrowProps={arrowProps}
  48. originPoint={arrowData}
  49. placement={placement}
  50. overlayStyle={overlayStyle}
  51. >
  52. {title}
  53. </TooltipContent>
  54. </PositionWrapper>
  55. );
  56. return (
  57. <Fragment>
  58. {wrapTrigger(children)}
  59. {createPortal(<AnimatePresence>{tooltipContent}</AnimatePresence>, document.body)}
  60. </Fragment>
  61. );
  62. }
  63. const TooltipContent = styled(Overlay)`
  64. padding: ${space(1)} ${space(1.5)};
  65. overflow-wrap: break-word;
  66. max-width: 225px;
  67. color: ${p => p.theme.textColor};
  68. font-size: ${p => p.theme.fontSizeSmall};
  69. line-height: 1.2;
  70. text-align: center;
  71. `;
  72. export {Tooltip, TooltipProps};