Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Re-add new tooltip component #982

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
306 changes: 306 additions & 0 deletions src/components/AbsoluteTooltip.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
import PropTypes from 'prop-types';

import _JSXStyle from 'styled-jsx/style'; // eslint-disable-line no-unused-vars

/**
* A tooltip with an absolute position.
*/
const AbsoluteTooltip = props => {
const {bbox, colors, loading_state} = props;
const is_loading = loading_state && loading_state.is_loading;

return (
<>
<div className={`dcc-tooltip-bounding-box`}>
<span
data-dash-is-loading={is_loading || undefined}
className={`hover hover-${props.direction}`}
>
<span
className={`hover-content ${props.className}`}
style={props.style}
>
{is_loading ? (
<span>{props.loading_text}</span>
) : (
props.children
)}
</span>
</span>
</div>
<style jsx>{`
.dcc-tooltip-bounding-box {
position: absolute;
top: ${bbox.y0}px;
left: ${bbox.x0}px;
width: ${bbox.x1 - bbox.x0}px;
height: ${bbox.y1 - bbox.y0}px;
display: ${props.show ? 'inline-block' : 'none'};
pointer-events: ${props.targetable ? 'auto' : 'none'};
}

.hover {
position: absolute;
}

.hover-right {
/* Offset so that the triangle caret lands directly on what's hovered */
transform: translate(5px, 0);
top: 50%;
left: 100%;
}

.hover-left {
transform: translate(-5px, 0);
top: 50%;
}

.hover-bottom {
transform: translate(0, 6px);
top: 100%;
left: 50%;
}

.hover-top {
transform: translate(0, -5px);
left: 50%;
}

.hover-content {
position: absolute;
border: 1px solid ${colors.border};
border-radius: 2px;
padding: 5px 10px;
background: ${colors.background};
white-space: nowrap;
z-index: ${props.zindex};
pointer-events: none;
}

.hover .hover-content,
.hover-right .hover-content {
transform: translate(0, -50%);
}

.hover-left .hover-content {
transform: translate(-100%, -50%);
}

.hover-top .hover-content {
transform: translate(-50%, -100%);
}

.hover-bottom .hover-content {
transform: translate(-50%, 0);
}

/* Add a small triangle on the left side of the box */
.hover:before,
.hover:after {
content: '';
width: 0;
height: 0;
position: absolute;
border-style: solid;
top: -6px;
}

.hover:before,
.hover:after,
.hover-right:before,
.hover-right:after {
border-width: 6px 6px 6px 0;
}

.hover-top:before,
.hover-top:after {
border-width: 6px 6px 0 6px;
}

.hover-bottom:before,
.hover-bottom:after {
border-width: 0 6px 6px 6px;
}

.hover-left:before,
.hover-left:after {
border-width: 6px 0 6px 6px;
}

.hover:before,
.hover-right:before {
border-color: transparent ${colors.border} transparent
transparent;
left: -5px;
}

.hover:after,
.hover-right:after {
border-color: transparent ${colors.background} transparent
transparent;
left: -4px;
}

.hover-left:before {
border-color: transparent transparent transparent
${colors.border};
left: -1px;
}

.hover-left:after {
border-color: transparent transparent transparent
${colors.background};
left: -2px;
}

.hover-top:before,
.hover-top:after,
.hover-bottom:before,
.hover-bottom:after {
left: -6px;
}

.hover-bottom:before {
border-color: transparent transparent ${colors.border}
transparent;
}

.hover-bottom:after {
border-color: transparent transparent ${colors.background}
transparent;
top: -5px;
}

.hover-top:before {
border-color: ${colors.border} transparent transparent
transparent;
top: -1px;
}

.hover-top:after {
border-color: ${colors.background} transparent transparent
transparent;
top: -2px;
}
`}</style>
</>
);
};

AbsoluteTooltip.defaultProps = {
show: true,
targetable: false,
direction: 'right',
colors: {
border: '#d6d6d6',
background: 'white',
},
className: '',
zindex: 1,
loading_text: 'Loading...',
};

AbsoluteTooltip.propTypes = {
// Dash specific props
/**
* The children of this component
*/
children: PropTypes.node,

/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* The class of the tooltip
*/
className: PropTypes.string,

/**
* The style of the tooltip
*/
style: PropTypes.object,

/**
* Dash-assigned callback that gets fired when the value changes.
*/
setProps: PropTypes.func,

// Component specific props
/**
* The bounding boxes coordinates
*/
bbox: PropTypes.exact({
x0: PropTypes.number,
y0: PropTypes.number,
x1: PropTypes.number,
y1: PropTypes.number,
}),

/**
* Whether to show the tooltip or not
*/
show: PropTypes.bool,

/**
* Defines the direction in which the hover opens.
*/
direction: PropTypes.oneOf(['top', 'right', 'bottom', 'left']),

/**
* Holds the colors used by the ToolTip component. If you set these, you should specify colors for all properties, so:
* colors: {
* border: '#d6d6d6',
* primary: '#1975FA',
* background: '#f9f9f9'
* }
*/
colors: PropTypes.exact({
border: PropTypes.string,
background: PropTypes.string,
}),

/**
* Prevents rendering of given element, while keeping child elements, e.g. script elements, active.
*/
hidden: PropTypes.string,

/**
* Object that holds the loading state object coming from dash-renderer
*/
loading_state: PropTypes.shape({
/**
* Determines if the component is loading or not
*/
is_loading: PropTypes.bool,
/**
* Holds which property is loading
*/
prop_name: PropTypes.string,
/**
* Holds the name of the component that is loading
*/
component_name: PropTypes.string,
}),

/**
* The text displayed in the tooltip when loading
*/
loading_text: PropTypes.string,

/**
* This corresponds to the `z-index` CSS property you want to assign to the tooltip. Components with higher values will be displayed first on the screen.
*/
zindex: PropTypes.number,

/**
* Whether the tooltip can be targeted (hovered, selected, interacted) or not. When set to `False`,
* the tooltip's `pointer-events` CSS property will be `none` and it can't be the target of pointer events.
*/
targetable: PropTypes.bool,
};

export default AbsoluteTooltip;
4 changes: 4 additions & 0 deletions src/fragments/Graph.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ const filterEventData = (gd, eventData, event) => {
const pointData = filter(function (o) {
return !includes(type(o), ['Object', 'Array']);
}, fullPoint);
// permit a bounding box to pass through, if present
if (has('bbox', fullPoint)) {
pointData.bbox = fullPoint.bbox;
}
if (
has('curveNumber', fullPoint) &&
has('pointNumber', fullPoint) &&
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable import/prefer-default-export */
import AbsoluteTooltip from './components/AbsoluteTooltip.react';
import ConfirmDialog from './components/ConfirmDialog.react';
import ConfirmDialogProvider from './components/ConfirmDialogProvider.react';
import Dropdown from './components/Dropdown.react';
Expand Down Expand Up @@ -28,6 +29,7 @@ import 'react-dates/lib/css/_datepicker.css';
import './components/css/[email protected]';

export {
AbsoluteTooltip,
Checklist,
ConfirmDialog,
ConfirmDialogProvider,
Expand Down