|
1 |
| -import React, { useState, ReactChild, ReactNode } from 'react'; |
| 1 | +import React, { |
| 2 | + useState, |
| 3 | + ReactChild, |
| 4 | + ReactNode, |
| 5 | + forwardRef, |
| 6 | + useImperativeHandle, |
| 7 | + Ref, |
| 8 | +} from 'react'; |
2 | 9 | import styled from 'styled-components';
|
3 |
| - |
4 | 10 | import Arrow from './Arrow';
|
5 | 11 | import TooltipWrapper, { FadeEasing } from './TooltipWrapper';
|
6 | 12 | import Bubble from './Bubble';
|
7 | 13 |
|
| 14 | +export interface FlashTooltipRef { |
| 15 | + flashTooltip: () => Promise<void>; |
| 16 | +} |
| 17 | + |
8 | 18 | export type Placement = 'left' | 'right' | 'top' | 'bottom';
|
9 | 19 | export type TooltipBehavior = 'hover' | 'click' | 'ref';
|
10 | 20 |
|
@@ -75,86 +85,98 @@ export type TooltipProps = {
|
75 | 85 | */
|
76 | 86 | className?: string;
|
77 | 87 | children?: ReactChild | ReactNode | null;
|
| 88 | + ref: Ref<FlashTooltipRef>; |
78 | 89 | };
|
79 | 90 |
|
80 | 91 | const Container = styled.div`
|
81 | 92 | position: relative;
|
82 | 93 | display: inline-block;
|
83 | 94 | `;
|
84 | 95 |
|
85 |
| -const Tooltip = ({ |
86 |
| - behavior = 'hover', |
87 |
| - arrowWidth = 8, |
88 |
| - background = '', |
89 |
| - border = '', |
90 |
| - children = null, |
91 |
| - color = '', |
92 |
| - content, |
93 |
| - fadeDuration = 150, |
94 |
| - fadeEasing = 'linear', |
95 |
| - fixed = false, |
96 |
| - fontSize = '', |
97 |
| - offset = 0, |
98 |
| - padding = 0.7, |
99 |
| - placement = 'top', |
100 |
| - radius = 0, |
101 |
| - zIndex = 1, |
102 |
| - className = '', |
103 |
| - durationOnClick = 1000, |
104 |
| -}: TooltipProps) => { |
105 |
| - const [isOpen, setIsOpen] = useState(false); |
| 96 | +const Tooltip = forwardRef<FlashTooltipRef, TooltipProps>( |
| 97 | + ( |
| 98 | + { |
| 99 | + behavior = 'hover', |
| 100 | + arrowWidth = 8, |
| 101 | + background = '', |
| 102 | + border = '', |
| 103 | + children = null, |
| 104 | + color = '', |
| 105 | + content, |
| 106 | + fadeDuration = 150, |
| 107 | + fadeEasing = 'linear', |
| 108 | + fixed = false, |
| 109 | + fontSize = '', |
| 110 | + offset = 0, |
| 111 | + padding = 0.7, |
| 112 | + placement = 'top', |
| 113 | + radius = 0, |
| 114 | + zIndex = 1, |
| 115 | + className = '', |
| 116 | + durationOnClick = 1000, |
| 117 | + }, |
| 118 | + ref |
| 119 | + ) => { |
| 120 | + const [isOpen, setIsOpen] = useState(false); |
| 121 | + |
| 122 | + const timeDelay = (msDuration: number) => |
| 123 | + new Promise((resolve) => setTimeout(() => resolve(true), msDuration)); |
106 | 124 |
|
107 |
| - const timeDelay = (msDuration: number) => |
108 |
| - new Promise((resolve) => setTimeout(() => resolve(true), msDuration)); |
| 125 | + const showTooltip = () => setIsOpen(true); |
109 | 126 |
|
110 |
| - const showTooltip = () => setIsOpen(true); |
| 127 | + const hideTooltip = () => setIsOpen(false); |
111 | 128 |
|
112 |
| - const hideTooltip = () => setIsOpen(false); |
| 129 | + const flashTooltip = async () => { |
| 130 | + if (isOpen) return; |
113 | 131 |
|
114 |
| - const flashTooltip = async () => { |
115 |
| - if (isOpen) return; |
| 132 | + showTooltip(); |
| 133 | + await timeDelay(durationOnClick); |
| 134 | + hideTooltip(); |
| 135 | + }; |
116 | 136 |
|
117 |
| - showTooltip(); |
118 |
| - await timeDelay(durationOnClick); |
119 |
| - hideTooltip(); |
120 |
| - }; |
| 137 | + useImperativeHandle(ref, () => ({ |
| 138 | + flashTooltip, |
| 139 | + })); |
121 | 140 |
|
122 |
| - const tooltipElement = ( |
123 |
| - <TooltipWrapper |
124 |
| - isOpen={!children || fixed ? true : isOpen} |
125 |
| - placement={placement} |
126 |
| - offset={offset + arrowWidth} |
127 |
| - zIndex={zIndex} |
128 |
| - fadeEasing={fadeEasing} |
129 |
| - fadeDuration={fadeDuration} |
130 |
| - > |
131 |
| - <Bubble |
132 |
| - background={background} |
133 |
| - border={border} |
134 |
| - color={color} |
135 |
| - radius={radius} |
136 |
| - fontSize={fontSize} |
137 |
| - padding={padding} |
| 141 | + const tooltipElement = ( |
| 142 | + <TooltipWrapper |
| 143 | + isOpen={!children || fixed ? true : isOpen} |
| 144 | + placement={placement} |
| 145 | + offset={offset + arrowWidth} |
| 146 | + zIndex={zIndex} |
| 147 | + fadeEasing={fadeEasing} |
| 148 | + fadeDuration={fadeDuration} |
138 | 149 | >
|
139 |
| - <Arrow width={arrowWidth} background={background} border={border} placement={placement} /> |
140 |
| - {content} |
141 |
| - </Bubble> |
142 |
| - </TooltipWrapper> |
143 |
| - ); |
| 150 | + <Bubble |
| 151 | + background={background} |
| 152 | + border={border} |
| 153 | + color={color} |
| 154 | + radius={radius} |
| 155 | + fontSize={fontSize} |
| 156 | + padding={padding} |
| 157 | + > |
| 158 | + <Arrow width={arrowWidth} background={background} border={border} placement={placement} /> |
| 159 | + {content} |
| 160 | + </Bubble> |
| 161 | + </TooltipWrapper> |
| 162 | + ); |
144 | 163 |
|
145 |
| - return ( |
146 |
| - <Container |
147 |
| - className={className} |
148 |
| - onClick={behavior === 'click' ? flashTooltip : undefined} |
149 |
| - onMouseEnter={behavior === 'hover' && !fixed ? showTooltip : undefined} |
150 |
| - onMouseLeave={behavior === 'hover' && !fixed ? hideTooltip : undefined} |
151 |
| - > |
152 |
| - <> |
153 |
| - {children} |
154 |
| - {tooltipElement} |
155 |
| - </> |
156 |
| - </Container> |
157 |
| - ); |
158 |
| -}; |
| 164 | + return ( |
| 165 | + <Container |
| 166 | + className={className} |
| 167 | + onClick={behavior === 'click' ? flashTooltip : undefined} |
| 168 | + onMouseEnter={behavior === 'hover' && !fixed ? showTooltip : undefined} |
| 169 | + onMouseLeave={behavior === 'hover' && !fixed ? hideTooltip : undefined} |
| 170 | + > |
| 171 | + <> |
| 172 | + {children} |
| 173 | + {tooltipElement} |
| 174 | + </> |
| 175 | + </Container> |
| 176 | + ); |
| 177 | + } |
| 178 | +); |
| 179 | + |
| 180 | +Tooltip.displayName = 'Tooltip'; |
159 | 181 |
|
160 | 182 | export default Tooltip;
|
0 commit comments