forked from airbnb/visx
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
new(xychart): add BarSeries (airbnb#808)
* new(xychart): add withRegisteredData, pull out SeriesProps * internal(xychart): factor out logic from LineSeries, fix Datum types * new(xychart): iterate on BarSeries + add to example * fix(xychart): rewrite useDataRegistry to update properly with hooks * new(demo/xychart): add series rendering controls * test(xychart): add useDataRegistry, withRegisteredData, BarSeries tests * deps(xychart): remove uuid dep * fix(xychart): fix withRegisteredData, improve BarSeries logic * fix(withRegisteredData): one more round on generics * fix(withRegisteredData): two more rounds on generics * fix(withRegisteredData): remove unused test var * internal(xychart/useDataRegistry): use single memo * lint(xychart/useDataRegistry)
- Loading branch information
1 parent
b036a23
commit 5813f1c
Showing
26 changed files
with
558 additions
and
128 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
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,92 @@ | ||
import React, { useContext, useCallback, useMemo } from 'react'; | ||
import { AxisScale } from '@visx/axis'; | ||
import DataContext from '../../context/DataContext'; | ||
import { SeriesProps } from '../../types'; | ||
import withRegisteredData, { WithRegisteredDataProps } from '../../enhancers/withRegisteredData'; | ||
import getScaledValueFactory from '../../utils/getScaledValueFactory'; | ||
import isValidNumber from '../../typeguards/isValidNumber'; | ||
import getScaleBandwidth from '../../utils/getScaleBandwidth'; | ||
|
||
type BarSeriesProps< | ||
XScale extends AxisScale, | ||
YScale extends AxisScale, | ||
Datum extends object | ||
> = SeriesProps<XScale, YScale, Datum> & { | ||
/** Whether bars should be rendered horizontally instead of vertically. */ | ||
horizontal?: boolean; | ||
/** | ||
* Specify bar padding when bar thickness does not come from a `band` scale. | ||
* Accepted values are [0, 1], 0 = no padding, 1 = no bar, defaults to 0.1. | ||
*/ | ||
barPadding?: number; | ||
}; | ||
|
||
// Fallback bandwidth estimate assumes no missing data values (divides chart space by # datum) | ||
const getFallbackBandwidth = (fullBarWidth: number, barPadding: number) => | ||
// clamp padding to [0, 1], bar thickness = (1-padding) * availableSpace | ||
fullBarWidth * (1 - Math.min(1, Math.max(0, barPadding))); | ||
|
||
function BarSeries<XScale extends AxisScale, YScale extends AxisScale, Datum extends object>({ | ||
barPadding = 0.1, | ||
data, | ||
dataKey, | ||
horizontal, | ||
xAccessor, | ||
xScale, | ||
yAccessor, | ||
yScale, | ||
}: BarSeriesProps<XScale, YScale, Datum> & WithRegisteredDataProps<XScale, YScale, Datum>) { | ||
const { colorScale, theme, innerWidth = 0, innerHeight = 0 } = useContext(DataContext); | ||
const getScaledX = useCallback(getScaledValueFactory(xScale, xAccessor), [xScale, xAccessor]); | ||
const getScaledY = useCallback(getScaledValueFactory(yScale, yAccessor), [yScale, yAccessor]); | ||
const [xMin, xMax] = xScale.range().map(Number); | ||
const [yMax, yMin] = yScale.range().map(Number); | ||
|
||
const scaleBandwidth = getScaleBandwidth(horizontal ? yScale : xScale); | ||
const barThickness = | ||
scaleBandwidth || | ||
getFallbackBandwidth((horizontal ? innerHeight : innerWidth) / data.length, barPadding); | ||
|
||
// try to figure out the 0 baseline for correct rendering of negative values | ||
// we aren't sure if these are numeric scales or not ahead of time | ||
const maybeXZero = xScale(0); | ||
const maybeYZero = yScale(0); | ||
const xZeroPosition = isValidNumber(maybeXZero) | ||
? // if maybeXZero _is_ a number, but the scale is not clamped and it's outside the domain | ||
// fallback to the scale's minimum | ||
Math.max(maybeXZero, Math.min(xMin, xMax)) | ||
: Math.min(xMin, xMax); | ||
const yZeroPosition = isValidNumber(maybeYZero) | ||
? Math.min(maybeYZero, Math.max(yMin, yMax)) | ||
: Math.max(yMin, yMax); | ||
|
||
const color = colorScale?.(dataKey) ?? theme?.colors?.[0] ?? '#222'; | ||
|
||
const bars = useMemo(() => { | ||
const xOffset = horizontal ? 0 : -barThickness / 2; | ||
const yOffset = horizontal ? -barThickness / 2 : 0; | ||
return data.map(datum => { | ||
const x = getScaledX(datum) + xOffset; | ||
const y = getScaledY(datum) + yOffset; | ||
const barLength = horizontal ? x - xZeroPosition : y - yZeroPosition; | ||
|
||
return { | ||
x: horizontal ? xZeroPosition + Math.min(0, barLength) : x, | ||
y: horizontal ? y : yZeroPosition + Math.min(0, barLength), | ||
width: horizontal ? Math.abs(barLength) : barThickness, | ||
height: horizontal ? barThickness : Math.abs(barLength), | ||
fill: color, // @TODO allow prop overriding | ||
}; | ||
}); | ||
}, [barThickness, color, data, getScaledX, getScaledY, horizontal, xZeroPosition, yZeroPosition]); | ||
|
||
return ( | ||
<g className="vx-bar-series"> | ||
{bars.map(({ x, y, width, height, fill }, i) => ( | ||
<rect key={i} x={x} y={y} width={width} height={height} fill={fill} /> | ||
))} | ||
</g> | ||
); | ||
} | ||
|
||
export default withRegisteredData(BarSeries); |
Oops, something went wrong.