diff --git a/docs/data/charts-component-api-pages.ts b/docs/data/charts-component-api-pages.ts
index aff842dcf2190..3a0d808b2d06b 100644
--- a/docs/data/charts-component-api-pages.ts
+++ b/docs/data/charts-component-api-pages.ts
@@ -25,6 +25,10 @@ const apiPages: MuiPage[] = [
pathname: '/x/api/charts/bar-element',
title: 'BarElement',
},
+ {
+ pathname: '/x/api/charts/bar-label',
+ title: 'BarLabel',
+ },
{
pathname: '/x/api/charts/bar-plot',
title: 'BarPlot',
diff --git a/docs/data/charts/bars/BarLabel.js b/docs/data/charts/bars/BarLabel.js
new file mode 100644
index 0000000000000..c684e33fa3e4c
--- /dev/null
+++ b/docs/data/charts/bars/BarLabel.js
@@ -0,0 +1,14 @@
+import * as React from 'react';
+import { BarChart } from '@mui/x-charts/BarChart';
+
+export default function BarLabel() {
+ return (
+
+ );
+}
diff --git a/docs/data/charts/bars/BarLabel.tsx b/docs/data/charts/bars/BarLabel.tsx
new file mode 100644
index 0000000000000..c684e33fa3e4c
--- /dev/null
+++ b/docs/data/charts/bars/BarLabel.tsx
@@ -0,0 +1,14 @@
+import * as React from 'react';
+import { BarChart } from '@mui/x-charts/BarChart';
+
+export default function BarLabel() {
+ return (
+
+ );
+}
diff --git a/docs/data/charts/bars/BarLabel.tsx.preview b/docs/data/charts/bars/BarLabel.tsx.preview
new file mode 100644
index 0000000000000..12bc56aa174ed
--- /dev/null
+++ b/docs/data/charts/bars/BarLabel.tsx.preview
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/docs/data/charts/bars/CustomLabels.js b/docs/data/charts/bars/CustomLabels.js
new file mode 100644
index 0000000000000..8e22ead904871
--- /dev/null
+++ b/docs/data/charts/bars/CustomLabels.js
@@ -0,0 +1,22 @@
+import * as React from 'react';
+import { BarChart } from '@mui/x-charts/BarChart';
+
+export default function CustomLabels() {
+ return (
+ {
+ if ((item.value ?? 0) > 10) {
+ return 'High';
+ }
+ return context.bar.height < 60 ? null : item.value?.toString();
+ }}
+ width={600}
+ height={350}
+ />
+ );
+}
diff --git a/docs/data/charts/bars/CustomLabels.tsx b/docs/data/charts/bars/CustomLabels.tsx
new file mode 100644
index 0000000000000..8e22ead904871
--- /dev/null
+++ b/docs/data/charts/bars/CustomLabels.tsx
@@ -0,0 +1,22 @@
+import * as React from 'react';
+import { BarChart } from '@mui/x-charts/BarChart';
+
+export default function CustomLabels() {
+ return (
+ {
+ if ((item.value ?? 0) > 10) {
+ return 'High';
+ }
+ return context.bar.height < 60 ? null : item.value?.toString();
+ }}
+ width={600}
+ height={350}
+ />
+ );
+}
diff --git a/docs/data/charts/bars/CustomLabels.tsx.preview b/docs/data/charts/bars/CustomLabels.tsx.preview
new file mode 100644
index 0000000000000..44fe9443bee57
--- /dev/null
+++ b/docs/data/charts/bars/CustomLabels.tsx.preview
@@ -0,0 +1,15 @@
+ {
+ if ((item.value ?? 0) > 10) {
+ return 'High';
+ }
+ return context.bar.height < 60 ? null : item.value?.toString();
+ }}
+ width={600}
+ height={350}
+/>
\ No newline at end of file
diff --git a/docs/data/charts/bars/bars.md b/docs/data/charts/bars/bars.md
index 9ac226e43ddd4..04483c3724f7a 100644
--- a/docs/data/charts/bars/bars.md
+++ b/docs/data/charts/bars/bars.md
@@ -1,7 +1,7 @@
---
title: React Bar chart
productId: x-charts
-components: BarChart, BarElement, BarPlot, ChartsGrid, ChartsOnAxisClickHandler
+components: BarChart, BarElement, BarPlot, ChartsGrid, ChartsOnAxisClickHandler, BarLabel
---
# Charts - Bars
@@ -108,6 +108,25 @@ It will work with any positive value and will be properly applied to horizontal
{{"demo": "BorderRadius.js"}}
+## Labels
+
+You can display labels on the bars.
+To do so, the `BarChart` or `BarPlot` accepts a `barLabel` property.
+It can either get a function that gets the bar item and some context.
+Or you can pass `'value'` to display the raw value of the bar.
+
+{{"demo": "BarLabel.js"}}
+
+### Custom Labels
+
+You can display, change or hide labels based on conditional logic.
+To do so, provide a function to the `barLabel`.
+Labels are not displayed if the function returns `null`.
+
+In the example we display a `'High'` text on values higher than 10, and hide values when the generated bar height is lower than 60px.
+
+{{"demo": "CustomLabels.js"}}
+
## Click event
Bar charts provides two click handlers:
diff --git a/docs/pages/x/api/charts/bar-chart.json b/docs/pages/x/api/charts/bar-chart.json
index 809e11c9a76c6..4f65db4878404 100644
--- a/docs/pages/x/api/charts/bar-chart.json
+++ b/docs/pages/x/api/charts/bar-chart.json
@@ -14,6 +14,7 @@
"text": "highlight docs"
}
},
+ "barLabel": { "type": { "name": "union", "description": "'value'
| func" } },
"borderRadius": { "type": { "name": "number" } },
"bottomAxis": {
"type": { "name": "union", "description": "object
| string" },
@@ -131,6 +132,12 @@
"default": "BarElementPath",
"class": null
},
+ {
+ "name": "barLabel",
+ "description": "The component that renders the bar label.",
+ "default": "BarLabel",
+ "class": null
+ },
{
"name": "legend",
"description": "Custom rendering of the legend.",
diff --git a/docs/pages/x/api/charts/bar-label.js b/docs/pages/x/api/charts/bar-label.js
new file mode 100644
index 0000000000000..48aeaf5b8d312
--- /dev/null
+++ b/docs/pages/x/api/charts/bar-label.js
@@ -0,0 +1,23 @@
+import * as React from 'react';
+import ApiPage from 'docs/src/modules/components/ApiPage';
+import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations';
+import jsonPageContent from './bar-label.json';
+
+export default function Page(props) {
+ const { descriptions, pageContent } = props;
+ return ;
+}
+
+Page.getInitialProps = () => {
+ const req = require.context(
+ 'docsx/translations/api-docs/charts/bar-label',
+ false,
+ /\.\/bar-label.*.json$/,
+ );
+ const descriptions = mapApiPageTranslations(req);
+
+ return {
+ descriptions,
+ pageContent: jsonPageContent,
+ };
+};
diff --git a/docs/pages/x/api/charts/bar-label.json b/docs/pages/x/api/charts/bar-label.json
new file mode 100644
index 0000000000000..b5a68df0e457e
--- /dev/null
+++ b/docs/pages/x/api/charts/bar-label.json
@@ -0,0 +1,41 @@
+{
+ "props": {},
+ "name": "BarLabel",
+ "imports": [
+ "import { BarLabel } from '@mui/x-charts/BarChart';",
+ "import { BarLabel } from '@mui/x-charts';"
+ ],
+ "slots": [
+ {
+ "name": "barLabel",
+ "description": "The component that renders the bar label.",
+ "default": "BarLabel",
+ "class": null
+ }
+ ],
+ "classes": [
+ {
+ "key": "faded",
+ "className": "MuiBarLabel-faded",
+ "description": "Styles applied to the root element if it is faded.",
+ "isGlobal": false
+ },
+ {
+ "key": "highlighted",
+ "className": "MuiBarLabel-highlighted",
+ "description": "Styles applied to the root element if it is highlighted.",
+ "isGlobal": false
+ },
+ {
+ "key": "root",
+ "className": "MuiBarLabel-root",
+ "description": "Styles applied to the root element.",
+ "isGlobal": false
+ }
+ ],
+ "muiName": "MuiBarLabel",
+ "filename": "/packages/x-charts/src/BarChart/BarLabel/BarLabel.tsx",
+ "inheritance": null,
+ "demos": "",
+ "cssComponent": false
+}
diff --git a/docs/pages/x/api/charts/bar-plot.json b/docs/pages/x/api/charts/bar-plot.json
index 51afe2f2a2d19..cb04173a43115 100644
--- a/docs/pages/x/api/charts/bar-plot.json
+++ b/docs/pages/x/api/charts/bar-plot.json
@@ -1,5 +1,6 @@
{
"props": {
+ "barLabel": { "type": { "name": "union", "description": "'value'
| func" } },
"borderRadius": { "type": { "name": "number" } },
"onItemClick": {
"type": { "name": "func" },
@@ -27,6 +28,12 @@
"description": "The component that renders the bar.",
"default": "BarElementPath",
"class": null
+ },
+ {
+ "name": "barLabel",
+ "description": "The component that renders the bar label.",
+ "default": "BarLabel",
+ "class": null
}
],
"classes": [],
diff --git a/docs/translations/api-docs/charts/bar-chart/bar-chart.json b/docs/translations/api-docs/charts/bar-chart/bar-chart.json
index 88dc2317541f3..6adc0b412648b 100644
--- a/docs/translations/api-docs/charts/bar-chart/bar-chart.json
+++ b/docs/translations/api-docs/charts/bar-chart/bar-chart.json
@@ -5,6 +5,9 @@
"description": "The configuration of axes highlight. Default is set to 'band' in the bar direction. Depends on layout
prop.",
"seeMoreText": "See {{link}} for more details."
},
+ "barLabel": {
+ "description": "If provided, the function will be used to format the label of the bar. It can be set to 'value' to display the current value."
+ },
"borderRadius": { "description": "Defines the border radius of the bar element." },
"bottomAxis": {
"description": "Indicate which axis to display the bottom of the charts. Can be a string (the id of the axis) or an object ChartsXAxisProps
."
@@ -76,6 +79,7 @@
"axisTick": "Custom component for the axis tick.",
"axisTickLabel": "Custom component for tick label.",
"bar": "The component that renders the bar.",
+ "barLabel": "The component that renders the bar label.",
"itemContent": "Custom component for displaying tooltip content when triggered by item event.",
"legend": "Custom rendering of the legend.",
"loadingOverlay": "Overlay component rendered when the chart is in a loading state.",
diff --git a/docs/translations/api-docs/charts/bar-label/bar-label.json b/docs/translations/api-docs/charts/bar-label/bar-label.json
new file mode 100644
index 0000000000000..9d908b3e5797d
--- /dev/null
+++ b/docs/translations/api-docs/charts/bar-label/bar-label.json
@@ -0,0 +1,18 @@
+{
+ "componentDescription": "",
+ "propDescriptions": {},
+ "classDescriptions": {
+ "faded": {
+ "description": "Styles applied to {{nodeName}} if {{conditions}}.",
+ "nodeName": "the root element",
+ "conditions": "it is faded"
+ },
+ "highlighted": {
+ "description": "Styles applied to {{nodeName}} if {{conditions}}.",
+ "nodeName": "the root element",
+ "conditions": "it is highlighted"
+ },
+ "root": { "description": "Styles applied to the root element." }
+ },
+ "slotDescriptions": { "barLabel": "The component that renders the bar label." }
+}
diff --git a/docs/translations/api-docs/charts/bar-plot/bar-plot.json b/docs/translations/api-docs/charts/bar-plot/bar-plot.json
index b56710ba45b29..a4eed37a0972e 100644
--- a/docs/translations/api-docs/charts/bar-plot/bar-plot.json
+++ b/docs/translations/api-docs/charts/bar-plot/bar-plot.json
@@ -1,6 +1,9 @@
{
"componentDescription": "",
"propDescriptions": {
+ "barLabel": {
+ "description": "If provided, the function will be used to format the label of the bar. It can be set to 'value' to display the current value."
+ },
"borderRadius": { "description": "Defines the border radius of the bar element." },
"onItemClick": {
"description": "Callback fired when a bar item is clicked.",
@@ -14,5 +17,8 @@
"slots": { "description": "Overridable component slots." }
},
"classDescriptions": {},
- "slotDescriptions": { "bar": "The component that renders the bar." }
+ "slotDescriptions": {
+ "bar": "The component that renders the bar.",
+ "barLabel": "The component that renders the bar label."
+ }
}
diff --git a/packages/x-charts/src/BarChart/BarChart.tsx b/packages/x-charts/src/BarChart/BarChart.tsx
index 48e017d914e3e..5e315d8e69ae2 100644
--- a/packages/x-charts/src/BarChart/BarChart.tsx
+++ b/packages/x-charts/src/BarChart/BarChart.tsx
@@ -138,6 +138,7 @@ const BarChart = React.forwardRef(function BarChart(props: BarChartProps, ref) {
slots,
slotProps,
loading,
+ barLabel,
} = props;
const id = useId();
@@ -197,6 +198,7 @@ const BarChart = React.forwardRef(function BarChart(props: BarChartProps, ref) {
skipAnimation={skipAnimation}
onItemClick={onItemClick}
borderRadius={borderRadius}
+ barLabel={barLabel}
/>
@@ -232,6 +234,14 @@ BarChart.propTypes = {
x: PropTypes.oneOf(['band', 'line', 'none']),
y: PropTypes.oneOf(['band', 'line', 'none']),
}),
+ /**
+ * If provided, the function will be used to format the label of the bar.
+ * It can be set to 'value' to display the current value.
+ * @param {BarItem} item The item to format.
+ * @param {BarLabelContext} context data about the bar.
+ * @returns {string} The formatted label.
+ */
+ barLabel: PropTypes.oneOfType([PropTypes.oneOf(['value']), PropTypes.func]),
/**
* Defines the border radius of the bar element.
*/
diff --git a/packages/x-charts/src/BarChart/BarLabel/BarLabel.tsx b/packages/x-charts/src/BarChart/BarLabel/BarLabel.tsx
new file mode 100644
index 0000000000000..80aa8ba4dbb15
--- /dev/null
+++ b/packages/x-charts/src/BarChart/BarLabel/BarLabel.tsx
@@ -0,0 +1,52 @@
+import * as React from 'react';
+import { styled, useThemeProps } from '@mui/material/styles';
+import { animated } from '@react-spring/web';
+import PropTypes from 'prop-types';
+import { barLabelClasses } from './barLabelClasses';
+import { BarLabelOwnerState } from './BarLabel.types';
+
+export const BarLabelComponent = styled(animated.text, {
+ name: 'MuiBarLabel',
+ slot: 'Root',
+ overridesResolver: (_, styles) => [
+ { [`&.${barLabelClasses.faded}`]: styles.faded },
+ { [`&.${barLabelClasses.highlighted}`]: styles.highlighted },
+ styles.root,
+ ],
+})(({ theme }) => ({
+ ...theme?.typography?.body2,
+ stroke: 'none',
+ fill: (theme.vars || theme)?.palette?.text?.primary,
+ transition: 'opacity 0.2s ease-in, fill 0.2s ease-in',
+ textAnchor: 'middle',
+ dominantBaseline: 'central',
+ pointerEvents: 'none',
+ opacity: 1,
+ [`&.${barLabelClasses.faded}`]: {
+ opacity: 0.3,
+ },
+}));
+
+export type BarLabelProps = Omit, 'ref' | 'id'> & BarLabelOwnerState;
+
+function BarLabel(props: BarLabelProps) {
+ const themeProps = useThemeProps({ props, name: 'MuiBarLabel' });
+
+ const { seriesId, dataIndex, color, isFaded, isHighlighted, classes, ...otherProps } = themeProps;
+
+ return ;
+}
+
+BarLabel.propTypes = {
+ // ----------------------------- Warning --------------------------------
+ // | These PropTypes are generated from the TypeScript type definitions |
+ // | To update them edit the TypeScript types and run "yarn proptypes" |
+ // ----------------------------------------------------------------------
+ classes: PropTypes.object,
+ dataIndex: PropTypes.number.isRequired,
+ isFaded: PropTypes.bool.isRequired,
+ isHighlighted: PropTypes.bool.isRequired,
+ seriesId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
+} as any;
+
+export { BarLabel };
diff --git a/packages/x-charts/src/BarChart/BarLabel/BarLabel.types.ts b/packages/x-charts/src/BarChart/BarLabel/BarLabel.types.ts
new file mode 100644
index 0000000000000..bc0c73ff96317
--- /dev/null
+++ b/packages/x-charts/src/BarChart/BarLabel/BarLabel.types.ts
@@ -0,0 +1,46 @@
+import { SeriesId } from '../../models/seriesType/common';
+import type { BarLabelClasses } from './barLabelClasses';
+
+export interface BarLabelOwnerState {
+ seriesId: SeriesId;
+ dataIndex: number;
+ color: string;
+ isFaded: boolean;
+ isHighlighted: boolean;
+ classes?: Partial;
+}
+
+export type BarItem = {
+ /**
+ * The series id of the bar.
+ */
+ seriesId: SeriesId;
+ /**
+ * The index of the data point in the series.
+ */
+ dataIndex: number;
+ /**
+ * The value of the data point.
+ */
+ value: number | null;
+};
+
+export type BarLabelContext = {
+ bar: {
+ /**
+ * The height of the bar.
+ * It could be used to control the label based on the bar size.
+ */
+ height: number;
+ /**
+ * The width of the bar.
+ * It could be used to control the label based on the bar size.
+ */
+ width: number;
+ };
+};
+
+export type BarLabelFunction = (
+ item: BarItem,
+ context: BarLabelContext,
+) => string | null | undefined;
diff --git a/packages/x-charts/src/BarChart/BarLabel/BarLabelItem.tsx b/packages/x-charts/src/BarChart/BarLabel/BarLabelItem.tsx
new file mode 100644
index 0000000000000..6c8c2bcd945b0
--- /dev/null
+++ b/packages/x-charts/src/BarChart/BarLabel/BarLabelItem.tsx
@@ -0,0 +1,170 @@
+import * as React from 'react';
+import { useSlotProps } from '@mui/base/utils';
+import PropTypes from 'prop-types';
+import { InteractionContext } from '../../context/InteractionProvider';
+import { getIsFaded, getIsHighlighted } from '../../hooks/useInteractionItemProps';
+import { useUtilityClasses } from './barLabelClasses';
+import { HighlighContext } from '../../context/HighlightProvider';
+import { BarLabelOwnerState, BarItem, BarLabelContext } from './BarLabel.types';
+import { getBarLabel } from './getBarLabel';
+import { BarLabel, BarLabelProps } from './BarLabel';
+
+export interface BarLabelSlots {
+ /**
+ * The component that renders the bar label.
+ * @default BarLabel
+ */
+ barLabel?: React.JSXElementConstructor;
+}
+
+export interface BarLabelSlotProps {
+ barLabel?: Partial;
+}
+
+export type BarLabelItemProps = Omit &
+ Pick & {
+ /**
+ * The props used for each component slot.
+ * @default {}
+ */
+ slotProps?: BarLabelSlotProps;
+ /**
+ * Overridable component slots.
+ * @default {}
+ */
+ slots?: BarLabelSlots;
+ /**
+ * The height of the bar.
+ */
+ height: number;
+ /**
+ * The width of the bar.
+ */
+ width: number;
+ /**
+ * The value of the data point.
+ */
+ value: number | null;
+ /**
+ * If provided, the function will be used to format the label of the bar.
+ * It can be set to 'value' to display the current value.
+ * @param {BarItem} item The item to format.
+ * @param {BarLabelContext} context data about the bar.
+ * @returns {string} The formatted label.
+ */
+ barLabel?: 'value' | ((item: BarItem, context: BarLabelContext) => string | null | undefined);
+ };
+
+/**
+ * @ignore - internal component.
+ */
+function BarLabelItem(props: BarLabelItemProps) {
+ const {
+ seriesId,
+ classes: innerClasses,
+ color,
+ style,
+ dataIndex,
+ barLabel,
+ slots,
+ slotProps,
+ height,
+ width,
+ value,
+ ...other
+ } = props;
+ const { item } = React.useContext(InteractionContext);
+ const { scope } = React.useContext(HighlighContext);
+
+ const isHighlighted = getIsHighlighted(item, { type: 'bar', seriesId, dataIndex }, scope);
+ const isFaded = !isHighlighted && getIsFaded(item, { type: 'bar', seriesId, dataIndex }, scope);
+
+ const ownerState = {
+ seriesId,
+ classes: innerClasses,
+ color,
+ isFaded,
+ isHighlighted,
+ dataIndex,
+ };
+ const classes = useUtilityClasses(ownerState);
+
+ const Component = slots?.barLabel ?? BarLabel;
+
+ const { ownerState: barLabelOwnerState, ...barLabelProps } = useSlotProps({
+ elementType: Component,
+ externalSlotProps: slotProps?.barLabel,
+ additionalProps: {
+ ...other,
+ style,
+ className: classes.root,
+ },
+ ownerState,
+ });
+
+ if (!barLabel) {
+ return null;
+ }
+
+ const formattedLabelText = getBarLabel({
+ barLabel,
+ value,
+ dataIndex,
+ seriesId,
+ height,
+ width,
+ });
+
+ if (!formattedLabelText) {
+ return null;
+ }
+
+ return (
+
+ {formattedLabelText}
+
+ );
+}
+
+BarLabelItem.propTypes = {
+ // ----------------------------- Warning --------------------------------
+ // | These PropTypes are generated from the TypeScript type definitions |
+ // | To update them edit the TypeScript types and run "yarn proptypes" |
+ // ----------------------------------------------------------------------
+ /**
+ * If provided, the function will be used to format the label of the bar.
+ * It can be set to 'value' to display the current value.
+ * @param {BarItem} item The item to format.
+ * @param {BarLabelContext} context data about the bar.
+ * @returns {string} The formatted label.
+ */
+ barLabel: PropTypes.oneOfType([PropTypes.oneOf(['value']), PropTypes.func]),
+ classes: PropTypes.object,
+ color: PropTypes.string.isRequired,
+ dataIndex: PropTypes.number.isRequired,
+ /**
+ * The height of the bar.
+ */
+ height: PropTypes.number.isRequired,
+ seriesId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
+ /**
+ * The props used for each component slot.
+ * @default {}
+ */
+ slotProps: PropTypes.object,
+ /**
+ * Overridable component slots.
+ * @default {}
+ */
+ slots: PropTypes.object,
+ /**
+ * The value of the data point.
+ */
+ value: PropTypes.number,
+ /**
+ * The width of the bar.
+ */
+ width: PropTypes.number.isRequired,
+} as any;
+
+export { BarLabelItem };
diff --git a/packages/x-charts/src/BarChart/BarLabel/BarLabelPlot.tsx b/packages/x-charts/src/BarChart/BarLabel/BarLabelPlot.tsx
new file mode 100644
index 0000000000000..21799a6da2279
--- /dev/null
+++ b/packages/x-charts/src/BarChart/BarLabel/BarLabelPlot.tsx
@@ -0,0 +1,98 @@
+import * as React from 'react';
+import PropTypes from 'prop-types';
+import { useTransition } from '@react-spring/web';
+import type { AnimationData, CompletedBarData } from '../types';
+import { BarLabelItem, BarLabelItemProps } from './BarLabelItem';
+
+const leaveStyle = ({ layout, yOrigin, x, width, y, xOrigin, height }: AnimationData) => ({
+ ...(layout === 'vertical'
+ ? {
+ y: yOrigin,
+ x: x + width / 2,
+ height: 0,
+ width,
+ }
+ : {
+ y: y + height / 2,
+ x: xOrigin,
+ height,
+ width: 0,
+ }),
+});
+
+const enterStyle = ({ x, width, y, height }: AnimationData) => ({
+ x: x + width / 2,
+ y: y + height / 2,
+ height,
+ width,
+});
+
+type BarLabelPlotProps = {
+ bars: CompletedBarData[];
+ skipAnimation?: boolean;
+ barLabel?: BarLabelItemProps['barLabel'];
+};
+
+/**
+ * @ignore - internal component.
+ */
+function BarLabelPlot(props: BarLabelPlotProps) {
+ const { bars, skipAnimation, ...other } = props;
+
+ const barLabelTransition = useTransition(bars, {
+ keys: (bar) => `${bar.seriesId}-${bar.dataIndex}`,
+ from: leaveStyle,
+ leave: null,
+ enter: enterStyle,
+ update: enterStyle,
+ immediate: skipAnimation,
+ });
+
+ return (
+
+ {barLabelTransition((style, { seriesId, dataIndex, color, value, width, height }) => (
+
+ ))}
+
+ );
+}
+
+BarLabelPlot.propTypes = {
+ // ----------------------------- Warning --------------------------------
+ // | These PropTypes are generated from the TypeScript type definitions |
+ // | To update them edit the TypeScript types and run "yarn proptypes" |
+ // ----------------------------------------------------------------------
+ barLabel: PropTypes.oneOfType([PropTypes.oneOf(['value']), PropTypes.func]),
+ bars: PropTypes.arrayOf(
+ PropTypes.shape({
+ color: PropTypes.string.isRequired,
+ dataIndex: PropTypes.number.isRequired,
+ height: PropTypes.number.isRequired,
+ highlightScope: PropTypes.shape({
+ faded: PropTypes.oneOf(['global', 'none', 'series']),
+ highlighted: PropTypes.oneOf(['item', 'none', 'series']),
+ }),
+ layout: PropTypes.oneOf(['horizontal', 'vertical']),
+ maskId: PropTypes.string.isRequired,
+ seriesId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
+ value: PropTypes.number,
+ width: PropTypes.number.isRequired,
+ x: PropTypes.number.isRequired,
+ xOrigin: PropTypes.number.isRequired,
+ y: PropTypes.number.isRequired,
+ yOrigin: PropTypes.number.isRequired,
+ }),
+ ).isRequired,
+ skipAnimation: PropTypes.bool,
+} as any;
+
+export { BarLabelPlot };
diff --git a/packages/x-charts/src/BarChart/BarLabel/barLabelClasses.tsx b/packages/x-charts/src/BarChart/BarLabel/barLabelClasses.tsx
new file mode 100644
index 0000000000000..b892abd628835
--- /dev/null
+++ b/packages/x-charts/src/BarChart/BarLabel/barLabelClasses.tsx
@@ -0,0 +1,34 @@
+import generateUtilityClass from '@mui/utils/generateUtilityClass';
+import generateUtilityClasses from '@mui/utils/generateUtilityClasses';
+import composeClasses from '@mui/utils/composeClasses';
+import type { BarLabelOwnerState } from './BarLabel.types';
+
+export interface BarLabelClasses {
+ /** Styles applied to the root element. */
+ root: string;
+ /** Styles applied to the root element if it is highlighted. */
+ highlighted: string;
+ /** Styles applied to the root element if it is faded. */
+ faded: string;
+}
+
+export type BarLabelClassKey = keyof BarLabelClasses;
+
+export function getBarLabelUtilityClass(slot: string) {
+ return generateUtilityClass('MuiBarLabel', slot);
+}
+
+export const barLabelClasses = generateUtilityClasses('MuiBarLabel', [
+ 'root',
+ 'highlighted',
+ 'faded',
+]);
+
+export const useUtilityClasses = (ownerState: BarLabelOwnerState) => {
+ const { classes, seriesId, isFaded, isHighlighted } = ownerState;
+ const slots = {
+ root: ['root', `series-${seriesId}`, isHighlighted && 'highlighted', isFaded && 'faded'],
+ };
+
+ return composeClasses(slots, getBarLabelUtilityClass, classes);
+};
diff --git a/packages/x-charts/src/BarChart/BarLabel/getBarLabel.ts b/packages/x-charts/src/BarChart/BarLabel/getBarLabel.ts
new file mode 100644
index 0000000000000..2459c764d956e
--- /dev/null
+++ b/packages/x-charts/src/BarChart/BarLabel/getBarLabel.ts
@@ -0,0 +1,20 @@
+import { SeriesId } from '../../models/seriesType/common';
+import { BarLabelFunction } from './BarLabel.types';
+
+export const getBarLabel = (options: {
+ barLabel: 'value' | BarLabelFunction;
+ value: number | null;
+ dataIndex: number;
+ seriesId: SeriesId;
+ height: number;
+ width: number;
+}): string | null | undefined => {
+ const { barLabel, value, dataIndex, seriesId, height, width } = options;
+
+ if (barLabel === 'value') {
+ // We don't want to show the label if the value is 0
+ return value ? value?.toString() : null;
+ }
+
+ return barLabel({ seriesId, dataIndex, value }, { bar: { height, width } });
+};
diff --git a/packages/x-charts/src/BarChart/BarLabel/index.ts b/packages/x-charts/src/BarChart/BarLabel/index.ts
new file mode 100644
index 0000000000000..8778a7e4605b4
--- /dev/null
+++ b/packages/x-charts/src/BarChart/BarLabel/index.ts
@@ -0,0 +1,6 @@
+export { BarLabel } from './BarLabel';
+export type { BarLabelProps } from './BarLabel';
+export { barLabelClasses, getBarLabelUtilityClass } from './barLabelClasses';
+export type { BarLabelSlotProps, BarLabelSlots } from './BarLabelItem';
+export type { BarLabelOwnerState, BarItem, BarLabelContext } from './BarLabel.types';
+export type { BarLabelClasses, BarLabelClassKey } from './barLabelClasses';
diff --git a/packages/x-charts/src/BarChart/BarPlot.tsx b/packages/x-charts/src/BarChart/BarPlot.tsx
index 5d09ff6008abd..4451bddff5868 100644
--- a/packages/x-charts/src/BarChart/BarPlot.tsx
+++ b/packages/x-charts/src/BarChart/BarPlot.tsx
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { useTransition } from '@react-spring/web';
import { SeriesContext } from '../context/SeriesContextProvider';
import { CartesianContext } from '../context/CartesianContextProvider';
-import { BarElement, BarElementProps, BarElementSlotProps, BarElementSlots } from './BarElement';
+import { BarElement, BarElementSlotProps, BarElementSlots } from './BarElement';
import { AxisDefaultized, isBandScaleConfig, isPointScaleConfig } from '../models/axis';
import { FormatterResult } from '../models/seriesType/config';
import { BarItemIdentifier } from '../models';
@@ -12,6 +12,8 @@ import getColor from './getColor';
import { useChartId } from '../hooks';
import { AnimationData, CompletedBarData, MaskData } from './types';
import { BarClipPath } from './BarClipPath';
+import { BarLabelItemProps, BarLabelSlotProps, BarLabelSlots } from './BarLabel/BarLabelItem';
+import { BarLabelPlot } from './BarLabel/BarLabelPlot';
/**
* Solution of the equations
@@ -45,11 +47,11 @@ function getBandSize({
};
}
-export interface BarPlotSlots extends BarElementSlots {}
+export interface BarPlotSlots extends BarElementSlots, BarLabelSlots {}
-export interface BarPlotSlotProps extends BarElementSlotProps {}
+export interface BarPlotSlotProps extends BarElementSlotProps, BarLabelSlotProps {}
-export interface BarPlotProps extends Pick {
+export interface BarPlotProps extends Pick {
/**
* If `true`, animations are skipped.
* @default false
@@ -68,6 +70,16 @@ export interface BarPlotProps extends Pick ({
*/
function BarPlot(props: BarPlotProps) {
const { completedData, masksData } = useAggregatedData();
- const { skipAnimation, onItemClick, borderRadius, ...other } = props;
+ const { skipAnimation, onItemClick, borderRadius, barLabel, ...other } = props;
const transition = useTransition(completedData, {
keys: (bar) => `${bar.seriesId}-${bar.dataIndex}`,
from: leaveStyle,
@@ -329,6 +341,14 @@ function BarPlot(props: BarPlotProps) {
return {barElement};
})}
+ {barLabel && (
+
+ )}
);
}
@@ -338,6 +358,14 @@ BarPlot.propTypes = {
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "pnpm proptypes" |
// ----------------------------------------------------------------------
+ /**
+ * If provided, the function will be used to format the label of the bar.
+ * It can be set to 'value' to display the current value.
+ * @param {BarItem} item The item to format.
+ * @param {BarLabelContext} context data about the bar.
+ * @returns {string} The formatted label.
+ */
+ barLabel: PropTypes.oneOfType([PropTypes.oneOf(['value']), PropTypes.func]),
/**
* Defines the border radius of the bar element.
*/
diff --git a/packages/x-charts/src/BarChart/index.ts b/packages/x-charts/src/BarChart/index.ts
index b84ff348687d3..2c0b9ffa33ab9 100644
--- a/packages/x-charts/src/BarChart/index.ts
+++ b/packages/x-charts/src/BarChart/index.ts
@@ -1,3 +1,4 @@
export * from './BarChart';
export * from './BarPlot';
export * from './BarElement';
+export * from './BarLabel';
diff --git a/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx b/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx
index 0f232fd0f0a69..437d7c763b4da 100644
--- a/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx
+++ b/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx
@@ -29,7 +29,7 @@ export interface SparkLineChartSlots
LinePlotSlots,
MarkPlotSlots,
LineHighlightPlotSlots,
- BarPlotSlots,
+ Omit,
ChartsTooltipSlots {}
export interface SparkLineChartSlotProps
extends AreaPlotSlotProps,
diff --git a/packages/x-charts/src/themeAugmentation/components.d.ts b/packages/x-charts/src/themeAugmentation/components.d.ts
index 60862604ce60d..c9cc3dd4f2faf 100644
--- a/packages/x-charts/src/themeAugmentation/components.d.ts
+++ b/packages/x-charts/src/themeAugmentation/components.d.ts
@@ -40,6 +40,10 @@ export interface ChartsComponents {
defaultProps?: ComponentsProps['MuiBarElement'];
styleOverrides?: ComponentsOverrides['MuiBarElement'];
};
+ MuiBarLabel?: {
+ defaultProps?: ComponentsProps['MuiBarLabel'];
+ styleOverrides?: ComponentsOverrides['MuiBarLabel'];
+ };
MuiLineChart?: {
defaultProps?: ComponentsProps['MuiLineChart'];
};
diff --git a/packages/x-charts/src/themeAugmentation/index.js b/packages/x-charts/src/themeAugmentation/index.js
index 9eb356e20e39d..cf3f797ea6e6a 100644
--- a/packages/x-charts/src/themeAugmentation/index.js
+++ b/packages/x-charts/src/themeAugmentation/index.js
@@ -1 +1 @@
-// Prefer to use `import type {} from '@mui/x-date-pickers/themeAugmentation';` instead to avoid importing an empty file.
+// Prefer to use `import type {} from '@mui/x-charts/themeAugmentation';` instead to avoid importing an empty file.
diff --git a/packages/x-charts/src/themeAugmentation/overrides.d.ts b/packages/x-charts/src/themeAugmentation/overrides.d.ts
index 3a1dc5e2891ae..c2699e9f67d0a 100644
--- a/packages/x-charts/src/themeAugmentation/overrides.d.ts
+++ b/packages/x-charts/src/themeAugmentation/overrides.d.ts
@@ -1,3 +1,4 @@
+import { BarLabelClassKey } from '../BarChart';
import { BarElementClassKey } from '../BarChart/BarElement';
import { ChartsAxisClassKey } from '../ChartsAxis';
import { ChartsAxisHighlightClassKey } from '../ChartsAxisHighlight';
@@ -16,6 +17,8 @@ export interface PickersComponentNameToClassKey {
// BarChart components
MuiBarElement: BarElementClassKey;
+ MuiBarLabel: BarLabelClassKey;
+
// LineChart components
MuiAreaElement: AreaElementClassKey;
diff --git a/packages/x-charts/src/themeAugmentation/props.d.ts b/packages/x-charts/src/themeAugmentation/props.d.ts
index 207d4f9d69ca7..0189a5b19e679 100644
--- a/packages/x-charts/src/themeAugmentation/props.d.ts
+++ b/packages/x-charts/src/themeAugmentation/props.d.ts
@@ -1,3 +1,4 @@
+import { BarLabelProps } from '../BarChart/BarLabel';
import { BarChartProps } from '../BarChart/BarChart';
import { BarElementProps } from '../BarChart/BarElement';
import { ChartsAxisProps } from '../ChartsAxis';
@@ -27,6 +28,7 @@ export interface ChartsComponentsPropsList {
// BarChart components
MuiBarChart: BarChartProps;
MuiBarElement: BarElementProps;
+ MuiBarLabel: BarLabelProps;
// LineChart components
MuiLineChart: LineChartProps;
MuiAreaElement: AreaElementProps;
diff --git a/scripts/x-charts.exports.json b/scripts/x-charts.exports.json
index 8e34fbddc6b84..f0848fb3231b1 100644
--- a/scripts/x-charts.exports.json
+++ b/scripts/x-charts.exports.json
@@ -35,7 +35,17 @@
{ "name": "BarElementProps", "kind": "TypeAlias" },
{ "name": "BarElementSlotProps", "kind": "Interface" },
{ "name": "BarElementSlots", "kind": "Interface" },
+ { "name": "BarItem", "kind": "TypeAlias" },
{ "name": "BarItemIdentifier", "kind": "TypeAlias" },
+ { "name": "BarLabel", "kind": "Function" },
+ { "name": "barLabelClasses", "kind": "Variable" },
+ { "name": "BarLabelClasses", "kind": "Interface" },
+ { "name": "BarLabelClassKey", "kind": "TypeAlias" },
+ { "name": "BarLabelContext", "kind": "TypeAlias" },
+ { "name": "BarLabelOwnerState", "kind": "Interface" },
+ { "name": "BarLabelProps", "kind": "TypeAlias" },
+ { "name": "BarLabelSlotProps", "kind": "Interface" },
+ { "name": "BarLabelSlots", "kind": "Interface" },
{ "name": "BarPlot", "kind": "Function" },
{ "name": "BarPlotProps", "kind": "Interface" },
{ "name": "BarPlotSlotProps", "kind": "Interface" },
@@ -140,6 +150,7 @@
{ "name": "getAxisHighlightUtilityClass", "kind": "Function" },
{ "name": "getAxisUtilityClass", "kind": "Function" },
{ "name": "getBarElementUtilityClass", "kind": "Function" },
+ { "name": "getBarLabelUtilityClass", "kind": "Function" },
{ "name": "getChartsGridUtilityClass", "kind": "Function" },
{ "name": "getChartsTooltipUtilityClass", "kind": "Function" },
{ "name": "getGaugeUtilityClass", "kind": "Function" },