|
1 |
| -import { useState } from 'react'; |
| 1 | +import { useState, forwardRef } from 'react'; |
2 | 2 | import PropTypes from 'prop-types';
|
3 |
| -const Tooltip = ({ |
4 |
| - children, |
5 |
| - content, |
6 |
| - position = 'bottom', |
7 |
| - delay = 300, |
8 |
| - className = '', |
9 |
| -}) => { |
10 |
| - const [isVisible, setIsVisible] = useState(false); |
11 |
| - const [isDelayed, setIsDelayed] = useState(false); |
12 |
| - const showTooltip = () => { |
13 |
| - const timer = setTimeout(() => { |
14 |
| - setIsDelayed(true); |
15 |
| - }, delay); |
16 |
| - setIsVisible(true); |
17 |
| - return () => clearTimeout(timer); |
18 |
| - }; |
19 |
| - const hideTooltip = () => { |
20 |
| - setIsVisible(false); |
21 |
| - setIsDelayed(false); |
22 |
| - }; |
23 |
| - // Position classes - increase the margins to create more space |
24 |
| - const positions = { |
25 |
| - top: 'bottom-full left-1/2 -translate-x-1/2 mb-8', |
26 |
| - bottom: 'top-full left-1/2 -translate-x-1/2 mt-10', |
27 |
| - left: 'right-full top-1/2 -translate-y-1/2 mr-8', |
28 |
| - right: 'left-full top-1/2 -translate-y-1/2 ml-8', |
29 |
| - }; |
30 |
| - // Custom background color for both tooltip and triangle |
31 |
| - const bgColor = '#526B78'; |
32 |
| - return ( |
33 |
| - <div |
34 |
| - className={`relative flex items-center justify-center ${className}`} |
35 |
| - onMouseEnter={showTooltip} |
36 |
| - onMouseLeave={hideTooltip} |
37 |
| - onFocus={showTooltip} |
38 |
| - onBlur={hideTooltip} |
39 |
| - > |
40 |
| - {children} |
41 |
| - {isVisible && ( |
42 |
| - <div |
43 |
| - className={` |
| 3 | + |
| 4 | +const Tooltip = forwardRef( |
| 5 | + ( |
| 6 | + { |
| 7 | + children, |
| 8 | + content, |
| 9 | + position = 'bottom', |
| 10 | + delay = 300, |
| 11 | + className = '', |
| 12 | + onHide, |
| 13 | + }, |
| 14 | + ref |
| 15 | + ) => { |
| 16 | + const [isVisible, setIsVisible] = useState(false); |
| 17 | + const [isDelayed, setIsDelayed] = useState(false); |
| 18 | + const showTooltip = () => { |
| 19 | + const timer = setTimeout(() => { |
| 20 | + setIsDelayed(true); |
| 21 | + }, delay); |
| 22 | + setIsVisible(true); |
| 23 | + return () => clearTimeout(timer); |
| 24 | + }; |
| 25 | + const hideTooltip = () => { |
| 26 | + setIsVisible(false); |
| 27 | + setIsDelayed(false); |
| 28 | + if (onHide) onHide(); |
| 29 | + }; |
| 30 | + // Position classes - increase the margins to create more space |
| 31 | + const positions = { |
| 32 | + top: 'bottom-full left-1/2 -translate-x-1/2 mb-8', |
| 33 | + bottom: 'top-full left-1/2 -translate-x-1/2 mt-10', |
| 34 | + left: 'right-full top-1/2 -translate-y-1/2 mr-8', |
| 35 | + right: 'left-full top-1/2 -translate-y-1/2 ml-8', |
| 36 | + }; |
| 37 | + // Custom background color for both tooltip and triangle |
| 38 | + const bgColor = '#526B78'; |
| 39 | + return ( |
| 40 | + <div |
| 41 | + ref={ref} |
| 42 | + className={`relative flex items-center justify-center ${className}`} |
| 43 | + onMouseEnter={showTooltip} |
| 44 | + onMouseLeave={hideTooltip} |
| 45 | + onBlur={hideTooltip} |
| 46 | + > |
| 47 | + {children} |
| 48 | + {isVisible && ( |
| 49 | + <div |
| 50 | + className={` |
44 | 51 | absolute z-[9999]
|
45 | 52 | ${positions[position]}
|
46 | 53 | ${isDelayed ? 'opacity-100' : 'opacity-0'}
|
47 | 54 | transition-opacity duration-200
|
48 | 55 | pointer-events-none
|
49 | 56 | `}
|
50 |
| - > |
51 |
| - <div |
52 |
| - className="text-white text-xs font-medium p-10 rounded-md whitespace-nowrap shadow-xl" |
53 |
| - style={{ backgroundColor: bgColor }} |
54 | 57 | >
|
55 |
| - {content} |
56 |
| - {/* Triangle/Arrow - Reduced size */} |
57 |
| - {position === 'top' && ( |
58 |
| - <div |
59 |
| - className="absolute top-full left-1/2 -translate-x-1/2 border-solid border-l-[6px] border-r-[6px] border-t-[6px] border-l-transparent border-r-transparent" |
60 |
| - style={{ borderTopColor: bgColor }} |
61 |
| - ></div> |
62 |
| - )} |
63 |
| - {position === 'bottom' && ( |
64 |
| - <div |
65 |
| - className="absolute -top-[6px] left-1/2 -translate-x-1/2 border-solid border-l-[6px] border-r-[6px] border-b-[6px] border-l-transparent border-r-transparent" |
66 |
| - style={{ borderBottomColor: bgColor }} |
67 |
| - ></div> |
68 |
| - )} |
69 |
| - {position === 'left' && ( |
70 |
| - <div |
71 |
| - className="absolute top-1/2 -translate-y-1/2 right-[-6px] border-solid border-t-[6px] border-b-[6px] border-l-[6px] border-t-transparent border-b-transparent" |
72 |
| - style={{ borderLeftColor: bgColor }} |
73 |
| - ></div> |
74 |
| - )} |
75 |
| - {position === 'right' && ( |
76 |
| - <div |
77 |
| - className="absolute top-1/2 -translate-y-1/2 left-[-6px] border-solid border-t-[6px] border-b-[6px] border-r-[6px] border-t-transparent border-b-transparent" |
78 |
| - style={{ borderRightColor: bgColor }} |
79 |
| - ></div> |
80 |
| - )} |
| 58 | + <div |
| 59 | + className="text-white text-xs font-medium p-10 rounded-md whitespace-nowrap shadow-xl" |
| 60 | + style={{ backgroundColor: bgColor }} |
| 61 | + > |
| 62 | + {content} |
| 63 | + {/* Triangle/Arrow - Reduced size */} |
| 64 | + {position === 'top' && ( |
| 65 | + <div |
| 66 | + className="absolute top-full left-1/2 -translate-x-1/2 border-solid border-l-[6px] border-r-[6px] border-t-[6px] border-l-transparent border-r-transparent" |
| 67 | + style={{ borderTopColor: bgColor }} |
| 68 | + ></div> |
| 69 | + )} |
| 70 | + {position === 'bottom' && ( |
| 71 | + <div |
| 72 | + className="absolute -top-[6px] left-1/2 -translate-x-1/2 border-solid border-l-[6px] border-r-[6px] border-b-[6px] border-l-transparent border-r-transparent" |
| 73 | + style={{ borderBottomColor: bgColor }} |
| 74 | + ></div> |
| 75 | + )} |
| 76 | + {position === 'left' && ( |
| 77 | + <div |
| 78 | + className="absolute top-1/2 -translate-y-1/2 right-[-6px] border-solid border-t-[6px] border-b-[6px] border-l-[6px] border-t-transparent border-b-transparent" |
| 79 | + style={{ borderLeftColor: bgColor }} |
| 80 | + ></div> |
| 81 | + )} |
| 82 | + {position === 'right' && ( |
| 83 | + <div |
| 84 | + className="absolute top-1/2 -translate-y-1/2 left-[-6px] border-solid border-t-[6px] border-b-[6px] border-r-[6px] border-t-transparent border-b-transparent" |
| 85 | + style={{ borderRightColor: bgColor }} |
| 86 | + ></div> |
| 87 | + )} |
| 88 | + </div> |
81 | 89 | </div>
|
82 |
| - </div> |
83 |
| - )} |
84 |
| - </div> |
85 |
| - ); |
86 |
| -}; |
| 90 | + )} |
| 91 | + </div> |
| 92 | + ); |
| 93 | + } |
| 94 | +); |
| 95 | + |
| 96 | +Tooltip.displayName = 'Tooltip'; |
87 | 97 |
|
88 | 98 | Tooltip.propTypes = {
|
89 | 99 | children: PropTypes.node.isRequired,
|
90 | 100 | content: PropTypes.node.isRequired,
|
91 | 101 | position: PropTypes.oneOf(['top', 'right', 'bottom', 'left']),
|
92 | 102 | delay: PropTypes.number,
|
93 | 103 | className: PropTypes.string,
|
| 104 | + onHide: PropTypes.func, |
94 | 105 | };
|
95 | 106 |
|
96 | 107 | export default Tooltip;
|
0 commit comments