Skip to content

Commit

Permalink
[charts] Allow percentage values for pie chart center and radius (#11464
Browse files Browse the repository at this point in the history
)
  • Loading branch information
alexfauquette authored Dec 21, 2023
1 parent bffa6fc commit 4106101
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 86 deletions.
6 changes: 1 addition & 5 deletions docs/pages/x/api/charts/pie-arc-label-plot.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
{
"props": {
"outerRadius": {
"type": { "name": "number" },
"default": "R_max The maximal radius that fit into the drawing area.",
"required": true
},
"outerRadius": { "type": { "name": "number" }, "required": true },
"arcLabel": {
"type": {
"name": "union",
Expand Down
6 changes: 1 addition & 5 deletions docs/pages/x/api/charts/pie-arc-plot.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
{
"props": {
"outerRadius": {
"type": { "name": "number" },
"default": "R_max The maximal radius that fit into the drawing area.",
"required": true
},
"outerRadius": { "type": { "name": "number" }, "required": true },
"cornerRadius": { "type": { "name": "number" }, "default": "0" },
"faded": {
"type": {
Expand Down
11 changes: 3 additions & 8 deletions packages/x-charts/src/PieChart/PieArcLabelPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { useTransition } from '@react-spring/web';
import {
ComputedPieRadius,
DefaultizedPieSeriesType,
DefaultizedPieValueType,
PieSeriesType,
Expand All @@ -13,7 +14,6 @@ import {
useTransformData,
} from './dataTransform/useTransformData';
import { PieArcLabel, PieArcLabelProps } from './PieArcLabel';
import { DefaultizedProps } from '../models/helpers';

const RATIO = 180 / Math.PI;

Expand Down Expand Up @@ -46,23 +46,19 @@ export interface PieArcLabelPlotSlotProps {
}

export interface PieArcLabelPlotProps
extends DefaultizedProps<
Pick<
extends Pick<
DefaultizedPieSeriesType,
| 'data'
| 'faded'
| 'highlighted'
| 'innerRadius'
| 'outerRadius'
| 'cornerRadius'
| 'paddingAngle'
| 'arcLabel'
| 'arcLabelMinAngle'
| 'id'
| 'highlightScope'
>,
'outerRadius'
> {
ComputedPieRadius {
/**
* Overridable component slots.
* @default {}
Expand Down Expand Up @@ -227,7 +223,6 @@ PieArcLabelPlot.propTypes = {
innerRadius: PropTypes.number,
/**
* The radius between circle center and the end of the arc.
* @default R_max The maximal radius that fit into the drawing area.
*/
outerRadius: PropTypes.number.isRequired,
/**
Expand Down
19 changes: 4 additions & 15 deletions packages/x-charts/src/PieChart/PieArcPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { useTransition } from '@react-spring/web';
import { PieArc, PieArcProps } from './PieArc';
import {
ComputedPieRadius,
DefaultizedPieSeriesType,
DefaultizedPieValueType,
PieItemIdentifier,
Expand All @@ -13,7 +14,6 @@ import {
ValueWithHighlight,
useTransformData,
} from './dataTransform/useTransformData';
import { DefaultizedProps } from '../models/helpers';

export interface PieArcPlotSlots {
pieArc?: React.JSXElementConstructor<PieArcProps>;
Expand All @@ -24,21 +24,11 @@ export interface PieArcPlotSlotProps {
}

export interface PieArcPlotProps
extends DefaultizedProps<
Pick<
extends Pick<
DefaultizedPieSeriesType,
| 'data'
| 'faded'
| 'highlighted'
| 'innerRadius'
| 'outerRadius'
| 'cornerRadius'
| 'paddingAngle'
| 'id'
| 'highlightScope'
'data' | 'faded' | 'highlighted' | 'cornerRadius' | 'paddingAngle' | 'id' | 'highlightScope'
>,
'outerRadius'
> {
ComputedPieRadius {
/**
* Overridable component slots.
* @default {}
Expand Down Expand Up @@ -218,7 +208,6 @@ PieArcPlot.propTypes = {
onClick: PropTypes.func,
/**
* The radius between circle center and the end of the arc.
* @default R_max The maximal radius that fit into the drawing area.
*/
outerRadius: PropTypes.number.isRequired,
/**
Expand Down
8 changes: 4 additions & 4 deletions packages/x-charts/src/PieChart/PieChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ PieChart.propTypes = {
arcLabelMinAngle: PropTypes.number,
color: PropTypes.string,
cornerRadius: PropTypes.number,
cx: PropTypes.number,
cy: PropTypes.number,
cx: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
cy: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
data: PropTypes.arrayOf(
PropTypes.shape({
color: PropTypes.string,
Expand Down Expand Up @@ -342,8 +342,8 @@ PieChart.propTypes = {
highlighted: PropTypes.oneOf(['item', 'none', 'series']),
}),
id: PropTypes.string,
innerRadius: PropTypes.number,
outerRadius: PropTypes.number,
innerRadius: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
outerRadius: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
paddingAngle: PropTypes.number,
sortingValues: PropTypes.oneOfType([
PropTypes.oneOf(['asc', 'desc', 'none']),
Expand Down
52 changes: 27 additions & 25 deletions packages/x-charts/src/PieChart/PiePlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SeriesContext } from '../context/SeriesContextProvider';
import { DrawingContext } from '../context/DrawingProvider';
import { PieArcPlot, PieArcPlotProps, PieArcPlotSlotProps, PieArcPlotSlots } from './PieArcPlot';
import { PieArcLabelPlotSlots, PieArcLabelPlotSlotProps, PieArcLabelPlot } from './PieArcLabelPlot';
import { getPercentageValue } from '../internals/utils';

export interface PiePlotSlots extends PieArcPlotSlots, PieArcLabelPlotSlots {}

Expand Down Expand Up @@ -48,37 +49,36 @@ function PiePlot(props: PiePlotProps) {
}
const availableRadius = Math.min(width, height) / 2;

const center = {
x: left + width / 2,
y: top + height / 2,
};
const { series, seriesOrder } = seriesData;

return (
<g>
{seriesOrder.map((seriesId) => {
const {
innerRadius,
outerRadius,
innerRadius: innerRadiusParam,
outerRadius: outerRadiusParam,
cornerRadius,
paddingAngle,
data,
cx,
cy,
cx: cxParam,
cy: cyParam,
highlighted,
faded,
highlightScope,
} = series[seriesId];

const outerRadius = getPercentageValue(
outerRadiusParam ?? availableRadius,
availableRadius,
);
const innerRadius = getPercentageValue(innerRadiusParam ?? 0, availableRadius);
const cx = getPercentageValue(cxParam ?? '50%', width);
const cy = getPercentageValue(cyParam ?? '50%', height);
return (
<g
key={seriesId}
transform={`translate(${cx === undefined ? center.x : left + cx}, ${
cy === undefined ? center.y : top + cy
})`}
>
<g key={seriesId} transform={`translate(${left + cx}, ${top + cy})`}>
<PieArcPlot
innerRadius={innerRadius}
outerRadius={outerRadius ?? availableRadius}
outerRadius={outerRadius}
cornerRadius={cornerRadius}
paddingAngle={paddingAngle}
id={seriesId}
Expand All @@ -96,24 +96,26 @@ function PiePlot(props: PiePlotProps) {
})}
{seriesOrder.map((seriesId) => {
const {
innerRadius,
outerRadius,
innerRadius: innerRadiusParam,
outerRadius: outerRadiusParam,
cornerRadius,
paddingAngle,
arcLabel,
arcLabelMinAngle,
data,
cx,
cy,
cx: cxParam,
cy: cyParam,
highlightScope,
} = series[seriesId];
const outerRadius = getPercentageValue(
outerRadiusParam ?? availableRadius,
availableRadius,
);
const innerRadius = getPercentageValue(innerRadiusParam ?? 0, availableRadius);
const cx = getPercentageValue(cxParam ?? '50%', width);
const cy = getPercentageValue(cyParam ?? '50%', height);
return (
<g
key={seriesId}
transform={`translate(${cx === undefined ? center.x : left + cx}, ${
cy === undefined ? center.y : top + cy
})`}
>
<g key={seriesId} transform={`translate(${left + cx}, ${top + cy})`}>
<PieArcLabelPlot
innerRadius={innerRadius}
outerRadius={outerRadius ?? availableRadius}
Expand Down
27 changes: 10 additions & 17 deletions packages/x-charts/src/PieChart/dataTransform/useTransformData.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as React from 'react';
import { InteractionContext } from '../../context/InteractionProvider';
import { DefaultizedPieSeriesType, DefaultizedPieValueType } from '../../models/seriesType/pie';
import {
ComputedPieRadius,
DefaultizedPieSeriesType,
DefaultizedPieValueType,
} from '../../models/seriesType/pie';
import { getIsHighlighted, getIsFaded } from '../../hooks/useInteractionItemProps';
import { DefaultizedProps } from '../../models/helpers';

export interface AnimatedObject {
innerRadius: number;
Expand All @@ -19,21 +22,11 @@ export interface ValueWithHighlight extends DefaultizedPieValueType, AnimatedObj
}

export function useTransformData(
series: DefaultizedProps<
Pick<
DefaultizedPieSeriesType,
| 'innerRadius'
| 'outerRadius'
| 'cornerRadius'
| 'paddingAngle'
| 'id'
| 'highlightScope'
| 'highlighted'
| 'faded'
| 'data'
>,
'outerRadius'
>,
series: Pick<
DefaultizedPieSeriesType,
'cornerRadius' | 'paddingAngle' | 'id' | 'highlightScope' | 'highlighted' | 'faded' | 'data'
> &
ComputedPieRadius,
) {
const {
id: seriesId,
Expand Down
31 changes: 31 additions & 0 deletions packages/x-charts/src/internals/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,34 @@ export function getSVGPoint(svg: SVGSVGElement, event: MouseEvent) {
pt.y = event.clientY;
return pt.matrixTransform(svg.getScreenCTM()!.inverse());
}

/**
* Helper that converts values and percentages into values.
* @param value The value provided by the developer. Can either be a number or a string with '%' or 'px'.
* @param refValue The numerical value associated to 100%.
* @returns The numerical value associated to the provided value.
*/
export function getPercentageValue(value: number | string, refValue: number) {
if (typeof value === 'number') {
return value;
}
if (value === '100%') {
// Avoid potential rounding issues
return refValue;
}
if (value.endsWith('%')) {
const percentage = Number.parseFloat(value.slice(0, value.length - 1));
if (!Number.isNaN(percentage)) {
return (percentage * refValue) / 100;
}
}
if (value.endsWith('px')) {
const val = Number.parseFloat(value.slice(0, value.length - 2));
if (!Number.isNaN(val)) {
return val;
}
}
throw Error(
`MUI-Charts: Received an unknown value "${value}". It should be a number, or a string with a percentage value.`,
);
}
37 changes: 30 additions & 7 deletions packages/x-charts/src/models/seriesType/pie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@ export interface PieSeriesType<Tdata = PieValueType> extends CommonSeriesType<Td
data: Tdata[];
/**
* The radius between circle center and the begining of the arc.
* Can be a number (in px) or a string with a percentage such as '50%'.
* The '100%' is the maximal radius that fit into the drawing area.
* @default 0
*/
innerRadius?: number;
innerRadius?: number | string;
/**
* The radius between circle center and the end of the arc.
* @default R_max The maximal radius that fit into the drawing area.
* Can be a number (in px) or a string with a percentage such as '50%'.
* The '100%' is the maximal radius that fit into the drawing area.
* @default '100%'
*/
outerRadius?: number;
outerRadius?: number | string;
/**
* The radius applied to arc corners (similar to border radius).
* @default 0
Expand Down Expand Up @@ -58,14 +62,18 @@ export interface PieSeriesType<Tdata = PieValueType> extends CommonSeriesType<Td
arcLabelMinAngle?: number;
/**
* The x coordinate of the pie center.
* @default width/2 the center of the drawing area.
* Can be a number (in px) or a string with a percentage such as '50%'.
* The '100%' is the width the drawing area.
* @default '50%'
*/
cx?: number;
cx?: number | string;
/**
* The y coordinate of the pie center.
* @default height/2 the center of the drawing area.
* Can be a number (in px) or a string with a percentage such as '50%'.
* The '100%' is the height the drawing area.
* @default '50%'
*/
cy?: number;
cy?: number | string;
/**
* Override the arc attibutes when it is highlighted.
*/
Expand Down Expand Up @@ -112,3 +120,18 @@ export interface DefaultizedPieSeriesType
extends DefaultizedProps<PieSeriesType, CommonDefaultizedProps> {
data: DefaultizedPieValueType[];
}

/**
* Props received when the parent components has done the percentage conversion.
*/
export interface ComputedPieRadius {
/**
* The radius between circle center and the begining of the arc.
* @default 0
*/
innerRadius?: number;
/**
* The radius between circle center and the end of the arc.
*/
outerRadius: number;
}
1 change: 1 addition & 0 deletions scripts/x-charts.exports.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
{ "name": "cheerfulFiestaPalette", "kind": "Variable" },
{ "name": "cheerfulFiestaPaletteDark", "kind": "Variable" },
{ "name": "cheerfulFiestaPaletteLight", "kind": "Variable" },
{ "name": "ComputedPieRadius", "kind": "Interface" },
{ "name": "ContinuouseScaleName", "kind": "TypeAlias" },
{ "name": "CurveType", "kind": "TypeAlias" },
{ "name": "DEFAULT_MARGINS", "kind": "Variable" },
Expand Down

0 comments on commit 4106101

Please sign in to comment.