Fix Floating Legend (#4296)

* Move dygraph legend styles into own stylesheet

* Refactor legend positioning to break outside threesizer

* Remove commented out code
pull/4307/head
Alex Paxton 2018-08-27 16:56:53 -07:00 committed by GitHub
parent 9f168cacbe
commit 74ce00ea65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 180 additions and 206 deletions

View File

@ -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);
}
}

View File

@ -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)
}
}

View File

@ -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)`,
}
}

View File

@ -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';

View File

@ -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);
}
}