@gravity-ui/charts 1.46.1 → 1.48.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 (195) hide show
  1. package/dist/cjs/components/AxisX/prepare-axis-data.js +9 -6
  2. package/dist/cjs/components/AxisY/prepare-axis-data.js +11 -4
  3. package/dist/cjs/components/ChartInner/index.js +1 -0
  4. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +1 -0
  5. package/dist/cjs/components/ChartInner/useChartInnerProps.js +12 -5
  6. package/dist/cjs/core/axes/types.d.ts +4 -2
  7. package/dist/cjs/core/axes/x-axis.js +2 -0
  8. package/dist/cjs/core/axes/y-axis.js +2 -0
  9. package/dist/cjs/core/brush/index.d.ts +2 -0
  10. package/dist/cjs/core/brush/index.js +2 -0
  11. package/dist/cjs/{hooks/useBrush → core/brush}/types.d.ts +2 -2
  12. package/dist/{esm/hooks/useBrush → cjs/core/brush}/utils.d.ts +1 -1
  13. package/dist/cjs/core/chart/index.d.ts +1 -0
  14. package/dist/cjs/core/chart/index.js +1 -0
  15. package/dist/cjs/core/chart/types.d.ts +8 -0
  16. package/dist/cjs/core/index.d.ts +3 -0
  17. package/dist/cjs/core/index.js +3 -0
  18. package/dist/cjs/core/layout/chart-dimensions.d.ts +1 -1
  19. package/dist/cjs/core/range-slider/index.d.ts +2 -0
  20. package/dist/cjs/core/range-slider/index.js +2 -0
  21. package/dist/cjs/core/range-slider/types.d.ts +4 -0
  22. package/dist/cjs/{hooks/useRangeSlider → core/range-slider}/utils.d.ts +5 -5
  23. package/dist/cjs/{hooks/useRangeSlider → core/range-slider}/utils.js +1 -1
  24. package/dist/cjs/core/scales/x-scale.d.ts +2 -2
  25. package/dist/cjs/core/scales/y-scale.js +21 -0
  26. package/dist/cjs/core/series/prepare-legend.d.ts +1 -1
  27. package/dist/cjs/core/series/prepare-scatter.js +11 -3
  28. package/dist/cjs/core/series/types.d.ts +8 -0
  29. package/dist/cjs/core/shapes/area/prepare-data.js +8 -50
  30. package/dist/cjs/core/shapes/area/renderer.js +8 -14
  31. package/dist/cjs/core/shapes/area/types.d.ts +6 -5
  32. package/dist/cjs/core/shapes/bar-x/prepare-data.js +49 -35
  33. package/dist/cjs/core/shapes/bar-x/renderer.js +6 -12
  34. package/dist/cjs/core/shapes/bar-y/renderer.js +6 -12
  35. package/dist/cjs/core/shapes/data-labels.d.ts +15 -0
  36. package/dist/cjs/core/shapes/data-labels.js +15 -0
  37. package/dist/cjs/core/shapes/funnel/renderer.js +6 -11
  38. package/dist/cjs/core/shapes/heatmap/prepare-data.js +1 -0
  39. package/dist/cjs/core/shapes/heatmap/renderer.js +6 -11
  40. package/dist/cjs/core/shapes/heatmap/types.d.ts +1 -0
  41. package/dist/cjs/core/shapes/line/prepare-data.js +22 -59
  42. package/dist/cjs/core/shapes/line/renderer.js +7 -13
  43. package/dist/cjs/core/shapes/line/types.d.ts +5 -4
  44. package/dist/cjs/core/shapes/radar/renderer.js +8 -12
  45. package/dist/cjs/core/shapes/sankey/renderer.js +6 -12
  46. package/dist/cjs/core/shapes/scatter/prepare-data.d.ts +5 -2
  47. package/dist/cjs/core/shapes/scatter/prepare-data.js +43 -4
  48. package/dist/cjs/core/shapes/scatter/renderer.d.ts +2 -2
  49. package/dist/cjs/core/shapes/scatter/renderer.js +9 -1
  50. package/dist/cjs/core/shapes/scatter/types.d.ts +6 -1
  51. package/dist/cjs/core/shapes/utils.d.ts +24 -0
  52. package/dist/cjs/core/shapes/utils.js +48 -0
  53. package/dist/cjs/core/shapes/waterfall/renderer.js +6 -12
  54. package/dist/cjs/core/shapes/x-range/renderer.js +7 -13
  55. package/dist/cjs/core/types/chart/axis.d.ts +20 -0
  56. package/dist/cjs/core/types/chart/base.d.ts +17 -3
  57. package/dist/cjs/core/types/chart/scatter.d.ts +2 -0
  58. package/dist/cjs/core/types/chart/tooltip.d.ts +3 -3
  59. package/dist/cjs/core/types/formatter.d.ts +1 -40
  60. package/dist/cjs/core/utils/data-labels.d.ts +46 -0
  61. package/dist/cjs/core/utils/data-labels.js +64 -0
  62. package/dist/cjs/core/utils/format.d.ts +2 -2
  63. package/dist/cjs/core/utils/get-closest-data.js +13 -8
  64. package/dist/cjs/core/utils/index.d.ts +1 -0
  65. package/dist/cjs/core/utils/index.js +1 -0
  66. package/dist/cjs/core/zoom/index.d.ts +2 -0
  67. package/dist/cjs/core/zoom/index.js +2 -0
  68. package/dist/{esm/hooks/useZoom → cjs/core/zoom}/utils.d.ts +3 -3
  69. package/dist/{esm/hooks/useZoom → cjs/core/zoom}/utils.js +1 -1
  70. package/dist/cjs/core/zoom/zoom.d.ts +3 -3
  71. package/dist/cjs/hooks/index.d.ts +2 -2
  72. package/dist/cjs/hooks/index.js +2 -2
  73. package/dist/cjs/hooks/types.d.ts +2 -8
  74. package/dist/cjs/hooks/useBrush/index.d.ts +1 -1
  75. package/dist/cjs/hooks/useBrush/index.js +1 -1
  76. package/dist/cjs/hooks/useRangeSlider/index.js +3 -3
  77. package/dist/cjs/hooks/useRangeSlider/types.d.ts +5 -7
  78. package/dist/cjs/hooks/useShapes/index.d.ts +1 -1
  79. package/dist/cjs/hooks/useShapes/index.js +5 -3
  80. package/dist/cjs/hooks/useShapes/scatter/index.d.ts +2 -2
  81. package/dist/cjs/hooks/useShapes/scatter/index.js +4 -1
  82. package/dist/cjs/hooks/useShapes/styles.css +8 -25
  83. package/dist/cjs/hooks/useShapes/utils.d.ts +1 -1
  84. package/dist/cjs/hooks/useZoom/index.d.ts +1 -1
  85. package/dist/cjs/hooks/useZoom/index.js +1 -1
  86. package/dist/cjs/index.d.ts +1 -0
  87. package/dist/cjs/index.js +1 -0
  88. package/dist/cjs/libs/format-number/index.js +82 -14
  89. package/dist/cjs/libs/format-number/presets.d.ts +40 -0
  90. package/dist/cjs/libs/format-number/presets.js +66 -0
  91. package/dist/cjs/libs/format-number/types.d.ts +82 -3
  92. package/dist/esm/components/AxisX/prepare-axis-data.js +9 -6
  93. package/dist/esm/components/AxisY/prepare-axis-data.js +11 -4
  94. package/dist/esm/components/ChartInner/index.js +1 -0
  95. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +1 -0
  96. package/dist/esm/components/ChartInner/useChartInnerProps.js +12 -5
  97. package/dist/esm/core/axes/types.d.ts +4 -2
  98. package/dist/esm/core/axes/x-axis.js +2 -0
  99. package/dist/esm/core/axes/y-axis.js +2 -0
  100. package/dist/esm/core/brush/index.d.ts +2 -0
  101. package/dist/esm/core/brush/index.js +2 -0
  102. package/dist/esm/{hooks/useBrush → core/brush}/types.d.ts +2 -2
  103. package/dist/esm/core/brush/types.js +1 -0
  104. package/dist/{cjs/hooks/useBrush → esm/core/brush}/utils.d.ts +1 -1
  105. package/dist/esm/core/chart/index.d.ts +1 -0
  106. package/dist/esm/core/chart/index.js +1 -0
  107. package/dist/esm/core/chart/types.d.ts +8 -0
  108. package/dist/esm/core/chart/types.js +1 -0
  109. package/dist/esm/core/index.d.ts +3 -0
  110. package/dist/esm/core/index.js +3 -0
  111. package/dist/esm/core/layout/chart-dimensions.d.ts +1 -1
  112. package/dist/esm/core/range-slider/index.d.ts +2 -0
  113. package/dist/esm/core/range-slider/index.js +2 -0
  114. package/dist/esm/core/range-slider/types.d.ts +4 -0
  115. package/dist/esm/core/range-slider/types.js +1 -0
  116. package/dist/esm/{hooks/useRangeSlider → core/range-slider}/utils.d.ts +5 -5
  117. package/dist/esm/{hooks/useRangeSlider → core/range-slider}/utils.js +1 -1
  118. package/dist/esm/core/scales/x-scale.d.ts +2 -2
  119. package/dist/esm/core/scales/y-scale.js +21 -0
  120. package/dist/esm/core/series/prepare-legend.d.ts +1 -1
  121. package/dist/esm/core/series/prepare-scatter.js +11 -3
  122. package/dist/esm/core/series/types.d.ts +8 -0
  123. package/dist/esm/core/shapes/area/prepare-data.js +8 -50
  124. package/dist/esm/core/shapes/area/renderer.js +8 -14
  125. package/dist/esm/core/shapes/area/types.d.ts +6 -5
  126. package/dist/esm/core/shapes/bar-x/prepare-data.js +49 -35
  127. package/dist/esm/core/shapes/bar-x/renderer.js +6 -12
  128. package/dist/esm/core/shapes/bar-y/renderer.js +6 -12
  129. package/dist/esm/core/shapes/data-labels.d.ts +15 -0
  130. package/dist/esm/core/shapes/data-labels.js +15 -0
  131. package/dist/esm/core/shapes/funnel/renderer.js +6 -11
  132. package/dist/esm/core/shapes/heatmap/prepare-data.js +1 -0
  133. package/dist/esm/core/shapes/heatmap/renderer.js +6 -11
  134. package/dist/esm/core/shapes/heatmap/types.d.ts +1 -0
  135. package/dist/esm/core/shapes/line/prepare-data.js +22 -59
  136. package/dist/esm/core/shapes/line/renderer.js +7 -13
  137. package/dist/esm/core/shapes/line/types.d.ts +5 -4
  138. package/dist/esm/core/shapes/radar/renderer.js +8 -12
  139. package/dist/esm/core/shapes/sankey/renderer.js +6 -12
  140. package/dist/esm/core/shapes/scatter/prepare-data.d.ts +5 -2
  141. package/dist/esm/core/shapes/scatter/prepare-data.js +43 -4
  142. package/dist/esm/core/shapes/scatter/renderer.d.ts +2 -2
  143. package/dist/esm/core/shapes/scatter/renderer.js +9 -1
  144. package/dist/esm/core/shapes/scatter/types.d.ts +6 -1
  145. package/dist/esm/core/shapes/utils.d.ts +24 -0
  146. package/dist/esm/core/shapes/utils.js +48 -0
  147. package/dist/esm/core/shapes/waterfall/renderer.js +6 -12
  148. package/dist/esm/core/shapes/x-range/renderer.js +7 -13
  149. package/dist/esm/core/types/chart/axis.d.ts +20 -0
  150. package/dist/esm/core/types/chart/base.d.ts +17 -3
  151. package/dist/esm/core/types/chart/scatter.d.ts +2 -0
  152. package/dist/esm/core/types/chart/tooltip.d.ts +3 -3
  153. package/dist/esm/core/types/formatter.d.ts +1 -40
  154. package/dist/esm/core/utils/data-labels.d.ts +46 -0
  155. package/dist/esm/core/utils/data-labels.js +64 -0
  156. package/dist/esm/core/utils/format.d.ts +2 -2
  157. package/dist/esm/core/utils/get-closest-data.js +13 -8
  158. package/dist/esm/core/utils/index.d.ts +1 -0
  159. package/dist/esm/core/utils/index.js +1 -0
  160. package/dist/esm/core/zoom/index.d.ts +2 -0
  161. package/dist/esm/core/zoom/index.js +2 -0
  162. package/dist/esm/core/zoom/types.js +1 -0
  163. package/dist/{cjs/hooks/useZoom → esm/core/zoom}/utils.d.ts +3 -3
  164. package/dist/{cjs/hooks/useZoom → esm/core/zoom}/utils.js +1 -1
  165. package/dist/esm/core/zoom/zoom.d.ts +3 -3
  166. package/dist/esm/hooks/index.d.ts +2 -2
  167. package/dist/esm/hooks/index.js +2 -2
  168. package/dist/esm/hooks/types.d.ts +2 -8
  169. package/dist/esm/hooks/useBrush/index.d.ts +1 -1
  170. package/dist/esm/hooks/useBrush/index.js +1 -1
  171. package/dist/esm/hooks/useRangeSlider/index.js +3 -3
  172. package/dist/esm/hooks/useRangeSlider/types.d.ts +5 -7
  173. package/dist/esm/hooks/useShapes/index.d.ts +1 -1
  174. package/dist/esm/hooks/useShapes/index.js +5 -3
  175. package/dist/esm/hooks/useShapes/scatter/index.d.ts +2 -2
  176. package/dist/esm/hooks/useShapes/scatter/index.js +4 -1
  177. package/dist/esm/hooks/useShapes/styles.css +8 -25
  178. package/dist/esm/hooks/useShapes/utils.d.ts +1 -1
  179. package/dist/esm/hooks/useZoom/index.d.ts +1 -1
  180. package/dist/esm/hooks/useZoom/index.js +1 -1
  181. package/dist/esm/index.d.ts +1 -0
  182. package/dist/esm/index.js +1 -0
  183. package/dist/esm/libs/format-number/index.js +82 -14
  184. package/dist/esm/libs/format-number/presets.d.ts +40 -0
  185. package/dist/esm/libs/format-number/presets.js +66 -0
  186. package/dist/esm/libs/format-number/types.d.ts +82 -3
  187. package/package.json +1 -1
  188. /package/dist/cjs/{hooks/useBrush → core/brush}/types.js +0 -0
  189. /package/dist/cjs/{hooks/useBrush → core/brush}/utils.js +0 -0
  190. /package/dist/cjs/{hooks/useZoom → core/chart}/types.js +0 -0
  191. /package/dist/{esm/hooks/useBrush → cjs/core/range-slider}/types.js +0 -0
  192. /package/dist/cjs/{hooks/useZoom → core/zoom}/types.d.ts +0 -0
  193. /package/dist/{esm/hooks/useZoom → cjs/core/zoom}/types.js +0 -0
  194. /package/dist/esm/{hooks/useBrush → core/brush}/utils.js +0 -0
  195. /package/dist/esm/{hooks/useZoom → core/zoom}/types.d.ts +0 -0
