@mui/x-charts 8.17.0 → 8.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,8 +26,14 @@ export type BarLabelProps = Omit<React.SVGProps<SVGTextElement>, 'ref' | 'id' |
26
26
  * Height of the bar this label belongs to.
27
27
  */
28
28
  height: number;
29
+ /**
30
+ * The placement of the bar label.
31
+ * It controls whether the label is rendered in the center or outside the bar.
32
+ * @default 'center'
33
+ */
34
+ placement?: 'center' | 'outside';
29
35
  };
30
- declare function BarLabel(inProps: BarLabelProps): import("react/jsx-runtime").JSX.Element;
36
+ declare function BarLabel(inProps: BarLabelProps): React.JSX.Element;
31
37
  declare namespace BarLabel {
32
38
  var propTypes: any;
33
39
  }
@@ -16,7 +16,7 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
16
16
  var _useAnimateBarLabel = require("../../hooks/animation/useAnimateBarLabel");
17
17
  var _barLabelClasses = require("./barLabelClasses");
18
18
  var _jsxRuntime = require("react/jsx-runtime");
19
- const _excluded = ["seriesId", "dataIndex", "color", "isFaded", "isHighlighted", "classes", "skipAnimation", "layout", "xOrigin", "yOrigin"];
19
+ const _excluded = ["seriesId", "dataIndex", "color", "isFaded", "isHighlighted", "classes", "skipAnimation", "layout", "xOrigin", "yOrigin", "placement"];
20
20
  const BarLabelComponent = exports.BarLabelComponent = (0, _styles.styled)('text', {
21
21
  name: 'MuiBarLabel',
22
22
  slot: 'Root',
@@ -31,8 +31,6 @@ const BarLabelComponent = exports.BarLabelComponent = (0, _styles.styled)('text'
31
31
  stroke: 'none',
32
32
  fill: (theme.vars || theme)?.palette?.text?.primary,
33
33
  transition: 'opacity 0.2s ease-in, fill 0.2s ease-in',
34
- textAnchor: 'middle',
35
- dominantBaseline: 'central',
36
34
  pointerEvents: 'none',
37
35
  opacity: 1,
38
36
  [`&.${_barLabelClasses.barLabelClasses.faded}`]: {
@@ -46,7 +44,40 @@ function BarLabel(inProps) {
46
44
  });
47
45
  const otherProps = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
48
46
  const animatedProps = (0, _useAnimateBarLabel.useAnimateBarLabel)(props);
49
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(BarLabelComponent, (0, _extends2.default)({}, otherProps, animatedProps));
47
+ const textAnchor = getTextAnchor(props);
48
+ const dominantBaseline = getDominantBaseline(props);
49
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(BarLabelComponent, (0, _extends2.default)({
50
+ textAnchor: textAnchor,
51
+ dominantBaseline: dominantBaseline
52
+ }, otherProps, animatedProps));
53
+ }
54
+ function getTextAnchor({
55
+ placement,
56
+ layout,
57
+ xOrigin,
58
+ x
59
+ }) {
60
+ if (placement === 'outside') {
61
+ if (layout === 'horizontal') {
62
+ return x < xOrigin ? 'end' : 'start';
63
+ }
64
+ return 'middle';
65
+ }
66
+ return 'middle';
67
+ }
68
+ function getDominantBaseline({
69
+ placement,
70
+ layout,
71
+ yOrigin,
72
+ y
73
+ }) {
74
+ if (placement === 'outside') {
75
+ if (layout === 'horizontal') {
76
+ return 'central';
77
+ }
78
+ return y < yOrigin ? 'auto' : 'hanging';
79
+ }
80
+ return 'central';
50
81
  }
51
82
  process.env.NODE_ENV !== "production" ? BarLabel.propTypes = {
52
83
  // ----------------------------- Warning --------------------------------
@@ -62,6 +93,12 @@ process.env.NODE_ENV !== "production" ? BarLabel.propTypes = {
62
93
  isFaded: _propTypes.default.bool.isRequired,
63
94
  isHighlighted: _propTypes.default.bool.isRequired,
64
95
  layout: _propTypes.default.oneOf(['horizontal', 'vertical']).isRequired,
96
+ /**
97
+ * The placement of the bar label.
98
+ * It controls whether the label is rendered in the center or outside the bar.
99
+ * @default 'center'
100
+ */
101
+ placement: _propTypes.default.oneOf(['center', 'outside']),
65
102
  seriesId: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]).isRequired,
66
103
  skipAnimation: _propTypes.default.bool.isRequired,
67
104
  /**
@@ -67,6 +67,12 @@ export type BarLabelItemProps = Omit<BarLabelOwnerState, 'isFaded' | 'isHighligh
67
67
  * @returns {string} The formatted label.
68
68
  */
69
69
  barLabel?: 'value' | ((item: BarItem, context: BarLabelContext) => string | null | undefined);
70
+ /**
71
+ * The placement of the bar label.
72
+ * It controls whether the label is rendered in the center or outside the bar.
73
+ * @default 'center'
74
+ */
75
+ barLabelPlacement?: BarLabelProps['placement'];
70
76
  };
71
77
  /**
72
78
  * @ignore - internal component.
@@ -16,7 +16,7 @@ var _getBarLabel = require("./getBarLabel");
16
16
  var _BarLabel = require("./BarLabel");
17
17
  var _useItemHighlighted = require("../../hooks/useItemHighlighted");
18
18
  var _jsxRuntime = require("react/jsx-runtime");
19
- const _excluded = ["seriesId", "classes", "color", "dataIndex", "barLabel", "slots", "slotProps", "xOrigin", "yOrigin", "x", "y", "width", "height", "value", "skipAnimation", "layout"],
19
+ const _excluded = ["seriesId", "classes", "color", "dataIndex", "barLabel", "slots", "slotProps", "xOrigin", "yOrigin", "x", "y", "width", "height", "value", "skipAnimation", "layout", "barLabelPlacement"],
20
20
  _excluded2 = ["ownerState"];
21
21
  /**
22
22
  * @ignore - internal component.
@@ -38,7 +38,8 @@ function BarLabelItem(props) {
38
38
  height,
39
39
  value,
40
40
  skipAnimation,
41
- layout
41
+ layout,
42
+ barLabelPlacement
42
43
  } = props,
43
44
  other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
44
45
  const {
@@ -70,6 +71,7 @@ function BarLabelItem(props) {
70
71
  y,
71
72
  width,
72
73
  height,
74
+ placement: barLabelPlacement,
73
75
  className: classes.root
74
76
  }),
75
77
  ownerState
@@ -1,14 +1,12 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
- var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
5
4
  Object.defineProperty(exports, "__esModule", {
6
5
  value: true
7
6
  });
8
7
  exports.BarLabelPlot = BarLabelPlot;
9
8
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
10
9
  var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
11
- var React = _interopRequireWildcard(require("react"));
12
10
  var _BarLabelItem = require("./BarLabelItem");
13
11
  var _barClasses = require("../barClasses");
14
12
  var _jsxRuntime = require("react/jsx-runtime");
@@ -59,7 +57,8 @@ function BarLabelPlot(props) {
59
57
  skipAnimation: skipAnimation ?? false,
60
58
  layout: layout ?? 'vertical'
61
59
  }, other, {
62
- barLabel: barLabel
60
+ barLabel: barLabel,
61
+ barLabelPlacement: processedSeries.barLabelPlacement || 'center'
63
62
  }), dataIndex))
64
63
  }, seriesId);
65
64
  }
@@ -162,6 +162,11 @@ process.env.NODE_ENV !== "production" ? BarPlot.propTypes = {
162
162
  * @returns {string} The formatted label.
163
163
  */
164
164
  barLabel: _propTypes.default.oneOfType([_propTypes.default.oneOf(['value']), _propTypes.default.func]),
165
+ /**
166
+ * The placement of the bar label.
167
+ * It controls whether the label is rendered inside or outside the bar.
168
+ */
169
+ barLabelPlacement: _propTypes.default.oneOf(['outside', 'inside']),
165
170
  /**
166
171
  * Defines the border radius of the bar element.
167
172
  */
@@ -13,6 +13,7 @@ export interface ProcessedBarSeriesData {
13
13
  seriesId: SeriesId;
14
14
  data: ProcessedBarData[];
15
15
  barLabel?: BarSeriesType['barLabel'];
16
+ barLabelPlacement?: BarSeriesType['barLabelPlacement'];
16
17
  }
17
18
  export interface ProcessedBarData extends AnimationData {
18
19
  seriesId: SeriesId;
@@ -63,8 +63,8 @@ function useBarPlotData(drawingArea, xAxes, yAxes) {
63
63
  seriesId,
64
64
  dataIndex,
65
65
  layout: series[seriesId].layout,
66
- xOrigin: xScale(0) ?? 0,
67
- yOrigin: yScale(0) ?? 0
66
+ xOrigin: Math.round(xScale(0) ?? 0),
67
+ yOrigin: Math.round(yScale(0) ?? 0)
68
68
  }, barDimensions, {
69
69
  color: colorGetter(dataIndex),
70
70
  value: series[seriesId].data[dataIndex],
@@ -100,6 +100,7 @@ function useBarPlotData(drawingArea, xAxes, yAxes) {
100
100
  return {
101
101
  seriesId,
102
102
  barLabel: series[seriesId].barLabel,
103
+ barLabelPlacement: series[seriesId].barLabelPlacement,
103
104
  data: seriesDataPoints
104
105
  };
105
106
  });
package/CHANGELOG.md CHANGED
@@ -5,6 +5,106 @@
5
5
  All notable changes to this project will be documented in this file.
6
6
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
7
7
 
8
+ ## 8.18.0
9
+
10
+ <!-- generated comparing v8.17.0..master -->
11
+
12
+ _Nov 13, 2025_
13
+
14
+ We'd like to extend a big thank you to the 14 contributors who made this release possible. Here are some highlights ✨:
15
+
16
+ - Add `barLabelPlacement` property to customize the bar label position in bar charts, enabling labels to be placed above bars.
17
+
18
+ ![image](https://github.com/user-attachments/assets/4bc3a75b-74b8-4c6d-896b-5f5bf837bcda)
19
+
20
+ - Add `source` property to the date/time picker lifecycle and event handler context, enabling clearer differentiation between changes initiated by the picker UI and those from direct field input.
21
+ - 🐞 Bugfixes
22
+ - 📚 Documentation improvements
23
+
24
+ Special thanks go out to these community members for their valuable contributions:
25
+ @htollefsen, @sai6855, @Sigdriv
26
+
27
+ The following team members contributed to this release:
28
+ @arminmeh, @bernardobelchior, @brijeshb42, @cherniavskii, @flaviendelangle, @JCQuintas, @michelengelen, @noraleonte, @prakhargupta1, @rita-codes, @siriwatknp
29
+
30
+ ### Data Grid
31
+
32
+ #### `@mui/x-data-grid@8.18.0`
33
+
34
+ - [DataGrid] Allow default event in the column action cell item click event handler (#20272) @arminmeh
35
+ - [DataGrid] Remove unnecessary generic from `useGridApiRef` (#20277) @cherniavskii
36
+
37
+ #### `@mui/x-data-grid-pro@8.18.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
38
+
39
+ Same changes as in `@mui/x-data-grid@8.18.0`.
40
+
41
+ #### `@mui/x-data-grid-premium@8.18.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
42
+
43
+ Same changes as in `@mui/x-data-grid-pro@8.18.0`, plus:
44
+
45
+ - [DataGridPremium] Return the correct `cellParams` value from the aggregation cells (#20224) @arminmeh
46
+
47
+ ### Date and Time Pickers
48
+
49
+ #### `@mui/x-date-pickers@8.18.0`
50
+
51
+ - [pickers] Keep invalid date state consistent (#20040) @michelengelen
52
+ - [pickers] Adds new `source` property to `onChange` and `onAccept` context object (#20234) @michelengelen
53
+
54
+ #### `@mui/x-date-pickers-pro@8.18.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
55
+
56
+ Same changes as in `@mui/x-date-pickers@8.18.0`.
57
+
58
+ ### Charts
59
+
60
+ #### `@mui/x-charts@8.18.0`
61
+
62
+ - [charts] Add prop for positioning a bar label (#20194) @Sigdriv
63
+ - [charts] Fix applying dark mode styles in `ChartAxisZoomSliderThumb` (#20232) @sai6855
64
+
65
+ #### `@mui/x-charts-pro@8.18.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
66
+
67
+ Same changes as in `@mui/x-charts@8.18.0`, plus:
68
+
69
+ - [charts-pro] Allow specifying Content Security Policy nonce on export (#20053) @bernardobelchior
70
+ - [charts-pro] Fix applying dark mode styles to slider (#20220) @sai6855
71
+ - [charts-pro] Sankey should respect node order (#20065) @JCQuintas
72
+
73
+ #### `@mui/x-charts-premium@8.18.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
74
+
75
+ Same changes as in `@mui/x-charts-pro@8.18.0`.
76
+
77
+ ### Tree View
78
+
79
+ #### `@mui/x-tree-view@8.18.0`
80
+
81
+ - [tree view] Prepare tests for the new store structure (#20225) @flaviendelangle
82
+ - [tree view] Prepare the item plugin files for the store migration (#20240) @flaviendelangle
83
+ - [tree view] Use `TreeItemId` type instead of raw string (#20233) @flaviendelangle
84
+
85
+ #### `@mui/x-tree-view-pro@8.18.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
86
+
87
+ Same changes as in `@mui/x-tree-view@8.18.0`.
88
+
89
+ ### Codemod
90
+
91
+ #### `@mui/x-codemod@8.18.0`
92
+
93
+ Internal changes.
94
+
95
+ ### Docs
96
+
97
+ - [charts] Add a demo for a custom tick label (#20073) @prakhargupta1
98
+ - [charts] Create `useAxes()` hook documentation page (#20229) @JCQuintas
99
+ - [charts] Fix logo alignment (#20228) @JCQuintas
100
+ - [charts] Fixes typo in import example (#20236) @htollefsen
101
+ - [Data Grid] Add recipe for cursor pagination with data source (#19700) @siriwatknp
102
+ - [Data Grid] Add a demo for pinned rows aggregation (#20198) @cherniavskii
103
+
104
+ ### Core
105
+
106
+ - [docs-infra] Use deployment config from docs-infra package (#20243) @brijeshb42
107
+
8
108
  ## 8.17.0
9
109
 
10
110
  _Nov 5, 2025_
@@ -26,8 +26,14 @@ export type BarLabelProps = Omit<React.SVGProps<SVGTextElement>, 'ref' | 'id' |
26
26
  * Height of the bar this label belongs to.
27
27
  */
28
28
  height: number;
29
+ /**
30
+ * The placement of the bar label.
31
+ * It controls whether the label is rendered in the center or outside the bar.
32
+ * @default 'center'
33
+ */
34
+ placement?: 'center' | 'outside';
29
35
  };
30
- declare function BarLabel(inProps: BarLabelProps): import("react/jsx-runtime").JSX.Element;
36
+ declare function BarLabel(inProps: BarLabelProps): React.JSX.Element;
31
37
  declare namespace BarLabel {
32
38
  var propTypes: any;
33
39
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
4
4
  import _extends from "@babel/runtime/helpers/esm/extends";
5
- const _excluded = ["seriesId", "dataIndex", "color", "isFaded", "isHighlighted", "classes", "skipAnimation", "layout", "xOrigin", "yOrigin"];
5
+ const _excluded = ["seriesId", "dataIndex", "color", "isFaded", "isHighlighted", "classes", "skipAnimation", "layout", "xOrigin", "yOrigin", "placement"];
6
6
  import * as React from 'react';
7
7
  import { styled, useThemeProps } from '@mui/material/styles';
8
8
  import PropTypes from 'prop-types';
@@ -23,8 +23,6 @@ export const BarLabelComponent = styled('text', {
23
23
  stroke: 'none',
24
24
  fill: (theme.vars || theme)?.palette?.text?.primary,
25
25
  transition: 'opacity 0.2s ease-in, fill 0.2s ease-in',
26
- textAnchor: 'middle',
27
- dominantBaseline: 'central',
28
26
  pointerEvents: 'none',
29
27
  opacity: 1,
30
28
  [`&.${barLabelClasses.faded}`]: {
@@ -38,7 +36,40 @@ function BarLabel(inProps) {
38
36
  });
39
37
  const otherProps = _objectWithoutPropertiesLoose(props, _excluded);
40
38
  const animatedProps = useAnimateBarLabel(props);
41
- return /*#__PURE__*/_jsx(BarLabelComponent, _extends({}, otherProps, animatedProps));
39
+ const textAnchor = getTextAnchor(props);
40
+ const dominantBaseline = getDominantBaseline(props);
41
+ return /*#__PURE__*/_jsx(BarLabelComponent, _extends({
42
+ textAnchor: textAnchor,
43
+ dominantBaseline: dominantBaseline
44
+ }, otherProps, animatedProps));
45
+ }
46
+ function getTextAnchor({
47
+ placement,
48
+ layout,
49
+ xOrigin,
50
+ x
51
+ }) {
52
+ if (placement === 'outside') {
53
+ if (layout === 'horizontal') {
54
+ return x < xOrigin ? 'end' : 'start';
55
+ }
56
+ return 'middle';
57
+ }
58
+ return 'middle';
59
+ }
60
+ function getDominantBaseline({
61
+ placement,
62
+ layout,
63
+ yOrigin,
64
+ y
65
+ }) {
66
+ if (placement === 'outside') {
67
+ if (layout === 'horizontal') {
68
+ return 'central';
69
+ }
70
+ return y < yOrigin ? 'auto' : 'hanging';
71
+ }
72
+ return 'central';
42
73
  }
43
74
  process.env.NODE_ENV !== "production" ? BarLabel.propTypes = {
44
75
  // ----------------------------- Warning --------------------------------
@@ -54,6 +85,12 @@ process.env.NODE_ENV !== "production" ? BarLabel.propTypes = {
54
85
  isFaded: PropTypes.bool.isRequired,
55
86
  isHighlighted: PropTypes.bool.isRequired,
56
87
  layout: PropTypes.oneOf(['horizontal', 'vertical']).isRequired,
88
+ /**
89
+ * The placement of the bar label.
90
+ * It controls whether the label is rendered in the center or outside the bar.
91
+ * @default 'center'
92
+ */
93
+ placement: PropTypes.oneOf(['center', 'outside']),
57
94
  seriesId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
58
95
  skipAnimation: PropTypes.bool.isRequired,
59
96
  /**
@@ -67,6 +67,12 @@ export type BarLabelItemProps = Omit<BarLabelOwnerState, 'isFaded' | 'isHighligh
67
67
  * @returns {string} The formatted label.
68
68
  */
69
69
  barLabel?: 'value' | ((item: BarItem, context: BarLabelContext) => string | null | undefined);
70
+ /**
71
+ * The placement of the bar label.
72
+ * It controls whether the label is rendered in the center or outside the bar.
73
+ * @default 'center'
74
+ */
75
+ barLabelPlacement?: BarLabelProps['placement'];
70
76
  };
71
77
  /**
72
78
  * @ignore - internal component.
@@ -1,6 +1,6 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3
- const _excluded = ["seriesId", "classes", "color", "dataIndex", "barLabel", "slots", "slotProps", "xOrigin", "yOrigin", "x", "y", "width", "height", "value", "skipAnimation", "layout"],
3
+ const _excluded = ["seriesId", "classes", "color", "dataIndex", "barLabel", "slots", "slotProps", "xOrigin", "yOrigin", "x", "y", "width", "height", "value", "skipAnimation", "layout", "barLabelPlacement"],
4
4
  _excluded2 = ["ownerState"];
5
5
  import * as React from 'react';
6
6
  import useSlotProps from '@mui/utils/useSlotProps';
@@ -30,7 +30,8 @@ function BarLabelItem(props) {
30
30
  height,
31
31
  value,
32
32
  skipAnimation,
33
- layout
33
+ layout,
34
+ barLabelPlacement
34
35
  } = props,
35
36
  other = _objectWithoutPropertiesLoose(props, _excluded);
36
37
  const {
@@ -62,6 +63,7 @@ function BarLabelItem(props) {
62
63
  y,
63
64
  width,
64
65
  height,
66
+ placement: barLabelPlacement,
65
67
  className: classes.root
66
68
  }),
67
69
  ownerState
@@ -1,7 +1,6 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3
3
  const _excluded = ["processedSeries", "skipAnimation"];
4
- import * as React from 'react';
5
4
  import { BarLabelItem } from "./BarLabelItem.js";
6
5
  import { useUtilityClasses } from "../barClasses.js";
7
6
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -51,7 +50,8 @@ function BarLabelPlot(props) {
51
50
  skipAnimation: skipAnimation ?? false,
52
51
  layout: layout ?? 'vertical'
53
52
  }, other, {
54
- barLabel: barLabel
53
+ barLabel: barLabel,
54
+ barLabelPlacement: processedSeries.barLabelPlacement || 'center'
55
55
  }), dataIndex))
56
56
  }, seriesId);
57
57
  }
@@ -155,6 +155,11 @@ process.env.NODE_ENV !== "production" ? BarPlot.propTypes = {
155
155
  * @returns {string} The formatted label.
156
156
  */
157
157
  barLabel: PropTypes.oneOfType([PropTypes.oneOf(['value']), PropTypes.func]),
158
+ /**
159
+ * The placement of the bar label.
160
+ * It controls whether the label is rendered inside or outside the bar.
161
+ */
162
+ barLabelPlacement: PropTypes.oneOf(['outside', 'inside']),
158
163
  /**
159
164
  * Defines the border radius of the bar element.
160
165
  */
@@ -13,6 +13,7 @@ export interface ProcessedBarSeriesData {
13
13
  seriesId: SeriesId;
14
14
  data: ProcessedBarData[];
15
15
  barLabel?: BarSeriesType['barLabel'];
16
+ barLabelPlacement?: BarSeriesType['barLabelPlacement'];
16
17
  }
17
18
  export interface ProcessedBarData extends AnimationData {
18
19
  seriesId: SeriesId;
@@ -55,8 +55,8 @@ export function useBarPlotData(drawingArea, xAxes, yAxes) {
55
55
  seriesId,
56
56
  dataIndex,
57
57
  layout: series[seriesId].layout,
58
- xOrigin: xScale(0) ?? 0,
59
- yOrigin: yScale(0) ?? 0
58
+ xOrigin: Math.round(xScale(0) ?? 0),
59
+ yOrigin: Math.round(yScale(0) ?? 0)
60
60
  }, barDimensions, {
61
61
  color: colorGetter(dataIndex),
62
62
  value: series[seriesId].data[dataIndex],
@@ -92,6 +92,7 @@ export function useBarPlotData(drawingArea, xAxes, yAxes) {
92
92
  return {
93
93
  seriesId,
94
94
  barLabel: series[seriesId].barLabel,
95
+ barLabelPlacement: series[seriesId].barLabelPlacement,
95
96
  data: seriesDataPoints
96
97
  };
97
98
  });
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import type { BarLabelProps } from "../../BarChart/index.js";
3
- type UseAnimateBarLabelParams = Pick<BarLabelProps, 'xOrigin' | 'yOrigin' | 'x' | 'y' | 'width' | 'height' | 'layout' | 'skipAnimation'> & {
3
+ type UseAnimateBarLabelParams = Pick<BarLabelProps, 'xOrigin' | 'yOrigin' | 'x' | 'y' | 'width' | 'height' | 'layout' | 'skipAnimation' | 'placement'> & {
4
4
  ref?: React.Ref<SVGTextElement>;
5
5
  };
6
6
  type UseAnimateBarLabelReturn = {
@@ -5,14 +5,12 @@ function barLabelPropsInterpolator(from, to) {
5
5
  const interpolateY = interpolateNumber(from.y, to.y);
6
6
  const interpolateWidth = interpolateNumber(from.width, to.width);
7
7
  const interpolateHeight = interpolateNumber(from.height, to.height);
8
- return t => {
9
- return {
10
- x: interpolateX(t),
11
- y: interpolateY(t),
12
- width: interpolateWidth(t),
13
- height: interpolateHeight(t)
14
- };
15
- };
8
+ return t => ({
9
+ x: interpolateX(t),
10
+ y: interpolateY(t),
11
+ width: interpolateWidth(t),
12
+ height: interpolateHeight(t)
13
+ });
16
14
  }
17
15
 
18
16
  /**
@@ -25,15 +23,21 @@ function barLabelPropsInterpolator(from, to) {
25
23
  * pass the ref returned by this hook to the `path` element and the `ref` provided as argument will also be called.
26
24
  */
27
25
  export function useAnimateBarLabel(props) {
26
+ const {
27
+ initialX,
28
+ currentX,
29
+ initialY,
30
+ currentY
31
+ } = props.placement === 'outside' ? getOutsidePlacement(props) : getCenterPlacement(props);
28
32
  const initialProps = {
29
- x: props.layout === 'vertical' ? props.x + props.width / 2 : props.xOrigin,
30
- y: props.layout === 'vertical' ? props.yOrigin : props.y + props.height / 2,
33
+ x: initialX,
34
+ y: initialY,
31
35
  width: props.width,
32
36
  height: props.height
33
37
  };
34
38
  const currentProps = {
35
- x: props.x + props.width / 2,
36
- y: props.y + props.height / 2,
39
+ x: currentX,
40
+ y: currentY,
37
41
  width: props.width,
38
42
  height: props.height
39
43
  };
@@ -50,4 +54,49 @@ export function useAnimateBarLabel(props) {
50
54
  skip: props.skipAnimation,
51
55
  ref: props.ref
52
56
  });
57
+ }
58
+ const LABEL_OFFSET = 4;
59
+ function getCenterPlacement(props) {
60
+ return {
61
+ initialX: props.layout === 'vertical' ? props.x + props.width / 2 : props.xOrigin,
62
+ initialY: props.layout === 'vertical' ? props.yOrigin : props.y + props.height / 2,
63
+ currentX: props.x + props.width / 2,
64
+ currentY: props.y + props.height / 2
65
+ };
66
+ }
67
+ function getOutsidePlacement(props) {
68
+ let initialY = 0;
69
+ let currentY = 0;
70
+ let initialX = 0;
71
+ let currentX = 0;
72
+ if (props.layout === 'vertical') {
73
+ const shouldPlaceAbove = props.y < props.yOrigin;
74
+ if (shouldPlaceAbove) {
75
+ initialY = props.yOrigin - LABEL_OFFSET;
76
+ currentY = props.y - LABEL_OFFSET;
77
+ } else {
78
+ initialY = props.yOrigin + LABEL_OFFSET;
79
+ currentY = props.y + props.height + LABEL_OFFSET;
80
+ }
81
+ return {
82
+ initialX: props.x + props.width / 2,
83
+ currentX: props.x + props.width / 2,
84
+ initialY,
85
+ currentY
86
+ };
87
+ }
88
+ const shouldPlaceToTheLeft = props.x < props.xOrigin;
89
+ if (shouldPlaceToTheLeft) {
90
+ initialX = props.xOrigin;
91
+ currentX = props.x - LABEL_OFFSET;
92
+ } else {
93
+ initialX = props.xOrigin;
94
+ currentX = props.x + props.width + LABEL_OFFSET;
95
+ }
96
+ return {
97
+ initialX,
98
+ currentX,
99
+ initialY: props.y + props.height / 2,
100
+ currentY: props.y + props.height / 2
101
+ };
53
102
  }