@gravity-ui/chartkit 4.6.0 → 4.6.1

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.
@@ -4,9 +4,19 @@ import debounce from 'lodash/debounce';
4
4
  import { getRandomCKId } from '../../../utils';
5
5
  import { Chart } from './components';
6
6
  const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
7
+ const { data, onLoad, onRender } = props;
7
8
  const ref = React.useRef(null);
8
9
  const debounced = React.useRef();
9
10
  const [dimensions, setDimensions] = React.useState();
11
+ //FIXME: add chartPerfomance data to callbacks;
12
+ React.useLayoutEffect(() => {
13
+ if (onLoad) {
14
+ onLoad({});
15
+ }
16
+ if (onRender) {
17
+ onRender({});
18
+ }
19
+ }, []);
10
20
  const handleResize = React.useCallback(() => {
11
21
  var _a;
12
22
  const parentElement = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.parentElement;
@@ -44,6 +54,6 @@ const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
44
54
  width: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) || '100%',
45
55
  height: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) || '100%',
46
56
  position: 'relative',
47
- } }, (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: props.data }))));
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 }))));
48
58
  });
49
59
  export default D3Widget;
@@ -5,7 +5,6 @@ type Props = {
5
5
  width: number;
6
6
  height: number;
7
7
  scale: ChartScale;
8
- chartWidth: number;
9
8
  };
10
- export declare const AxisX: React.MemoExoticComponent<({ axis, width, height, scale, chartWidth }: Props) => React.JSX.Element>;
9
+ export declare const AxisX: React.MemoExoticComponent<({ axis, width, height, scale }: Props) => React.JSX.Element>;
11
10
  export {};
@@ -18,14 +18,12 @@ function getLabelFormatter({ axis, scale }) {
18
18
  });
19
19
  };
20
20
  }
21
- export const AxisX = React.memo(({ axis, width, height, scale, chartWidth }) => {
21
+ export const AxisX = React.memo(({ axis, width, height, scale }) => {
22
22
  const ref = React.useRef(null);
23
23
  React.useEffect(() => {
24
24
  if (!ref.current) {
25
25
  return;
26
26
  }
27
- const svgElement = select(ref.current);
28
- svgElement.selectAll('*').remove();
29
27
  const xAxisGenerator = axisBottom({
30
28
  scale: scale,
31
29
  ticks: {
@@ -43,22 +41,12 @@ export const AxisX = React.memo(({ axis, width, height, scale, chartWidth }) =>
43
41
  color: axis.lineColor,
44
42
  },
45
43
  });
46
- svgElement.call(xAxisGenerator).attr('class', b());
47
- if (axis.labels.enabled) {
48
- svgElement.style('font-size', axis.labels.style.fontSize);
49
- }
50
- // add an ellipsis to the labels on the right that go beyond the boundaries of the chart
51
- svgElement.selectAll('.tick text').each(function () {
52
- var _a;
53
- const node = this;
54
- const textRect = node.getBBox();
55
- const matrix = ((_a = node.transform.baseVal.consolidate()) === null || _a === void 0 ? void 0 : _a.matrix) || {};
56
- const right = matrix.a * textRect.right + matrix.c * textRect.bottom + matrix.e;
57
- if (right > chartWidth) {
58
- const maxWidth = textRect.width - (right - chartWidth) * 2;
59
- select(node).call(setEllipsisForOverflowText, maxWidth);
60
- }
61
- });
44
+ const svgElement = select(ref.current);
45
+ svgElement.selectAll('*').remove();
46
+ svgElement
47
+ .call(xAxisGenerator)
48
+ .attr('class', b())
49
+ .style('font-size', axis.labels.style.fontSize);
62
50
  // add an axis header if necessary
63
51
  if (axis.title.text) {
64
52
  const textY = axis.title.height + parseInt(axis.labels.style.fontSize) + axis.labels.padding;
@@ -65,7 +65,7 @@ export const Chart = (props) => {
65
65
  xScale && yScale && (React.createElement(React.Fragment, null,
66
66
  React.createElement(AxisY, { axises: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale }),
67
67
  React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
68
- React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale, chartWidth: width })))),
68
+ React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale })))),
69
69
  shapes),
