@gravity-ui/chartkit 4.7.3 → 4.9.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 (86) hide show
  1. package/build/plugins/d3/examples/bar-x/GroupedColumns.js +4 -2
  2. package/build/plugins/d3/examples/bar-y/Basic.d.ts +2 -0
  3. package/build/plugins/d3/examples/bar-y/Basic.js +43 -0
  4. package/build/plugins/d3/examples/bar-y/GroupedColumns.d.ts +2 -0
  5. package/build/plugins/d3/examples/bar-y/GroupedColumns.js +48 -0
  6. package/build/plugins/d3/examples/bar-y/StackedColumns.d.ts +2 -0
  7. package/build/plugins/d3/examples/bar-y/StackedColumns.js +47 -0
  8. package/build/plugins/d3/examples/combined/LineAndBarX.d.ts +2 -0
  9. package/build/plugins/d3/examples/combined/LineAndBarX.js +61 -0
  10. package/build/plugins/d3/examples/line/Basic.d.ts +2 -0
  11. package/build/plugins/d3/examples/line/Basic.js +66 -0
  12. package/build/plugins/d3/examples/nintendoGames.d.ts +40 -10
  13. package/build/plugins/d3/examples/nintendoGames.js +2416 -2189
  14. package/build/plugins/d3/renderer/D3Widget.js +27 -11
  15. package/build/plugins/d3/renderer/components/Chart.d.ts +0 -2
  16. package/build/plugins/d3/renderer/components/Chart.js +4 -6
  17. package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.d.ts +2 -2
  18. package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.js +42 -35
  19. package/build/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.d.ts +0 -2
  20. package/build/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.js +118 -48
  21. package/build/plugins/d3/renderer/components/Tooltip/index.d.ts +1 -0
  22. package/build/plugins/d3/renderer/components/Tooltip/index.js +5 -4
  23. package/build/plugins/d3/renderer/components/styles.css +1 -0
  24. package/build/plugins/d3/renderer/constants/defaults/series-options.d.ts +9 -2
  25. package/build/plugins/d3/renderer/constants/defaults/series-options.js +27 -0
  26. package/build/plugins/d3/renderer/hooks/useAxisScales/index.js +15 -1
  27. package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +3 -3
  28. package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +3 -3
  29. package/build/plugins/d3/renderer/hooks/useSeries/constants.d.ts +3 -0
  30. package/build/plugins/d3/renderer/hooks/useSeries/constants.js +5 -0
  31. package/build/plugins/d3/renderer/hooks/useSeries/index.d.ts +1 -1
  32. package/build/plugins/d3/renderer/hooks/useSeries/index.js +2 -1
  33. package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-x.d.ts +10 -0
  34. package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-x.js +38 -0
  35. package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-y.d.ts +10 -0
  36. package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-y.js +50 -0
  37. package/build/plugins/d3/renderer/hooks/useSeries/prepare-line-series.d.ts +11 -0
  38. package/build/plugins/d3/renderer/hooks/useSeries/prepare-line-series.js +32 -0
  39. package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.d.ts +2 -1
  40. package/build/plugins/d3/renderer/hooks/useSeries/prepareSeries.js +21 -60
  41. package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +24 -2
  42. package/build/plugins/d3/renderer/hooks/useSeries/utils.d.ts +3 -1
  43. package/build/plugins/d3/renderer/hooks/useSeries/utils.js +13 -0
  44. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +4 -6
  45. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.d.ts +11 -0
  46. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.js +87 -0
  47. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.d.ts +12 -0
  48. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +114 -0
  49. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/types.d.ts +10 -0
  50. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/types.js +1 -0
  51. package/build/plugins/d3/renderer/hooks/useShapes/constants.d.ts +3 -0
  52. package/build/plugins/d3/renderer/hooks/useShapes/constants.js +3 -0
  53. package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +4 -4
  54. package/build/plugins/d3/renderer/hooks/useShapes/index.js +35 -5
  55. package/build/plugins/d3/renderer/hooks/useShapes/line/index.d.ts +11 -0
  56. package/build/plugins/d3/renderer/hooks/useShapes/line/index.js +98 -0
  57. package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.d.ts +11 -0
  58. package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.js +21 -0
  59. package/build/plugins/d3/renderer/hooks/useShapes/line/types.d.ts +16 -0
  60. package/build/plugins/d3/renderer/hooks/useShapes/line/types.js +1 -0
  61. package/build/plugins/d3/renderer/hooks/useShapes/pie.d.ts +0 -2
  62. package/build/plugins/d3/renderer/hooks/useShapes/pie.js +3 -3
  63. package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.d.ts +0 -2
  64. package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.js +6 -6
  65. package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.d.ts +1 -1
  66. package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.js +3 -34
  67. package/build/plugins/d3/renderer/hooks/useShapes/styles.css +6 -0
  68. package/build/plugins/d3/renderer/hooks/useShapes/utils.d.ts +17 -0
  69. package/build/plugins/d3/renderer/hooks/useShapes/utils.js +25 -0
  70. package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +3 -3
  71. package/build/plugins/d3/renderer/utils/text.d.ts +5 -7
  72. package/build/plugins/d3/renderer/utils/text.js +4 -15
  73. package/build/types/widget-data/bar-y.d.ts +55 -0
  74. package/build/types/widget-data/bar-y.js +1 -0
  75. package/build/types/widget-data/base.d.ts +4 -0
  76. package/build/types/widget-data/index.d.ts +2 -0
  77. package/build/types/widget-data/index.js +2 -0
  78. package/build/types/widget-data/line.d.ts +37 -0
  79. package/build/types/widget-data/line.js +1 -0
  80. package/build/types/widget-data/series.d.ts +53 -3
  81. package/build/types/widget-data/tooltip.d.ts +15 -1
  82. package/build/utils/index.d.ts +1 -1
  83. package/build/utils/index.js +1 -1
  84. package/build/utils/performance.d.ts +3 -0
  85. package/build/utils/performance.js +8 -0
  86. package/package.json +3 -2
