@gravity-ui/charts 1.44.0 → 1.46.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.
Files changed (99) hide show
  1. package/dist/cjs/components/ChartInner/index.js +2 -2
  2. package/dist/cjs/components/ChartInner/styles.css +2 -2
  3. package/dist/cjs/components/ChartInner/useChartInnerProps.js +1 -1
  4. package/dist/cjs/components/ChartInner/utils/title.d.ts +2 -1
  5. package/dist/cjs/components/ChartInner/utils/title.js +51 -14
  6. package/dist/cjs/components/Title/index.d.ts +4 -2
  7. package/dist/cjs/components/Title/index.js +9 -2
  8. package/dist/cjs/core/constants/defaults/annotation.d.ts +12 -0
  9. package/dist/cjs/core/constants/defaults/annotation.js +12 -0
  10. package/dist/cjs/core/constants/defaults/index.d.ts +1 -0
  11. package/dist/cjs/core/constants/defaults/index.js +1 -0
  12. package/dist/cjs/core/series/constants.d.ts +1 -1
  13. package/dist/cjs/core/series/constants.js +1 -1
  14. package/dist/cjs/core/series/prepare-annotation.d.ts +12 -0
  15. package/dist/cjs/core/series/prepare-annotation.js +31 -0
  16. package/dist/cjs/core/series/types.d.ts +16 -0
  17. package/dist/cjs/core/types/chart/annotation.d.ts +45 -0
  18. package/dist/cjs/core/types/chart/annotation.js +1 -0
  19. package/dist/cjs/core/types/chart/area.d.ts +8 -0
  20. package/dist/cjs/core/types/chart/bar-x.d.ts +6 -0
  21. package/dist/cjs/core/types/chart/line.d.ts +8 -0
  22. package/dist/cjs/core/types/chart/marker.d.ts +6 -4
  23. package/dist/cjs/core/types/chart/series.d.ts +7 -0
  24. package/dist/cjs/core/types/chart/title.d.ts +18 -0
  25. package/dist/cjs/core/types/chart/tooltip.d.ts +1 -0
  26. package/dist/cjs/core/types/index.d.ts +1 -0
  27. package/dist/cjs/core/types/index.js +1 -0
  28. package/dist/cjs/core/utils/text.d.ts +8 -0
  29. package/dist/cjs/core/utils/text.js +9 -1
  30. package/dist/cjs/hooks/types.d.ts +6 -3
  31. package/dist/cjs/hooks/useShapes/HtmlLayer.js +4 -3
  32. package/dist/cjs/hooks/useShapes/annotation/index.d.ts +14 -0
  33. package/dist/cjs/hooks/useShapes/annotation/index.js +200 -0
  34. package/dist/cjs/hooks/useShapes/area/index.d.ts +2 -0
  35. package/dist/cjs/hooks/useShapes/area/index.js +21 -2
  36. package/dist/cjs/hooks/useShapes/area/prepare-data.d.ts +2 -1
  37. package/dist/cjs/hooks/useShapes/area/prepare-data.js +38 -20
  38. package/dist/cjs/hooks/useShapes/area/types.d.ts +4 -0
  39. package/dist/cjs/hooks/useShapes/bar-x/index.d.ts +2 -0
  40. package/dist/cjs/hooks/useShapes/bar-x/index.js +30 -2
  41. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +10 -2
  42. package/dist/cjs/hooks/useShapes/bar-x/types.d.ts +2 -0
  43. package/dist/cjs/hooks/useShapes/index.js +5 -3
  44. package/dist/cjs/hooks/useShapes/line/index.d.ts +2 -0
  45. package/dist/cjs/hooks/useShapes/line/index.js +18 -4
  46. package/dist/cjs/hooks/useShapes/line/prepare-data.d.ts +2 -1
  47. package/dist/cjs/hooks/useShapes/line/prepare-data.js +28 -10
  48. package/dist/cjs/hooks/useShapes/line/types.d.ts +4 -0
  49. package/dist/cjs/types/chart-ui.d.ts +2 -0
  50. package/dist/esm/components/ChartInner/index.js +2 -2
  51. package/dist/esm/components/ChartInner/styles.css +2 -2
  52. package/dist/esm/components/ChartInner/useChartInnerProps.js +1 -1
  53. package/dist/esm/components/ChartInner/utils/title.d.ts +2 -1
  54. package/dist/esm/components/ChartInner/utils/title.js +51 -14
  55. package/dist/esm/components/Title/index.d.ts +4 -2
  56. package/dist/esm/components/Title/index.js +9 -2
  57. package/dist/esm/core/constants/defaults/annotation.d.ts +12 -0
  58. package/dist/esm/core/constants/defaults/annotation.js +12 -0
  59. package/dist/esm/core/constants/defaults/index.d.ts +1 -0
  60. package/dist/esm/core/constants/defaults/index.js +1 -0
  61. package/dist/esm/core/series/constants.d.ts +1 -1
  62. package/dist/esm/core/series/constants.js +1 -1
  63. package/dist/esm/core/series/prepare-annotation.d.ts +12 -0
  64. package/dist/esm/core/series/prepare-annotation.js +31 -0
  65. package/dist/esm/core/series/types.d.ts +16 -0
  66. package/dist/esm/core/types/chart/annotation.d.ts +45 -0
  67. package/dist/esm/core/types/chart/annotation.js +1 -0
  68. package/dist/esm/core/types/chart/area.d.ts +8 -0
  69. package/dist/esm/core/types/chart/bar-x.d.ts +6 -0
  70. package/dist/esm/core/types/chart/line.d.ts +8 -0
  71. package/dist/esm/core/types/chart/marker.d.ts +6 -4
  72. package/dist/esm/core/types/chart/series.d.ts +7 -0
  73. package/dist/esm/core/types/chart/title.d.ts +18 -0
  74. package/dist/esm/core/types/chart/tooltip.d.ts +1 -0
  75. package/dist/esm/core/types/index.d.ts +1 -0
  76. package/dist/esm/core/types/index.js +1 -0
  77. package/dist/esm/core/utils/text.d.ts +8 -0
  78. package/dist/esm/core/utils/text.js +9 -1
  79. package/dist/esm/hooks/types.d.ts +6 -3
  80. package/dist/esm/hooks/useShapes/HtmlLayer.js +4 -3
  81. package/dist/esm/hooks/useShapes/annotation/index.d.ts +14 -0
  82. package/dist/esm/hooks/useShapes/annotation/index.js +200 -0
  83. package/dist/esm/hooks/useShapes/area/index.d.ts +2 -0
  84. package/dist/esm/hooks/useShapes/area/index.js +21 -2
  85. package/dist/esm/hooks/useShapes/area/prepare-data.d.ts +2 -1
  86. package/dist/esm/hooks/useShapes/area/prepare-data.js +38 -20
  87. package/dist/esm/hooks/useShapes/area/types.d.ts +4 -0
  88. package/dist/esm/hooks/useShapes/bar-x/index.d.ts +2 -0
  89. package/dist/esm/hooks/useShapes/bar-x/index.js +30 -2
  90. package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +10 -2
  91. package/dist/esm/hooks/useShapes/bar-x/types.d.ts +2 -0
  92. package/dist/esm/hooks/useShapes/index.js +5 -3
  93. package/dist/esm/hooks/useShapes/line/index.d.ts +2 -0
  94. package/dist/esm/hooks/useShapes/line/index.js +18 -4
  95. package/dist/esm/hooks/useShapes/line/prepare-data.d.ts +2 -1
  96. package/dist/esm/hooks/useShapes/line/prepare-data.js +28 -10
  97. package/dist/esm/hooks/useShapes/line/types.d.ts +4 -0
  98. package/dist/esm/types/chart-ui.d.ts +2 -0
  99. package/package.json +2 -2
