Skip to content

Commit 2717a76

Browse files
committed
new Tooltip component
1 parent f8cf5e4 commit 2717a76

File tree

1 file changed

+275
-0
lines changed

1 file changed

+275
-0
lines changed
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
import PropTypes from 'prop-types';
2+
3+
import _JSXStyle from 'styled-jsx/style'; // eslint-disable-line no-unused-vars
4+
5+
/**
6+
* A tooltip with an absolute position.
7+
*/
8+
const Tooltip = props => {
9+
const {bbox, border_color, background_color, loading_state} = props;
10+
const is_loading = loading_state?.is_loading;
11+
const show = props.show && bbox;
12+
13+
return (
14+
<>
15+
<div className='dcc-tooltip-bounding-box'>
16+
<span
17+
data-dash-is-loading={is_loading || undefined}
18+
className={`hover hover-${props.direction}`}
19+
>
20+
<span
21+
className={`hover-content ${props.className}`}
22+
style={props.style}
23+
>
24+
{is_loading ? (
25+
<span>{props.loading_text}</span>
26+
) : (
27+
props.children
28+
)}
29+
</span>
30+
</span>
31+
</div>
32+
<style jsx>{`
33+
.dcc-tooltip-bounding-box {
34+
position: absolute;
35+
top: ${bbox?.y0 || 0}px;
36+
left: ${bbox?.x0 || 0}px;
37+
width: ${bbox?.x1 - bbox?.x0 || 0}px;
38+
height: ${bbox?.y1 - bbox?.y0 || 0}px;
39+
display: ${show ? 'inline-block' : 'none'};
40+
pointer-events: ${props.targetable ? 'auto' : 'none'};
41+
}
42+
.hover {
43+
position: absolute;
44+
}
45+
.hover-right {
46+
/* Offset so that the triangle caret lands directly on what's hovered */
47+
transform: translate(5px, 0);
48+
top: 50%;
49+
left: 100%;
50+
}
51+
.hover-left {
52+
transform: translate(-5px, 0);
53+
top: 50%;
54+
}
55+
.hover-bottom {
56+
transform: translate(0, 6px);
57+
top: 100%;
58+
left: 50%;
59+
}
60+
.hover-top {
61+
transform: translate(0, -5px);
62+
left: 50%;
63+
}
64+
.hover-content {
65+
position: absolute;
66+
border: 1px solid ${border_color};
67+
border-radius: 2px;
68+
padding: 5px 10px;
69+
background: ${background_color};
70+
white-space: nowrap;
71+
z-index: ${props.zindex};
72+
pointer-events: none;
73+
}
74+
.hover .hover-content,
75+
.hover-right .hover-content {
76+
transform: translate(0, -50%);
77+
}
78+
.hover-left .hover-content {
79+
transform: translate(-100%, -50%);
80+
}
81+
.hover-top .hover-content {
82+
transform: translate(-50%, -100%);
83+
}
84+
.hover-bottom .hover-content {
85+
transform: translate(-50%, 0);
86+
}
87+
/* Add a small triangle on the left side of the box */
88+
.hover:before,
89+
.hover:after {
90+
content: '';
91+
width: 0;
92+
height: 0;
93+
position: absolute;
94+
border-style: solid;
95+
top: -6px;
96+
z-index: ${props.zindex};
97+
}
98+
.hover:before,
99+
.hover:after,
100+
.hover-right:before,
101+
.hover-right:after {
102+
border-width: 6px 6px 6px 0;
103+
}
104+
.hover-top:before,
105+
.hover-top:after {
106+
border-width: 6px 6px 0 6px;
107+
}
108+
.hover-bottom:before,
109+
.hover-bottom:after {
110+
border-width: 0 6px 6px 6px;
111+
}
112+
.hover-left:before,
113+
.hover-left:after {
114+
border-width: 6px 0 6px 6px;
115+
}
116+
.hover:before,
117+
.hover-right:before {
118+
border-color: transparent ${border_color} transparent
119+
transparent;
120+
left: -5px;
121+
}
122+
.hover:after,
123+
.hover-right:after {
124+
border-color: transparent ${background_color} transparent
125+
transparent;
126+
left: -4px;
127+
}
128+
.hover-left:before {
129+
border-color: transparent transparent transparent
130+
${border_color};
131+
left: -1px;
132+
}
133+
.hover-left:after {
134+
border-color: transparent transparent transparent
135+
${background_color};
136+
left: -2px;
137+
}
138+
.hover-top:before,
139+
.hover-top:after,
140+
.hover-bottom:before,
141+
.hover-bottom:after {
142+
left: -6px;
143+
}
144+
.hover-bottom:before {
145+
border-color: transparent transparent ${border_color}
146+
transparent;
147+
}
148+
.hover-bottom:after {
149+
border-color: transparent transparent ${background_color}
150+
transparent;
151+
top: -5px;
152+
}
153+
.hover-top:before {
154+
border-color: ${border_color} transparent transparent
155+
transparent;
156+
top: -1px;
157+
}
158+
.hover-top:after {
159+
border-color: ${background_color} transparent transparent
160+
transparent;
161+
top: -2px;
162+
}
163+
`}</style>
164+
</>
165+
);
166+
};
167+
168+
Tooltip.defaultProps = {
169+
show: true,
170+
targetable: false,
171+
direction: 'right',
172+
border_color: '#d6d6d6',
173+
background_color: 'white',
174+
className: '',
175+
zindex: 1,
176+
loading_text: 'Loading...',
177+
};
178+
179+
Tooltip.propTypes = {
180+
/**
181+
* The contents of the tooltip
182+
*/
183+
children: PropTypes.node,
184+
185+
/**
186+
* The ID of this component, used to identify dash components
187+
* in callbacks. The ID needs to be unique across all of the
188+
* components in an app.
189+
*/
190+
id: PropTypes.string,
191+
192+
/**
193+
* The class of the tooltip
194+
*/
195+
className: PropTypes.string,
196+
197+
/**
198+
* The style of the tooltip
199+
*/
200+
style: PropTypes.object,
201+
202+
/**
203+
* The bounding box coordinates of the item to label, in px relative to
204+
* the positioning parent of the Tooltip component.
205+
*/
206+
bbox: PropTypes.exact({
207+
x0: PropTypes.number,
208+
y0: PropTypes.number,
209+
x1: PropTypes.number,
210+
y1: PropTypes.number,
211+
}),
212+
213+
/**
214+
* Whether to show the tooltip
215+
*/
216+
show: PropTypes.bool,
217+
218+
/**
219+
* The side of the `bbox` on which the tooltip should open.
220+
*/
221+
direction: PropTypes.oneOf(['top', 'right', 'bottom', 'left']),
222+
223+
/**
224+
* Color of the tooltip border, as a CSS color string.
225+
*/
226+
border_color: PropTypes.string,
227+
228+
/**
229+
* Color of the tooltip background, as a CSS color string.
230+
*/
231+
background_color: PropTypes.string,
232+
233+
/**
234+
* The text displayed in the tooltip while loading
235+
*/
236+
loading_text: PropTypes.string,
237+
238+
/**
239+
* The `z-index` CSS property to assign to the tooltip. Components with
240+
* higher values will be displayed on top of components with lower values.
241+
*/
242+
zindex: PropTypes.number,
243+
244+
/**
245+
* Whether the tooltip itself can be targeted by pointer events.
246+
* For tooltips triggered by hover events, typically this should be left
247+
* `false` to avoid the tooltip interfering with those same events.
248+
*/
249+
targetable: PropTypes.bool,
250+
251+
/**
252+
* Dash-assigned callback that gets fired when the value changes.
253+
*/
254+
setProps: PropTypes.func,
255+
256+
/**
257+
* Object that holds the loading state object coming from dash-renderer
258+
*/
259+
loading_state: PropTypes.shape({
260+
/**
261+
* Determines if the component is loading or not
262+
*/
263+
is_loading: PropTypes.bool,
264+
/**
265+
* Holds which property is loading
266+
*/
267+
prop_name: PropTypes.string,
268+
/**
269+
* Holds the name of the component that is loading
270+
*/
271+
component_name: PropTypes.string,
272+
}),
273+
};
274+
275+
export default Tooltip;

0 commit comments

Comments
 (0)