Tooltip components

Summary: _typescript_

Reviewed By: passy, bnelo12

Differential Revision: D16830063

fbshipit-source-id: bd0cf3c109caba74ee5a8f9915d46630007702fe
This commit is contained in:
Daniel Büchele
2019-08-20 03:18:32 -07:00
committed by Facebook Github Bot
parent 5770a206be
commit 2a76461fb8
5 changed files with 116 additions and 76 deletions

View File

@@ -18,7 +18,7 @@ import {createStore} from 'redux';
import {persistStore} from 'redux-persist'; import {persistStore} from 'redux-persist';
import reducers, {Actions, State as StoreState} from './reducers/index'; import reducers, {Actions, State as StoreState} from './reducers/index';
import dispatcher from './dispatcher/index'; import dispatcher from './dispatcher/index';
import TooltipProvider from './ui/components/TooltipProvider.js'; import TooltipProvider from './ui/components/TooltipProvider';
import config from './utils/processConfig'; import config from './utils/processConfig';
import {stateSanitizer} from './utils/reduxDevToolsConfig'; import {stateSanitizer} from './utils/reduxDevToolsConfig';
import {initLauncherHooks} from './utils/launcher'; import {initLauncherHooks} from './utils/launcher';

View File

@@ -5,12 +5,9 @@
* @format * @format
*/ */
import type TooltipProvider from './TooltipProvider.js'; import TooltipProvider, {TooltipOptions} from './TooltipProvider';
import type {TooltipOptions} from './TooltipProvider.js'; import styled from 'react-emotion';
import React, {Component} from 'react';
import styled from '../styled/index.js';
import {Component} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const TooltipContainer = styled('div')({ const TooltipContainer = styled('div')({
@@ -19,14 +16,14 @@ const TooltipContainer = styled('div')({
type TooltipProps = { type TooltipProps = {
/** Content shown in the tooltip */ /** Content shown in the tooltip */
title: React$Node, title: React.ReactNode;
/** Component that will show the tooltip */ /** Component that will show the tooltip */
children: React$Node, children: React.ReactNode;
options?: TooltipOptions, options?: TooltipOptions;
}; };
type TooltipState = { type TooltipState = {
open: boolean, open: boolean;
}; };
export default class Tooltip extends Component<TooltipProps, TooltipState> { export default class Tooltip extends Component<TooltipProps, TooltipState> {
@@ -35,10 +32,10 @@ export default class Tooltip extends Component<TooltipProps, TooltipState> {
}; };
context: { context: {
TOOLTIP_PROVIDER: TooltipProvider, TOOLTIP_PROVIDER: TooltipProvider;
}; };
ref: ?HTMLDivElement; ref: HTMLDivElement | null;
state = { state = {
open: false, open: false,
@@ -66,7 +63,7 @@ export default class Tooltip extends Component<TooltipProps, TooltipState> {
this.setState({open: false}); this.setState({open: false});
}; };
setRef = (ref: ?HTMLDivElement) => { setRef = (ref: HTMLDivElement | null) => {
this.ref = ref; this.ref = ref;
}; };

View File

@@ -5,11 +5,24 @@
* @format * @format
*/ */
import styled from '../styled/index.js'; import styled from 'react-emotion';
import {colors} from './colors.tsx'; import {colors} from './colors';
import {Component} from 'react'; import {Component} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {
TopProperty,
LeftProperty,
BottomProperty,
RightProperty,
BackgroundColorProperty,
LineHeightProperty,
PaddingProperty,
BorderRadiusProperty,
MaxWidthProperty,
ColorProperty,
WidthProperty,
} from 'csstype';
import React from 'react';
const defaultOptions = { const defaultOptions = {
backgroundColor: colors.blueGrey, backgroundColor: colors.blueGrey,
@@ -25,19 +38,34 @@ const defaultOptions = {
}; };
export type TooltipOptions = { export type TooltipOptions = {
backgroundColor?: string, backgroundColor?: string;
position?: 'below' | 'above' | 'toRight' | 'toLeft', position?: 'below' | 'above' | 'toRight' | 'toLeft';
color?: string, color?: string;
showTail?: boolean, showTail?: boolean;
maxWidth?: string, maxWidth?: string;
width?: string, width?: string;
borderRadius?: number, borderRadius?: number;
padding?: string, padding?: string;
lineHeight?: string, lineHeight?: string;
delay?: number, // in milliseconds delay?: number; // in milliseconds
}; };
const TooltipBubble = styled('div')(props => ({ const TooltipBubble = styled('div')(
(props: {
top: TopProperty<number>;
left: LeftProperty<number>;
bottom: BottomProperty<number>;
right: RightProperty<number>;
options: {
backgroundColor: BackgroundColorProperty;
lineHeight: LineHeightProperty<number>;
padding: PaddingProperty<number>;
borderRadius: BorderRadiusProperty<number>;
width: WidthProperty<number>;
maxWidth: MaxWidthProperty<number>;
color: ColorProperty;
};
}) => ({
position: 'absolute', position: 'absolute',
zIndex: 99999999999, zIndex: 99999999999,
backgroundColor: props.options.backgroundColor, backgroundColor: props.options.backgroundColor,
@@ -51,7 +79,8 @@ const TooltipBubble = styled('div')(props => ({
bottom: props.bottom, bottom: props.bottom,
right: props.right, right: props.right,
color: props.options.color, color: props.options.color,
})); }),
);
// vertical offset on bubble when position is 'below' // vertical offset on bubble when position is 'below'
const BUBBLE_BELOW_POSITION_VERTICAL_OFFSET = -10; const BUBBLE_BELOW_POSITION_VERTICAL_OFFSET = -10;
@@ -64,7 +93,16 @@ const TAIL_AB_POSITION_HORIZONTAL_OFFSET = 15;
// vertical offset on tail when position is 'toLeft' or 'toRight' // vertical offset on tail when position is 'toLeft' or 'toRight'
const TAIL_LR_POSITION_HORIZONTAL_OFFSET = 5; const TAIL_LR_POSITION_HORIZONTAL_OFFSET = 5;
const TooltipTail = styled('div')(props => ({ const TooltipTail = styled('div')(
(props: {
top: TopProperty<number>;
left: LeftProperty<number>;
bottom: BottomProperty<number>;
right: RightProperty<number>;
options: {
backgroundColor: BackgroundColorProperty;
};
}) => ({
position: 'absolute', position: 'absolute',
display: 'block', display: 'block',
whiteSpace: 'pre', whiteSpace: 'pre',
@@ -78,26 +116,27 @@ const TooltipTail = styled('div')(props => ({
left: props.left, left: props.left,
bottom: props.bottom, bottom: props.bottom,
right: props.right, right: props.right,
})); }),
);
type TooltipProps = { type TooltipProps = {
children: React$Node, children: React.ReactNode;
}; };
type TooltipObject = { type TooltipObject = {
rect: ClientRect, rect: ClientRect;
title: React$Node, title: React.ReactNode;
options: TooltipOptions, options: TooltipOptions;
}; };
type TooltipState = { type TooltipState = {
tooltip: ?TooltipObject, tooltip: TooltipObject | null | undefined;
timeoutID: ?TimeoutID, timeoutID: ReturnType<typeof setTimeout> | null | undefined;
}; };
export default class TooltipProvider extends Component< export default class TooltipProvider extends Component<
TooltipProps, TooltipProps,
TooltipState, TooltipState
> { > {
static childContextTypes = { static childContextTypes = {
TOOLTIP_PROVIDER: PropTypes.object, TOOLTIP_PROVIDER: PropTypes.object,
@@ -112,7 +151,11 @@ export default class TooltipProvider extends Component<
return {TOOLTIP_PROVIDER: this}; return {TOOLTIP_PROVIDER: this};
} }
open(container: HTMLDivElement, title: React$Node, options: TooltipOptions) { open(
container: HTMLDivElement,
title: React.ReactNode,
options: TooltipOptions,
) {
const node = container.childNodes[0]; const node = container.childNodes[0];
if (node == null || !(node instanceof HTMLElement)) { if (node == null || !(node instanceof HTMLElement)) {
return; return;
@@ -153,10 +196,10 @@ export default class TooltipProvider extends Component<
return null; return null;
} }
let left = 'auto'; let left: LeftProperty<number> = 'auto';
let top = 'auto'; let top: TopProperty<number> = 'auto';
let bottom = 'auto'; let bottom: BottomProperty<number> = 'auto';
let right = 'auto'; let right: RightProperty<number> = 'auto';
if (opts.position === 'below') { if (opts.position === 'below') {
left = tooltip.rect.left + TAIL_AB_POSITION_HORIZONTAL_OFFSET; left = tooltip.rect.left + TAIL_AB_POSITION_HORIZONTAL_OFFSET;
@@ -189,10 +232,10 @@ export default class TooltipProvider extends Component<
getTooltipBubble(tooltip: TooltipObject) { getTooltipBubble(tooltip: TooltipObject) {
const opts = Object.assign(defaultOptions, tooltip.options); const opts = Object.assign(defaultOptions, tooltip.options);
let left = 'auto'; let left: LeftProperty<number> = 'auto';
let top = 'auto'; let top: TopProperty<number> = 'auto';
let bottom = 'auto'; let bottom: BottomProperty<number> = 'auto';
let right = 'auto'; let right: RightProperty<number> = 'auto';
if (opts.position === 'below') { if (opts.position === 'below') {
left = tooltip.rect.left; left = tooltip.rect.left;

View File

@@ -8,7 +8,7 @@
import DataDescription from './DataDescription.js'; import DataDescription from './DataDescription.js';
import {Component} from 'react'; import {Component} from 'react';
import ContextMenu from '../ContextMenu.js'; import ContextMenu from '../ContextMenu.js';
import Tooltip from '../Tooltip.js'; import Tooltip from '../Tooltip.tsx';
import styled from '../../styled/index.js'; import styled from '../../styled/index.js';
import DataPreview from './DataPreview.js'; import DataPreview from './DataPreview.js';
import createPaste from '../../../fb-stubs/createPaste.tsx'; import createPaste from '../../../fb-stubs/createPaste.tsx';

View File

@@ -130,8 +130,8 @@ export {default as TextParagraph} from './components/TextParagraph.tsx';
export {default as Link} from './components/Link.js'; export {default as Link} from './components/Link.js';
export {default as PathBreadcrumbs} from './components/PathBreadcrumbs.tsx'; export {default as PathBreadcrumbs} from './components/PathBreadcrumbs.tsx';
export {default as ModalOverlay} from './components/ModalOverlay.tsx'; export {default as ModalOverlay} from './components/ModalOverlay.tsx';
export {default as Tooltip} from './components/Tooltip.js'; export {default as Tooltip} from './components/Tooltip.tsx';
export {default as TooltipProvider} from './components/TooltipProvider.js'; export {default as TooltipProvider} from './components/TooltipProvider.tsx';
export {default as ResizeSensor} from './components/ResizeSensor.tsx'; export {default as ResizeSensor} from './components/ResizeSensor.tsx';
export {default as StatusIndicator} from './components/StatusIndicator.tsx'; export {default as StatusIndicator} from './components/StatusIndicator.tsx';