@@ -0,0 +1,200 @@
1
+ import { select } from 'd3-selection';
2
+ import { DESCENDER_RATIO } from '../../../core/utils/text';
3
+ import { block } from '../../../utils';
4
+ const b = block('annotation');
5
+ const ARROW_WIDTH = 18;
6
+ const ARROW_HEIGHT = 9;
7
+ // Base arrow path pointing downward (for "top" placement).
8
+ // Elliptical arc matching gravity-ui/uikit Popup arrow geometry.
9
+ // uikit builds the arrow from two 28×30 circles ($arrow-circle-width/height) with
10
+ // a 5px inset box-shadow ($arrow-border), clipped in 9×9 wrappers.
11
+ // The visible curve follows the inner edge of the ring:
12
+ // rx = circle_width/2 - (arrow_border - border_width) = 14 - 4 = 10
13
+ // ry = circle_height/2 - (arrow_border - border_width) = 15 - 4 = 11
14
+ const ARROW_RX = 10;
15
+ const ARROW_RY = 11;
16
+ const ARROW_PATH = (() => {
17
+ const hw = ARROW_WIDTH / 2;
18
+ const h = ARROW_HEIGHT;
19
+ return `M ${-hw},0 A ${ARROW_RX} ${ARROW_RY} 0 0 1 0,${h} A ${ARROW_RX} ${ARROW_RY} 0 0 1 ${hw},0 Z`;
20
+ })();
21
+ function getArrowRotation(placement) {
22
+ switch (placement) {
23
+ case 'top':
24
+ return 0;
25
+ case 'bottom':
26
+ return 180;
27
+ case 'right':
28
+ return 90;
29
+ case 'left':
30
+ default:
31
+ return -90;
32
+ }
33
+ }
34
+ function clampX(x, width, plotWidth) {
35
+ return Math.max(0, Math.min(x, plotWidth - width));
36
+ }
37
+ function clampY(y, height, plotHeight) {
38
+ return Math.max(0, Math.min(y, plotHeight - height));
39
+ }
40
+ function calculateLayout(args) {
41
+ const { anchorX, anchorY, popupWidth, popupHeight, offset, plotWidth, plotHeight } = args;
42
+ // Minimum distance from popup edge to arrow center (arrow half-width + border radius clearance)
43
+ const arrowEdgePadding = ARROW_WIDTH / 2;
44
+ // Check if anchor falls within popup's horizontal span (for top/bottom placement)
45
+ function isAnchorInPopupX(popupX) {
46
+ return (anchorX >= popupX + arrowEdgePadding &&
47
+ anchorX <= popupX + popupWidth - arrowEdgePadding);
48
+ }
49
+ // Check if anchor falls within popup's vertical span (for right/left placement)
50
+ function isAnchorInPopupY(popupY) {
51
+ return (anchorY >= popupY + arrowEdgePadding &&
52
+ anchorY <= popupY + popupHeight - arrowEdgePadding);
53
+ }
54
+ // Try top
55
+ const topY = anchorY - offset - ARROW_HEIGHT - popupHeight;
56
+ if (topY >= 0) {
57
+ const popupX = clampX(anchorX - popupWidth / 2, popupWidth, plotWidth);
58
+ if (isAnchorInPopupX(popupX)) {
59
+ return {
60
+ arrowX: anchorX,
61
+ arrowY: anchorY,
62
+ popupX,
63
+ popupY: topY,
64
+ placement: 'top',
65
+ showArrow: true,
66
+ };
67
+ }
68
+ }
69
+ // Try bottom
70
+ const bottomY = anchorY + offset + ARROW_HEIGHT;
71
+ if (bottomY + popupHeight <= plotHeight) {
72
+ const popupX = clampX(anchorX - popupWidth / 2, popupWidth, plotWidth);
73
+ if (isAnchorInPopupX(popupX)) {
74
+ return {
75
+ arrowX: anchorX,
76
+ arrowY: anchorY,
77
+ popupX,
78
+ popupY: bottomY,
79
+ placement: 'bottom',
80
+ showArrow: true,
81
+ };
82
+ }
83
+ }
84
+ // Try right
85
+ const rightX = anchorX + offset + ARROW_HEIGHT;
86
+ if (rightX + popupWidth <= plotWidth) {
87
+ const popupY = clampY(anchorY - popupHeight / 2, popupHeight, plotHeight);
88
+ if (isAnchorInPopupY(popupY)) {
89
+ return {
90
+ arrowX: anchorX,
91
+ arrowY: anchorY,
92
+ popupX: rightX,
93
+ popupY,
94
+ placement: 'right',
95
+ showArrow: true,
96
+ };
97
+ }
98
+ }
99
+ // Try left
100
+ const leftX = anchorX - offset - ARROW_HEIGHT - popupWidth;
101
+ if (leftX >= 0) {
102
+ const popupY = clampY(anchorY - popupHeight / 2, popupHeight, plotHeight);
103
+ if (isAnchorInPopupY(popupY)) {
104
+ return {
105
+ arrowX: anchorX,
106
+ arrowY: anchorY,
107
+ popupX: leftX,
108
+ popupY,
109
+ placement: 'left',
110
+ showArrow: true,
111
+ };
112
+ }
113
+ }
114
+ // Fallback: no arrow, popup near anchor (prefer above, then below)
115
+ const popupX = clampX(anchorX - popupWidth / 2, popupWidth, plotWidth);
116
+ const fallbackTopY = anchorY - offset - popupHeight;
117
+ const popupY = fallbackTopY >= 0 ? fallbackTopY : Math.min(plotHeight - popupHeight, anchorY + offset);
118
+ return {
119
+ arrowX: anchorX,
120
+ arrowY: anchorY,
121
+ popupX,
122
+ popupY,
123
+ placement: 'top',
124
+ showArrow: false,
125
+ };
126
+ }
127
+ function getArrowTranslate(layout, popupWidth, popupHeight) {
128
+ const { arrowX, arrowY, popupX, popupY, placement } = layout;
129
+ // Overlap by 0.5px to avoid subpixel gap between arrow and popup rect
130
+ const overlap = 0.5;
131
+ switch (placement) {
132
+ case 'top':
133
+ return `translate(${arrowX}, ${popupY + popupHeight - overlap})`;
134
+ case 'bottom':
135
+ return `translate(${arrowX}, ${popupY + overlap})`;
136
+ case 'right':
137
+ return `translate(${popupX + overlap}, ${arrowY})`;
138
+ case 'left':
139
+ default:
140
+ return `translate(${popupX + popupWidth - overlap}, ${arrowY})`;
141
+ }
142
+ }
143
+ export function renderAnnotations(args) {
144
+ const { container, anchors, plotWidth, plotHeight } = args;
145
+ container.selectAll(`.${b()}`).remove();
146
+ if (!anchors.length) {
147
+ return;
148
+ }
149
+ const groups = container
150
+ .selectAll(`.${b()}`)
151
+ .data(anchors)
152
+ .join('g')
153
+ .attr('class', b());
154
+ groups.each(function (d) {
155
+ const g = select(this);
156
+ const { annotation, x: anchorX, y: anchorY } = d;
157
+ const { label, popup } = annotation;
158
+ const [paddingV, paddingH] = popup.padding;
159
+ const popupWidth = label.size.width + paddingH * 2;
160
+ const popupHeight = label.size.height + paddingV * 2;
161
+ const layout = calculateLayout({
162
+ anchorX,
163
+ anchorY,
164
+ popupWidth,
165
+ popupHeight,
166
+ offset: popup.offset,
167
+ plotWidth,
168
+ plotHeight,
169
+ });
170
+ // Popup background
171
+ g.append('rect')
172
+ .attr('class', b('popup'))
173
+ .attr('x', layout.popupX)
174
+ .attr('y', layout.popupY)
175
+ .attr('width', popupWidth)
176
+ .attr('height', popupHeight)
177
+ .attr('rx', popup.borderRadius)
178
+ .attr('ry', popup.borderRadius)
179
+ .attr('fill', popup.backgroundColor);
180
+ // Arrow
181
+ if (layout.showArrow) {
182
+ const arrowTranslate = getArrowTranslate(layout, popupWidth, popupHeight);
183
+ const arrowRotation = getArrowRotation(layout.placement);
184
+ g.append('path')
185
+ .attr('class', b('arrow'))
186
+ .attr('d', ARROW_PATH)
187
+ .attr('fill', popup.backgroundColor)
188
+ .attr('transform', `${arrowTranslate} rotate(${arrowRotation})`);
189
+ }
190
+ // Text
191
+ g.append('text')
192
+ .attr('class', b('text'))
193
+ .text(label.text)
194
+ .attr('x', layout.popupX + paddingH)
195
+ .attr('y', layout.popupY + paddingV + label.size.height * (1 - DESCENDER_RATIO))
196
+ .style('font-size', label.style.fontSize)
197
+ .style('font-weight', label.style.fontWeight || '')
198
+ .style('fill', label.style.fontColor || '');
199
+ });
200
+ }
@@ -3,6 +3,8 @@ import type { Dispatch } from 'd3-dispatch';
3
3
  import type { PreparedSeriesOptions } from '../../useSeries/types';
