Skip to content

Commit 44fc0ed

Browse files
fix: add fallback if onTransitionEnd never fires
1 parent 7b83b18 commit 44fc0ed

File tree

2 files changed

+31
-4
lines changed

2 files changed

+31
-4
lines changed

src/components/Tooltip/Tooltip.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useTooltip } from 'components/TooltipProvider'
66
import useIsomorphicLayoutEffect from 'utils/use-isomorphic-layout-effect'
77
import { getScrollParent } from 'utils/get-scroll-parent'
88
import { computeTooltipPosition } from 'utils/compute-positions'
9+
import { cssTimeToMs } from 'utils/css-time-to-ms'
910
import coreStyles from './core-styles.module.css'
1011
import styles from './styles.module.css'
1112
import type {
@@ -67,6 +68,7 @@ const Tooltip = ({
6768
const tooltipArrowRef = useRef<HTMLElement>(null)
6869
const tooltipShowDelayTimerRef = useRef<NodeJS.Timeout | null>(null)
6970
const tooltipHideDelayTimerRef = useRef<NodeJS.Timeout | null>(null)
71+
const missedTransitionTimerRef = useRef<NodeJS.Timeout | null>(null)
7072
const [actualPlacement, setActualPlacement] = useState(place)
7173
const [inlineStyles, setInlineStyles] = useState({})
7274
const [inlineArrowStyles, setInlineArrowStyles] = useState({})
@@ -211,13 +213,28 @@ const Tooltip = ({
211213
if (show === wasShowing.current) {
212214
return
213215
}
216+
if (missedTransitionTimerRef.current) {
217+
clearTimeout(missedTransitionTimerRef.current)
218+
}
214219
wasShowing.current = show
215220
if (show) {
216221
afterShow?.()
217222
} else {
218223
/**
219224
* see `onTransitionEnd` on tooltip wrapper
220225
*/
226+
const style = getComputedStyle(document.body)
227+
const transitionShowDelay = cssTimeToMs(style.getPropertyValue('--rt-transition-show-delay'))
228+
missedTransitionTimerRef.current = setTimeout(() => {
229+
/**
230+
* if the tooltip switches from `show === true` to `show === false` too fast
231+
* the transition never runs, so `onTransitionEnd` callback never gets fired
232+
*/
233+
setRendered(false)
234+
setImperativeOptions(null)
235+
afterHide?.()
236+
// +25ms just to make sure `onTransitionEnd` (if it gets fired) has time to run
237+
}, transitionShowDelay + 25)
221238
}
222239
}, [show])
223240

@@ -811,10 +828,9 @@ const Tooltip = ({
811828
clickable && coreStyles['clickable'],
812829
)}
813830
onTransitionEnd={(event: TransitionEvent) => {
814-
/**
815-
* @warning if `--rt-transition-closing-delay` is set to 0,
816-
* the tooltip will be stuck (but not visible) on the DOM
817-
*/
831+
if (missedTransitionTimerRef.current) {
832+
clearTimeout(missedTransitionTimerRef.current)
833+
}
818834
if (show || event.propertyName !== 'opacity') {
819835
return
820836
}

src/utils/css-time-to-ms.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const cssTimeToMs = (time: string): number => {
2+
const match = time.match(/^([\d.]+)(m?s?)$/)
3+
if (!match) {
4+
return 0
5+
}
6+
const [, amount, unit] = match
7+
if (unit !== 's' && unit !== 'ms') {
8+
return 0
9+
}
10+
return Number(amount) * (unit === 'ms' ? 1 : 1000)
11+
}

0 commit comments

Comments
 (0)