@gravity-ui/charts 1.51.1 → 1.51.5

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 (197) hide show
  1. package/dist/cjs/components/index.js +25 -17
  2. package/dist/cjs/core/series/plugin.d.ts +14 -0
  3. package/dist/cjs/core/series/plugin.js +1 -0
  4. package/dist/cjs/core/series/prepareSeries.d.ts +3 -10
  5. package/dist/cjs/core/series/prepareSeries.js +3 -126
  6. package/dist/cjs/core/series/seriesRegistry.d.ts +3 -0
  7. package/dist/cjs/core/series/seriesRegistry.js +12 -0
  8. package/dist/cjs/core/shapes/area/get-tooltip-data.d.ts +3 -0
  9. package/dist/cjs/core/shapes/area/get-tooltip-data.js +19 -0
  10. package/dist/cjs/core/shapes/bar-x/get-tooltip-data.d.ts +3 -0
  11. package/dist/cjs/core/shapes/bar-x/get-tooltip-data.js +20 -0
  12. package/dist/cjs/core/shapes/bar-y/get-tooltip-data.d.ts +3 -0
  13. package/dist/cjs/core/shapes/bar-y/get-tooltip-data.js +32 -0
  14. package/dist/cjs/core/shapes/funnel/get-tooltip-data.d.ts +3 -0
  15. package/dist/cjs/core/shapes/funnel/get-tooltip-data.js +21 -0
  16. package/dist/cjs/core/shapes/funnel/prepare-data.js +6 -4
  17. package/dist/cjs/core/shapes/heatmap/get-tooltip-data.d.ts +3 -0
  18. package/dist/cjs/core/shapes/heatmap/get-tooltip-data.js +21 -0
  19. package/dist/cjs/core/shapes/line/get-tooltip-data.d.ts +3 -0
  20. package/dist/cjs/core/shapes/line/get-tooltip-data.js +18 -0
  21. package/dist/cjs/core/shapes/pie/get-tooltip-data.d.ts +3 -0
  22. package/dist/cjs/core/shapes/pie/get-tooltip-data.js +27 -0
  23. package/dist/cjs/core/shapes/radar/get-tooltip-data.d.ts +3 -0
  24. package/dist/cjs/core/shapes/radar/get-tooltip-data.js +35 -0
  25. package/dist/cjs/core/shapes/sankey/get-tooltip-data.d.ts +3 -0
  26. package/dist/cjs/core/shapes/sankey/get-tooltip-data.js +32 -0
  27. package/dist/cjs/core/shapes/scatter/get-tooltip-data.d.ts +3 -0
  28. package/dist/cjs/core/shapes/scatter/get-tooltip-data.js +19 -0
  29. package/dist/cjs/core/shapes/treemap/get-tooltip-data.d.ts +3 -0
  30. package/dist/cjs/core/shapes/treemap/get-tooltip-data.js +18 -0
  31. package/dist/cjs/core/shapes/waterfall/get-tooltip-data.d.ts +3 -0
  32. package/dist/cjs/core/shapes/waterfall/get-tooltip-data.js +15 -0
  33. package/dist/cjs/core/shapes/x-range/get-tooltip-data.d.ts +3 -0
  34. package/dist/cjs/core/shapes/x-range/get-tooltip-data.js +22 -0
  35. package/dist/cjs/core/types/chart/funnel.d.ts +9 -4
  36. package/dist/cjs/core/utils/get-closest-data.d.ts +2 -11
  37. package/dist/cjs/core/utils/get-closest-data.js +47 -326
  38. package/dist/cjs/core/utils/tooltip-helpers.d.ts +43 -0
  39. package/dist/cjs/core/utils/tooltip-helpers.js +72 -0
  40. package/dist/cjs/index.d.ts +1 -0
  41. package/dist/cjs/index.js +1 -0
  42. package/dist/cjs/plugins/area/index.d.ts +3 -0
  43. package/dist/cjs/plugins/area/index.js +5 -0
  44. package/dist/cjs/{core/series/prepare-area.d.ts → plugins/area/prepare.d.ts} +2 -2
  45. package/dist/cjs/{core/series/prepare-area.js → plugins/area/prepare.js} +4 -4
  46. package/dist/cjs/plugins/bar-x/index.d.ts +3 -0
  47. package/dist/cjs/plugins/bar-x/index.js +5 -0
  48. package/dist/{esm/core/series/prepare-bar-x.d.ts → cjs/plugins/bar-x/prepare.d.ts} +1 -1
  49. package/dist/{esm/core/series/prepare-bar-x.js → cjs/plugins/bar-x/prepare.js} +4 -4
  50. package/dist/cjs/plugins/bar-y/index.d.ts +3 -0
  51. package/dist/cjs/plugins/bar-y/index.js +5 -0
  52. package/dist/cjs/{core/series/prepare-bar-y.d.ts → plugins/bar-y/prepare.d.ts} +2 -2
  53. package/dist/cjs/{core/series/prepare-bar-y.js → plugins/bar-y/prepare.js} +4 -4
  54. package/dist/cjs/plugins/funnel/index.d.ts +3 -0
  55. package/dist/cjs/plugins/funnel/index.js +5 -0
  56. package/dist/cjs/{core/series/prepare-funnel.d.ts → plugins/funnel/prepare.d.ts} +2 -2
  57. package/dist/{esm/core/series/prepare-funnel.js → cjs/plugins/funnel/prepare.js} +15 -9
  58. package/dist/cjs/plugins/heatmap/index.d.ts +3 -0
  59. package/dist/cjs/plugins/heatmap/index.js +10 -0
  60. package/dist/{esm/core/series/prepare-heatmap.d.ts → cjs/plugins/heatmap/prepare.d.ts} +1 -1
  61. package/dist/{esm/core/series/prepare-heatmap.js → cjs/plugins/heatmap/prepare.js} +4 -4
  62. package/dist/cjs/plugins/index.d.ts +1 -0
  63. package/dist/cjs/plugins/index.js +27 -0
  64. package/dist/cjs/plugins/line/index.d.ts +3 -0
  65. package/dist/cjs/plugins/line/index.js +5 -0
  66. package/dist/cjs/{core/series/prepare-line.d.ts → plugins/line/prepare.d.ts} +2 -2
  67. package/dist/cjs/{core/series/prepare-line.js → plugins/line/prepare.js} +3 -3
  68. package/dist/cjs/plugins/pie/index.d.ts +3 -0
  69. package/dist/cjs/plugins/pie/index.js +5 -0
  70. package/dist/cjs/{core/series/prepare-pie.d.ts → plugins/pie/prepare.d.ts} +2 -2
  71. package/dist/cjs/plugins/pie/prepare.js +84 -0
  72. package/dist/cjs/plugins/radar/index.d.ts +3 -0
  73. package/dist/cjs/plugins/radar/index.js +5 -0
  74. package/dist/cjs/{core/series/prepare-radar.d.ts → plugins/radar/prepare.d.ts} +2 -2
  75. package/dist/cjs/{core/series/prepare-radar.js → plugins/radar/prepare.js} +4 -4
  76. package/dist/cjs/plugins/sankey/index.d.ts +3 -0
  77. package/dist/cjs/plugins/sankey/index.js +5 -0
  78. package/dist/cjs/{core/series/prepare-sankey.d.ts → plugins/sankey/prepare.d.ts} +1 -1
  79. package/dist/cjs/{core/series/prepare-sankey.js → plugins/sankey/prepare.js} +3 -3
  80. package/dist/cjs/plugins/scatter/index.d.ts +3 -0
  81. package/dist/cjs/plugins/scatter/index.js +10 -0
  82. package/dist/cjs/{core/series/prepare-scatter.d.ts → plugins/scatter/prepare.d.ts} +1 -1
  83. package/dist/{esm/core/series/prepare-scatter.js → cjs/plugins/scatter/prepare.js} +4 -4
  84. package/dist/cjs/plugins/treemap/index.d.ts +3 -0
  85. package/dist/cjs/plugins/treemap/index.js +5 -0
  86. package/dist/{esm/core/series/prepare-treemap.d.ts → cjs/plugins/treemap/prepare.d.ts} +1 -1
  87. package/dist/cjs/{core/series/prepare-treemap.js → plugins/treemap/prepare.js} +4 -4
  88. package/dist/cjs/plugins/waterfall/index.d.ts +3 -0
  89. package/dist/cjs/plugins/waterfall/index.js +5 -0
  90. package/dist/cjs/{core/series/prepare-waterfall.d.ts → plugins/waterfall/prepare.d.ts} +1 -1
  91. package/dist/{esm/core/series/prepare-waterfall.js → cjs/plugins/waterfall/prepare.js} +4 -4
  92. package/dist/cjs/plugins/x-range/index.d.ts +3 -0
  93. package/dist/cjs/plugins/x-range/index.js +5 -0
  94. package/dist/cjs/{core/series/prepare-x-range.d.ts → plugins/x-range/prepare.d.ts} +1 -1
  95. package/dist/cjs/{core/series/prepare-x-range.js → plugins/x-range/prepare.js} +4 -4
  96. package/dist/cjs/setup-jsdom.d.ts +1 -0
  97. package/dist/cjs/setup-jsdom.js +30 -4
  98. package/dist/esm/components/index.js +25 -17
  99. package/dist/esm/core/series/plugin.d.ts +14 -0
  100. package/dist/esm/core/series/plugin.js +1 -0
  101. package/dist/esm/core/series/prepareSeries.d.ts +3 -10
  102. package/dist/esm/core/series/prepareSeries.js +3 -126
  103. package/dist/esm/core/series/seriesRegistry.d.ts +3 -0
  104. package/dist/esm/core/series/seriesRegistry.js +12 -0
  105. package/dist/esm/core/shapes/area/get-tooltip-data.d.ts +3 -0
  106. package/dist/esm/core/shapes/area/get-tooltip-data.js +19 -0
  107. package/dist/esm/core/shapes/bar-x/get-tooltip-data.d.ts +3 -0
  108. package/dist/esm/core/shapes/bar-x/get-tooltip-data.js +20 -0
  109. package/dist/esm/core/shapes/bar-y/get-tooltip-data.d.ts +3 -0
  110. package/dist/esm/core/shapes/bar-y/get-tooltip-data.js +32 -0
  111. package/dist/esm/core/shapes/funnel/get-tooltip-data.d.ts +3 -0
  112. package/dist/esm/core/shapes/funnel/get-tooltip-data.js +21 -0
  113. package/dist/esm/core/shapes/funnel/prepare-data.js +6 -4
  114. package/dist/esm/core/shapes/heatmap/get-tooltip-data.d.ts +3 -0
  115. package/dist/esm/core/shapes/heatmap/get-tooltip-data.js +21 -0
  116. package/dist/esm/core/shapes/line/get-tooltip-data.d.ts +3 -0
  117. package/dist/esm/core/shapes/line/get-tooltip-data.js +18 -0
  118. package/dist/esm/core/shapes/pie/get-tooltip-data.d.ts +3 -0
  119. package/dist/esm/core/shapes/pie/get-tooltip-data.js +27 -0
  120. package/dist/esm/core/shapes/radar/get-tooltip-data.d.ts +3 -0
  121. package/dist/esm/core/shapes/radar/get-tooltip-data.js +35 -0
  122. package/dist/esm/core/shapes/sankey/get-tooltip-data.d.ts +3 -0
  123. package/dist/esm/core/shapes/sankey/get-tooltip-data.js +32 -0
  124. package/dist/esm/core/shapes/scatter/get-tooltip-data.d.ts +3 -0
  125. package/dist/esm/core/shapes/scatter/get-tooltip-data.js +19 -0
  126. package/dist/esm/core/shapes/treemap/get-tooltip-data.d.ts +3 -0
  127. package/dist/esm/core/shapes/treemap/get-tooltip-data.js +18 -0
  128. package/dist/esm/core/shapes/waterfall/get-tooltip-data.d.ts +3 -0
  129. package/dist/esm/core/shapes/waterfall/get-tooltip-data.js +15 -0
  130. package/dist/esm/core/shapes/x-range/get-tooltip-data.d.ts +3 -0
  131. package/dist/esm/core/shapes/x-range/get-tooltip-data.js +22 -0
  132. package/dist/esm/core/types/chart/funnel.d.ts +9 -4
  133. package/dist/esm/core/utils/get-closest-data.d.ts +2 -11
  134. package/dist/esm/core/utils/get-closest-data.js +47 -326
  135. package/dist/esm/core/utils/tooltip-helpers.d.ts +43 -0
  136. package/dist/esm/core/utils/tooltip-helpers.js +72 -0
  137. package/dist/esm/index.d.ts +1 -0
  138. package/dist/esm/index.js +1 -0
  139. package/dist/esm/plugins/area/index.d.ts +3 -0
  140. package/dist/esm/plugins/area/index.js +5 -0
  141. package/dist/esm/{core/series/prepare-area.d.ts → plugins/area/prepare.d.ts} +2 -2
  142. package/dist/esm/{core/series/prepare-area.js → plugins/area/prepare.js} +4 -4
  143. package/dist/esm/plugins/bar-x/index.d.ts +3 -0
  144. package/dist/esm/plugins/bar-x/index.js +5 -0
  145. package/dist/{cjs/core/series/prepare-bar-x.d.ts → esm/plugins/bar-x/prepare.d.ts} +1 -1
  146. package/dist/{cjs/core/series/prepare-bar-x.js → esm/plugins/bar-x/prepare.js} +4 -4
  147. package/dist/esm/plugins/bar-y/index.d.ts +3 -0
  148. package/dist/esm/plugins/bar-y/index.js +5 -0
  149. package/dist/esm/{core/series/prepare-bar-y.d.ts → plugins/bar-y/prepare.d.ts} +5 -5
  150. package/dist/esm/{core/series/prepare-bar-y.js → plugins/bar-y/prepare.js} +4 -4
  151. package/dist/esm/plugins/funnel/index.d.ts +3 -0
  152. package/dist/esm/plugins/funnel/index.js +5 -0
  153. package/dist/esm/{core/series/prepare-funnel.d.ts → plugins/funnel/prepare.d.ts} +2 -2
  154. package/dist/{cjs/core/series/prepare-funnel.js → esm/plugins/funnel/prepare.js} +15 -9
  155. package/dist/esm/plugins/heatmap/index.d.ts +3 -0
  156. package/dist/esm/plugins/heatmap/index.js +10 -0
  157. package/dist/{cjs/core/series/prepare-heatmap.d.ts → esm/plugins/heatmap/prepare.d.ts} +1 -1
  158. package/dist/{cjs/core/series/prepare-heatmap.js → esm/plugins/heatmap/prepare.js} +4 -4
  159. package/dist/esm/plugins/index.d.ts +1 -0
  160. package/dist/esm/plugins/index.js +27 -0
  161. package/dist/esm/plugins/line/index.d.ts +3 -0
  162. package/dist/esm/plugins/line/index.js +5 -0
  163. package/dist/esm/{core/series/prepare-line.d.ts → plugins/line/prepare.d.ts} +2 -2
  164. package/dist/esm/{core/series/prepare-line.js → plugins/line/prepare.js} +3 -3
  165. package/dist/esm/plugins/pie/index.d.ts +3 -0
  166. package/dist/esm/plugins/pie/index.js +5 -0
  167. package/dist/esm/{core/series/prepare-pie.d.ts → plugins/pie/prepare.d.ts} +2 -2
  168. package/dist/esm/plugins/pie/prepare.js +84 -0
  169. package/dist/esm/plugins/radar/index.d.ts +3 -0
  170. package/dist/esm/plugins/radar/index.js +5 -0
  171. package/dist/esm/{core/series/prepare-radar.d.ts → plugins/radar/prepare.d.ts} +2 -2
  172. package/dist/esm/{core/series/prepare-radar.js → plugins/radar/prepare.js} +4 -4
  173. package/dist/esm/plugins/sankey/index.d.ts +3 -0
  174. package/dist/esm/plugins/sankey/index.js +5 -0
  175. package/dist/esm/{core/series/prepare-sankey.d.ts → plugins/sankey/prepare.d.ts} +1 -1
  176. package/dist/esm/{core/series/prepare-sankey.js → plugins/sankey/prepare.js} +3 -3
  177. package/dist/esm/plugins/scatter/index.d.ts +3 -0
  178. package/dist/esm/plugins/scatter/index.js +10 -0
  179. package/dist/esm/{core/series/prepare-scatter.d.ts → plugins/scatter/prepare.d.ts} +1 -1
  180. package/dist/{cjs/core/series/prepare-scatter.js → esm/plugins/scatter/prepare.js} +4 -4
  181. package/dist/esm/plugins/treemap/index.d.ts +3 -0
  182. package/dist/esm/plugins/treemap/index.js +5 -0
  183. package/dist/{cjs/core/series/prepare-treemap.d.ts → esm/plugins/treemap/prepare.d.ts} +1 -1
  184. package/dist/esm/{core/series/prepare-treemap.js → plugins/treemap/prepare.js} +4 -4
  185. package/dist/esm/plugins/waterfall/index.d.ts +3 -0
  186. package/dist/esm/plugins/waterfall/index.js +5 -0
  187. package/dist/esm/{core/series/prepare-waterfall.d.ts → plugins/waterfall/prepare.d.ts} +1 -1
  188. package/dist/{cjs/core/series/prepare-waterfall.js → esm/plugins/waterfall/prepare.js} +4 -4
  189. package/dist/esm/plugins/x-range/index.d.ts +3 -0
  190. package/dist/esm/plugins/x-range/index.js +5 -0
  191. package/dist/esm/{core/series/prepare-x-range.d.ts → plugins/x-range/prepare.d.ts} +1 -1
  192. package/dist/esm/{core/series/prepare-x-range.js → plugins/x-range/prepare.js} +4 -4
  193. package/dist/esm/setup-jsdom.d.ts +1 -0
  194. package/dist/esm/setup-jsdom.js +30 -4
  195. package/package.json +4 -2
  196. package/dist/cjs/core/series/prepare-pie.js +0 -83
  197. package/dist/esm/core/series/prepare-pie.js +0 -83
