-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Enhancement] Create factory for histogram and line chart, add brush …
…handle to range brush (#1274) - create factory for hsitogram and linechart - improve brush interaction - improve line chart - improve histogram - add initial range-plot test Signed-off-by: Shan He <heshan0131@gmail.com>
- Loading branch information
1 parent
6681d2e
commit ad65170
Showing
20 changed files
with
549 additions
and
292 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import React, {useMemo} from 'react'; | ||
import {scaleLinear} from 'd3-scale'; | ||
import {max} from 'd3-array'; | ||
import styled from 'styled-components'; | ||
import classnames from 'classnames'; | ||
|
||
const histogramStyle = { | ||
highlightW: 0.7, | ||
unHighlightedW: 0.4 | ||
}; | ||
|
||
const HistogramWrapper = styled.svg` | ||
overflow: visible; | ||
.histogram-bars { | ||
rect { | ||
fill: ${props => props.theme.histogramFillOutRange}; | ||
} | ||
rect.in-range { | ||
fill: ${props => props.theme.histogramFillInRange}; | ||
} | ||
} | ||
`; | ||
|
||
function HistogramPlotFactory() { | ||
const HistogramPlot = ({width, height, margin, isRanged, histogram, value, brushComponent}) => { | ||
const domain = useMemo(() => [histogram[0].x0, histogram[histogram.length - 1].x1], [ | ||
histogram | ||
]); | ||
const dataId = Object.keys(histogram[0]).filter(k => k !== 'x0' && k !== 'x1')[0]; | ||
|
||
// use 1st for now | ||
const getValue = useMemo(() => d => d[dataId], [dataId]); | ||
|
||
const x = useMemo( | ||
() => | ||
scaleLinear() | ||
.domain(domain) | ||
.range([0, width]), | ||
[domain, width] | ||
); | ||
|
||
const y = useMemo( | ||
() => | ||
scaleLinear() | ||
.domain([0, max(histogram, getValue)]) | ||
.range([0, height]), | ||
[histogram, height, getValue] | ||
); | ||
|
||
const barWidth = width / histogram.length; | ||
|
||
return ( | ||
<HistogramWrapper width={width} height={height} style={{marginTop: `${margin.top}px`}}> | ||
<g className="histogram-bars"> | ||
{histogram.map(bar => { | ||
const inRange = bar.x1 <= value[1] + 1 && bar.x0 >= value[0]; | ||
const wRatio = inRange ? histogramStyle.highlightW : histogramStyle.unHighlightedW; | ||
return ( | ||
<rect | ||
className={classnames({'in-range': inRange})} | ||
key={bar.x0} | ||
height={y(getValue(bar))} | ||
width={barWidth * wRatio} | ||
x={x(bar.x0) + (barWidth * (1 - wRatio)) / 2} | ||
rx={1} | ||
ry={1} | ||
y={height - y(getValue(bar))} | ||
/> | ||
); | ||
})} | ||
</g> | ||
<g transform={`translate(${isRanged ? 0 : barWidth / 2}, 0)`}>{brushComponent}</g> | ||
</HistogramWrapper> | ||
); | ||
}; | ||
|
||
const EmpptyOrPlot = props => | ||
!props.histogram || !props.histogram.length ? null : <HistogramPlot {...props} />; | ||
|
||
return EmpptyOrPlot; | ||
} | ||
export default HistogramPlotFactory; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// Copyright (c) 2020 Uber Technologies, Inc. | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in | ||
// all copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
// THE SOFTWARE. | ||
|
||
import React, {useMemo} from 'react'; | ||
import moment from 'moment'; | ||
import { | ||
HorizontalGridLines, | ||
LineSeries, | ||
XYPlot, | ||
CustomSVGSeries, | ||
Hint, | ||
YAxis, | ||
MarkSeries | ||
} from 'react-vis'; | ||
import styled from 'styled-components'; | ||
import {getTimeWidgetHintFormatter} from 'utils/filter-utils'; | ||
|
||
const LineChartWrapper = styled.div` | ||
.rv-xy-plot { | ||
/* important for rendering hint */ | ||
position: relative; | ||
} | ||
.rv-xy-plot__inner { | ||
/* important to show axis */ | ||
overflow: visible; | ||
} | ||
.rv-xy-plot__grid-lines__line { | ||
stroke: ${props => props.theme.histogramFillOutRange}; | ||
stroke-dasharray: 1px 4px; | ||
} | ||
.rv-xy-plot__axis__tick__text { | ||
font-size: 9px; | ||
fill: ${props => props.theme.textColor}; | ||
} | ||
`; | ||
|
||
const StyledHint = styled.div` | ||
background-color: #d3d8e0; | ||
border-radius: 2px; | ||
color: ${props => props.theme.textColorLT}; | ||
font-size: 9px; | ||
margin: 4px; | ||
padding: 3px 6px; | ||
pointer-events: none; | ||
user-select: none; | ||
`; | ||
|
||
const HintContent = ({x, y, format}) => ( | ||
<StyledHint> | ||
<div className="hint--x">{format(x)}</div> | ||
<div className="row">{y}</div> | ||
</StyledHint> | ||
); | ||
|
||
const MARGIN = {top: 0, bottom: 0, left: 0, right: 0}; | ||
function LineChartFactory() { | ||
const LineChart = ({ | ||
brushComponent, | ||
brushing, | ||
color, | ||
enableChartHover, | ||
height, | ||
hoveredDP, | ||
isEnlarged, | ||
lineChart, | ||
margin, | ||
onMouseMove, | ||
width | ||
}) => { | ||
const {xDomain, series, yDomain} = lineChart; | ||
const hintFormatter = useMemo(() => { | ||
return getTimeWidgetHintFormatter(xDomain); | ||
}, [xDomain]); | ||
|
||
const brushData = useMemo(() => { | ||
return [{x: series[0].x, y: yDomain[1], customComponent: () => brushComponent}]; | ||
}, [series, yDomain, brushComponent]); | ||
|
||
return ( | ||
<LineChartWrapper style={{marginTop: `${margin.top}px`}}> | ||
<XYPlot | ||
xType="time" | ||
width={width} | ||
height={height} | ||
margin={MARGIN} | ||
onMouseLeave={() => { | ||
onMouseMove(null); | ||
}} | ||
> | ||
<HorizontalGridLines tickTotal={3} /> | ||
<LineSeries | ||
style={{fill: 'none'}} | ||
strokeWidth={2} | ||
color={color} | ||
data={series} | ||
onNearestX={enableChartHover ? onMouseMove : null} | ||
/> | ||
<MarkSeries data={hoveredDP ? [hoveredDP] : []} color={color} size={3} /> | ||
<CustomSVGSeries data={brushData} /> | ||
{isEnlarged && <YAxis tickTotal={3} />} | ||
{hoveredDP && enableChartHover && !brushing ? ( | ||
<Hint value={hoveredDP}> | ||
<HintContent {...hoveredDP} format={val => moment.utc(val).format(hintFormatter)} /> | ||
</Hint> | ||
) : null} | ||
</XYPlot> | ||
</LineChartWrapper> | ||
); | ||
}; | ||
return LineChart; | ||
} | ||
|
||
export default LineChartFactory; |
Oops, something went wrong.