Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,27 @@ export default function AnimatedBarSeries<
XScale extends AxisScale,
YScale extends AxisScale,
Datum extends object,
>({ colorAccessor, ...props }: Omit<BaseBarSeriesProps<XScale, YScale, Datum>, 'BarsComponent'>) {
>({
colorAccessor,
radius,
radiusAll,
radiusTop,
radiusRight,
radiusBottom,
radiusLeft,
...props
}: Omit<BaseBarSeriesProps<XScale, YScale, Datum>, 'BarsComponent'>) {
return (
<BaseBarSeries<XScale, YScale, Datum>
{...props}
// @TODO currently generics for non-SeriesProps are not passed correctly in
// withRegisteredData HOC
colorAccessor={colorAccessor as BaseBarSeriesProps<XScale, YScale, object>['colorAccessor']}
radius={radius as BaseBarSeriesProps<XScale, YScale, object>['radius']}
radiusAll={radiusAll as BaseBarSeriesProps<XScale, YScale, object>['radiusAll']}
radiusTop={radiusTop as BaseBarSeriesProps<XScale, YScale, object>['radiusTop']}
radiusRight={radiusRight as BaseBarSeriesProps<XScale, YScale, object>['radiusRight']}
radiusBottom={radiusBottom as BaseBarSeriesProps<XScale, YScale, object>['radiusBottom']}
BarsComponent={AnimatedBars}
/>
);
Expand Down
11 changes: 11 additions & 0 deletions packages/visx-xychart/src/components/series/BarSeries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import Bars from './private/Bars';

function BarSeries<XScale extends AxisScale, YScale extends AxisScale, Datum extends object>({
colorAccessor,
radius,
radiusAll,
radiusTop,
radiusRight,
radiusBottom,
radiusLeft,
...props
}: Omit<BaseBarSeriesProps<XScale, YScale, Datum>, 'BarsComponent'>) {
return (
Expand All @@ -13,6 +19,11 @@ function BarSeries<XScale extends AxisScale, YScale extends AxisScale, Datum ext
// @TODO currently generics for non-SeriesProps are not passed correctly in
// withRegisteredData HOC
colorAccessor={colorAccessor as BaseBarSeriesProps<XScale, YScale, object>['colorAccessor']}
radius={radius as BaseBarSeriesProps<XScale, YScale, object>['radius']}
radiusAll={radiusAll as BaseBarSeriesProps<XScale, YScale, object>['radiusAll']}
radiusTop={radiusTop as BaseBarSeriesProps<XScale, YScale, object>['radiusTop']}
radiusRight={radiusRight as BaseBarSeriesProps<XScale, YScale, object>['radiusRight']}
radiusBottom={radiusBottom as BaseBarSeriesProps<XScale, YScale, object>['radiusBottom']}
BarsComponent={Bars}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Datum extends object>({ x, y, width, height, fill }: Bar<Datum>) {
return {
x,
y,
Expand All @@ -23,15 +23,15 @@ type BarTransitionConfig<Scale extends AxisScale> = {
horizontal?: boolean;
};

function useBarTransitionConfig<Scale extends AxisScale>({
function useBarTransitionConfig<Scale extends AxisScale, Datum extends object>({
scale,
horizontal,
}: BarTransitionConfig<Scale>) {
const shouldAnimateX = !!horizontal;
return useMemo(() => {
const scaleBaseline = getScaleBaseline(scale);

function fromLeave({ x, y, width, height, fill }: Bar) {
function fromLeave({ x, y, width, height, fill }: Bar<Datum>) {
return {
x: shouldAnimateX ? scaleBaseline ?? 0 : x,
y: shouldAnimateX ? y : scaleBaseline ?? 0,
Expand All @@ -48,12 +48,16 @@ function useBarTransitionConfig<Scale extends AxisScale>({
leave: fromLeave,
enter: enterUpdate,
update: enterUpdate,
keys: (bar: Bar) => bar.key,
keys: (bar: Bar<Datum>) => bar.key,
};
}, [scale, shouldAnimateX]);
}

function AnimatedBarsRounded<XScale extends AxisScale, YScale extends AxisScale>({
function AnimatedBarsRounded<
XScale extends AxisScale,
YScale extends AxisScale,
Datum extends object,
>({
bars,
xScale,
yScale,
Expand All @@ -65,29 +69,30 @@ function AnimatedBarsRounded<XScale extends AxisScale, YScale extends AxisScale>
radiusBottom,
radiusLeft,
...pathProps
}: BarsProps<XScale, YScale> & { radius: number }) {
}: Omit<BarsProps<XScale, YScale, Datum>, 'radius'> &
Required<Pick<BarsProps<XScale, YScale, Datum>, 'radius'>>) {
return (
// eslint-disable-next-line react/jsx-no-useless-fragment
<>
{bars.map(({ key, fill, x, y, width, height }) => (
{bars.map(({ key, ...barProps }) => (
<BarRounded
key={key}
x={x}
y={y}
width={width}
height={height}
radius={radius}
all={radiusAll}
top={radiusTop}
right={radiusRight}
bottom={radiusBottom}
left={radiusLeft}
x={barProps.x}
y={barProps.y}
width={barProps.width}
height={barProps.height}
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}
>
{({ path }) => (
<AnimatedPath
className="visx-bar visx-bar-rounded"
d={path}
fill={fill}
fill={barProps.fill}
{...pathProps}
/>
)}
Expand All @@ -97,7 +102,11 @@ function AnimatedBarsRounded<XScale extends AxisScale, YScale extends AxisScale>
);
}

function AnimatedBarsUnrounded<XScale extends AxisScale, YScale extends AxisScale>({
function AnimatedBarsUnrounded<
XScale extends AxisScale,
YScale extends AxisScale,
Datum extends object,
>({
bars,
xScale,
yScale,
Expand All @@ -109,7 +118,7 @@ function AnimatedBarsUnrounded<XScale extends AxisScale, YScale extends AxisScal
radiusBottom,
radiusLeft,
...rectProps
}: BarsProps<XScale, YScale>) {
}: BarsProps<XScale, YScale, Datum>) {
const animatedBars = useTransition(bars, {
...useBarTransitionConfig({ horizontal, scale: horizontal ? xScale : yScale }),
});
Expand Down Expand Up @@ -145,9 +154,11 @@ function AnimatedBarsUnrounded<XScale extends AxisScale, YScale extends AxisScal
}

/** Wrapper component which renders a Bars component depending on whether it needs rounded corners. */
export default function AnimatedBars<XScale extends AxisScale, YScale extends AxisScale>(
props: BarsProps<XScale, YScale>,
) {
export default function AnimatedBars<
XScale extends AxisScale,
YScale extends AxisScale,
Datum extends object,
>(props: BarsProps<XScale, YScale, Datum>) {
return props.radius == null ? (
<AnimatedBarsUnrounded {...props} />
) : (
Expand Down
16 changes: 8 additions & 8 deletions packages/visx-xychart/src/components/series/private/Bars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<Datum extends object>({
bars,
horizontal,
xScale,
Expand All @@ -16,7 +16,7 @@ export default function Bars({
radiusBottom,
radiusLeft,
...restProps
}: BarsProps<AxisScale, AxisScale>) {
}: BarsProps<AxisScale, AxisScale, Datum>) {
const isFocusable = Boolean(restProps.onFocus || restProps.onBlur);
return (
<>
Expand All @@ -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}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<BarsProps<XScale, YScale>>;
BarsComponent: React.FC<BarsProps<XScale, YScale, Datum>>;
} & Pick<
SeriesProps<XScale, YScale, Datum>,
| 'onPointerMove'
Expand Down Expand Up @@ -186,7 +186,7 @@ export default function BaseBarGroup<
fill: colorAccessor?.(bar, index) ?? colorScale(key),
};
})
.filter((bar) => bar) as Bar[],
.filter((bar) => bar) as Bar<Datum>[],
};
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export type BaseBarSeriesProps<
Datum extends object,
> = SeriesProps<XScale, YScale, Datum> & {
/** Rendered component which is passed BarsProps by BaseBarSeries after processing. */
BarsComponent: React.FC<BarsProps<XScale, YScale>>;
BarsComponent: React.FC<BarsProps<XScale, YScale, Datum>>;
/**
* 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.
Expand All @@ -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<XScale, YScale>,
BarsProps<XScale, YScale, Datum>,
'radius' | 'radiusAll' | 'radiusTop' | 'radiusRight' | 'radiusBottom' | 'radiusLeft'
>;

Expand Down Expand Up @@ -85,6 +85,7 @@ function BaseBarSeries<XScale extends AxisScale, YScale extends AxisScale, Datum
if (!isValidNumber(barLength)) return null;

return {
datum,
key: `${index}`,
x: horizontal ? xZeroPosition + Math.min(0, barLength) : x,
y: horizontal ? y : yZeroPosition + Math.min(0, barLength),
Expand All @@ -93,7 +94,7 @@ function BaseBarSeries<XScale extends AxisScale, YScale extends AxisScale, Datum
fill: colorAccessor?.(datum, index) ?? color,
};
})
.filter((bar) => bar) as Bar[];
.filter((bar) => bar) as Bar<Datum>[];
}, [
barThickness,
color,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export type BaseBarStackProps<
| React.ReactElement<BarStackChildProps<XScale, YScale, Datum>>
| React.ReactElement<BarStackChildProps<XScale, YScale, Datum>>[];
/** Rendered component which is passed BarsProps by BaseBarStack after processing. */
BarsComponent: React.FC<BarsProps<XScale, YScale>>;
BarsComponent: React.FC<BarsProps<XScale, YScale, Datum>>;
} & Pick<StackPathConfig<Datum, string>, 'offset' | 'order'> &
Pick<
SeriesProps<XScale, YScale, Datum>,
Expand Down Expand Up @@ -192,7 +192,7 @@ function BaseBarStack<
: colorScale(barStack.key),
};
})
.filter((bar) => bar) as Bar[],
.filter((bar) => bar) as Bar<Datum>[],
};
})
.filter((series) => series);
Expand Down
28 changes: 18 additions & 10 deletions packages/visx-xychart/src/types/series.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ export type SeriesProps<
};

/** Bar shape. */
export type Bar = {
export type Bar<Datum extends object> = {
/** Datum being represented. */
datum: Datum;
/** Unique key for Bar (not dataKey). */
key: string;
/** X coordinate of Bar. */
Expand All @@ -132,27 +134,33 @@ export type Bar = {
fill?: string;
};

/** Given Bar props, returns the radius size of the bar. */
export type BarsRadiusSizeFunc<Datum extends object> = (bar: Omit<Bar<Datum>, 'key'>) => number;

/** Given Bar props, returns true if the radius should be applied, otherwise false. */
export type BarsApplyRadiusFunc<Datum extends object> = (bar: Omit<Bar<Datum>, 'key'>) => boolean;

/** Props for base Bars components */
export type BarsProps<XScale extends AxisScale, YScale extends AxisScale> = {
bars: Bar[];
export type BarsProps<XScale extends AxisScale, YScale extends AxisScale, Datum extends object> = {
bars: Bar<Datum>[];
xScale: XScale;
yScale: YScale;
horizontal?: boolean;
/** Optional radius to apply to bar corners. */
radius?: number;
radius?: number | BarsRadiusSizeFunc<Datum>;
/** Whether to apply radius to all corners. */
radiusAll?: boolean;
radiusAll?: boolean | BarsApplyRadiusFunc<Datum>;
/** Whether to apply radius to top corners. */
radiusTop?: boolean;
radiusTop?: boolean | BarsApplyRadiusFunc<Datum>;
/** Whether to apply radius to right corners. */
radiusRight?: boolean;
radiusRight?: boolean | BarsApplyRadiusFunc<Datum>;
/** Whether to apply radius to bottom corners. */
radiusBottom?: boolean;
radiusBottom?: boolean | BarsApplyRadiusFunc<Datum>;
/** Whether to apply radius to left corners. */
radiusLeft?: boolean;
radiusLeft?: boolean | BarsApplyRadiusFunc<Datum>;
} & Omit<
SVGProps<SVGRectElement | SVGPathElement>,
'x' | 'y' | 'width' | 'height' | 'ref' | 'children'
'x' | 'y' | 'width' | 'height' | 'ref' | 'children' | 'radius'
>;

// BarStack transforms its child series Datum into CombinedData<XScale, YScale>
Expand Down