Skip to content

Commit b05416d

Browse files
committed
Implement tooltip as tsx
1 parent 9a9df1f commit b05416d

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import React from 'react';
2+
3+
import _JSXStyle from 'styled-jsx/style';
4+
import {TooltipProps} from '../../types';
5+
6+
/**
7+
* A tooltip with an absolute position.
8+
*/
9+
const Tooltip = ({
10+
show = true,
11+
targetable = false,
12+
direction = 'right',
13+
border_color = '#d6d6d6',
14+
background_color = 'white',
15+
className = '',
16+
zindex = 1,
17+
loading_text = 'Loading...',
18+
...props
19+
}: TooltipProps) => {
20+
const {bbox, id} = props;
21+
const show_tooltip = show && bbox;
22+
23+
const ctx = window.dash_component_api.useDashContext();
24+
const is_loading = ctx.useLoading();
25+
26+
return (
27+
<>
28+
<div className="dcc-tooltip-bounding-box">
29+
<div
30+
className={`hover hover-${direction}`}
31+
data-dash-is-loading={is_loading}
32+
>
33+
<span
34+
id={id}
35+
className={`hover-content ${className}`}
36+
style={props.style}
37+
>
38+
{is_loading ? (
39+
<span>{loading_text}</span>
40+
) : (
41+
props.children
42+
)}
43+
</span>
44+
</div>
45+
</div>
46+
<style jsx>{`
47+
.dcc-tooltip-bounding-box {
48+
position: absolute;
49+
top: ${bbox?.y0 || 0}px;
50+
left: ${bbox?.x0 || 0}px;
51+
width: ${bbox?.x1 - bbox?.x0 || 0}px;
52+
height: ${bbox?.y1 - bbox?.y0 || 0}px;
53+
display: ${show_tooltip ? 'inline-block' : 'none'};
54+
pointer-events: ${targetable ? 'auto' : 'none'};
55+
}
56+
.hover {
57+
position: absolute;
58+
}
59+
.hover-right {
60+
/* Offset so that the triangle caret lands directly on what's hovered */
61+
transform: translate(5px, 0);
62+
top: 50%;
63+
left: 100%;
64+
}
65+
.hover-left {
66+
transform: translate(-5px, 0);
67+
top: 50%;
68+
}
69+
.hover-bottom {
70+
transform: translate(0, 6px);
71+
top: 100%;
72+
left: 50%;
73+
}
74+
.hover-top {
75+
transform: translate(0, -5px);
76+
left: 50%;
77+
}
78+
.hover-content {
79+
position: absolute;
80+
border: 1px solid ${border_color};
81+
border-radius: 2px;
82+
padding: 5px 10px;
83+
background: ${background_color};
84+
white-space: nowrap;
85+
z-index: ${zindex};
86+
pointer-events: none;
87+
}
88+
.hover .hover-content,
89+
.hover-right .hover-content {
90+
transform: translate(0, -50%);
91+
}
92+
.hover-left .hover-content {
93+
transform: translate(-100%, -50%);
94+
}
95+
.hover-top .hover-content {
96+
transform: translate(-50%, -100%);
97+
}
98+
.hover-bottom .hover-content {
99+
transform: translate(-50%, 0);
100+
}
101+
/* Add a small triangle on the left side of the box */
102+
.hover:before,
103+
.hover:after {
104+
content: '';
105+
width: 0;
106+
height: 0;
107+
position: absolute;
108+
border-style: solid;
109+
top: -6px;
110+
z-index: ${zindex};
111+
}
112+
.hover:before,
113+
.hover:after,
114+
.hover-right:before,
115+
.hover-right:after {
116+
border-width: 6px 6px 6px 0;
117+
}
118+
.hover-top:before,
119+
.hover-top:after {
120+
border-width: 6px 6px 0 6px;
121+
}
122+
.hover-bottom:before,
123+
.hover-bottom:after {
124+
border-width: 0 6px 6px 6px;
125+
}
126+
.hover-left:before,
127+
.hover-left:after {
128+
border-width: 6px 0 6px 6px;
129+
}
130+
.hover:before,
131+
.hover-right:before {
132+
border-color: transparent ${border_color} transparent
133+
transparent;
134+
left: -5px;
135+
}
136+
.hover:after,
137+
.hover-right:after {
138+
border-color: transparent ${background_color} transparent
139+
transparent;
140+
left: -4px;
141+
}
142+
.hover-left:before {
143+
border-color: transparent transparent transparent
144+
${border_color};
145+
left: -1px;
146+
}
147+
.hover-left:after {
148+
border-color: transparent transparent transparent
149+
${background_color};
150+
left: -2px;
151+
}
152+
.hover-top:before,
153+
.hover-top:after,
154+
.hover-bottom:before,
155+
.hover-bottom:after {
156+
left: -6px;
157+
}
158+
.hover-bottom:before {
159+
border-color: transparent transparent ${border_color}
160+
transparent;
161+
}
162+
.hover-bottom:after {
163+
border-color: transparent transparent ${background_color}
164+
transparent;
165+
top: -5px;
166+
}
167+
.hover-top:before {
168+
border-color: ${border_color} transparent transparent
169+
transparent;
170+
top: -1px;
171+
}
172+
.hover-top:after {
173+
border-color: ${background_color} transparent transparent
174+
transparent;
175+
top: -2px;
176+
}
177+
`}</style>
178+
</>
179+
);
180+
};
181+
182+
export default Tooltip;