4
4
  import type { PreparedAreaData } from './types';
5
5
  type Args = {
6
+ boundsHeight: number;
7
+ boundsWidth: number;
6
8
  clipPathId: string;
7
9
  htmlLayout: HTMLElement | null;
8
10
  preparedData: PreparedAreaData[];
@@ -6,15 +6,17 @@ import get from 'lodash/get';
6
6
  import { filterOverlappingLabels } from '../../../core/utils';
7
7
  import { block } from '../../../utils';
8
8
  import { HtmlLayer } from '../HtmlLayer';
9
+ import { renderAnnotations } from '../annotation';
9
10
  import { getMarkerHaloVisibility, getMarkerVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
10
11
  import { setActiveState } from '../utils';
11
12
  const b = block('area');
12
13
  export const AreaSeriesShapes = (args) => {
13
- const { dispatcher, preparedData, seriesOptions, htmlLayout, clipPathId } = args;
14
+ const { boundsHeight, boundsWidth, dispatcher, preparedData, seriesOptions, htmlLayout, clipPathId, } = args;
14
15
  const hoveredDataRef = React.useRef(null);
15
16
  const plotRef = React.useRef(null);
16
17
  const markersRef = React.useRef(null);
17
18
  const hoverMarkersRef = React.useRef(null);
19
+ const annotationsRef = React.useRef(null);
18
20
  const allowOverlapDataLabels = React.useMemo(() => {
19
21
  return preparedData.some((d) => d === null || d === void 0 ? void 0 : d.series.dataLabels.allowOverlap);
20
22
  }, [preparedData]);
@@ -83,6 +85,15 @@ export const AreaSeriesShapes = (args) => {
83
85
  .data(markers)
84
86
  .join('g')
85
87
  .call(renderMarker);
88
+ if (annotationsRef.current) {
89
+ const anchors = preparedData.flatMap((d) => d.annotations);
90
+ renderAnnotations({
91
+ anchors,
92
+ container: select(annotationsRef.current),
93
+ plotHeight: boundsHeight,
94
+ plotWidth: boundsWidth,
95
+ });
96
+ }
86
97
  const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
87
98
  const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
88
99
  function handleShapeHover(data) {
@@ -189,7 +200,14 @@ export const AreaSeriesShapes = (args) => {
189
200
  return () => {
190
201
  dispatcher === null || dispatcher === void 0 ? void 0 : dispatcher.on('hover-shape.area', null);
191
202
  };
192
- }, [allowOverlapDataLabels, dispatcher, preparedData, seriesOptions]);
203
+ }, [
204
+ allowOverlapDataLabels,
205
+ boundsHeight,
206
+ boundsWidth,
207
+ dispatcher,
208
+ preparedData,
209
+ seriesOptions,
210
+ ]);
193
211
  const htmlLayerData = React.useMemo(() => {
194
212
  const items = preparedData.map((d) => d === null || d === void 0 ? void 0 : d.htmlLabels).flat();
195
213
  if (allowOverlapDataLabels) {
@@ -201,5 +219,6 @@ export const AreaSeriesShapes = (args) => {
201
219
  React.createElement("g", { ref: plotRef, className: b(), clipPath: `url(#${clipPathId})` }),
202
220
  React.createElement("g", { ref: markersRef }),
203
221
  React.createElement("g", { ref: hoverMarkersRef }),
222
+ React.createElement("g", { ref: annotationsRef }),
204
223
  React.createElement(HtmlLayer, { preparedData: htmlLayerData, htmlLayout: htmlLayout })));
205
224
  };
@@ -1,10 +1,11 @@
1
1
  import type { PreparedSplit } from '../../../core/layout/split-types';
2
2
  import type { ChartScale } from '../../../core/scales/types';
3
3
  import type { PreparedXAxis, PreparedYAxis } from '../../useAxis/types';
4
- import type { PreparedAreaSeries } from '../../useSeries/types';
4
+ import type { PreparedAreaSeries, PreparedSeriesOptions } from '../../useSeries/types';
5
5
  import type { PreparedAreaData } from './types';
6
6
  export declare const prepareAreaData: (args: {
7
7
  series: PreparedAreaSeries[];
8
+ seriesOptions?: PreparedSeriesOptions;
8
9
  xAxis: PreparedXAxis;
9
10
  xScale: ChartScale;
10
11
  yAxis: PreparedYAxis[];
@@ -1,6 +1,7 @@
1
1
  import { group, min, sort } from 'd3-array';
2
2
  import isNil from 'lodash/isNil';
3
3
  import round from 'lodash/round';
4
+ import { prepareAnnotation } from '../../../core/series/prepare-annotation';
4
5
  import { getDataCategoryValue, getLabelsSize, getTextSizeFn } from '../../../core/utils';
5
6
  import { getFormattedValue } from '../../../core/utils/format';
6
7
  import { getXValue, getYValue } from '../utils';
@@ -76,8 +77,8 @@ async function prepareDataLabels({ series, points, xMax, yAxisTop, isOutsideBoun
76
77
  return { svgLabels, htmlLabels };
77
78
  }
78
79
  export const prepareAreaData = async (args) => {
79
- var _a, _b, _c, _d;
80
- const { series, xAxis, xScale, yAxis, yScale, split, isOutsideBounds, isRangeSlider } = args;
80
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
81
+ const { series, seriesOptions, xAxis, xScale, yAxis, yScale, split, isOutsideBounds, isRangeSlider, } = args;
81
82
  const [_xMin, xRangeMax] = xScale.range();
82
83
  const xMax = xRangeMax;
83
84
  const result = [];
@@ -165,16 +166,25 @@ export const prepareAreaData = async (args) => {
165
166
  : d.x);
166
167
  return m.set(key, d);
167
168
  }, new Map());
168
- const points = xValues.reduce((pointsAcc, [x, xValue], index) => {
169
- var _a, _b, _c, _d, _e, _f, _g;
169
+ const annotationOpts = (_d = seriesOptions === null || seriesOptions === void 0 ? void 0 : seriesOptions.area) === null || _d === void 0 ? void 0 : _d.annotation;
170
+ const points = [];
171
+ for (let xIdx = 0; xIdx < xValues.length; xIdx++) {
172
+ const [x, xValue] = xValues[xIdx];
170
173
  const rawData = seriesData.get(x);
171
174
  const d = rawData !== null && rawData !== void 0 ? rawData : {
172
175
  x,
173
176
  y: 0,
174
177
  };
175
- let yDataValue = (_a = d.y) !== null && _a !== void 0 ? _a : null;
178
+ let yDataValue = (_e = d.y) !== null && _e !== void 0 ? _e : null;
179
+ const pointAnnotation = d.annotation && !isRangeSlider
180
+ ? await prepareAnnotation({
181
+ annotation: d.annotation,
182
+ optionsLabel: annotationOpts === null || annotationOpts === void 0 ? void 0 : annotationOpts.label,
183
+ optionsPopup: annotationOpts === null || annotationOpts === void 0 ? void 0 : annotationOpts.popup,
184
+ })
185
+ : undefined;
176
186
  if (s.nullMode === 'connect' && (yDataValue === null || !rawData)) {
177
- return pointsAcc;
187
+ continue;
178
188
  }
179
189
  if (yDataValue && isPercentStacking) {
180
190
  yDataValue = Number(yDataValue) * ratio[x];
@@ -188,21 +198,23 @@ export const prepareAreaData = async (args) => {
188
198
  });
189
199
  if (typeof yDataValue === 'number' && yValue !== null) {
190
200
  yValue = round(yValue, 2);
191
- const prevPoint = seriesData.get((_b = xValues[index - 1]) === null || _b === void 0 ? void 0 : _b[0]);
192
- const nextPoint = seriesData.get((_c = xValues[index + 1]) === null || _c === void 0 ? void 0 : _c[0]);
201
+ const prevPoint = seriesData.get((_f = xValues[xIdx - 1]) === null || _f === void 0 ? void 0 : _f[0]);
202
+ const nextPoint = seriesData.get((_g = xValues[xIdx + 1]) === null || _g === void 0 ? void 0 : _g[0]);
193
203
  const currentPointStackHeight = Math.abs(yMin - yValue);
194
204
  if (yDataValue >= 0) {
195
205
  const positiveStackHeights = positiveStackValues.get(x);
196
- let prevSectionStackHeight = (_d = positiveStackHeights === null || positiveStackHeights === void 0 ? void 0 : positiveStackHeights.prev) !== null && _d !== void 0 ? _d : 0;
197
- let nextSectionStackHeight = (_e = positiveStackHeights === null || positiveStackHeights === void 0 ? void 0 : positiveStackHeights.next) !== null && _e !== void 0 ? _e : 0;
206
+ let prevSectionStackHeight = (_h = positiveStackHeights === null || positiveStackHeights === void 0 ? void 0 : positiveStackHeights.prev) !== null && _h !== void 0 ? _h : 0;
207
+ let nextSectionStackHeight = (_j = positiveStackHeights === null || positiveStackHeights === void 0 ? void 0 : positiveStackHeights.next) !== null && _j !== void 0 ? _j : 0;
198
208
  const point = {
199
209
  y0: yAxisTop + yMin - prevSectionStackHeight,
200
210
  x: xValue,
201
211
  y: yAxisTop + yValue - prevSectionStackHeight,
212
+ color: (_l = (_k = d.marker) === null || _k === void 0 ? void 0 : _k.color) !== null && _l !== void 0 ? _l : d.color,
202
213
  data: d,
203
214
  series: s,
215
+ annotation: pointAnnotation,
204
216
  };
205
- pointsAcc.push(point);
217
+ points.push(point);
206
218
  if (prevSectionStackHeight !== nextSectionStackHeight) {
207
219
  const point2 = {
208
220
  y0: yAxisTop + yMin - nextSectionStackHeight,
@@ -211,7 +223,7 @@ export const prepareAreaData = async (args) => {
211
223
  data: d,
212
224
  series: s,
213
225
  };
214
- pointsAcc.push(point2);
226
+ points.push(point2);
215
227
  if (isPercentStacking) {
216
228
  const newYValue = yAxisTop +
217
229
  yValue -
@@ -235,9 +247,9 @@ export const prepareAreaData = async (args) => {
235
247
  }
236
248
  else {
237
249
  const negativeStackHeights = negativeStackValues.get(x);
238
- let prevSectionStackHeight = (_f = negativeStackHeights === null || negativeStackHeights === void 0 ? void 0 : negativeStackHeights.prev) !== null && _f !== void 0 ? _f : 0;
239
- let nextSectionStackHeight = (_g = negativeStackHeights === null || negativeStackHeights === void 0 ? void 0 : negativeStackHeights.next) !== null && _g !== void 0 ? _g : 0;
240
- pointsAcc.push({
250
+ let prevSectionStackHeight = (_m = negativeStackHeights === null || negativeStackHeights === void 0 ? void 0 : negativeStackHeights.prev) !== null && _m !== void 0 ? _m : 0;
251
+ let nextSectionStackHeight = (_o = negativeStackHeights === null || negativeStackHeights === void 0 ? void 0 : negativeStackHeights.next) !== null && _o !== void 0 ? _o : 0;
252
+ points.push({
241
253
  y0: yAxisTop + yMin + prevSectionStackHeight,
242
254
  x: xValue,
243
255
  y: yAxisTop + yValue + prevSectionStackHeight,
@@ -245,7 +257,7 @@ export const prepareAreaData = async (args) => {
245
257
  series: s,
246
258
  });
247
259
  if (prevSectionStackHeight !== nextSectionStackHeight) {
248
- pointsAcc.push({
260
+ points.push({
249
261
  y0: yAxisTop + yMin + nextSectionStackHeight,
250
262
  x: xValue,
251
263
  y: yAxisTop + yValue + nextSectionStackHeight,
@@ -268,7 +280,7 @@ export const prepareAreaData = async (args) => {
268
280
  }
269
281
  }
270
282
  else {
271
- pointsAcc.push({
283
+ points.push({
272
284
  y0: yAxisTop + yMin,
273
285
  x: xValue,
274
286
  y: null,
@@ -276,8 +288,7 @@ export const prepareAreaData = async (args) => {
276
288
  series: s,
277
289
  });
278
290
  }
279
- return pointsAcc;
280
- }, []);
291
+ }
281
292
  let markers = [];
282
293
  const hasPerPointNormalMarkers = s.data.some((d) => { var _a, _b, _c; return (_c = (_b = (_a = d.marker) === null || _a === void 0 ? void 0 : _a.states) === null || _b === void 0 ? void 0 : _b.normal) === null || _c === void 0 ? void 0 : _c.enabled; });
283
294
  if (s.marker.states.normal.enabled || hasPerPointNormalMarkers) {
@@ -298,7 +309,14 @@ export const prepareAreaData = async (args) => {
298
309
  return markersAcc;
299
310
  }, []);
300
311
  }
312
+ const annotations = points.reduce((result, p) => {
313
+ if (p.annotation && p.y !== null) {
314
+ result.push({ annotation: p.annotation, x: p.x, y: p.y });
315
+ }
316
+ return result;
317
+ }, []);
301
318
  seriesStackData.push({
319
+ annotations,
302
320
  points,
303
321
  markers,
304
322
  svgLabels: [],
@@ -315,7 +333,7 @@ export const prepareAreaData = async (args) => {
315
333
  for (let itemIndex = 0; itemIndex < seriesStackData.length; itemIndex++) {
316
334
  const item = seriesStackData[itemIndex];
317
335
  const currentYAxis = yAxis[item.series.yAxis];
318
- const itemYAxisTop = ((_d = split.plots[currentYAxis.plotIndex]) === null || _d === void 0 ? void 0 : _d.top) || 0;
336
+ const itemYAxisTop = ((_p = split.plots[currentYAxis.plotIndex]) === null || _p === void 0 ? void 0 : _p.top) || 0;
319
337
  if (item.series.dataLabels.enabled && !isRangeSlider) {
320
338
  const labelsData = await prepareDataLabels({
321
339
  series: item.series,
@@ -1,11 +1,14 @@
1
+ import type { PreparedAnnotation } from '../../../core/series/types';
1
2
  import type { AreaSeriesData, HtmlItem, LabelData } from '../../../types';
2
3
  import type { PreparedAreaSeries } from '../../useSeries/types';
4
+ import type { AnnotationAnchor } from '../annotation';
3
5
  export type PointData = {
4
6
  y0: number;
5
7
  x: number;
6
8
  y: number | null;
7
9
  data: AreaSeriesData;
8
10
  series: PreparedAreaSeries;
11
+ annotation?: PreparedAnnotation;
9
12
  color?: string;
10
13
  };
11
14
  export type MarkerPointData = PointData & {
@@ -18,6 +21,7 @@ export type MarkerData = {
18
21
  clipped: boolean;
19
22
  };
20
23
  export type PreparedAreaData = {
24
+ annotations: AnnotationAnchor[];
21
25
  id: string;
22
26
  points: PointData[];
23
27
  markers: MarkerData[];
@@ -5,6 +5,8 @@ import type { PreparedBarXData } from './types';
5
5
  export { prepareBarXData } from './prepare-data';
6
6
  export * from './types';
7
7
  type Args = {
8
+ boundsHeight: number;
9
+ boundsWidth: number;
8
10
  clipPathId: string;
9
11
  htmlLayout: HTMLElement | null;
10
12
  preparedData: PreparedBarXData[];
@@ -5,14 +5,16 @@ import get from 'lodash/get';
5
5
  import { filterOverlappingLabels } from '../../../core/utils';
6
6
  import { block } from '../../../utils';
7
7
  import { HtmlLayer } from '../HtmlLayer';
8
+ import { renderAnnotations } from '../annotation';
8
9
  import { getRectPath } from '../utils';
9
10
  export { prepareBarXData } from './prepare-data';
10
11
  export * from './types';
11
12
  const b = block('bar-x');
12
13
  export const BarXSeriesShapes = (args) => {
13
- const { dispatcher, preparedData, seriesOptions, htmlLayout, clipPathId } = args;
14
+ const { boundsHeight, boundsWidth, dispatcher, preparedData, seriesOptions, htmlLayout, clipPathId, } = args;
14
15
  const hoveredDataRef = React.useRef(null);
15
16
  const ref = React.useRef(null);
17
+ const annotationsRef = React.useRef(null);
16
18
  const allowOverlapDataLabels = React.useMemo(() => {
17
19
  return preparedData.some((d) => d === null || d === void 0 ? void 0 : d.series.dataLabels.allowOverlap);
18
20
  }, [preparedData]);
@@ -65,6 +67,24 @@ export const BarXSeriesShapes = (args) => {
65
67
  .style('font-size', (d) => d.style.fontSize)
66
68
  .style('font-weight', (d) => d.style.fontWeight || null)
67
69
  .style('fill', (d) => d.style.fontColor || null);
70
+ if (annotationsRef.current) {
71
+ const anchors = [];
72
+ for (const d of preparedData) {
73
+ if (d.annotation) {
74
+ anchors.push({
75
+ annotation: d.annotation,
76
+ x: d.x + d.width / 2,
77
+ y: d.y,
78
+ });
79
+ }
80
+ }
81
+ renderAnnotations({
82
+ anchors,
83
+ container: select(annotationsRef.current),
84
+ plotHeight: boundsHeight,
85
+ plotWidth: boundsWidth,
86
+ });
87
+ }
68
88
  function handleShapeHover(data) {
69
89
  hoveredDataRef.current = data;
70
90
  const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
@@ -112,7 +132,14 @@ export const BarXSeriesShapes = (args) => {
112
132
  return () => {
113
133
  dispatcher === null || dispatcher === void 0 ? void 0 : dispatcher.on('hover-shape.bar-x', null);
114
134
  };
115
- }, [allowOverlapDataLabels, dispatcher, preparedData, seriesOptions]);
135
+ }, [
136
+ allowOverlapDataLabels,
137
+ boundsHeight,
138
+ boundsWidth,
139
+ dispatcher,
140
+ preparedData,
141
+ seriesOptions,
142
+ ]);
116
143
  const htmlLayerData = React.useMemo(() => {
117
144
  const items = preparedData.map((d) => d === null || d === void 0 ? void 0 : d.htmlLabels).flat();
118
145
  if (allowOverlapDataLabels) {
@@ -122,5 +149,6 @@ export const BarXSeriesShapes = (args) => {
122
149
  }, [allowOverlapDataLabels, preparedData]);
123
150
  return (React.createElement(React.Fragment, null,
124
151
  React.createElement("g", { ref: ref, className: b(), clipPath: `url(#${clipPathId})` }),
152
+ React.createElement("g", { ref: annotationsRef }),
125
153
  React.createElement(HtmlLayer, { preparedData: htmlLayerData, htmlLayout: htmlLayout })));
126
154
  };
@@ -1,5 +1,6 @@
1
1
  import { ascending, descending, max, min, reverse, sort } from 'd3-array';
2
2
  import get from 'lodash/get';
3
+ import { prepareAnnotation } from '../../../core/series/prepare-annotation';
3
4
  import { getDataCategoryValue, getLabelsSize } from '../../../core/utils';
4
5
  import { getFormattedValue } from '../../../core/utils/format';
5
6
  import { MIN_BAR_GAP, MIN_BAR_GROUP_GAP, MIN_BAR_WIDTH } from '../../constants';
@@ -35,7 +36,7 @@ async function getLabelData(d, xMax) {
35
36
  };
36
37
  }
37
38
  export const prepareBarXData = async (args) => {
38
- var _a, _b, _c, _d, _e, _f;
39
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
39
40
  const { series, seriesOptions, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight, split, isRangeSlider, } = args;
40
41
  const stackGap = seriesOptions['bar-x'].stackGap;
41
42
  const categories = (_a = xAxis === null || xAxis === void 0 ? void 0 : xAxis.categories) !== null && _a !== void 0 ? _a : [];
@@ -178,6 +179,13 @@ export const prepareBarXData = async (args) => {
178
179
  continue;
179
180
  }
180
181
  const barData = {
182
+ annotation: yValue.data.annotation && !isRangeSlider
183
+ ? await prepareAnnotation({
184
+ annotation: yValue.data.annotation,
185
+ optionsLabel: (_g = (_f = seriesOptions['bar-x']) === null || _f === void 0 ? void 0 : _f.annotation) === null || _g === void 0 ? void 0 : _g.label,
186
+ optionsPopup: (_j = (_h = seriesOptions['bar-x']) === null || _h === void 0 ? void 0 : _h.annotation) === null || _j === void 0 ? void 0 : _j.popup,
187
+ })
188
+ : undefined,
181
189
  x,
182
190
  y: barPositionY,
183
191
  width: rectWidth,
@@ -220,7 +228,7 @@ export const prepareBarXData = async (args) => {
220
228
  barData.x >= xMax ||
221
229
  barData.y + barData.height <= 0 ||
222
230
  barData.y >= plotHeight;
223
- const isZeroValue = ((_f = barData.data.y) !== null && _f !== void 0 ? _f : 0) === 0;
231
+ const isZeroValue = ((_k = barData.data.y) !== null && _k !== void 0 ? _k : 0) === 0;
224
232
  if (barData.series.dataLabels.enabled &&
225
233
  !isRangeSlider &&
226
234
  (!isBarOutsideBounds || isZeroValue)) {
@@ -1,6 +1,8 @@
1
+ import type { PreparedAnnotation } from '../../../core/series/types';
1
2
  import type { HtmlItem, LabelData, TooltipDataChunkBarX } from '../../../types';
2
3
  import type { PreparedBarXSeries } from '../../useSeries/types';
3
4
  export type PreparedBarXData = Omit<TooltipDataChunkBarX, 'series'> & {
5
+ annotation?: PreparedAnnotation;
4
6
  x: number;
5
7
  y: number;
6
8
  width: number;
@@ -62,7 +62,7 @@ export async function getShapes(args) {
62
62
  split,
63
63
  isRangeSlider,
64
64
  });
65
- shapes[index] = (React.createElement(BarXSeriesShapes, { key: SERIES_TYPE.BarX, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
65
+ shapes[index] = (React.createElement(BarXSeriesShapes, { key: SERIES_TYPE.BarX, boundsHeight: boundsHeight, boundsWidth: boundsWidth, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
66
66
  shapesData.splice(index, 0, ...preparedData);
67
67
  layers.push(...preparedData);
68
68
  }
@@ -104,6 +104,7 @@ export async function getShapes(args) {
104
104
  if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
105
105
  const preparedData = await prepareLineData({
106
106
  series: chartSeries,
107
+ seriesOptions,
107
108
  xAxis,
108
109
  xScale,
109
110
  yAxis,
@@ -118,7 +119,7 @@ export async function getShapes(args) {
118
119
  yAxis,
119
120
  zoomState,
120
121
  });
121
- shapes[index] = (React.createElement(LineSeriesShapes, { key: groupId, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: resultClipPathId }));
122
+ shapes[index] = (React.createElement(LineSeriesShapes, { key: groupId, boundsHeight: boundsHeight, boundsWidth: boundsWidth, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: resultClipPathId }));
122
123
  shapesData.splice(index, 0, ...preparedData);
123
124
  layers.push(...preparedData);
124
125
  }
@@ -128,6 +129,7 @@ export async function getShapes(args) {
128
129
  if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
129
130
  const preparedData = await prepareAreaData({
130
131
  series: chartSeries,
132
+ seriesOptions,
131
133
  xAxis,
132
134
  xScale,
133
135
  yAxis,
@@ -136,7 +138,7 @@ export async function getShapes(args) {
136
138
  isOutsideBounds,
137
139
  isRangeSlider,
138
140
  });
139
- shapes[index] = (React.createElement(AreaSeriesShapes, { key: SERIES_TYPE.Area, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
141
+ shapes[index] = (React.createElement(AreaSeriesShapes, { key: SERIES_TYPE.Area, boundsHeight: boundsHeight, boundsWidth: boundsWidth, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
140
142
  shapesData.splice(index, 0, ...preparedData);
141
143
  layers.push(...preparedData);
142
144
  }
@@ -3,6 +3,8 @@ import type { Dispatch } from 'd3-dispatch';
3
3
  import type { PreparedSeriesOptions } from '../../useSeries/types';
4
4
  import type { PreparedLineData } from './types';
5
5
  type Args = {
6
+ boundsHeight: number;
7
+ boundsWidth: number;
6
8
  clipPathId: string;
7
9
  htmlLayout: HTMLElement | null;
8
10
  preparedData: PreparedLineData[];