@gravity-ui/chartkit 5.0.0 → 5.1.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 (31) hide show
  1. package/build/plugins/d3/renderer/components/Chart.js +9 -0
  2. package/build/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.js +17 -5
  3. package/build/plugins/d3/renderer/d3-dispatcher.js +1 -1
  4. package/build/plugins/d3/renderer/hooks/useSeries/prepare-line.js +1 -0
  5. package/build/plugins/d3/renderer/hooks/useSeries/prepare-pie.js +1 -0
  6. package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +2 -0
  7. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/index.js +2 -1
  8. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +1 -0
  9. package/build/plugins/d3/renderer/hooks/useShapes/bar-x/types.d.ts +1 -0
  10. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.js +2 -1
  11. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +1 -0
  12. package/build/plugins/d3/renderer/hooks/useShapes/bar-y/types.d.ts +1 -0
  13. package/build/plugins/d3/renderer/hooks/useShapes/line/index.js +2 -1
  14. package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.js +1 -0
  15. package/build/plugins/d3/renderer/hooks/useShapes/line/types.d.ts +1 -0
  16. package/build/plugins/d3/renderer/hooks/useShapes/marker.js +8 -1
  17. package/build/plugins/d3/renderer/hooks/useShapes/pie/index.js +18 -8
  18. package/build/plugins/d3/renderer/hooks/useShapes/pie/prepare-data.js +1 -0
  19. package/build/plugins/d3/renderer/hooks/useShapes/pie/types.d.ts +1 -0
  20. package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.js +12 -2
  21. package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.js +2 -0
  22. package/build/plugins/d3/renderer/hooks/useShapes/scatter/types.d.ts +1 -0
  23. package/build/plugins/d3/renderer/hooks/useShapes/treemap/index.js +10 -3
  24. package/build/types/widget-data/area.d.ts +15 -0
  25. package/build/types/widget-data/bar-x.d.ts +2 -0
  26. package/build/types/widget-data/bar-y.d.ts +2 -0
  27. package/build/types/widget-data/chart.d.ts +6 -0
  28. package/build/types/widget-data/line.d.ts +9 -0
  29. package/build/types/widget-data/pie.d.ts +2 -0
  30. package/build/types/widget-data/scatter.d.ts +3 -0
  31. package/package.json +1 -1
@@ -60,6 +60,15 @@ export const Chart = (props) => {
60
60
  yScale,
61
61
  svgContainer: svgRef.current,
62
62
  });
63
+ React.useEffect(() => {
64
+ var _a, _b, _c, _d;
65
+ if ((_b = (_a = data.chart) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.click) {
66
+ dispatcher.on('click-chart', (_d = (_c = data.chart) === null || _c === void 0 ? void 0 : _c.events) === null || _d === void 0 ? void 0 : _d.click);
67
+ }
68
+ return () => {
69
+ dispatcher.on('click-chart', null);
70
+ };
71
+ }, [dispatcher]);
63
72
  const boundsOffsetTop = chart.margin.top;
64
73
  const boundsOffsetLeft = chart.margin.left + getWidthOccupiedByYAxis({ preparedAxis: yAxis });
65
74
  return (React.createElement(React.Fragment, null,
@@ -101,7 +101,7 @@ export const TooltipTriggerArea = (args) => {
101
101
  });
102
102
  return sort(result, (item) => item.y);
103
103
  }, [shapesData]);
