diff --git a/packages/visx-xychart/src/components/series/AnimatedBarSeries.tsx b/packages/visx-xychart/src/components/series/AnimatedBarSeries.tsx index 654629ee14..f2feecaebd 100644 --- a/packages/visx-xychart/src/components/series/AnimatedBarSeries.tsx +++ b/packages/visx-xychart/src/components/series/AnimatedBarSeries.tsx @@ -7,13 +7,27 @@ export default function AnimatedBarSeries< XScale extends AxisScale, YScale extends AxisScale, Datum extends object, ->({ colorAccessor, ...props }: Omit, 'BarsComponent'>) { +>({ + colorAccessor, + radius, + radiusAll, + radiusTop, + radiusRight, + radiusBottom, + radiusLeft, + ...props +}: Omit, 'BarsComponent'>) { return ( {...props} // @TODO currently generics for non-SeriesProps are not passed correctly in // withRegisteredData HOC colorAccessor={colorAccessor as BaseBarSeriesProps['colorAccessor']} + radius={radius as BaseBarSeriesProps['radius']} + radiusAll={radiusAll as BaseBarSeriesProps['radiusAll']} + radiusTop={radiusTop as BaseBarSeriesProps['radiusTop']} + radiusRight={radiusRight as BaseBarSeriesProps['radiusRight']} + radiusBottom={radiusBottom as BaseBarSeriesProps['radiusBottom']} BarsComponent={AnimatedBars} /> ); diff --git a/packages/visx-xychart/src/components/series/BarSeries.tsx b/packages/visx-xychart/src/components/series/BarSeries.tsx index 6768ef17a5..b0cac67c08 100644 --- a/packages/visx-xychart/src/components/series/BarSeries.tsx +++ b/packages/visx-xychart/src/components/series/BarSeries.tsx @@ -5,6 +5,12 @@ import Bars from './private/Bars'; function BarSeries({ colorAccessor, + radius, + radiusAll, + radiusTop, + radiusRight, + radiusBottom, + radiusLeft, ...props }: Omit, 'BarsComponent'>) { return ( @@ -13,6 +19,11 @@ function BarSeries['colorAccessor']} + radius={radius as BaseBarSeriesProps['radius']} + radiusAll={radiusAll as BaseBarSeriesProps['radiusAll']} + radiusTop={radiusTop as BaseBarSeriesProps['radiusTop']} + radiusRight={radiusRight as BaseBarSeriesProps['radiusRight']} + radiusBottom={radiusBottom as BaseBarSeriesProps['radiusBottom']} BarsComponent={Bars} /> ); diff --git a/packages/visx-xychart/src/components/series/private/AnimatedBars.tsx b/packages/visx-xychart/src/components/series/private/AnimatedBars.tsx index 912caaa9aa..dbc6ecc991 100644 --- a/packages/visx-xychart/src/components/series/private/AnimatedBars.tsx +++ b/packages/visx-xychart/src/components/series/private/AnimatedBars.tsx @@ -7,7 +7,7 @@ import { cleanColor, colorHasUrl } from '../../../utils/cleanColorString'; import getScaleBaseline from '../../../utils/getScaleBaseline'; import AnimatedPath from './AnimatedPath'; -function enterUpdate({ x, y, width, height, fill }: Bar) { +function enterUpdate({ x, y, width, height, fill }: Bar) { return { x, y, @@ -23,7 +23,7 @@ type BarTransitionConfig = { horizontal?: boolean; }; -function useBarTransitionConfig({ +function useBarTransitionConfig({ scale, horizontal, }: BarTransitionConfig) { @@ -31,7 +31,7 @@ function useBarTransitionConfig({ return useMemo(() => { const scaleBaseline = getScaleBaseline(scale); - function fromLeave({ x, y, width, height, fill }: Bar) { + function fromLeave({ x, y, width, height, fill }: Bar) { return { x: shouldAnimateX ? scaleBaseline ?? 0 : x, y: shouldAnimateX ? y : scaleBaseline ?? 0, @@ -48,12 +48,16 @@ function useBarTransitionConfig({ leave: fromLeave, enter: enterUpdate, update: enterUpdate, - keys: (bar: Bar) => bar.key, + keys: (bar: Bar) => bar.key, }; }, [scale, shouldAnimateX]); } -function AnimatedBarsRounded({ +function AnimatedBarsRounded< + XScale extends AxisScale, + YScale extends AxisScale, + Datum extends object, +>({ bars, xScale, yScale, @@ -65,29 +69,30 @@ function AnimatedBarsRounded radiusBottom, radiusLeft, ...pathProps -}: BarsProps & { radius: number }) { +}: Omit, 'radius'> & + Required, 'radius'>>) { return ( // eslint-disable-next-line react/jsx-no-useless-fragment <> - {bars.map(({ key, fill, x, y, width, height }) => ( + {bars.map(({ key, ...barProps }) => ( {({ path }) => ( )} @@ -97,7 +102,11 @@ function AnimatedBarsRounded ); } -function AnimatedBarsUnrounded({ +function AnimatedBarsUnrounded< + XScale extends AxisScale, + YScale extends AxisScale, + Datum extends object, +>({ bars, xScale, yScale, @@ -109,7 +118,7 @@ function AnimatedBarsUnrounded) { +}: BarsProps) { const animatedBars = useTransition(bars, { ...useBarTransitionConfig({ horizontal, scale: horizontal ? xScale : yScale }), }); @@ -145,9 +154,11 @@ function AnimatedBarsUnrounded( - props: BarsProps, -) { +export default function AnimatedBars< + XScale extends AxisScale, + YScale extends AxisScale, + Datum extends object, +>(props: BarsProps) { return props.radius == null ? ( ) : ( diff --git a/packages/visx-xychart/src/components/series/private/Bars.tsx b/packages/visx-xychart/src/components/series/private/Bars.tsx index c2eace6555..44f7bb5e09 100644 --- a/packages/visx-xychart/src/components/series/private/Bars.tsx +++ b/packages/visx-xychart/src/components/series/private/Bars.tsx @@ -4,7 +4,7 @@ import { BarRounded } from '@visx/shape'; import React from 'react'; import { BarsProps } from '../../../types'; -export default function Bars({ +export default function Bars({ bars, horizontal, xScale, @@ -16,7 +16,7 @@ export default function Bars({ radiusBottom, radiusLeft, ...restProps -}: BarsProps) { +}: BarsProps) { const isFocusable = Boolean(restProps.onFocus || restProps.onBlur); return ( <> @@ -34,12 +34,12 @@ export default function Bars({ key={key} className="visx-bar" tabIndex={isFocusable ? 0 : undefined} - radius={radius} - all={radiusAll} - top={radiusTop} - right={radiusRight} - bottom={radiusBottom} - left={radiusLeft} + radius={typeof radius === 'function' ? radius(barProps) : radius} + all={typeof radiusAll === 'function' ? radiusAll(barProps) : radiusAll} + top={typeof radiusTop === 'function' ? radiusTop(barProps) : radiusTop} + right={typeof radiusRight === 'function' ? radiusRight(barProps) : radiusRight} + bottom={typeof radiusBottom === 'function' ? radiusBottom(barProps) : radiusBottom} + left={typeof radiusLeft === 'function' ? radiusLeft(barProps) : radiusLeft} {...barProps} {...restProps} /> diff --git a/packages/visx-xychart/src/components/series/private/BaseBarGroup.tsx b/packages/visx-xychart/src/components/series/private/BaseBarGroup.tsx index ef17c1d348..fa84b6bc22 100644 --- a/packages/visx-xychart/src/components/series/private/BaseBarGroup.tsx +++ b/packages/visx-xychart/src/components/series/private/BaseBarGroup.tsx @@ -31,7 +31,7 @@ export type BaseBarGroupProps< /** Comparator function to sort `dataKeys` within a bar group. By default the DOM rendering order of `BarGroup`s `children` is used. */ sortBars?: (dataKeyA: string, dataKeyB: string) => number; /** Rendered component which is passed BarsProps by BaseBarGroup after processing. */ - BarsComponent: React.FC>; + BarsComponent: React.FC>; } & Pick< SeriesProps, | 'onPointerMove' @@ -186,7 +186,7 @@ export default function BaseBarGroup< fill: colorAccessor?.(bar, index) ?? colorScale(key), }; }) - .filter((bar) => bar) as Bar[], + .filter((bar) => bar) as Bar[], }; }); diff --git a/packages/visx-xychart/src/components/series/private/BaseBarSeries.tsx b/packages/visx-xychart/src/components/series/private/BaseBarSeries.tsx index 366f9da968..a7eea9f095 100644 --- a/packages/visx-xychart/src/components/series/private/BaseBarSeries.tsx +++ b/packages/visx-xychart/src/components/series/private/BaseBarSeries.tsx @@ -16,7 +16,7 @@ export type BaseBarSeriesProps< Datum extends object, > = SeriesProps & { /** Rendered component which is passed BarsProps by BaseBarSeries after processing. */ - BarsComponent: React.FC>; + BarsComponent: React.FC>; /** * 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. @@ -25,7 +25,7 @@ export type BaseBarSeriesProps< /** Given a Datum, returns its color. Falls back to theme color if unspecified or if a null-ish value is returned. */ colorAccessor?: (d: Datum, index: number) => string | null | undefined; } & Pick< - BarsProps, + BarsProps, 'radius' | 'radiusAll' | 'radiusTop' | 'radiusRight' | 'radiusBottom' | 'radiusLeft' >; @@ -85,6 +85,7 @@ function BaseBarSeries bar) as Bar[]; + .filter((bar) => bar) as Bar[]; }, [ barThickness, color, diff --git a/packages/visx-xychart/src/components/series/private/BaseBarStack.tsx b/packages/visx-xychart/src/components/series/private/BaseBarStack.tsx index db159bc90f..add9a5db77 100644 --- a/packages/visx-xychart/src/components/series/private/BaseBarStack.tsx +++ b/packages/visx-xychart/src/components/series/private/BaseBarStack.tsx @@ -39,7 +39,7 @@ export type BaseBarStackProps< | React.ReactElement> | React.ReactElement>[]; /** Rendered component which is passed BarsProps by BaseBarStack after processing. */ - BarsComponent: React.FC>; + BarsComponent: React.FC>; } & Pick, 'offset' | 'order'> & Pick< SeriesProps, @@ -192,7 +192,7 @@ function BaseBarStack< : colorScale(barStack.key), }; }) - .filter((bar) => bar) as Bar[], + .filter((bar) => bar) as Bar[], }; }) .filter((series) => series); diff --git a/packages/visx-xychart/src/types/series.ts b/packages/visx-xychart/src/types/series.ts index 2c249085a7..e523da7ef6 100644 --- a/packages/visx-xychart/src/types/series.ts +++ b/packages/visx-xychart/src/types/series.ts @@ -117,7 +117,9 @@ export type SeriesProps< }; /** Bar shape. */ -export type Bar = { +export type Bar = { + /** Datum being represented. */ + datum: Datum; /** Unique key for Bar (not dataKey). */ key: string; /** X coordinate of Bar. */ @@ -132,27 +134,33 @@ export type Bar = { fill?: string; }; +/** Given Bar props, returns the radius size of the bar. */ +export type BarsRadiusSizeFunc = (bar: Omit, 'key'>) => number; + +/** Given Bar props, returns true if the radius should be applied, otherwise false. */ +export type BarsApplyRadiusFunc = (bar: Omit, 'key'>) => boolean; + /** Props for base Bars components */ -export type BarsProps = { - bars: Bar[]; +export type BarsProps = { + bars: Bar[]; xScale: XScale; yScale: YScale; horizontal?: boolean; /** Optional radius to apply to bar corners. */ - radius?: number; + radius?: number | BarsRadiusSizeFunc; /** Whether to apply radius to all corners. */ - radiusAll?: boolean; + radiusAll?: boolean | BarsApplyRadiusFunc; /** Whether to apply radius to top corners. */ - radiusTop?: boolean; + radiusTop?: boolean | BarsApplyRadiusFunc; /** Whether to apply radius to right corners. */ - radiusRight?: boolean; + radiusRight?: boolean | BarsApplyRadiusFunc; /** Whether to apply radius to bottom corners. */ - radiusBottom?: boolean; + radiusBottom?: boolean | BarsApplyRadiusFunc; /** Whether to apply radius to left corners. */ - radiusLeft?: boolean; + radiusLeft?: boolean | BarsApplyRadiusFunc; } & Omit< SVGProps, - 'x' | 'y' | 'width' | 'height' | 'ref' | 'children' + 'x' | 'y' | 'width' | 'height' | 'ref' | 'children' | 'radius' >; // BarStack transforms its child series Datum into CombinedData