Skip to content

Commit

Permalink
[docs] Document how to customize a subsection of a line chart (mui#13210
Browse files Browse the repository at this point in the history
)

Signed-off-by: Alexandre Fauquette <[email protected]>
Co-authored-by: Jose C Quintas Jr <[email protected]>
  • Loading branch information
2 people authored and DungTiger committed Jul 23, 2024
1 parent 34b1e9a commit 0e2d8cf
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 0 deletions.
70 changes: 70 additions & 0 deletions docs/data/charts/line-demo/LineWithPrediction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as React from 'react';
import { LineChart, AnimatedLine } from '@mui/x-charts/LineChart';
import { useChartId, useDrawingArea, useXScale } from '@mui/x-charts/hooks';

function CustomAnimatedLine(props) {
const { limit, sxBefore, sxAfter, ...other } = props;
const { top, bottom, height, left, width } = useDrawingArea();
const scale = useXScale();
const chartId = useChartId();

if (limit === undefined) {
return <AnimatedLine {...other} />;
}

const limitPosition = scale(limit); // Convert value to x coordinate.

if (limitPosition === undefined) {
return <AnimatedLine {...other} />;
}

const clipIdleft = `${chartId}-${props.ownerState.id}-line-limit-${limit}-1`;
const clipIdRight = `${chartId}-${props.ownerState.id}-line-limit-${limit}-2`;
return (
<React.Fragment>
{/* Clip to show the line before the limit */}
<clipPath id={clipIdleft}>
<rect
x={left}
y={0}
width={limitPosition - left}
height={top + height + bottom}
/>
</clipPath>
{/* Clip to show the line after the limit */}
<clipPath id={clipIdRight}>
<rect
x={limitPosition}
y={0}
width={left + width - limitPosition}
height={top + height + bottom}
/>
</clipPath>
<g clipPath={`url(#${clipIdleft})`}>
<AnimatedLine {...other} sx={sxBefore} />
</g>
<g clipPath={`url(#${clipIdRight})`}>
<AnimatedLine {...other} sx={sxAfter} />
</g>
</React.Fragment>
);
}

export default function LineWithPrediction() {
return (
<LineChart
series={[
{
type: 'line',
data: [1, 2, 3, 4, 1, 2, 3, 4, 5],
valueFormatter: (v, i) => `${v}${i.dataIndex > 5 ? ' (estimated)' : ''}`,
},
]}
xAxis={[{ data: [0, 1, 2, 3, 4, 5, 6, 7, 8] }]}
height={200}
width={400}
slots={{ line: CustomAnimatedLine }}
slotProps={{ line: { limit: 5, sxAfter: { strokeDasharray: '10 5' } } }}
/>
);
}
77 changes: 77 additions & 0 deletions docs/data/charts/line-demo/LineWithPrediction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as React from 'react';
import { LineChart, AnimatedLine, AnimatedLineProps } from '@mui/x-charts/LineChart';
import { useChartId, useDrawingArea, useXScale } from '@mui/x-charts/hooks';
import { SxProps, Theme } from '@mui/system';

interface CustomAnimatedLineProps extends AnimatedLineProps {
limit?: number;
sxBefore?: SxProps<Theme>;
sxAfter?: SxProps<Theme>;
}

function CustomAnimatedLine(props: CustomAnimatedLineProps) {
const { limit, sxBefore, sxAfter, ...other } = props;
const { top, bottom, height, left, width } = useDrawingArea();
const scale = useXScale();
const chartId = useChartId();

if (limit === undefined) {
return <AnimatedLine {...other} />;
}

const limitPosition = scale(limit); // Convert value to x coordinate.

if (limitPosition === undefined) {
return <AnimatedLine {...other} />;
}

const clipIdleft = `${chartId}-${props.ownerState.id}-line-limit-${limit}-1`;
const clipIdRight = `${chartId}-${props.ownerState.id}-line-limit-${limit}-2`;
return (
<React.Fragment>
{/* Clip to show the line before the limit */}
<clipPath id={clipIdleft}>
<rect
x={left}
y={0}
width={limitPosition - left}
height={top + height + bottom}
/>
</clipPath>
{/* Clip to show the line after the limit */}
<clipPath id={clipIdRight}>
<rect
x={limitPosition}
y={0}
width={left + width - limitPosition}
height={top + height + bottom}
/>
</clipPath>
<g clipPath={`url(#${clipIdleft})`}>
<AnimatedLine {...other} sx={sxBefore} />
</g>
<g clipPath={`url(#${clipIdRight})`}>
<AnimatedLine {...other} sx={sxAfter} />
</g>
</React.Fragment>
);
}

export default function LineWithPrediction() {
return (
<LineChart
series={[
{
type: 'line',
data: [1, 2, 3, 4, 1, 2, 3, 4, 5],
valueFormatter: (v, i) => `${v}${i.dataIndex > 5 ? ' (estimated)' : ''}`,
},
]}
xAxis={[{ data: [0, 1, 2, 3, 4, 5, 6, 7, 8] }]}
height={200}
width={400}
slots={{ line: CustomAnimatedLine }}
slotProps={{ line: { limit: 5, sxAfter: { strokeDasharray: '10 5' } } as any }}
/>
);
}
14 changes: 14 additions & 0 deletions docs/data/charts/line-demo/LineWithPrediction.tsx.preview
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<LineChart
series={[
{
type: 'line',
data: [1, 2, 3, 4, 1, 2, 3, 4, 5],
valueFormatter: (v, i) => `${v}${i.dataIndex > 5 ? ' (estimated)' : ''}`,
},
]}
xAxis={[{ data: [0, 1, 2, 3, 4, 5, 6, 7, 8] }]}
height={200}
width={400}
slots={{ line: CustomAnimatedLine }}
slotProps={{ line: { limit: 5, sxAfter: { strokeDasharray: '10 5' } } as any }}
/>
12 changes: 12 additions & 0 deletions docs/data/charts/line-demo/line-demo.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,15 @@ components: LineChart, LineElement, LineHighlightElement, LineHighlightPlot, Lin
## LineChartConnectNulls

{{"demo": "LineChartConnectNulls.js"}}

## Line with forecast

To show that parts of the data have different meanings, you can render stylised lines for each of them.

In the following example, the chart shows a dotted line to exemplify that the data is estimated.
To do so, the `slots.line` is set with a custom components that render the default line twice.

- The first one is clipped to show known values (from the left of the chart to the limit).
- The second one is clipped to show predictions (from the limit to the right of the chart) with dash styling.

{{"demo": "LineWithPrediction.js"}}

0 comments on commit 0e2d8cf

Please sign in to comment.