@@ -0,0 +1,3 @@
1
+ import type { GetTooltipDataArgs, GetTooltipDataResult } from '../../utils/tooltip-helpers';
2
+ import type { PreparedXRangeData } from './types';
3
+ export declare function getTooltipData(args: GetTooltipDataArgs<PreparedXRangeData>): GetTooltipDataResult;
@@ -0,0 +1,22 @@
1
+ import { sort } from 'd3-array';
2
+ export function getTooltipData(args) {
3
+ const { data, position } = args;
4
+ const [pointerX, pointerY] = position;
5
+ const pointsInRange = data.filter((d) => pointerX >= d.x &&
6
+ pointerX <= d.x + d.width &&
7
+ pointerY >= d.y &&
8
+ pointerY <= d.y + d.height);
9
+ if (pointsInRange.length === 0) {
10
+ return { chunks: [] };
11
+ }
12
+ const closestByX = pointsInRange.length === 1
13
+ ? pointsInRange[0]
14
+ : sort(pointsInRange, (d) => Math.abs(d.x + d.width / 2 - pointerX))[0];
15
+ return {
16
+ chunks: pointsInRange.map((d) => ({
17
+ data: d.data,
18
+ series: d.series,
19
+ closest: d === closestByX,
20
+ })),
21
+ };
22
+ }
@@ -34,18 +34,23 @@ export interface FunnelSeries<T = MeaningfulAny> extends Omit<BaseSeries, 'dataL
34
34
  * width alone, exaggerating differences between large and small values. Use only for