components/dash-core-components/src/types.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,3 +517,81 @@ export interface TextAreaProps {
517517
*/
518518
persistence_type?: PersistenceTypes;
519519
}
520+
521+
export interface TooltipProps {
522+
/**
523+
* The contents of the tooltip
524+
*/
525+
children?: React.ReactNode;
526+
527+
/**
528+
* The ID of this component, used to identify dash components
529+
* in callbacks. The ID needs to be unique across all of the
530+
* components in an app.
531+
*/
532+
id?: string;
533+
534+
/**
535+
* The class of the tooltip
536+
*/
537+
className?: string;
538+
539+
/**
540+
* The style of the tooltip
541+
*/
542+
style?: React.CSSProperties;
543+
544+
/**
545+
* The bounding box coordinates of the item to label, in px relative to
546+
* the positioning parent of the Tooltip component.
547+
*/
548+
bbox?: {
549+
x0: number;
550+
y0: number;
551+
x1: number;
552+
y1: number;
553+
};
554+
555+
/**
556+
* Whether to show the tooltip
557+
*/
558+
show?: boolean;
559+
560+
/**
561+
* The side of the `bbox` on which the tooltip should open.
562+
*/
563+
direction?: 'top' | 'right' | 'bottom' | 'left';
564+
565+
/**
566+
* Color of the tooltip border, as a CSS color string.
567+
*/
568+
border_color?: string;
569+
570+
/**
571+
* Color of the tooltip background, as a CSS color string.
572+
*/
573+
background_color?: string;
574+
575+
/**
576+
* The text displayed in the tooltip while loading
577+
*/
578+
loading_text?: string;
579+
580+
/**
581+
* The `z-index` CSS property to assign to the tooltip. Components with
582+
* higher values will be displayed on top of components with lower values.
583+
*/
584+
zindex?: number;
585+
586+
/**
587+
* Whether the tooltip itself can be targeted by pointer events.
588+
* For tooltips triggered by hover events, typically this should be left
589+
* `false` to avoid the tooltip interfering with those same events.
590+
*/
591+
targetable?: boolean;
592+
593+
/**
594+
* Dash-assigned callback that gets fired when the value changes.
595+
*/
596+
setProps: (props: Partial<TooltipProps>) => void;
597+
}

0 commit comments

Comments
 (0)