@@ -3,37 +3,59 @@ import get from 'lodash/get';
3
3
  import { prepareAnnotation } from '../../series/prepare-annotation';
4
4
  import { getSeriesStackId } from '../../series/utils';
5
5
  import { MIN_BAR_GAP, MIN_BAR_GROUP_GAP, MIN_BAR_WIDTH } from '../../shapes/bar-constants';
6
- import { getDataCategoryValue, getLabelsSize } from '../../utils';
6
+ import { getDataCategoryValue, getLabelsSize, getTextSizeFn } from '../../utils';
7
7
  import { getBandSize } from '../../utils/band-size';
8
8
  import { getFormattedValue } from '../../utils/format';
9
9
  const isSeriesDataValid = (d) => d.y !== null;
10
10
  async function getLabelData(d, xMax) {
11
11
  var _a;
12
12
  if (!d.series.dataLabels.enabled) {
13
- return undefined;
13
+ return {};
14
14
  }
15
15
  const text = getFormattedValue(Object.assign({ value: (_a = d.data.label) !== null && _a !== void 0 ? _a : d.data.y }, d.series.dataLabels));
16
16
  const style = d.series.dataLabels.style;
17
- const html = d.series.dataLabels.html;
18
- const { maxHeight: height, maxWidth: width } = await getLabelsSize({
19
- labels: [text],
20
- style,
21
- html,
22
- });
23
- let y = Math.max(height, d.y - d.series.dataLabels.padding);
24
- if (d.series.dataLabels.inside) {
25
- y = d.y + d.height / 2;
17
+ if (d.series.dataLabels.html) {
18
+ const { maxHeight: height, maxWidth: width } = await getLabelsSize({
19
+ labels: [text],
20
+ style,
21
+ html: true,
22
+ });
23
+ let y = Math.max(height, d.y - d.series.dataLabels.padding);
24
+ if (d.series.dataLabels.inside) {
25
+ y = d.y + d.height / 2;
26
+ }
27
+ const centerX = Math.min(xMax - width / 2, Math.max(width / 2, d.x + d.width / 2));
28
+ return {
29
+ htmlLabel: {
30
+ content: text,
31
+ x: centerX - width / 2,
32
+ y: y - height,
33
+ size: { width, height },
34
+ style,
35
+ },
36
+ };
37
+ }
38
+ else {
39
+ const getTextSize = getTextSizeFn({ style });
40
+ const { width, height, hangingOffset } = await getTextSize(text);
41
+ let y = Math.max(hangingOffset, d.y - height + hangingOffset - d.series.dataLabels.padding);
42
+ if (d.series.dataLabels.inside) {
43
+ const centerY = d.y + d.height / 2;
44
+ y = Math.min(d.y + d.height - height + hangingOffset, centerY - height / 2 + hangingOffset);
45
+ }
46
+ const centerX = Math.min(xMax - width / 2, Math.max(width / 2, d.x + d.width / 2));
47
+ return {
48
+ svgLabel: {
49
+ text,
50
+ x: centerX,
51
+ y,
52
+ style,
53
+ size: { width, height, hangingOffset },
54
+ textAnchor: 'middle',
55
+ series: d.series,
56
+ },
57
+ };
26
58
  }
27
- const centerX = Math.min(xMax - width / 2, Math.max(width / 2, d.x + d.width / 2));
28
- return {
29
- text,
30
- x: html ? centerX - width / 2 : centerX,
31
- y: html ? y - height : y,
32
- style,
33
- size: { width, height },
34
- textAnchor: 'middle',
35
- series: d.series,
36
- };
37
59
  }
38
60
  export const prepareBarXData = async (args) => {
39
61
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
@@ -232,20 +254,12 @@ export const prepareBarXData = async (args) => {
232
254
  if (barData.series.dataLabels.enabled &&
233
255
  !isRangeSlider &&
234
256
  (!isBarOutsideBounds || isZeroValue)) {
235
- const label = await getLabelData(barData, xMax);
236
- if (label) {
237
- if (barData.series.dataLabels.html) {
238
- barData.htmlLabels.push({
239
- x: label.x,
240
- y: label.y,
241
- content: label.text,
242
- size: label.size,
243
- style: label.style,
244
- });
245
- }
246
- else {
247
- barData.svgLabels.push(label);
248
- }
257
+ const { svgLabel, htmlLabel } = await getLabelData(barData, xMax);
258
+ if (svgLabel) {
259
+ barData.svgLabels.push(svgLabel);
260
+ }
261
+ if (htmlLabel) {
262
+ barData.htmlLabels.push(htmlLabel);
249
263
  }
250
264
  }
251
265
  }
@@ -4,6 +4,7 @@ import get from 'lodash/get';
4
4
  import { block } from '../../../utils';
5
5
  import { filterOverlappingLabels } from '../../utils';
6
6
  import { renderAnnotations } from '../annotation';
7
+ import { renderDataLabels } from '../data-labels';
7
8
  import { getRectPath } from '../utils';
8
9
  const b = block('bar-x');
9
10
  export function renderBarX(elements, preparedData, seriesOptions, allowOverlapDataLabels, dispatcher) {
@@ -37,18 +38,11 @@ export function renderBarX(elements, preparedData, seriesOptions, allowOverlapDa
37
38
  if (!allowOverlapDataLabels) {
38
39
  dataLabels = filterOverlappingLabels(dataLabels);
39
40
  }
40
- const labelSelection = svgElement
41
- .selectAll('text')
42
- .data(dataLabels)
43
- .join('text')
44
- .html((d) => d.text)
45
- .attr('class', b('label'))
46
- .attr('x', (d) => d.x)
47
- .attr('y', (d) => d.y)
48
- .attr('text-anchor', (d) => d.textAnchor)
49
- .style('font-size', (d) => d.style.fontSize)
50
- .style('font-weight', (d) => d.style.fontWeight || null)
51
- .style('fill', (d) => d.style.fontColor || null);
41
+ const labelSelection = renderDataLabels({
42
+ container: svgElement,
43
+ data: dataLabels,
44
+ className: b('label'),
45
+ });
52
46
  const annotationAnchors = [];
53
47
  for (const d of preparedData) {
54
48
  if (d.annotation) {
@@ -2,6 +2,7 @@ import { color } from 'd3-color';
2
2
  import { select } from 'd3-selection';
3
3
  import get from 'lodash/get';
4
4
  import { block } from '../../../utils';
5
+ import { renderDataLabels } from '../data-labels';
5
6
  import { getAdjustedRectBorderPath, getAdjustedRectPath } from './utils';
6
7
  const b = block('bar-y');
7
8
  export function renderBarY(elements, preparedData, seriesOptions, dispatcher) {
@@ -31,18 +32,11 @@ export function renderBarY(elements, preparedData, seriesOptions, dispatcher) {
31
32
  .attr('fill-rule', 'evenodd')
32
33
  .attr('opacity', (d) => d.data.opacity || null)
33
34
  .attr('pointer-events', 'none');
34
- const labelSelection = svgElement
35
- .selectAll('text')
36
- .data(dataLabels)
37
- .join('text')
38
- .html((d) => d.text)
39
- .attr('class', b('label'))
40
- .attr('x', (d) => d.x)
41
- .attr('y', (d) => d.y)
42
- .attr('text-anchor', (d) => d.textAnchor)
43
- .style('font-size', (d) => d.style.fontSize)
44
- .style('font-weight', (d) => d.style.fontWeight || null)
45
- .style('fill', (d) => d.style.fontColor || null);
35
+ const labelSelection = renderDataLabels({
36
+ container: svgElement,
37
+ data: dataLabels,
38
+ className: b('label'),
39
+ });
46
40
  const hoverOptions = get(seriesOptions, 'bar-y.states.hover');
47
41
  const inactiveOptions = get(seriesOptions, 'bar-y.states.inactive');
48
42
  function handleShapeHover(data) {
@@ -0,0 +1,15 @@
1
+ import type { Selection } from 'd3-selection';
2
+ import type { BaseTextStyle } from '../types/chart/base';
3
+ type RenderableLabelData = {
4
+ text: string;
5
+ x: number;
6
+ y: number;
7
+ textAnchor: 'start' | 'end' | 'middle';
8
+ style: BaseTextStyle;
9
+ };
10
+ export declare function renderDataLabels<T extends RenderableLabelData>(args: {
11
+ container: Selection<SVGGElement, unknown, null, undefined>;
12
+ data: T[];
13
+ className: string;
14
+ }): Selection<SVGTextElement, T, SVGGElement, unknown>;
15
+ export {};
@@ -0,0 +1,15 @@
1
+ export function renderDataLabels(args) {
2
+ const { container, data, className } = args;
3
+ return container
4
+ .selectAll('text')
5
+ .data(data)
6
+ .join('text')
7
+ .html((d) => d.text)
8
+ .attr('class', className)
9
+ .attr('x', (d) => d.x)
10
+ .attr('y', (d) => d.y)
11
+ .attr('text-anchor', (d) => d.textAnchor)
12
+ .style('font-size', (d) => d.style.fontSize)
13
+ .style('font-weight', (d) => d.style.fontWeight || null)
14
+ .style('fill', (d) => d.style.fontColor || null);
15
+ }
@@ -2,6 +2,7 @@ import { color } from 'd3-color';
2
2
  import { select } from 'd3-selection';
3
3
  import { block } from '../../../utils';
4
4
  import { getLineDashArray } from '../../utils';
5
+ import { renderDataLabels } from '../data-labels';
5
6
  const b = block('funnel');
6
7
  export function renderFunnel(elements, preparedData, seriesOptions, dispatcher) {
7
8
  var _a, _b;
@@ -44,17 +45,11 @@ export function renderFunnel(elements, preparedData, seriesOptions, dispatcher)
44
45
  connectorLines.append('path').attr('d', (d) => d.linePath[0].toString());
45
46
  connectorLines.append('path').attr('d', (d) => d.linePath[1].toString());
46
47
  // dataLabels
47
- svgElement
48
- .selectAll('text')
49
- .data(preparedData.svgLabels)
50
- .join('text')
51
- .html((d) => d.text)
52
- .attr('class', b('label'))
53
- .attr('x', (d) => d.x)
54
- .attr('y', (d) => d.y)
55
- .style('font-size', (d) => d.style.fontSize)
56
- .style('font-weight', (d) => d.style.fontWeight || null)
57
- .style('fill', (d) => d.style.fontColor || null);
48
+ renderDataLabels({
49
+ container: svgElement,
50
+ data: preparedData.svgLabels,
51
+ className: b('label'),
52
+ });
58
53
  function handleShapeHover(data) {
59
54
  const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
60
55
  if (hoverEnabled) {
@@ -84,6 +84,7 @@ export async function prepareHeatmapData({ series, xAxis, xScale, yAxis, yScale,
84
84
  x: item.x + item.width / 2 - size.width / 2,
85
85
  y: item.y + item.height / 2 - size.height / 2 + size.hangingOffset,
86
86
  text,
87
+ textAnchor: 'start',
87
88
  style: series.dataLabels.style,
88
89
  });
89
90
  }
@@ -1,6 +1,7 @@
1
1
  import { color } from 'd3-color';
2
2
  import { select } from 'd3-selection';
3
3
  import { block } from '../../../utils';
4
+ import { renderDataLabels } from '../data-labels';
4
5
  const b = block('heatmap');
5
6
  export function renderHeatmap(elements, preparedData, seriesOptions, dispatcher) {
6
7
  var _a, _b;
@@ -20,17 +21,11 @@ export function renderHeatmap(elements, preparedData, seriesOptions, dispatcher)
20
21
  .attr('stroke', (d) => d.borderColor)
21
22
  .attr('stroke-width', (d) => d.borderWidth);
22
23
  // dataLabels
23
- svgElement
24
- .selectAll('text')
25
- .data(preparedData.labels)
26
- .join('text')
27
- .html((d) => d.text)
28
- .attr('class', b('label'))
29
- .attr('x', (d) => d.x)
30
- .attr('y', (d) => d.y)
31
- .style('font-size', (d) => d.style.fontSize)
32
- .style('font-weight', (d) => d.style.fontWeight || null)
33
- .style('fill', (d) => d.style.fontColor || null);
24
+ renderDataLabels({
25
+ container: svgElement,
26
+ data: preparedData.labels,
27
+ className: b('label'),
28
+ });
34
29
  function handleShapeHover(data) {
35
30
  const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
36
31
  if (hoverEnabled) {
@@ -14,6 +14,7 @@ export type HeatmapLabel = {
14
14
  x: number;
15
15
  y: number;
16
16
  text: string;
17
+ textAnchor: 'start' | 'end' | 'middle';
17
18
  style: BaseTextStyle;
18
19
  };
19
20
  export type PreparedHeatmapData = {
@@ -1,22 +1,8 @@
1
1
  import { prepareAnnotation } from '../../series/prepare-annotation';
2
- import { filterOverlappingLabels, getLabelsSize, getTextSizeFn } from '../../utils';
3
- import { getFormattedValue } from '../../utils/format';
4
- import { getXValue, getYValue } from '../utils';
5
- async function getHtmlLabel(point, series, xMax) {
6
- var _a;
7
- const content = String((_a = point.data.label) !== null && _a !== void 0 ? _a : point.data.y);
8
- const size = await getLabelsSize({ labels: [content], html: true });
9
- const width = size.maxWidth;
10
- return {
11
- x: Math.min(xMax - size.maxWidth, Math.max(0, point.x - width / 2)),
12
- y: Math.max(0, point.y - series.dataLabels.padding - size.maxHeight),
13
- content,
14
- size: { width, height: size.maxHeight },
15
- style: series.dataLabels.style,
16
- };
17
- }
2
+ import { filterOverlappingLabels, preparePointDataLabels } from '../../utils';
3
+ import { getXValue, getYValue, markHiddenPointsOutOfYRange } from '../utils';
18
4
  export const prepareLineData = async (args) => {
19
- var _a, _b, _c, _d, _e, _f, _g;
5
+ var _a, _b, _c, _d, _e, _f;
20
6
  const { series, seriesOptions, xAxis, yAxis, xScale, yScale, split, isOutsideBounds, isRangeSlider, otherLayers, } = args;
21
7
  const [_xMin, xRangeMax] = xScale.range();
22
8
  const xMax = xRangeMax;
@@ -58,46 +44,15 @@ export const prepareLineData = async (args) => {
58
44
  let htmlElements = [];
59
45
  let svgLabels = [];
60
46
  if (s.dataLabels.enabled && !isRangeSlider) {
61
- if (s.dataLabels.html) {
62
- const list = await Promise.all(points.reduce((result, p) => {
63
- if (p.y === null || p.x === null || isOutsideBounds(p.x, p.y)) {
64
- return result;
65
- }
66
- result.push(getHtmlLabel(p, s, xMax));
67
- return result;
68
- }, []));
69
- htmlElements.push(...list);
70
- }
71
- else {
72
- const getTextSize = getTextSizeFn({ style: s.dataLabels.style });
73
- for (let index = 0; index < points.length; index++) {
74
- const point = points[index];
75
- if (point.y !== null &&
76
- point.x !== null &&
77
- !isOutsideBounds(point.x, point.y)) {
78
- const labelValue = (_e = point.data.label) !== null && _e !== void 0 ? _e : point.data.y;
79
- const text = getFormattedValue(Object.assign({ value: labelValue }, s.dataLabels));
80
- const labelSize = await getTextSize(text);
81
- const style = s.dataLabels.style;
82
- const y = Math.max(yAxisTop, point.y -
83
- s.dataLabels.padding -
84
- labelSize.height +
85
- labelSize.hangingOffset);
86
- const x = Math.min(xMax - labelSize.width, Math.max(0, point.x - labelSize.width / 2));
87
- const labelData = {
88
- text,
89
- x,
90
- y,
91
- style,
92
- size: labelSize,
93
- textAnchor: 'start',
94
- series: s,
95
- active: true,
96
- };
97
- svgLabels.push(labelData);
98
- }
99
- }
100
- }
47
+ const labelsData = await preparePointDataLabels({
48
+ series: s,
49
+ points,
50
+ xMax,
51
+ yAxisTop,
52
+ isOutsideBounds,
53
+ });
54
+ svgLabels = labelsData.svgLabels;
55
+ htmlElements = labelsData.htmlLabels;
101
56
  }
102
57
  if (!s.dataLabels.allowOverlap) {
103
58
  svgLabels = filterOverlappingLabels(svgLabels, otherLayers.map((l) => l.svgLabels).flat());
@@ -129,6 +84,14 @@ export const prepareLineData = async (args) => {
129
84
  }
130
85
  return result;
131
86
  }, []);
87
+ markHiddenPointsOutOfYRange({
88
+ points,
89
+ yScale: seriesYScale,
90
+ yAxisTop,
91
+ axisMin: seriesYAxis.min,
92
+ axisMax: seriesYAxis.max,
93
+ getDataY: (p) => p.data.y,
94
+ });
132
95
  const result = {
133
96
  annotations,
134
97
  points,
@@ -140,11 +103,11 @@ export const prepareLineData = async (args) => {
140
103
  id: s.id,
141
104
  htmlLabels: htmlElements,
142
105
  color: s.color,
143
- lineWidth: (_f = (isRangeSlider ? s.rangeSlider.lineWidth : undefined)) !== null && _f !== void 0 ? _f : s.lineWidth,
106
+ lineWidth: (_e = (isRangeSlider ? s.rangeSlider.lineWidth : undefined)) !== null && _e !== void 0 ? _e : s.lineWidth,
144
107
  dashStyle: s.dashStyle,
145
108
  linecap: s.linecap,
146
109
  linejoin: s.linejoin,
147
- opacity: (_g = (isRangeSlider ? s.rangeSlider.opacity : undefined)) !== null && _g !== void 0 ? _g : s.opacity,
110
+ opacity: (_f = (isRangeSlider ? s.rangeSlider.opacity : undefined)) !== null && _f !== void 0 ? _f : s.opacity,
148
111
  };
149
112
  acc.push(result);
150
113
  }
@@ -5,6 +5,7 @@ import get from 'lodash/get';
5
5
  import { block } from '../../../utils';
6
6
  import { getLineDashArray } from '../../utils';
7
7
  import { renderAnnotations } from '../annotation';
8
+ import { renderDataLabels } from '../data-labels';
8
9
  import { getMarkerHaloVisibility, getMarkerVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
9
10
  import { setActiveState } from '../utils';
10
11
  const b = block('line');
@@ -17,7 +18,7 @@ export function renderLine(elements, preparedData, seriesOptions, dispatcher) {
17
18
  const hoverOptions = get(seriesOptions, 'line.states.hover');
18
19
  const inactiveOptions = get(seriesOptions, 'line.states.inactive');
19
20
  const line = lineGenerator()
20
- .defined((d) => d.y !== null && d.x !== null)
21
+ .defined((d) => d.y !== null && d.x !== null && !d.hiddenInLine)
21
22
  .x((d) => d.x)
22
23
  .y((d) => d.y);
23
24
  plotSvgElement.selectAll('*').remove();
@@ -38,18 +39,11 @@ export function renderLine(elements, preparedData, seriesOptions, dispatcher) {
38
39
  const dataLabels = preparedData.reduce((acc, d) => {
39
40
  return acc.concat(d.svgLabels);
40
41
  }, []);
41
- const labelsSelection = plotSvgElement
42
- .selectAll('text')
43
- .data(dataLabels)
44
- .join('text')
45
- .html((d) => d.text)
46
- .attr('class', b('label'))
47
- .attr('x', (d) => d.x)
48
- .attr('y', (d) => d.y)
49
- .attr('text-anchor', (d) => d.textAnchor)
50
- .style('font-size', (d) => d.style.fontSize)
51
- .style('font-weight', (d) => d.style.fontWeight || null)
52
- .style('fill', (d) => d.style.fontColor || null);
42
+ const labelsSelection = renderDataLabels({
43
+ container: plotSvgElement,
44
+ data: dataLabels,
45
+ className: b('label'),
46
+ });
53
47
  const markers = preparedData.reduce((acc, d) => acc.concat(d.markers), []);
54
48
  const markerSelection = markersSvgElement
55
49
  .selectAll('marker')
@@ -2,12 +2,13 @@ import type { HtmlItem, LabelData, LineSeriesData, LineSeriesLineBaseStyle } fro
2
2
  import type { DashStyle, LineCap, LineJoin } from '../../constants';
3
3
  import type { AnnotationAnchor, PreparedAnnotation, PreparedLineSeries } from '../../series/types';
4
4
  export type PointData = {
5
- x: number | null;
6
- y: number | null;
7
- data: LineSeriesData;
8
- series: PreparedLineSeries;
9
5
  annotation?: PreparedAnnotation;
10
6
  color?: string;
7
+ data: LineSeriesData;
8
+ hiddenInLine?: boolean;
9
+ series: PreparedLineSeries;
10
+ x: number | null;
11
+ y: number | null;
11
12
  };
12
13
  export type MarkerPointData = PointData & {
13
14
  y: number;
@@ -3,6 +3,7 @@ import { select } from 'd3-selection';
3
3
  import { line } from 'd3-shape';
4
4
  import get from 'lodash/get';
5
5
  import { block } from '../../../utils';
6
+ import { renderDataLabels } from '../../shapes/data-labels';
6
7
  import { getMarkerHaloVisibility, getMarkerVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../../shapes/marker';
7
8
  import { setActiveState } from '../../shapes/utils';
8
9
  const b = block('radar');
@@ -59,18 +60,13 @@ export function renderRadar(elements, preparedData, seriesOptions, dispatcher) {
59
60
  .join('g')
60
61
  .call(renderMarker);
61
62
  // Render labels
62
- radarSelection
63
- .selectAll('text')
64
- .data((radarData) => radarData.labels)
65
- .join('text')
66
- .html((d) => d.text)
67
- .attr('class', b('label'))
68
- .attr('x', (d) => d.x)
69
- .attr('y', (d) => d.y)
70
- .attr('text-anchor', (d) => d.textAnchor)
71
- .style('font-size', (d) => d.style.fontSize)
72
- .style('font-weight', (d) => d.style.fontWeight || null)
73
- .style('fill', (d) => d.style.fontColor || null);
63
+ radarSelection.each(function (radarData) {
64
+ renderDataLabels({
65
+ container: select(this),
66
+ data: radarData.labels,
67
+ className: b('label'),
68
+ });
69
+ });
74
70
  // Handle hover events
75
71
  const eventName = `hover-shape.radar`;
76
72
  const hoverOptions = get(seriesOptions, 'radar.states.hover');
@@ -1,5 +1,6 @@
1
1
  import { select } from 'd3-selection';
2
2
  import { block } from '../../../utils';
3
+ import { renderDataLabels } from '../data-labels';
3
4
  const b = block('sankey');
4
5
  export function renderSankey(elements, preparedData, _seriesOptions, dispatcher) {
5
6
  const svgElement = select(elements.plot);
@@ -28,18 +29,11 @@ export function renderSankey(elements, preparedData, _seriesOptions, dispatcher)
28
29
  .attr('stroke', (d) => d.color)
29
30
  .attr('stroke-width', (d) => d.strokeWidth);
30
31
  // dataLabels
31
- svgElement
32
- .append('g')
33
- .selectAll()
34
- .data(preparedData.labels)
35
- .join('text')
36
- .html((d) => d.text)
37
- .attr('class', b('label'))
38
- .attr('x', (d) => d.x)
39
- .attr('y', (d) => d.y)
40
- .attr('dy', '0.35em')
41
- .attr('text-anchor', (d) => d.textAnchor)
42
- .attr('fill', (d) => { var _a; return (_a = d.style.fontColor) !== null && _a !== void 0 ? _a : null; });
32
+ renderDataLabels({
33
+ container: svgElement.append('g'),
34
+ data: preparedData.labels,
35
+ className: b('label'),
36
+ }).attr('dy', '0.35em');
43
37
  const eventName = `hover-shape.sankey`;
44
38
  dispatcher === null || dispatcher === void 0 ? void 0 : dispatcher.on(eventName, (_data) => {
45
39
  // no-op hover handler
@@ -1,12 +1,15 @@
1
1
  import type { PreparedXAxis, PreparedYAxis } from '../../axes/types';
2
+ import type { PreparedSplit } from '../../layout/split-types';
2
3
  import type { ChartScale } from '../../scales/types';
3
4
  import type { PreparedScatterSeries } from '../../series/types';
4
- import type { PreparedScatterData } from './types';
5
+ import type { PreparedScatterShapeData } from './types';
5
6
  export declare function prepareScatterData(args: {
6
7
  series: PreparedScatterSeries[];
7
8
  xAxis: PreparedXAxis;
8
9
  xScale: ChartScale;
9
10
  yAxis: PreparedYAxis[];
10
11
  yScale: (ChartScale | undefined)[];
12
+ split: PreparedSplit;
11
13
  isOutsideBounds: (x: number, y: number) => boolean;
12
- }): PreparedScatterData[];
14
+ isRangeSlider?: boolean;
15
+ }): Promise<PreparedScatterShapeData>;
@@ -1,6 +1,6 @@
1
1
  import get from 'lodash/get';
2
2
  import { getXValue, getYValue } from '../../shapes/utils';
3
- import { getDataCategoryValue } from '../../utils';
3
+ import { filterOverlappingLabels, getDataCategoryValue, preparePointDataLabels } from '../../utils';
4
4
  function getFilteredLinearScatterData(data) {
5
5
  return data.filter((d) => typeof d.x === 'number' && typeof d.y === 'number');
6
6
  }
@@ -32,9 +32,12 @@ function getFilteredCategoryScatterData(args) {
32
32
  return xInRange && yInRange;
33
33
  });
34
34
  }
35
- export function prepareScatterData(args) {
36
- const { series, xAxis, xScale, yAxis, yScale, isOutsideBounds } = args;
37
- return series.reduce((acc, s) => {
35
+ export async function prepareScatterData(args) {
36
+ var _a;
37
+ const { series, xAxis, xScale, yAxis, yScale, split, isOutsideBounds, isRangeSlider } = args;
38
+ const [_xMin, xRangeMax] = xScale.range();
39
+ const xMax = xRangeMax;
40
+ const markers = series.reduce((acc, s) => {
38
41
  const yAxisIndex = get(s, 'yAxis', 0);
39
42
  const seriesYAxis = yAxis[yAxisIndex];
40
43
  const seriesYScale = yScale[yAxisIndex];
@@ -74,4 +77,40 @@ export function prepareScatterData(args) {
74
77
  });
75
78
  return acc;
76
79
  }, []);
80
+ const allSvgLabels = [];
81
+ const allHtmlLabels = [];
82
+ if (!isRangeSlider) {
83
+ for (const s of series) {
84
+ if (!s.dataLabels.enabled) {
85
+ continue;
86
+ }
87
+ const yAxisIndex = get(s, 'yAxis', 0);
88
+ const seriesYAxis = yAxis[yAxisIndex];
89
+ const seriesYScale = yScale[yAxisIndex];
90
+ if (!seriesYScale) {
91
+ continue;
92
+ }
93
+ const yAxisTop = ((_a = split.plots[seriesYAxis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
94
+ const seriesPoints = markers
95
+ .filter((m) => m.point.series.id === s.id && !m.clipped)
96
+ .map((m) => m.point);
97
+ const { svgLabels, htmlLabels } = await preparePointDataLabels({
98
+ series: s,
99
+ points: seriesPoints,
100
+ xMax,
101
+ yAxisTop,
102
+ isOutsideBounds,
103
+ anchorYOffset: s.marker.states.normal.radius,
104
+ });
105
+ if (s.dataLabels.allowOverlap) {
106
+ allSvgLabels.push(...svgLabels);
107
+ allHtmlLabels.push(...htmlLabels);
108
+ }
109
+ else {
110
+ allSvgLabels.push(...filterOverlappingLabels(svgLabels, allSvgLabels));
111
+ allHtmlLabels.push(...filterOverlappingLabels(htmlLabels, allHtmlLabels));
112
+ }
113
+ }
114
+ }
115
+ return { markers, svgLabels: allSvgLabels, htmlLabels: allHtmlLabels };
77
116
  }
@@ -1,6 +1,6 @@
1
1
  import type { Dispatch } from 'd3-dispatch';
2
2
  import type { PreparedSeriesOptions } from '../../series/types';
3
- import type { PreparedScatterData } from './types';
3
+ import type { PreparedScatterShapeData } from './types';
4
4
  export declare function renderScatter(elements: {
5
5
  plot: SVGGElement;
6
- }, preparedData: PreparedScatterData[], seriesOptions: PreparedSeriesOptions, dispatcher?: Dispatch<object>): () => void;
6
+ }, preparedData: PreparedScatterShapeData, seriesOptions: PreparedSeriesOptions, dispatcher?: Dispatch<object>): () => void;
@@ -1,7 +1,10 @@
1
1
  import { select } from 'd3-selection';
2
2
  import get from 'lodash/get';
3
+ import { block } from '../../../utils';
3
4
  import { getMarkerHaloVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../../shapes/marker';
4
5
  import { setActiveState, shapeKey } from '../../shapes/utils';
6
+ import { renderDataLabels } from '../data-labels';
7
+ const b = block('scatter');
5
8
  export function renderScatter(elements, preparedData, seriesOptions, dispatcher) {
6
9
  const svgElement = select(elements.plot);
7
10
  const hoverOptions = get(seriesOptions, 'scatter.states.hover');
@@ -9,11 +12,16 @@ export function renderScatter(elements, preparedData, seriesOptions, dispatcher)
9
12
  svgElement.selectAll('*').remove();
10
13
  const selection = svgElement
11
14
  .selectAll('path')
12
- .data(preparedData, shapeKey)
15
+ .data(preparedData.markers, shapeKey)
13
16
  .join('g')
14
17
  .call(renderMarker)
15
18
  .attr('opacity', (d) => d.point.opacity)
16
19
  .attr('cursor', (d) => d.point.series.cursor);
20
+ renderDataLabels({
21
+ container: svgElement,
22
+ data: preparedData.svgLabels,
23
+ className: b('label'),
24
+ });
17
25
  const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
18
26
  const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
19
27
  function handleShapeHover(data) {