Merge pull request #1679 from influxdata/get-legend-rekt
Update legend functionalitypull/10616/head
commit
0a80cac953
|
@ -7,104 +7,34 @@ import _ from 'lodash'
|
|||
import Dygraphs from 'src/external/dygraph'
|
||||
import getRange from 'shared/parsing/getRangeForDygraph'
|
||||
|
||||
const LINE_COLORS = [
|
||||
'#00C9FF',
|
||||
'#9394FF',
|
||||
'#4ED8A0',
|
||||
'#ff0054',
|
||||
'#ffcc00',
|
||||
'#33aa99',
|
||||
'#9dfc5d',
|
||||
'#92bcc3',
|
||||
'#ca96fb',
|
||||
'#ff00f0',
|
||||
'#38b94a',
|
||||
'#3844b9',
|
||||
'#a0725b',
|
||||
]
|
||||
|
||||
const darkenColor = colorStr => {
|
||||
// Defined in dygraph-utils.js
|
||||
const color = Dygraphs.toRGB_(colorStr)
|
||||
color.r = Math.floor((255 + color.r) / 2)
|
||||
color.g = Math.floor((255 + color.g) / 2)
|
||||
color.b = Math.floor((255 + color.b) / 2)
|
||||
return `rgb(${color.r},${color.g},${color.b})`
|
||||
}
|
||||
// Bar Graph code below is from http://dygraphs.com/tests/plotters.html
|
||||
const multiColumnBarPlotter = e => {
|
||||
// We need to handle all the series simultaneously.
|
||||
if (e.seriesIndex !== 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const g = e.dygraph
|
||||
const ctx = e.drawingContext
|
||||
const sets = e.allSeriesPoints
|
||||
const yBottom = e.dygraph.toDomYCoord(0)
|
||||
|
||||
// Find the minimum separation between x-values.
|
||||
// This determines the bar width.
|
||||
let minSep = Infinity
|
||||
for (let j = 0; j < sets.length; j++) {
|
||||
const points = sets[j]
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
const sep = points[i].canvasx - points[i - 1].canvasx
|
||||
if (sep < minSep) {
|
||||
minSep = sep
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const barWidth = Math.floor(2.0 / 3 * minSep)
|
||||
|
||||
const fillColors = []
|
||||
const strokeColors = g.getColors()
|
||||
for (let i = 0; i < strokeColors.length; i++) {
|
||||
fillColors.push(darkenColor(strokeColors[i]))
|
||||
}
|
||||
|
||||
for (let j = 0; j < sets.length; j++) {
|
||||
ctx.fillStyle = fillColors[j]
|
||||
ctx.strokeStyle = strokeColors[j]
|
||||
for (let i = 0; i < sets[j].length; i++) {
|
||||
const p = sets[j][i]
|
||||
const centerX = p.canvasx
|
||||
const xLeft = sets.length === 1
|
||||
? centerX - barWidth / 2
|
||||
: centerX - barWidth / 2 * (1 - j / (sets.length - 1))
|
||||
|
||||
ctx.fillRect(
|
||||
xLeft,
|
||||
p.canvasy,
|
||||
barWidth / sets.length,
|
||||
yBottom - p.canvasy
|
||||
)
|
||||
|
||||
ctx.strokeRect(
|
||||
xLeft,
|
||||
p.canvasy,
|
||||
barWidth / sets.length,
|
||||
yBottom - p.canvasy
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
import {LINE_COLORS, multiColumnBarPlotter} from 'src/shared/graphs/helpers'
|
||||
import DygraphLegend from 'src/shared/components/DygraphLegend'
|
||||
|
||||
export default class Dygraph extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
legend: {
|
||||
x: null,
|
||||
series: [],
|
||||
},
|
||||
sortType: '',
|
||||
filterText: '',
|
||||
isSynced: false,
|
||||
isHidden: true,
|
||||
isAscending: true,
|
||||
isSnipped: false,
|
||||
isFilterVisible: false,
|
||||
}
|
||||
|
||||
// optional workaround for dygraph.updateOptions breaking legends
|
||||
// a la http://stackoverflow.com/questions/38371876/dygraph-dynamic-update-legend-values-disappear
|
||||
// this.lastMouseMoveEvent = null
|
||||
// this.isMouseOverGraph = false
|
||||
|
||||
this.getTimeSeries = ::this.getTimeSeries
|
||||
this.sync = ::this.sync
|
||||
this.getTimeSeries = ::this.getTimeSeries
|
||||
this.handleSortLegend = ::this.handleSortLegend
|
||||
this.handleLegendInputChange = ::this.handleLegendInputChange
|
||||
this.handleSnipLabel = ::this.handleSnipLabel
|
||||
this.handleHideLegend = ::this.handleHideLegend
|
||||
this.handleToggleFilter = ::this.handleToggleFilter
|
||||
this.visibility = ::this.visibility
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -133,8 +63,8 @@ export default class Dygraph extends Component {
|
|||
options,
|
||||
} = this.props
|
||||
|
||||
const graphContainerNode = this.graphContainer
|
||||
const legendContainerNode = this.legendContainer
|
||||
const graphRef = this.graphRef
|
||||
const legendRef = this.legendRef
|
||||
let finalLineColors = overrideLineColors
|
||||
|
||||
if (finalLineColors === null) {
|
||||
|
@ -148,7 +78,6 @@ export default class Dygraph extends Component {
|
|||
}),
|
||||
],
|
||||
labelsSeparateLines: false,
|
||||
labelsDiv: legendContainerNode,
|
||||
labelsKMB: true,
|
||||
rightGap: 0,
|
||||
highlightSeriesBackgroundAlpha: 1.0,
|
||||
|
@ -158,6 +87,7 @@ export default class Dygraph extends Component {
|
|||
gridLineWidth: 1,
|
||||
highlightCircleSize: 3,
|
||||
animatedZooms: true,
|
||||
hideOverlayOnMouseOut: false,
|
||||
colors: finalLineColors,
|
||||
series: dygraphSeries,
|
||||
axes: {
|
||||
|
@ -172,20 +102,30 @@ export default class Dygraph extends Component {
|
|||
strokeWidth: 2,
|
||||
highlightCircleSize: 5,
|
||||
},
|
||||
unhighlightCallback: () => {
|
||||
legendContainerNode.className = 'container--dygraph-legend hidden' // hide
|
||||
legendFormatter: legend => {
|
||||
if (!legend.x) {
|
||||
return ''
|
||||
}
|
||||
|
||||
// part of optional workaround for preventing updateOptions from breaking legend
|
||||
// this.isMouseOverGraph = false
|
||||
const {state: {legend: prevLegend}} = this
|
||||
const highlighted = legend.series.find(s => s.isHighlighted)
|
||||
const prevHighlighted = prevLegend.series.find(s => s.isHighlighted)
|
||||
|
||||
const y = highlighted && highlighted.y
|
||||
const prevY = prevHighlighted && prevHighlighted.y
|
||||
|
||||
if (legend.x === prevLegend.x && y === prevY) {
|
||||
return ''
|
||||
}
|
||||
|
||||
this.setState({legend})
|
||||
return ''
|
||||
},
|
||||
highlightCallback: e => {
|
||||
// don't make visible yet, but render on DOM to capture position for calcs
|
||||
legendContainerNode.style.visibility = 'hidden'
|
||||
legendContainerNode.className = 'container--dygraph-legend'
|
||||
|
||||
// Move the Legend on hover
|
||||
const graphRect = graphContainerNode.getBoundingClientRect()
|
||||
const legendRect = legendContainerNode.getBoundingClientRect()
|
||||
const graphRect = graphRef.getBoundingClientRect()
|
||||
const legendRect = legendRef.getBoundingClientRect()
|
||||
|
||||
const graphWidth = graphRect.width + 32 // Factoring in padding from parent
|
||||
const graphHeight = graphRect.height
|
||||
const graphBottom = graphRect.bottom
|
||||
|
@ -211,16 +151,28 @@ export default class Dygraph extends Component {
|
|||
? graphHeight + 8 - legendHeight
|
||||
: graphHeight + 8
|
||||
|
||||
legendContainerNode.style.visibility = 'visible' // show
|
||||
legendContainerNode.style.left = `${legendLeft}px`
|
||||
legendContainerNode.style.top = `${legendTop}px`
|
||||
legendRef.style.left = `${legendLeft}px`
|
||||
legendRef.style.top = `${legendTop}px`
|
||||
|
||||
// part of optional workaround for preventing updateOptions from breaking legend
|
||||
// this.isMouseOverGraph = true
|
||||
// this.lastMouseMoveEvent = e
|
||||
this.setState({isHidden: false})
|
||||
},
|
||||
drawCallback: () => {
|
||||
legendContainerNode.className = 'container--dygraph-legend hidden' // hide
|
||||
unhighlightCallback: e => {
|
||||
const {top, bottom, left, right} = legendRef.getBoundingClientRect()
|
||||
|
||||
const mouseY = e.clientY
|
||||
const mouseX = e.clientX
|
||||
|
||||
const mouseInLegendY = mouseY <= bottom && mouseY >= top
|
||||
const mouseInLegendX = mouseX <= right && mouseX >= left
|
||||
const isMouseHoveringLegend = mouseInLegendY && mouseInLegendX
|
||||
|
||||
if (!isMouseHoveringLegend) {
|
||||
this.setState({isHidden: true})
|
||||
|
||||
if (!this.visibility().find(bool => bool === true)) {
|
||||
this.setState({filterText: ''})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -228,7 +180,7 @@ export default class Dygraph extends Component {
|
|||
defaultOptions.plotter = multiColumnBarPlotter
|
||||
}
|
||||
|
||||
this.dygraph = new Dygraphs(graphContainerNode, timeSeries, {
|
||||
this.dygraph = new Dygraphs(graphRef, timeSeries, {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
})
|
||||
|
@ -264,6 +216,22 @@ export default class Dygraph extends Component {
|
|||
return shallowCompare(this, nextProps, nextState)
|
||||
}
|
||||
|
||||
visibility() {
|
||||
const timeSeries = this.getTimeSeries()
|
||||
const {filterText, legend} = this.state
|
||||
const series = _.get(timeSeries, '0', [])
|
||||
const numSeries = series.length
|
||||
return Array(numSeries ? numSeries - 1 : numSeries)
|
||||
.fill(true)
|
||||
.map((s, i) => {
|
||||
if (!legend.series[i]) {
|
||||
return true
|
||||
}
|
||||
|
||||
return !!legend.series[i].label.match(filterText)
|
||||
})
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const {
|
||||
labels,
|
||||
|
@ -273,6 +241,7 @@ export default class Dygraph extends Component {
|
|||
ruleValues,
|
||||
isBarGraph,
|
||||
} = this.props
|
||||
|
||||
const dygraph = this.dygraph
|
||||
if (!dygraph) {
|
||||
throw new Error(
|
||||
|
@ -281,11 +250,7 @@ export default class Dygraph extends Component {
|
|||
}
|
||||
|
||||
const timeSeries = this.getTimeSeries()
|
||||
|
||||
const legendContainerNode = this.legendContainer
|
||||
legendContainerNode.className = 'container--dygraph-legend hidden' // hide
|
||||
|
||||
dygraph.updateOptions({
|
||||
const updateOptions = {
|
||||
labels,
|
||||
file: timeSeries,
|
||||
axes: {
|
||||
|
@ -301,12 +266,10 @@ export default class Dygraph extends Component {
|
|||
underlayCallback: options.underlayCallback,
|
||||
series: dygraphSeries,
|
||||
plotter: isBarGraph ? multiColumnBarPlotter : null,
|
||||
})
|
||||
// part of optional workaround for preventing updateOptions from breaking legend
|
||||
// if (this.lastMouseMoveEvent) {
|
||||
// dygraph.mouseMove_(this.lastMouseMoveEvent)
|
||||
// }
|
||||
visibility: this.visibility(),
|
||||
}
|
||||
|
||||
dygraph.updateOptions(updateOptions)
|
||||
dygraph.resize()
|
||||
const {w} = this.dygraph.getArea()
|
||||
this.props.setResolution(w)
|
||||
|
@ -319,21 +282,77 @@ export default class Dygraph extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleSortLegend(sortType) {
|
||||
this.setState({sortType, isAscending: !this.state.isAscending})
|
||||
}
|
||||
|
||||
handleLegendInputChange(e) {
|
||||
this.setState({filterText: e.target.value})
|
||||
}
|
||||
|
||||
handleSnipLabel() {
|
||||
this.setState({isSnipped: !this.state.isSnipped})
|
||||
}
|
||||
|
||||
handleToggleFilter() {
|
||||
this.setState({
|
||||
isFilterVisible: !this.state.isFilterVisible,
|
||||
filterText: '',
|
||||
})
|
||||
}
|
||||
|
||||
handleHideLegend(e) {
|
||||
const {top, bottom, left, right} = this.graphRef.getBoundingClientRect()
|
||||
|
||||
const mouseY = e.clientY
|
||||
const mouseX = e.clientX
|
||||
|
||||
const mouseInGraphY = mouseY <= bottom && mouseY >= top
|
||||
const mouseInGraphX = mouseX <= right && mouseX >= left
|
||||
const isMouseHoveringGraph = mouseInGraphY && mouseInGraphX
|
||||
|
||||
if (!isMouseHoveringGraph) {
|
||||
this.setState({isHidden: true})
|
||||
if (!this.visibility().find(bool => bool === true)) {
|
||||
this.setState({filterText: ''})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
legend,
|
||||
filterText,
|
||||
isAscending,
|
||||
sortType,
|
||||
isHidden,
|
||||
isSnipped,
|
||||
isFilterVisible,
|
||||
} = this.state
|
||||
|
||||
return (
|
||||
<div className="dygraph-child">
|
||||
<div
|
||||
ref={r => {
|
||||
this.graphContainer = r
|
||||
}}
|
||||
style={this.props.containerStyle}
|
||||
className="dygraph-child-container"
|
||||
<DygraphLegend
|
||||
{...legend}
|
||||
sortType={sortType}
|
||||
onHide={this.handleHideLegend}
|
||||
isHidden={isHidden}
|
||||
isFilterVisible={isFilterVisible}
|
||||
isSnipped={isSnipped}
|
||||
filterText={filterText}
|
||||
isAscending={isAscending}
|
||||
onSnip={this.handleSnipLabel}
|
||||
onSort={this.handleSortLegend}
|
||||
legendRef={el => (this.legendRef = el)}
|
||||
onInputChange={this.handleLegendInputChange}
|
||||
onToggleFilter={this.handleToggleFilter}
|
||||
/>
|
||||
<div
|
||||
ref={r => {
|
||||
this.legendContainer = r
|
||||
this.graphRef = r
|
||||
}}
|
||||
className={'container--dygraph-legend hidden'}
|
||||
style={this.props.containerStyle}
|
||||
className="dygraph-child-container"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import _ from 'lodash'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const removeMeasurement = (label = '') => {
|
||||
const [measurement] = label.match(/^(.*)[.]/g) || ['']
|
||||
return label.replace(measurement, '')
|
||||
}
|
||||
|
||||
const DygraphLegend = ({
|
||||
series,
|
||||
onSort,
|
||||
onSnip,
|
||||
onHide,
|
||||
isHidden,
|
||||
isFilterVisible,
|
||||
isSnipped,
|
||||
sortType,
|
||||
legendRef,
|
||||
filterText,
|
||||
isAscending,
|
||||
onInputChange,
|
||||
onToggleFilter,
|
||||
xHTML,
|
||||
}) => {
|
||||
const sorted = _.sortBy(
|
||||
series,
|
||||
({y, label}) => (sortType === 'numeric' ? y : label)
|
||||
)
|
||||
const ordered = isAscending ? sorted : sorted.reverse()
|
||||
const filtered = ordered.filter(s => s.label.match(filterText))
|
||||
const hidden = isHidden ? 'hidden' : ''
|
||||
|
||||
const renderSortAlpha = (
|
||||
<div
|
||||
className={classnames('sort-btn btn btn-sm btn-square', {
|
||||
'btn-primary': sortType !== 'numeric',
|
||||
'btn-default': sortType === 'numeric',
|
||||
'sort-btn--asc': isAscending && sortType !== 'numeric',
|
||||
'sort-btn--desc': !isAscending && sortType !== 'numeric',
|
||||
})}
|
||||
onClick={() => onSort('alphabetic')}
|
||||
>
|
||||
<div className="sort-btn--arrow" />
|
||||
<div className="sort-btn--top">A</div>
|
||||
<div className="sort-btn--bottom">Z</div>
|
||||
</div>
|
||||
)
|
||||
const renderSortNum = (
|
||||
<button
|
||||
className={classnames('sort-btn btn btn-sm btn-square', {
|
||||
'btn-primary': sortType === 'numeric',
|
||||
'btn-default': sortType !== 'numeric',
|
||||
'sort-btn--asc': isAscending && sortType === 'numeric',
|
||||
'sort-btn--desc': !isAscending && sortType === 'numeric',
|
||||
})}
|
||||
onClick={() => onSort('numeric')}
|
||||
>
|
||||
<div className="sort-btn--arrow" />
|
||||
<div className="sort-btn--top">0</div>
|
||||
<div className="sort-btn--bottom">9</div>
|
||||
</button>
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`dygraph-legend ${hidden}`}
|
||||
ref={legendRef}
|
||||
onMouseLeave={onHide}
|
||||
>
|
||||
<div className="dygraph-legend--header">
|
||||
<div className="dygraph-legend--timestamp">{xHTML}</div>
|
||||
{renderSortAlpha}
|
||||
{renderSortNum}
|
||||
<button
|
||||
className={classnames('btn btn-square btn-sm', {
|
||||
'btn-default': !isFilterVisible,
|
||||
'btn-primary': isFilterVisible,
|
||||
})}
|
||||
onClick={onToggleFilter}
|
||||
>
|
||||
<span className="icon search" />
|
||||
</button>
|
||||
<button className="btn btn-default btn-sm" onClick={onSnip}>
|
||||
Snip
|
||||
</button>
|
||||
</div>
|
||||
{isFilterVisible
|
||||
? <input
|
||||
className="dygraph-legend--filter form-control input-sm"
|
||||
type="text"
|
||||
value={filterText}
|
||||
onChange={onInputChange}
|
||||
placeholder="Filter items..."
|
||||
autoFocus={true}
|
||||
/>
|
||||
: null}
|
||||
<div className="dygraph-legend--divider" />
|
||||
<div className="dygraph-legend--contents">
|
||||
{filtered.map(({label, color, yHTML, isHighlighted}) => {
|
||||
const seriesClass = isHighlighted
|
||||
? 'dygraph-legend--row highlight'
|
||||
: 'dygraph-legend--row'
|
||||
return (
|
||||
<div key={label + color} className={seriesClass}>
|
||||
<span style={{color}}>
|
||||
{isSnipped ? removeMeasurement(label) : label}
|
||||
</span>
|
||||
<figure>{yHTML || 'no value'}</figure>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const {arrayOf, bool, func, number, shape, string} = PropTypes
|
||||
|
||||
DygraphLegend.propTypes = {
|
||||
x: number,
|
||||
xHTML: string,
|
||||
series: arrayOf(
|
||||
shape({
|
||||
color: string,
|
||||
dashHTML: string,
|
||||
isVisible: bool,
|
||||
label: string,
|
||||
y: number,
|
||||
yHTML: string,
|
||||
})
|
||||
),
|
||||
dygraph: shape(),
|
||||
onSnip: func.isRequired,
|
||||
onHide: func.isRequired,
|
||||
onSort: func.isRequired,
|
||||
onInputChange: func.isRequired,
|
||||
onToggleFilter: func.isRequired,
|
||||
filterText: string.isRequired,
|
||||
isAscending: bool.isRequired,
|
||||
sortType: string.isRequired,
|
||||
isHidden: bool.isRequired,
|
||||
legendRef: func.isRequired,
|
||||
isSnipped: bool.isRequired,
|
||||
isFilterVisible: bool.isRequired,
|
||||
}
|
||||
|
||||
export default DygraphLegend
|
|
@ -0,0 +1,87 @@
|
|||
/* eslint-disable no-magic-numbers */
|
||||
import Dygraphs from 'src/external/dygraph'
|
||||
|
||||
export const LINE_COLORS = [
|
||||
'#00C9FF',
|
||||
'#9394FF',
|
||||
'#4ED8A0',
|
||||
'#ff0054',
|
||||
'#ffcc00',
|
||||
'#33aa99',
|
||||
'#9dfc5d',
|
||||
'#92bcc3',
|
||||
'#ca96fb',
|
||||
'#ff00f0',
|
||||
'#38b94a',
|
||||
'#3844b9',
|
||||
'#a0725b',
|
||||
]
|
||||
|
||||
export const darkenColor = colorStr => {
|
||||
// Defined in dygraph-utils.js
|
||||
const color = Dygraphs.toRGB_(colorStr)
|
||||
color.r = Math.floor((255 + color.r) / 2)
|
||||
color.g = Math.floor((255 + color.g) / 2)
|
||||
color.b = Math.floor((255 + color.b) / 2)
|
||||
return `rgb(${color.r},${color.g},${color.b})`
|
||||
}
|
||||
|
||||
// Bar Graph code below is from http://dygraphs.com/tests/plotters.html
|
||||
export const multiColumnBarPlotter = e => {
|
||||
// We need to handle all the series simultaneously.
|
||||
if (e.seriesIndex !== 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const g = e.dygraph
|
||||
const ctx = e.drawingContext
|
||||
const sets = e.allSeriesPoints
|
||||
const yBottom = e.dygraph.toDomYCoord(0)
|
||||
|
||||
// Find the minimum separation between x-values.
|
||||
// This determines the bar width.
|
||||
let minSep = Infinity
|
||||
for (let j = 0; j < sets.length; j++) {
|
||||
const points = sets[j]
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
const sep = points[i].canvasx - points[i - 1].canvasx
|
||||
if (sep < minSep) {
|
||||
minSep = sep
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const barWidth = Math.floor(2.0 / 3 * minSep)
|
||||
|
||||
const fillColors = []
|
||||
const strokeColors = g.getColors()
|
||||
for (let i = 0; i < strokeColors.length; i++) {
|
||||
fillColors.push(darkenColor(strokeColors[i]))
|
||||
}
|
||||
|
||||
for (let j = 0; j < sets.length; j++) {
|
||||
ctx.fillStyle = fillColors[j]
|
||||
ctx.strokeStyle = strokeColors[j]
|
||||
for (let i = 0; i < sets[j].length; i++) {
|
||||
const p = sets[j][i]
|
||||
const centerX = p.canvasx
|
||||
const xLeft = sets.length === 1
|
||||
? centerX - barWidth / 2
|
||||
: centerX - barWidth / 2 * (1 - j / (sets.length - 1))
|
||||
|
||||
ctx.fillRect(
|
||||
xLeft,
|
||||
p.canvasy,
|
||||
barWidth / sets.length,
|
||||
yBottom - p.canvasy
|
||||
)
|
||||
|
||||
ctx.strokeRect(
|
||||
xLeft,
|
||||
p.canvasy,
|
||||
barWidth / sets.length,
|
||||
yBottom - p.canvasy
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.graph-vertical-marker {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
@ -21,69 +20,6 @@
|
|||
background: linear-gradient(to bottom, fade-out($g20-white, 1) 0%,fade-out($g20-white, 0.71) 6%,fade-out($g20-white, 0.71) 80%,fade-out($g20-white, 1) 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='fade-out($g20-white, 0.71)', endColorstr='fade-out($g20-white, 0.71)',GradientType=0 );
|
||||
}
|
||||
.container--dygraph-legend {
|
||||
transform: translateX(-50%);
|
||||
background-color: $g0-obsidian;
|
||||
display: block !important;
|
||||
position: absolute;
|
||||
padding: 11px;
|
||||
z-index: 500;
|
||||
font-size: 13px;
|
||||
color: $g12-forge;
|
||||
border-radius: 3px;
|
||||
font-weight: 600;
|
||||
line-height: 13px;
|
||||
pointer-events: none;
|
||||
|
||||
&.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only animate position that's controlled during rendering.
|
||||
* See http://stackoverflow.com/a/17117992
|
||||
*/
|
||||
// transition: all 0.1s ease;
|
||||
// transition-property: top, right, bottom, left;
|
||||
|
||||
/* Row */
|
||||
/* Styles for Key go here, get overrided by > b */
|
||||
> span {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
opacity: 0.5;
|
||||
padding-top: 4px;
|
||||
font-size: 13px;
|
||||
line-height: 13px;
|
||||
font-weight: 600 !important;
|
||||
color: $g19-ghost;
|
||||
margin: 0;
|
||||
|
||||
/* Border on top of first row */
|
||||
&:first-child {
|
||||
border-top: 2px solid $g4-onyx;
|
||||
padding-top: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
/* Legend Key */
|
||||
> b {
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.highlight {
|
||||
font-weight: 600;
|
||||
opacity: 1;
|
||||
|
||||
> b {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Axis Labels */
|
||||
.dygraph-axis-label {
|
||||
|
@ -136,7 +72,7 @@
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* Single Stat Cells */
|
||||
.single-stat {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
@ -187,3 +123,135 @@
|
|||
.single-stat--small .single-stat--shadow:after {
|
||||
box-shadow: fade-out($g2-kevlar, 0.3) 0 0 30px 10px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Legend Styles
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
.dygraph-child-container .dygraph-legend {
|
||||
display: none !important; // hide default legend
|
||||
}
|
||||
.dygraph-legend {
|
||||
background-color: $g0-obsidian;
|
||||
display: block !important;
|
||||
position: absolute;
|
||||
padding: 11px;
|
||||
z-index: 500;
|
||||
border-radius: 3px;
|
||||
min-width: 350px;
|
||||
user-select: text;
|
||||
transform: translateX(-50%);
|
||||
box-shadow: 0 0 10px 2px $g2-kevlar;
|
||||
|
||||
&.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.dygraph-legend--header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
> .btn { margin-left: 4px; }
|
||||
}
|
||||
.dygraph-legend--timestamp {
|
||||
margin-right: 8px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-weight: 600;
|
||||
color: $g13-mist;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
.dygraph-legend--filter {
|
||||
flex: 1 0 0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.dygraph-legend--divider {
|
||||
width: 100%;
|
||||
margin: 8px 0;
|
||||
height: 2px;
|
||||
background-color: $g5-pepper;
|
||||
}
|
||||
.dygraph-legend--contents {
|
||||
font-size: 13px;
|
||||
color: $g15-platinum;
|
||||
font-weight: 600;
|
||||
line-height: 13px;
|
||||
max-height: 123px;
|
||||
overflow-y: auto;
|
||||
@include custom-scrollbar-round($g0-obsidian,$g3-castle);
|
||||
}
|
||||
.dygraph-legend--row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
flex-wrap: nowrap;
|
||||
opacity: 0.5;
|
||||
font-size: 13px;
|
||||
line-height: 13px;
|
||||
padding: 3px 0;
|
||||
|
||||
span {
|
||||
font-weight: 600;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
figure {
|
||||
padding-left: 10px;
|
||||
font-family: $code-font;
|
||||
}
|
||||
|
||||
&.highlight {
|
||||
opacity: 1;
|
||||
background-color: $g3-castle;
|
||||
figure {color: $g20-white;}
|
||||
}
|
||||
&.highlight:only-child {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sorting Buttons */
|
||||
.sort-btn {
|
||||
position: relative;
|
||||
}
|
||||
.sort-btn--arrow {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
height: calc(100% - 16px);
|
||||
width: 2px;
|
||||
background-color: $g20-white;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 0.25s ease;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) scaleX(0.7);
|
||||
border-style: solid;
|
||||
border-width: 6px;
|
||||
border-color: transparent;
|
||||
border-bottom-color: $g20-white;
|
||||
}
|
||||
}
|
||||
.sort-btn--asc .sort-btn--arrow {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.sort-btn--top,
|
||||
.sort-btn--bottom {
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
font-weight: 900;
|
||||
color: $g20-white;
|
||||
left: 6px;
|
||||
}
|
||||
.sort-btn--top {
|
||||
top: -5px;
|
||||
}
|
||||
.sort-btn--bottom {
|
||||
bottom: -6px;
|
||||
}
|
||||
|
|
|
@ -246,6 +246,7 @@ $rule-builder--radius-lg: 5px;
|
|||
left: (($rule-builder--dot / 2) - $rule-builder--left-gutter);
|
||||
}
|
||||
.container--dygraph-legend {
|
||||
transform: translateX(-50%);
|
||||
background-color: $g5-pepper;
|
||||
|
||||
> span:first-child {
|
||||
|
|
Loading…
Reference in New Issue