Skip to content

Commit

Permalink
feat(bar): improve bar components:
Browse files Browse the repository at this point in the history
- add ability to skip label radius according to width/height
- diverge at 0 to correctly handle negative values
- add innerPadding to be able to add space between each grouped/stacked bar
- add borderRadius option
- add ability to set custom bar item component
- add ability to reverse chart
  • Loading branch information
Raphaël Benitte committed Sep 8, 2017
1 parent c5a63c9 commit 640debc
Show file tree
Hide file tree
Showing 13 changed files with 734 additions and 508 deletions.
9 changes: 6 additions & 3 deletions src/components/cartesian/markers/CartesianMarkersItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ const CartesianMarkersItem = ({
scale,
value,
theme,
style,
lineStyle,
textStyle,
legend,
legendPosition,
legendOffsetX,
Expand Down Expand Up @@ -205,6 +206,7 @@ const CartesianMarkersItem = ({
transform={`translate(${legendProps.x}, ${legendProps.y}) rotate(${legendProps.rotation})`}
textAnchor={legendProps.textAnchor}
alignmentBaseline="central"
style={textStyle}
>
{legend}
</text>
Expand All @@ -220,7 +222,7 @@ const CartesianMarkersItem = ({
y2={y2}
stroke={theme.markers.lineColor}
strokeWidth={theme.markers.lineStrokeWidth}
style={style}
style={lineStyle}
/>
{legendNode}
</g>
Expand All @@ -234,7 +236,8 @@ CartesianMarkersItem.propTypes = {
axis: PropTypes.oneOf(['x', 'y']).isRequired,
scale: PropTypes.func.isRequired,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
style: PropTypes.object,
lineStyle: PropTypes.object,
textStyle: PropTypes.object,

legend: PropTypes.string,
legendPosition: PropTypes.oneOf([
Expand Down
117 changes: 66 additions & 51 deletions src/components/charts/bar/Bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import SvgWrapper from '../SvgWrapper'
import Grid from '../../axes/Grid'
import CartesianMarkers from '../../cartesian/markers/CartesianMarkers'
import Axes from '../../axes/Axes'
import BarItem from './BarItem'
import BarItemLabel from './BarItemLabel'

const barWillEnterHorizontal = ({ style }) => ({
x: style.x.val,
Expand Down Expand Up @@ -54,6 +52,7 @@ const Bar = ({

groupMode,
layout,
reverse,
minValue,
maxValue,

Expand All @@ -63,6 +62,7 @@ const Bar = ({
outerWidth,
outerHeight,
padding,
innerPadding,

// axes & grid
axisTop,
Expand All @@ -72,20 +72,23 @@ const Bar = ({
enableGridX,
enableGridY,

// customization
barComponent,

// labels
enableLabels,
getLabelsLinkColor,
getLabelsTextColor,
label,
labelFormat,
enableLabel,
getLabel,
labelSkipWidth,
labelSkipHeight,
getLabelTextColor,

// markers
markers,

// theming
theme,
getColor,
borderRadius,

// motion
animate,
Expand All @@ -98,6 +101,7 @@ const Bar = ({
}) => {
const options = {
layout,
reverse,
data,
getIndex,
keys,
Expand All @@ -107,7 +111,10 @@ const Bar = ({
height,
getColor,
padding,
innerPadding,
}
const result =
groupMode === 'grouped' ? generateGroupedBars(options) : generateStackedBars(options)

const motionProps = {
animate,
Expand All @@ -120,17 +127,33 @@ const Bar = ({
stiffness: motionStiffness,
}

const result =
groupMode === 'grouped' ? generateGroupedBars(options) : generateStackedBars(options)
let willEnter = layout === 'vertical' ? barWillEnterVertical : barWillEnterHorizontal
let willLeave =
const willEnter = layout === 'vertical' ? barWillEnterVertical : barWillEnterHorizontal
const willLeave =
layout === 'vertical'
? barWillLeaveVertical(springConfig)
: barWillLeaveHorizontal(springConfig)

const shouldRenderLabel = ({ width, height }) => {
if (!enableLabel) return false
if (labelSkipWidth > 0 && width < labelSkipWidth) return false
if (labelSkipHeight > 0 && height < labelSkipHeight) return false
return true
}

return (
<Container isInteractive={isInteractive} theme={theme}>
{({ showTooltip, hideTooltip }) => {
const commonProps = {
borderRadius,
enableLabel,
labelSkipWidth,
labelSkipHeight,
showTooltip,
hideTooltip,
onClick,
theme,
}

let bars
if (animate === true) {
bars = (
Expand All @@ -150,34 +173,35 @@ const Bar = ({
>
{interpolatedStyles => (
<g>
{interpolatedStyles.map(({ key, style, data }) => (
<BarItem
key={key}
{...data}
{...style}
width={Math.max(style.width, 0)}
height={Math.max(style.height, 0)}
showTooltip={showTooltip}
hideTooltip={hideTooltip}
onClick={onClick}
theme={theme}
/>
))}
{interpolatedStyles.map(({ key, style, data: bar }) => {
const baseProps = { ...bar, ...style }

return React.createElement(barComponent, {
key,
...baseProps,
...commonProps,
shouldRenderLabel: shouldRenderLabel(baseProps),
width: Math.max(style.width, 0),
height: Math.max(style.height, 0),
label: getLabel(bar.data),
labelColor: getLabelTextColor(baseProps, theme),
})
})}
</g>
)}
</TransitionMotion>
)
} else {
bars = result.bars.map(d => (
<BarItem
key={d.key}
{...d}
showTooltip={showTooltip}
hideTooltip={hideTooltip}
onClick={onClick}
theme={theme}
/>
))
bars = result.bars.map(d =>
React.createElement(barComponent, {
key: d.key,
...d,
...commonProps,
label: getLabel(d),
shouldRenderLabel: shouldRenderLabel(d),
labelColor: getLabelTextColor(d, theme),
})
)
}

return (
Expand All @@ -190,14 +214,6 @@ const Bar = ({
yScale={enableGridY ? result.yScale : null}
{...motionProps}
/>
<CartesianMarkers
markers={markers}
width={width}
height={height}
xScale={result.xScale}
yScale={result.yScale}
theme={theme}
/>
<Axes
xScale={result.xScale}
yScale={result.yScale}
Expand All @@ -211,15 +227,14 @@ const Bar = ({
{...motionProps}
/>
{bars}
{enableLabels &&
result.bars.map(d => (
<BarItemLabel
{...d}
getLabel={getLabel}
textColor={getLabelsTextColor(d, theme)}
linkColor={getLabelsLinkColor(d, theme)}
/>
))}
<CartesianMarkers
markers={markers}
width={width}
height={height}
xScale={result.xScale}
yScale={result.yScale}
theme={theme}
/>
</SvgWrapper>
)
}}
Expand Down
4 changes: 4 additions & 0 deletions src/components/charts/bar/BarCanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ class BarCanvas extends Component {

// layout
layout,
reverse,
groupMode,
padding,
innerPadding,

// axes
axisTop,
Expand All @@ -80,6 +82,7 @@ class BarCanvas extends Component {

const options = {
layout,
reverse,
data,
getIndex,
keys,
Expand All @@ -89,6 +92,7 @@ class BarCanvas extends Component {
height,
getColor,
padding,
innerPadding,
}

const result =
Expand Down
52 changes: 40 additions & 12 deletions src/components/charts/bar/BarItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,17 @@ const BarItem = ({
y,
width,
height,
borderRadius,
color,

label,
shouldRenderLabel,
labelColor,

showTooltip,
hideTooltip,
onClick,

theme,
}) => {
const handleTooltip = e =>
Expand All @@ -38,17 +45,33 @@ const BarItem = ({
)

return (
<rect
x={x}
y={y}
width={width}
height={height}
fill={color}
onMouseEnter={handleTooltip}
onMouseMove={handleTooltip}
onMouseLeave={hideTooltip}
onClick={onClick}
/>
<g transform={`translate(${x}, ${y})`}>
<rect
width={width}
height={height}
rx={borderRadius}
ry={borderRadius}
fill={color}
onMouseEnter={handleTooltip}
onMouseMove={handleTooltip}
onMouseLeave={hideTooltip}
onClick={onClick}
/>
{shouldRenderLabel && (
<text
x={width / 2}
y={height / 2}
textAnchor="middle"
alignmentBaseline="central"
style={{
pointerEvents: 'none',
fill: labelColor,
}}
>
{label}
</text>
)}
</g>
)
}

Expand All @@ -64,6 +87,11 @@ BarItem.propTypes = {
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
color: PropTypes.string.isRequired,
borderRadius: PropTypes.number.isRequired,

label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
shouldRenderLabel: PropTypes.bool.isRequired,
labelColor: PropTypes.string.isRequired,

showTooltip: PropTypes.func.isRequired,
hideTooltip: PropTypes.func.isRequired,
Expand All @@ -75,7 +103,7 @@ BarItem.propTypes = {

const enhance = compose(
withPropsOnChange(['data', 'onClick'], ({ data, onClick }) => ({
onClick: () => onClick(data),
onClick: event => onClick(data, event),
})),
pure
)
Expand Down
Loading

0 comments on commit 640debc

Please sign in to comment.