Skip to content

Commit 8d7a4c5

Browse files
Brush, MultilineBrush chart and blank-chart implementation
1 parent dc70650 commit 8d7a4c5

10 files changed

+531
-3
lines changed

example/container.js

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export default class ContainerExample extends Component {
3434
<NavItem eventKey="line" href="/example/line">Line</NavItem>
3535
<NavItem eventKey="pie" href="/example/pie">Pie</NavItem>
3636
<NavItem eventKey="scatter" href="/example/scatter">Scatter</NavItem>
37+
<NavItem eventKey="multi_line_brush" href="/example/multi_line_brush">Brush Chart</NavItem>
3738
</Nav>
3839
</div>
3940
{this.props.children}

example/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import BarStackHorizontal from './src/bar_stack_horizontal'
1313
import Donut from './src/donut'
1414
import Line from './src/line'
1515
import Pie from './src/pie'
16+
import MultiLineBrushChart from './src/multi_line_brush'
1617
import Scatter from './src/scatter'
1718

1819
// Declarative route configuration (could also load this config lazily
@@ -34,7 +35,8 @@ ReactDOM.render((
3435
<Route path="donut" component={Donut}/>
3536
<Route path="line" component={Line}/>
3637
<Route path="pie" component={Pie}/>
37-
<Route path="scatter" component={Scatter}/>
38+
<Route path="multi_line_brush" component={MultiLineBrushChart}/>
39+
<Route path="scatter" component={Scatter}/>
3840
</Route>
3941
</Router>
4042
), document.getElementById('root'))

example/src/multi_line_brush.jsx

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"use strict";
2+
3+
var React = require('react');
4+
var MultiLineBrushChart = require('../../src').MultiLineBrushChart;
5+
var generalChartData = require('dsv?delimiter=\t!./data/temp.tsv');
6+
7+
var chartSeries = [
8+
{
9+
field: 'New York',
10+
name: 'New York Temp',
11+
color: '#ff7f0e'
12+
},
13+
{
14+
field: 'San Francisco',
15+
name: 'San Francisco Temp',
16+
color: '#2ca02c'
17+
},
18+
{
19+
field: 'Austin',
20+
name: 'Austin Temp',
21+
color: '#7777ff'
22+
}
23+
],
24+
interpolate = 'monotone',
25+
x = function(d) {
26+
var parseDate = d3.time.format("%Y%m%d").parse;
27+
return parseDate(d.date);
28+
},
29+
xScale = 'time';
30+
chartSeries.reverse();
31+
32+
33+
module.exports = React.createClass({
34+
getInitialState: function() {
35+
return {
36+
width: 1400,
37+
height: 400,
38+
series: chartSeries,
39+
sliderChartWidth: 1400,
40+
sliderChartHeight:130,
41+
generalChartData: generalChartData,
42+
sliderChartData: generalChartData
43+
}
44+
},
45+
onClick: function() {
46+
this.setState({
47+
width: this.state.width === 600? 400: 600,
48+
height: this.state.width === 600? 600: 400,
49+
series: this.state.width === 600? [{
50+
field: 'Austin',
51+
name: 'Austin Temp',
52+
color: '#7777ff'
53+
}]: chartSeries
54+
})
55+
},
56+
setNewDomain: function(axis, val) {
57+
console.log("Axis : " + axis + " Value : " + val);
58+
// you can render data or filter existing data or whatever based on brush values
59+
},
60+
setNewDomainForSlider: function(axis, val) {
61+
console.log("Axis : " + axis + " Value : " + val);
62+
// you can render data or filter existing data or whatever based on brush values
63+
},
64+
65+
render: function() {
66+
return (
67+
<div>
68+
<button onClick={this.onClick}>toggle</button>
69+
<MultiLineBrushChart
70+
width= {this.state.sliderChartWidth}
71+
height= {this.state.sliderChartHeight}
72+
data= {this.state.generalChartData}
73+
chartSeries= {this.state.series}
74+
xScale={xScale}
75+
x= {x}
76+
isBrushable={true}
77+
showLegend={false}
78+
setNewDomain={this.setNewDomainForSlider}
79+
keepBrushOn={true}
80+
brushStyle={{"fill": "#DDD", "fill-opacity": ".75", "shape-rendering": "crispEdges"}}
81+
yGridStyleClassName={"yGridStyleCls"}
82+
chartClassName={"lineChartStyleCls"}
83+
yAxisClassName={"timeSliderYAxis"}
84+
/>
85+
<MultiLineBrushChart
86+
width= {this.state.width}
87+
height= {this.state.height}
88+
data= {this.state.sliderChartData}
89+
chartSeries= {this.state.series}
90+
xScale={xScale}
91+
x= {x}
92+
isBrushable={false}
93+
setNewDomain={this.setNewDomain}
94+
keepBrushOn={false}
95+
brushStyle={{"fill": "#DDD", "fill-opacity": ".75", "shape-rendering": "crispEdges"}}
96+
noDataTitleText={"No Data"}
97+
/>
98+
</div>
99+
)
100+
}
101+
})

src/commonProps.jsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import D3Scale from 'd3-scale';
44

55
const width = 960;
66
const height = 500;
7-
const margins = {top: 80, right: 100, bottom: 80, left: 100};
7+
const margins = {top: 40, right: 100, bottom: 40, left: 100};
88

99
export default {
1010
width: width,
@@ -14,7 +14,8 @@ export default {
1414
xScale: 'linear',
1515
yScale: 'linear',
1616
showXGrid: true,
17-
showYGrid: true
17+
showYGrid: true,
18+
showLegend: true
1819
}
1920

2021
export const pieProps = {

src/components/blank_chart.jsx

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"use strict";
2+
3+
var React = require('react');
4+
var Component = require('react').Component;
5+
var Chart = require('../../src').Chart;
6+
var Xgrid = require('react-d3-core').Xgrid;
7+
var Ygrid = require('react-d3-core').Ygrid;
8+
var Xaxis = require('react-d3-core').Xaxis;
9+
var Yaxis = require('react-d3-core').Yaxis;
10+
11+
import StraightLine from '../utils/straightLine';
12+
13+
/*
14+
Renders blank chart.
15+
User can provide any text to be diplayed on blank chart.
16+
*/
17+
18+
export default class BlankChart extends Component {
19+
constructor(props) {
20+
super(props);
21+
}
22+
23+
static defaultProps = {
24+
showXGrid: true,
25+
showYGrid: false,
26+
xDomain: [0,10], // its always good to pass xDomain values for x-axis in case if ther is no data the scale will still have valid values or else it will be default.
27+
yDomain: [0],
28+
yTicks: [0],
29+
noDataTitleText: ""
30+
}
31+
32+
renderNoDataTitle (x,y,textValue) {
33+
return (<g><text className = "chartNoData" x = {x} y = {y}>{textValue}</text></g>)
34+
}
35+
36+
render() {
37+
const {
38+
width,
39+
height,
40+
margins,
41+
showXGrid,
42+
showYGrid,
43+
xDomain,
44+
noDataTitleText,
45+
yTicks // do not display Y ticks for blank chart
46+
} = this.props;
47+
48+
var xgrid, ygrid, textXMargin, textYMargin;
49+
50+
if(showXGrid) xgrid = <Xgrid {...this.props}/>
51+
if(showYGrid) ygrid = <Ygrid {...this.props}/>
52+
textXMargin = width/2 - margins.right
53+
textYMargin = height/2 - margins.bottom
54+
55+
return ( <Chart width = {width} height = {height} {...this.props}>
56+
<StraightLine x1={0 - 20} y1={0} x2={width - margins.right } y2={0} {...this.props}/>
57+
{xgrid}
58+
{ygrid}
59+
<Xaxis {...this.props}/>
60+
<Yaxis {...this.props}/>
61+
<StraightLine x1={0 - 20} y1={height - margins.top - margins.bottom} x2={width - 100} y2={height - margins.top - margins.bottom} {...this.props}/>
62+
{noDataTitleText ? this.renderNoDataTitle(textXMargin,textYMargin,noDataTitleText) : null}
63+
</Chart>)
64+
}
65+
}
66+

src/components/brush.jsx

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
"use strict";
2+
3+
import {
4+
default as React,
5+
PropTypes,
6+
Component,
7+
} from 'react';
8+
9+
import CommonProps from '../commonProps';
10+
11+
import {
12+
scale
13+
} from 'react-d3-core';
14+
15+
import {
16+
default as ReactDOM
17+
} from 'react-dom';
18+
19+
/*
20+
Renders d3 brush and returns new x-axis values depending on brush movements.
21+
*/
22+
export default class Brush extends Component {
23+
24+
constructor(props) {
25+
super(props);
26+
27+
this.state = {
28+
xBrushScaleSet: this._mkXScale(props)
29+
}
30+
}
31+
32+
static defaultProps = CommonProps
33+
34+
_mkXScale(props) {
35+
const {
36+
xScale,
37+
xRange,
38+
xDomain,
39+
xRangeRoundBands,
40+
} = props;
41+
42+
var newXScale = {
43+
scale: xScale,
44+
range: xRange,
45+
domain: xDomain,
46+
rangeRoundBands: xRangeRoundBands
47+
}
48+
49+
return scale(newXScale);
50+
}
51+
52+
componentWillReceiveProps(nextProps) {
53+
this.state = {
54+
xBrushScaleSet: this._mkXScale(nextProps)
55+
}
56+
}
57+
58+
componentDidUpdate() {
59+
this._updateBrush()
60+
}
61+
62+
componentDidMount() {
63+
this._updateBrush()
64+
}
65+
66+
_updateBrush() {
67+
const {
68+
xBrushScaleSet,
69+
yBrushScaleSet
70+
} = this.state;
71+
72+
const {
73+
height,
74+
margins,
75+
brushExtent, // used to set default brush drag area hightlighted
76+
keepBrushOn, // if true the brush will be visible after mouse release and user can move it left/right. If not after mouse release it will disappear
77+
setNewDomain, // callback method to which brush will pass the axis and new xDomain values on brush
78+
brushStyle // user defined brush style or else use default
79+
} = this.props;
80+
81+
82+
// create d3 svg bursh with xDaomin values
83+
var brush = d3.svg.brush()
84+
.x(xBrushScaleSet)
85+
86+
// if user wants to keep brush area selected
87+
if (brushExtent)
88+
brush = brush.extent(brushExtent)
89+
90+
brush = brush.on("brushend", () => {
91+
var newDomain = brush.empty() ? xBrushScaleSet.domain() : brush.extent();
92+
if (newDomain.length) {
93+
setNewDomain("x", newDomain)
94+
if (!keepBrushOn)
95+
d3.select(ReactDOM.findDOMNode(this.refs.brushRect)).call(brush.clear());
96+
}
97+
});
98+
99+
var brushDom = d3.select(ReactDOM.findDOMNode(this.refs.brushRect))
100+
.call(brush)
101+
.selectAll('rect')
102+
.attr("y", -4)
103+
.attr("height", height - margins.bottom - margins.top + 3)
104+
105+
// apply user defined brush style if provided
106+
if(brushStyle) {
107+
for(var key in brushStyle) {
108+
brushDom.style(key, brushStyle[key]);
109+
}
110+
} else {
111+
brushDom.style('fill', '#DDD')
112+
.style('fill-opacity', .75)
113+
.style('shape-rendering', 'crispEdges')
114+
}
115+
}
116+
117+
render() {
118+
return <g ref = "brushRect" className = "react-d3-basic__brush__rect"></g>
119+
}
120+
}

0 commit comments

Comments
 (0)