Add crosshair component at hovertime
parent
803a93babc
commit
bc4288861d
|
@ -0,0 +1,41 @@
|
||||||
|
import React, {PropTypes, Component} from 'react'
|
||||||
|
import {DYGRAPH_CONTAINER_XLABEL_MARGIN} from 'shared/constants'
|
||||||
|
|
||||||
|
class Crosshair extends Component {
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
dygraph,
|
||||||
|
staticLegendHeight,
|
||||||
|
hoverTime,
|
||||||
|
handleCrosshairRef,
|
||||||
|
} = this.props
|
||||||
|
const crosshairleft = Math.round(
|
||||||
|
Math.max(-1000, dygraph.toDomXCoord(hoverTime)) || -1000 + 1
|
||||||
|
)
|
||||||
|
const crosshairHeight = `calc(100% - ${staticLegendHeight +
|
||||||
|
DYGRAPH_CONTAINER_XLABEL_MARGIN}px)`
|
||||||
|
return (
|
||||||
|
<div className="new-crosshair" ref={el => handleCrosshairRef(el)}>
|
||||||
|
<div
|
||||||
|
className="new-crosshair--crosshair"
|
||||||
|
style={{
|
||||||
|
left: crosshairleft + 1,
|
||||||
|
height: crosshairHeight,
|
||||||
|
zIndex: 1999,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {func, number, shape, string} = PropTypes
|
||||||
|
|
||||||
|
Crosshair.propTypes = {
|
||||||
|
dygraph: shape({}),
|
||||||
|
staticLegendHeight: number,
|
||||||
|
hoverTime: string,
|
||||||
|
handleCrosshairRef: func,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Crosshair
|
|
@ -9,6 +9,7 @@ import Dygraphs from 'src/external/dygraph'
|
||||||
import DygraphLegend from 'src/shared/components/DygraphLegend'
|
import DygraphLegend from 'src/shared/components/DygraphLegend'
|
||||||
import StaticLegend from 'src/shared/components/StaticLegend'
|
import StaticLegend from 'src/shared/components/StaticLegend'
|
||||||
import Annotations from 'src/shared/components/Annotations'
|
import Annotations from 'src/shared/components/Annotations'
|
||||||
|
import Crosshair from 'src/shared/components/Crosshair'
|
||||||
|
|
||||||
import getRange, {getStackedRange} from 'shared/parsing/getRangeForDygraph'
|
import getRange, {getStackedRange} from 'shared/parsing/getRangeForDygraph'
|
||||||
import {DISPLAY_OPTIONS} from 'src/dashboards/constants'
|
import {DISPLAY_OPTIONS} from 'src/dashboards/constants'
|
||||||
|
@ -196,10 +197,26 @@ class Dygraph extends Component {
|
||||||
onZoom(this.formatTimeRange(lower), this.formatTimeRange(upper))
|
onZoom(this.formatTimeRange(lower), this.formatTimeRange(upper))
|
||||||
}
|
}
|
||||||
|
|
||||||
highlightCallback = (e, x) => {
|
clampWithinGraphTimerange = timestamp => {
|
||||||
|
const [xRangeStart] = this.dygraph.xAxisRange()
|
||||||
|
return Math.max(xRangeStart, timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
eventToTimestamp = ({pageX: pxBetweenMouseAndPage}) => {
|
||||||
|
const {
|
||||||
|
left: pxBetweenGraphAndPage,
|
||||||
|
} = this.crosshairRef.getBoundingClientRect()
|
||||||
|
const graphXCoordinate = pxBetweenMouseAndPage - pxBetweenGraphAndPage
|
||||||
|
const timestamp = this.dygraph.toDataXCoord(graphXCoordinate)
|
||||||
|
const clamped = this.clampWithinGraphTimerange(timestamp)
|
||||||
|
return `${clamped}`
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightCallback = e => {
|
||||||
const {onSetHoverTime} = this.props
|
const {onSetHoverTime} = this.props
|
||||||
|
const newTime = this.eventToTimestamp(e)
|
||||||
if (onSetHoverTime) {
|
if (onSetHoverTime) {
|
||||||
onSetHoverTime(x.toString())
|
onSetHoverTime(newTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,6 +328,7 @@ class Dygraph extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAnnotationsRef = ref => (this.annotationsRef = ref)
|
handleAnnotationsRef = ref => (this.annotationsRef = ref)
|
||||||
|
handleCrosshairRef = ref => (this.crosshairRef = ref)
|
||||||
|
|
||||||
handleReceiveStaticLegendHeight = staticLegendHeight => {
|
handleReceiveStaticLegendHeight = staticLegendHeight => {
|
||||||
this.setState({staticLegendHeight})
|
this.setState({staticLegendHeight})
|
||||||
|
@ -318,8 +336,7 @@ class Dygraph extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {isHidden, staticLegendHeight} = this.state
|
const {isHidden, staticLegendHeight} = this.state
|
||||||
const {staticLegend, children} = this.props
|
const {staticLegend, children, hoverTime} = this.props
|
||||||
|
|
||||||
const nestedGraph = (children && children.length && children[0]) || children
|
const nestedGraph = (children && children.length && children[0]) || children
|
||||||
let dygraphStyle = {...this.props.containerStyle, zIndex: '2'}
|
let dygraphStyle = {...this.props.containerStyle, zIndex: '2'}
|
||||||
if (staticLegend) {
|
if (staticLegend) {
|
||||||
|
@ -331,22 +348,28 @@ class Dygraph extends Component {
|
||||||
height: `calc(100% - ${staticLegendHeight + cellVerticalPadding}px)`,
|
height: `calc(100% - ${staticLegendHeight + cellVerticalPadding}px)`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dygraph-child" onMouseLeave={this.deselectCrosshair}>
|
<div className="dygraph-child" onMouseLeave={this.deselectCrosshair}>
|
||||||
{this.dygraph &&
|
{this.dygraph &&
|
||||||
<Annotations
|
<div>
|
||||||
dygraph={this.dygraph}
|
<Annotations
|
||||||
annotationsRef={this.handleAnnotationsRef}
|
dygraph={this.dygraph}
|
||||||
staticLegendHeight={staticLegendHeight}
|
annotationsRef={this.handleAnnotationsRef}
|
||||||
/>}
|
staticLegendHeight={staticLegendHeight}
|
||||||
{this.dygraph &&
|
/>
|
||||||
<DygraphLegend
|
<DygraphLegend
|
||||||
isHidden={isHidden}
|
isHidden={isHidden}
|
||||||
dygraph={this.dygraph}
|
dygraph={this.dygraph}
|
||||||
onHide={this.handleHideLegend}
|
onHide={this.handleHideLegend}
|
||||||
onShow={this.handleShowLegend}
|
onShow={this.handleShowLegend}
|
||||||
/>}
|
/>
|
||||||
|
<Crosshair
|
||||||
|
dygraph={this.dygraph}
|
||||||
|
staticLegendHeight={staticLegendHeight}
|
||||||
|
hoverTime={hoverTime}
|
||||||
|
handleCrosshairRef={this.handleCrosshairRef}
|
||||||
|
/>
|
||||||
|
</div>}
|
||||||
<div
|
<div
|
||||||
ref={r => {
|
ref={r => {
|
||||||
this.graphRef = r
|
this.graphRef = r
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
@import 'components/annotations';
|
@import 'components/annotations';
|
||||||
|
@import 'components/crosshairs';
|
||||||
@import 'components/ceo-display-options';
|
@import 'components/ceo-display-options';
|
||||||
@import 'components/confirm-button';
|
@import 'components/confirm-button';
|
||||||
@import 'components/confirm-buttons';
|
@import 'components/confirm-buttons';
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
$crosshair-color: $g20-white;
|
||||||
|
$crosshair-color__drag: $c-hydrogen;
|
||||||
|
|
||||||
|
$window0: rgba($crosshair-color, 0);
|
||||||
|
$window15: rgba($crosshair-color, 0.15);
|
||||||
|
$window35: rgba($crosshair-color, 0.35);
|
||||||
|
|
||||||
|
$active-window0: rgba($crosshair-color__drag, 0);
|
||||||
|
$active-window15: rgba($crosshair-color__drag, 0.15);
|
||||||
|
$active-window35: rgba($crosshair-color__drag, 0.35);
|
||||||
|
|
||||||
|
$timestamp-font-size: 14px;
|
||||||
|
$timestamp-font-weight: 600;
|
||||||
|
|
||||||
|
.crosshair {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
z-index: 3;
|
||||||
|
background-color: $crosshair-color;
|
||||||
|
height: calc(100% - 36px);
|
||||||
|
width: 2px;
|
||||||
|
transform: translateX(
|
||||||
|
-1px
|
||||||
|
); // translate should always be half with width to horizontally center the crosshair pos
|
||||||
|
transition: background-color 0.25s ease;
|
||||||
|
visibility: visible;
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
background-color: $crosshair-color__drag;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.crosshair-window {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
background: linear-gradient(to bottom, $window15 0%, $window0 100%);
|
||||||
|
border-top: 2px dotted $window35;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
$active-window15 0%,
|
||||||
|
$active-window0 100%
|
||||||
|
);
|
||||||
|
border-top: 2px dotted $active-window35;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
New crosshairs
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
.new-crosshair {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 0;
|
||||||
|
top: 8px;
|
||||||
|
width: calc(100% - 32px);
|
||||||
|
height: calc(100% - 16px);
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-crosshair--crosshair {
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
height: calc(100% - 20px);
|
||||||
|
width: 0.5px;
|
||||||
|
transform: translateX(-1px);
|
||||||
|
background: linear-gradient(to bottom, $c-dreamsicle 0%, $c-dreamsicle 100%);
|
||||||
|
transition: opacity 0.4s ease;
|
||||||
|
z-index: 5;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-crosshair-tooltip {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background: linear-gradient(to bottom, $c-pool 0%, $c-ocean 100%);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: calc(100% + 8px);
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-crosshair-helper {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $c-neutrino;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-crosshair-timestamp {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: $timestamp-font-size;
|
||||||
|
line-height: $timestamp-font-size;
|
||||||
|
font-weight: $timestamp-font-weight;
|
||||||
|
color: $g20-white;
|
||||||
|
}
|
Loading…
Reference in New Issue