-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conglei horizontal bar #127
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/* eslint react/prop-types: 0 */ | ||
import React from 'react'; | ||
import { timeParse, timeFormat } from 'd3-time-format'; | ||
|
||
import { | ||
XYChart, | ||
CrossHair, | ||
XAxis, | ||
YAxis, | ||
theme, | ||
withScreenSize, | ||
BarSeries, | ||
PatternLines, | ||
} from '@data-ui/xy-chart'; | ||
|
||
import colors, { allColors } from '@data-ui/theme/lib/color'; | ||
|
||
import { timeSeriesData } from './data'; | ||
|
||
export const parseDate = timeParse('%Y%m%d'); | ||
export const formatDate = timeFormat('%b %d'); | ||
export const formatYear = timeFormat('%Y'); | ||
export const dateFormatter = date => formatYear(parseDate(date)); | ||
|
||
const categoryHorizontalData = timeSeriesData.map((d, i) => ({ | ||
x: d.y, | ||
y: i + 1, | ||
})); | ||
|
||
const categoryData = timeSeriesData.map((d, i) => ({ | ||
x: i + 1, | ||
y: d.y, | ||
})); | ||
|
||
class HorizontalBarChartExample extends React.PureComponent { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
direction: 'horizontal', | ||
}; | ||
} | ||
|
||
renderControls() { | ||
return ( | ||
<div className="bar-demo--form"> | ||
<div> | ||
Direction: | ||
<label> | ||
<input | ||
type="radio" | ||
value="horizontal" | ||
onChange={e => this.setState({ direction: e.target.value })} | ||
checked={this.state.direction === 'horizontal'} | ||
/>{' '} | ||
horizonal | ||
</label> | ||
<label> | ||
<input | ||
type="radio" | ||
value="vertical" | ||
onChange={e => this.setState({ direction: e.target.value })} | ||
checked={this.state.direction !== 'horizontal'} | ||
/>{' '} | ||
vertical | ||
</label> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
render() { | ||
const { screenWidth } = this.props; | ||
const { direction } = this.state; | ||
const categoryScale = { type: 'band', paddingInner: 0.15 }; | ||
const valueScale = { type: 'linear' }; | ||
const horizontal = direction === 'horizontal'; | ||
|
||
return ( | ||
<div className="horizontal-bar-demo"> | ||
{this.renderControls()} | ||
<XYChart | ||
theme={theme} | ||
width={Math.min(700, screenWidth / 1.5)} | ||
height={Math.min(700 / 2, screenWidth / 1.5 / 2)} | ||
ariaLabel="Required label" | ||
xScale={horizontal ? valueScale : categoryScale} | ||
yScale={horizontal ? categoryScale : valueScale} | ||
margin={{ left: 100, top: 64, bottom: 64 }} | ||
> | ||
<PatternLines | ||
id="brush_pattern" | ||
height={12} | ||
width={12} | ||
stroke={allColors.blue[2]} | ||
strokeWidth={1} | ||
orientation={['diagonal']} | ||
/> | ||
<BarSeries | ||
horizontal={horizontal} | ||
data={horizontal ? categoryHorizontalData : categoryData} | ||
/> | ||
<CrossHair | ||
showHorizontalLine={false} | ||
fullHeight | ||
stroke={colors.darkGray} | ||
circleFill={allColors.blue[7]} | ||
circleStroke="white" | ||
/> | ||
<YAxis numTicks={5} orientation="left" /> | ||
<XAxis numTicks={5} /> | ||
</XYChart> | ||
|
||
<style type="text/css"> | ||
{` | ||
.horizontal-bar-demo { | ||
display: flex; | ||
flex-direction: row; | ||
flex-wrap: wrap; | ||
align-items: center; | ||
} | ||
|
||
.bar-demo--form > div { | ||
display: flex; | ||
justify-content: space-between; | ||
margin-bottom: 8px; | ||
margin-right: 12px; | ||
} | ||
`} | ||
</style> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default withScreenSize(HorizontalBarChartExample); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,20 +11,20 @@ import sharedSeriesProps from '../utils/sharedSeriesProps'; | |
|
||
const propTypes = { | ||
...sharedSeriesProps, | ||
barWidth: PropTypes.number, | ||
data: barSeriesDataShape.isRequired, | ||
fill: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), | ||
fillOpacity: PropTypes.oneOfType([PropTypes.func, PropTypes.number]), | ||
stroke: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), | ||
strokeWidth: PropTypes.oneOfType([PropTypes.func, PropTypes.number]), | ||
horizontal: PropTypes.bool, | ||
}; | ||
|
||
const defaultProps = { | ||
barWidth: null, | ||
fill: themeColors.default, | ||
fillOpacity: null, | ||
stroke: '#FFFFFF', | ||
strokeWidth: 1, | ||
horizontal: false, | ||
}; | ||
|
||
const x = d => d.x; | ||
|
@@ -34,7 +34,6 @@ const noEventsStyles = { pointerEvents: 'none' }; | |
export default class BarSeries extends React.PureComponent { | ||
render() { | ||
const { | ||
barWidth, | ||
data, | ||
disableMouseEvents, | ||
fill, | ||
|
@@ -46,24 +45,30 @@ export default class BarSeries extends React.PureComponent { | |
onClick, | ||
onMouseMove, | ||
onMouseLeave, | ||
horizontal, | ||
} = this.props; | ||
if (!xScale || !yScale) return null; | ||
const valueScale = horizontal ? xScale : yScale; | ||
const categoryScale = horizontal ? yScale : xScale; | ||
const barWidth = | ||
categoryScale.barWidth || (categoryScale.bandwidth && categoryScale.bandwidth()) || 0; | ||
const valueField = horizontal ? x : y; | ||
const categoryField = horizontal ? y : x; | ||
|
||
if (!xScale || !yScale || !barWidth) return null; | ||
|
||
const maxHeight = (yScale.range() || [0])[0]; | ||
const offset = xScale.offset || 0; | ||
const maxBarLength = (valueScale.range() || [0])[0]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed! |
||
const offset = categoryScale.offset || 0; | ||
|
||
return ( | ||
<Group style={disableMouseEvents ? noEventsStyles : null}> | ||
{data.map((d, i) => { | ||
const barHeight = maxHeight - yScale(y(d)); | ||
const barLength = horizontal ? valueScale(valueField(d)) : maxBarLength - valueScale(valueField(d)); | ||
const color = d.fill || callOrValue(fill, d, i); | ||
const barX = xScale(x(d)) - offset; | ||
const barPosition = categoryScale(categoryField(d)) - offset; | ||
|
||
return ( | ||
isDefined(d.y) && ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do you think we should update this to be dependent on horizontal? eg |
||
<FocusBlurHandler | ||
key={`bar-${barX}`} | ||
key={`bar-${barPosition}`} | ||
onBlur={disableMouseEvents ? null : onMouseLeave} | ||
onFocus={ | ||
disableMouseEvents | ||
|
@@ -74,10 +79,10 @@ export default class BarSeries extends React.PureComponent { | |
} | ||
> | ||
<Bar | ||
x={barX} | ||
y={maxHeight - barHeight} | ||
width={barWidth} | ||
height={barHeight} | ||
x={horizontal ? 0 : barPosition} | ||
y={horizontal ? barPosition : maxBarLength - barLength} | ||
width={horizontal ? barLength : barWidth} | ||
height={horizontal ? barWidth : barLength} | ||
fill={color} | ||
fillOpacity={d.fillOpacity || callOrValue(fillOpacity, d, i)} | ||
stroke={d.stroke || callOrValue(stroke, d, i)} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if you want to reference this or not, I like patterns :) I'd update the id if so, or remove component if not.