@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
@@ -6,16 +6,19 @@ import get from 'lodash/get';
6
6
  import { getLineDashArray } 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('line');
12
13
  export const LineSeriesShapes = (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
  React.useEffect(() => {
21
+ var _a, _b;
19
22
  if (!plotRef.current || !markersRef.current) {
20
23
  return () => { };
21
24
  }
@@ -64,6 +67,15 @@ export const LineSeriesShapes = (args) => {
64
67
  .data(markers)
65
68
  .join('g')
66
69
  .call(renderMarker);
70
+ if (annotationsRef.current) {
71
+ const anchors = preparedData.flatMap((d) => d.annotations);
72
+ renderAnnotations({
73
+ anchors,
74
+ container: select(annotationsRef.current),
75
+ plotHeight: boundsHeight,
76
+ plotWidth: boundsWidth,
77
+ });
78
+ }
67
79
  const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
68
80
  const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
69
81
  function handleShapeHover(data) {
@@ -165,11 +177,12 @@ export const LineSeriesShapes = (args) => {
165
177
  if (hoveredDataRef.current !== null) {
166
178
  handleShapeHover(hoveredDataRef.current);
167
179
  }
168
- dispatcher === null || dispatcher === void 0 ? void 0 : dispatcher.on('hover-shape.line', handleShapeHover);
180
+ const eventName = `hover-shape.line-${(_b = (_a = preparedData[0]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : 'unknown'}`;
181
+ dispatcher === null || dispatcher === void 0 ? void 0 : dispatcher.on(eventName, handleShapeHover);
169
182
  return () => {
170
- dispatcher === null || dispatcher === void 0 ? void 0 : dispatcher.on('hover-shape.line', null);
183
+ dispatcher === null || dispatcher === void 0 ? void 0 : dispatcher.on(eventName, null);
171
184
  };
172
- }, [dispatcher, preparedData, seriesOptions]);
185
+ }, [boundsHeight, boundsWidth, dispatcher, preparedData, seriesOptions]);
173
186
  const htmlLayerData = React.useMemo(() => {
174
187
  const items = preparedData.map((d) => d === null || d === void 0 ? void 0 : d.htmlLabels).flat();
175
188
  return { htmlElements: items };
@@ -178,5 +191,6 @@ export const LineSeriesShapes = (args) => {
178
191
  React.createElement("g", { ref: plotRef, className: b(), clipPath: `url(#${clipPathId})` }),
179
192
  React.createElement("g", { ref: markersRef }),
180
193
  React.createElement("g", { ref: hoverMarkersRef }),
194
+ React.createElement("g", { ref: annotationsRef }),
181
195
  React.createElement(HtmlLayer, { preparedData: htmlLayerData, htmlLayout: htmlLayout })));
182
196
  };
@@ -2,10 +2,11 @@ import type { PreparedSplit } from '../../../core/layout/split-types';
2
2
  import type { ChartScale } from '../../../core/scales/types';
3
3
  import type { ShapeDataWithLabels } from '../../../types';
4
4
  import type { PreparedXAxis, PreparedYAxis } from '../../useAxis/types';
5
- import type { PreparedLineSeries } from '../../useSeries/types';
5
+ import type { PreparedLineSeries, PreparedSeriesOptions } from '../../useSeries/types';
6
6
  import type { PreparedLineData } from './types';
7
7
  export declare const prepareLineData: (args: {
8
8
  series: PreparedLineSeries[];
9
+ seriesOptions?: PreparedSeriesOptions;
9
10
  xAxis: PreparedXAxis;
10
11
  xScale: ChartScale;
11
12
  yAxis: PreparedYAxis[];
@@ -1,3 +1,4 @@
1
+ import { prepareAnnotation } from '../../../core/series/prepare-annotation';
1
2
  import { filterOverlappingLabels, getLabelsSize, getTextSizeFn } from '../../../core/utils';
2
3
  import { getFormattedValue } from '../../../core/utils/format';
3
4
  import { getXValue, getYValue } from '../utils';
@@ -15,8 +16,8 @@ async function getHtmlLabel(point, series, xMax) {
15
16
  };
16
17
  }
17
18
  export const prepareLineData = async (args) => {
18
- var _a, _b, _c, _d;
19
- const { series, xAxis, yAxis, xScale, yScale, split, isOutsideBounds, isRangeSlider, otherLayers, } = args;
19
+ var _a, _b, _c, _d, _e, _f, _g;
20
+ const { series, seriesOptions, xAxis, yAxis, xScale, yScale, split, isOutsideBounds, isRangeSlider, otherLayers, } = args;
20
21
  const [_xMin, xRangeMax] = xScale.range();
21
22
  const xMax = xRangeMax;
22
23
  const acc = [];
@@ -29,21 +30,31 @@ export const prepareLineData = async (args) => {
29
30
  if (!seriesYScale) {
30
31
  continue;
31
32
  }
32
- const points = s.data.map((d) => {
33
+ const annotationOpts = (_b = seriesOptions === null || seriesOptions === void 0 ? void 0 : seriesOptions.line) === null || _b === void 0 ? void 0 : _b.annotation;
34
+ const points = [];
35
+ for (let j = 0; j < s.data.length; j++) {
36
+ const d = s.data[j];
33
37
  const yValue = getYValue({
34
38
  point: d,
35
39
  points: s.data,
36
40
  yAxis: seriesYAxis,
37
41
  yScale: seriesYScale,
38
42
  });
39
- return {
43
+ points.push({
40
44
  x: getXValue({ point: d, points: s.data, xAxis, xScale }),
41
45
  y: yValue === null ? null : yAxisTop + yValue,
42
- active: true,
46
+ color: (_d = (_c = d.marker) === null || _c === void 0 ? void 0 : _c.color) !== null && _d !== void 0 ? _d : d.color,
43
47
  data: d,
44
48
  series: s,
45
- };
46
- });
49
+ annotation: d.annotation && !isRangeSlider
50
+ ? await prepareAnnotation({
51
+ annotation: d.annotation,
52
+ optionsLabel: annotationOpts === null || annotationOpts === void 0 ? void 0 : annotationOpts.label,
53
+ optionsPopup: annotationOpts === null || annotationOpts === void 0 ? void 0 : annotationOpts.popup,
54
+ })
55
+ : undefined,
56
+ });
57
+ }
47
58
  let htmlElements = [];
48
59
  let svgLabels = [];
49
60
  if (s.dataLabels.enabled && !isRangeSlider) {
@@ -64,7 +75,7 @@ export const prepareLineData = async (args) => {
64
75
  if (point.y !== null &&
65
76
  point.x !== null &&
66
77
  !isOutsideBounds(point.x, point.y)) {
67
- const labelValue = (_b = point.data.label) !== null && _b !== void 0 ? _b : point.data.y;
78
+ const labelValue = (_e = point.data.label) !== null && _e !== void 0 ? _e : point.data.y;
68
79
  const text = getFormattedValue(Object.assign({ value: labelValue }, s.dataLabels));
69
80
  const labelSize = await getTextSize(text);
70
81
  const style = s.dataLabels.style;
@@ -112,7 +123,14 @@ export const prepareLineData = async (args) => {
112
123
  return result;
113
124
  }, []);
114
125
  }
126
+ const annotations = points.reduce((result, p) => {
127
+ if (p.annotation && p.x !== null && p.y !== null) {
128
+ result.push({ annotation: p.annotation, x: p.x, y: p.y });
129
+ }
130
+ return result;
131
+ }, []);
115
132
  const result = {
133
+ annotations,
116
134
  points,
117
135
  markers,
118
136
  svgLabels: svgLabels,
@@ -122,11 +140,11 @@ export const prepareLineData = async (args) => {
122
140
  id: s.id,
123
141
  htmlLabels: htmlElements,
124
142
  color: s.color,
125
- lineWidth: (_c = (isRangeSlider ? s.rangeSlider.lineWidth : undefined)) !== null && _c !== void 0 ? _c : s.lineWidth,
143
+ lineWidth: (_f = (isRangeSlider ? s.rangeSlider.lineWidth : undefined)) !== null && _f !== void 0 ? _f : s.lineWidth,
126
144
  dashStyle: s.dashStyle,
127
145
  linecap: s.linecap,
128
146
  linejoin: s.linejoin,
129
- opacity: (_d = (isRangeSlider ? s.rangeSlider.opacity : undefined)) !== null && _d !== void 0 ? _d : s.opacity,
147
+ opacity: (_g = (isRangeSlider ? s.rangeSlider.opacity : undefined)) !== null && _g !== void 0 ? _g : s.opacity,
130
148
  };
131
149
  acc.push(result);
132
150
  }
@@ -1,11 +1,14 @@
1
1
  import type { DashStyle, LineCap, LineJoin } from '../../../core/constants';
2
+ import type { PreparedAnnotation } from '../../../core/series/types';
2
3
  import type { HtmlItem, LabelData, LineSeriesData, LineSeriesLineBaseStyle } from '../../../types';
3
4
  import type { PreparedLineSeries } from '../../useSeries/types';
5
+ import type { AnnotationAnchor } from '../annotation';
4
6
  export type PointData = {
5
7
  x: number | null;
6
8
  y: number | null;
7
9
  data: LineSeriesData;
8
10
  series: PreparedLineSeries;
11
+ annotation?: PreparedAnnotation;
9
12
  color?: string;
10
13
  };
11
14
  export type MarkerPointData = PointData & {
@@ -19,6 +22,7 @@ export type MarkerData = {
19
22
  clipped: boolean;
20
23
  };
21
24
  export type PreparedLineData = {
25
+ annotations: AnnotationAnchor[];
22
26
  id: string;
23
27
  points: PointData[];
24
28
  markers: MarkerData[];
@@ -24,6 +24,8 @@ export interface HtmlItem {
24
24
  height: number;
25
25
  };
26
26
  style?: BaseTextStyle & React.CSSProperties;
27
+ /** Coordinate space for positioning: 'plot' uses the plot area origin, 'chart' uses the full chart origin. Defaults to 'plot'. */
28
+ scope?: 'plot' | 'chart';
27
29
  }
28
30
  export interface ShapeDataWithLabels {
29
31
  svgLabels: LabelData[];
@@ -256,7 +256,7 @@ export const ChartInner = (props) => {
256
256
  React.createElement("rect", { x: 0, y: 0, width: boundsWidth, height: boundsHeight })),
257
257
  React.createElement("clipPath", { id: getClipPathIdByBounds({ clipPathId, bounds: 'horizontal' }) },
258
258
  React.createElement("rect", { x: 0, y: -boundsHeight, width: boundsWidth, height: boundsHeight * 3 }))),
259
- preparedTitle && React.createElement(Title, Object.assign({}, preparedTitle)),
259
+ preparedTitle && React.createElement(Title, Object.assign({}, preparedTitle, { htmlLayout: htmlLayout })),
260
260
  React.createElement("g", { transform: `translate(0, ${boundsOffsetTop})` }, preparedSplit === null || preparedSplit === void 0 ? void 0 : preparedSplit.plots.map((plot, index) => {
261
261
  return React.createElement(PlotTitle, { key: `plot-${index}`, title: plot.title });
262
262
  })),
@@ -285,7 +285,7 @@ export const ChartInner = (props) => {
285
285
  // when starting to select an area, the tooltip remains in the position where the selection began
286
286
  onPointerMove: throttledHandlePointerMove, onPointerLeave: handlePointerLeave, onTouchStart: throttledHandleTouchMove, onTouchMove: throttledHandleTouchMove, onClick: handleChartClick }, initialized ? chartContent : null),
287
287
  React.createElement("div", { className: b('html-layer'), ref: setHtmlLayout, style: {
288
- '--g-html-layout-transform': `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
288
+ '--g-html-layout-plot-transform': `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
289
289
  } }),
290
290
  Object.keys(zoomState).length > 0 && (preparedChart === null || preparedChart === void 0 ? void 0 : preparedChart.zoom) && (React.createElement(Button, { className: b('reset-zoom-button'), onClick: () => {
291
291
  var _a;
@@ -10,6 +10,6 @@
10
10
  .gcharts-chart__html-layer {
11
11
  display: contents;
12
12
  }
13
- .gcharts-chart__html-layer-item {
14
- transform: var(--g-html-layout-transform);
13
+ .gcharts-chart__html-layer-item_plot {
14
+ transform: var(--g-html-layout-plot-transform);
15
15
  }
@@ -53,6 +53,7 @@ export function useChartInnerProps(props) {
53
53
  const preparedTitle = await getPreparedTitle({
54
54
  title: data.title,
55
55
  chartWidth: width,
56
+ chartHeight: height,
56
57
  chartMargin: (_a = data.chart) === null || _a === void 0 ? void 0 : _a.margin,
57
58
  });
58
59
  const preparedChart = getPreparedChart({
@@ -203,7 +204,6 @@ export function useChartInnerProps(props) {
203
204
  getYAxisWidth,
204
205
  legendConfig,
205
206
  });
206
- //end
207
207
  const newStateValue = {
208
208
  allPreparedSeries,
209
209
  boundsHeight,
@@ -1,7 +1,8 @@
1
1
  import type { PreparedTitle } from '../../../hooks/types';
2
2
  import type { ChartData, ChartMargin } from '../../../types';
3
- export declare const getPreparedTitle: ({ title, chartWidth, chartMargin, }: {
3
+ export declare const getPreparedTitle: ({ title, chartWidth, chartHeight, chartMargin, }: {
4
4
  title: ChartData["title"];
5
5
  chartWidth: number;
6
+ chartHeight: number;
6
7
  chartMargin?: Partial<ChartMargin>;
7
8
  }) => Promise<PreparedTitle | undefined>;
@@ -1,26 +1,64 @@
1
- import get from 'lodash/get';
2
- import { getTextSizeFn, getTextWithElipsis, wrapText } from '../../../core/utils';
1
+ import { calculateNumericProperty, getLabelsSize, getTextSizeFn, getTextWithElipsis, wrapText, } from '../../../core/utils';
3
2
  const DEFAULT_TITLE_FONT_SIZE = '15px';
4
3
  const DEFAULT_TITLE_MARGIN = 10;
5
- export const getPreparedTitle = async ({ title, chartWidth, chartMargin, }) => {
6
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
4
+ const DEFAULT_TITLE_MAX_HEIGHT = '50%';
5
+ export const getPreparedTitle = async ({ title, chartWidth, chartHeight, chartMargin, }) => {
6
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
7
+ const titleText = title === null || title === void 0 ? void 0 : title.text;
8
+ if (!titleText) {
9
+ return undefined;
10
+ }
7
11
  const chartMarginTop = (_a = chartMargin === null || chartMargin === void 0 ? void 0 : chartMargin.top) !== null && _a !== void 0 ? _a : 0;
8
12
  const chartMarginLeft = (_b = chartMargin === null || chartMargin === void 0 ? void 0 : chartMargin.left) !== null && _b !== void 0 ? _b : 0;
9
13
  const chartMarginRight = (_c = chartMargin === null || chartMargin === void 0 ? void 0 : chartMargin.right) !== null && _c !== void 0 ? _c : 0;
10
- const titleText = get(title, 'text');
11
14
  const titleStyle = {
12
15
  fontSize: (_e = (_d = title === null || title === void 0 ? void 0 : title.style) === null || _d === void 0 ? void 0 : _d.fontSize) !== null && _e !== void 0 ? _e : DEFAULT_TITLE_FONT_SIZE,
13
16
  fontWeight: (_g = (_f = title === null || title === void 0 ? void 0 : title.style) === null || _f === void 0 ? void 0 : _f.fontWeight) !== null && _g !== void 0 ? _g : 'var(--g-text-subheader-font-weight)',
14
17
  fontColor: (_j = (_h = title === null || title === void 0 ? void 0 : title.style) === null || _h === void 0 ? void 0 : _h.fontColor) !== null && _j !== void 0 ? _j : 'var(--g-color-text-primary)',
15
18
  };
16
- if (!titleText) {
17
- return undefined;
19
+ const usableWidth = chartWidth - chartMarginLeft - chartMarginRight;
20
+ const titleMargin = (_k = title === null || title === void 0 ? void 0 : title.margin) !== null && _k !== void 0 ? _k : DEFAULT_TITLE_MARGIN;
21
+ if (title === null || title === void 0 ? void 0 : title.html) {
22
+ const titleSize = await getLabelsSize({
23
+ labels: [titleText],
24
+ style: Object.assign(Object.assign({}, titleStyle), { maxWidth: `${usableWidth}px` }),
25
+ html: true,
26
+ });
27
+ const resolvedMaxHeight = calculateNumericProperty({
28
+ value: (_l = title.maxHeight) !== null && _l !== void 0 ? _l : DEFAULT_TITLE_MAX_HEIGHT,
29
+ base: chartHeight,
30
+ });
31
+ const titleHeight = resolvedMaxHeight === undefined
32
+ ? titleSize.maxHeight
33
+ : Math.min(titleSize.maxHeight, resolvedMaxHeight);
34
+ const qa = title === null || title === void 0 ? void 0 : title.qa;
35
+ const qaAttr = qa ? ` data-qa="${qa}"` : '';
36
+ const maxHeightStyle = resolvedMaxHeight === undefined ? '' : ` max-height: ${resolvedMaxHeight}px;`;
37
+ const htmlContent = `<div${qaAttr} style="max-width: ${usableWidth}px; overflow: hidden;${maxHeightStyle}">${titleText}</div>`;
38
+ const titleWidth = Math.min(titleSize.maxWidth, usableWidth);
39
+ return {
40
+ text: titleText,
41
+ style: titleStyle,
42
+ height: titleHeight,
43
+ margin: titleMargin,
44
+ qa,
45
+ html: true,
46
+ htmlElements: [
47
+ {
48
+ x: chartMarginLeft + (usableWidth - titleWidth) / 2,
49
+ y: chartMarginTop,
50
+ content: htmlContent,
51
+ size: { width: titleWidth, height: titleHeight },
52
+ style: titleStyle,
53
+ scope: 'chart',
54
+ },
55
+ ],
56
+ };
18
57
  }
58
+ const xCenter = chartMarginLeft + usableWidth / 2;
19
59
  const getTitleTextSize = getTextSizeFn({ style: titleStyle });
20
- const maxRowCount = (_k = title === null || title === void 0 ? void 0 : title.maxRowCount) !== null && _k !== void 0 ? _k : 1;
60
+ const maxRowCount = (_m = title === null || title === void 0 ? void 0 : title.maxRowCount) !== null && _m !== void 0 ? _m : 1;
21
61
  const contentRows = [];
22
- const usableWidth = chartWidth - chartMarginLeft - chartMarginRight;
23
- const xCenter = chartMarginLeft + usableWidth / 2;
24
62
  if (maxRowCount > 1) {
25
63
  let titleTextRows = await wrapText({
26
64
  text: titleText,
@@ -33,7 +71,7 @@ export const getPreparedTitle = async ({ title, chartWidth, chartMargin, }) => {
33
71
  acc.push(row);
34
72
  }
35
73
  else {
36
- acc[maxRowCount - 1].text += row.text;
74
+ acc[maxRowCount - 1] = Object.assign(Object.assign({}, acc[maxRowCount - 1]), { text: acc[maxRowCount - 1].text + row.text });
37
75
  }
38
76
  return acc;
39
77
  }, []);
@@ -71,12 +109,11 @@ export const getPreparedTitle = async ({ title, chartWidth, chartMargin, }) => {
71
109
  });
72
110
  }
73
111
  const totalTextHeight = contentRows.reduce((acc, row) => acc + row.size.height, 0);
74
- const titleHeight = totalTextHeight;
75
112
  return {
76
113
  text: titleText,
77
114
  style: titleStyle,
78
- height: titleHeight,
79
- margin: (_l = title === null || title === void 0 ? void 0 : title.margin) !== null && _l !== void 0 ? _l : DEFAULT_TITLE_MARGIN,
115
+ height: totalTextHeight,
116
+ margin: titleMargin,
80
117
  qa: title === null || title === void 0 ? void 0 : title.qa,
81
118
  contentRows,
82
119
  };
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { PreparedTitle } from '../../hooks';
3
- type Props = PreparedTitle;
4
- export declare const Title: (props: Props) => React.JSX.Element;
3
+ type Props = PreparedTitle & {
4
+ htmlLayout?: HTMLElement | null;
5
+ };
6
+ export declare const Title: (props: Props) => React.JSX.Element | null;
5
7
  export {};
@@ -1,9 +1,16 @@
1
1
  import React from 'react';
2
+ import { HtmlLayer } from '../../hooks/useShapes/HtmlLayer';
2
3
  export const Title = (props) => {
3
- const { style, qa, contentRows } = props;
4
+ const { style, qa, contentRows, html, htmlElements, htmlLayout } = props;
5
+ if (html) {
6
+ if (!htmlLayout || !htmlElements) {
7
+ return null;
8
+ }
9
+ return React.createElement(HtmlLayer, { htmlLayout: htmlLayout, preparedData: { htmlElements } });
10
+ }
4
11
  return (React.createElement("text", { dominantBaseline: "hanging", textAnchor: "middle", style: {
5
12
  fill: style === null || style === void 0 ? void 0 : style.fontColor,
6
13
  fontSize: style === null || style === void 0 ? void 0 : style.fontSize,
7
14
  fontWeight: style === null || style === void 0 ? void 0 : style.fontWeight,
8
- }, "data-qa": qa }, contentRows.map((row, i) => (React.createElement("tspan", { key: i, x: row.x, y: row.y, dominantBaseline: "hanging", dangerouslySetInnerHTML: { __html: row.text } })))));
15
+ }, "data-qa": qa }, contentRows === null || contentRows === void 0 ? void 0 : contentRows.map((row, i) => (React.createElement("tspan", { key: i, x: row.x, y: row.y, dominantBaseline: "hanging", dangerouslySetInnerHTML: { __html: row.text } })))));
9
16
  };
@@ -0,0 +1,12 @@
1
+ export declare const annotationLabelDefaults: {
2
+ style: {
3
+ fontSize: string;
4
+ fontColor: string;
5
+ };
6
+ };
7
+ export declare const annotationPopupDefaults: {
8
+ backgroundColor: string;
9
+ borderRadius: number;
10
+ offset: number;
11
+ padding: [number, number];
12
+ };
@@ -0,0 +1,12 @@
1
+ export const annotationLabelDefaults = {
2
+ style: {
3
+ fontSize: '13px',
4
+ fontColor: 'var(--g-color-text-light-primary)',
5
+ },
6
+ };
7
+ export const annotationPopupDefaults = {
8
+ backgroundColor: 'var(--g-color-base-float-heavy)',
9
+ borderRadius: 4,
10
+ offset: 5,
11
+ padding: [4, 8],
12
+ };
@@ -1,3 +1,4 @@
1
+ export * from './annotation';
1
2
  export * from './axis';
2
3
  export * from './brush';
3
4
  export * from './data-labels';
@@ -1,3 +1,4 @@
1
+ export * from './annotation';
1
2
  export * from './axis';
2
3
  export * from './brush';
3
4
  export * from './data-labels';
@@ -4,4 +4,4 @@ export declare const DEFAULT_LEGEND_SYMBOL_SIZE = 8;
4
4
  export declare const DEFAULT_LEGEND_SYMBOL_PADDING = 5;
5
5
  export declare const DEFAULT_DATALABELS_PADDING = 5;
6
6
  export declare const DEFAULT_HALO_OPTIONS: Required<Halo>;
7
- export declare const DEFAULT_POINT_MARKER_OPTIONS: Omit<Required<PointMarkerOptions>, 'enabled'>;
7
+ export declare const DEFAULT_POINT_MARKER_OPTIONS: Omit<Required<PointMarkerOptions>, 'color' | 'enabled'>;
@@ -7,8 +7,8 @@ export const DEFAULT_HALO_OPTIONS = {
7
7
  size: 6,
8
8
  };
9
9
  export const DEFAULT_POINT_MARKER_OPTIONS = {
10
- radius: 4,
11
10
  borderColor: '',
12
11
  borderWidth: 0,
12
+ radius: 4,
13
13
  symbol: 'circle',
14
14
  };
@@ -0,0 +1,12 @@
1
+ import type { ChartAnnotationLabel, ChartAnnotationPopup } from '../../types';
2
+ import type { PreparedAnnotation } from './types';
3
+ type AnnotationOptionsLabel = Omit<ChartAnnotationLabel, 'text'> | undefined;
4
+ export declare function prepareAnnotation(args: {
5
+ annotation: {
6
+ label: ChartAnnotationLabel;
7
+ popup?: ChartAnnotationPopup;
8
+ };
9
+ optionsLabel?: AnnotationOptionsLabel;
10
+ optionsPopup?: ChartAnnotationPopup;
11
+ }): Promise<PreparedAnnotation>;
12
+ export {};
@@ -0,0 +1,31 @@
1
+ import { annotationLabelDefaults, annotationPopupDefaults } from '../constants';
2
+ import { getTextSizeFn } from '../utils';
3
+ function resolvePadding(padding) {
4
+ if (padding === undefined) {
5
+ return annotationPopupDefaults.padding;
6
+ }
7
+ if (typeof padding === 'number') {
8
+ return [padding, padding];
9
+ }
10
+ return padding;
11
+ }
12
+ export async function prepareAnnotation(args) {
13
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
14
+ const { annotation, optionsLabel, optionsPopup } = args;
15
+ const style = Object.assign(Object.assign(Object.assign({}, annotationLabelDefaults.style), optionsLabel === null || optionsLabel === void 0 ? void 0 : optionsLabel.style), annotation.label.style);
16
+ const getTextSize = getTextSizeFn({ style });
17
+ const textSize = await getTextSize(annotation.label.text);
18
+ return {
19
+ label: {
20
+ style,
21
+ text: annotation.label.text,
22
+ size: { height: textSize.height, width: textSize.width },
23
+ },
24
+ popup: {
25
+ backgroundColor: (_c = (_b = (_a = annotation.popup) === null || _a === void 0 ? void 0 : _a.backgroundColor) !== null && _b !== void 0 ? _b : optionsPopup === null || optionsPopup === void 0 ? void 0 : optionsPopup.backgroundColor) !== null && _c !== void 0 ? _c : annotationPopupDefaults.backgroundColor,
26
+ borderRadius: (_f = (_e = (_d = annotation.popup) === null || _d === void 0 ? void 0 : _d.borderRadius) !== null && _e !== void 0 ? _e : optionsPopup === null || optionsPopup === void 0 ? void 0 : optionsPopup.borderRadius) !== null && _f !== void 0 ? _f : annotationPopupDefaults.borderRadius,
27
+ offset: (_j = (_h = (_g = annotation.popup) === null || _g === void 0 ? void 0 : _g.offset) !== null && _h !== void 0 ? _h : optionsPopup === null || optionsPopup === void 0 ? void 0 : optionsPopup.offset) !== null && _j !== void 0 ? _j : annotationPopupDefaults.offset,
28
+ padding: resolvePadding((_l = (_k = annotation.popup) === null || _k === void 0 ? void 0 : _k.padding) !== null && _l !== void 0 ? _l : optionsPopup === null || optionsPopup === void 0 ? void 0 : optionsPopup.padding),
29
+ },
30
+ };
31
+ }
@@ -1,5 +1,21 @@
1
1
  import type { AreaSeries, AreaSeriesData, BarXSeries, BarXSeriesData, BarYSeries, BarYSeriesData, BaseTextStyle, ChartLegend, ChartSeries, ChartSeriesRangeSliderOptions, ConnectorCurve, ConnectorShape, FunnelSeries, FunnelSeriesData, HeatmapSeries, HeatmapSeriesData, LineSeries, LineSeriesData, LineSeriesLineBaseStyle, PathLegendSymbolOptions, PieSeries, PieSeriesData, RadarSeries, RadarSeriesCategory, RadarSeriesData, RectLegendSymbolOptions, SankeySeries, SankeySeriesData, ScatterSeries, ScatterSeriesData, SymbolLegendSymbolOptions, TreemapSeries, TreemapSeriesData, ValueFormat, WaterfallSeries, WaterfallSeriesData, XRangeSeries, XRangeSeriesData } from '../../types';
2
2
  import type { DashStyle, LayoutAlgorithm, LineCap, LineJoin, SeriesOptionsDefaults, SymbolType } from '../constants';
3
+ export type PreparedAnnotation = {
4
+ label: {
5
+ size: {
6
+ height: number;
7
+ width: number;
8
+ };
9
+ style: BaseTextStyle;
10
+ text: string;
11
+ };
12
+ popup: {
13
+ backgroundColor: string;
14
+ borderRadius: number;
15
+ offset: number;
16
+ padding: [number, number];
17
+ };
18
+ };
3
19
  export type RectLegendSymbol = {
4
20
  shape: 'rect';
5
21
  } & Required<RectLegendSymbolOptions>;
@@ -0,0 +1,45 @@
1
+ import type { BaseTextStyle } from './base';
2
+ export interface ChartAnnotationLabel {
3
+ /** Annotation text */
4
+ text: string;
5
+ /** Text style. fontColor defaults to 'var(--g-color-text-light-primary)' */
6
+ style?: Partial<BaseTextStyle>;
7
+ }
8
+ export interface ChartAnnotationPopup {
9
+ /**
10
+ * Background color of the popup.
11
+ * @default 'var(--g-color-base-float-heavy)'
12
+ */
13
+ backgroundColor?: string;
14
+ /**
15
+ * Border radius of the popup.
16
+ * @default 4
17
+ */
18
+ borderRadius?: number;
19
+ /**
20
+ * Distance in pixels between the anchor point and the popup edge along the main axis.
21
+ * The main axis depends on the automatically chosen placement:
22
+ * for top/bottom — vertical distance, for left/right — horizontal distance.
23
+ * @default 5
24
+ */
25
+ offset?: number;
26
+ /**
27
+ * Popup padding. Number or [vertical, horizontal].
28
+ * @default [4, 8]
29
+ */
30
+ padding?: number | [number, number];
31
+ }
32
+ /** Default annotation settings applied to all data points in a series */
33
+ export interface ChartAnnotationSeriesOptions {
34
+ /** Default label style for annotations */
35
+ label?: Omit<ChartAnnotationLabel, 'text'>;
36
+ /** Default popup settings for annotations */
37
+ popup?: ChartAnnotationPopup;
38
+ }
39
+ /** Annotation for a specific data point. Renders as a popup with text label near the data point. */
40
+ export interface ChartPointAnnotation {
41
+ /** Text content and style of the annotation */
42
+ label: ChartAnnotationLabel;
43
+ /** Visual settings for the annotation popup container (background, padding, etc.) */
44
+ popup?: ChartAnnotationPopup;
45
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,6 @@
1
1
  import type { SERIES_TYPE } from '../../constants';
2
2
  import type { MeaningfulAny } from '../misc';
3
+ import type { ChartPointAnnotation } from './annotation';
3
4
  import type { BaseSeries, BaseSeriesData, BaseSeriesLegend } from './base';
4
5
  import type { RectLegendSymbolOptions } from './legend';
5
6
  import type { PointMarkerOptions } from './marker';
@@ -23,6 +24,8 @@ export interface AreaSeriesData<T = MeaningfulAny> extends BaseSeriesData<T> {
23
24
  label?: string | number;
24
25
  /** Individual marker options for the point. */
25
26
  marker?: {
27
+ /** Fill color of the marker for this point */
28
+ color?: string;
26
29
  /** States for a single point marker. */
27
30
  states?: {
28
31
  /** The normal state of a single point marker. */
@@ -35,6 +38,11 @@ export interface AreaSeriesData<T = MeaningfulAny> extends BaseSeriesData<T> {
35
38
  };
36
39
  };
37
40
  };
41
+ /**
42
+ * Annotation displayed near this data point as a bubble with text label and optional marker.
43
+ * Useful for highlighting specific values, events, or adding contextual notes.
44
+ */
45
+ annotation?: ChartPointAnnotation;
38
46
  }
39
47
  export type AreaMarkerSymbol = 'circle' | 'square';
40
48
  export interface AreaMarkerOptions extends PointMarkerOptions {
@@ -1,5 +1,6 @@
1
1
  import type { SERIES_TYPE } from '../../constants';
2
2
  import type { MeaningfulAny } from '../misc';
3
+ import type { ChartPointAnnotation } from './annotation';
3
4
  import type { BaseSeries, BaseSeriesData, BaseSeriesLegend } from './base';
4
5
  import type { RectLegendSymbolOptions } from './legend';
5
6
  import type { ChartSeriesOptions, ChartSeriesRangeSliderOptions } from './series';
@@ -27,6 +28,11 @@ export interface BarXSeriesData<T = MeaningfulAny> extends BaseSeriesData<T> {
27
28
  label?: string | number;
28
29
  /** Individual opacity for the bar-x column. */
29
30
  opacity?: number;
31
+ /**
32
+ * Annotation displayed near this data point as a bubble with text label.
33
+ * Useful for highlighting specific values, events, or adding contextual notes.
34
+ */
35
+ annotation?: ChartPointAnnotation;
30
36
  }
31
37
  export interface BarXSeries<T = MeaningfulAny> extends BaseSeries {
32
38
  type: typeof SERIES_TYPE.BarX;
@@ -1,5 +1,6 @@
1
1
  import type { DashStyle, LineCap, LineJoin, SERIES_TYPE } from '../../constants';
2
2
  import type { MeaningfulAny } from '../misc';
3
+ import type { ChartPointAnnotation } from './annotation';
3
4
  import type { BaseSeries, BaseSeriesData, BaseSeriesLegend } from './base';
4
5
  import type { RectLegendSymbolOptions } from './legend';
5
6
  import type { PointMarkerOptions } from './marker';
@@ -22,12 +23,19 @@ export interface LineSeriesData<T = MeaningfulAny> extends BaseSeriesData<T> {
22
23
  /** Data label value of the point. If not specified, the y value is used. */
23
24
  label?: string | number;
24
25
  marker?: {
26
+ /** Fill color of the marker for this point */
27
+ color?: string;
25
28
  states?: {
26
29
  normal?: {
27
30
  enabled: boolean;
28
31
  };
29
32
  };
30
33
  };
34
+ /**
35
+ * Annotation displayed near this data point as a bubble with text label and optional marker.
36
+ * Useful for highlighting specific values, events, or adding contextual notes.
37
+ */
38
+ annotation?: ChartPointAnnotation;
31
39
  }
32
40
  export interface LineSeriesLineBaseStyle {
33
41
  /**