Skip to content

Commit

Permalink
RadialBar refactor and types (recharts#5084)
Browse files Browse the repository at this point in the history
  • Loading branch information
PavelVanecek authored Oct 6, 2024
1 parent d44402f commit 4f26e1f
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 116 deletions.
1 change: 0 additions & 1 deletion src/cartesian/Bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,6 @@ export function computeBarRectangles({
let value, x, y, width, height, background;

if (stackedData) {
// @ts-expect-error d3 stack series type says it's an array, but actually it's a tuple
value = truncateByDomain(stackedData[dataStartIndex + index], stackedDomain);
} else {
value = getValueByDataKey(entry, dataKey);
Expand Down
250 changes: 157 additions & 93 deletions src/polar/RadialBar.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
/**
* @fileOverview Render a group of radial bar
*/
// eslint-disable-next-line max-classes-per-file
import React, { PureComponent, ReactElement } from 'react';
import clsx from 'clsx';
import Animate from 'react-smooth';
import isEqual from 'lodash/isEqual';
import isFunction from 'lodash/isFunction';

import { Series } from 'victory-vendor/d3-shape';
import { parseCornerRadius, RadialBarSector, RadialBarSectorProps } from '../util/RadialBarUtils';
import { Props as SectorProps } from '../shape/Sector';
import { Layer } from '../container/Layer';
Expand All @@ -22,8 +20,8 @@ import {
getValueByDataKey,
truncateByDomain,
getBaseValueOfBar,
getTooltipItem,
getTooltipNameProp,
BarPositionPosition,
} from '../util/ChartUtils';
import {
LegendType,
Expand All @@ -35,8 +33,10 @@ import {
AnimationDuration,
ActiveShape,
LayoutType,
DataKey,
NumberDomain,
CategoricalDomain,
} from '../util/types';
import { polarToCartesian } from '../util/PolarUtils';
import type { Payload as LegendPayload } from '../component/DefaultLegendContent';
import { useLegendPayloadDispatch } from '../context/legendPayloadContext';
import {
Expand All @@ -49,8 +49,8 @@ import { TooltipPayloadConfiguration } from '../state/tooltipSlice';
import { SetTooltipEntrySettings } from '../state/SetTooltipEntrySettings';
import { ReportBar } from '../state/ReportBar';
import { PolarGraphicalItemContext } from '../context/PolarGraphicalItemContext';
// TODO: Cause of circular dependency. Needs refactoring of functions that need them.
// import { AngleAxisProps, RadiusAxisProps } from './types';
import { BaseAxisWithScale } from '../state/selectors/axisSelectors';
import { ChartData } from '../state/chartDataSlice';

type RadialBarDataItem = SectorProps & {
value?: any;
Expand All @@ -61,7 +61,7 @@ type RadialBarDataItem = SectorProps & {
type RadialBarBackground = ActiveShape<SectorProps>;

type RadialBarSectorsProps = {
sectors: SectorProps[];
sectors: ReadonlyArray<SectorProps>;
allOtherRadialBarProps: RadialBarProps;
};

Expand Down Expand Up @@ -134,7 +134,7 @@ interface InternalRadialBarProps {
*/
barSize?: number;
maxBarSize?: number;
data?: RadialBarDataItem[];
data?: ReadonlyArray<RadialBarDataItem>;
legendType?: LegendType;
tooltipType?: TooltipType;
hide?: boolean;
Expand All @@ -153,18 +153,18 @@ export type RadialBarProps = PresentationAttributesAdaptChildEvent<any, SVGEleme

interface State {
readonly isAnimationFinished?: boolean;
readonly prevData?: RadialBarDataItem[];
readonly curData?: RadialBarDataItem[];
readonly prevData?: ReadonlyArray<RadialBarDataItem>;
readonly curData?: ReadonlyArray<RadialBarDataItem>;
readonly prevAnimationId?: string | number;
}

type RadialBarComposedData = {
data: RadialBarDataItem[];
data: ReadonlyArray<RadialBarDataItem>;
layout: LayoutType;
};

type RadialBarPayloadInputProps = {
data: RadialBarDataItem[];
data: ReadonlyArray<RadialBarDataItem>;
legendType?: LegendType;
};

Expand Down Expand Up @@ -244,7 +244,7 @@ class RadialBarWithState extends PureComponent<RadialBarProps, State> {
}
};

renderSectorsStatically(sectors: SectorProps[]) {
renderSectorsStatically(sectors: ReadonlyArray<SectorProps>) {
return <RadialBarSectors sectors={sectors} allOtherRadialBarProps={this.props} />;
}

Expand Down Expand Up @@ -301,7 +301,7 @@ class RadialBarWithState extends PureComponent<RadialBarProps, State> {
return this.renderSectorsStatically(data);
}

renderBackground(sectors?: RadialBarDataItem[]) {
renderBackground(sectors?: ReadonlyArray<RadialBarDataItem>) {
const { cornerRadius } = this.props;
const backgroundProps = filterProps(this.props.background, false);

Expand Down Expand Up @@ -363,7 +363,7 @@ const defaultRadialBarProps: Partial<RadialBarProps> = {
minPointSize: 0,
hide: false,
legendType: 'rect',
data: [] as RadialBarDataItem[],
data: [] as ReadonlyArray<RadialBarDataItem>,
isAnimationActive: !Global.isSsr,
animationBegin: 0,
animationDuration: 1500,
Expand All @@ -372,6 +372,126 @@ const defaultRadialBarProps: Partial<RadialBarProps> = {
cornerIsExternal: false,
};

function computeRadialBarDataItems({
displayedData,
stackedData,
dataStartIndex,
stackedDomain,
dataKey,
baseValue,
layout,
radiusAxis,
radiusAxisTicks,
bandSize,
pos,
angleAxis,
minPointSize,
cx,
cy,
angleAxisTicks,
cells,
startAngle: rootStartAngle,
endAngle: rootEndAngle,
}: {
displayedData: ChartData;
stackedData: Series<Record<number, number>, DataKey<any>> | undefined;
dataStartIndex: number;
stackedDomain: NumberDomain | CategoricalDomain | undefined;
dataKey: DataKey<any>;
baseValue: number | unknown;
layout: LayoutType;
radiusAxis: BaseAxisWithScale;
radiusAxisTicks: Array<TickItem>;
bandSize: number;
pos: BarPositionPosition;
angleAxis: BaseAxisWithScale;
minPointSize: number;
cx: number;
cy: number;
angleAxisTicks: Array<TickItem>;
cells: ReadonlyArray<ReactElement> | undefined;
startAngle: number;
endAngle: number;
}) {
return displayedData.map((entry: unknown, index: number) => {
let value, innerRadius, outerRadius, startAngle, endAngle, backgroundSector;

if (stackedData) {
// @ts-expect-error truncateByDomain expects only numerical domain, but it can received categorical domain too
value = truncateByDomain(stackedData[dataStartIndex + index], stackedDomain);
} else {
value = getValueByDataKey(entry, dataKey);
if (!Array.isArray(value)) {
value = [baseValue, value];
}
}

if (layout === 'radial') {
innerRadius = getCateCoordinateOfBar({
axis: radiusAxis,
ticks: radiusAxisTicks,
bandSize,
offset: pos.offset,
entry,
index,
});
endAngle = angleAxis.scale(value[1]);
startAngle = angleAxis.scale(value[0]);
outerRadius = innerRadius + pos.size;
const deltaAngle = endAngle - startAngle;

if (Math.abs(minPointSize) > 0 && Math.abs(deltaAngle) < Math.abs(minPointSize)) {
const delta = mathSign(deltaAngle || minPointSize) * (Math.abs(minPointSize) - Math.abs(deltaAngle));

endAngle += delta;
}
backgroundSector = {
background: {
cx,
cy,
innerRadius,
outerRadius,
startAngle: rootStartAngle,
endAngle: rootEndAngle,
},
};
} else {
innerRadius = radiusAxis.scale(value[0]);
outerRadius = radiusAxis.scale(value[1]);
startAngle = getCateCoordinateOfBar({
axis: angleAxis,
ticks: angleAxisTicks,
bandSize,
offset: pos.offset,
entry,
index,
});
endAngle = startAngle + pos.size;
const deltaRadius = outerRadius - innerRadius;

if (Math.abs(minPointSize) > 0 && Math.abs(deltaRadius) < Math.abs(minPointSize)) {
const delta = mathSign(deltaRadius || minPointSize) * (Math.abs(minPointSize) - Math.abs(deltaRadius));
outerRadius += delta;
}
}

return {
// @ts-expect-error can't spread unknown
...entry,
...backgroundSector,
payload: entry,
value: stackedData ? value : value[1],
cx,
cy,
innerRadius,
outerRadius,
startAngle,
endAngle,
...(cells && cells[index] && cells[index].props),
};
});
}

export class RadialBar extends PureComponent<RadialBarProps> {
static displayName = 'RadialBar';

Expand Down Expand Up @@ -416,83 +536,27 @@ export class RadialBar extends PureComponent<RadialBarProps> {
const stackedDomain = stackedData ? numericAxis.scale.domain() : null;
const baseValue = getBaseValueOfBar({ numericAxis });
const cells = findAllByType(children, Cell);
const sectors = displayedData.map((entry: any, index: number) => {
let value, innerRadius, outerRadius, startAngle, endAngle, backgroundSector;

if (stackedData) {
value = truncateByDomain(stackedData[dataStartIndex + index], stackedDomain);
} else {
value = getValueByDataKey(entry, dataKey);
if (!Array.isArray(value)) {
value = [baseValue, value];
}
}

if (layout === 'radial') {
innerRadius = getCateCoordinateOfBar({
axis: radiusAxis,
ticks: radiusAxisTicks,
bandSize,
offset: pos.offset,
entry,
index,
});
endAngle = angleAxis.scale(value[1]);
startAngle = angleAxis.scale(value[0]);
outerRadius = innerRadius + pos.size;
const deltaAngle = endAngle - startAngle;

if (Math.abs(minPointSize) > 0 && Math.abs(deltaAngle) < Math.abs(minPointSize)) {
const delta = mathSign(deltaAngle || minPointSize) * (Math.abs(minPointSize) - Math.abs(deltaAngle));

endAngle += delta;
}
backgroundSector = {
background: {
cx,
cy,
innerRadius,
outerRadius,
startAngle: props.startAngle,
endAngle: props.endAngle,
},
};
} else {
innerRadius = radiusAxis.scale(value[0]);
outerRadius = radiusAxis.scale(value[1]);
startAngle = getCateCoordinateOfBar({
axis: angleAxis,
ticks: angleAxisTicks,
bandSize,
offset: pos.offset,
entry,
index,
});
endAngle = startAngle + pos.size;
const deltaRadius = outerRadius - innerRadius;

if (Math.abs(minPointSize) > 0 && Math.abs(deltaRadius) < Math.abs(minPointSize)) {
const delta = mathSign(deltaRadius || minPointSize) * (Math.abs(minPointSize) - Math.abs(deltaRadius));
outerRadius += delta;
}
}

return {
...entry,
...backgroundSector,
payload: entry,
value: stackedData ? value : value[1],
cx,
cy,
innerRadius,
outerRadius,
startAngle,
endAngle,
...(cells && cells[index] && cells[index].props),
// @ts-expect-error missing types
tooltipPayload: [getTooltipItem(item, entry)],
tooltipPosition: polarToCartesian(cx, cy, (innerRadius + outerRadius) / 2, (startAngle + endAngle) / 2),
};
const sectors = computeRadialBarDataItems({
angleAxis,
angleAxisTicks,
bandSize,
baseValue,
cells,
cx,
cy,
dataKey,
dataStartIndex,
displayedData,
endAngle: props.endAngle,
layout,
minPointSize,
pos,
radiusAxis,
radiusAxisTicks,
// @ts-expect-error any
stackedData,
stackedDomain,
startAngle: props.startAngle,
});

return { data: sectors, layout };
Expand Down
Loading

0 comments on commit 4f26e1f

Please sign in to comment.