104
- const handleMouseMove = (e) => {
104
+ const getShapeData = (point) => {
105
105
  var _a, _b;
106
106
  const { left: ownLeft, top: ownTop } = ((_a = rectRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) || {
107
107
  left: 0,
@@ -111,9 +111,9 @@ export const TooltipTriggerArea = (args) => {
111
111
  left: 0,
112
112
  top: 0,
113
113
  };
114
- const [pointerX, pointerY] = pointer(e, svgContainer);
115
- const hoverShapeData = [];
116
- hoverShapeData === null || hoverShapeData === void 0 ? void 0 : hoverShapeData.push(...getBarXShapeData({
114
+ const [pointerX, pointerY] = point; //pointer(e, svgContainer);
115
+ const result = [];
116
+ result === null || result === void 0 ? void 0 : result.push(...getBarXShapeData({
117
117
  shapesData,
118
118
  point: [pointerX, pointerY],
119
119
  left: ownLeft - containerLeft,
@@ -127,6 +127,11 @@ export const TooltipTriggerArea = (args) => {
127
127
  data: barYData,
128
128
  point: [pointerX - (ownLeft - containerLeft), pointerY - (ownTop - containerTop)],
129
129
  }));
130
+ return result;
131
+ };
132
+ const handleMouseMove = (e) => {
133
+ const [pointerX, pointerY] = pointer(e, svgContainer);
134
+ const hoverShapeData = getShapeData([pointerX, pointerY]);
130
135
  if (hoverShapeData.length) {
131
136
  const position = [pointerX, pointerY];
132
137
  dispatcher.call('hover-shape', e.target, hoverShapeData, position);
@@ -137,5 +142,12 @@ export const TooltipTriggerArea = (args) => {
137
142
  throttledHandleMouseMove.cancel();
138
143
  dispatcher.call('hover-shape', {}, undefined);
139
144
  };
140
- return (React.createElement("rect", { ref: rectRef, width: boundsWidth, height: boundsHeight, fill: "transparent", onMouseMove: throttledHandleMouseMove, onMouseLeave: handleMouseLeave }));
145
+ const handleClick = (e) => {
146
+ const [pointerX, pointerY] = pointer(e, svgContainer);
147
+ const shapeData = getShapeData([pointerX, pointerY]);
148
+ if (shapeData.length) {
149
+ dispatcher.call('click-chart', undefined, { point: get(shapeData, '[0].data'), series: get(shapeData, '[0].series') }, e);
150
+ }
151
+ };
152
+ return (React.createElement("rect", { ref: rectRef, width: boundsWidth, height: boundsHeight, fill: "transparent", onMouseMove: throttledHandleMouseMove, onMouseLeave: handleMouseLeave, onClick: handleClick }));
141
153
  };
@@ -1,4 +1,4 @@
1
1
  import { dispatch } from 'd3';
2
2
  export const getD3Dispatcher = () => {
3
- return dispatch('hover-shape');
3
+ return dispatch('hover-shape', 'click-chart');
4
4
  };
@@ -72,6 +72,7 @@ export function prepareLineSeries(args) {
72
72
  marker: prepareMarker(series, seriesOptions),
73
73
  dashStyle: dashStyle,
74
74
  linecap: prepareLinecap(dashStyle, series, seriesOptions),
75
+ opacity: get(series, 'opacity', null),
75
76
  };
76
77
  return prepared;
77
78
  }, []);
@@ -52,6 +52,7 @@ export function preparePieSeries(args) {
52
52
  },
53
53
  },
54
54
  renderCustomShape: series.renderCustomShape,
55
+ opacity: get(dataItem, 'opacity', null),
55
56
  };
56
57
  return result;
57
58
  });
@@ -130,6 +130,7 @@ export type PreparedPieSeries = {
130
130
  };
131
131
  };
132
132
  renderCustomShape?: PieSeries['renderCustomShape'];
133
+ opacity: number | null;
133
134
  } & BasePreparedSeries;
134
135
  export type PreparedLineSeries = {
135
136
  type: LineSeries['type'];
@@ -161,6 +162,7 @@ export type PreparedLineSeries = {
161
162
  };
162
163
  dashStyle: DashStyle;
163
164
  linecap: LineCap;
165
+ opacity: number | null;
164
166
  } & BasePreparedSeries;