35
35
  * decorative purposes or when visual familiarity with the funnel metaphor is more
36
36
  * important than analytical precision.
37
- *
38
37
  * @default 'rectangle'
39
38
  */
40
39
  shape?: 'rectangle' | 'trapezoid';
41
40
  /** Lines or areas connecting the funnel segments. */
42
41
  connectors?: {
43
42
  enabled?: boolean;
44
- /** The height of the connector area relative to the funnel segment. */
43
+ /**
44
+ * The height of the connector area between funnel segments.
45
+ * Accepts a pixel number, a pixel string (e.g. `'10px'`), or a percentage string
46
+ * (e.g. `'25%'`) relative to the segment band height.
47
+ *
48
+ * Defaults to `0` for `'trapezoid'` and `'25%'` for `'rectangle'`.
49
+ */
45
50
  height?: string | number;
46
51
  /** Option for line stroke style */
47
52
  lineDashStyle?: DashStyle;
48
- /** Opacity for the connector line. */
53
+ /** Opacity for the connector line. Defaults to `0` for `'trapezoid'`, `1` for `'rectangle'`. */
49
54
  lineOpacity?: number;
50
55
  /** Connector line color. */
51
56
  lineColor?: string;
@@ -53,7 +58,7 @@ export interface FunnelSeries<T = MeaningfulAny> extends Omit<BaseSeries, 'dataL
53
58
  lineWidth?: number;
54
59
  /** Connector area color. */
55
60
  areaColor?: string;
56
- /** Opacity for the connector area. */
61
+ /** Opacity for the connector area fill. Defaults to `0` for `'trapezoid'`, `0.25` for `'rectangle'`. */
57
62
  areaOpacity?: number;
58
63
  };
