-
Notifications
You must be signed in to change notification settings - Fork 2.9k
/
BaseEducationalTooltip.tsx
94 lines (81 loc) · 3.41 KB
/
BaseEducationalTooltip.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import React, {memo, useEffect, useRef, useState} from 'react';
import type {LayoutRectangle, NativeSyntheticEvent} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import GenericTooltip from '@components/Tooltip/GenericTooltip';
import type {EducationalTooltipProps} from '@components/Tooltip/types';
import ONYXKEYS from '@src/ONYXKEYS';
import measureTooltipCoordinate from './measureTooltipCoordinate';
type LayoutChangeEventWithTarget = NativeSyntheticEvent<{layout: LayoutRectangle; target: HTMLElement}>;
/**
* A component used to wrap an element intended for displaying a tooltip.
* This tooltip would show immediately without user's interaction and hide after 5 seconds.
*/
function BaseEducationalTooltip({children, onHideTooltip, shouldAutoDismiss = false, ...props}: EducationalTooltipProps) {
const hideTooltipRef = useRef<() => void>();
const [shouldMeasure, setShouldMeasure] = useState(false);
const show = useRef<() => void>();
const [modal] = useOnyx(ONYXKEYS.MODAL);
const shouldShow = !modal?.willAlertModalBecomeVisible && !modal?.isVisible;
useEffect(
() => () => {
if (!hideTooltipRef.current) {
return;
}
hideTooltipRef.current();
},
[],
);
// Automatically hide tooltip after 5 seconds
useEffect(() => {
if (!hideTooltipRef.current || !shouldAutoDismiss) {
return;
}
// If the modal is open, hide the tooltip immediately and clear the timeout
if (!shouldShow) {
hideTooltipRef.current();
return;
}
// Automatically hide tooltip after 5 seconds if shouldAutoDismiss is true
const timerID = setTimeout(() => {
hideTooltipRef.current?.();
onHideTooltip?.();
}, 5000);
return () => {
clearTimeout(timerID);
};
}, [shouldAutoDismiss, shouldShow, onHideTooltip]);
useEffect(() => {
if (!shouldMeasure || !shouldShow) {
return;
}
// When tooltip is used inside an animated view (e.g. popover), we need to wait for the animation to finish before measuring content.
setTimeout(() => {
show.current?.();
}, 500);
}, [shouldMeasure, shouldShow]);
return (
<GenericTooltip
shouldForceAnimate
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
onHideTooltip={onHideTooltip}
>
{({showTooltip, hideTooltip, updateTargetBounds}) => {
// eslint-disable-next-line react-compiler/react-compiler
hideTooltipRef.current = hideTooltip;
return React.cloneElement(children as React.ReactElement, {
onLayout: (e: LayoutChangeEventWithTarget) => {
if (!shouldMeasure) {
setShouldMeasure(true);
}
// e.target is specific to native, use e.nativeEvent.target on web instead
const target = e.target || e.nativeEvent.target;
show.current = () => measureTooltipCoordinate(target, updateTargetBounds, showTooltip);
},
});
}}
</GenericTooltip>
);
}
BaseEducationalTooltip.displayName = 'BaseEducationalTooltip';
export default memo(BaseEducationalTooltip);