165
167
  export type PreparedAreaSeries = {
166
168
  type: AreaSeries['type'];
@@ -27,7 +27,8 @@ export const BarXSeriesShapes = (args) => {
27
27
  .attr('y', (d) => d.y)
28
28
  .attr('height', (d) => d.height)
29
29
  .attr('width', (d) => d.width)
30
- .attr('fill', (d) => d.data.color || d.series.color);
30
+ .attr('fill', (d) => d.data.color || d.series.color)
31
+ .attr('opacity', (d) => d.opacity);
31
32
  let dataLabels = preparedData.map((d) => d.label).filter(Boolean);
32
33
  if (!((_a = preparedData[0]) === null || _a === void 0 ? void 0 : _a.series.dataLabels.allowOverlap)) {
33
34
  dataLabels = filterOverlappingLabels(dataLabels);
@@ -117,6 +117,7 @@ export const prepareBarXData = (args) => {
117
117
  y: y - stackHeight,
118
118
  width: rectWidth,
119
119
  height,
120
+ opacity: get(yValue.data, 'opacity', null),
120
121
  data: yValue.data,
121
122
  series: yValue.series,
122
123
  };
@@ -6,6 +6,7 @@ export type PreparedBarXData = Omit<TooltipDataChunkBarX, 'series'> & {
6
6
  y: number;
7
7
  width: number;
8
8
  height: number;
9
+ opacity: number | null;
9
10
  series: PreparedBarXSeries;
10
11
  label?: LabelData;
11
12
  };
@@ -23,7 +23,8 @@ export const BarYSeriesShapes = (args) => {
23
23
  .attr('y', (d) => d.y)
24
24
  .attr('height', (d) => d.height)
25
25
  .attr('width', (d) => d.width)
26
- .attr('fill', (d) => d.color);
26
+ .attr('fill', (d) => d.color)
27
+ .attr('opacity', (d) => d.data.opacity || null);
27
28
  const dataLabels = preparedData.filter((d) => d.series.dataLabels.enabled);
28
29
  const labelSelection = svgElement
29
30
  .selectAll('text')
@@ -104,6 +104,7 @@ export const prepareBarYData = (args) => {
104
104
  width,
105
105
  height: barHeight,
106
106
  color: data.color || s.color,
107
+ opacity: get(data, 'opacity', null),
107
108
  data,
108
109
  series: s,
109
110
  });
@@ -6,5 +6,6 @@ export type PreparedBarYData = Omit<TooltipDataChunkBarX, 'series'> & {
6
6
  width: number;
7
7
  height: number;
8
8
  color: string;
9
+ opacity: number | null;
9
10
  series: PreparedBarYSeries;
10
11
  };
@@ -31,7 +31,8 @@ export const LineSeriesShapes = (args) => {
31
31
  .attr('stroke-width', (d) => d.width)
32
32
  .attr('stroke-linejoin', (d) => d.linecap)
33
33
  .attr('stroke-linecap', (d) => d.linecap)
34
- .attr('stroke-dasharray', (d) => getLineDashArray(d.dashStyle, d.width));
34
+ .attr('stroke-dasharray', (d) => getLineDashArray(d.dashStyle, d.width))
35
+ .attr('opacity', (d) => d.opacity);
35
36
  let dataLabels = preparedData.reduce((acc, d) => {
36
37
  return acc.concat(d.labels);
37
38
  }, []);
@@ -63,6 +63,7 @@ export const prepareLineData = (args) => {
63
63
  id: s.id,
64
64
  dashStyle: s.dashStyle,
65
65
  linecap: s.linecap,
66
+ opacity: s.opacity,
66
67
  };
67
68
  acc.push(result);
68
69
  return acc;
@@ -25,4 +25,5 @@ export type PreparedLineData = {
25
25
  labels: LabelData[];
26
26
  dashStyle: DashStyle;
27
27
  linecap: LineCap;
28
+ opacity: number | null;
28
29
  };
@@ -36,7 +36,14 @@ export function renderMarker(selection) {
36
36
  }
37
37
  export function getMarkerVisibility(d) {
38
38
  const markerStates = d.point.series.marker.states;
39
- const enabled = (markerStates.hover.enabled && d.hovered) || markerStates.normal.enabled;
39
+ let enabled;
40
+ if (d.hovered) {
41
+ enabled = markerStates.hover.enabled && d.hovered;
42
+ }
43
+ else {
44
+ enabled =
45
+ markerStates.normal.enabled || get(d.point.data, 'marker.states.normal.enabled', false);
46
+ }
40
47
  return enabled ? '' : 'hidden';
41
48
  }
42
49
  export function getMarkerHaloVisibility(d) {
@@ -68,7 +68,8 @@ export function PieSeriesShapes(args) {
68
68
  return arcGenerator(d);
69
69
  })
70
70
  .attr('class', b('segment'))
71
- .attr('fill', (d) => d.data.color);
71
+ .attr('fill', (d) => d.data.color)
72
+ .attr('opacity', (d) => d.data.opacity);
72
73
  shapesSelection
73
74
  .selectAll('text')
74
75
  .data((pieData) => pieData.labels)
@@ -114,17 +115,20 @@ export function PieSeriesShapes(args) {
114
115
  nodes[index].append(customShape);
115
116
  }
116
117
  });
118
+ const getSelectedSegment = (element) => {
119
+ const datum = select(element).datum();
120
+ const seriesId = get(datum, 'data.series.id', get(datum, 'series.id'));
121
+ return preparedData.reduce((result, pie) => {
122
+ var _a;
123
+ return result || ((_a = pie.segments.find((s) => s.data.series.id === seriesId)) === null || _a === void 0 ? void 0 : _a.data);
124
+ }, undefined);
125
+ };
117
126
  const eventName = `hover-shape.pie`;
118
127
  const hoverOptions = get(seriesOptions, 'pie.states.hover');
119
128
  const inactiveOptions = get(seriesOptions, 'pie.states.inactive');
120
129
  svgElement
121
130
  .on('mousemove', (e) => {
122
- const datum = select(e.target).datum();
123
- const seriesId = get(datum, 'data.series.id', get(datum, 'series.id'));
124
- const currentSegment = preparedData.reduce((result, pie) => {
125
- var _a;
126
- return (result || ((_a = pie.segments.find((s) => s.data.series.id === seriesId)) === null || _a === void 0 ? void 0 : _a.data));
127
- }, undefined);
131
+ const currentSegment = getSelectedSegment(e.target);
128
132
  if (currentSegment) {
129
133
  const data = {
130
134
  series: {
@@ -132,13 +136,19 @@ export function PieSeriesShapes(args) {
132
136
  type: 'pie',
133
137
  name: currentSegment.series.name,
134
138
  },
135
- data: currentSegment.series,
139
+ data: currentSegment.series.data,
136
140
  };
137
141
  dispatcher.call('hover-shape', {}, [data], pointer(e, svgContainer));
138
142
  }
139
143
  })
140
144
  .on('mouseleave', () => {
141
145
  dispatcher.call('hover-shape', {}, undefined);
146
+ })
147
+ .on('click', (e) => {
148
+ const selectedSegment = getSelectedSegment(e.target);
149
+ if (selectedSegment) {
150
+ dispatcher.call('click-chart', undefined, { point: selectedSegment.series.data, series: selectedSegment.series }, e);
151
+ }
142
152
  });
143
153
  dispatcher.on(eventName, (data) => {
144
154
  const selectedSeriesId = data === null || data === void 0 ? void 0 : data[0].series.id;
@@ -45,6 +45,7 @@ export function preparePieData(args) {
45
45
  return {
46
46
  value: item.value,
47
47
  color: item.color,
48
+ opacity: item.opacity,
48
49
  series: item,
49
50
  hovered: false,
50
51
  active: true,
@@ -5,6 +5,7 @@ import { PreparedPieSeries } from '../../useSeries/types';
5
5
  export type SegmentData = {
6
6
  value: number;
7
7
  color: string;
8
+ opacity: number | null;
8
9
  series: PreparedPieSeries;
9
10
  hovered: boolean;
10
11
  active: boolean;
@@ -22,10 +22,14 @@ export function ScatterSeriesShape(props) {
22
22
  .data(preparedData, shapeKey)
23
23
  .join('g')
24
24
  .call(renderMarker)
25
- .attr('fill', (d) => d.point.data.color || d.point.series.color || '');
25
+ .attr('fill', (d) => d.point.data.color || d.point.series.color || '')
26
+ .attr('opacity', (d) => d.point.opacity);
27
+ const getSelectedPoint = (element) => {
28
+ return select(element).datum();
29
+ };
26
30
  svgElement
27
31
  .on('mousemove', (e) => {
28
- const datum = select(e.target).datum();
32
+ const datum = getSelectedPoint(e.target);
29
33
  if (!datum) {
30
34
  return;
31
35
  }
@@ -42,6 +46,12 @@ export function ScatterSeriesShape(props) {
42
46
  })
43
47
  .on('mouseleave', () => {
44
48
  dispatcher.call('hover-shape', {}, undefined);
49
+ })
50
+ .on('click', (e) => {
51
+ const datum = getSelectedPoint(e.target);
52
+ if (datum) {
53
+ dispatcher.call('click-chart', undefined, { point: datum.point.data, series: datum.point.series }, e);
54
+ }
45
55
  });
46
56
  const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
47
57
  const inactiveEnabled = inactiveOptions === null || inactiveOptions === void 0 ? void 0 : inactiveOptions.enabled;
@@ -1,3 +1,4 @@
1
+ import get from 'lodash/get';
1
2
  import { getXValue, getYValue } from '../utils';
2
3
  const getFilteredLinearScatterData = (data) => {
3
4
  return data.filter((d) => typeof d.x === 'number' && typeof d.y === 'number');
@@ -15,6 +16,7 @@ export const prepareScatterData = (args) => {
15
16
  series: s,
16
17
  x: getXValue({ point: d, xAxis, xScale }),
17
18
  y: getYValue({ point: d, yAxis, yScale }),
19
+ opacity: get(d, 'opacity', null),
18
20
  },
19
21
  hovered: false,
20
22
  active: true,
@@ -3,6 +3,7 @@ import { PreparedScatterSeries } from '../../useSeries/types';
3
3
  type PointData = {
4
4
  x: number;
5
5
  y: number;
6
+ opacity: number | null;
6
7
  data: ScatterSeriesData;
7
8
  series: PreparedScatterSeries;
8
9
  };
@@ -44,17 +44,24 @@ export const TreemapSeriesShape = (props) => {
44
44
  .style('font-weight', () => { var _a; return ((_a = series.dataLabels.style) === null || _a === void 0 ? void 0 : _a.fontWeight) || null; })
45
45
  .style('fill', () => { var _a; return ((_a = series.dataLabels.style) === null || _a === void 0 ? void 0 : _a.fontColor) || null; })
46
46
  .call(setEllipsisForOverflowTexts, (d) => d.width);
47
- const eventName = `hover-shape.pie`;
47
+ const getSelectedPart = (node) => {
48
+ const hoveredRect = select(node);
49
+ return hoveredRect.datum();
50
+ };
51
+ const eventName = `hover-shape.treemap`;
48
52
  const hoverOptions = get(seriesOptions, 'treemap.states.hover');
49
53
  const inactiveOptions = get(seriesOptions, 'treemap.states.inactive');
50
54
  svgElement
51
55
  .on('mousemove', (e) => {
52
- const hoveredRect = select(e.target);
53
- const datum = hoveredRect.datum();
56
+ const datum = getSelectedPart(e.target);
54
57
  dispatcher.call('hover-shape', {}, [{ data: datum.data, series }], pointer(e, svgContainer));
55
58
  })
56
59
  .on('mouseleave', () => {
57
60
  dispatcher.call('hover-shape', {}, undefined);
61
+ })
62
+ .on('click', (e) => {
63
+ const datum = getSelectedPart(e.target);
64
+ dispatcher.call('click-chart', undefined, { point: datum.data, series }, e);
58
65
  });
59
66
  dispatcher.on(eventName, (data) => {
60
67
  const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
@@ -19,6 +19,21 @@ export type AreaSeriesData<T = any> = BaseSeriesData<T> & {
19
19
  y?: string | number;
20
20
  /** Data label value of the point. If not specified, the y value is used. */
21
21
  label?: string | number;
22
+ /** Individual marker options for the point. */
23
+ marker?: {
24
+ /** States for a single point marker. */
25
+ states?: {
26
+ /** The normal state of a single point marker. */
27
+ normal?: {
28
+ /**
29
+ * Enable or disable the point marker.
30
+ *
31
+ * @default false
32
+ * */
33
+ enabled: boolean;
34
+ };
35
+ };
36
+ };
22
37
  };
23
38
  export type AreaMarkerSymbol = 'circle' | 'square';
24
39
  export type AreaMarkerOptions = PointMarkerOptions & {
@@ -25,6 +25,8 @@ export type BarXSeriesData<T = any> = BaseSeriesData<T> & {
25
25
  category?: string;
26
26
  /** Data label value of the bar-x column. If not specified, the y value is used. */
27
27
  label?: string | number;
28
+ /** Individual opacity for the bar-x column. */
29
+ opacity?: number;
28
30
  };
29
31
  export type BarXSeries<T = any> = BaseSeries & {
30
32
  type: typeof SeriesType.BarX;
@@ -19,6 +19,8 @@ export type BarYSeriesData<T = any> = BaseSeriesData<T> & {
19
19
  y?: string | number;
20
20
  /** Data label value of the bar. If not specified, the x value is used. */
21
21
  label?: string | number;
22
+ /** Individual opacity for the bar. */
23
+ opacity?: number;
22
24
  };
23
25
  export type BarYSeries<T = any> = BaseSeries & {
24
26
  type: typeof SeriesType.BarY;
@@ -6,4 +6,10 @@ export type ChartMargin = {
6
6
  };
7
7
  export type ChartKitWidgetChart = {
8
8
  margin?: Partial<ChartMargin>;
9
+ events?: {
10
+ click?: (data: {
11
+ point: unknown;
12
+ series: unknown;
13
+ }, event: PointerEvent) => void;
14
+ };
9
15
  };
@@ -19,6 +19,13 @@ export type LineSeriesData<T = any> = BaseSeriesData<T> & {
19
19
  y?: string | number;
20
20
  /** Data label value of the point. If not specified, the y value is used. */
21
21
  label?: string | number;
22
+ marker?: {
23
+ states?: {
24
+ normal?: {
25
+ enabled: boolean;
26
+ };
27
+ };
28
+ };
22
29
  };
23
30
  export type LineSeries<T = any> = BaseSeries & {
24
31
  type: typeof SeriesType.Line;
@@ -42,4 +49,6 @@ export type LineSeries<T = any> = BaseSeries & {
42
49
  dashStyle?: `${DashStyle}`;
43
50
  /** Option for line cap style */
44
51
  linecap?: `${LineCap}`;
52
+ /** Individual opacity for the line. */
53
+ opacity?: number;
45
54
  };
@@ -11,6 +11,8 @@ export type PieSeriesData<T = any> = BaseSeriesData<T> & {
11
11
  visible?: boolean;
12
12
  /** Initial data label of the pie segment. If not specified, the value is used. */
13
13
  label?: string;
14
+ /** Individual opacity for the pie segment. */
15
+ opacity?: number;
14
16
  };
15
17
  export type ConnectorShape = 'straight-line' | 'polyline';
16
18
  export type ConnectorCurve = 'linear' | 'basic';
@@ -22,7 +22,10 @@ export type ScatterSeriesData<T = any> = BaseSeriesData<T> & {
22
22
  * @deprecated use `x` or `y` instead
23
23
  */
24
24
  category?: string;
25
+ /** Individual radius for the point. */
25
26
  radius?: number;
27
+ /** Individual opacity for the point. */
28
+ opacity?: number;
26
29
  };
27
30
  export type ScatterSeries<T = any> = BaseSeries & {
28
31
  type: typeof SeriesType.Scatter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/chartkit",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "React component used to render charts based on any sources you need",
5
5
  "license": "MIT",
6
6
  "repository": "git@github.com:gravity-ui/ChartKit.git",