59
64
  dataLabels?: Omit<BaseDataLabels, 'allowOverlap'> & {
@@ -1,19 +1,10 @@
1
1
  import type { ShapeData } from '../../hooks/useShapes';
2
- import type { ChartSeries, ChartSeriesData, TooltipDataChunk } from '../../types';
2
+ import type { TooltipDataChunk } from '../../types';
3
3
  type GetClosestPointsArgs = {
4
4
  position: [number, number];
5
5
  shapesData: ShapeData[];
6
6
  boundsHeight: number;
7
7
  boundsWidth: number;
8
8
  };
9
- export type ShapePoint = {
10
- x: number;
11
- y0: number;
12
- y1: number;
13
- data: ChartSeriesData;
14
- series: ChartSeries;
15
- subTotal?: number;
16
- sourceX?: number;
17
- };
9
+ export type { ShapePoint } from './tooltip-helpers';
18
10
  export declare function getClosestPoints(args: GetClosestPointsArgs): TooltipDataChunk[];
19
- export {};
@@ -1,56 +1,34 @@
1
- import { bisector, sort } from 'd3-array';
2
- import { Delaunay } from 'd3-delaunay';
3
1
  import get from 'lodash/get';
4
2
  import groupBy from 'lodash/groupBy';
5
- function getClosestYIndex(items, y) {
6
- var _a, _b, _c, _d;
7
- let closestYIndex = -1;
8
- if (y < ((_a = items[0]) === null || _a === void 0 ? void 0 : _a.y0)) {
9
- closestYIndex = 0;
10
- }
11
- else if (y > ((_b = items[items.length - 1]) === null || _b === void 0 ? void 0 : _b.y1)) {
12
- closestYIndex = items.length - 1;
13
- }
14
- else {
15
- closestYIndex = items.findIndex((p) => y > p.y0 && y < p.y1);
16
- if (closestYIndex === -1) {
17
- const sortedY = sort(items.map((p, index) => ({ index, y: p.y1 + (p.y0 - p.y1) / 2 })), (p) => p.y);
18
- const sortedYIndex = bisector((p) => p.y).center(sortedY, y);
19
- closestYIndex = (_d = (_c = sortedY[sortedYIndex]) === null || _c === void 0 ? void 0 : _c.index) !== null && _d !== void 0 ? _d : -1;
20
- }
21
- }
22
- return closestYIndex;
23
- }
24
- function getClosestPointsByXValue(x, y, points) {
25
- var _a;
26
- const sorted = sort(points, (p) => p.x);
27
- const closestXIndex = bisector((p) => p.x).center(sorted, x);
28
- if (sorted.length === 0 || closestXIndex === -1) {
29
- return [];
30
- }
31
- const closestX = Math.round(sorted[closestXIndex].x);
32
- const filtered = points.filter((p) => Math.round(p.x) === closestX);
33
- const groupedBySeries = Object.values(groupBy(filtered, (p) => get(p.series, 'id'))).map((items) => {
34
- const sortedByY = sort(items, (p) => p.y0);
35
- const index = getClosestYIndex(sortedByY, y);
36
- return sortedByY[index === -1 ? 0 : index];
37
- });
38
- const closestPoints = sort(groupedBySeries, (p) => p.y0);
39
- const pointsWithSourceX = closestPoints.filter((p) => p.sourceX !== undefined);
40
- const uniqueSourceX = new Set(pointsWithSourceX.map((p) => p.sourceX));
41
- if (pointsWithSourceX.length > 1 && uniqueSourceX.size > 1) {
42
- const sortedBySourceX = sort(pointsWithSourceX, (p) => { var _a; return (_a = p.sourceX) !== null && _a !== void 0 ? _a : 0; });
43
- const closestSourceXIdx = bisector((p) => { var _a; return (_a = p.sourceX) !== null && _a !== void 0 ? _a : 0; }).center(sortedBySourceX, x);
44
- const closestSourceX = (_a = sortedBySourceX[closestSourceXIdx]) === null || _a === void 0 ? void 0 : _a.sourceX;
45
- const candidates = closestPoints.filter((p) => p.sourceX === undefined || p.sourceX === closestSourceX);
46
- const sortedCandidates = sort(candidates, (p) => p.y0);
47
- const winnerIdx = getClosestYIndex(sortedCandidates, y);
48
- const winner = sortedCandidates[winnerIdx === -1 ? 0 : winnerIdx];
49
- return closestPoints.map((p) => (Object.assign(Object.assign({}, p), { closest: p === winner })));
50
- }
51
- const closestYIndex = getClosestYIndex(closestPoints, y);
52
- return closestPoints.map((p, i) => (Object.assign(Object.assign({}, p), { closest: i === closestYIndex })));
53
- }
3
+ import { getTooltipData as areaGetTooltipData } from '../shapes/area/get-tooltip-data';
4
+ import { getTooltipData as barXGetTooltipData } from '../shapes/bar-x/get-tooltip-data';
5
+ import { getTooltipData as barYGetTooltipData } from '../shapes/bar-y/get-tooltip-data';
6
+ import { getTooltipData as funnelGetTooltipData } from '../shapes/funnel/get-tooltip-data';
7
+ import { getTooltipData as heatmapGetTooltipData } from '../shapes/heatmap/get-tooltip-data';
8
+ import { getTooltipData as lineGetTooltipData } from '../shapes/line/get-tooltip-data';
9
+ import { getTooltipData as pieGetTooltipData } from '../shapes/pie/get-tooltip-data';
10
+ import { getTooltipData as radarGetTooltipData } from '../shapes/radar/get-tooltip-data';
11
+ import { getTooltipData as sankeyGetTooltipData } from '../shapes/sankey/get-tooltip-data';
12
+ import { getTooltipData as scatterGetTooltipData } from '../shapes/scatter/get-tooltip-data';
13
+ import { getTooltipData as treemapGetTooltipData } from '../shapes/treemap/get-tooltip-data';
14
+ import { getTooltipData as waterfallGetTooltipData } from '../shapes/waterfall/get-tooltip-data';
15
+ import { getTooltipData as xRangeGetTooltipData } from '../shapes/x-range/get-tooltip-data';
16
+ import { getClosestPointsByXValue } from './tooltip-helpers';
17
+ const tooltipFnByType = {
18
+ line: lineGetTooltipData,
19
+ area: areaGetTooltipData,
20
+ 'bar-x': barXGetTooltipData,
21
+ 'bar-y': barYGetTooltipData,
22
+ waterfall: waterfallGetTooltipData,
23
+ scatter: scatterGetTooltipData,
24
+ pie: pieGetTooltipData,
25
+ treemap: treemapGetTooltipData,
26
+ heatmap: heatmapGetTooltipData,
27
+ sankey: sankeyGetTooltipData,
28
+ radar: radarGetTooltipData,
29
+ funnel: funnelGetTooltipData,
30
+ 'x-range': xRangeGetTooltipData,
31
+ };
54
32
  function getSeriesType(shapeData) {
55
33
  return (get(shapeData, 'series.type') ||
56
34
  get(shapeData, 'point.series.type') ||
@@ -60,283 +38,26 @@ export function getClosestPoints(args) {
60
38
  const { position, shapesData, boundsHeight, boundsWidth } = args;
61
39
  const [pointerX, pointerY] = position;
62
40
  const result = [];
41
+ const xLookupPoints = [];
63
42
  const groups = groupBy(shapesData, getSeriesType);
64
- const closestPointsByXValue = [];
65
- Object.entries(groups).forEach(([seriesType, list]) => {
66
- var _a, _b, _c, _d, _e;
67
- switch (seriesType) {
68
- case 'line': {
69
- const linePoints = list.reduce((acc, d) => {
70
- acc.push(...d.points.reduce((accPoints, p) => {
71
- if (p.y !== null && p.x !== null && !p.hiddenInLine) {
72
- accPoints.push({
73
- data: p.data,
74
- series: p.series,
75
- x: p.x,
76
- y0: p.y,
77
- y1: p.y,
78
- });
79
- }
80
- return accPoints;
81
- }, []));
82
- return acc;
83
- }, []);
84
- closestPointsByXValue.push(...linePoints);
85
- break;
86
- }
87
- case 'area': {
88
- const areaPoints = list.reduce((acc, d) => {
89
- for (const p of d.points) {
90
- if (p.y === null || p.hiddenInLine) {
91
- continue;
92
- }
93
- acc.push({
94
- data: p.data,
95
- series: p.series,
96
- x: p.x,
97
- y0: p.y0,
98
- y1: p.y,
99
- });
100
- }
101
- return acc;
102
- }, []);
103
- closestPointsByXValue.push(...areaPoints);
104
- break;
105
- }
106
- case 'bar-x': {
107
- const barXList = list;
108
- const barXGroups = groupBy(barXList, (d) => String(d.data.x));
109
- const barXPoints = [];
110
- Object.values(barXGroups).forEach((group) => {
111
- const groupCenterX = group.reduce((sum, d) => sum + d.x + d.width / 2, 0) / group.length;
112
- group.forEach((d) => {
113
- barXPoints.push({
114
- data: d.data,
115
- series: d.series,
116
- x: groupCenterX,
117
- y0: d.y,
118
- y1: d.y + d.height,
119
- sourceX: d.x + d.width / 2,
120
- });
121
- });
122
- });
123
- closestPointsByXValue.push(...barXPoints);
124
- break;
125
- }
126
- case 'waterfall': {
127
- const points = list.map((d) => ({
128
- data: d.data,
129
- series: d.series,
130
- x: d.x + d.width / 2,
131
- y0: d.y,
132
- y1: d.y + d.height,
133
- subTotal: d.subTotal,
134
- }));
135
- result.push(...getClosestPointsByXValue(pointerX, pointerY, points));
136
- break;
137
- }
138
- case 'bar-y': {
139
- const points = list;
140
- const sorted = sort(points, (p) => p.y);
141
- const closestYIndex = bisector((p) => p.y + p.height / 2).center(sorted, pointerY);
142
- const closestYPoint = sorted[closestYIndex];
143
- let selectedPoints = [];
144
- let closestPointXValue = -1;
145
- if (closestYPoint) {
146
- selectedPoints = points.filter((p) => p.data.y === closestYPoint.data.y);
147
- const closestPoints = sort(selectedPoints.filter((p) => p.y === closestYPoint.y), (p) => p.x);
148
- const lastPoint = closestPoints[closestPoints.length - 1];
149
- if (pointerX < ((_a = closestPoints[0]) === null || _a === void 0 ? void 0 : _a.x)) {
150
- closestPointXValue = closestPoints[0].x;
151
- }
152
- else if (lastPoint && pointerX > lastPoint.x + lastPoint.width) {
153
- closestPointXValue = lastPoint.x;
154
- }
155
- else {
156
- closestPointXValue = (_b = closestPoints.find((p) => pointerX > p.x && pointerX < p.x + p.width)) === null || _b === void 0 ? void 0 : _b.x;
157
- }
158
- }
159
- result.push(...selectedPoints.map((p) => ({
160
- data: p.data,
161
- series: p.series,
162
- closest: p.x === closestPointXValue && p.y === closestYPoint.y,
163
- })));
164
- break;
165
- }
166
- case 'scatter': {
167
- const points = list;
168
- const delaunayX = Delaunay.from(points, (d) => d.point.x, (d) => d.point.y);
169
- const closestPoint = points[delaunayX.find(pointerX, pointerY)];
170
- if (closestPoint) {
171
- result.push({
172
- data: closestPoint.point.data,
173
- series: closestPoint.point.series,
174
- closest: true,
175
- });
176
- }
177
- break;
178
- }
179
- case 'pie': {
180
- const points = list.map((d) => d.segments).flat();
181
- const closestPoint = points.find((p) => {
182
- const { center } = p.data.pie;
183
- const x = pointerX - center[0];
184
- const y = pointerY - center[1];
185
- let angle = Math.atan2(y, x) + 0.5 * Math.PI;
186
- angle = angle < 0 ? Math.PI * 2 + angle : angle;
187
- const polarRadius = getRadius({ center, pointer: [pointerX, pointerY] });
188
- return (angle >= p.startAngle && angle <= p.endAngle && polarRadius < p.data.radius);
189
- });
190
- if (closestPoint) {
191
- result.push({
192
- data: closestPoint.data.series.data,
193
- series: closestPoint.data.series,
194
- closest: true,
195
- });
196
- }
197
- break;
198
- }
199
- case 'treemap': {
200
- const data = list;
201
- const closestPoint = (_c = data[0]) === null || _c === void 0 ? void 0 : _c.leaves.find((l) => {
202
- return (pointerX >= l.x0 && pointerX <= l.x1 && pointerY >= l.y0 && pointerY <= l.y1);
203
- });
204
- if (closestPoint) {
205
- result.push({
206
- data: closestPoint.data,
207
- series: data[0].series,
208
- closest: true,
209
- });
210
- }
211
- break;
212
- }
213
- case 'heatmap': {
214
- const data = list;
215
- const closestPoint = (_d = data[0]) === null || _d === void 0 ? void 0 : _d.items.find((cell) => {
216
- return (pointerX >= cell.x &&
217
- pointerX <= cell.x + cell.width &&
218
- pointerY >= cell.y &&
219
- pointerY <= cell.y + cell.height);
220
- });
221
- if (closestPoint) {
222
- result.push({
223
- data: closestPoint.data,
224
- series: data[0].series,
225
- closest: true,
226
- });
227
- }
228
- break;
229
- }
230
- case 'sankey': {
231
- const [data] = list;
232
- const closestLink = data.links.find((d) => {
233
- var _a;
234
- return isInsidePath({
235
- path: (_a = d.path) !== null && _a !== void 0 ? _a : '',
236
- strokeWidth: d.strokeWidth,
237
- point: [pointerX, pointerY],
238
- width: boundsWidth,
239
- height: boundsHeight,
240
- });
241
- });
242
- if (closestLink) {
243
- result.push({
244
- data: closestLink.source,
245
- target: closestLink.target,
246
- series: data.series,
247
- closest: true,
248
- });
249
- }
250
- break;
251
- }
252
- case 'radar': {
253
- const [radarData] = list;
254
- const radius = getRadius({ center: radarData.center, pointer: [pointerX, pointerY] });
255
- if (radius <= radarData.radius) {
256
- const radarShapes = radarData.shapes.filter((shape) => isInsidePath({
257
- path: shape.path,
258
- point: [pointerX, pointerY],
259
- width: boundsWidth,
260
- height: boundsHeight,
261
- strokeWidth: shape.borderWidth,
262
- }));
263
- const points = radarShapes.map((shape) => shape.points).flat();
264
- const delaunayX = Delaunay.from(points, (d) => d.position[0], (d) => d.position[1]);
265
- const closestPoint = points[delaunayX.find(pointerX, pointerY)];
266
- if (closestPoint) {
267
- radarData.shapes.forEach((shape) => {
268
- result.push({
269
- data: shape.points[closestPoint.index].data,
270
- series: shape.series,
271
- category: shape.series.categories[closestPoint.index],
272
- closest: shape.series === closestPoint.series,
273
- });
274
- });
275
- }
276
- }
277
- break;
278
- }
279
- case 'funnel': {
280
- const data = list;
281
- const closestPoint = (_e = data[0]) === null || _e === void 0 ? void 0 : _e.items.find((item) => {
282
- return (pointerX >= item.x &&
283
- pointerX <= item.x + item.width &&
284
- pointerY >= item.y &&
285
- pointerY <= item.y + item.height);
286
- });
287
- if (closestPoint) {
288
- result.push({
289
- data: closestPoint.data,
290
- series: closestPoint.series,
291
- closest: true,
292
- });
293
- }
294
- break;
295
- }
296
- case 'x-range': {
297
- const data = list;
298
- const pointsInXRange = data.filter((d) => pointerX >= d.x &&
299
- pointerX <= d.x + d.width &&
300
- pointerY >= d.y &&
301
- pointerY <= d.y + d.height);
302
- if (pointsInXRange.length === 0) {
303
- break;
304
- }
305
- const closestByX = pointsInXRange.length === 1
306
- ? pointsInXRange[0]
307
- : sort(pointsInXRange, (d) => Math.abs(d.x + d.width / 2 - pointerX))[0];
308
- result.push(...pointsInXRange.map((d) => ({
309
- data: d.data,
310
- series: d.series,
311
- closest: d === closestByX,
312
- })));
313
- break;
314
- }
43
+ for (const [seriesType, list] of Object.entries(groups)) {
44
+ const fn = tooltipFnByType[seriesType];
45
+ if (!fn) {
46
+ continue;
47
+ }
48
+ const { chunks, xLookupPoints: seriesXLookupPoints } = fn({
49
+ data: list,
50
+ position,
51
+ boundsWidth,
52
+ boundsHeight,
53
+ });
54
+ result.push(...chunks);
55
+ if (seriesXLookupPoints) {
56
+ xLookupPoints.push(...seriesXLookupPoints);
315
57
  }
316
- });
317
- if (closestPointsByXValue.length) {
318
- result.push(...getClosestPointsByXValue(pointerX, pointerY, closestPointsByXValue));
319
58
  }
320
- return result;
321
- }
322
- function isInsidePath(args) {
323
- const { path, point, width, height, strokeWidth } = args;
324
- const canvas = document.createElement('canvas');
325
- canvas.width = width;
326
- canvas.height = height;
327
- const ctx = canvas.getContext('2d');
328
- if (ctx) {
329
- ctx.lineWidth = strokeWidth;
330
- const path2D = new Path2D(path);
331
- ctx.stroke(path2D);
332
- return ctx.isPointInPath(path2D, ...point) || ctx.isPointInStroke(path2D, ...point);
59
+ if (xLookupPoints.length) {
60
+ result.push(...getClosestPointsByXValue(pointerX, pointerY, xLookupPoints));
333
61
  }
334
- return null;
335
- }
336
- function getRadius(args) {
337
- const { pointer, center } = args;
338
- const x = pointer[0] - center[0];
339
- const y = pointer[1] - center[1];
340
- const polarRadius = Math.sqrt(x * x + y * y);
341
- return polarRadius;
62
+ return result;
342
63
  }
@@ -0,0 +1,43 @@
1
+ import type { ChartSeries, ChartSeriesData, MeaningfulAny, TooltipDataChunk } from '../../types';
2
+ export type ShapePoint = {
3
+ x: number;
4
+ y0: number;
5
+ y1: number;
6
+ data: ChartSeriesData;
7
+ series: ChartSeries;
8
+ subTotal?: number;
9
+ sourceX?: number;
10
+ };
11
+ export interface GetTooltipDataArgs<TShapeData = unknown> {
12
+ data: TShapeData[];
13
+ position: [number, number];
14
+ boundsWidth: number;
15
+ boundsHeight: number;
16
+ }
17
+ export interface GetTooltipDataResult {
18
+ chunks: TooltipDataChunk[];
19
+ xLookupPoints?: ShapePoint[];
20
+ }
21
+ export type GetTooltipDataFn = (args: GetTooltipDataArgs<MeaningfulAny>) => GetTooltipDataResult;
22
+ export declare function getClosestYIndex(items: ShapePoint[], y: number): number;
23
+ export declare function getClosestPointsByXValue(x: number, y: number, points: ShapePoint[]): {
24
+ closest: boolean;
25
+ x: number;
26
+ y0: number;
27
+ y1: number;
28
+ data: ChartSeriesData;
29
+ series: ChartSeries;
30
+ subTotal?: number;
31
+ sourceX?: number;
32
+ }[];
33
+ export declare function isInsidePath(args: {
34
+ path: string;
35
+ point: [number, number];
36
+ width: number;
37
+ height: number;
38
+ strokeWidth: number;
39
+ }): boolean | null;
40
+ export declare function getRadius(args: {
41
+ pointer: [number, number];
42
+ center: [number, number];
43
+ }): number;
@@ -0,0 +1,72 @@
1
+ import { bisector, sort } from 'd3-array';
2
+ import get from 'lodash/get';
3
+ import groupBy from 'lodash/groupBy';
4
+ export function getClosestYIndex(items, y) {
5
+ var _a, _b, _c, _d;
6
+ let closestYIndex = -1;
7
+ if (y < ((_a = items[0]) === null || _a === void 0 ? void 0 : _a.y0)) {
8
+ closestYIndex = 0;
9
+ }
10
+ else if (y > ((_b = items[items.length - 1]) === null || _b === void 0 ? void 0 : _b.y1)) {
11
+ closestYIndex = items.length - 1;
12
+ }
13
+ else {
14
+ closestYIndex = items.findIndex((p) => y > p.y0 && y < p.y1);
15
+ if (closestYIndex === -1) {
16
+ const sortedY = sort(items.map((p, index) => ({ index, y: p.y1 + (p.y0 - p.y1) / 2 })), (p) => p.y);
17
+ const sortedYIndex = bisector((p) => p.y).center(sortedY, y);
18
+ closestYIndex = (_d = (_c = sortedY[sortedYIndex]) === null || _c === void 0 ? void 0 : _c.index) !== null && _d !== void 0 ? _d : -1;
19
+ }
20
+ }
21
+ return closestYIndex;
22
+ }
23
+ export function getClosestPointsByXValue(x, y, points) {
24
+ var _a;
25
+ const sorted = sort(points, (p) => p.x);
26
+ const closestXIndex = bisector((p) => p.x).center(sorted, x);
27
+ if (sorted.length === 0 || closestXIndex === -1) {
28
+ return [];
29
+ }
30
+ const closestX = Math.round(sorted[closestXIndex].x);
31
+ const filtered = points.filter((p) => Math.round(p.x) === closestX);
32
+ const groupedBySeries = Object.values(groupBy(filtered, (p) => get(p.series, 'id'))).map((items) => {
33
+ const sortedByY = sort(items, (p) => p.y0);
34
+ const index = getClosestYIndex(sortedByY, y);
35
+ return sortedByY[index === -1 ? 0 : index];
36
+ });
37
+ const closestPoints = sort(groupedBySeries, (p) => p.y0);
38
+ const pointsWithSourceX = closestPoints.filter((p) => p.sourceX !== undefined);
39
+ const uniqueSourceX = new Set(pointsWithSourceX.map((p) => p.sourceX));
40
+ if (pointsWithSourceX.length > 1 && uniqueSourceX.size > 1) {
41
+ const sortedBySourceX = sort(pointsWithSourceX, (p) => { var _a; return (_a = p.sourceX) !== null && _a !== void 0 ? _a : 0; });
42
+ const closestSourceXIdx = bisector((p) => { var _a; return (_a = p.sourceX) !== null && _a !== void 0 ? _a : 0; }).center(sortedBySourceX, x);
43
+ const closestSourceX = (_a = sortedBySourceX[closestSourceXIdx]) === null || _a === void 0 ? void 0 : _a.sourceX;
44
+ const candidates = closestPoints.filter((p) => p.sourceX === undefined || p.sourceX === closestSourceX);
45
+ const sortedCandidates = sort(candidates, (p) => p.y0);
46
+ const winnerIdx = getClosestYIndex(sortedCandidates, y);
47
+ const winner = sortedCandidates[winnerIdx === -1 ? 0 : winnerIdx];
48
+ return closestPoints.map((p) => (Object.assign(Object.assign({}, p), { closest: p === winner })));
49
+ }
50
+ const closestYIndex = getClosestYIndex(closestPoints, y);
51
+ return closestPoints.map((p, i) => (Object.assign(Object.assign({}, p), { closest: i === closestYIndex })));
52
+ }
53
+ export function isInsidePath(args) {
54
+ const { path, point, width, height, strokeWidth } = args;
55
+ const canvas = document.createElement('canvas');
56
+ canvas.width = width;
57
+ canvas.height = height;
58
+ const ctx = canvas.getContext('2d');
59
+ if (ctx) {
60
+ ctx.lineWidth = strokeWidth;
61
+ const path2D = new Path2D(path);
62
+ ctx.stroke(path2D);
63
+ return ctx.isPointInPath(path2D, ...point) || ctx.isPointInStroke(path2D, ...point);
64
+ }
65
+ return null;
66
+ }
67
+ export function getRadius(args) {
68
+ const { pointer, center } = args;
69
+ const x = pointer[0] - center[0];
70
+ const y = pointer[1] - center[1];
71
+ return Math.sqrt(x * x + y * y);
72
+ }
@@ -1,3 +1,4 @@
1
+ import './plugins';
1
2
  export { CustomShapeRenderer } from './utils';
2
3
  export { getFormattedValue } from './core/utils';
3
4
  export { getDefaultTooltipHeaderFormat } from './core/utils/tooltip';
package/dist/cjs/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import './plugins';
1
2
  export { CustomShapeRenderer } from './utils';
2
3
  export { getFormattedValue } from './core/utils';
3
4
  export { getDefaultTooltipHeaderFormat } from './core/utils/tooltip';
@@ -0,0 +1,3 @@
1
+ import type { SeriesPlugin } from '../../core/series/plugin';
2
+ import type { AreaSeries } from '../../types';
3
+ export declare const areaPlugin: SeriesPlugin<AreaSeries>;
@@ -0,0 +1,5 @@
1
+ import { prepareArea } from './prepare';
2
+ export const areaPlugin = {
3
+ type: 'area',
4
+ prepareSeries: ({ series, seriesOptions, legend, colorScale }) => prepareArea({ series: series, seriesOptions, legend, colorScale }),
5
+ };
@@ -1,10 +1,10 @@
1
1
  import type { ScaleOrdinal } from 'd3-scale';
2
+ import type { PreparedAreaSeries, PreparedLegend } from '../../core/series/types';
2
3
  import type { AreaSeries, ChartSeriesOptions } from '../../types';
3
- import type { PreparedAreaSeries, PreparedLegend } from './types';
4
4
  export declare const DEFAULT_LINE_WIDTH = 1;
5
5
  export declare const DEFAULT_MARKER: {
6
6
  enabled: boolean;
7
- symbol: `${import("../constants").SymbolType}`;
7
+ symbol: `${import("../../core/constants").SymbolType}`;
8
8
  borderColor: string;
9
9
  borderWidth: number;
10
10
  radius: number;
@@ -1,9 +1,9 @@
1
1
  import get from 'lodash/get';
2
2
  import merge from 'lodash/merge';
3
- import { DEFAULT_DATALABELS_STYLE, seriesRangeSliderOptionsDefaults } from '../constants';
4
- import { getUniqId } from '../utils';
5
- import { DEFAULT_DATALABELS_PADDING, DEFAULT_HALO_OPTIONS, DEFAULT_POINT_MARKER_OPTIONS, } from './constants';
6
- import { getSeriesStackId, prepareLegendSymbol } from './utils';
3
+ import { DEFAULT_DATALABELS_STYLE, seriesRangeSliderOptionsDefaults } from '../../core/constants';
4
+ import { DEFAULT_DATALABELS_PADDING, DEFAULT_HALO_OPTIONS, DEFAULT_POINT_MARKER_OPTIONS, } from '../../core/series/constants';
5
+ import { getSeriesStackId, prepareLegendSymbol } from '../../core/series/utils';
6
+ import { getUniqId } from '../../core/utils';
7
7
  export const DEFAULT_LINE_WIDTH = 1;
8
8
  export const DEFAULT_MARKER = Object.assign(Object.assign({}, DEFAULT_POINT_MARKER_OPTIONS), { enabled: false });
9
9
  function prepareMarker(series, seriesOptions) {
@@ -0,0 +1,3 @@
1
+ import type { SeriesPlugin } from '../../core/series/plugin';
2
+ import type { BarXSeries } from '../../types';
3
+ export declare const barXPlugin: SeriesPlugin<BarXSeries>;
@@ -0,0 +1,5 @@
1
+ import { prepareBarXSeries } from './prepare';
2
+ export const barXPlugin = {
3
+ type: 'bar-x',
4
+ prepareSeries: ({ series, seriesOptions, legend, colorScale }) => prepareBarXSeries({ series: series, seriesOptions, legend, colorScale }),
5
+ };