@@ -1,28 +1,44 @@
1
1
  import React from 'react';
2
2
  import { select } from 'd3';
3
3
  import debounce from 'lodash/debounce';
4
- import { getRandomCKId } from '../../../utils';
4
+ import afterFrame from 'afterframe';
5
+ import { getRandomCKId, measurePerformance } from '../../../utils';
5
6
  import { Chart } from './components';
6
7
  const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
7
- const { data, onLoad, onRender } = props;
8
+ const { data, onLoad, onRender, onChartLoad } = props;
8
9
  const ref = React.useRef(null);
9
10
  const debounced = React.useRef();
10
11
  const [dimensions, setDimensions] = React.useState();
11
- //FIXME: add chartPerfomance data to callbacks;
12
+ const performanceMeasure = React.useRef(measurePerformance());
12
13
  React.useLayoutEffect(() => {
13
- if (onLoad) {
14
- onLoad({});
14
+ if (onChartLoad) {
15
+ onChartLoad({});
15
16
  }
16
- if (onRender) {
17
- onRender({});
17
+ }, [onChartLoad]);
18
+ React.useLayoutEffect(() => {
19
+ if (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) {
20
+ if (!performanceMeasure.current) {
21
+ performanceMeasure.current = measurePerformance();
22
+ }
23
+ afterFrame(() => {
24
+ var _a;
25
+ const renderTime = (_a = performanceMeasure.current) === null || _a === void 0 ? void 0 : _a.end();
26
+ onRender === null || onRender === void 0 ? void 0 : onRender({
27
+ renderTime,
28
+ });
29
+ onLoad === null || onLoad === void 0 ? void 0 : onLoad({
30
+ widgetRendering: renderTime,
31
+ });
32
+ performanceMeasure.current = null;
33
+ });
18
34
  }
19
- }, []);
35
+ }, [data, onRender, onLoad, dimensions]);
20
36
  const handleResize = React.useCallback(() => {
21
37
  var _a;
22
38
  const parentElement = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.parentElement;
23
39
  if (parentElement) {
24
- const { top, left, width, height } = parentElement.getBoundingClientRect();
25
- setDimensions({ top, left, width, height });
40
+ const { width, height } = parentElement.getBoundingClientRect();
41
+ setDimensions({ width, height });
26
42
  }
27
43
  }, []);
28
44
  const debuncedHandleResize = React.useMemo(() => {
@@ -54,6 +70,6 @@ const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
54
70
  width: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) || '100%',
55
71
  height: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) || '100%',
56
72
  position: 'relative',
57
- } }, (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) && (React.createElement(Chart, { top: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.top) || 0, left: dimensions.left || 0, width: dimensions.width, height: dimensions.height, data: data }))));
73
+ } }, (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) && (React.createElement(Chart, { width: dimensions.width, height: dimensions.height, data: data }))));
58
74
  });
59
75
  export default D3Widget;
@@ -2,8 +2,6 @@ import React from 'react';
2
2
  import type { ChartKitWidgetData } from '../../../../types';
3
3
  import './styles.css';
