Fix Floating Legend (#4296)
* Move dygraph legend styles into own stylesheet * Refactor legend positioning to break outside threesizer * Remove commented out codepull/4307/head
parent
9f168cacbe
commit
74ce00ea65
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
Floating Legend Styles
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
.dygraph-child-container .dygraph-legend {
|
||||
display: none !important; // hide default legend
|
||||
}
|
||||
.dygraph-legend {
|
||||
background-color: $g0-obsidian;
|
||||
display: block !important;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 8px;
|
||||
z-index: $dygraph-legend-z;
|
||||
border-radius: 3px;
|
||||
user-select: text;
|
||||
@extend %drop-shadow;
|
||||
|
||||
&.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;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
color: $g13-mist;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
|
||||
.dygraph-legend--filter {
|
||||
flex: 1 0 0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.dygraph-legend--contents {
|
||||
font-size: 13px;
|
||||
color: $g15-platinum;
|
||||
font-weight: 600;
|
||||
line-height: 13px;
|
||||
max-height: 123px;
|
||||
margin-top: 8px;
|
||||
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: 1;
|
||||
font-size: 11px;
|
||||
line-height: 11px;
|
||||
font-weight: 600;
|
||||
padding: 3px 0;
|
||||
|
||||
span {
|
||||
font-weight: 900;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
figure {
|
||||
white-space: nowrap;
|
||||
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--rotator {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
transition: transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
.sort-btn--top,
|
||||
.sort-btn--bottom {
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
font-weight: 900;
|
||||
color: $g20-white;
|
||||
transition: transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
.sort-btn--top {
|
||||
left: 3px;
|
||||
top: -3px;
|
||||
}
|
||||
.sort-btn--bottom {
|
||||
bottom: -4px;
|
||||
left: 12px;
|
||||
}
|
||||
|
||||
// Toggled State
|
||||
.sort-btn.sort-btn--desc {
|
||||
.sort-btn--rotator {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.sort-btn--top,
|
||||
.sort-btn--bottom {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
}
|
|
@ -1,17 +1,27 @@
|
|||
// Libraries
|
||||
import React, {PureComponent, ChangeEvent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import _ from 'lodash'
|
||||
import classnames from 'classnames'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import * as actions from 'src/dashboards/actions'
|
||||
import {SeriesLegendData} from 'src/types/dygraphs'
|
||||
// Components
|
||||
import DygraphLegendSort from 'src/shared/components/DygraphLegendSort'
|
||||
|
||||
// Actions
|
||||
import * as actions from 'src/dashboards/actions'
|
||||
|
||||
// Utils
|
||||
import {makeLegendStyles} from 'src/shared/graphs/helpers'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
// Constants
|
||||
import {NO_CELL} from 'src/shared/constants'
|
||||
|
||||
// Types
|
||||
import {SeriesLegendData} from 'src/types/dygraphs'
|
||||
import {DygraphClass} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
|
@ -265,9 +275,8 @@ class DygraphLegend extends PureComponent<Props, State> {
|
|||
hoverTime,
|
||||
} = this.props
|
||||
|
||||
const cursorOffset = 16
|
||||
const legendPosition = dygraph.toDomXCoord(hoverTime) + cursorOffset
|
||||
return makeLegendStyles(graphDiv, this.legendRef, legendPosition)
|
||||
const legendHoverX = dygraph.toDomXCoord(hoverTime)
|
||||
return makeLegendStyles(graphDiv, this.legendRef, legendHoverX)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable no-magic-numbers */
|
||||
import {toRGB_} from 'dygraphs/src/dygraph-utils'
|
||||
import {CSSProperties} from 'react'
|
||||
|
||||
export const LINE_COLORS = [
|
||||
'#00C9FF',
|
||||
|
@ -102,64 +103,53 @@ export const barPlotter = e => {
|
|||
}
|
||||
}
|
||||
|
||||
export const makeLegendStyles = (graph, legend, hoverTimeX) => {
|
||||
if (!graph || !legend || hoverTimeX === null) {
|
||||
export const makeLegendStyles = (
|
||||
graphDiv,
|
||||
legendDiv,
|
||||
legendMouseX
|
||||
): CSSProperties => {
|
||||
if (!graphDiv || !legendDiv || legendMouseX === null) {
|
||||
return {}
|
||||
}
|
||||
|
||||
// Move the Legend on hover
|
||||
const chronografChromeSize = 60 // Width & Height of navigation page elements
|
||||
const graphRect = graph.getBoundingClientRect()
|
||||
const legendRect = legend.getBoundingClientRect()
|
||||
const graphRect = graphDiv.getBoundingClientRect()
|
||||
const legendRect = legendDiv.getBoundingClientRect()
|
||||
|
||||
const graphWidth = graphRect.width + 32 // Factoring in padding from parent
|
||||
const graphHeight = graphRect.height
|
||||
const graphBottom = graphRect.bottom
|
||||
const legendWidth = legendRect.width
|
||||
const legendHeight = legendRect.height
|
||||
const screenHeight = window.innerHeight
|
||||
const legendMaxLeft = graphWidth - legendWidth / 2
|
||||
const mouseX = legendMouseX > 0 ? legendMouseX : 0
|
||||
const halfLegendWidth = legendRect.width / 2
|
||||
|
||||
let marginTop = 0
|
||||
let legendLeft = hoverTimeX
|
||||
const minimumX = 0
|
||||
const maximumX = graphRect.width - legendRect.width
|
||||
|
||||
// Enforcing max & min legend offsets
|
||||
if (hoverTimeX < legendWidth / 2) {
|
||||
legendLeft = legendWidth / 2
|
||||
} else if (hoverTimeX > legendMaxLeft) {
|
||||
legendLeft = legendMaxLeft
|
||||
const minimumY = -60
|
||||
|
||||
let translateX = mouseX - halfLegendWidth
|
||||
let translateY = graphRect.height
|
||||
|
||||
// Enforce Left Edge of Graph
|
||||
if (mouseX - halfLegendWidth < minimumX) {
|
||||
translateX = 0
|
||||
}
|
||||
|
||||
// Disallow screen overflow of legend
|
||||
const isLegendBottomClipped = graphBottom + legendHeight > screenHeight
|
||||
const isLegendTopClipped = legendHeight > graphRect.top - chronografChromeSize
|
||||
const willLegendFitLeft = hoverTimeX - chronografChromeSize > legendWidth
|
||||
|
||||
let legendTop = graphHeight + 8
|
||||
|
||||
// If legend is only clipped on the bottom, position above graph
|
||||
if (isLegendBottomClipped && !isLegendTopClipped) {
|
||||
legendTop = -legendHeight
|
||||
marginTop = 7
|
||||
// Enforce Right Edge of Graph
|
||||
if (mouseX - halfLegendWidth >= maximumX) {
|
||||
translateX = maximumX
|
||||
}
|
||||
|
||||
// If legend is clipped on top and bottom, posiition on either side of crosshair
|
||||
if (isLegendBottomClipped && isLegendTopClipped) {
|
||||
legendTop = 0
|
||||
// Prevent Legend from rendering off screen
|
||||
const rightMargin = window.innerWidth - (mouseX + graphRect.left)
|
||||
const LEGEND_BUFFER = 14
|
||||
if (window.innerHeight - graphRect.bottom < legendRect.height) {
|
||||
translateX = mouseX + LEGEND_BUFFER
|
||||
translateY = minimumY
|
||||
|
||||
if (willLegendFitLeft) {
|
||||
legendLeft = hoverTimeX - legendWidth / 2
|
||||
legendLeft -= 8
|
||||
} else {
|
||||
legendLeft = hoverTimeX + legendWidth / 2
|
||||
legendLeft += 32
|
||||
if (rightMargin < legendRect.width + LEGEND_BUFFER) {
|
||||
translateX = mouseX - (legendRect.width + LEGEND_BUFFER)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
left: `${legendLeft}px`,
|
||||
top: `${legendTop}px`,
|
||||
marginTop: `${marginTop}px`,
|
||||
transform: `translate(${translateX}px, ${translateY}px)`,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
@import 'src/shared/components/TimeMachine/CellNoteEditor';
|
||||
@import 'src/shared/components/MarkdownCell';
|
||||
@import 'src/sources/components/DashboardStep';
|
||||
@import 'src/shared/components/DygraphLegend';
|
||||
@import 'src/dashboards/components/GraphTypeSelector';
|
||||
|
||||
|
||||
|
|
|
@ -61,160 +61,3 @@
|
|||
padding: 0 0 0 4px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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: 8px;
|
||||
z-index: $dygraph-legend-z;
|
||||
border-radius: 3px;
|
||||
user-select: text;
|
||||
transform: translateX(-50%);
|
||||
@extend %drop-shadow;
|
||||
|
||||
&.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// Arrow (default is on top of legend aka below graph)
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-width: 8px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
}
|
||||
&.dygraph-legend--top:after {
|
||||
top: -16px;
|
||||
border-bottom-color: $g0-obsidian;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
&.dygraph-legend--bottom:after {
|
||||
bottom: -16px;
|
||||
border-top-color: $g0-obsidian;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
&.dygraph-legend--left:after {
|
||||
left: -16px;
|
||||
border-right-color: $g0-obsidian;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
&.dygraph-legend--right:after {
|
||||
right: -16px;
|
||||
border-left-color: $g0-obsidian;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
.dygraph-legend--header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
> .btn {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
.dygraph-legend--timestamp {
|
||||
margin-right: 8px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
font-weight: 600;
|
||||
color: $g13-mist;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
.dygraph-legend--filter {
|
||||
flex: 1 0 0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.dygraph-legend--contents {
|
||||
font-size: 13px;
|
||||
color: $g15-platinum;
|
||||
font-weight: 600;
|
||||
line-height: 13px;
|
||||
max-height: 123px;
|
||||
margin-top: 8px;
|
||||
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: 1;
|
||||
font-size: 11px;
|
||||
line-height: 11px;
|
||||
font-weight: 600;
|
||||
padding: 3px 0;
|
||||
|
||||
span {
|
||||
font-weight: 900;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
figure {
|
||||
white-space: nowrap;
|
||||
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--rotator {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
transition: transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
.sort-btn--top,
|
||||
.sort-btn--bottom {
|
||||
position: absolute;
|
||||
font-size: 10px;
|
||||
font-weight: 900;
|
||||
color: $g20-white;
|
||||
transition: transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
.sort-btn--top {
|
||||
left: 3px;
|
||||
top: -3px;
|
||||
}
|
||||
.sort-btn--bottom {
|
||||
bottom: -4px;
|
||||
left: 12px;
|
||||
}
|
||||
// Toggled State
|
||||
.sort-btn.sort-btn--desc {
|
||||
.sort-btn--rotator {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.sort-btn--top,
|
||||
.sort-btn--bottom {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue