Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

Tooltip.tsx 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import React, { ReactElement, useCallback, useEffect, useRef, useState } from 'react';
  2. import { useDispatch, useSelector } from 'react-redux';
  3. import { keyframes } from 'tss-react';
  4. import { makeStyles } from 'tss-react/mui';
  5. import { IReduxState } from '../../../app/types';
  6. import { isMobileBrowser } from '../../environment/utils';
  7. import Popover from '../../popover/components/Popover.web';
  8. import { withPixelLineHeight } from '../../styles/functions.web';
  9. import { hideTooltip, showTooltip } from '../actions';
  10. const TOOLTIP_DELAY = 300;
  11. const ANIMATION_DURATION = 0.2;
  12. interface IProps {
  13. children: ReactElement;
  14. containerClassName?: string;
  15. content: string;
  16. position?: 'top' | 'bottom' | 'left' | 'right';
  17. }
  18. const useStyles = makeStyles()(theme => {
  19. return {
  20. container: {
  21. backgroundColor: theme.palette.uiBackground,
  22. borderRadius: '3px',
  23. padding: theme.spacing(2),
  24. ...withPixelLineHeight(theme.typography.labelRegular),
  25. color: theme.palette.text01,
  26. position: 'relative',
  27. '&.mounting-animation': {
  28. animation: `${keyframes`
  29. 0% {
  30. opacity: 0;
  31. }
  32. 100% {
  33. opacity: 1;
  34. }
  35. `} ${ANIMATION_DURATION}s forwards ease-in`
  36. },
  37. '&.unmounting': {
  38. animation: `${keyframes`
  39. 0% {
  40. opacity: 1;
  41. }
  42. 100% {
  43. opacity: 0;
  44. }
  45. `} ${ANIMATION_DURATION}s forwards ease-out`
  46. }
  47. }
  48. };
  49. });
  50. const Tooltip = ({ containerClassName, content, children, position = 'top' }: IProps) => {
  51. const dispatch = useDispatch();
  52. const [ visible, setVisible ] = useState(false);
  53. const [ isUnmounting, setIsUnmounting ] = useState(false);
  54. const { classes, cx } = useStyles();
  55. const timeoutID = useRef({
  56. open: 0,
  57. close: 0
  58. });
  59. const {
  60. content: storeContent,
  61. previousContent,
  62. visible: isVisible
  63. } = useSelector((state: IReduxState) => state['features/base/tooltip']);
  64. if (isMobileBrowser()) {
  65. return children;
  66. }
  67. const contentComponent = (
  68. <div
  69. className = { cx(classes.container, previousContent === '' && 'mounting-animation',
  70. isUnmounting && 'unmounting') }>
  71. {content}
  72. </div>
  73. );
  74. const openPopover = () => {
  75. setVisible(true);
  76. dispatch(showTooltip(content));
  77. };
  78. const closePopover = () => {
  79. setVisible(false);
  80. dispatch(hideTooltip(content));
  81. setIsUnmounting(false);
  82. };
  83. const onPopoverOpen = useCallback(() => {
  84. clearTimeout(timeoutID.current.close);
  85. timeoutID.current.close = 0;
  86. if (!visible) {
  87. if (isVisible) {
  88. openPopover();
  89. } else {
  90. timeoutID.current.open = window.setTimeout(() => {
  91. openPopover();
  92. }, TOOLTIP_DELAY);
  93. }
  94. }
  95. }, [ visible, isVisible ]);
  96. const onPopoverClose = useCallback(() => {
  97. clearTimeout(timeoutID.current.open);
  98. if (visible) {
  99. timeoutID.current.close = window.setTimeout(() => {
  100. setIsUnmounting(true);
  101. }, TOOLTIP_DELAY);
  102. }
  103. }, [ visible ]);
  104. useEffect(() => {
  105. if (isUnmounting) {
  106. setTimeout(() => {
  107. if (timeoutID.current.close !== 0) {
  108. closePopover();
  109. }
  110. }, (ANIMATION_DURATION * 1000) + 10);
  111. }
  112. }, [ isUnmounting ]);
  113. useEffect(() => {
  114. if (storeContent !== content) {
  115. closePopover();
  116. clearTimeout(timeoutID.current.close);
  117. timeoutID.current.close = 0;
  118. }
  119. }, [ storeContent ]);
  120. return (
  121. <Popover
  122. allowClick = { true }
  123. className = { containerClassName }
  124. content = { contentComponent }
  125. onPopoverClose = { onPopoverClose }
  126. onPopoverOpen = { onPopoverOpen }
  127. position = { position }
  128. visible = { visible }>
  129. {children}
  130. </Popover>
  131. );
  132. };
  133. export default Tooltip;