@gravity-ui/charts 1.6.5 → 1.6.6
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/dist/cjs/hooks/useShapes/treemap/index.js +2 -3
- package/dist/cjs/hooks/useShapes/treemap/prepare-data.js +23 -8
- package/dist/cjs/hooks/useShapes/treemap/types.d.ts +0 -1
- package/dist/cjs/hooks/useSplit/index.js +25 -22
- package/dist/cjs/utils/chart/text.d.ts +14 -3
- package/dist/cjs/utils/chart/text.js +63 -12
- package/dist/cjs/utils/misc.d.ts +3 -0
- package/dist/cjs/utils/misc.js +8 -0
- package/dist/esm/hooks/useShapes/treemap/index.js +2 -3
- package/dist/esm/hooks/useShapes/treemap/prepare-data.js +23 -8
- package/dist/esm/hooks/useShapes/treemap/types.d.ts +0 -1
- package/dist/esm/hooks/useSplit/index.js +25 -22
- package/dist/esm/utils/chart/text.d.ts +14 -3
- package/dist/esm/utils/chart/text.js +63 -12
- package/dist/esm/utils/misc.d.ts +3 -0
- package/dist/esm/utils/misc.js +8 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { color, select } from 'd3';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
|
-
import { block
|
|
4
|
+
import { block } from '../../../utils';
|
|
5
5
|
import { HtmlLayer } from '../HtmlLayer';
|
|
6
6
|
const b = block('treemap');
|
|
7
7
|
export const TreemapSeriesShape = (props) => {
|
|
@@ -44,8 +44,7 @@ export const TreemapSeriesShape = (props) => {
|
|
|
44
44
|
.attr('y', (d) => d.y)
|
|
45
45
|
.style('font-size', () => series.dataLabels.style.fontSize)
|
|
46
46
|
.style('font-weight', () => { var _a; return ((_a = series.dataLabels.style) === null || _a === void 0 ? void 0 : _a.fontWeight) || null; })
|
|
47
|
-
.style('fill', () => { var _a; return ((_a = series.dataLabels.style) === null || _a === void 0 ? void 0 : _a.fontColor) || null; })
|
|
48
|
-
.call(setEllipsisForOverflowTexts, (d) => d.width);
|
|
47
|
+
.style('fill', () => { var _a; return ((_a = series.dataLabels.style) === null || _a === void 0 ? void 0 : _a.fontColor) || null; });
|
|
49
48
|
const eventName = `hover-shape.treemap`;
|
|
50
49
|
const hoverOptions = get(seriesOptions, 'treemap.states.hover');
|
|
51
50
|
const inactiveOptions = get(seriesOptions, 'treemap.states.inactive');
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { ascending, descending, sort, stratify, treemap, treemapBinary, treemapDice, treemapSlice, treemapSliceDice, treemapSquarify, } from 'd3';
|
|
2
2
|
import { LayoutAlgorithm } from '../../../constants';
|
|
3
|
-
import { getLabelsSize } from '../../../utils';
|
|
3
|
+
import { getLabelsSize, getTextSizeFn, getTextWithElipsis } from '../../../utils';
|
|
4
4
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
5
5
|
const DEFAULT_PADDING = 1;
|
|
6
6
|
function getLabels(args) {
|
|
7
7
|
const { data, options: { html, padding, align, style }, } = args;
|
|
8
|
+
const getTextSize = getTextSizeFn({ style });
|
|
8
9
|
return data.reduce((acc, d) => {
|
|
9
10
|
const texts = Array.isArray(d.data.name) ? d.data.name : [d.data.name];
|
|
10
11
|
const left = d.x0 + padding;
|
|
@@ -15,11 +16,22 @@ function getLabels(args) {
|
|
|
15
16
|
texts.forEach((text) => {
|
|
16
17
|
var _a;
|
|
17
18
|
const label = getFormattedValue(Object.assign({ value: text }, args.options));
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
let labelMaxHeight = 0;
|
|
20
|
+
let labelMaxWidth = 0;
|
|
21
|
+
if (html) {
|
|
22
|
+
const size = (_a = getLabelsSize({
|
|
23
|
+
labels: [label],
|
|
24
|
+
style: Object.assign(Object.assign({}, style), { maxWidth: `${spaceWidth}px`, maxHeight: `${availableSpaceHeight}px` }),
|
|
25
|
+
html,
|
|
26
|
+
})) !== null && _a !== void 0 ? _a : {};
|
|
27
|
+
labelMaxHeight = size.maxHeight;
|
|
28
|
+
labelMaxWidth = size.maxWidth;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const size = getTextSize(label);
|
|
32
|
+
labelMaxHeight = size.height;
|
|
33
|
+
labelMaxWidth = size.width;
|
|
34
|
+
}
|
|
23
35
|
let x = left;
|
|
24
36
|
const y = prevLabelsHeight + d.y0 + padding;
|
|
25
37
|
const labelWidth = Math.min(labelMaxWidth, spaceWidth);
|
|
@@ -53,10 +65,13 @@ function getLabels(args) {
|
|
|
53
65
|
size: { width: labelWidth, height: labelHeight },
|
|
54
66
|
}
|
|
55
67
|
: {
|
|
56
|
-
text:
|
|
68
|
+
text: getTextWithElipsis({
|
|
69
|
+
text: label,
|
|
70
|
+
getTextWidth: (s) => getTextSize(s).width,
|
|
71
|
+
maxWidth: labelWidth,
|
|
72
|
+
}),
|
|
57
73
|
x,
|
|
58
74
|
y,
|
|
59
|
-
width: labelWidth,
|
|
60
75
|
nodeData: d.data,
|
|
61
76
|
};
|
|
62
77
|
acc.push(item);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import get from 'lodash/get';
|
|
2
3
|
import { calculateNumericProperty, getHorisontalSvgTextHeight } from '../../utils';
|
|
3
4
|
const DEFAULT_TITLE_FONT_SIZE = '15px';
|
|
@@ -31,27 +32,29 @@ export function getPlotHeight(args) {
|
|
|
31
32
|
return boundsHeight;
|
|
32
33
|
}
|
|
33
34
|
export const useSplit = (args) => {
|
|
34
|
-
var _a;
|
|
35
35
|
const { split, boundsHeight, chartWidth } = args;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
plots: plots
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
36
|
+
return React.useMemo(() => {
|
|
37
|
+
var _a;
|
|
38
|
+
const splitGap = (_a = calculateNumericProperty({ value: split === null || split === void 0 ? void 0 : split.gap, base: boundsHeight })) !== null && _a !== void 0 ? _a : 0;
|
|
39
|
+
const plotHeight = getPlotHeight({ split: split, boundsHeight, gap: splitGap });
|
|
40
|
+
const plots = (split === null || split === void 0 ? void 0 : split.plots) || [];
|
|
41
|
+
return {
|
|
42
|
+
plots: plots.map((p, index) => {
|
|
43
|
+
const title = preparePlotTitle({
|
|
44
|
+
title: p.title,
|
|
45
|
+
plotIndex: index,
|
|
46
|
+
gap: splitGap,
|
|
47
|
+
plotHeight,
|
|
48
|
+
chartWidth,
|
|
49
|
+
});
|
|
50
|
+
const top = index * (plotHeight + splitGap);
|
|
51
|
+
return {
|
|
52
|
+
top: top + title.height,
|
|
53
|
+
height: plotHeight - title.height,
|
|
54
|
+
title,
|
|
55
|
+
};
|
|
56
|
+
}),
|
|
57
|
+
gap: splitGap,
|
|
58
|
+
};
|
|
59
|
+
}, [split, boundsHeight, chartWidth]);
|
|
57
60
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Selection } from 'd3';
|
|
2
2
|
import type { BaseTextStyle, MeaningfulAny } from '../../types';
|
|
3
|
-
export declare function handleOverflowingText(tSpan: SVGTSpanElement | null, maxWidth: number): void;
|
|
4
|
-
export declare function setEllipsisForOverflowText<T>(selection: Selection<SVGTextElement, T, null, unknown>, maxWidth: number): void;
|
|
5
|
-
export declare function setEllipsisForOverflowTexts<T>(selection: Selection<SVGTextElement, T, MeaningfulAny, unknown>, maxWidth: ((datum: T) => number) | number): void;
|
|
3
|
+
export declare function handleOverflowingText(tSpan: SVGTSpanElement | null, maxWidth: number, textWidth?: number): void;
|
|
4
|
+
export declare function setEllipsisForOverflowText<T>(selection: Selection<SVGTextElement, T, null, unknown>, maxWidth: number, textWidth?: number): void;
|
|
5
|
+
export declare function setEllipsisForOverflowTexts<T>(selection: Selection<SVGTextElement, T, MeaningfulAny, unknown>, maxWidth: ((datum: T) => number) | number, currentWidth?: (datum: T) => number): void;
|
|
6
6
|
export declare function hasOverlappingLabels({ width, labels, padding, style, }: {
|
|
7
7
|
width: number;
|
|
8
8
|
labels: string[];
|
|
@@ -27,3 +27,14 @@ export declare function wrapText(args: {
|
|
|
27
27
|
style?: BaseTextStyle;
|
|
28
28
|
width: number;
|
|
29
29
|
}): TextRow[];
|
|
30
|
+
export declare function getTextSizeFn({ style }: {
|
|
31
|
+
style: BaseTextStyle;
|
|
32
|
+
}): (str: string) => {
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
};
|
|
36
|
+
export declare function getTextWithElipsis({ text: originalText, getTextWidth, maxWidth, }: {
|
|
37
|
+
text: string;
|
|
38
|
+
getTextWidth: (s: string) => number;
|
|
39
|
+
maxWidth: number;
|
|
40
|
+
}): string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { select } from 'd3-selection';
|
|
2
|
-
export function handleOverflowingText(tSpan, maxWidth) {
|
|
2
|
+
export function handleOverflowingText(tSpan, maxWidth, textWidth) {
|
|
3
3
|
var _a, _b, _c;
|
|
4
4
|
if (!tSpan) {
|
|
5
5
|
return;
|
|
@@ -8,15 +8,20 @@ export function handleOverflowingText(tSpan, maxWidth) {
|
|
|
8
8
|
if (!svg) {
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
-
const textNode = tSpan.closest('text');
|
|
12
|
-
const angle = ((_a = Array.from((textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal) || []).find((item) => item.angle)) === null || _a === void 0 ? void 0 : _a.angle) || 0;
|
|
13
|
-
const revertRotation = svg.createSVGTransform();
|
|
14
|
-
revertRotation.setRotate(-angle, 0, 0);
|
|
15
|
-
textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal.appendItem(revertRotation);
|
|
16
11
|
let text = tSpan.textContent || '';
|
|
17
12
|
// We believe that if the text goes beyond the boundaries of less than a pixel, it's not a big deal.
|
|
18
13
|
// Math.floor helps to solve the problem with the difference in rounding when comparing textLength with maxWidth.
|
|
19
|
-
let textLength = Math.floor(((
|
|
14
|
+
let textLength = textWidth !== null && textWidth !== void 0 ? textWidth : Math.floor(((_a = tSpan.getBoundingClientRect()) === null || _a === void 0 ? void 0 : _a.width) || 0);
|
|
15
|
+
if (textLength < maxWidth) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const textNode = tSpan.closest('text');
|
|
19
|
+
const angle = ((_b = Array.from((textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal) || []).find((item) => item.angle)) === null || _b === void 0 ? void 0 : _b.angle) || 0;
|
|
20
|
+
if (angle) {
|
|
21
|
+
const revertRotation = svg.createSVGTransform();
|
|
22
|
+
revertRotation.setRotate(-angle, 0, 0);
|
|
23
|
+
textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal.appendItem(revertRotation);
|
|
24
|
+
}
|
|
20
25
|
while (textLength > maxWidth && text.length > 1) {
|
|
21
26
|
text = text.slice(0, -1);
|
|
22
27
|
tSpan.textContent = text + '…';
|
|
@@ -25,18 +30,30 @@ export function handleOverflowingText(tSpan, maxWidth) {
|
|
|
25
30
|
if (textLength > maxWidth) {
|
|
26
31
|
tSpan.textContent = '';
|
|
27
32
|
}
|
|
28
|
-
|
|
33
|
+
if (angle) {
|
|
34
|
+
textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal.removeItem((textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal.length) - 1);
|
|
35
|
+
}
|
|
29
36
|
}
|
|
30
|
-
export function setEllipsisForOverflowText(selection, maxWidth) {
|
|
37
|
+
export function setEllipsisForOverflowText(selection, maxWidth, textWidth) {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const originalTextWidth = textWidth !== null && textWidth !== void 0 ? textWidth : Math.floor(((_b = (_a = selection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) === null || _b === void 0 ? void 0 : _b.width) || 0);
|
|
40
|
+
if (originalTextWidth <= maxWidth) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
31
43
|
const text = selection.text();
|
|
32
44
|
selection.text(null).append('title').text(text);
|
|
33
45
|
const tSpan = selection.append('tspan').text(text).style('dominant-baseline', 'inherit');
|
|
34
|
-
handleOverflowingText(tSpan.node(), maxWidth);
|
|
46
|
+
handleOverflowingText(tSpan.node(), maxWidth, originalTextWidth);
|
|
35
47
|
}
|
|
36
|
-
export function setEllipsisForOverflowTexts(selection, maxWidth) {
|
|
48
|
+
export function setEllipsisForOverflowTexts(selection, maxWidth, currentWidth) {
|
|
37
49
|
selection.each(function (datum) {
|
|
50
|
+
var _a, _b;
|
|
51
|
+
const textSelection = select(this);
|
|
38
52
|
const textMaxWidth = typeof maxWidth === 'function' ? maxWidth(datum) : maxWidth;
|
|
39
|
-
|
|
53
|
+
const textWidth = currentWidth
|
|
54
|
+
? currentWidth(datum)
|
|
55
|
+
: Math.floor(((_b = (_a = textSelection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) === null || _b === void 0 ? void 0 : _b.width) || 0);
|
|
56
|
+
setEllipsisForOverflowText(textSelection, textMaxWidth, textWidth);
|
|
40
57
|
});
|
|
41
58
|
}
|
|
42
59
|
export function hasOverlappingLabels({ width, labels, padding = 0, style, }) {
|
|
@@ -148,3 +165,37 @@ export function wrapText(args) {
|
|
|
148
165
|
return acc;
|
|
149
166
|
}, []);
|
|
150
167
|
}
|
|
168
|
+
export function getTextSizeFn({ style }) {
|
|
169
|
+
const map = {};
|
|
170
|
+
const setSymbolSize = (s) => {
|
|
171
|
+
const size = getLabelsSize({
|
|
172
|
+
labels: [s],
|
|
173
|
+
style,
|
|
174
|
+
});
|
|
175
|
+
map[s] = { width: size.maxWidth, height: size.maxHeight };
|
|
176
|
+
};
|
|
177
|
+
return (str) => {
|
|
178
|
+
let width = 0;
|
|
179
|
+
let height = 0;
|
|
180
|
+
[...str].forEach((s) => {
|
|
181
|
+
if (!map[s]) {
|
|
182
|
+
setSymbolSize(s);
|
|
183
|
+
}
|
|
184
|
+
width += map[s].width;
|
|
185
|
+
height = Math.max(height, map[s].height);
|
|
186
|
+
});
|
|
187
|
+
return { width, height };
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
export function getTextWithElipsis({ text: originalText, getTextWidth, maxWidth, }) {
|
|
191
|
+
let text = originalText;
|
|
192
|
+
let textLength = getTextWidth(text);
|
|
193
|
+
while (textLength > maxWidth && text.length > 1) {
|
|
194
|
+
text = text.slice(0, -2) + '…';
|
|
195
|
+
textLength = getTextWidth(text);
|
|
196
|
+
}
|
|
197
|
+
if (textLength > maxWidth) {
|
|
198
|
+
text = '';
|
|
199
|
+
}
|
|
200
|
+
return text;
|
|
201
|
+
}
|
package/dist/cjs/utils/misc.d.ts
CHANGED
|
@@ -8,3 +8,6 @@ export declare function getUniqId(): string;
|
|
|
8
8
|
* More details [here](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform#examples).
|
|
9
9
|
*/
|
|
10
10
|
export declare function isMacintosh(): boolean;
|
|
11
|
+
export declare function measurePerformance(): {
|
|
12
|
+
end(): number;
|
|
13
|
+
};
|
package/dist/cjs/utils/misc.js
CHANGED
|
@@ -18,3 +18,11 @@ export function getUniqId() {
|
|
|
18
18
|
export function isMacintosh() {
|
|
19
19
|
return typeof navigator === 'undefined' ? false : /Mac|iP(hone|[oa]d)/.test(navigator.platform);
|
|
20
20
|
}
|
|
21
|
+
export function measurePerformance() {
|
|
22
|
+
const timestamp = performance.now();
|
|
23
|
+
return {
|
|
24
|
+
end() {
|
|
25
|
+
return performance.now() - timestamp;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { color, select } from 'd3';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
|
-
import { block
|
|
4
|
+
import { block } from '../../../utils';
|
|
5
5
|
import { HtmlLayer } from '../HtmlLayer';
|
|
6
6
|
const b = block('treemap');
|
|
7
7
|
export const TreemapSeriesShape = (props) => {
|
|
@@ -44,8 +44,7 @@ export const TreemapSeriesShape = (props) => {
|
|
|
44
44
|
.attr('y', (d) => d.y)
|
|
45
45
|
.style('font-size', () => series.dataLabels.style.fontSize)
|
|
46
46
|
.style('font-weight', () => { var _a; return ((_a = series.dataLabels.style) === null || _a === void 0 ? void 0 : _a.fontWeight) || null; })
|
|
47
|
-
.style('fill', () => { var _a; return ((_a = series.dataLabels.style) === null || _a === void 0 ? void 0 : _a.fontColor) || null; })
|
|
48
|
-
.call(setEllipsisForOverflowTexts, (d) => d.width);
|
|
47
|
+
.style('fill', () => { var _a; return ((_a = series.dataLabels.style) === null || _a === void 0 ? void 0 : _a.fontColor) || null; });
|
|
49
48
|
const eventName = `hover-shape.treemap`;
|
|
50
49
|
const hoverOptions = get(seriesOptions, 'treemap.states.hover');
|
|
51
50
|
const inactiveOptions = get(seriesOptions, 'treemap.states.inactive');
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { ascending, descending, sort, stratify, treemap, treemapBinary, treemapDice, treemapSlice, treemapSliceDice, treemapSquarify, } from 'd3';
|
|
2
2
|
import { LayoutAlgorithm } from '../../../constants';
|
|
3
|
-
import { getLabelsSize } from '../../../utils';
|
|
3
|
+
import { getLabelsSize, getTextSizeFn, getTextWithElipsis } from '../../../utils';
|
|
4
4
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
5
5
|
const DEFAULT_PADDING = 1;
|
|
6
6
|
function getLabels(args) {
|
|
7
7
|
const { data, options: { html, padding, align, style }, } = args;
|
|
8
|
+
const getTextSize = getTextSizeFn({ style });
|
|
8
9
|
return data.reduce((acc, d) => {
|
|
9
10
|
const texts = Array.isArray(d.data.name) ? d.data.name : [d.data.name];
|
|
10
11
|
const left = d.x0 + padding;
|
|
@@ -15,11 +16,22 @@ function getLabels(args) {
|
|
|
15
16
|
texts.forEach((text) => {
|
|
16
17
|
var _a;
|
|
17
18
|
const label = getFormattedValue(Object.assign({ value: text }, args.options));
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
let labelMaxHeight = 0;
|
|
20
|
+
let labelMaxWidth = 0;
|
|
21
|
+
if (html) {
|
|
22
|
+
const size = (_a = getLabelsSize({
|
|
23
|
+
labels: [label],
|
|
24
|
+
style: Object.assign(Object.assign({}, style), { maxWidth: `${spaceWidth}px`, maxHeight: `${availableSpaceHeight}px` }),
|
|
25
|
+
html,
|
|
26
|
+
})) !== null && _a !== void 0 ? _a : {};
|
|
27
|
+
labelMaxHeight = size.maxHeight;
|
|
28
|
+
labelMaxWidth = size.maxWidth;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const size = getTextSize(label);
|
|
32
|
+
labelMaxHeight = size.height;
|
|
33
|
+
labelMaxWidth = size.width;
|
|
34
|
+
}
|
|
23
35
|
let x = left;
|
|
24
36
|
const y = prevLabelsHeight + d.y0 + padding;
|
|
25
37
|
const labelWidth = Math.min(labelMaxWidth, spaceWidth);
|
|
@@ -53,10 +65,13 @@ function getLabels(args) {
|
|
|
53
65
|
size: { width: labelWidth, height: labelHeight },
|
|
54
66
|
}
|
|
55
67
|
: {
|
|
56
|
-
text:
|
|
68
|
+
text: getTextWithElipsis({
|
|
69
|
+
text: label,
|
|
70
|
+
getTextWidth: (s) => getTextSize(s).width,
|
|
71
|
+
maxWidth: labelWidth,
|
|
72
|
+
}),
|
|
57
73
|
x,
|
|
58
74
|
y,
|
|
59
|
-
width: labelWidth,
|
|
60
75
|
nodeData: d.data,
|
|
61
76
|
};
|
|
62
77
|
acc.push(item);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import get from 'lodash/get';
|
|
2
3
|
import { calculateNumericProperty, getHorisontalSvgTextHeight } from '../../utils';
|
|
3
4
|
const DEFAULT_TITLE_FONT_SIZE = '15px';
|
|
@@ -31,27 +32,29 @@ export function getPlotHeight(args) {
|
|
|
31
32
|
return boundsHeight;
|
|
32
33
|
}
|
|
33
34
|
export const useSplit = (args) => {
|
|
34
|
-
var _a;
|
|
35
35
|
const { split, boundsHeight, chartWidth } = args;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
plots: plots
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
36
|
+
return React.useMemo(() => {
|
|
37
|
+
var _a;
|
|
38
|
+
const splitGap = (_a = calculateNumericProperty({ value: split === null || split === void 0 ? void 0 : split.gap, base: boundsHeight })) !== null && _a !== void 0 ? _a : 0;
|
|
39
|
+
const plotHeight = getPlotHeight({ split: split, boundsHeight, gap: splitGap });
|
|
40
|
+
const plots = (split === null || split === void 0 ? void 0 : split.plots) || [];
|
|
41
|
+
return {
|
|
42
|
+
plots: plots.map((p, index) => {
|
|
43
|
+
const title = preparePlotTitle({
|
|
44
|
+
title: p.title,
|
|
45
|
+
plotIndex: index,
|
|
46
|
+
gap: splitGap,
|
|
47
|
+
plotHeight,
|
|
48
|
+
chartWidth,
|
|
49
|
+
});
|
|
50
|
+
const top = index * (plotHeight + splitGap);
|
|
51
|
+
return {
|
|
52
|
+
top: top + title.height,
|
|
53
|
+
height: plotHeight - title.height,
|
|
54
|
+
title,
|
|
55
|
+
};
|
|
56
|
+
}),
|
|
57
|
+
gap: splitGap,
|
|
58
|
+
};
|
|
59
|
+
}, [split, boundsHeight, chartWidth]);
|
|
57
60
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Selection } from 'd3';
|
|
2
2
|
import type { BaseTextStyle, MeaningfulAny } from '../../types';
|
|
3
|
-
export declare function handleOverflowingText(tSpan: SVGTSpanElement | null, maxWidth: number): void;
|
|
4
|
-
export declare function setEllipsisForOverflowText<T>(selection: Selection<SVGTextElement, T, null, unknown>, maxWidth: number): void;
|
|
5
|
-
export declare function setEllipsisForOverflowTexts<T>(selection: Selection<SVGTextElement, T, MeaningfulAny, unknown>, maxWidth: ((datum: T) => number) | number): void;
|
|
3
|
+
export declare function handleOverflowingText(tSpan: SVGTSpanElement | null, maxWidth: number, textWidth?: number): void;
|
|
4
|
+
export declare function setEllipsisForOverflowText<T>(selection: Selection<SVGTextElement, T, null, unknown>, maxWidth: number, textWidth?: number): void;
|
|
5
|
+
export declare function setEllipsisForOverflowTexts<T>(selection: Selection<SVGTextElement, T, MeaningfulAny, unknown>, maxWidth: ((datum: T) => number) | number, currentWidth?: (datum: T) => number): void;
|
|
6
6
|
export declare function hasOverlappingLabels({ width, labels, padding, style, }: {
|
|
7
7
|
width: number;
|
|
8
8
|
labels: string[];
|
|
@@ -27,3 +27,14 @@ export declare function wrapText(args: {
|
|
|
27
27
|
style?: BaseTextStyle;
|
|
28
28
|
width: number;
|
|
29
29
|
}): TextRow[];
|
|
30
|
+
export declare function getTextSizeFn({ style }: {
|
|
31
|
+
style: BaseTextStyle;
|
|
32
|
+
}): (str: string) => {
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
};
|
|
36
|
+
export declare function getTextWithElipsis({ text: originalText, getTextWidth, maxWidth, }: {
|
|
37
|
+
text: string;
|
|
38
|
+
getTextWidth: (s: string) => number;
|
|
39
|
+
maxWidth: number;
|
|
40
|
+
}): string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { select } from 'd3-selection';
|
|
2
|
-
export function handleOverflowingText(tSpan, maxWidth) {
|
|
2
|
+
export function handleOverflowingText(tSpan, maxWidth, textWidth) {
|
|
3
3
|
var _a, _b, _c;
|
|
4
4
|
if (!tSpan) {
|
|
5
5
|
return;
|
|
@@ -8,15 +8,20 @@ export function handleOverflowingText(tSpan, maxWidth) {
|
|
|
8
8
|
if (!svg) {
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
-
const textNode = tSpan.closest('text');
|
|
12
|
-
const angle = ((_a = Array.from((textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal) || []).find((item) => item.angle)) === null || _a === void 0 ? void 0 : _a.angle) || 0;
|
|
13
|
-
const revertRotation = svg.createSVGTransform();
|
|
14
|
-
revertRotation.setRotate(-angle, 0, 0);
|
|
15
|
-
textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal.appendItem(revertRotation);
|
|
16
11
|
let text = tSpan.textContent || '';
|
|
17
12
|
// We believe that if the text goes beyond the boundaries of less than a pixel, it's not a big deal.
|
|
18
13
|
// Math.floor helps to solve the problem with the difference in rounding when comparing textLength with maxWidth.
|
|
19
|
-
let textLength = Math.floor(((
|
|
14
|
+
let textLength = textWidth !== null && textWidth !== void 0 ? textWidth : Math.floor(((_a = tSpan.getBoundingClientRect()) === null || _a === void 0 ? void 0 : _a.width) || 0);
|
|
15
|
+
if (textLength < maxWidth) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const textNode = tSpan.closest('text');
|
|
19
|
+
const angle = ((_b = Array.from((textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal) || []).find((item) => item.angle)) === null || _b === void 0 ? void 0 : _b.angle) || 0;
|
|
20
|
+
if (angle) {
|
|
21
|
+
const revertRotation = svg.createSVGTransform();
|
|
22
|
+
revertRotation.setRotate(-angle, 0, 0);
|
|
23
|
+
textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal.appendItem(revertRotation);
|
|
24
|
+
}
|
|
20
25
|
while (textLength > maxWidth && text.length > 1) {
|
|
21
26
|
text = text.slice(0, -1);
|
|
22
27
|
tSpan.textContent = text + '…';
|
|
@@ -25,18 +30,30 @@ export function handleOverflowingText(tSpan, maxWidth) {
|
|
|
25
30
|
if (textLength > maxWidth) {
|
|
26
31
|
tSpan.textContent = '';
|
|
27
32
|
}
|
|
28
|
-
|
|
33
|
+
if (angle) {
|
|
34
|
+
textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal.removeItem((textNode === null || textNode === void 0 ? void 0 : textNode.transform.baseVal.length) - 1);
|
|
35
|
+
}
|
|
29
36
|
}
|
|
30
|
-
export function setEllipsisForOverflowText(selection, maxWidth) {
|
|
37
|
+
export function setEllipsisForOverflowText(selection, maxWidth, textWidth) {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const originalTextWidth = textWidth !== null && textWidth !== void 0 ? textWidth : Math.floor(((_b = (_a = selection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) === null || _b === void 0 ? void 0 : _b.width) || 0);
|
|
40
|
+
if (originalTextWidth <= maxWidth) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
31
43
|
const text = selection.text();
|
|
32
44
|
selection.text(null).append('title').text(text);
|
|
33
45
|
const tSpan = selection.append('tspan').text(text).style('dominant-baseline', 'inherit');
|
|
34
|
-
handleOverflowingText(tSpan.node(), maxWidth);
|
|
46
|
+
handleOverflowingText(tSpan.node(), maxWidth, originalTextWidth);
|
|
35
47
|
}
|
|
36
|
-
export function setEllipsisForOverflowTexts(selection, maxWidth) {
|
|
48
|
+
export function setEllipsisForOverflowTexts(selection, maxWidth, currentWidth) {
|
|
37
49
|
selection.each(function (datum) {
|
|
50
|
+
var _a, _b;
|
|
51
|
+
const textSelection = select(this);
|
|
38
52
|
const textMaxWidth = typeof maxWidth === 'function' ? maxWidth(datum) : maxWidth;
|
|
39
|
-
|
|
53
|
+
const textWidth = currentWidth
|
|
54
|
+
? currentWidth(datum)
|
|
55
|
+
: Math.floor(((_b = (_a = textSelection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) === null || _b === void 0 ? void 0 : _b.width) || 0);
|
|
56
|
+
setEllipsisForOverflowText(textSelection, textMaxWidth, textWidth);
|
|
40
57
|
});
|
|
41
58
|
}
|
|
42
59
|
export function hasOverlappingLabels({ width, labels, padding = 0, style, }) {
|
|
@@ -148,3 +165,37 @@ export function wrapText(args) {
|
|
|
148
165
|
return acc;
|
|
149
166
|
}, []);
|
|
150
167
|
}
|
|
168
|
+
export function getTextSizeFn({ style }) {
|
|
169
|
+
const map = {};
|
|
170
|
+
const setSymbolSize = (s) => {
|
|
171
|
+
const size = getLabelsSize({
|
|
172
|
+
labels: [s],
|
|
173
|
+
style,
|
|
174
|
+
});
|
|
175
|
+
map[s] = { width: size.maxWidth, height: size.maxHeight };
|
|
176
|
+
};
|
|
177
|
+
return (str) => {
|
|
178
|
+
let width = 0;
|
|
179
|
+
let height = 0;
|
|
180
|
+
[...str].forEach((s) => {
|
|
181
|
+
if (!map[s]) {
|
|
182
|
+
setSymbolSize(s);
|
|
183
|
+
}
|
|
184
|
+
width += map[s].width;
|
|
185
|
+
height = Math.max(height, map[s].height);
|
|
186
|
+
});
|
|
187
|
+
return { width, height };
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
export function getTextWithElipsis({ text: originalText, getTextWidth, maxWidth, }) {
|
|
191
|
+
let text = originalText;
|
|
192
|
+
let textLength = getTextWidth(text);
|
|
193
|
+
while (textLength > maxWidth && text.length > 1) {
|
|
194
|
+
text = text.slice(0, -2) + '…';
|
|
195
|
+
textLength = getTextWidth(text);
|
|
196
|
+
}
|
|
197
|
+
if (textLength > maxWidth) {
|
|
198
|
+
text = '';
|
|
199
|
+
}
|
|
200
|
+
return text;
|
|
201
|
+
}
|
package/dist/esm/utils/misc.d.ts
CHANGED
|
@@ -8,3 +8,6 @@ export declare function getUniqId(): string;
|
|
|
8
8
|
* More details [here](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform#examples).
|
|
9
9
|
*/
|
|
10
10
|
export declare function isMacintosh(): boolean;
|
|
11
|
+
export declare function measurePerformance(): {
|
|
12
|
+
end(): number;
|
|
13
|
+
};
|
package/dist/esm/utils/misc.js
CHANGED
|
@@ -18,3 +18,11 @@ export function getUniqId() {
|
|
|
18
18
|
export function isMacintosh() {
|
|
19
19
|
return typeof navigator === 'undefined' ? false : /Mac|iP(hone|[oa]d)/.test(navigator.platform);
|
|
20
20
|
}
|
|
21
|
+
export function measurePerformance() {
|
|
22
|
+
const timestamp = performance.now();
|
|
23
|
+
return {
|
|
24
|
+
end() {
|
|
25
|
+
return performance.now() - timestamp;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|