4
4
  type Props = {
5
- top: number;
6
- left: number;
7
5
  width: number;
8
6
  height: number;
9
7
  data: ChartKitWidgetData;
@@ -14,8 +14,8 @@ import './styles.css';
14
14
  const b = block('d3');
15
15
  export const Chart = (props) => {
16
16
  // FIXME: add data validation
17
- const { top, left, width, height, data } = props;
18
- const svgRef = React.createRef();
17
+ const { width, height, data } = props;
18
+ const svgRef = React.useRef(null);
19
19
  const dispatcher = React.useMemo(() => {
20
20
  return getD3Dispatcher();
21
21
  }, []);
@@ -50,8 +50,6 @@ export const Chart = (props) => {
50
50
  });
51
51
  const { hovered, pointerPosition } = useTooltip({ dispatcher, tooltip });
52
52
  const { shapes, shapesData } = useShapes({
53
- top,
54
- left,
55
53
  boundsWidth,
56
54
  boundsHeight,
57
55
  dispatcher,
@@ -74,7 +72,7 @@ export const Chart = (props) => {
74
72
  React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
75
73
  React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale })))),
76
74
  shapes,
77
- (tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabled) && Boolean(shapesData.length) && (React.createElement(TooltipTriggerArea, { boundsWidth: boundsWidth, boundsHeight: boundsHeight, dispatcher: dispatcher, offsetLeft: left, offsetTop: top, shapesData: shapesData, svgContainer: svgRef.current }))),
75
+ (tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabled) && Boolean(shapesData.length) && (React.createElement(TooltipTriggerArea, { boundsWidth: boundsWidth, boundsHeight: boundsHeight, dispatcher: dispatcher, shapesData: shapesData, svgContainer: svgRef.current }))),
78
76
  preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick }))),
79
- React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: tooltip, xAxis: xAxis, yAxis: yAxis[0], hovered: hovered, pointerPosition: pointerPosition })));
77
+ React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: tooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0], hovered: hovered, pointerPosition: pointerPosition })));
80
78
  };
@@ -2,9 +2,9 @@ import React from 'react';
2
2
  import type { TooltipDataChunk } from '../../../../../types';
3
3
  import type { PreparedAxis } from '../../hooks';
4
4
  type Props = {
5
- hovered: TooltipDataChunk;
5
+ hovered: TooltipDataChunk[];
6
6
  xAxis: PreparedAxis;
7
7
  yAxis: PreparedAxis;
8
8
  };
9
- export declare const DefaultContent: ({ hovered, xAxis, yAxis }: Props) => React.JSX.Element | null;
9
+ export declare const DefaultContent: ({ hovered, xAxis, yAxis }: Props) => React.JSX.Element;
10
10
  export {};
@@ -5,13 +5,16 @@ import { formatNumber } from '../../../../shared';
5
5
  import { getDataCategoryValue } from '../../utils';
6
6
  const DEFAULT_DATE_FORMAT = 'DD.MM.YY';
