@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.
- package/build/plugins/d3/renderer/D3Widget.js +11 -1
- package/build/plugins/d3/renderer/components/AxisX.d.ts +1 -2
- package/build/plugins/d3/renderer/components/AxisX.js +7 -19
- package/build/plugins/d3/renderer/components/Chart.js +1 -1
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x.js +2 -1
- package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +56 -27
- package/build/plugins/d3/renderer/utils/axis.js +0 -6
- package/build/plugins/d3/renderer/utils/text.js +1 -1
- package/package.json +1 -1
|
@@ -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:
|
|
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
|
|
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
|
|
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
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
})
|
|
69
|
-
.remove();
|
|
70
|
-
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
71
98
|
}
|
|
72
|
-
selection
|
|
73
|
-
|
|
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).
|
|
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