diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index e8469cc1..5080d3ca 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -6,6 +6,7 @@ import { useTooltip } from 'components/TooltipProvider' import useIsomorphicLayoutEffect from 'utils/use-isomorphic-layout-effect' import { getScrollParent } from 'utils/get-scroll-parent' import { computeTooltipPosition } from 'utils/compute-positions' +import { cssTimeToMs } from 'utils/css-time-to-ms' import coreStyles from './core-styles.module.css' import styles from './styles.module.css' import type { @@ -67,6 +68,7 @@ const Tooltip = ({ const tooltipArrowRef = useRef(null) const tooltipShowDelayTimerRef = useRef(null) const tooltipHideDelayTimerRef = useRef(null) + const missedTransitionTimerRef = useRef(null) const [actualPlacement, setActualPlacement] = useState(place) const [inlineStyles, setInlineStyles] = useState({}) const [inlineArrowStyles, setInlineArrowStyles] = useState({}) @@ -211,6 +213,9 @@ const Tooltip = ({ if (show === wasShowing.current) { return } + if (missedTransitionTimerRef.current) { + clearTimeout(missedTransitionTimerRef.current) + } wasShowing.current = show if (show) { afterShow?.() @@ -218,6 +223,18 @@ const Tooltip = ({ /** * see `onTransitionEnd` on tooltip wrapper */ + const style = getComputedStyle(document.body) + const transitionShowDelay = cssTimeToMs(style.getPropertyValue('--rt-transition-show-delay')) + missedTransitionTimerRef.current = setTimeout(() => { + /** + * if the tooltip switches from `show === true` to `show === false` too fast + * the transition never runs, so `onTransitionEnd` callback never gets fired + */ + setRendered(false) + setImperativeOptions(null) + afterHide?.() + // +25ms just to make sure `onTransitionEnd` (if it gets fired) has time to run + }, transitionShowDelay + 25) } }, [show]) @@ -811,10 +828,9 @@ const Tooltip = ({ clickable && coreStyles['clickable'], )} onTransitionEnd={(event: TransitionEvent) => { - /** - * @warning if `--rt-transition-closing-delay` is set to 0, - * the tooltip will be stuck (but not visible) on the DOM - */ + if (missedTransitionTimerRef.current) { + clearTimeout(missedTransitionTimerRef.current) + } if (show || event.propertyName !== 'opacity') { return } diff --git a/src/utils/css-time-to-ms.ts b/src/utils/css-time-to-ms.ts new file mode 100644 index 00000000..fd038935 --- /dev/null +++ b/src/utils/css-time-to-ms.ts @@ -0,0 +1,11 @@ +export const cssTimeToMs = (time: string): number => { + const match = time.match(/^([\d.]+)(m?s?)$/) + if (!match) { + return 0 + } + const [, amount, unit] = match + if (unit !== 's' && unit !== 'ms') { + return 0 + } + return Number(amount) * (unit === 'ms' ? 1 : 1000) +}