7
7
  const getRowData = (fieldName, axis, data) => {
8
- const categories = get(axis, 'categories', []);
9
8
  switch (axis.type) {
10
9
  case 'category': {
10
+ const categories = get(axis, 'categories', []);
11
11
  return getDataCategoryValue({ axisDirection: fieldName, categories, data });
12
12
  }
13
13
  case 'datetime': {
14
14
  const value = get(data, fieldName);
15
+ if (!value) {
16
+ return undefined;
17
+ }
15
18
  return dateTime({ input: value }).format(DEFAULT_DATE_FORMAT);
16
19
  }
17
20
  case 'linear':
@@ -24,40 +27,44 @@ const getRowData = (fieldName, axis, data) => {
24
27
  const getXRowData = (xAxis, data) => getRowData('x', xAxis, data);
25
28
  const getYRowData = (yAxis, data) => getRowData('y', yAxis, data);
26
29
  export const DefaultContent = ({ hovered, xAxis, yAxis }) => {
27
- const { data, series } = hovered;
28
- switch (series.type) {
29
- case 'scatter': {
30
- const xRow = getXRowData(xAxis, data);
31
- const yRow = getYRowData(yAxis, data);
32
- return (React.createElement("div", null,
33
- React.createElement("div", null,
34
- React.createElement("span", null, "X:\u00A0"),
35
- React.createElement("b", null, xRow)),
36
- React.createElement("div", null,
37
- React.createElement("span", null, "Y:\u00A0"),
38
- React.createElement("b", null, yRow))));
39
- }
40
- case 'bar-x': {
41
- const xRow = getXRowData(xAxis, data);
42
- const yRow = getYRowData(yAxis, data);
43
- return (React.createElement("div", null,
44
- React.createElement("div", null, xRow),
45
- React.createElement("div", null,
30
+ return (React.createElement(React.Fragment, null, hovered.map(({ data, series }, i) => {
31
+ const id = get(series, 'id', i);
32
+ switch (series.type) {
33
+ case 'scatter':
34
+ case 'line':
35
+ case 'bar-x': {
36
+ const xRow = getXRowData(xAxis, data);
37
+ const yRow = getYRowData(yAxis, data);
38
+ return (React.createElement("div", { key: id },
39
+ React.createElement("div", null, xRow),
40
+ React.createElement("div", null,
41
+ React.createElement("span", null,
42
+ React.createElement("b", null, series.name),
43
+ ": ",
44
+ yRow))));
45
+ }
46
+ case 'bar-y': {
47
+ const xRow = getXRowData(xAxis, data);
48
+ const yRow = getYRowData(yAxis, data);
49
+ return (React.createElement("div", { key: id },
50
+ React.createElement("div", null, yRow),
51
+ React.createElement("div", null,
52
+ React.createElement("span", null,
53
+ React.createElement("b", null, series.name),
54
+ ": ",
55
+ xRow))));
56
+ }
57
+ case 'pie': {
58
+ const pieSeries = series;
59
+ return (React.createElement("div", { key: id },
46
60
  React.createElement("span", null,
47
- React.createElement("b", null, series.name),
48
- ": ",
49
- yRow))));
50
- }
51
- case 'pie': {
52
- const pieSeries = series;
53
- return (React.createElement("div", null,
54
- React.createElement("span", null,
55
- pieSeries.name || pieSeries.id,
56
- "\u00A0"),
57
- React.createElement("span", null, pieSeries.value)));
61
+ pieSeries.name || pieSeries.id,
62
+ "\u00A0"),
63
+ React.createElement("span", null, pieSeries.value)));
64
+ }
65
+ default: {
66
+ return null;
67
+ }
58
68
  }
59
- default: {
60
- return null;
61
- }
62
- }
69
+ })));
63
70
  };
@@ -5,8 +5,6 @@ type Args = {
5
5
  boundsWidth: number;
6
6
  boundsHeight: number;
7
7
  dispatcher: Dispatch<object>;
8
- offsetTop: number;
9
- offsetLeft: number;
10
8
  shapesData: ShapeData[];
11
9
  svgContainer: SVGSVGElement | null;
12
10
  };
@@ -1,66 +1,136 @@
1
1
  import React from 'react';
2
2
  import throttle from 'lodash/throttle';
3
- import { bisector, pointer, sort } from 'd3';
3
+ import { bisector, pointer, sort, group } from 'd3';
4
4
  import { extractD3DataFromNode, isNodeContainsD3Data } from '../../utils';
5
5
  const THROTTLE_DELAY = 50;
6
6
  const isNodeContainsData = (node) => {
7
7
  return isNodeContainsD3Data(node);
8
8
  };
9
- const getCalculationType = (shapesData) => {
10
- if (shapesData.every((d) => d.series.type === 'bar-x')) {
11
- return 'x-primary';
9
+ function getBarXShapeData(args) {
10
+ var _a;
11
+ const { shapesData, point: [pointerX, pointerY], top, left, xData, container, } = args;
12
+ const barWidthOffset = shapesData[0].width / 2;
13
+ const xPosition = pointerX - left - barWidthOffset;
14
+ const xDataIndex = bisector((d) => d.x).center(xData, xPosition);
15
+ const xNodes = Array.from((container === null || container === void 0 ? void 0 : container.querySelectorAll(`[x="${(_a = xData[xDataIndex]) === null || _a === void 0 ? void 0 : _a.x}"]`)) || []);
16
+ if (xNodes.length === 1 && isNodeContainsData(xNodes[0])) {
17
+ return [extractD3DataFromNode(xNodes[0])];
12
18
  }
13
- return 'none';
14
- };
19
+ if (xNodes.length > 1 && xNodes.every(isNodeContainsData)) {
20
+ const yPosition = pointerY - top;
21
+ const xyNode = xNodes.find((node, i) => {
22
+ const { y, height } = extractD3DataFromNode(node);
23
+ if (i === xNodes.length - 1) {
24
+ return yPosition <= y + height;
25
+ }
26
+ return yPosition >= y && yPosition <= y + height;
27
+ });
28
+ if (xyNode) {
29
+ return [extractD3DataFromNode(xyNode)];
30
+ }
31
+ }
32
+ return [];
33
+ }
34
+ function getLineShapesData(args) {
35
+ const { xData, point: [pointerX], } = args;
36
+ const xDataIndex = bisector((d) => d.x).center(xData, pointerX);
37
+ const selectedLineShape = xData[xDataIndex];
38
+ if (selectedLineShape) {
39
+ return [
40
+ {
41
+ series: selectedLineShape.series,
42
+ data: selectedLineShape.data,
43
+ },
44
+ ];
45
+ }
46
+ return [];
47
+ }
48
+ function getBarYData(args) {
49
+ var _a;
50
+ const { data, point: [pointerX, pointerY], } = args;
51
+ const yDataIndex = bisector((d) => d.y).center(data, pointerY);
52
+ const shapesByY = ((_a = data[yDataIndex]) === null || _a === void 0 ? void 0 : _a.items) || [];
53
+ const xDataIndex = bisector((d) => d.x).left(shapesByY, pointerX);
54
+ const result = shapesByY[Math.min(xDataIndex, shapesByY.length - 1)];
55
+ return result
56
+ ? [
57
+ {
58
+ series: result.series,
59
+ data: result.data,
60
+ },
61
+ ]
62
+ : [];
63
+ }
15
64
  export const TooltipTriggerArea = (args) => {
16
- const { boundsWidth, boundsHeight, dispatcher, offsetTop, offsetLeft, shapesData, svgContainer } = args;
65
+ const { boundsWidth, boundsHeight, dispatcher, shapesData, svgContainer } = args;
17
66
  const rectRef = React.useRef(null);
18
- const calculationType = React.useMemo(() => {
19
- return getCalculationType(shapesData);
67
+ const xBarData = React.useMemo(() => {
68
+ const result = shapesData
69
+ .filter((sd) => sd.series.type === 'bar-x')
70
+ .map((sd) => ({ x: sd.x, data: sd }));
71
+ return sort(result, (item) => item.x);
72
+ }, [shapesData]);
73
+ const xLineData = React.useMemo(() => {
74
+ const result = shapesData
75
+ .filter((sd) => sd.series.type === 'line')
76
+ .reduce((acc, sd) => {
77
+ return acc.concat(sd.points.map((d) => ({
78
+ x: d.x,
79
+ data: d.data,
80
+ series: sd.series,
81
+ })));
82
+ }, []);
83
+ return sort(result, (item) => item.x);
20
84
  }, [shapesData]);
21
- const xData = React.useMemo(() => {
22
- return calculationType === 'x-primary'
23
- ? sort(new Set(shapesData.map((d) => d.x)))
24
- : [];
25
- }, [shapesData, calculationType]);
26
- const handleXprimaryMouseMove = (e) => {
27
- var _a, _b, _c;
28
- const { left, top } = ((_a = rectRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) || { left: 0, top: 0 };
85
+ const barYData = React.useMemo(() => {
86
+ const barYShapeData = shapesData.filter((sd) => sd.series.type === 'bar-y');
87
+ const result = Array.from(group(barYShapeData, (sd) => sd.y)).map(([y, shapes]) => {
88
+ const yValue = y + shapes[0].height / 2;
89
+ return {
90
+ y: yValue,
91
+ items: sort(shapes.map((shape) => {
92
+ const preparedData = shape;
93
+ return {
94
+ x: preparedData.x + preparedData.width,
95
+ data: preparedData.data,
96
+ series: shape.series,
97
+ };
98
+ }), (item) => item.x),
99
+ };
100
+ });
101
+ return sort(result, (item) => item.y);
102
+ }, [shapesData]);
103
+ const handleMouseMove = (e) => {
104
+ var _a, _b;
105
+ const { left: ownLeft, top: ownTop } = ((_a = rectRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) || {
106
+ left: 0,
107
+ top: 0,
108
+ };
109
+ const { left: containerLeft, top: containerTop } = (svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) || {
110
+ left: 0,
111
+ top: 0,
112
+ };
29
113
  const [pointerX, pointerY] = pointer(e, svgContainer);
30
- const barWidthOffset = shapesData[0].width / 2;
31
- const xPosition = pointerX - left - barWidthOffset - window.pageXOffset;
32
- const xDataIndex = bisector((d) => d).center(xData, xPosition);
33
- const xNodes = Array.from(((_c = (_b = rectRef.current) === null || _b === void 0 ? void 0 : _b.parentElement) === null || _c === void 0 ? void 0 : _c.querySelectorAll(`[x="${xData[xDataIndex]}"]`)) || []);
34
- let hoverShapeData;
35
- if (xNodes.length === 1 && isNodeContainsData(xNodes[0])) {
36
- hoverShapeData = [extractD3DataFromNode(xNodes[0])];
37
- }
38
- else if (xNodes.length > 1 && xNodes.every(isNodeContainsData)) {
39
- const yPosition = pointerY - top - window.pageYOffset;
40
- const xyNode = xNodes.find((node, i) => {
41
- const { y, height } = extractD3DataFromNode(node);
42
- if (i === xNodes.length - 1) {
43
- return yPosition <= y + height;
44
- }
45
- return yPosition >= y && yPosition <= y + height;
46
- });
47
- if (xyNode) {
48
- hoverShapeData = [extractD3DataFromNode(xyNode)];
49
- }
50
- }
51
- if (hoverShapeData) {
52
- const position = [pointerX - offsetLeft, pointerY - offsetTop];
114
+ const hoverShapeData = [];
115
+ hoverShapeData === null || hoverShapeData === void 0 ? void 0 : hoverShapeData.push(...getBarXShapeData({
116
+ shapesData,
117
+ point: [pointerX, pointerY],
118
+ left: ownLeft - containerLeft,
119
+ top: ownTop - containerTop,
120
+ xData: xBarData,
121
+ container: (_b = rectRef.current) === null || _b === void 0 ? void 0 : _b.parentElement,
122
+ }), ...getLineShapesData({
123
+ xData: xLineData,
124
+ point: [pointerX - (ownLeft - containerLeft), pointerY - (ownTop - containerTop)],
125
+ }), ...getBarYData({
126
+ data: barYData,
127
+ point: [pointerX - (ownLeft - containerLeft), pointerY - (ownTop - containerTop)],
128
+ }));
129
+ if (hoverShapeData.length) {
130
+ const position = [pointerX, pointerY];
53
131
  dispatcher.call('hover-shape', e.target, hoverShapeData, position);
54
132
  }
55
133
  };
56
- const handleMouseMove = (e) => {
57
- switch (calculationType) {
58
- case 'x-primary': {
59
- handleXprimaryMouseMove(e);
60
- return;
61
- }
62
- }
63
- };
64
134
  const throttledHandleMouseMove = throttle(handleMouseMove, THROTTLE_DELAY);
65
135
  const handleMouseLeave = () => {
66
136
  throttledHandleMouseMove.cancel();
@@ -6,6 +6,7 @@ export * from './TooltipTriggerArea';
6
6
  type TooltipProps = {
7
7
  dispatcher: Dispatch<object>;
8
8
  tooltip: PreparedTooltip;
9
+ svgContainer: SVGSVGElement | null;
9
10
  xAxis: PreparedAxis;
10
11
  yAxis: PreparedAxis;
11
12
  hovered?: TooltipDataChunk[];
@@ -6,7 +6,7 @@ export * from './TooltipTriggerArea';
6
6
  const b = block('d3-tooltip');
7
7
  const POINTER_OFFSET_X = 20;
8
8
  export const Tooltip = (props) => {
9
- const { tooltip, xAxis, yAxis, hovered, pointerPosition } = props;
9
+ const { tooltip, svgContainer, xAxis, yAxis, hovered, pointerPosition } = props;
10
10
  const ref = React.useRef(null);
11
11
  const size = React.useMemo(() => {
12
12
  if (ref.current && hovered) {
@@ -20,8 +20,9 @@ export const Tooltip = (props) => {
20
20
  if (hovered && pointerPosition && size) {
21
21
  const { clientWidth } = document.documentElement;
22
22
  const { width, height } = size;
23
+ const rect = (svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) || { left: 0, top: 0 };
23
24
  const [pointerLeft, pointetTop] = pointerPosition;
24
- const outOfRightBoudary = pointerLeft + width + POINTER_OFFSET_X >= clientWidth;
25
+ const outOfRightBoudary = pointerLeft + width + rect.left + POINTER_OFFSET_X >= clientWidth;
25
26
  const outOfTopBoundary = pointetTop - height / 2 <= 0;
26
27
  const left = outOfRightBoudary
27
28
  ? pointerLeft - width - POINTER_OFFSET_X
@@ -33,14 +34,14 @@ export const Tooltip = (props) => {
33
34
  return { left: -1000, top: -1000 };
34
35
  }
35
36
  return undefined;
36
- }, [hovered, pointerPosition, size]);
37
+ }, [hovered, pointerPosition, size, svgContainer]);
37
38
  const content = React.useMemo(() => {
38
39
  var _a;
39
40
  if (!hovered) {
40
41
  return null;
41
42
  }
42
43
  const customTooltip = (_a = tooltip.renderer) === null || _a === void 0 ? void 0 : _a.call(tooltip, { hovered });
43
- return isNil(customTooltip) ? (React.createElement(DefaultContent, { hovered: hovered[0], xAxis: xAxis, yAxis: yAxis })) : (customTooltip);
44
+ return isNil(customTooltip) ? (React.createElement(DefaultContent, { hovered: hovered, xAxis: xAxis, yAxis: yAxis })) : (customTooltip);
44
45
  }, [hovered, tooltip, xAxis, yAxis]);
45
46
  if (!position || !hovered) {
46
47
  return null;
@@ -79,4 +79,5 @@
79
79
  border-radius: 3px;
80
80
  box-shadow: 0 2px 12px var(--g-color-sfx-shadow);
81
81
  z-index: 100001;
82
+ text-wrap: nowrap;
82
83
  }
@@ -1,11 +1,18 @@
1
1
  import type { ChartKitWidgetSeriesOptions } from '../../../../../types';
2
- type DefauleBarXSeriesOptions = Partial<ChartKitWidgetSeriesOptions['bar-x']> & {
2
+ type DefaultBarXSeriesOptions = Partial<ChartKitWidgetSeriesOptions['bar-x']> & {
3
3
  'bar-x': {
4
4
  barMaxWidth: number;
5
5
  barPadding: number;
6
6
  groupPadding: number;
7
7
  };
8
8
  };
9
- export type SeriesOptionsDefaults = Partial<ChartKitWidgetSeriesOptions> & DefauleBarXSeriesOptions;
9
+ type DefaultBarYSeriesOptions = Partial<ChartKitWidgetSeriesOptions['bar-x']> & {
10
+ 'bar-y': {
11
+ barMaxWidth: number;
12
+ barPadding: number;
13
+ groupPadding: number;
14
+ };
15
+ };
16
+ export type SeriesOptionsDefaults = Partial<ChartKitWidgetSeriesOptions> & DefaultBarXSeriesOptions & DefaultBarYSeriesOptions;
10
17
  export declare const seriesOptionsDefaults: SeriesOptionsDefaults;
11
18
  export {};
@@ -14,6 +14,21 @@ export const seriesOptionsDefaults = {
14
14
  },
15
15
  },
16
16
  },
17
+ 'bar-y': {
18
+ barMaxWidth: 50,
19
+ barPadding: 0.1,
20
+ groupPadding: 0.2,
21
+ states: {
22
+ hover: {
23
+ enabled: true,
24
+ brightness: 0.3,
25
+ },
26
+ inactive: {
27
+ enabled: true,
28
+ opacity: 0.5,
29
+ },
30
+ },
31
+ },
17
32
  pie: {
18
33
  states: {
19
34
  hover: {
@@ -38,4 +53,16 @@ export const seriesOptionsDefaults = {
38
53
  },
39
54
  },
40
55
  },
56
+ line: {
57
+ states: {
58
+ hover: {
59
+ enabled: true,
60
+ brightness: 0.3,
61
+ },
62
+ inactive: {
63
+ enabled: true,
64
+ opacity: 0.5,
65
+ },
66
+ },
67
+ },
41
68
  };
@@ -62,13 +62,27 @@ export function createYScale(axis, series, boundsHeight) {
62
62
  }
63
63
  throw new Error('Failed to create yScale');
64
64
  }
65
+ function calculateXAxisPadding(series) {
66
+ let result = 0;
67
+ series.forEach((s) => {
68
+ switch (s.type) {
69
+ case 'bar-y': {
70
+ // Since labels can be located to the right of the bar, need to add an additional space
71
+ const labelsMaxWidth = get(s, 'dataLabels.maxWidth', 0);
72
+ result = Math.max(result, labelsMaxWidth);
73
+ break;
74
+ }
75
+ }
76
+ });
77
+ return result;
78
+ }
65
79
  export function createXScale(axis, series, boundsWidth) {
66
80
  const xMin = get(axis, 'min');
67
81
  const xType = get(axis, 'type', 'linear');
68
82
  const xCategories = get(axis, 'categories');
69
83
  const xTimestamps = get(axis, 'timestamps');
70
84
  const maxPadding = get(axis, 'maxPadding', 0);
71
- const xAxisMinPadding = boundsWidth * maxPadding;
85
+ const xAxisMinPadding = boundsWidth * maxPadding + calculateXAxisPadding(series);
72
86
  const xRange = [0, boundsWidth - xAxisMinPadding];
73
87
  switch (xType) {
74
88
  case 'linear': {
@@ -1,6 +1,6 @@
1
1
  import get from 'lodash/get';
2
2
  import { axisLabelsDefaults, DEFAULT_AXIS_LABEL_FONT_SIZE, xAxisTitleDefaults, } from '../../constants';
3
- import { calculateCos, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsMaxHeight, getMaxTickCount, getTicksCount, getXAxisItems, hasOverlappingLabels, } from '../../utils';
3
+ import { calculateCos, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getMaxTickCount, getTicksCount, getXAxisItems, hasOverlappingLabels, } from '../../utils';
4
4
  import { createXScale } from '../useAxisScales';
5
5
  function getLabelSettings({ axis, series, width, autoRotation = true, }) {
6
6
  const scale = createXScale(axis, series, width);
@@ -27,14 +27,14 @@ function getLabelSettings({ axis, series, width, autoRotation = true, }) {
27
27
  const defaultRotation = overlapping && autoRotation ? -45 : 0;
28
28
  const rotation = axis.labels.rotation || defaultRotation;
29
29
  const labelsHeight = rotation
30
- ? getLabelsMaxHeight({
30
+ ? getLabelsSize({
31
31
  labels,
32
32
  style: {
33
33
  'font-size': axis.labels.style.fontSize,
34
34
  'font-weight': axis.labels.style.fontWeight || 'normal',
35
35
  },
36
36
  rotation,
37
- })
37
+ }).maxHeight
38
38
  : axis.labels.lineHeight;
39
39
  const maxHeight = rotation ? calculateCos(rotation) * axis.labels.maxWidth : labelsHeight;
40
40
  return { height: Math.min(maxHeight, labelsHeight), rotation };
@@ -1,6 +1,6 @@
1
1
  import get from 'lodash/get';
2
2
  import { axisLabelsDefaults, DEFAULT_AXIS_LABEL_FONT_SIZE, yAxisTitleDefaults, } from '../../constants';
3
- import { getHorisontalSvgTextHeight, formatAxisTickLabel, getClosestPointsRange, getScaleTicks, getLabelsMaxWidth, } from '../../utils';
3
+ import { getHorisontalSvgTextHeight, formatAxisTickLabel, getClosestPointsRange, getScaleTicks, getLabelsSize, } from '../../utils';
4
4
  import { createYScale } from '../useAxisScales';
5
5
  const getAxisLabelMaxWidth = (args) => {
6
6
  const { axis, series } = args;
@@ -16,14 +16,14 @@ const getAxisLabelMaxWidth = (args) => {
16
16
  value: tick,
17
17
  step,
18
18
  }));
19
- return getLabelsMaxWidth({
19
+ return getLabelsSize({
20
20
  labels,
21
21
  style: {
22
22
  'font-size': axis.labels.style.fontSize,
23
23
  'font-weight': axis.labels.style.fontWeight || '',
24
24
  },
25
25
  rotation: axis.labels.rotation,
26
- });
26
+ }).maxWidth;
27
27
  };
28
28
  export const getPreparedYAxis = ({ series, yAxis, }) => {
29
29
  // FIXME: add support for n axises
@@ -1 +1,4 @@
1
+ import { BaseTextStyle } from '../../../../../types';
1
2
  export declare const DEFAULT_LEGEND_SYMBOL_SIZE = 8;
3
+ export declare const DEFAULT_DATALABELS_PADDING = 5;
4
+ export declare const DEFAULT_DATALABELS_STYLE: BaseTextStyle;
@@ -1 +1,6 @@
1
1
  export const DEFAULT_LEGEND_SYMBOL_SIZE = 8;
2
+ export const DEFAULT_DATALABELS_PADDING = 5;
3
+ export const DEFAULT_DATALABELS_STYLE = {
4
+ fontSize: '11px',
5
+ fontWeight: 'bold',
6
+ };
@@ -1,4 +1,4 @@
1
- import type { ChartKitWidgetData } from '../../../../../types/widget-data';
1
+ import type { ChartKitWidgetData } from '../../../../../types';
2
2
  import type { PreparedAxis, PreparedChart } from '../useChartOptions/types';
3
3
  import type { PreparedSeries, OnLegendItemClick } from './types';
4
4
  type Args = {
@@ -17,12 +17,13 @@ export const useSeries = (args) => {
17
17
  acc.push(...prepareSeries({
18
18
  type: seriesType,
19
19
  series: seriesList,
20
+ seriesOptions,
20
21
  legend: preparedLegend,
21
22
  colorScale,
22
23
  }));
23
24
  return acc;
24
25
  }, []);
25
- }, [series, preparedLegend]);
26
+ }, [series, seriesOptions, preparedLegend]);
26
27
  const preparedSeriesOptions = React.useMemo(() => {
27
28
  return getPreparedOptions(seriesOptions);
28
29
  }, [seriesOptions]);