70
70
  preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick }))),
71
71
  React.createElement(Tooltip, { hovered: hovered, pointerPosition: pointerPosition, tooltip: tooltip, xAxis: xAxis, yAxis: yAxis[0] })));
@@ -4,6 +4,7 @@ import get from 'lodash/get';
4
4
  import { block } from '../../../../../utils/cn';
5
5
  import { getDataCategoryValue } from '../../utils';
6
6
  import { DEFAULT_BAR_X_SERIES_OPTIONS } from './defaults';
7
+ const MIN_RECT_WIDTH = 1;
7
8
  const MIN_RECT_GAP = 1;
8
9
  const MIN_GROUP_GAP = 1;
9
10
  const DEFAULT_LABEL_PADDING = 7;
@@ -72,7 +73,7 @@ function prepareData(args) {
72
73
  const groupGap = Math.max(bandWidth * groupPadding, MIN_GROUP_GAP);
73
74
  const groupWidth = bandWidth - groupGap;
74
75
  const rectGap = Math.max(bandWidth * barPadding, MIN_RECT_GAP);
75
- const rectWidth = Math.min(groupWidth / maxGroupSize - rectGap, barMaxWidth);
76
+ const rectWidth = Math.max(MIN_RECT_WIDTH, Math.min(groupWidth / maxGroupSize - rectGap, barMaxWidth));
76
77
  const result = [];
77
78
  Object.entries(data).forEach(([xValue, val]) => {
78
79
  const stacks = Object.values(val);
@@ -1,5 +1,6 @@
1
+ import { select } from 'd3';
1
2
  import { getXAxisItems, getXAxisOffset, getXTickPosition } from '../axis';
2
- import { hasOverlappingLabels } from '../text';
3
+ import { hasOverlappingLabels, setEllipsisForOverflowText } from '../text';
3
4
  function addDomain(selection, options) {
4
5
  const { size, color } = options;
5
6
  selection
@@ -18,7 +19,9 @@ export function axisBottom(args) {
18
19
  const position = getXTickPosition({ scale, offset });
19
20
  const values = getXAxisItems({ scale, count: ticksCount, maxCount: maxTickCount });
20
21
  return function (selection) {
21
- var _a, _b;
22
+ var _a, _b, _c, _e;
23
+ const x = ((_b = (_a = selection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) === null || _b === void 0 ? void 0 : _b.x) || 0;
24
+ const right = x + domainSize;
22
25
  selection
23
26
  .selectAll('.tick')
24
27
  .data(values)
@@ -36,6 +39,14 @@ export function axisBottom(args) {
36
39
  .attr('transform', function (d) {
37
40
  return `translate(${position(d) + offset},0)`;
38
41
  });
42
+ // Remove tick that has the same x coordinate like domain
43
+ selection
44
+ .select('.tick')
45
+ .filter((d) => {
46
+ return position(d) === 0;
47
+ })
48
+ .select('line')
49
+ .remove();
39
50
  const labels = selection.selectAll('.tick text');
40
51
  const labelNodes = labels.nodes();
41
52
  const overlapping = hasOverlappingLabels({
@@ -44,32 +55,50 @@ export function axisBottom(args) {
44
55
  padding: labelsPaddings,
45
56
  style: labelsStyle,
46
57
  });
47
- if (overlapping) {
48
- if (autoRotation) {
49
- const labelHeight = (_b = (_a = labelNodes[0]) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) === null || _b === void 0 ? void 0 : _b.height;
50
- const labelOffset = (labelHeight / 2 + labelsMargin) / 2;
51
- labels
52
- .attr('text-anchor', 'end')
53
- .attr('transform', `rotate(-45) translate(-${labelOffset}, -${labelOffset})`);
54
- }
55
- else {
56
- // remove overlapping labels
57
- let elementX = 0;
58
- selection
59
- .selectAll('.tick')
60
- .filter(function () {
61
- const node = this;
62
- const r = node.getBoundingClientRect();
63
- if (r.left < elementX) {
64
- return true;
58
+ const rotationAngle = overlapping && autoRotation ? '-45' : undefined;
59
+ if (rotationAngle) {
60
+ const labelHeight = (_e = (_c = labelNodes[0]) === null || _c === void 0 ? void 0 : _c.getBoundingClientRect()) === null || _e === void 0 ? void 0 : _e.height;
61
+ const labelOffset = (labelHeight / 2 + labelsMargin) / 2;
62
+ labels
63
+ .attr('text-anchor', 'end')
64
+ .attr('transform', `rotate(${rotationAngle}) translate(-${labelOffset}, -${labelOffset})`);
65
+ }
66
+ else {
67
+ // remove overlapping labels
68
+ let elementX = 0;
69
+ selection
70
+ .selectAll('.tick')
71
+ .filter(function () {
72
+ const node = this;
73
+ const r = node.getBoundingClientRect();
74
+ if (r.left < elementX) {
75
+ return true;
76
+ }
77
+ elementX = r.right + labelsPaddings;
78
+ return false;
79
+ })
80
+ .remove();
81
+ // add an ellipsis to the labels that go beyond the boundaries of the chart
82
+ labels.each(function (_d, i, nodes) {
83
+ if (i === nodes.length - 1) {
84
+ const currentElement = this;
85
+ const prevElement = nodes[i - 1];
86
+ const text = select(currentElement);
87
+ const currentElementPosition = currentElement.getBoundingClientRect();
88
+ const prevElementPosition = prevElement === null || prevElement === void 0 ? void 0 : prevElement.getBoundingClientRect();
89
+ const lackingSpace = Math.max(0, currentElementPosition.right - right);
90
+ if (lackingSpace) {
91
+ const remainSpace = right - ((prevElementPosition === null || prevElementPosition === void 0 ? void 0 : prevElementPosition.right) || 0) - labelsPaddings;
92
+ const translateX = currentElementPosition.width / 2 - lackingSpace;
93
+ text.attr('text-anchor', 'end').attr('transform', `translate(${translateX},0)`);
94
+ setEllipsisForOverflowText(text, remainSpace);
65
95
  }
66
- elementX = r.right + labelsPaddings;
67
- return false;
68
- })
69
- .remove();
70
- }
96
+ }
97
+ });
71
98
  }
72
- selection.call(addDomain, { size: domainSize, color: domainColor });
73
- selection.attr('text-anchor', 'middle').style('font-size', (labelsStyle === null || labelsStyle === void 0 ? void 0 : labelsStyle.fontSize) || '');
99
+ selection
100
+ .call(addDomain, { size: domainSize, color: domainColor })
101
+ .attr('text-anchor', 'middle')
102
+ .style('font-size', (labelsStyle === null || labelsStyle === void 0 ? void 0 : labelsStyle.fontSize) || '');
74
103
  };
75
104
  }
@@ -30,17 +30,11 @@ export function getXTickPosition({ scale, offset }) {
30
30
  return isBandScale(scale) ? center(scale.copy(), offset) : number(scale.copy());
31
31
  }
32
32
  export function getXAxisItems({ scale, count, maxCount, }) {
33
- const offset = getXAxisOffset();
34
33
  let values = getScaleTicks(scale, count);
35
- const position = getXTickPosition({ scale, offset });
36
34
  if (values.length > maxCount) {
37
35
  const step = Math.ceil(values.length / maxCount);
38
36
  values = values.filter((_, i) => i % step === 0);
39
37
  }
40
- // Remove tick that has the same x coordinate like domain
41
- if (values.length && position(values[0]) === 0) {
42
- values = values.slice(1);
43
- }
44
38
  return values;
45
39
  }
46
40
  export function getMaxTickCount({ axis, width }) {
@@ -2,7 +2,7 @@ import { select } from 'd3';
2
2
  export function setEllipsisForOverflowText(selection, maxWidth) {
3
3
  var _a, _b;
4
4
  let text = selection.text();
5
- selection.text(null).attr('text-anchor', 'left').append('title').text(text);
5
+ selection.text(null).append('title').text(text);
6
6
  const tSpan = selection.append('tspan').text(text);
7
7
  let textLength = ((_a = tSpan.node()) === null || _a === void 0 ? void 0 : _a.getComputedTextLength()) || 0;
8
8
  while (textLength > maxWidth && text.length > 1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/chartkit",
3
- "version": "4.6.0",
3
+ "version": "4.6.1",
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",