diff --git a/docs/data/charts/funnel/FunnelBorderRadius.js b/docs/data/charts/funnel/FunnelBorderRadius.js new file mode 100644 index 0000000000000..fc51bc47653a2 --- /dev/null +++ b/docs/data/charts/funnel/FunnelBorderRadius.js @@ -0,0 +1,29 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { Unstable_FunnelChart as FunnelChart } from '@mui/x-charts-pro/FunnelChart'; + +export default function FunnelBorderRadius() { + return ( + + + + + ); +} diff --git a/docs/data/charts/funnel/FunnelBorderRadius.tsx b/docs/data/charts/funnel/FunnelBorderRadius.tsx new file mode 100644 index 0000000000000..fc51bc47653a2 --- /dev/null +++ b/docs/data/charts/funnel/FunnelBorderRadius.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { Unstable_FunnelChart as FunnelChart } from '@mui/x-charts-pro/FunnelChart'; + +export default function FunnelBorderRadius() { + return ( + + + + + ); +} diff --git a/docs/data/charts/funnel/FunnelCurves.js b/docs/data/charts/funnel/FunnelCurves.js index 4e67aafae13a1..141b4deb6089b 100644 --- a/docs/data/charts/funnel/FunnelCurves.js +++ b/docs/data/charts/funnel/FunnelCurves.js @@ -22,6 +22,12 @@ export default function FunnelCurves() { min: 0, max: 20, }, + borderRadius: { + knob: 'slider', + defaultValue: 0, + min: 0, + max: 20, + }, }} renderDemo={(props) => ( @@ -29,6 +35,7 @@ export default function FunnelCurves() { series={[ { curve: props.curveType, + borderRadius: props.borderRadius, layout: 'vertical', ...populationByEducationLevelPercentageSeries, }, @@ -41,6 +48,7 @@ export default function FunnelCurves() { series={[ { curve: props.curveType, + borderRadius: props.borderRadius, layout: 'horizontal', ...populationByEducationLevelPercentageSeries, }, @@ -55,7 +63,10 @@ export default function FunnelCurves() { return `import { FunnelChart } from '@mui/x-charts-pro/FunnelChart'; `; diff --git a/docs/data/charts/funnel/FunnelCurves.tsx b/docs/data/charts/funnel/FunnelCurves.tsx index be4827ecef762..cc63c398762a0 100644 --- a/docs/data/charts/funnel/FunnelCurves.tsx +++ b/docs/data/charts/funnel/FunnelCurves.tsx @@ -23,6 +23,12 @@ export default function FunnelCurves() { min: 0, max: 20, }, + borderRadius: { + knob: 'slider', + defaultValue: 0, + min: 0, + max: 20, + }, } as const } renderDemo={(props) => ( @@ -31,6 +37,7 @@ export default function FunnelCurves() { series={[ { curve: props.curveType, + borderRadius: props.borderRadius, layout: 'vertical', ...populationByEducationLevelPercentageSeries, }, @@ -43,6 +50,7 @@ export default function FunnelCurves() { series={[ { curve: props.curveType, + borderRadius: props.borderRadius, layout: 'horizontal', ...populationByEducationLevelPercentageSeries, }, @@ -57,7 +65,10 @@ export default function FunnelCurves() { return `import { FunnelChart } from '@mui/x-charts-pro/FunnelChart'; `; diff --git a/docs/data/charts/funnel/funnel.md b/docs/data/charts/funnel/funnel.md index 74c2879a15f9d..ba94ff1a48c2b 100644 --- a/docs/data/charts/funnel/funnel.md +++ b/docs/data/charts/funnel/funnel.md @@ -67,6 +67,24 @@ It accepts a number that represents the gap in pixels. {{"demo": "FunnelGap.js"}} +### Border radius + +The border radius of the sections can be customized by the `borderRadius` property. +It accepts a number that represents the radius in pixels. + +- The `bump` curve interpolation will not respect the border radius. +- The `linear` curve respects the border radius to some extent due to the angle of the sections. +- The `step` curve will respect the border radius. + +To understand how the border radius interacts with the `curve` prop, see the [curve interpolation example](/x/react-charts/funnel/#curve-interpolation) above. + +The `borderRadius` property will also behave differently depending on whether the `gap` property is greater than 0. + +- If the `gap` is 0, the border radius will be applied to the corners of the sections that are not connected to another section. +- If the `gap` is greater than 0, the border radius will be applied to all the corners of the sections. + +{{"demo": "FunnelBorderRadius.js"}} + ### Colors The funnel colors can be customized in two ways. diff --git a/packages/x-charts-pro/src/FunnelChart/FunnelPlot.tsx b/packages/x-charts-pro/src/FunnelChart/FunnelPlot.tsx index ce2b461536bff..0d61e68c5aa37 100644 --- a/packages/x-charts-pro/src/FunnelChart/FunnelPlot.tsx +++ b/packages/x-charts-pro/src/FunnelChart/FunnelPlot.tsx @@ -67,8 +67,6 @@ const useAggregatedData = (gap: number | undefined) => { const xScale = xAxis[xAxisId].scale; const yScale = yAxis[yAxisId].scale; - const curve = getFunnelCurve(currentSeries.curve, isHorizontal, gap); - const xPosition = ( value: number, bandIndex: number, @@ -107,6 +105,15 @@ const useAggregatedData = (gap: number | undefined) => { }) : currentSeries.sectionLabel; + const curve = getFunnelCurve( + currentSeries.curve, + isHorizontal, + gap, + dataIndex, + currentSeries.dataPoints.length, + currentSeries.borderRadius, + ); + const line = d3Line() .x((d) => xPosition(d.x, baseScaleConfig.data?.[dataIndex], d.stackOffset, d.useBandWidth), diff --git a/packages/x-charts-pro/src/FunnelChart/curves/borderRadiusPolygon.ts b/packages/x-charts-pro/src/FunnelChart/curves/borderRadiusPolygon.ts new file mode 100644 index 0000000000000..ac42441538b6e --- /dev/null +++ b/packages/x-charts-pro/src/FunnelChart/curves/borderRadiusPolygon.ts @@ -0,0 +1,49 @@ +import { Point } from './curve.types'; + +const distance = (p1: Point, p2: Point) => Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2); +const lerp = (a: number, b: number, x: number) => a + (b - a) * x; +const lerp2D = (p1: Point, p2: Point, t: number) => ({ + x: lerp(p1.x, p2.x, t), + y: lerp(p1.y, p2.y, t), +}); + +/** + * Draws a polygon with rounded corners + * @param {CanvasRenderingContext2D} ctx The canvas context + * @param {Array} points A list of `{x, y}` points + * @param {number} radius how much to round the corners + */ +export function borderRadiusPolygon( + ctx: CanvasRenderingContext2D, + points: Point[], + radius: number | number[], +): void { + const numPoints = points.length; + + radius = Array.isArray(radius) ? radius : Array(numPoints).fill(radius); + + const corners: Point[][] = []; + for (let i = 0; i < numPoints; i += 1) { + const lastPoint = points[i]; + const thisPoint = points[(i + 1) % numPoints]; + const nextPoint = points[(i + 2) % numPoints]; + + const lastEdgeLength = distance(lastPoint, thisPoint); + const lastOffsetDistance = Math.min(lastEdgeLength / 2, radius[i] ?? 0); + const start = lerp2D(thisPoint, lastPoint, lastOffsetDistance / lastEdgeLength); + + const nextEdgeLength = distance(nextPoint, thisPoint); + const nextOffsetDistance = Math.min(nextEdgeLength / 2, radius[i] ?? 0); + const end = lerp2D(thisPoint, nextPoint, nextOffsetDistance / nextEdgeLength); + + corners.push([start, thisPoint, end]); + } + + ctx.moveTo(corners[0][0].x, corners[0][0].y); + for (const [start, ctrl, end] of corners) { + ctx.lineTo(start.x, start.y); + ctx.quadraticCurveTo(ctrl.x, ctrl.y, end.x, end.y); + } + + ctx.closePath(); +} diff --git a/packages/x-charts-pro/src/FunnelChart/curves/bump.ts b/packages/x-charts-pro/src/FunnelChart/curves/bump.ts index c20c58f9de25f..4cdd2db0e6b32 100644 --- a/packages/x-charts-pro/src/FunnelChart/curves/bump.ts +++ b/packages/x-charts-pro/src/FunnelChart/curves/bump.ts @@ -1,18 +1,17 @@ +/* eslint-disable class-methods-use-this */ import { CurveGenerator } from '@mui/x-charts-vendor/d3-shape'; /** * This is a custom "bump" curve generator. + * It draws smooth curves for the 4 provided points, + * with the option to add a gap between sections while also properly handling the border radius. * - * It takes into account the gap between the points and draws a smooth curve between them. - * - * It is based on the d3-shape bump curve generator. + * The implementation is based on the d3-shape bump curve generator. * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/bump.js */ export class Bump implements CurveGenerator { private context: CanvasRenderingContext2D; - private line: number = NaN; - private x: number = NaN; private y: number = NaN; @@ -23,30 +22,22 @@ export class Bump implements CurveGenerator { private gap: number = 0; - constructor(context: CanvasRenderingContext2D, isHorizontal: boolean, gap: number = 0) { + constructor( + context: CanvasRenderingContext2D, + { isHorizontal, gap }: { isHorizontal: boolean; gap?: number }, + ) { this.context = context; this.isHorizontal = isHorizontal; - this.gap = gap / 2; + this.gap = (gap ?? 0) / 2; } - areaStart(): void { - this.line = 0; - } + areaStart(): void {} - areaEnd(): void { - this.line = NaN; - } + areaEnd(): void {} - lineStart(): void { - this.currentPoint = 0; - } + lineStart(): void {} - lineEnd() { - if (this.line || (this.line !== 0 && this.currentPoint === 1)) { - this.context.closePath(); - } - this.line = 1 - this.line; - } + lineEnd(): void {} point(x: number, y: number): void { x = +x; @@ -65,6 +56,9 @@ export class Bump implements CurveGenerator { this.context.bezierCurveTo((this.x + x) / 2, this.y, (this.x + x) / 2, y, x + this.gap, y); } + if (this.currentPoint === 3) { + this.context.closePath(); + } this.currentPoint += 1; this.x = x; this.y = y; @@ -84,6 +78,9 @@ export class Bump implements CurveGenerator { this.context.bezierCurveTo(this.x, (this.y + y) / 2, x, (this.y + y) / 2, x, y + this.gap); } + if (this.currentPoint === 3) { + this.context.closePath(); + } this.currentPoint += 1; this.x = x; this.y = y; diff --git a/packages/x-charts-pro/src/FunnelChart/curves/curve.types.ts b/packages/x-charts-pro/src/FunnelChart/curves/curve.types.ts index 572047b231d55..e3f231ff8173c 100644 --- a/packages/x-charts-pro/src/FunnelChart/curves/curve.types.ts +++ b/packages/x-charts-pro/src/FunnelChart/curves/curve.types.ts @@ -6,3 +6,8 @@ export type FunnelCurveOptions = { gap?: number; }; export type FunnelCurveType = 'linear' | 'step' | 'bump'; + +export type Point = { + x: number; + y: number; +}; diff --git a/packages/x-charts-pro/src/FunnelChart/curves/funnelStep.ts b/packages/x-charts-pro/src/FunnelChart/curves/funnelStep.ts deleted file mode 100644 index ff03a34e9bd55..0000000000000 --- a/packages/x-charts-pro/src/FunnelChart/curves/funnelStep.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { CurveGenerator } from '@mui/x-charts-vendor/d3-shape'; - -/** - * This is a custom "step" curve generator for the funnel chart. - * It is used to draw the funnel using "rectangles" without having to rework the rendering logic. - * - * It takes into account the gap between the points and draws a smooth curve between them. - * - * It is based on the d3-shape step curve generator. - * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/step.js - */ -export class FunnelStep implements CurveGenerator { - private context: CanvasRenderingContext2D; - - private line: number = NaN; - - private x: number = NaN; - - private y: number = NaN; - - private currentPoint: number = 0; - - private isHorizontal: boolean = false; - - private gap: number = 0; - - constructor(context: CanvasRenderingContext2D, isHorizontal: boolean, gap: number = 0) { - this.context = context; - this.isHorizontal = isHorizontal; - this.gap = gap / 2; - } - - areaStart(): void { - this.line = 0; - } - - areaEnd(): void { - this.line = NaN; - } - - lineStart(): void { - this.x = NaN; - this.y = NaN; - this.currentPoint = 0; - } - - lineEnd(): void { - if (this.currentPoint === 2) { - this.context.lineTo(this.x, this.y); - } - if (this.line || (this.line !== 0 && this.currentPoint === 1)) { - this.context.closePath(); - } - if (this.line >= 0) { - this.line = 1 - this.line; - } - } - - point(x: number, y: number): void { - x = +x; - y = +y; - - // 0 is the top-left corner. - if (this.isHorizontal) { - if (this.currentPoint === 0) { - this.context.moveTo(x + this.gap, y); - } else if (this.currentPoint === 1 || this.currentPoint === 2) { - this.context.lineTo(x - this.gap, this.y); - this.context.lineTo(x - this.gap, y); - } else { - this.context.lineTo(this.x - this.gap, y); - this.context.lineTo(x + this.gap, y); - } - - this.currentPoint += 1; - this.x = x; - this.y = y; - return; - } - - // 0 is the top-right corner. - if (this.currentPoint === 0) { - this.context.moveTo(x, y + this.gap); - } else if (this.currentPoint === 3) { - this.context.lineTo(x, this.y - this.gap); - this.context.lineTo(x, y + this.gap); - } else { - this.context.lineTo(this.x, y - this.gap); - this.context.lineTo(x, y - this.gap); - } - - this.currentPoint += 1; - this.x = x; - this.y = y; - } -} diff --git a/packages/x-charts-pro/src/FunnelChart/curves/getFunnelCurve.ts b/packages/x-charts-pro/src/FunnelChart/curves/getFunnelCurve.ts index bfde8fcb7c422..85c874060bc8f 100644 --- a/packages/x-charts-pro/src/FunnelChart/curves/getFunnelCurve.ts +++ b/packages/x-charts-pro/src/FunnelChart/curves/getFunnelCurve.ts @@ -1,12 +1,12 @@ import { CurveFactory } from '@mui/x-charts-vendor/d3-shape'; import { FunnelCurveType } from './curve.types'; -import { FunnelStep } from './funnelStep'; +import { Step } from './step'; import { Linear } from './linear'; import { Bump } from './bump'; const curveConstructor = (curve: FunnelCurveType | undefined) => { if (curve === 'step') { - return FunnelStep; + return Step; } if (curve === 'bump') { return Bump; @@ -17,7 +17,17 @@ const curveConstructor = (curve: FunnelCurveType | undefined) => { export const getFunnelCurve = ( curve: FunnelCurveType | undefined, isHorizontal: boolean, - gap: number = 0, + gap: number | undefined, + dataIndex: number, + totalDataPoints: number, + borderRadius: number | undefined, ): CurveFactory => { - return (context) => new (curveConstructor(curve))(context as any, isHorizontal, gap); + return (context) => + new (curveConstructor(curve))(context as any, { + isHorizontal, + gap, + position: dataIndex, + sections: totalDataPoints, + borderRadius, + }); }; diff --git a/packages/x-charts-pro/src/FunnelChart/curves/linear.ts b/packages/x-charts-pro/src/FunnelChart/curves/linear.ts index 3ad11b75c5f8b..016d92921944e 100644 --- a/packages/x-charts-pro/src/FunnelChart/curves/linear.ts +++ b/packages/x-charts-pro/src/FunnelChart/curves/linear.ts @@ -1,4 +1,7 @@ +/* eslint-disable class-methods-use-this */ import { CurveGenerator } from '@mui/x-charts-vendor/d3-shape'; +import { Point } from './curve.types'; +import { borderRadiusPolygon } from './borderRadiusPolygon'; // From point1 to point2, get the x value from y const xFromY = @@ -26,102 +29,110 @@ const yFromX = /** * This is a custom "linear" curve generator. + * It draws straight lines for the 4 provided points, + * with the option to add a gap between sections while also properly handling the border radius. * - * It takes into account the gap between the points and draws a smooth curve between them. - * - * It is based on the d3-shape linear curve generator. + * The implementation is based on the d3-shape linear curve generator. * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/linear.js */ export class Linear implements CurveGenerator { private context: CanvasRenderingContext2D; - private line: number = NaN; - - private x: number = NaN; - - private y: number = NaN; + private position: number = 0; - private currentPoint: number = 0; + private sections: number = 0; private isHorizontal: boolean = false; private gap: number = 0; - constructor(context: CanvasRenderingContext2D, isHorizontal: boolean, gap: number = 0) { + private borderRadius: number = 0; + + private points: Point[] = []; + + constructor( + context: CanvasRenderingContext2D, + { + isHorizontal, + gap, + position, + sections, + borderRadius, + }: { + isHorizontal: boolean; + gap?: number; + position?: number; + sections?: number; + borderRadius?: number; + }, + ) { this.context = context; this.isHorizontal = isHorizontal; - this.gap = gap / 2; + this.gap = (gap ?? 0) / 2; + this.position = position ?? 0; + this.sections = sections ?? 1; + this.borderRadius = borderRadius ?? 0; } - areaStart(): void { - this.line = 0; - } + areaStart(): void {} - areaEnd(): void { - this.line = NaN; - } + areaEnd(): void {} - lineStart(): void { - this.currentPoint = 0; - } + lineStart(): void {} - lineEnd() { - if (this.line || (this.line !== 0 && this.currentPoint === 1)) { - this.context.closePath(); + lineEnd(): void {} + + point(xIn: number, yIn: number): void { + this.points.push({ x: xIn, y: yIn }); + if (this.points.length < 4) { + return; } - this.line = 1 - this.line; - } - point(x: number, y: number): void { - x = +x; - y = +y; - - // We draw the lines only at currentPoint 1 & 3 because we need - // The data of a pair of points to draw the lines. - // Hence currentPoint 1 draws a line from point 0 to point 1 and point 1 to point 2. - // currentPoint 3 draws a line from point 2 to point 3 and point 3 to point 0. - - if (this.isHorizontal) { - const yGetter = yFromX(this.x, this.y, x, y); - let xGap = 0; - - // 0 is the top-left corner. - if (this.currentPoint === 1) { - xGap = this.x + this.gap; - this.context.moveTo(xGap, yGetter(xGap)); - this.context.lineTo(xGap, yGetter(xGap)); - xGap = x - this.gap; - this.context.lineTo(xGap, yGetter(xGap)); - } else if (this.currentPoint === 3) { - xGap = this.x - this.gap; - this.context.lineTo(xGap, yGetter(xGap)); - xGap = x + this.gap; - this.context.lineTo(xGap, yGetter(xGap)); + // Add gaps where they are needed. + this.points = this.points.map((point, index) => { + const slopeStart = this.points.at(index <= 1 ? 0 : 2)!; + const slopeEnd = this.points.at(index <= 1 ? 1 : 3)!; + const yGetter = yFromX( + slopeStart.x - this.gap, + slopeStart.y, + slopeEnd.x - this.gap, + slopeEnd.y, + ); + if (this.isHorizontal) { + const xGap = point.x + (index === 0 || index === 3 ? this.gap : -this.gap); + + return { + x: xGap, + y: yGetter(xGap), + }; } - } - if (!this.isHorizontal) { - const xGetter = xFromY(this.x, this.y, x, y); - let yGap = 0; - - // 0 is the top-right corner. - if (this.currentPoint === 1) { - yGap = this.y + this.gap; - this.context.moveTo(xGetter(yGap), yGap); - this.context.lineTo(xGetter(yGap), yGap); - yGap = y - this.gap; - this.context.lineTo(xGetter(yGap), yGap); - } else if (this.currentPoint === 3) { - yGap = this.y - this.gap; - this.context.lineTo(xGetter(yGap), yGap); - yGap = y + this.gap; - this.context.lineTo(xGetter(yGap), yGap); + const xGetter = xFromY( + slopeStart.x, + slopeStart.y - this.gap, + slopeEnd.x, + slopeEnd.y - this.gap, + ); + const yGap = point.y + (index === 0 || index === 3 ? this.gap : -this.gap); + return { + x: xGetter(yGap), + y: yGap, + }; + }); + + const getBorderRadius = () => { + if (this.gap > 0) { + return this.borderRadius; } - } + if (this.position === 0) { + return [0, 0, this.borderRadius, this.borderRadius]; + } + if (this.position === this.sections - 1) { + return [this.borderRadius, this.borderRadius]; + } + return 0; + }; - // Increment the values - this.currentPoint += 1; - this.x = x; - this.y = y; + borderRadiusPolygon(this.context, this.points, getBorderRadius()); } } diff --git a/packages/x-charts-pro/src/FunnelChart/curves/step.ts b/packages/x-charts-pro/src/FunnelChart/curves/step.ts new file mode 100644 index 0000000000000..932c54ffc4b1e --- /dev/null +++ b/packages/x-charts-pro/src/FunnelChart/curves/step.ts @@ -0,0 +1,106 @@ +/* eslint-disable class-methods-use-this */ +import { CurveGenerator } from '@mui/x-charts-vendor/d3-shape'; +import { Point } from './curve.types'; +import { borderRadiusPolygon } from './borderRadiusPolygon'; + +const max = (numbers: number[]) => Math.max(...numbers, -Infinity); +const min = (numbers: number[]) => Math.min(...numbers, Infinity); + +/** + * This is a custom "step" curve generator. + * It is used to draw "rectangles" from 4 points without having to rework the rendering logic, + * with the option to add a gap between sections while also properly handling the border radius. + * + * It takes the min and max of the x and y coordinates of the points to create a rectangle. + * + * The implementation is based on the d3-shape step curve generator. + * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/step.js + */ +export class Step implements CurveGenerator { + private context: CanvasRenderingContext2D; + + private isHorizontal: boolean = false; + + private gap: number = 0; + + private borderRadius: number = 0; + + private position: number = 0; + + private points: Point[] = []; + + constructor( + context: CanvasRenderingContext2D, + { + isHorizontal, + gap, + position, + borderRadius, + }: { + isHorizontal: boolean; + gap?: number; + position?: number; + sections?: number; + borderRadius?: number; + }, + ) { + this.context = context; + this.isHorizontal = isHorizontal; + this.gap = (gap ?? 0) / 2; + this.position = position ?? 0; + this.borderRadius = borderRadius ?? 0; + } + + areaStart(): void {} + + areaEnd(): void {} + + lineStart(): void {} + + lineEnd(): void {} + + point(xIn: number, yIn: number): void { + this.points.push({ x: xIn, y: yIn }); + if (this.points.length < 4) { + return; + } + + // Ensure we have rectangles instead of trapezoids. + this.points = this.points.map((_, index) => { + const allX = this.points.map((p) => p.x); + const allY = this.points.map((p) => p.y); + if (this.isHorizontal) { + return { + x: index === 1 || index === 2 ? max(allX) : min(allX), + y: index <= 1 ? max(allY) : min(allY), + }; + } + return { + x: index <= 1 ? min(allX) : max(allX), + y: index === 1 || index === 2 ? max(allY) : min(allY), + }; + }); + + // Add gaps where they are needed. + this.points = this.points.map((point, index) => { + if (this.isHorizontal) { + return { + x: point.x + (index === 0 || index === 3 ? this.gap : -this.gap), + y: point.y, + }; + } + return { + x: point.x, + y: point.y + (index === 0 || index === 3 ? this.gap : -this.gap), + }; + }); + + borderRadiusPolygon( + this.context, + this.points, + this.gap > 0 || this.position === 0 + ? this.borderRadius + : [this.borderRadius, this.borderRadius], + ); + } +} diff --git a/packages/x-charts-pro/src/FunnelChart/funnel.types.ts b/packages/x-charts-pro/src/FunnelChart/funnel.types.ts index f4d91a61c59ca..3a5bb138f66f2 100644 --- a/packages/x-charts-pro/src/FunnelChart/funnel.types.ts +++ b/packages/x-charts-pro/src/FunnelChart/funnel.types.ts @@ -58,6 +58,11 @@ export interface FunnelSeriesType * @default 'linear' */ curve?: FunnelCurveType; + /** + * The radius, in pixels, of the corners of the funnel sections. + * @default 8 + */ + borderRadius?: number; /** * The label configuration for the funnel plot. * Allows to customize the position and margin of the label that is displayed on the funnel sections. diff --git a/packages/x-charts-pro/src/FunnelChart/seriesConfig/getSeriesWithDefaultValues.ts b/packages/x-charts-pro/src/FunnelChart/seriesConfig/getSeriesWithDefaultValues.ts index 14cbadb5b9fc1..19c0764c80038 100644 --- a/packages/x-charts-pro/src/FunnelChart/seriesConfig/getSeriesWithDefaultValues.ts +++ b/packages/x-charts-pro/src/FunnelChart/seriesConfig/getSeriesWithDefaultValues.ts @@ -8,6 +8,7 @@ const getSeriesWithDefaultValues: GetSeriesWithDefaultValues<'funnel'> = ( return { id: seriesData.id ?? `auto-generated-id-${seriesIndex}`, ...seriesData, + borderRadius: seriesData.borderRadius ?? 8, data: seriesData.data.map((d, index) => ({ color: colors[index % colors.length], ...d,