From 79bbfa6e80ec286805d33901a764a23e136360d1 Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Thu, 19 Oct 2023 10:14:52 +0200 Subject: [PATCH] [charts] Use new text component to avoid tick label overflow on x-axis (#10648) Co-authored-by: Lukas Co-authored-by: Sam Sycamore <71297412+samuelsycamore@users.noreply.github.com> --- .../charts/axis/AxisCustomizationNoSnap.js | 3 - .../axis/AxisTextCustomizationNoSnap.js | 170 ++++++++++++++++++ docs/data/charts/axis/TickLabelPosition.js | 97 ++++++++++ docs/data/charts/axis/TickLabelPosition.tsx | 96 ++++++++++ docs/data/charts/axis/TickNumber.js | 2 +- docs/data/charts/axis/TickNumber.tsx | 2 +- docs/data/charts/axis/TickPosition.js | 96 ++++++++++ docs/data/charts/axis/TickPosition.tsx | 95 ++++++++++ .../data/charts/axis/TickPosition.tsx.preview | 16 ++ docs/data/charts/axis/axis.md | 49 ++++- docs/data/charts/lines/CSSCustomization.js | 2 +- docs/data/charts/lines/CSSCustomization.tsx | 2 +- docs/data/charts/lines/StackedAreas.js | 2 +- docs/data/charts/lines/StackedAreas.tsx | 2 +- docs/data/charts/stacking/StackOrderDemo.js | 22 ++- docs/data/charts/stacking/StackOrderDemo.tsx | 21 ++- docs/pages/x/api/charts/bar-chart.json | 8 +- docs/pages/x/api/charts/charts-axis.json | 8 +- docs/pages/x/api/charts/charts-x-axis.json | 24 ++- docs/pages/x/api/charts/charts-y-axis.json | 24 ++- docs/pages/x/api/charts/line-chart.json | 8 +- docs/pages/x/api/charts/pie-chart.json | 8 +- docs/pages/x/api/charts/scatter-chart.json | 8 +- docs/pages/x/api/charts/spark-line-chart.json | 2 +- .../api-docs/charts/charts-axis.json | 5 +- .../api-docs/charts/charts-x-axis.json | 25 ++- .../api-docs/charts/charts-y-axis.json | 25 ++- packages/x-charts/src/BarChart/BarChart.tsx | 48 +++++ .../x-charts/src/ChartsAxis/ChartsAxis.tsx | 32 ++++ .../x-charts/src/ChartsAxis/axisClasses.ts | 2 +- .../src/ChartsLegend/ChartsLegend.tsx | 33 ++-- .../x-charts/src/ChartsXAxis/ChartsXAxis.tsx | 140 ++++++++++++--- .../x-charts/src/ChartsYAxis/ChartsYAxis.tsx | 52 ++++-- packages/x-charts/src/LineChart/LineChart.tsx | 48 +++++ packages/x-charts/src/PieChart/PieChart.tsx | 48 +++++ .../src/ScatterChart/ScatterChart.tsx | 48 +++++ .../src/SparkLineChart/SparkLineChart.tsx | 4 + .../src/context/CartesianContextProvider.tsx | 16 ++ packages/x-charts/src/hooks/useMounted.ts | 20 +++ packages/x-charts/src/hooks/useTicks.ts | 43 ++++- .../src/internals/components/ChartsText.tsx | 52 +++--- packages/x-charts/src/internals/domUtils.ts | 1 - packages/x-charts/src/internals/geometry.ts | 47 +++++ packages/x-charts/src/models/axis.ts | 33 +++- .../x-charts/src/models/seriesType/line.ts | 4 +- 45 files changed, 1328 insertions(+), 165 deletions(-) create mode 100644 docs/data/charts/axis/AxisTextCustomizationNoSnap.js create mode 100644 docs/data/charts/axis/TickLabelPosition.js create mode 100644 docs/data/charts/axis/TickLabelPosition.tsx create mode 100644 docs/data/charts/axis/TickPosition.js create mode 100644 docs/data/charts/axis/TickPosition.tsx create mode 100644 docs/data/charts/axis/TickPosition.tsx.preview create mode 100644 packages/x-charts/src/hooks/useMounted.ts create mode 100644 packages/x-charts/src/internals/geometry.ts diff --git a/docs/data/charts/axis/AxisCustomizationNoSnap.js b/docs/data/charts/axis/AxisCustomizationNoSnap.js index e3c0a10209a0..f33f0352d481 100644 --- a/docs/data/charts/axis/AxisCustomizationNoSnap.js +++ b/docs/data/charts/axis/AxisCustomizationNoSnap.js @@ -17,7 +17,6 @@ const defaultXAxis = { disableTicks: false, fontSize: 12, label: 'My axis', - labelFontSize: 14, tickSize: 6, }; export default function AxisCustomizationNoSnap() { @@ -27,9 +26,7 @@ export default function AxisCustomizationNoSnap() { data={[ { propName: 'disableLine', knob: 'switch', defaultValue: false }, { propName: 'disableTicks', knob: 'switch', defaultValue: false }, - { propName: 'fontSize', knom: 'number', defaultValue: 12 }, { propName: 'label', knob: 'input', defaultValue: 'my axis' }, - { propName: 'labelFontSize', knom: 'number', defaultValue: 14 }, { propName: 'tickSize', knob: 'number', defaultValue: 6 }, ]} renderDemo={(props) => ( diff --git a/docs/data/charts/axis/AxisTextCustomizationNoSnap.js b/docs/data/charts/axis/AxisTextCustomizationNoSnap.js new file mode 100644 index 000000000000..9f8cc6aa3d6b --- /dev/null +++ b/docs/data/charts/axis/AxisTextCustomizationNoSnap.js @@ -0,0 +1,170 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import ChartsUsageDemo from 'docsx/src/modules/components/ChartsUsageDemo'; +import { BarChart } from '@mui/x-charts/BarChart'; + +const chartSetting = { + height: 300, +}; +const dataset = [ + { + london: 59, + paris: 57, + newYork: 86, + seoul: 21, + month: 'Jan', + }, + { + london: 50, + paris: 52, + newYork: 78, + seoul: 28, + month: 'Fev', + }, + { + london: 47, + paris: 53, + newYork: 106, + seoul: 41, + month: 'Mar', + }, + { + london: 54, + paris: 56, + newYork: 92, + seoul: 73, + month: 'Apr', + }, + { + london: 57, + paris: 69, + newYork: 92, + seoul: 99, + month: 'May', + }, + { + london: 60, + paris: 63, + newYork: 103, + seoul: 144, + month: 'June', + }, + { + london: 59, + paris: 60, + newYork: 105, + seoul: 319, + month: 'July', + }, + { + london: 65, + paris: 60, + newYork: 106, + seoul: 249, + month: 'Aug', + }, + { + london: 51, + paris: 51, + newYork: 95, + seoul: 131, + month: 'Sept', + }, + { + london: 60, + paris: 65, + newYork: 97, + seoul: 55, + month: 'Oct', + }, + { + london: 67, + paris: 64, + newYork: 76, + seoul: 48, + month: 'Nov', + }, + { + london: 61, + paris: 70, + newYork: 103, + seoul: 25, + month: 'Dec', + }, +]; + +const valueFormatter = (value) => `${value}mm`; + +export default function AxisTextCustomizationNoSnap() { + return ( + ( + + + + )} + getCode={({ props }) => + [ + `import { ScatterChart } from '@mui/x-charts/ScatterChart';`, + '', + `', + ].join('\n') + } + /> + ); +} diff --git a/docs/data/charts/axis/TickLabelPosition.js b/docs/data/charts/axis/TickLabelPosition.js new file mode 100644 index 000000000000..ea95912dce0a --- /dev/null +++ b/docs/data/charts/axis/TickLabelPosition.js @@ -0,0 +1,97 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; + +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function TickLabelPosition() { + return ( + + [0, 12].includes(time.getHours()), + tickLabelInterval: (time) => time.getHours() === 0, + }, + { + ...xAxisCommon, + id: 'topAxis', + scaleType: 'point', + }, + ]} + {...config} + /> + + ); +} + +const valueFormatter = (date) => + date.toLocaleDateString('fr-FR', { + month: '2-digit', + day: '2-digit', + }); + +const timeData = [ + new Date(2023, 7, 31), + new Date(2023, 7, 31, 3), + new Date(2023, 7, 31, 6), + new Date(2023, 7, 31, 9), + new Date(2023, 7, 31, 12), + new Date(2023, 7, 31, 15), + new Date(2023, 7, 31, 18), + new Date(2023, 8, 1), + new Date(2023, 8, 1, 3), + new Date(2023, 8, 1, 6), + new Date(2023, 8, 1, 9), + new Date(2023, 8, 1, 12), + new Date(2023, 8, 1, 15), + new Date(2023, 8, 1, 18), + new Date(2023, 8, 2), + new Date(2023, 8, 2, 3), + new Date(2023, 8, 2, 6), + new Date(2023, 8, 2, 9), + new Date(2023, 8, 2, 12), + new Date(2023, 8, 2, 15), + new Date(2023, 8, 2, 18), + new Date(2023, 8, 3), + new Date(2023, 8, 3, 3), + new Date(2023, 8, 3, 6), + new Date(2023, 8, 3, 9), + new Date(2023, 8, 3, 12), + new Date(2023, 8, 3, 15), + new Date(2023, 8, 3, 18), + new Date(2023, 8, 4), +]; + +const y1 = [ + 5, 5.5, 5.3, 4.9, 5, 6.2, 8.9, 10, 15, 30, 80, 90, 94, 93, 85, 86, 75, 70, 68, 50, + 20, 30, 35, 28, 25, 27, 30, 28, 25, +]; + +const y2 = [ + 90, 93, 89, 84, 85, 83, 73, 70, 63, 32, 30, 25, 18, 19, 23, 30, 32, 36, 40, 40, 42, + 45, 46, 42, 39, 40, 41, 43, 50, +]; + +const showMark = (params) => { + const { position } = params; + return position.getHours() === 0; +}; + +const config = { + series: [ + { data: y1, showMark }, + { data: y2, showMark }, + ], + height: 300, + topAxis: 'topAxis', + bottomAxis: 'bottomAxis', + leftAxis: null, +}; +const xAxisCommon = { + data: timeData, + scaleType: 'time', + valueFormatter, +}; diff --git a/docs/data/charts/axis/TickLabelPosition.tsx b/docs/data/charts/axis/TickLabelPosition.tsx new file mode 100644 index 000000000000..ce2ad07ff5da --- /dev/null +++ b/docs/data/charts/axis/TickLabelPosition.tsx @@ -0,0 +1,96 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { ShowMarkParams } from '@mui/x-charts/models'; +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function TickLabelPosition() { + return ( + + [0, 12].includes(time.getHours()), + tickLabelInterval: (time) => time.getHours() === 0, + }, + { + ...xAxisCommon, + id: 'topAxis', + scaleType: 'point', + }, + ]} + {...config} + /> + + ); +} + +const valueFormatter = (date: Date) => + date.toLocaleDateString('fr-FR', { + month: '2-digit', + day: '2-digit', + }); + +const timeData = [ + new Date(2023, 7, 31), + new Date(2023, 7, 31, 3), + new Date(2023, 7, 31, 6), + new Date(2023, 7, 31, 9), + new Date(2023, 7, 31, 12), + new Date(2023, 7, 31, 15), + new Date(2023, 7, 31, 18), + new Date(2023, 8, 1), + new Date(2023, 8, 1, 3), + new Date(2023, 8, 1, 6), + new Date(2023, 8, 1, 9), + new Date(2023, 8, 1, 12), + new Date(2023, 8, 1, 15), + new Date(2023, 8, 1, 18), + new Date(2023, 8, 2), + new Date(2023, 8, 2, 3), + new Date(2023, 8, 2, 6), + new Date(2023, 8, 2, 9), + new Date(2023, 8, 2, 12), + new Date(2023, 8, 2, 15), + new Date(2023, 8, 2, 18), + new Date(2023, 8, 3), + new Date(2023, 8, 3, 3), + new Date(2023, 8, 3, 6), + new Date(2023, 8, 3, 9), + new Date(2023, 8, 3, 12), + new Date(2023, 8, 3, 15), + new Date(2023, 8, 3, 18), + new Date(2023, 8, 4), +]; + +const y1 = [ + 5, 5.5, 5.3, 4.9, 5, 6.2, 8.9, 10, 15, 30, 80, 90, 94, 93, 85, 86, 75, 70, 68, 50, + 20, 30, 35, 28, 25, 27, 30, 28, 25, +]; +const y2 = [ + 90, 93, 89, 84, 85, 83, 73, 70, 63, 32, 30, 25, 18, 19, 23, 30, 32, 36, 40, 40, 42, + 45, 46, 42, 39, 40, 41, 43, 50, +]; + +const showMark = (params: ShowMarkParams) => { + const { position } = params as ShowMarkParams; + return position.getHours() === 0; +}; + +const config = { + series: [ + { data: y1, showMark }, + { data: y2, showMark }, + ], + height: 300, + topAxis: 'topAxis', + bottomAxis: 'bottomAxis', + leftAxis: null, +}; +const xAxisCommon = { + data: timeData, + scaleType: 'time', + valueFormatter, +} as const; diff --git a/docs/data/charts/axis/TickNumber.js b/docs/data/charts/axis/TickNumber.js index 4a475f1ffc8b..dc3c5a63f698 100644 --- a/docs/data/charts/axis/TickNumber.js +++ b/docs/data/charts/axis/TickNumber.js @@ -29,7 +29,7 @@ const valueFormatter = (date) => const config = { series: [{ data: y1 }, { data: y2 }], - height: 400, + height: 300, topAxis: 'half days', leftAxis: null, }; diff --git a/docs/data/charts/axis/TickNumber.tsx b/docs/data/charts/axis/TickNumber.tsx index 265a7fa02023..24792c3df942 100644 --- a/docs/data/charts/axis/TickNumber.tsx +++ b/docs/data/charts/axis/TickNumber.tsx @@ -29,7 +29,7 @@ const valueFormatter = (date: Date) => const config = { series: [{ data: y1 }, { data: y2 }], - height: 400, + height: 300, topAxis: 'half days', leftAxis: null, }; diff --git a/docs/data/charts/axis/TickPosition.js b/docs/data/charts/axis/TickPosition.js new file mode 100644 index 000000000000..e4ee1e8160d4 --- /dev/null +++ b/docs/data/charts/axis/TickPosition.js @@ -0,0 +1,96 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; + +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function TickPosition() { + return ( + + time.getHours() === 0, + }, + { + ...xAxisCommon, + id: 'topAxis', + scaleType: 'point', + }, + ]} + {...config} + /> + + ); +} + +const valueFormatter = (date) => + date.toLocaleDateString('fr-FR', { + month: '2-digit', + day: '2-digit', + }); + +const timeData = [ + new Date(2023, 7, 31), + new Date(2023, 7, 31, 3), + new Date(2023, 7, 31, 6), + new Date(2023, 7, 31, 9), + new Date(2023, 7, 31, 12), + new Date(2023, 7, 31, 15), + new Date(2023, 7, 31, 18), + new Date(2023, 8, 1), + new Date(2023, 8, 1, 3), + new Date(2023, 8, 1, 6), + new Date(2023, 8, 1, 9), + new Date(2023, 8, 1, 12), + new Date(2023, 8, 1, 15), + new Date(2023, 8, 1, 18), + new Date(2023, 8, 2), + new Date(2023, 8, 2, 3), + new Date(2023, 8, 2, 6), + new Date(2023, 8, 2, 9), + new Date(2023, 8, 2, 12), + new Date(2023, 8, 2, 15), + new Date(2023, 8, 2, 18), + new Date(2023, 8, 3), + new Date(2023, 8, 3, 3), + new Date(2023, 8, 3, 6), + new Date(2023, 8, 3, 9), + new Date(2023, 8, 3, 12), + new Date(2023, 8, 3, 15), + new Date(2023, 8, 3, 18), + new Date(2023, 8, 4), +]; + +const y1 = [ + 5, 5.5, 5.3, 4.9, 5, 6.2, 8.9, 10, 15, 30, 80, 90, 94, 93, 85, 86, 75, 70, 68, 50, + 20, 30, 35, 28, 25, 27, 30, 28, 25, +]; + +const y2 = [ + 90, 93, 89, 84, 85, 83, 73, 70, 63, 32, 30, 25, 18, 19, 23, 30, 32, 36, 40, 40, 42, + 45, 46, 42, 39, 40, 41, 43, 50, +]; + +const showMark = (params) => { + const { position } = params; + return position.getHours() === 0; +}; + +const config = { + series: [ + { data: y1, showMark }, + { data: y2, showMark }, + ], + height: 300, + topAxis: 'topAxis', + bottomAxis: 'bottomAxis', + leftAxis: null, +}; +const xAxisCommon = { + data: timeData, + scaleType: 'time', + valueFormatter, +}; diff --git a/docs/data/charts/axis/TickPosition.tsx b/docs/data/charts/axis/TickPosition.tsx new file mode 100644 index 000000000000..c18b8674dbfe --- /dev/null +++ b/docs/data/charts/axis/TickPosition.tsx @@ -0,0 +1,95 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { ShowMarkParams } from '@mui/x-charts/models'; +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function TickPosition() { + return ( + + time.getHours() === 0, + }, + { + ...xAxisCommon, + id: 'topAxis', + scaleType: 'point', + }, + ]} + {...config} + /> + + ); +} + +const valueFormatter = (date: Date) => + date.toLocaleDateString('fr-FR', { + month: '2-digit', + day: '2-digit', + }); + +const timeData = [ + new Date(2023, 7, 31), + new Date(2023, 7, 31, 3), + new Date(2023, 7, 31, 6), + new Date(2023, 7, 31, 9), + new Date(2023, 7, 31, 12), + new Date(2023, 7, 31, 15), + new Date(2023, 7, 31, 18), + new Date(2023, 8, 1), + new Date(2023, 8, 1, 3), + new Date(2023, 8, 1, 6), + new Date(2023, 8, 1, 9), + new Date(2023, 8, 1, 12), + new Date(2023, 8, 1, 15), + new Date(2023, 8, 1, 18), + new Date(2023, 8, 2), + new Date(2023, 8, 2, 3), + new Date(2023, 8, 2, 6), + new Date(2023, 8, 2, 9), + new Date(2023, 8, 2, 12), + new Date(2023, 8, 2, 15), + new Date(2023, 8, 2, 18), + new Date(2023, 8, 3), + new Date(2023, 8, 3, 3), + new Date(2023, 8, 3, 6), + new Date(2023, 8, 3, 9), + new Date(2023, 8, 3, 12), + new Date(2023, 8, 3, 15), + new Date(2023, 8, 3, 18), + new Date(2023, 8, 4), +]; + +const y1 = [ + 5, 5.5, 5.3, 4.9, 5, 6.2, 8.9, 10, 15, 30, 80, 90, 94, 93, 85, 86, 75, 70, 68, 50, + 20, 30, 35, 28, 25, 27, 30, 28, 25, +]; +const y2 = [ + 90, 93, 89, 84, 85, 83, 73, 70, 63, 32, 30, 25, 18, 19, 23, 30, 32, 36, 40, 40, 42, + 45, 46, 42, 39, 40, 41, 43, 50, +]; + +const showMark = (params: ShowMarkParams) => { + const { position } = params as ShowMarkParams; + return position.getHours() === 0; +}; + +const config = { + series: [ + { data: y1, showMark }, + { data: y2, showMark }, + ], + height: 300, + topAxis: 'topAxis', + bottomAxis: 'bottomAxis', + leftAxis: null, +}; +const xAxisCommon = { + data: timeData, + scaleType: 'time', + valueFormatter, +} as const; diff --git a/docs/data/charts/axis/TickPosition.tsx.preview b/docs/data/charts/axis/TickPosition.tsx.preview new file mode 100644 index 000000000000..660569fb3b5c --- /dev/null +++ b/docs/data/charts/axis/TickPosition.tsx.preview @@ -0,0 +1,16 @@ + time.getHours() === 0, + }, + { + ...xAxisCommon, + id: 'topAxis', + scaleType: 'point', + }, + ]} + {...config} +/> \ No newline at end of file diff --git a/docs/data/charts/axis/axis.md b/docs/data/charts/axis/axis.md index b535623a45c5..e0b59a21235a 100644 --- a/docs/data/charts/axis/axis.md +++ b/docs/data/charts/axis/axis.md @@ -67,7 +67,9 @@ xAxis={[{ min: 10, max: 50, }]} {{"demo": "MinMaxExample.js"}} -### Ticks positions +## Tick position + +### Automatic tick position You can customize the number of ticks with the property `tickNumber`. @@ -86,6 +88,43 @@ Here the top axis has a `tickMinStep` of half a day, and the bottom axis a `tick {{"demo": "TickNumber.js"}} +### Fixed tick positions + +If you want more control over the tick position, you can use the `tickInterval` property. + +This property accepts an array of values. +Ticks will be placed at those values. + +For axis with scale type `'point'`, the `tickInterval` property can be a filtering function of the type `(value, index) => boolean`. + +In the next demo, both axes are with `scaleType='point'`. +The top axis displays the default behavior. +It shows a tick for each point. +The bottom axis uses a filtering function to only display a tick at the beginning of a day. + +{{"demo": "TickPosition.js"}} + +### Filtering ticks label + +You can display labels only on a subset of ticks with the `tickLabelInterval` property. +It's a filtering function in the `(value, index) => boolean` form. + +For example `tickLabelInterval: (value, index) => index % 2 === 0` will show the label every two ticks. + +:::warning +The `value` and `index` arguments are those of the ticks, not the axis data. +::: + +By default, ticks are filtered such that their labels do not overlap. +You can override this behavior with `tickLabelInterval: () => true` which forces showing the tick label for each tick. + +In this example, the top axis is a reference for the default behavior. +Notice that tick labels do not overflow. + +At the bottom, you can see one tick for the beginning and the middle of the day but the tick label is only displayed for the beginning of the day. + +{{"demo": "TickLabelPosition.js"}} + ## Axis customization You can further customize the axis rendering besides the axis definition. @@ -114,7 +153,13 @@ Axes rendering can be further customized. Below is an interactive demonstration {{"demo": "AxisCustomizationNoSnap.js", "hideToolbar": true, "bg": "inline"}} -### Composition +### Text customization + +To customize the text elements (ticks label and the axis label) use the `tickLabelStyle` and `labelStyle` properties of the axis configuration. + +{{"demo": "AxisTextCustomizationNoSnap.js", "hideToolbar": true, "bg": "inline"}} + +## Composition If you are using composition, you have to provide the axis settings in the `` by using `xAxis` and `yAxis` props. diff --git a/docs/data/charts/lines/CSSCustomization.js b/docs/data/charts/lines/CSSCustomization.js index 256220f5f71c..480e020b8956 100644 --- a/docs/data/charts/lines/CSSCustomization.js +++ b/docs/data/charts/lines/CSSCustomization.js @@ -71,7 +71,7 @@ export default function CSSCustomization() { id: 'Years', data: years, scaleType: 'time', - valueFormatter: (date) => date.getFullYear(), + valueFormatter: (date) => date.getFullYear().toString(), }, ]} series={[ diff --git a/docs/data/charts/lines/CSSCustomization.tsx b/docs/data/charts/lines/CSSCustomization.tsx index 256220f5f71c..480e020b8956 100644 --- a/docs/data/charts/lines/CSSCustomization.tsx +++ b/docs/data/charts/lines/CSSCustomization.tsx @@ -71,7 +71,7 @@ export default function CSSCustomization() { id: 'Years', data: years, scaleType: 'time', - valueFormatter: (date) => date.getFullYear(), + valueFormatter: (date) => date.getFullYear().toString(), }, ]} series={[ diff --git a/docs/data/charts/lines/StackedAreas.js b/docs/data/charts/lines/StackedAreas.js index 76875e7605f5..5a895f8a4b44 100644 --- a/docs/data/charts/lines/StackedAreas.js +++ b/docs/data/charts/lines/StackedAreas.js @@ -62,7 +62,7 @@ export default function StackedAreas() { id: 'Years', data: years, scaleType: 'time', - valueFormatter: (date) => date.getFullYear(), + valueFormatter: (date) => date.getFullYear().toString(), }, ]} series={[ diff --git a/docs/data/charts/lines/StackedAreas.tsx b/docs/data/charts/lines/StackedAreas.tsx index 76875e7605f5..5a895f8a4b44 100644 --- a/docs/data/charts/lines/StackedAreas.tsx +++ b/docs/data/charts/lines/StackedAreas.tsx @@ -62,7 +62,7 @@ export default function StackedAreas() { id: 'Years', data: years, scaleType: 'time', - valueFormatter: (date) => date.getFullYear(), + valueFormatter: (date) => date.getFullYear().toString(), }, ]} series={[ diff --git a/docs/data/charts/stacking/StackOrderDemo.js b/docs/data/charts/stacking/StackOrderDemo.js index 2a838831f476..849e21d887f4 100644 --- a/docs/data/charts/stacking/StackOrderDemo.js +++ b/docs/data/charts/stacking/StackOrderDemo.js @@ -4,8 +4,6 @@ import TextField from '@mui/material/TextField'; import MenuItem from '@mui/material/MenuItem'; import { BarChart } from '@mui/x-charts/BarChart'; -import { axisClasses } from '@mui/x-charts/ChartsAxis'; - // Data comming from https://www.insee.fr/fr/statistiques/5013868 const commonTransportation = [ 6.5, 12.5, 17.2, 19.6, 20.1, 20.0, 19.5, 18.8, 18.2, 17.3, 16.4, 15.9, 15.2, 14.7, @@ -110,22 +108,22 @@ export default function StackOrderDemo() { diff --git a/docs/data/charts/stacking/StackOrderDemo.tsx b/docs/data/charts/stacking/StackOrderDemo.tsx index abcbbb16aa82..97449aeadfac 100644 --- a/docs/data/charts/stacking/StackOrderDemo.tsx +++ b/docs/data/charts/stacking/StackOrderDemo.tsx @@ -4,7 +4,6 @@ import TextField from '@mui/material/TextField'; import MenuItem from '@mui/material/MenuItem'; import { BarChart } from '@mui/x-charts/BarChart'; import { StackOrderType } from '@mui/x-charts/models'; -import { axisClasses } from '@mui/x-charts/ChartsAxis'; // Data comming from https://www.insee.fr/fr/statistiques/5013868 const commonTransportation = [ @@ -105,22 +104,22 @@ export default function StackOrderDemo() { diff --git a/docs/pages/x/api/charts/bar-chart.json b/docs/pages/x/api/charts/bar-chart.json index 1aebdcee51c3..e50ae45c3a0f 100644 --- a/docs/pages/x/api/charts/bar-chart.json +++ b/docs/pages/x/api/charts/bar-chart.json @@ -3,7 +3,7 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "xAxisIds[0] The id of the first provided axis" }, @@ -13,14 +13,14 @@ "leftAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "yAxisIds[0] The id of the first provided axis" }, "rightAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "null" }, @@ -30,7 +30,7 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "null" } diff --git a/docs/pages/x/api/charts/charts-axis.json b/docs/pages/x/api/charts/charts-axis.json index 284533555f66..bf524fb1148a 100644 --- a/docs/pages/x/api/charts/charts-axis.json +++ b/docs/pages/x/api/charts/charts-axis.json @@ -3,21 +3,21 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "xAxisIds[0] The id of the first provided axis" }, "leftAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "yAxisIds[0] The id of the first provided axis" }, "rightAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "null" }, @@ -26,7 +26,7 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "null" } diff --git a/docs/pages/x/api/charts/charts-x-axis.json b/docs/pages/x/api/charts/charts-x-axis.json index e1aa65e21140..ce99eb0bae3b 100644 --- a/docs/pages/x/api/charts/charts-x-axis.json +++ b/docs/pages/x/api/charts/charts-x-axis.json @@ -6,12 +6,32 @@ "disableTicks": { "type": { "name": "bool" } }, "fill": { "type": { "name": "string" }, "default": "'currentColor'" }, "label": { "type": { "name": "string" } }, - "labelFontSize": { "type": { "name": "number" }, "default": "14" }, + "labelFontSize": { + "type": { "name": "number" }, + "default": "14", + "deprecated": true, + "deprecationInfo": "Consider using labelStyle.fontSize instead." + }, + "labelStyle": { "type": { "name": "object" } }, "position": { "type": { "name": "enum", "description": "'bottom'
| 'top'" } }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" }, "stroke": { "type": { "name": "string" }, "default": "'currentColor'" }, - "tickFontSize": { "type": { "name": "number" }, "default": "12" }, + "tickFontSize": { + "type": { "name": "number" }, + "default": "12", + "deprecated": true, + "deprecationInfo": "Consider using tickLabelStyle.fontSize instead." + }, + "tickInterval": { + "type": { "name": "union", "description": "'auto'
| array
| func" }, + "default": "'auto'" + }, + "tickLabelInterval": { + "type": { "name": "union", "description": "'auto'
| func" }, + "default": "'auto'" + }, + "tickLabelStyle": { "type": { "name": "object" } }, "tickMaxStep": { "type": { "name": "number" } }, "tickMinStep": { "type": { "name": "number" } }, "tickNumber": { "type": { "name": "number" } }, diff --git a/docs/pages/x/api/charts/charts-y-axis.json b/docs/pages/x/api/charts/charts-y-axis.json index e49510b31850..812cac2fcade 100644 --- a/docs/pages/x/api/charts/charts-y-axis.json +++ b/docs/pages/x/api/charts/charts-y-axis.json @@ -6,12 +6,32 @@ "disableTicks": { "type": { "name": "bool" } }, "fill": { "type": { "name": "string" }, "default": "'currentColor'" }, "label": { "type": { "name": "string" } }, - "labelFontSize": { "type": { "name": "number" }, "default": "14" }, + "labelFontSize": { + "type": { "name": "number" }, + "default": "14", + "deprecated": true, + "deprecationInfo": "Consider using labelStyle.fontSize instead." + }, + "labelStyle": { "type": { "name": "object" } }, "position": { "type": { "name": "enum", "description": "'left'
| 'right'" } }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" }, "stroke": { "type": { "name": "string" }, "default": "'currentColor'" }, - "tickFontSize": { "type": { "name": "number" }, "default": "12" }, + "tickFontSize": { + "type": { "name": "number" }, + "default": "12", + "deprecated": true, + "deprecationInfo": "Consider using tickLabelStyle.fontSize instead." + }, + "tickInterval": { + "type": { "name": "union", "description": "'auto'
| array
| func" }, + "default": "'auto'" + }, + "tickLabelInterval": { + "type": { "name": "union", "description": "'auto'
| func" }, + "default": "'auto'" + }, + "tickLabelStyle": { "type": { "name": "object" } }, "tickMaxStep": { "type": { "name": "number" } }, "tickMinStep": { "type": { "name": "number" } }, "tickNumber": { "type": { "name": "number" } }, diff --git a/docs/pages/x/api/charts/line-chart.json b/docs/pages/x/api/charts/line-chart.json index 6f29efdbda6c..cea5dbd5a046 100644 --- a/docs/pages/x/api/charts/line-chart.json +++ b/docs/pages/x/api/charts/line-chart.json @@ -3,7 +3,7 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "xAxisIds[0] The id of the first provided axis" }, @@ -14,14 +14,14 @@ "leftAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "yAxisIds[0] The id of the first provided axis" }, "rightAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "null" }, @@ -30,7 +30,7 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "null" } diff --git a/docs/pages/x/api/charts/pie-chart.json b/docs/pages/x/api/charts/pie-chart.json index 5089fd20032e..486f97f02594 100644 --- a/docs/pages/x/api/charts/pie-chart.json +++ b/docs/pages/x/api/charts/pie-chart.json @@ -3,7 +3,7 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "xAxisIds[0] The id of the first provided axis" }, @@ -13,14 +13,14 @@ "leftAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "yAxisIds[0] The id of the first provided axis" }, "rightAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "null" }, @@ -28,7 +28,7 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "null" } diff --git a/docs/pages/x/api/charts/scatter-chart.json b/docs/pages/x/api/charts/scatter-chart.json index c0c77195ea16..9d956a28bf6d 100644 --- a/docs/pages/x/api/charts/scatter-chart.json +++ b/docs/pages/x/api/charts/scatter-chart.json @@ -3,7 +3,7 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "xAxisIds[0] The id of the first provided axis" }, @@ -13,14 +13,14 @@ "leftAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "yAxisIds[0] The id of the first provided axis" }, "rightAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "null" }, @@ -29,7 +29,7 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" }, "default": "null" } diff --git a/docs/pages/x/api/charts/spark-line-chart.json b/docs/pages/x/api/charts/spark-line-chart.json index 324c72d58cc7..fabe4546256d 100644 --- a/docs/pages/x/api/charts/spark-line-chart.json +++ b/docs/pages/x/api/charts/spark-line-chart.json @@ -27,7 +27,7 @@ "xAxis": { "type": { "name": "shape", - "description": "{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }" + "description": "{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }" } } }, diff --git a/docs/translations/api-docs/charts/charts-axis.json b/docs/translations/api-docs/charts/charts-axis.json index 6dba8cffb440..036b1002dee3 100644 --- a/docs/translations/api-docs/charts/charts-axis.json +++ b/docs/translations/api-docs/charts/charts-axis.json @@ -44,7 +44,10 @@ }, "tick": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks" }, "tickLabel": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks label" }, - "label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the axis label" }, + "label": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the group containing the axis label" + }, "directionX": { "description": "Styles applied to {{nodeName}}.", "nodeName": "x axes" }, "directionY": { "description": "Styles applied to {{nodeName}}.", "nodeName": "y axes" }, "top": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the top axis" }, diff --git a/docs/translations/api-docs/charts/charts-x-axis.json b/docs/translations/api-docs/charts/charts-x-axis.json index 69d91b072006..68c29dd7f5f0 100644 --- a/docs/translations/api-docs/charts/charts-x-axis.json +++ b/docs/translations/api-docs/charts/charts-x-axis.json @@ -32,6 +32,11 @@ "deprecated": "", "typeDescriptions": {} }, + "labelStyle": { + "description": "The style applied to the axis label.", + "deprecated": "", + "typeDescriptions": {} + }, "position": { "description": "Position of the axis.", "deprecated": "", @@ -57,6 +62,21 @@ "deprecated": "", "typeDescriptions": {} }, + "tickInterval": { + "description": "Defines which ticks are displayed. Its value can be: - 'auto' In such case the ticks are computed based on axis scale and other parameters. - a filtering function of the form (value, index) => boolean which is available only if the axis has a data property. - an array containing the values where ticks should be displayed.", + "deprecated": "", + "typeDescriptions": {} + }, + "tickLabelInterval": { + "description": "Defines which ticks get its label displayed. Its value can be: - 'auto' In such case, labels are displayed if they do not overlap with the previous one. - a filtering function of the form (value, index) => boolean. Warning: the index is tick index, not data ones.", + "deprecated": "", + "typeDescriptions": {} + }, + "tickLabelStyle": { + "description": "The style applied to ticks text.", + "deprecated": "", + "typeDescriptions": {} + }, "tickMaxStep": { "description": "Maximal step between two ticks. When using time data, the value is assumed to be in ms. Not supported by categorical axis (band, points).", "deprecated": "", @@ -90,7 +110,10 @@ }, "tick": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks" }, "tickLabel": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks label" }, - "label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the axis label" }, + "label": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the group containing the axis label" + }, "directionX": { "description": "Styles applied to {{nodeName}}.", "nodeName": "x axes" }, "directionY": { "description": "Styles applied to {{nodeName}}.", "nodeName": "y axes" }, "top": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the top axis" }, diff --git a/docs/translations/api-docs/charts/charts-y-axis.json b/docs/translations/api-docs/charts/charts-y-axis.json index 69d91b072006..68c29dd7f5f0 100644 --- a/docs/translations/api-docs/charts/charts-y-axis.json +++ b/docs/translations/api-docs/charts/charts-y-axis.json @@ -32,6 +32,11 @@ "deprecated": "", "typeDescriptions": {} }, + "labelStyle": { + "description": "The style applied to the axis label.", + "deprecated": "", + "typeDescriptions": {} + }, "position": { "description": "Position of the axis.", "deprecated": "", @@ -57,6 +62,21 @@ "deprecated": "", "typeDescriptions": {} }, + "tickInterval": { + "description": "Defines which ticks are displayed. Its value can be: - 'auto' In such case the ticks are computed based on axis scale and other parameters. - a filtering function of the form (value, index) => boolean which is available only if the axis has a data property. - an array containing the values where ticks should be displayed.", + "deprecated": "", + "typeDescriptions": {} + }, + "tickLabelInterval": { + "description": "Defines which ticks get its label displayed. Its value can be: - 'auto' In such case, labels are displayed if they do not overlap with the previous one. - a filtering function of the form (value, index) => boolean. Warning: the index is tick index, not data ones.", + "deprecated": "", + "typeDescriptions": {} + }, + "tickLabelStyle": { + "description": "The style applied to ticks text.", + "deprecated": "", + "typeDescriptions": {} + }, "tickMaxStep": { "description": "Maximal step between two ticks. When using time data, the value is assumed to be in ms. Not supported by categorical axis (band, points).", "deprecated": "", @@ -90,7 +110,10 @@ }, "tick": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks" }, "tickLabel": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks label" }, - "label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the axis label" }, + "label": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the group containing the axis label" + }, "directionX": { "description": "Styles applied to {{nodeName}}.", "nodeName": "x axes" }, "directionY": { "description": "Styles applied to {{nodeName}}.", "nodeName": "y axes" }, "top": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the top axis" }, diff --git a/packages/x-charts/src/BarChart/BarChart.tsx b/packages/x-charts/src/BarChart/BarChart.tsx index dd4b3631042f..eefa36e2167e 100644 --- a/packages/x-charts/src/BarChart/BarChart.tsx +++ b/packages/x-charts/src/BarChart/BarChart.tsx @@ -185,11 +185,19 @@ BarChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -222,11 +230,19 @@ BarChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -268,11 +284,19 @@ BarChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -351,11 +375,19 @@ BarChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -383,6 +415,7 @@ BarChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -391,6 +424,13 @@ BarChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -411,6 +451,7 @@ BarChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -419,6 +460,13 @@ BarChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx b/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx index 805c512a5648..2b24b1559837 100644 --- a/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx +++ b/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx @@ -132,11 +132,19 @@ ChartsAxis.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -158,11 +166,19 @@ ChartsAxis.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -184,11 +200,19 @@ ChartsAxis.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -220,11 +244,19 @@ ChartsAxis.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/ChartsAxis/axisClasses.ts b/packages/x-charts/src/ChartsAxis/axisClasses.ts index 81f4e42632a8..7386615ffa5a 100644 --- a/packages/x-charts/src/ChartsAxis/axisClasses.ts +++ b/packages/x-charts/src/ChartsAxis/axisClasses.ts @@ -14,7 +14,7 @@ export interface ChartsAxisClasses { tick: string; /** Styles applied to ticks label. */ tickLabel: string; - /** Styles applied to the axis label. */ + /** Styles applied to the group containing the axis label. */ label: string; /** Styles applied to x axes. */ directionX: string; diff --git a/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx b/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx index 3a36e0288efa..1f773fbb903f 100644 --- a/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx +++ b/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx @@ -9,7 +9,7 @@ import { FormattedSeries, SeriesContext } from '../context/SeriesContextProvider import { ChartsLegendClasses, getChartsLegendUtilityClass } from './chartsLegendClasses'; import { DefaultizedProps } from '../models/helpers'; import { LegendParams } from '../models/seriesType/config'; -import { ChartsText, getWordsByLines } from '../internals/components/ChartsText'; +import { ChartsText, ChartsTextStyle, getWordsByLines } from '../internals/components/ChartsText'; import { CardinalDirections } from '../models/layout'; export interface ChartsLegendSlotsComponent { @@ -91,7 +91,7 @@ export interface LegendRendererProps * Style applied to legend labels. * @default theme.typography.subtitle1 */ - labelStyle?: React.CSSProperties; + labelStyle?: ChartsTextStyle; /** * Width of the item mark (in px). * @default 20 @@ -159,20 +159,24 @@ function DefaultChartsLegend(props: LegendRendererProps) { const theme = useTheme(); const labelStyle = React.useMemo( - () => ({ - ...theme.typography.subtitle1, - color: 'inherit', - fill: (theme.vars || theme).palette.text.primary, - lineHeight: 1, - ...inLabelStyle, - }), + () => + ({ + ...theme.typography.subtitle1, + color: 'inherit', + dominantBaseline: 'central', + textAnchor: 'start', + fill: (theme.vars || theme).palette.text.primary, + lineHeight: 1, + ...inLabelStyle, + } as ChartsTextStyle), // To say to TS that the dominantBaseline and textAnchor are correct [inLabelStyle, theme], ); const padding = React.useMemo(() => getStandardizedPadding(paddingProps), [paddingProps]); const getItemSpace = React.useCallback( - (label: string, style?: React.CSSProperties) => { + (label: string, inStyle: ChartsTextStyle = {}) => { + const { rotate, dominantBaseline, ...style } = inStyle; const linesSize = getWordsByLines({ style, needsComputation: true, text: label }); const innerSize = { innerWidth: itemMarkWidth + markGap + Math.max(...linesSize.map((size) => size.width)), @@ -332,14 +336,7 @@ function DefaultChartsLegend(props: LegendRendererProps) { height={itemMarkHeight} fill={color} /> - + ))} diff --git a/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx b/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx index 7b7b3eeab499..548e7f6a8030 100644 --- a/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx +++ b/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx @@ -5,11 +5,13 @@ import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { useThemeProps, useTheme, Theme } from '@mui/material/styles'; import { CartesianContext } from '../context/CartesianContextProvider'; import { DrawingContext } from '../context/DrawingProvider'; -import useTicks from '../hooks/useTicks'; +import useTicks, { TickItemType } from '../hooks/useTicks'; import { ChartsXAxisProps } from '../models/axis'; import { getAxisUtilityClass } from '../ChartsAxis/axisClasses'; import { AxisRoot } from '../internals/components/AxisSharedComponents'; -import { ChartsText, ChartsTextProps } from '../internals/components/ChartsText'; +import { ChartsText, ChartsTextProps, getWordsByLines } from '../internals/components/ChartsText'; +import { getMinXTranslation } from '../internals/geometry'; +import { useMounted } from '../hooks/useMounted'; const useUtilityClasses = (ownerState: ChartsXAxisProps & { theme: Theme }) => { const { classes, position } = ownerState; @@ -25,12 +27,61 @@ const useUtilityClasses = (ownerState: ChartsXAxisProps & { theme: Theme }) => { return composeClasses(slots, getAxisUtilityClass, classes); }; +type LabelExtraData = { width: number; height: number; skipLabel?: boolean }; + +function addLabelDimension( + xTicks: TickItemType[], + { + tickLabelStyle: style, + tickLabelInterval, + isMounted, + }: Pick & { isMounted: boolean }, +): (TickItemType & LabelExtraData)[] { + const withDimension = xTicks.map((tick) => { + if (!isMounted || tick.formattedValue === undefined) { + return { ...tick, width: 0, height: 0 }; + } + const tickSizes = getWordsByLines({ style, needsComputation: true, text: tick.formattedValue }); + return { + ...tick, + width: Math.max(...tickSizes.map((size) => size.width)), + height: Math.max(tickSizes.length * tickSizes[0].height), + }; + }); + + if (typeof tickLabelInterval === 'function') { + return withDimension.map((item, index) => ({ + ...item, + skipLabel: !tickLabelInterval(item.value, index), + })); + } + + // Filter label to avoid overlap + let textStart = 0; + let textEnd = 0; + + return withDimension.map((item, labelIndex) => { + const { width, offset, labelOffset, height } = item; + + const distance = getMinXTranslation(width, height, style?.angle); + const textPosition = offset + labelOffset; + const gapRatio = 1.2; // Ratio applied to the minimal distance to add some margin. + + textStart = textPosition - (gapRatio * distance) / 2; + if (labelIndex > 0 && textStart < textEnd) { + // Except for the first label, we skip all label that overlap with the last accepted. + // Notice that the early return prevents `textEnd` from being updated. + return { ...item, skipLabel: true }; + } + textEnd = textPosition + (gapRatio * distance) / 2; + return item; + }); +} + const defaultProps = { position: 'bottom', disableLine: false, disableTicks: false, - tickFontSize: 12, - labelFontSize: 14, tickSize: 6, } as const; @@ -42,18 +93,24 @@ function ChartsXAxis(inProps: ChartsXAxisProps) { }, } = React.useContext(CartesianContext); + const isMounted = useMounted(); + const defaultizedProps = { ...defaultProps, ...settings, ...props }; const { position, disableLine, disableTicks, - tickFontSize, + tickLabelStyle, label, + labelStyle, + tickFontSize, labelFontSize, tickSize: tickSizeProp, valueFormatter, slots, slotProps, + tickInterval, + tickLabelInterval, } = defaultizedProps; const theme = useTheme(); @@ -63,13 +120,7 @@ function ChartsXAxis(inProps: ChartsXAxisProps) { const tickSize = disableTicks ? 4 : tickSizeProp; - const xTicks = useTicks({ scale: xScale, tickNumber, valueFormatter }); - const positionSigne = position === 'bottom' ? 1 : -1; - - const labelRefPoint = { - x: left + width / 2, - y: positionSigne * (tickFontSize + tickSize + 10), - }; + const positionSign = position === 'bottom' ? 1 : -1; const Line = slots?.axisLine ?? 'line'; const Tick = slots?.axisTick ?? 'line'; @@ -80,26 +131,41 @@ function ChartsXAxis(inProps: ChartsXAxisProps) { elementType: TickLabel, externalSlotProps: slotProps?.axisTickLabel, additionalProps: { - textAnchor: 'middle', - dominantBaseline: position === 'bottom' ? 'hanging' : 'auto', - style: { fontSize: tickFontSize }, + style: { + textAnchor: 'middle', + dominantBaseline: position === 'bottom' ? 'hanging' : 'auto', + fontSize: tickFontSize ?? 12, + ...tickLabelStyle, + }, className: classes.tickLabel, } as Partial, className: classes.tickLabel, ownerState: {}, }); + const xTicks = useTicks({ scale: xScale, tickNumber, valueFormatter, tickInterval }); + + const xTicksWithDimension = addLabelDimension(xTicks, { + tickLabelStyle: axisTickLabelProps.style, + tickLabelInterval, + isMounted, + }); + + const labelRefPoint = { + x: left + width / 2, + y: positionSign * (tickSize + 22), + }; + const axisLabelProps = useSlotProps({ elementType: Label, externalSlotProps: slotProps?.axisLabel, additionalProps: { - textAnchor: 'middle', - dominantBaseline: position === 'bottom' ? 'hanging' : 'auto', style: { - fontSize: labelFontSize, - transformOrigin: `${labelRefPoint.x}px ${labelRefPoint.y}px`, + fontSize: labelFontSize ?? 14, + textAnchor: 'middle', + dominantBaseline: position === 'bottom' ? 'hanging' : 'auto', + ...labelStyle, }, - className: classes.label, } as Partial, ownerState: {}, }); @@ -118,24 +184,23 @@ function ChartsXAxis(inProps: ChartsXAxisProps) { /> )} - {xTicks.map(({ formattedValue, offset, labelOffset }, index) => { + {xTicksWithDimension.map(({ formattedValue, offset, labelOffset, skipLabel }, index) => { const xTickLabel = labelOffset ?? 0; - const yTickLabel = positionSigne * (tickSize + 3); + const yTickLabel = positionSign * (tickSize + 3); return ( {!disableTicks && ( )} - {formattedValue !== undefined && ( + {formattedValue !== undefined && !skipLabel && ( @@ -188,8 +253,13 @@ ChartsXAxis.propTypes = { /** * The font size of the axis label. * @default 14 + * @deprecated Consider using `labelStyle.fontSize` instead. */ labelFontSize: PropTypes.number, + /** + * The style applied to the axis label. + */ + labelStyle: PropTypes.object, /** * Position of the axis. */ @@ -212,8 +282,28 @@ ChartsXAxis.propTypes = { /** * The font size of the axis ticks text. * @default 12 + * @deprecated Consider using `tickLabelStyle.fontSize` instead. */ tickFontSize: PropTypes.number, + /** + * Defines which ticks are displayed. Its value can be: + * - 'auto' In such case the ticks are computed based on axis scale and other parameters. + * - a filtering function of the form `(value, index) => boolean` which is available only if the axis has a data property. + * - an array containing the values where ticks should be displayed. + * @default 'auto' + */ + tickInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.array, PropTypes.func]), + /** + * Defines which ticks get its label displayed. Its value can be: + * - 'auto' In such case, labels are displayed if they do not overlap with the previous one. + * - a filtering function of the form (value, index) => boolean. Warning: the index is tick index, not data ones. + * @default 'auto' + */ + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + /** + * The style applied to ticks text. + */ + tickLabelStyle: PropTypes.object, /** * Maximal step between two ticks. * When using time data, the value is assumed to be in ms. diff --git a/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx b/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx index d2d532fd197a..9677195cb4e5 100644 --- a/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx +++ b/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx @@ -65,10 +65,10 @@ function ChartsYAxis(inProps: ChartsYAxisProps) { const yTicks = useTicks({ scale: yScale, tickNumber, valueFormatter }); - const positionSigne = position === 'right' ? 1 : -1; + const positionSign = position === 'right' ? 1 : -1; const labelRefPoint = { - x: positionSigne * (tickFontSize + tickSize + 10), + x: positionSign * (tickFontSize + tickSize + 10), y: top + height / 2, }; @@ -81,9 +81,11 @@ function ChartsYAxis(inProps: ChartsYAxisProps) { elementType: TickLabel, externalSlotProps: slotProps?.axisTickLabel, additionalProps: { - textAnchor: position === 'right' ? 'start' : 'end', - dominantBaseline: 'central', - style: { fontSize: tickFontSize }, + style: { + fontSize: tickFontSize, + textAnchor: position === 'right' ? 'start' : 'end', + dominantBaseline: 'central', + }, className: classes.tickLabel, } as Partial, ownerState: {}, @@ -93,14 +95,12 @@ function ChartsYAxis(inProps: ChartsYAxisProps) { elementType: Label, externalSlotProps: slotProps?.axisLabel, additionalProps: { - textAnchor: 'middle', - dominantBaseline: 'auto', style: { fontSize: labelFontSize, - transform: `rotate(${positionSigne * 90}deg)`, - transformOrigin: `${labelRefPoint.x}px ${labelRefPoint.y}px`, - }, - className: classes.label, + angle: positionSign * 90, + textAnchor: 'middle', + dominantBaseline: 'auto', + } as Partial['style'], } as Partial, ownerState: {}, }); @@ -120,13 +120,13 @@ function ChartsYAxis(inProps: ChartsYAxisProps) { )} {yTicks.map(({ formattedValue, offset, labelOffset }, index) => { - const xTickLabel = positionSigne * (tickSize + 2); + const xTickLabel = positionSign * (tickSize + 2); const yTickLabel = labelOffset; return ( {!disableTicks && ( @@ -136,7 +136,6 @@ function ChartsYAxis(inProps: ChartsYAxisProps) { @@ -189,8 +188,13 @@ ChartsYAxis.propTypes = { /** * The font size of the axis label. * @default 14 + * @deprecated Consider using `labelStyle.fontSize` instead. */ labelFontSize: PropTypes.number, + /** + * The style applied to the axis label. + */ + labelStyle: PropTypes.object, /** * Position of the axis. */ @@ -213,8 +217,28 @@ ChartsYAxis.propTypes = { /** * The font size of the axis ticks text. * @default 12 + * @deprecated Consider using `tickLabelStyle.fontSize` instead. */ tickFontSize: PropTypes.number, + /** + * Defines which ticks are displayed. Its value can be: + * - 'auto' In such case the ticks are computed based on axis scale and other parameters. + * - a filtering function of the form `(value, index) => boolean` which is available only if the axis has a data property. + * - an array containing the values where ticks should be displayed. + * @default 'auto' + */ + tickInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.array, PropTypes.func]), + /** + * Defines which ticks get its label displayed. Its value can be: + * - 'auto' In such case, labels are displayed if they do not overlap with the previous one. + * - a filtering function of the form (value, index) => boolean. Warning: the index is tick index, not data ones. + * @default 'auto' + */ + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + /** + * The style applied to ticks text. + */ + tickLabelStyle: PropTypes.object, /** * Maximal step between two ticks. * When using time data, the value is assumed to be in ms. diff --git a/packages/x-charts/src/LineChart/LineChart.tsx b/packages/x-charts/src/LineChart/LineChart.tsx index 12f153c7dbd2..e6cf01e55e9e 100644 --- a/packages/x-charts/src/LineChart/LineChart.tsx +++ b/packages/x-charts/src/LineChart/LineChart.tsx @@ -190,11 +190,19 @@ LineChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -230,11 +238,19 @@ LineChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -276,11 +292,19 @@ LineChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -366,11 +390,19 @@ LineChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -398,6 +430,7 @@ LineChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -406,6 +439,13 @@ LineChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -426,6 +466,7 @@ LineChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -434,6 +475,13 @@ LineChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/PieChart/PieChart.tsx b/packages/x-charts/src/PieChart/PieChart.tsx index a730a2828370..268c9788342b 100644 --- a/packages/x-charts/src/PieChart/PieChart.tsx +++ b/packages/x-charts/src/PieChart/PieChart.tsx @@ -156,11 +156,19 @@ PieChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -192,11 +200,19 @@ PieChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -239,11 +255,19 @@ PieChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -334,11 +358,19 @@ PieChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -366,6 +398,7 @@ PieChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -374,6 +407,13 @@ PieChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -394,6 +434,7 @@ PieChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -402,6 +443,13 @@ PieChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/ScatterChart/ScatterChart.tsx b/packages/x-charts/src/ScatterChart/ScatterChart.tsx index 2f2568b43355..75fa84546c91 100644 --- a/packages/x-charts/src/ScatterChart/ScatterChart.tsx +++ b/packages/x-charts/src/ScatterChart/ScatterChart.tsx @@ -143,11 +143,19 @@ ScatterChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -179,11 +187,19 @@ ScatterChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -225,11 +241,19 @@ ScatterChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -298,11 +322,19 @@ ScatterChart.propTypes = { fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -330,6 +362,7 @@ ScatterChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -338,6 +371,13 @@ ScatterChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -358,6 +398,7 @@ ScatterChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -366,6 +407,13 @@ ScatterChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx b/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx index 746d0ff568c6..18a71a0c0c67 100644 --- a/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx +++ b/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx @@ -313,6 +313,7 @@ SparkLineChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -321,6 +322,9 @@ SparkLineChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.array, PropTypes.func]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/context/CartesianContextProvider.tsx b/packages/x-charts/src/context/CartesianContextProvider.tsx index f35b88e2223f..a8f98e6f3f2e 100644 --- a/packages/x-charts/src/context/CartesianContextProvider.tsx +++ b/packages/x-charts/src/context/CartesianContextProvider.tsx @@ -309,6 +309,7 @@ CartesianContextProvider.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -317,6 +318,13 @@ CartesianContextProvider.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -337,6 +345,7 @@ CartesianContextProvider.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -345,6 +354,13 @@ CartesianContextProvider.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/hooks/useMounted.ts b/packages/x-charts/src/hooks/useMounted.ts new file mode 100644 index 000000000000..298fc91f814c --- /dev/null +++ b/packages/x-charts/src/hooks/useMounted.ts @@ -0,0 +1,20 @@ +import * as React from 'react'; +import useEnhancedEffect from '@mui/utils/useEnhancedEffect'; + +export function useMounted(defer = false) { + const [mountedState, setMountedState] = React.useState(false); + + useEnhancedEffect(() => { + if (!defer) { + setMountedState(true); + } + }, [defer]); + + React.useEffect(() => { + if (defer) { + setMountedState(true); + } + }, [defer]); + + return mountedState; +} diff --git a/packages/x-charts/src/hooks/useTicks.ts b/packages/x-charts/src/hooks/useTicks.ts index 143901bf24d4..70f094628199 100644 --- a/packages/x-charts/src/hooks/useTicks.ts +++ b/packages/x-charts/src/hooks/useTicks.ts @@ -20,6 +20,14 @@ export interface TickParams { * Not supported by categorical axis (band, points). */ tickNumber?: number; + /** + * Defines which ticks are displayed. Its value can be: + * - 'auto' In such case the ticks are computed based on axis scale and other parameters. + * - a filtering function of the form `(value, index) => boolean` which is available only if the axis has a data property. + * - an array containing the values where ticks should be displayed. + * @default 'auto' + */ + tickInterval?: 'auto' | ((value: any, index: number) => boolean) | any[]; } export function getTickNumber( @@ -40,13 +48,23 @@ export function getTickNumber( return Math.min(maxTicks, Math.max(minTicks, defaultizedTickNumber)); } +export type TickItemType = { + /** + * This property is undefined only if it's the tick closing the last band + */ + value?: any; + formattedValue?: string; + offset: number; + labelOffset: number; +}; + function useTicks( options: { scale: D3Scale; valueFormatter?: (value: any) => string; - } & Pick, -) { - const { scale, tickNumber, valueFormatter } = options; + } & Pick, +): TickItemType[] { + const { scale, tickNumber, valueFormatter, tickInterval } = options; return React.useMemo(() => { // band scale @@ -57,7 +75,8 @@ function useTicks( // scale type = 'band' return [ ...domain.map((value) => ({ - formattedValue: valueFormatter?.(value) ?? value, + value, + formattedValue: valueFormatter?.(value) ?? `${value}`, offset: scale(value)! - (scale.step() - scale.bandwidth()) / 2, labelOffset: scale.step() / 2, })), @@ -71,19 +90,27 @@ function useTicks( } // scale type = 'point' - return domain.map((value) => ({ - formattedValue: valueFormatter?.(value) ?? value, + const filteredDomain = + (typeof tickInterval === 'function' && domain.filter(tickInterval)) || + (typeof tickInterval === 'object' && tickInterval) || + domain; + + return filteredDomain.map((value) => ({ + value, + formattedValue: valueFormatter?.(value) ?? `${value}`, offset: scale(value)!, labelOffset: 0, })); } - return scale.ticks(tickNumber).map((value: any) => ({ + const ticks = typeof tickInterval === 'object' ? tickInterval : scale.ticks(tickNumber); + return ticks.map((value: any) => ({ + value, formattedValue: valueFormatter?.(value) ?? scale.tickFormat(tickNumber)(value), offset: scale(value), labelOffset: 0, })); - }, [tickNumber, scale, valueFormatter]); + }, [tickNumber, scale, valueFormatter, tickInterval]); } export default useTicks; diff --git a/packages/x-charts/src/internals/components/ChartsText.tsx b/packages/x-charts/src/internals/components/ChartsText.tsx index 619004ddd560..8c26f69ffe6a 100644 --- a/packages/x-charts/src/internals/components/ChartsText.tsx +++ b/packages/x-charts/src/internals/components/ChartsText.tsx @@ -1,6 +1,17 @@ import * as React from 'react'; import { getStringSize } from '../domUtils'; +export type ChartsTextBaseline = 'hanging' | 'central' | 'auto'; + +export interface ChartsTextStyle extends React.CSSProperties { + angle?: number; + /** + * The text baseline + * @default 'central' + */ + dominantBaseline?: ChartsTextBaseline; +} + interface GetWordsByLinesParams { /** * Text displayed. @@ -9,7 +20,7 @@ interface GetWordsByLinesParams { /** * Style applied to text elements. */ - style?: React.SVGAttributes<'text'>['style']; + style?: ChartsTextStyle; /** * If true, the line width is computed. * @default false @@ -17,20 +28,16 @@ interface GetWordsByLinesParams { needsComputation?: boolean; } -export type ChartsTextBaseline = 'hanging' | 'central' | 'auto'; - export interface ChartsTextProps - extends Omit, 'width' | 'ref'>, + extends Omit< + React.SVGTextElementAttributes, + 'width' | 'ref' | 'style' | 'dominantBaseline' + >, GetWordsByLinesParams { /** * Height of a text line (in `em`). */ lineHeight?: number; - /** - * The text baseline - * @default 'central' - */ - dominantBaseline?: ChartsTextBaseline; ownerState?: any; } @@ -42,16 +49,9 @@ export function getWordsByLines({ style, needsComputation, text }: GetWordsByLin } export function ChartsText(props: ChartsTextProps) { - const { - x, - y, - textAnchor = 'start', - dominantBaseline = 'central', - style, - text, - ownerState, - ...textProps - } = props; + const { x, y, style: styleProps, text, ownerState, ...textProps } = props; + + const { angle, textAnchor, dominantBaseline, ...style } = styleProps ?? {}; const wordsByLines = React.useMemo( () => getWordsByLines({ style, needsComputation: text.includes('\n'), text }), @@ -71,17 +71,17 @@ export function ChartsText(props: ChartsTextProps) { break; } - // const transforms = []; + const transforms = []; // if (scaleToFit) { // const lineWidth = wordsByLines[0].width; // transforms.push(`scale(${(isNumber(width as number) ? (width as number) / lineWidth : 1) / lineWidth})`); // } - // if (angle) { - // transforms.push(`rotate(${angle}, ${x}, ${y})`); - // } - // if (transforms.length) { - // textProps.transform = transforms.join(' '); - // } + if (angle) { + transforms.push(`rotate(${angle}, ${x}, ${y})`); + } + if (transforms.length) { + textProps.transform = transforms.join(' '); + } return ( !(typeof window !== 'undefined' && window.document && window.setTimeout); diff --git a/packages/x-charts/src/internals/geometry.ts b/packages/x-charts/src/internals/geometry.ts new file mode 100644 index 000000000000..eb2c6b3ff8c3 --- /dev/null +++ b/packages/x-charts/src/internals/geometry.ts @@ -0,0 +1,47 @@ +const ANGLE_APPROX = 5; // Angle (in deg) for which we approximate the rectangle as perfectly horizontal/vertical + +let warnedOnce = false; + +/** + * Return the minimal translation along the x-axis to avoid overflow of a rectangle of a given width, height, and rotation. + * This assumes that all rectangles have the same height and angle between -90 and 90. + * Otherwise it would be problematic because you need the height/width of the next rectangle to do the correct computation. + * @param width the side along the x axis. + * @param height the side along the y axis. + * @param angle the rotation in degrees. + */ +export function getMinXTranslation(width: number, height: number, angle: number = 0) { + if (process.env.NODE_ENV !== 'production') { + if (!warnedOnce && angle > 90 && angle < -90) { + warnedOnce = true; + console.warn( + [ + `MUI X: It seems you applied an angle larger than 90° or smaller than -90° to an axis text.`, + `This could cause some text overlapping.`, + `If you encounter a use case where it's needed, please open an issue.`, + ].join('\n'), + ); + } + } + const standardAngle = Math.min( + Math.abs(angle) % 180, + Math.abs((Math.abs(angle) % 180) - 180) % 180, + ); // Map from R to [0, 90] + + if (standardAngle < ANGLE_APPROX) { + // It's nearly horizontal + return width; + } + if (standardAngle > 90 - ANGLE_APPROX) { + // It's nearly vertical + return height; + } + + const radAngle = (standardAngle * Math.PI) / 180; + const angleSwich = Math.atan2(height, width); + + if (radAngle < angleSwich) { + return width / Math.cos(radAngle); + } + return height / Math.sin(radAngle); +} diff --git a/packages/x-charts/src/models/axis.ts b/packages/x-charts/src/models/axis.ts index 8bdc7b91da37..8180704031af 100644 --- a/packages/x-charts/src/models/axis.ts +++ b/packages/x-charts/src/models/axis.ts @@ -12,17 +12,17 @@ import { ChartsTextProps } from '../internals/components/ChartsText'; export type D3Scale = | ScaleBand - | ScaleLogarithmic + | ScaleLogarithmic | ScalePoint - | ScalePower - | ScaleTime - | ScaleLinear; + | ScalePower + | ScaleTime + | ScaleLinear; export type D3ContinuouseScale = - | ScaleLogarithmic - | ScalePower - | ScaleTime - | ScaleLinear; + | ScaleLogarithmic + | ScalePower + | ScaleTime + | ScaleLinear; export interface ChartsAxisSlotsComponent { axisLine?: React.JSXElementConstructor>; @@ -61,8 +61,24 @@ export interface ChartsAxisProps extends TickParams { /** * The font size of the axis ticks text. * @default 12 + * @deprecated Consider using `tickLabelStyle.fontSize` instead. */ tickFontSize?: number; + /** + * The style applied to ticks text. + */ + tickLabelStyle?: ChartsTextProps['style']; + /** + * The style applied to the axis label. + */ + labelStyle?: ChartsTextProps['style']; + /** + * Defines which ticks get its label displayed. Its value can be: + * - 'auto' In such case, labels are displayed if they do not overlap with the previous one. + * - a filtering function of the form (value, index) => boolean. Warning: the index is tick index, not data ones. + * @default 'auto' + */ + tickLabelInterval?: 'auto' | ((value: any, index: number) => boolean); /** * The label of the axis. */ @@ -70,6 +86,7 @@ export interface ChartsAxisProps extends TickParams { /** * The font size of the axis label. * @default 14 + * @deprecated Consider using `labelStyle.fontSize` instead. */ labelFontSize?: number; /** diff --git a/packages/x-charts/src/models/seriesType/line.ts b/packages/x-charts/src/models/seriesType/line.ts index 24e99d4622db..b095120c7b3f 100644 --- a/packages/x-charts/src/models/seriesType/line.ts +++ b/packages/x-charts/src/models/seriesType/line.ts @@ -16,7 +16,7 @@ export type CurveType = | 'stepBefore' | 'stepAfter'; -export interface ShowMarkParams { +export interface ShowMarkParams { /** * The item index. */ @@ -32,7 +32,7 @@ export interface ShowMarkParams { /** * The item position value. It likely comes from the axis `data` property. */ - position: number | Date; + position: AxisValue; /** * The item value. It comes from the series `data` property. */