@gravity-ui/charts 1.6.4 → 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/components/Axis/styles.css +3 -3
- package/dist/cjs/components/Legend/index.js +1 -1
- package/dist/cjs/components/Legend/styles.css +2 -2
- package/dist/cjs/hooks/useShapes/pie/index.js +1 -1
- package/dist/cjs/hooks/useShapes/styles.css +4 -4
- 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/axis-generators/bottom.js +1 -1
- package/dist/cjs/utils/chart/index.js +1 -1
- package/dist/cjs/utils/chart/text.d.ts +14 -3
- package/dist/cjs/utils/chart/text.js +64 -13
- package/dist/cjs/utils/chart-ui/pie-center-text.d.ts +3 -2
- package/dist/cjs/utils/chart-ui/pie-center-text.js +23 -10
- package/dist/cjs/utils/misc.d.ts +3 -0
- package/dist/cjs/utils/misc.js +8 -0
- package/dist/esm/components/Axis/styles.css +3 -3
- package/dist/esm/components/Legend/index.js +1 -1
- package/dist/esm/components/Legend/styles.css +2 -2
- package/dist/esm/hooks/useShapes/pie/index.js +1 -1
- package/dist/esm/hooks/useShapes/styles.css +4 -4
- 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/axis-generators/bottom.js +1 -1
- package/dist/esm/utils/chart/index.js +1 -1
- package/dist/esm/utils/chart/text.d.ts +14 -3
- package/dist/esm/utils/chart/text.js +64 -13
- package/dist/esm/utils/chart-ui/pie-center-text.d.ts +3 -2
- package/dist/esm/utils/chart-ui/pie-center-text.js +23 -10
- package/dist/esm/utils/misc.d.ts +3 -0
- package/dist/esm/utils/misc.js +8 -0
- package/package.json +1 -1
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
}
|
|
4
4
|
.gcharts-axis .tick text {
|
|
5
5
|
color: var(--g-color-text-secondary);
|
|
6
|
-
|
|
6
|
+
dominant-baseline: text-after-edge;
|
|
7
7
|
}
|
|
8
8
|
.gcharts-axis .tick line, .gcharts-axis .tick path {
|
|
9
9
|
stroke: var(--g-color-line-generic);
|
|
10
10
|
}
|
|
11
11
|
.gcharts-axis__title {
|
|
12
|
-
|
|
12
|
+
dominant-baseline: text-after-edge;
|
|
13
13
|
fill: var(--g-color-text-secondary);
|
|
14
14
|
}
|
|
15
15
|
.gcharts-axis__title tspan {
|
|
16
|
-
|
|
16
|
+
dominant-baseline: text-after-edge;
|
|
17
17
|
}
|
|
@@ -294,7 +294,7 @@ export const Legend = (props) => {
|
|
|
294
294
|
.attr('font-weight', (_c = legend.title.style.fontWeight) !== null && _c !== void 0 ? _c : null)
|
|
295
295
|
.attr('font-size', (_d = legend.title.style.fontSize) !== null && _d !== void 0 ? _d : null)
|
|
296
296
|
.attr('fill', (_e = legend.title.style.fontColor) !== null && _e !== void 0 ? _e : null)
|
|
297
|
-
.style('
|
|
297
|
+
.style('dominant-baseline', 'text-before-edge')
|
|
298
298
|
.html(legend.title.text);
|
|
299
299
|
}
|
|
300
300
|
const { left } = getLegendPosition({
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
}
|
|
20
20
|
.gcharts-legend__item-text {
|
|
21
21
|
fill: var(--g-color-text-secondary);
|
|
22
|
-
|
|
22
|
+
dominant-baseline: text-before-edge;
|
|
23
23
|
}
|
|
24
24
|
.gcharts-legend__item-text_unselected {
|
|
25
25
|
fill: var(--g-color-text-hint);
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
fill: var(--g-color-text-primary);
|
|
33
33
|
}
|
|
34
34
|
.gcharts-legend__pagination-counter, .gcharts-legend__pagination-arrow {
|
|
35
|
-
|
|
35
|
+
dominant-baseline: middle;
|
|
36
36
|
}
|
|
37
37
|
.gcharts-legend__pagination-arrow {
|
|
38
38
|
cursor: pointer;
|
|
@@ -147,7 +147,7 @@ export function PieSeriesShapes(args) {
|
|
|
147
147
|
});
|
|
148
148
|
return d;
|
|
149
149
|
});
|
|
150
|
-
const labelSelection = pieSelection.selectAll('
|
|
150
|
+
const labelSelection = pieSelection.selectAll(`.${b('label')} tspan`);
|
|
151
151
|
const connectorSelection = pieSelection.selectAll(connectorSelector);
|
|
152
152
|
labelSelection.merge(connectorSelection).datum((d, i, elements) => {
|
|
153
153
|
return setActiveState({
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
font-size: 11px;
|
|
10
10
|
font-weight: bold;
|
|
11
11
|
fill: var(--g-color-text-complementary);
|
|
12
|
-
|
|
12
|
+
dominant-baseline: text-before-edge;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
.gcharts-radar__label {
|
|
16
|
-
|
|
16
|
+
dominant-baseline: text-before-edge;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
.gcharts-bar-x__label {
|
|
@@ -24,14 +24,14 @@
|
|
|
24
24
|
.gcharts-bar-y__label {
|
|
25
25
|
user-select: none;
|
|
26
26
|
fill: var(--g-color-text-complementary);
|
|
27
|
-
|
|
27
|
+
dominant-baseline: text-after-edge;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
.gcharts-treemap__label {
|
|
31
31
|
user-select: none;
|
|
32
32
|
pointer-events: none;
|
|
33
33
|
fill: var(--g-color-text-complementary);
|
|
34
|
-
|
|
34
|
+
dominant-baseline: text-before-edge;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
.gcharts-waterfall__connector {
|
|
@@ -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
|
};
|
|
@@ -173,7 +173,7 @@ export const getHorisontalSvgTextHeight = (args) => {
|
|
|
173
173
|
const textSelection = container.append('text').text(text);
|
|
174
174
|
const fontSize = get(style, 'fontSize', DEFAULT_AXIS_LABEL_FONT_SIZE);
|
|
175
175
|
if (fontSize) {
|
|
176
|
-
textSelection.style('font-size', fontSize).style('
|
|
176
|
+
textSelection.style('font-size', fontSize).style('dominant-baseline', 'text-after-edge');
|
|
177
177
|
}
|
|
178
178
|
const height = ((_a = textSelection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().height) || 0;
|
|
179
179
|
container.remove();
|
|
@@ -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
|
-
const tSpan = selection.append('tspan').text(text).style('
|
|
34
|
-
handleOverflowingText(tSpan.node(), maxWidth);
|
|
45
|
+
const tSpan = selection.append('tspan').text(text).style('dominant-baseline', 'inherit');
|
|
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
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export declare function pieCenterText(text: string, options?: {
|
|
2
|
-
padding?: number;
|
|
2
|
+
padding?: string | number;
|
|
3
3
|
color?: string;
|
|
4
|
+
minFontSize?: number;
|
|
4
5
|
}): ((args: {
|
|
5
6
|
series: {
|
|
6
7
|
innerRadius: number;
|
|
7
8
|
};
|
|
8
|
-
}) =>
|
|
9
|
+
}) => SVGGElement | null) | undefined;
|
|
@@ -1,29 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { select } from 'd3-selection';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
|
-
import {
|
|
3
|
+
import { calculateNumericProperty } from '../chart/math';
|
|
4
|
+
import { getLabelsSize, handleOverflowingText } from '../chart/text';
|
|
4
5
|
const MAX_FONT_SIZE = 64;
|
|
6
|
+
const MIN_FONT_SIZE = 8;
|
|
5
7
|
export function pieCenterText(text, options) {
|
|
6
8
|
if (!text) {
|
|
7
9
|
return undefined;
|
|
8
10
|
}
|
|
9
|
-
const padding = get(options, 'padding', 12);
|
|
10
11
|
const color = get(options, 'color', 'currentColor');
|
|
11
12
|
return function (args) {
|
|
13
|
+
var _a, _b;
|
|
12
14
|
let fontSize = MAX_FONT_SIZE;
|
|
13
15
|
const textSize = getLabelsSize({ labels: [text], style: { fontSize: `${fontSize}px` } });
|
|
14
16
|
let availableSpace = args.series.innerRadius * 2;
|
|
17
|
+
const padding = (_a = calculateNumericProperty({
|
|
18
|
+
base: availableSpace,
|
|
19
|
+
value: options === null || options === void 0 ? void 0 : options.padding,
|
|
20
|
+
})) !== null && _a !== void 0 ? _a : 12;
|
|
15
21
|
if (padding < args.series.innerRadius) {
|
|
16
22
|
availableSpace -= padding * 2;
|
|
17
23
|
}
|
|
18
|
-
fontSize = (fontSize * availableSpace) / textSize.maxWidth;
|
|
19
|
-
const
|
|
20
|
-
container
|
|
24
|
+
fontSize = Math.max((_b = options === null || options === void 0 ? void 0 : options.minFontSize) !== null && _b !== void 0 ? _b : MIN_FONT_SIZE, (fontSize * availableSpace) / Math.max(textSize.maxWidth, textSize.maxHeight));
|
|
25
|
+
const tempWrapper = select(document.body).append('svg');
|
|
26
|
+
const container = tempWrapper.append('g');
|
|
27
|
+
const textSelection = container
|
|
21
28
|
.append('text')
|
|
22
|
-
.text(text)
|
|
23
|
-
.attr('text-anchor', 'middle')
|
|
24
|
-
.attr('alignment-baseline', 'middle')
|
|
25
29
|
.style('font-size', `${fontSize}px`)
|
|
26
30
|
.style('fill', color);
|
|
27
|
-
|
|
31
|
+
textSelection.append('title').text(text);
|
|
32
|
+
const tspan = textSelection
|
|
33
|
+
.append('tspan')
|
|
34
|
+
.attr('text-anchor', 'middle')
|
|
35
|
+
.attr('dominant-baseline', 'middle')
|
|
36
|
+
.text(text);
|
|
37
|
+
handleOverflowingText(tspan.node(), availableSpace);
|
|
38
|
+
const result = container.node();
|
|
39
|
+
tempWrapper.remove();
|
|
40
|
+
return result;
|
|
28
41
|
};
|
|
29
42
|
}
|
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
|
+
}
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
}
|
|
4
4
|
.gcharts-axis .tick text {
|
|
5
5
|
color: var(--g-color-text-secondary);
|
|
6
|
-
|
|
6
|
+
dominant-baseline: text-after-edge;
|
|
7
7
|
}
|
|
8
8
|
.gcharts-axis .tick line, .gcharts-axis .tick path {
|
|
9
9
|
stroke: var(--g-color-line-generic);
|
|
10
10
|
}
|
|
11
11
|
.gcharts-axis__title {
|
|
12
|
-
|
|
12
|
+
dominant-baseline: text-after-edge;
|
|
13
13
|
fill: var(--g-color-text-secondary);
|
|
14
14
|
}
|
|
15
15
|
.gcharts-axis__title tspan {
|
|
16
|
-
|
|
16
|
+
dominant-baseline: text-after-edge;
|
|
17
17
|
}
|
|
@@ -294,7 +294,7 @@ export const Legend = (props) => {
|
|
|
294
294
|
.attr('font-weight', (_c = legend.title.style.fontWeight) !== null && _c !== void 0 ? _c : null)
|
|
295
295
|
.attr('font-size', (_d = legend.title.style.fontSize) !== null && _d !== void 0 ? _d : null)
|
|
296
296
|
.attr('fill', (_e = legend.title.style.fontColor) !== null && _e !== void 0 ? _e : null)
|
|
297
|
-
.style('
|
|
297
|
+
.style('dominant-baseline', 'text-before-edge')
|
|
298
298
|
.html(legend.title.text);
|
|
299
299
|
}
|
|
300
300
|
const { left } = getLegendPosition({
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
}
|
|
20
20
|
.gcharts-legend__item-text {
|
|
21
21
|
fill: var(--g-color-text-secondary);
|
|
22
|
-
|
|
22
|
+
dominant-baseline: text-before-edge;
|
|
23
23
|
}
|
|
24
24
|
.gcharts-legend__item-text_unselected {
|
|
25
25
|
fill: var(--g-color-text-hint);
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
fill: var(--g-color-text-primary);
|
|
33
33
|
}
|
|
34
34
|
.gcharts-legend__pagination-counter, .gcharts-legend__pagination-arrow {
|
|
35
|
-
|
|
35
|
+
dominant-baseline: middle;
|
|
36
36
|
}
|
|
37
37
|
.gcharts-legend__pagination-arrow {
|
|
38
38
|
cursor: pointer;
|
|
@@ -147,7 +147,7 @@ export function PieSeriesShapes(args) {
|
|
|
147
147
|
});
|
|
148
148
|
return d;
|
|
149
149
|
});
|
|
150
|
-
const labelSelection = pieSelection.selectAll('
|
|
150
|
+
const labelSelection = pieSelection.selectAll(`.${b('label')} tspan`);
|
|
151
151
|
const connectorSelection = pieSelection.selectAll(connectorSelector);
|
|
152
152
|
labelSelection.merge(connectorSelection).datum((d, i, elements) => {
|
|
153
153
|
return setActiveState({
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
font-size: 11px;
|
|
10
10
|
font-weight: bold;
|
|
11
11
|
fill: var(--g-color-text-complementary);
|
|
12
|
-
|
|
12
|
+
dominant-baseline: text-before-edge;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
.gcharts-radar__label {
|
|
16
|
-
|
|
16
|
+
dominant-baseline: text-before-edge;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
.gcharts-bar-x__label {
|
|
@@ -24,14 +24,14 @@
|
|
|
24
24
|
.gcharts-bar-y__label {
|
|
25
25
|
user-select: none;
|
|
26
26
|
fill: var(--g-color-text-complementary);
|
|
27
|
-
|
|
27
|
+
dominant-baseline: text-after-edge;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
.gcharts-treemap__label {
|
|
31
31
|
user-select: none;
|
|
32
32
|
pointer-events: none;
|
|
33
33
|
fill: var(--g-color-text-complementary);
|
|
34
|
-
|
|
34
|
+
dominant-baseline: text-before-edge;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
.gcharts-waterfall__connector {
|
|
@@ -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
|
};
|
|
@@ -173,7 +173,7 @@ export const getHorisontalSvgTextHeight = (args) => {
|
|
|
173
173
|
const textSelection = container.append('text').text(text);
|
|
174
174
|
const fontSize = get(style, 'fontSize', DEFAULT_AXIS_LABEL_FONT_SIZE);
|
|
175
175
|
if (fontSize) {
|
|
176
|
-
textSelection.style('font-size', fontSize).style('
|
|
176
|
+
textSelection.style('font-size', fontSize).style('dominant-baseline', 'text-after-edge');
|
|
177
177
|
}
|
|
178
178
|
const height = ((_a = textSelection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().height) || 0;
|
|
179
179
|
container.remove();
|
|
@@ -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
|
-
const tSpan = selection.append('tspan').text(text).style('
|
|
34
|
-
handleOverflowingText(tSpan.node(), maxWidth);
|
|
45
|
+
const tSpan = selection.append('tspan').text(text).style('dominant-baseline', 'inherit');
|
|
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
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export declare function pieCenterText(text: string, options?: {
|
|
2
|
-
padding?: number;
|
|
2
|
+
padding?: string | number;
|
|
3
3
|
color?: string;
|
|
4
|
+
minFontSize?: number;
|
|
4
5
|
}): ((args: {
|
|
5
6
|
series: {
|
|
6
7
|
innerRadius: number;
|
|
7
8
|
};
|
|
8
|
-
}) =>
|
|
9
|
+
}) => SVGGElement | null) | undefined;
|
|
@@ -1,29 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { select } from 'd3-selection';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
|
-
import {
|
|
3
|
+
import { calculateNumericProperty } from '../chart/math';
|
|
4
|
+
import { getLabelsSize, handleOverflowingText } from '../chart/text';
|
|
4
5
|
const MAX_FONT_SIZE = 64;
|
|
6
|
+
const MIN_FONT_SIZE = 8;
|
|
5
7
|
export function pieCenterText(text, options) {
|
|
6
8
|
if (!text) {
|
|
7
9
|
return undefined;
|
|
8
10
|
}
|
|
9
|
-
const padding = get(options, 'padding', 12);
|
|
10
11
|
const color = get(options, 'color', 'currentColor');
|
|
11
12
|
return function (args) {
|
|
13
|
+
var _a, _b;
|
|
12
14
|
let fontSize = MAX_FONT_SIZE;
|
|
13
15
|
const textSize = getLabelsSize({ labels: [text], style: { fontSize: `${fontSize}px` } });
|
|
14
16
|
let availableSpace = args.series.innerRadius * 2;
|
|
17
|
+
const padding = (_a = calculateNumericProperty({
|
|
18
|
+
base: availableSpace,
|
|
19
|
+
value: options === null || options === void 0 ? void 0 : options.padding,
|
|
20
|
+
})) !== null && _a !== void 0 ? _a : 12;
|
|
15
21
|
if (padding < args.series.innerRadius) {
|
|
16
22
|
availableSpace -= padding * 2;
|
|
17
23
|
}
|
|
18
|
-
fontSize = (fontSize * availableSpace) / textSize.maxWidth;
|
|
19
|
-
const
|
|
20
|
-
container
|
|
24
|
+
fontSize = Math.max((_b = options === null || options === void 0 ? void 0 : options.minFontSize) !== null && _b !== void 0 ? _b : MIN_FONT_SIZE, (fontSize * availableSpace) / Math.max(textSize.maxWidth, textSize.maxHeight));
|
|
25
|
+
const tempWrapper = select(document.body).append('svg');
|
|
26
|
+
const container = tempWrapper.append('g');
|
|
27
|
+
const textSelection = container
|
|
21
28
|
.append('text')
|
|
22
|
-
.text(text)
|
|
23
|
-
.attr('text-anchor', 'middle')
|
|
24
|
-
.attr('alignment-baseline', 'middle')
|
|
25
29
|
.style('font-size', `${fontSize}px`)
|
|
26
30
|
.style('fill', color);
|
|
27
|
-
|
|
31
|
+
textSelection.append('title').text(text);
|
|
32
|
+
const tspan = textSelection
|
|
33
|
+
.append('tspan')
|
|
34
|
+
.attr('text-anchor', 'middle')
|
|
35
|
+
.attr('dominant-baseline', 'middle')
|
|
36
|
+
.text(text);
|
|
37
|
+
handleOverflowingText(tspan.node(), availableSpace);
|
|
38
|
+
const result = container.node();
|
|
39
|
+
tempWrapper.remove();
|
|
40
|
+
return result;
|
|
28
41
|
};
|
|
29
42
|
}
|
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
|
+
}
|