Update Tooltip implementation for Flipper
Summary:
Basic tooltips available. Use is:
<Tooltip
title="This is what will show up inside the tooltip"
options={{ // can include any or none of these (if not included default will be used)
position, // 'above', 'below', 'toRight', or 'toLeft'
showTail, // whether or not tooltip should have tail
delay, // how long to wait on hover before showing tooltip
// supported css properties
backgroundColor,
color,
maxWidth,
width,
borderRadius,
padding,
lineHeight,
}}>
<ElementTooltipWillShowUpFor/>
</Tooltip>
Reviewed By: danielbuechele
Differential Revision: D9596287
fbshipit-source-id: 233b1ad01b96264bbc1f62f3798e3d69d1ab4bae
This commit is contained in:
committed by
Facebook Github Bot
parent
cf3cb0d08f
commit
d26779cd16
@@ -6,34 +6,93 @@
|
||||
*/
|
||||
|
||||
import styled from '../styled/index.js';
|
||||
import {colors} from './colors.js';
|
||||
import {Component} from 'react';
|
||||
|
||||
const PropTypes = require('prop-types');
|
||||
|
||||
const TooltipBubble = styled('div')(props => ({
|
||||
backgroundColor: '#000',
|
||||
lineHeight: '25px',
|
||||
padding: '0 6px',
|
||||
borderRadius: 4,
|
||||
position: 'absolute',
|
||||
const defaultOptions = {
|
||||
backgroundColor: colors.blueGrey,
|
||||
position: 'below',
|
||||
color: colors.white,
|
||||
showTail: true,
|
||||
maxWidth: '200px',
|
||||
width: 'auto',
|
||||
borderRadius: 4,
|
||||
padding: '6px',
|
||||
lineHeight: '20px',
|
||||
delay: 0,
|
||||
};
|
||||
|
||||
export type TooltipOptions = {
|
||||
backgroundColor?: string,
|
||||
position?: 'below' | 'above' | 'toRight' | 'toLeft',
|
||||
color?: string,
|
||||
showTail?: boolean,
|
||||
maxWidth?: string,
|
||||
width?: string,
|
||||
borderRadius?: number,
|
||||
padding?: string,
|
||||
lineHeight?: string,
|
||||
delay?: number, // in milliseconds
|
||||
};
|
||||
|
||||
const TooltipBubble = styled('div')(props => ({
|
||||
position: 'absolute',
|
||||
zIndex: 99999999999,
|
||||
backgroundColor: props.options.backgroundColor,
|
||||
lineHeight: props.options.lineHeight,
|
||||
padding: props.options.padding,
|
||||
borderRadius: props.options.borderRadius,
|
||||
width: props.options.width,
|
||||
maxWidth: props.options.maxWidth,
|
||||
top: props.top,
|
||||
left: props.left,
|
||||
zIndex: 99999999999,
|
||||
pointerEvents: 'none',
|
||||
color: '#fff',
|
||||
marginTop: '-30px',
|
||||
bottom: props.bottom,
|
||||
right: props.right,
|
||||
color: props.options.color,
|
||||
}));
|
||||
|
||||
// vertical offset on bubble when position is 'below'
|
||||
const BUBBLE_BELOW_POSITION_VERTICAL_OFFSET = -10;
|
||||
// horizontal offset on bubble when position is 'toLeft' or 'toRight'
|
||||
const BUBBLE_LR_POSITION_HORIZONTAL_OFFSET = 5;
|
||||
// offset on bubble when tail is showing
|
||||
const BUBBLE_SHOWTAIL_OFFSET = 5;
|
||||
// horizontal offset on tail when position is 'above' or 'below'
|
||||
const TAIL_AB_POSITION_HORIZONTAL_OFFSET = 15;
|
||||
// vertical offset on tail when position is 'toLeft' or 'toRight'
|
||||
const TAIL_LR_POSITION_HORIZONTAL_OFFSET = 5;
|
||||
|
||||
const TooltipTail = styled('div')(props => ({
|
||||
position: 'absolute',
|
||||
display: 'block',
|
||||
whiteSpace: 'pre',
|
||||
height: '10px',
|
||||
width: '10px',
|
||||
lineHeight: '0',
|
||||
zIndex: 99999999998,
|
||||
transform: 'rotate(45deg)',
|
||||
backgroundColor: props.options.backgroundColor,
|
||||
top: props.top,
|
||||
left: props.left,
|
||||
bottom: props.bottom,
|
||||
right: props.right,
|
||||
}));
|
||||
|
||||
type TooltipProps = {
|
||||
children: React$Node,
|
||||
};
|
||||
|
||||
type TooltipObject = {
|
||||
rect: ClientRect,
|
||||
title: React$Node,
|
||||
options: TooltipOptions,
|
||||
};
|
||||
|
||||
type TooltipState = {
|
||||
tooltip: ?{
|
||||
rect: ClientRect,
|
||||
title: React$Node,
|
||||
},
|
||||
tooltip: ?TooltipObject,
|
||||
timeoutID: ?TimeoutID,
|
||||
};
|
||||
|
||||
export default class TooltipProvider extends Component<
|
||||
@@ -46,42 +105,149 @@ export default class TooltipProvider extends Component<
|
||||
|
||||
state = {
|
||||
tooltip: null,
|
||||
timeoutID: undefined,
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {TOOLTIP_PROVIDER: this};
|
||||
}
|
||||
|
||||
open(container: HTMLDivElement, title: React$Node) {
|
||||
open(container: HTMLDivElement, title: React$Node, options: TooltipOptions) {
|
||||
const node = container.childNodes[0];
|
||||
if (node == null || !(node instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.delay) {
|
||||
this.state.timeoutID = setTimeout(() => {
|
||||
this.setState({
|
||||
tooltip: {
|
||||
rect: node.getBoundingClientRect(),
|
||||
title,
|
||||
options: options,
|
||||
},
|
||||
});
|
||||
}, options.delay);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
tooltip: {
|
||||
rect: node.getBoundingClientRect(),
|
||||
title,
|
||||
options: options,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.state.timeoutID) {
|
||||
clearTimeout(this.state.timeoutID);
|
||||
}
|
||||
this.setState({tooltip: null});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {tooltip} = this.state;
|
||||
|
||||
let tooltipElem = null;
|
||||
if (tooltip != null) {
|
||||
tooltipElem = (
|
||||
<TooltipBubble top={tooltip.rect.top} left={tooltip.rect.left}>
|
||||
{tooltip.title}
|
||||
</TooltipBubble>
|
||||
);
|
||||
getTooltipTail(tooltip: TooltipObject) {
|
||||
const opts = Object.assign(defaultOptions, tooltip.options);
|
||||
if (!opts.showTail) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [tooltipElem, this.props.children];
|
||||
let left = 'auto';
|
||||
let top = 'auto';
|
||||
let bottom = 'auto';
|
||||
let right = 'auto';
|
||||
|
||||
if (opts.position === 'below') {
|
||||
left = tooltip.rect.left + TAIL_AB_POSITION_HORIZONTAL_OFFSET;
|
||||
top = tooltip.rect.bottom;
|
||||
} else if (opts.position === 'above') {
|
||||
left = tooltip.rect.left + TAIL_AB_POSITION_HORIZONTAL_OFFSET;
|
||||
bottom = window.innerHeight - tooltip.rect.top;
|
||||
} else if (opts.position === 'toRight') {
|
||||
left = tooltip.rect.right + TAIL_LR_POSITION_HORIZONTAL_OFFSET;
|
||||
top = tooltip.rect.top;
|
||||
} else if (opts.position === 'toLeft') {
|
||||
right =
|
||||
window.innerWidth -
|
||||
tooltip.rect.left +
|
||||
TAIL_LR_POSITION_HORIZONTAL_OFFSET;
|
||||
top = tooltip.rect.top;
|
||||
}
|
||||
|
||||
return (
|
||||
<TooltipTail
|
||||
key="tail"
|
||||
top={top}
|
||||
left={left}
|
||||
bottom={bottom}
|
||||
right={right}
|
||||
options={opts}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
getTooltipBubble(tooltip: TooltipObject) {
|
||||
const opts = Object.assign(defaultOptions, tooltip.options);
|
||||
let left = 'auto';
|
||||
let top = 'auto';
|
||||
let bottom = 'auto';
|
||||
let right = 'auto';
|
||||
|
||||
if (opts.position === 'below') {
|
||||
left = tooltip.rect.left;
|
||||
top = tooltip.rect.bottom;
|
||||
if (opts.showTail) {
|
||||
top += BUBBLE_SHOWTAIL_OFFSET;
|
||||
}
|
||||
} else if (opts.position === 'above') {
|
||||
bottom = window.innerHeight - tooltip.rect.top;
|
||||
if (opts.showTail) {
|
||||
bottom += BUBBLE_SHOWTAIL_OFFSET;
|
||||
}
|
||||
left = tooltip.rect.left;
|
||||
} else if (opts.position === 'toRight') {
|
||||
left = tooltip.rect.right + BUBBLE_LR_POSITION_HORIZONTAL_OFFSET;
|
||||
if (opts.showTail) {
|
||||
left += BUBBLE_SHOWTAIL_OFFSET;
|
||||
}
|
||||
top = tooltip.rect.top + BUBBLE_BELOW_POSITION_VERTICAL_OFFSET;
|
||||
} else if (opts.position === 'toLeft') {
|
||||
right =
|
||||
window.innerWidth -
|
||||
tooltip.rect.left +
|
||||
BUBBLE_LR_POSITION_HORIZONTAL_OFFSET;
|
||||
if (opts.showTail) {
|
||||
right += BUBBLE_SHOWTAIL_OFFSET;
|
||||
}
|
||||
top = tooltip.rect.top + BUBBLE_BELOW_POSITION_VERTICAL_OFFSET;
|
||||
}
|
||||
|
||||
return (
|
||||
<TooltipBubble
|
||||
key="bubble"
|
||||
top={top}
|
||||
left={left}
|
||||
bottom={bottom}
|
||||
right={right}
|
||||
options={opts}>
|
||||
{tooltip.title}
|
||||
</TooltipBubble>
|
||||
);
|
||||
}
|
||||
|
||||
getTooltipElement() {
|
||||
const {tooltip} = this.state;
|
||||
return (
|
||||
tooltip &&
|
||||
tooltip.title && [
|
||||
this.getTooltipTail(tooltip),
|
||||
this.getTooltipBubble(tooltip),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return [this.getTooltipElement(), this.props.children];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user