@gravity-ui/charts 1.6.7 → 1.7.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/dist/cjs/components/ChartInner/useChartInnerProps.js +2 -1
- package/dist/cjs/hooks/useChartOptions/index.js +4 -2
- package/dist/cjs/hooks/useChartOptions/types.d.ts +1 -0
- package/dist/cjs/hooks/useSeries/index.d.ts +1 -0
- package/dist/cjs/hooks/useSeries/index.js +3 -3
- package/dist/cjs/hooks/useSeries/prepare-pie.d.ts +1 -0
- package/dist/cjs/hooks/useSeries/prepare-pie.js +3 -3
- package/dist/cjs/hooks/useSeries/prepare-radar.d.ts +1 -0
- package/dist/cjs/hooks/useSeries/prepare-radar.js +3 -3
- package/dist/cjs/hooks/useSeries/prepare-waterfall.d.ts +1 -0
- package/dist/cjs/hooks/useSeries/prepare-waterfall.js +3 -3
- package/dist/cjs/hooks/useSeries/prepareSeries.d.ts +1 -0
- package/dist/cjs/hooks/useSeries/prepareSeries.js +9 -2
- package/dist/cjs/hooks/useShapes/pie/prepare-data.js +50 -24
- package/dist/cjs/types/index.d.ts +6 -0
- package/dist/cjs/utils/chart/axis-generators/bottom.js +1 -1
- package/dist/cjs/utils/chart/text.js +29 -7
- package/dist/esm/components/ChartInner/useChartInnerProps.js +2 -1
- package/dist/esm/hooks/useChartOptions/index.js +4 -2
- package/dist/esm/hooks/useChartOptions/types.d.ts +1 -0
- package/dist/esm/hooks/useSeries/index.d.ts +1 -0
- package/dist/esm/hooks/useSeries/index.js +3 -3
- package/dist/esm/hooks/useSeries/prepare-pie.d.ts +1 -0
- package/dist/esm/hooks/useSeries/prepare-pie.js +3 -3
- package/dist/esm/hooks/useSeries/prepare-radar.d.ts +1 -0
- package/dist/esm/hooks/useSeries/prepare-radar.js +3 -3
- package/dist/esm/hooks/useSeries/prepare-waterfall.d.ts +1 -0
- package/dist/esm/hooks/useSeries/prepare-waterfall.js +3 -3
- package/dist/esm/hooks/useSeries/prepareSeries.d.ts +1 -0
- package/dist/esm/hooks/useSeries/prepareSeries.js +9 -2
- package/dist/esm/hooks/useShapes/pie/prepare-data.js +50 -24
- package/dist/esm/types/index.d.ts +6 -0
- package/dist/esm/utils/chart/axis-generators/bottom.js +1 -1
- package/dist/esm/utils/chart/text.js +29 -7
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ export function useChartInnerProps(props) {
|
|
|
8
8
|
const { width, height, data, dispatcher, htmlLayout, svgContainer } = props;
|
|
9
9
|
const prevWidth = usePrevious(width);
|
|
10
10
|
const prevHeight = usePrevious(height);
|
|
11
|
-
const { chart, title, tooltip } = useChartOptions({ data });
|
|
11
|
+
const { chart, title, tooltip, colors } = useChartOptions({ data });
|
|
12
12
|
const xAxis = React.useMemo(() => getPreparedXAxis({ xAxis: data.xAxis, width, series: data.series.data }), [data, width]);
|
|
13
13
|
const yAxis = React.useMemo(() => getPreparedYAxis({
|
|
14
14
|
series: data.series.data,
|
|
@@ -22,6 +22,7 @@ export function useChartInnerProps(props) {
|
|
|
22
22
|
series: data.series,
|
|
23
23
|
legend: data.legend,
|
|
24
24
|
preparedYAxis: yAxis,
|
|
25
|
+
colors,
|
|
25
26
|
});
|
|
26
27
|
const { boundsWidth, boundsHeight } = useChartDimensions({
|
|
27
28
|
width,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { DEFAULT_PALETTE } from '../../constants';
|
|
2
3
|
import { getPreparedChart } from './chart';
|
|
3
4
|
import { getPreparedTitle } from './title';
|
|
4
5
|
import { getPreparedTooltip } from './tooltip';
|
|
5
6
|
export const useChartOptions = (args) => {
|
|
6
|
-
const { data: { chart, title, tooltip }, } = args;
|
|
7
|
+
const { data: { chart, title, tooltip, colors }, } = args;
|
|
7
8
|
const options = React.useMemo(() => {
|
|
8
9
|
const preparedTitle = getPreparedTitle({ title });
|
|
9
10
|
const preparedTooltip = getPreparedTooltip({ tooltip });
|
|
@@ -15,7 +16,8 @@ export const useChartOptions = (args) => {
|
|
|
15
16
|
chart: preparedChart,
|
|
16
17
|
title: preparedTitle,
|
|
17
18
|
tooltip: preparedTooltip,
|
|
19
|
+
colors: colors !== null && colors !== void 0 ? colors : DEFAULT_PALETTE,
|
|
18
20
|
};
|
|
19
|
-
}, [chart, title, tooltip]);
|
|
21
|
+
}, [chart, colors, title, tooltip]);
|
|
20
22
|
return options;
|
|
21
23
|
};
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { group, scaleOrdinal } from 'd3';
|
|
3
|
-
import { DEFAULT_PALETTE } from '../../constants';
|
|
4
3
|
import { getSeriesNames } from '../../utils';
|
|
5
4
|
import { getLegendComponents, getPreparedLegend } from './prepare-legend';
|
|
6
5
|
import { getPreparedOptions } from './prepare-options';
|
|
7
6
|
import { prepareSeries } from './prepareSeries';
|
|
8
7
|
import { getActiveLegendItems, getAllLegendItems } from './utils';
|
|
9
8
|
export const useSeries = (args) => {
|
|
10
|
-
const { chartWidth, chartHeight, chartMargin, legend, preparedYAxis, series: { data: series, options: seriesOptions }, } = args;
|
|
9
|
+
const { chartWidth, chartHeight, chartMargin, legend, preparedYAxis, series: { data: series, options: seriesOptions }, colors, } = args;
|
|
11
10
|
const preparedLegend = React.useMemo(() => getPreparedLegend({ legend, series }), [legend, series]);
|
|
12
11
|
const preparedSeries = React.useMemo(() => {
|
|
13
12
|
const seriesNames = getSeriesNames(series);
|
|
14
|
-
const colorScale = scaleOrdinal(seriesNames,
|
|
13
|
+
const colorScale = scaleOrdinal(seriesNames, colors);
|
|
15
14
|
const groupedSeries = group(series, (item) => item.type);
|
|
16
15
|
return Array.from(groupedSeries).reduce((acc, [seriesType, seriesList]) => {
|
|
17
16
|
acc.push(...prepareSeries({
|
|
@@ -20,6 +19,7 @@ export const useSeries = (args) => {
|
|
|
20
19
|
seriesOptions,
|
|
21
20
|
legend: preparedLegend,
|
|
22
21
|
colorScale,
|
|
22
|
+
colors,
|
|
23
23
|
}));
|
|
24
24
|
return acc;
|
|
25
25
|
}, []);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { scaleOrdinal } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
|
-
import { DEFAULT_DATALABELS_STYLE
|
|
3
|
+
import { DEFAULT_DATALABELS_STYLE } from '../../constants';
|
|
4
4
|
import { getUniqId } from '../../utils';
|
|
5
5
|
import { DEFAULT_DATALABELS_PADDING } from './constants';
|
|
6
6
|
import { prepareLegendSymbol } from './utils';
|
|
7
7
|
export function preparePieSeries(args) {
|
|
8
|
-
const { series, seriesOptions, legend } = args;
|
|
8
|
+
const { series, seriesOptions, legend, colors } = args;
|
|
9
9
|
const dataNames = series.data.map((d) => d.name);
|
|
10
|
-
const colorScale = scaleOrdinal(dataNames,
|
|
10
|
+
const colorScale = scaleOrdinal(dataNames, colors);
|
|
11
11
|
const stackId = getUniqId();
|
|
12
12
|
const seriesHoverState = get(seriesOptions, 'pie.states.hover');
|
|
13
13
|
const preparedSeries = series.data.map((dataItem, i) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { scaleOrdinal } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
3
|
import merge from 'lodash/merge';
|
|
4
|
-
import { DEFAULT_DATALABELS_STYLE
|
|
4
|
+
import { DEFAULT_DATALABELS_STYLE } from '../../constants';
|
|
5
5
|
import { getUniqId } from '../../utils';
|
|
6
6
|
import { DEFAULT_DATALABELS_PADDING, DEFAULT_HALO_OPTIONS, DEFAULT_POINT_MARKER_OPTIONS, } from './constants';
|
|
7
7
|
import { prepareLegendSymbol } from './utils';
|
|
@@ -26,8 +26,8 @@ function prepareMarker(series, seriesOptions) {
|
|
|
26
26
|
}
|
|
27
27
|
export function prepareRadarSeries(args) {
|
|
28
28
|
var _a, _b;
|
|
29
|
-
const { series: radarSeries, seriesOptions, legend } = args;
|
|
30
|
-
const colorScale = scaleOrdinal(radarSeries.map((s, index) => { var _a; return (_a = s.name) !== null && _a !== void 0 ? _a : `Series ${index + 1}`; }),
|
|
29
|
+
const { series: radarSeries, seriesOptions, legend, colors } = args;
|
|
30
|
+
const colorScale = scaleOrdinal(radarSeries.map((s, index) => { var _a; return (_a = s.name) !== null && _a !== void 0 ? _a : `Series ${index + 1}`; }), colors);
|
|
31
31
|
const categories = (_b = (_a = radarSeries.find((s) => s.categories)) === null || _a === void 0 ? void 0 : _a.categories) !== null && _b !== void 0 ? _b : [];
|
|
32
32
|
return radarSeries.map((series, index) => {
|
|
33
33
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -5,6 +5,7 @@ type PrepareWaterfallSeriesArgs = {
|
|
|
5
5
|
colorScale: ScaleOrdinal<string, string>;
|
|
6
6
|
series: WaterfallSeries[];
|
|
7
7
|
legend: PreparedLegend;
|
|
8
|
+
colors: string[];
|
|
8
9
|
};
|
|
9
10
|
export declare function prepareWaterfallSeries(args: PrepareWaterfallSeriesArgs): PreparedSeries[];
|
|
10
11
|
export {};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
|
-
import { DEFAULT_DATALABELS_STYLE
|
|
2
|
+
import { DEFAULT_DATALABELS_STYLE } from '../../constants';
|
|
3
3
|
import { getUniqId } from '../../utils';
|
|
4
4
|
import { DEFAULT_DATALABELS_PADDING } from './constants';
|
|
5
5
|
import { prepareLegendSymbol } from './utils';
|
|
6
6
|
export function prepareWaterfallSeries(args) {
|
|
7
7
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
8
|
-
const { colorScale, series: seriesList, legend } = args;
|
|
9
|
-
const [, negativeColor, positiveColor] =
|
|
8
|
+
const { colorScale, series: seriesList, legend, colors } = args;
|
|
9
|
+
const [, negativeColor, positiveColor] = colors;
|
|
10
10
|
const series = seriesList[0];
|
|
11
11
|
const common = {
|
|
12
12
|
id: '',
|
|
@@ -10,11 +10,16 @@ import { prepareScatterSeries } from './prepare-scatter';
|
|
|
10
10
|
import { prepareTreemap } from './prepare-treemap';
|
|
11
11
|
import { prepareWaterfallSeries } from './prepare-waterfall';
|
|
12
12
|
export function prepareSeries(args) {
|
|
13
|
-
const { type, series, seriesOptions, legend, colorScale } = args;
|
|
13
|
+
const { type, series, seriesOptions, legend, colors, colorScale } = args;
|
|
14
14
|
switch (type) {
|
|
15
15
|
case 'pie': {
|
|
16
16
|
return series.reduce((acc, singleSeries) => {
|
|
17
|
-
acc.push(...preparePieSeries({
|
|
17
|
+
acc.push(...preparePieSeries({
|
|
18
|
+
series: singleSeries,
|
|
19
|
+
seriesOptions,
|
|
20
|
+
legend,
|
|
21
|
+
colors,
|
|
22
|
+
}));
|
|
18
23
|
return acc;
|
|
19
24
|
}, []);
|
|
20
25
|
}
|
|
@@ -66,6 +71,7 @@ export function prepareSeries(args) {
|
|
|
66
71
|
series: series,
|
|
67
72
|
legend,
|
|
68
73
|
colorScale,
|
|
74
|
+
colors,
|
|
69
75
|
});
|
|
70
76
|
}
|
|
71
77
|
case 'sankey': {
|
|
@@ -81,6 +87,7 @@ export function prepareSeries(args) {
|
|
|
81
87
|
series: series,
|
|
82
88
|
seriesOptions,
|
|
83
89
|
legend,
|
|
90
|
+
colors,
|
|
84
91
|
});
|
|
85
92
|
}
|
|
86
93
|
default: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { arc, group, line as lineGenerator } from 'd3';
|
|
1
|
+
import { arc, group, line as lineGenerator, max } from 'd3';
|
|
2
2
|
import merge from 'lodash/merge';
|
|
3
3
|
import { DEFAULT_DATALABELS_STYLE } from '../../../constants';
|
|
4
|
-
import { calculateNumericProperty, getLabelsSize, getLeftPosition, isLabelsOverlapping, } from '../../../utils';
|
|
4
|
+
import { calculateNumericProperty, getLabelsSize, getLeftPosition, getTextSizeFn, isLabelsOverlapping, } from '../../../utils';
|
|
5
5
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
6
6
|
import { getCurveFactory, getInscribedAngle, pieGenerator } from './utils';
|
|
7
7
|
const FULL_CIRCLE = Math.PI * 2;
|
|
@@ -31,7 +31,8 @@ export function preparePieData(args) {
|
|
|
31
31
|
const minRadius = typeof propsMinRadius === 'number' ? propsMinRadius : maxRadius * 0.3;
|
|
32
32
|
const groupedPieSeries = group(preparedSeries, (pieSeries) => pieSeries.stackId);
|
|
33
33
|
const dataLabelsStyle = merge({}, DEFAULT_DATALABELS_STYLE, (_b = (_a = preparedSeries[0]) === null || _a === void 0 ? void 0 : _a.dataLabels) === null || _b === void 0 ? void 0 : _b.style);
|
|
34
|
-
const prepareItem = (stackId, items) => {
|
|
34
|
+
const prepareItem = ({ stackId, items, labels, }) => {
|
|
35
|
+
var _a;
|
|
35
36
|
const series = items[0];
|
|
36
37
|
const { center, borderWidth, borderColor, borderRadius, dataLabels } = series;
|
|
37
38
|
const data = {
|
|
@@ -53,15 +54,13 @@ export function preparePieData(args) {
|
|
|
53
54
|
size: series.states.hover.halo.size,
|
|
54
55
|
},
|
|
55
56
|
};
|
|
56
|
-
const {
|
|
57
|
-
labels: ['Some Label'],
|
|
58
|
-
style: dataLabelsStyle,
|
|
59
|
-
});
|
|
57
|
+
const labelMaxHeight = (_a = max(Object.values(labels).map((l) => { var _a, _b; return (_b = (_a = l.size) === null || _a === void 0 ? void 0 : _a.height) !== null && _b !== void 0 ? _b : 0; }))) !== null && _a !== void 0 ? _a : 0;
|
|
60
58
|
const segments = items.map((item) => {
|
|
61
59
|
var _a;
|
|
62
60
|
let maxSegmentRadius = maxRadius;
|
|
63
61
|
if (dataLabels.enabled) {
|
|
64
|
-
maxSegmentRadius -=
|
|
62
|
+
maxSegmentRadius -=
|
|
63
|
+
dataLabels.distance + dataLabels.connectorPadding + labelMaxHeight;
|
|
65
64
|
}
|
|
66
65
|
const segmentRadius = (_a = calculateNumericProperty({ value: item.radius, base: maxSegmentRadius })) !== null && _a !== void 0 ? _a : maxSegmentRadius;
|
|
67
66
|
return {
|
|
@@ -78,8 +77,40 @@ export function preparePieData(args) {
|
|
|
78
77
|
data.segments = pieGenerator(segments);
|
|
79
78
|
return data;
|
|
80
79
|
};
|
|
80
|
+
const getLabels = ({ series }) => {
|
|
81
|
+
const { dataLabels } = series[0];
|
|
82
|
+
if (!dataLabels.enabled) {
|
|
83
|
+
return {};
|
|
84
|
+
}
|
|
85
|
+
const getTextSize = getTextSizeFn({ style: dataLabelsStyle });
|
|
86
|
+
return series.reduce((acc, d) => {
|
|
87
|
+
const text = getFormattedValue(Object.assign({ value: d.data.label || d.data.value }, d.dataLabels));
|
|
88
|
+
let labelWidth = 0;
|
|
89
|
+
let labelHeight = 0;
|
|
90
|
+
if (dataLabels.html) {
|
|
91
|
+
const size = getLabelsSize({
|
|
92
|
+
labels: [text],
|
|
93
|
+
style: dataLabelsStyle,
|
|
94
|
+
html: true,
|
|
95
|
+
});
|
|
96
|
+
labelWidth = size.maxWidth;
|
|
97
|
+
labelHeight = size.maxHeight;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const size = getTextSize(text);
|
|
101
|
+
labelWidth = size.width;
|
|
102
|
+
labelHeight = size.height;
|
|
103
|
+
}
|
|
104
|
+
const label = {
|
|
105
|
+
text,
|
|
106
|
+
size: { width: labelWidth, height: labelHeight },
|
|
107
|
+
};
|
|
108
|
+
acc[d.id] = label;
|
|
109
|
+
return acc;
|
|
110
|
+
}, {});
|
|
111
|
+
};
|
|
81
112
|
const prepareLabels = (prepareLabelsArgs) => {
|
|
82
|
-
const { data, series, allowOverlow = true } = prepareLabelsArgs;
|
|
113
|
+
const { data, series, labels: labelsData, allowOverlow = true } = prepareLabelsArgs;
|
|
83
114
|
const { dataLabels } = series[0];
|
|
84
115
|
const labels = [];
|
|
85
116
|
const htmlLabels = [];
|
|
@@ -93,12 +124,7 @@ export function preparePieData(args) {
|
|
|
93
124
|
if (curveFactory) {
|
|
94
125
|
line = line.curve(curveFactory);
|
|
95
126
|
}
|
|
96
|
-
const {
|
|
97
|
-
const { maxHeight: labelHeight } = getLabelsSize({
|
|
98
|
-
labels: ['Some Label'],
|
|
99
|
-
style: dataLabelsStyle,
|
|
100
|
-
html: shouldUseHtml,
|
|
101
|
-
});
|
|
127
|
+
const { connectorPadding, distance } = dataLabels;
|
|
102
128
|
const connectorStartPointGenerator = arc()
|
|
103
129
|
.innerRadius((d) => d.data.radius)
|
|
104
130
|
.outerRadius((d) => d.data.radius);
|
|
@@ -114,14 +140,11 @@ export function preparePieData(args) {
|
|
|
114
140
|
let shouldStopLabelPlacement = false;
|
|
115
141
|
// eslint-disable-next-line complexity
|
|
116
142
|
series.forEach((d, index) => {
|
|
143
|
+
var _a, _b;
|
|
117
144
|
const prevLabel = labels[labels.length - 1];
|
|
118
|
-
const text =
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
style: dataLabelsStyle,
|
|
122
|
-
html: shouldUseHtml,
|
|
123
|
-
});
|
|
124
|
-
const labelWidth = labelSize.maxWidth;
|
|
145
|
+
const { text = '', size: labelSize } = labelsData[d.id];
|
|
146
|
+
const labelWidth = (_a = labelSize === null || labelSize === void 0 ? void 0 : labelSize.width) !== null && _a !== void 0 ? _a : 0;
|
|
147
|
+
const labelHeight = (_b = labelSize === null || labelSize === void 0 ? void 0 : labelSize.height) !== null && _b !== void 0 ? _b : 0;
|
|
125
148
|
const relatedSegment = data.segments[index];
|
|
126
149
|
/**
|
|
127
150
|
* Compute the label coordinates on the label arc for a given angle.
|
|
@@ -159,7 +182,7 @@ export function preparePieData(args) {
|
|
|
159
182
|
text,
|
|
160
183
|
x,
|
|
161
184
|
y,
|
|
162
|
-
style,
|
|
185
|
+
style: dataLabelsStyle,
|
|
163
186
|
size: { width: labelWidth, height: labelHeight },
|
|
164
187
|
maxWidth: labelWidth,
|
|
165
188
|
textAnchor: midAngle < Math.PI ? 'start' : 'end',
|
|
@@ -251,9 +274,11 @@ export function preparePieData(args) {
|
|
|
251
274
|
};
|
|
252
275
|
return Array.from(groupedPieSeries).map(([stackId, items]) => {
|
|
253
276
|
var _a;
|
|
254
|
-
const
|
|
277
|
+
const seriesLabels = getLabels({ series: items });
|
|
278
|
+
const data = prepareItem({ stackId, items, labels: seriesLabels });
|
|
255
279
|
const preparedLabels = prepareLabels({
|
|
256
280
|
data,
|
|
281
|
+
labels: seriesLabels,
|
|
257
282
|
series: items,
|
|
258
283
|
});
|
|
259
284
|
let maxLeftRightFreeSpace = Infinity;
|
|
@@ -329,6 +354,7 @@ export function preparePieData(args) {
|
|
|
329
354
|
const { labels, htmlLabels, connectors } = prepareLabels({
|
|
330
355
|
data,
|
|
331
356
|
series: items,
|
|
357
|
+
labels: seriesLabels,
|
|
332
358
|
allowOverlow: false,
|
|
333
359
|
});
|
|
334
360
|
if (typeof ((_a = items[0]) === null || _a === void 0 ? void 0 : _a.innerRadius) !== 'undefined') {
|
|
@@ -71,4 +71,10 @@ export interface ChartData<T = MeaningfulAny> {
|
|
|
71
71
|
* It can be used to visualize related information on multiple charts.
|
|
72
72
|
*/
|
|
73
73
|
split?: ChartSplit;
|
|
74
|
+
/** The color list of palette.
|
|
75
|
+
* If no color is set in series, the colors would be adopted sequentially and circularly from this list as the colors of series.
|
|
76
|
+
*
|
|
77
|
+
* @default ['#4DA2F1', '#FF3D64', '#8AD554', '#FFC636', '#FFB9DD', '#84D1EE', '#FF91A1', '#54A520', '#DB9100', '#BA74B3', '#1F68A9', '#ED65A9', '#0FA08D', '#FF7E00', '#E8B0A4', '#52A6C5', '#BE2443', '#70C1AF', '#FFB46C', '#DCA3D7']
|
|
78
|
+
* */
|
|
79
|
+
colors?: string[];
|
|
74
80
|
}
|
|
@@ -21,7 +21,7 @@ export function axisBottom(args) {
|
|
|
21
21
|
const position = getXTickPosition({ scale, offset });
|
|
22
22
|
const values = getXAxisItems({ scale, count: ticksCount, maxCount: maxTickCount });
|
|
23
23
|
const labelHeight = getLabelsSize({
|
|
24
|
-
labels: values,
|
|
24
|
+
labels: values.map(labelFormat),
|
|
25
25
|
style: labelsStyle,
|
|
26
26
|
}).maxHeight;
|
|
27
27
|
return function (selection) {
|
|
@@ -165,11 +165,27 @@ export function wrapText(args) {
|
|
|
165
165
|
return acc;
|
|
166
166
|
}, []);
|
|
167
167
|
}
|
|
168
|
+
const entityMap = {
|
|
169
|
+
'&': '&',
|
|
170
|
+
'<': '<',
|
|
171
|
+
'>': '>',
|
|
172
|
+
'"': '"',
|
|
173
|
+
"'": ''',
|
|
174
|
+
'/': '/',
|
|
175
|
+
'`': '`',
|
|
176
|
+
'=': '=',
|
|
177
|
+
};
|
|
178
|
+
function unescapeHtml(str) {
|
|
179
|
+
return Object.entries(entityMap).reduce((result, [key, value]) => {
|
|
180
|
+
return result.replace(value, key);
|
|
181
|
+
}, str);
|
|
182
|
+
}
|
|
168
183
|
export function getTextSizeFn({ style }) {
|
|
169
184
|
const map = {};
|
|
170
185
|
const setSymbolSize = (s) => {
|
|
186
|
+
const labels = [s === ' ' ? ' ' : s];
|
|
171
187
|
const size = getLabelsSize({
|
|
172
|
-
labels
|
|
188
|
+
labels,
|
|
173
189
|
style,
|
|
174
190
|
});
|
|
175
191
|
map[s] = { width: size.maxWidth, height: size.maxHeight };
|
|
@@ -177,7 +193,7 @@ export function getTextSizeFn({ style }) {
|
|
|
177
193
|
return (str) => {
|
|
178
194
|
let width = 0;
|
|
179
195
|
let height = 0;
|
|
180
|
-
[...str].forEach((s) => {
|
|
196
|
+
[...unescapeHtml(str)].forEach((s) => {
|
|
181
197
|
if (!map[s]) {
|
|
182
198
|
setSymbolSize(s);
|
|
183
199
|
}
|
|
@@ -187,14 +203,20 @@ export function getTextSizeFn({ style }) {
|
|
|
187
203
|
return { width, height };
|
|
188
204
|
};
|
|
189
205
|
}
|
|
206
|
+
// We ignore an inaccuracy of less than a pixel.
|
|
207
|
+
// To do this, we round the font size down when comparing it, and the size of the allowed space up.
|
|
190
208
|
export function getTextWithElipsis({ text: originalText, getTextWidth, maxWidth, }) {
|
|
191
|
-
let
|
|
192
|
-
|
|
193
|
-
|
|
209
|
+
let textWidth = Math.floor(getTextWidth(originalText));
|
|
210
|
+
const textMaxWidth = Math.ceil(maxWidth);
|
|
211
|
+
if (textWidth <= textMaxWidth) {
|
|
212
|
+
return originalText;
|
|
213
|
+
}
|
|
214
|
+
let text = originalText + '…';
|
|
215
|
+
while (textWidth > textMaxWidth && text.length > 2) {
|
|
194
216
|
text = text.slice(0, -2) + '…';
|
|
195
|
-
|
|
217
|
+
textWidth = Math.floor(getTextWidth(text));
|
|
196
218
|
}
|
|
197
|
-
if (
|
|
219
|
+
if (textWidth > textMaxWidth) {
|
|
198
220
|
text = '';
|
|
199
221
|
}
|
|
200
222
|
return text;
|
|
@@ -8,7 +8,7 @@ export function useChartInnerProps(props) {
|
|
|
8
8
|
const { width, height, data, dispatcher, htmlLayout, svgContainer } = props;
|
|
9
9
|
const prevWidth = usePrevious(width);
|
|
10
10
|
const prevHeight = usePrevious(height);
|
|
11
|
-
const { chart, title, tooltip } = useChartOptions({ data });
|
|
11
|
+
const { chart, title, tooltip, colors } = useChartOptions({ data });
|
|
12
12
|
const xAxis = React.useMemo(() => getPreparedXAxis({ xAxis: data.xAxis, width, series: data.series.data }), [data, width]);
|
|
13
13
|
const yAxis = React.useMemo(() => getPreparedYAxis({
|
|
14
14
|
series: data.series.data,
|
|
@@ -22,6 +22,7 @@ export function useChartInnerProps(props) {
|
|
|
22
22
|
series: data.series,
|
|
23
23
|
legend: data.legend,
|
|
24
24
|
preparedYAxis: yAxis,
|
|
25
|
+
colors,
|
|
25
26
|
});
|
|
26
27
|
const { boundsWidth, boundsHeight } = useChartDimensions({
|
|
27
28
|
width,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { DEFAULT_PALETTE } from '../../constants';
|
|
2
3
|
import { getPreparedChart } from './chart';
|
|
3
4
|
import { getPreparedTitle } from './title';
|
|
4
5
|
import { getPreparedTooltip } from './tooltip';
|
|
5
6
|
export const useChartOptions = (args) => {
|
|
6
|
-
const { data: { chart, title, tooltip }, } = args;
|
|
7
|
+
const { data: { chart, title, tooltip, colors }, } = args;
|
|
7
8
|
const options = React.useMemo(() => {
|
|
8
9
|
const preparedTitle = getPreparedTitle({ title });
|
|
9
10
|
const preparedTooltip = getPreparedTooltip({ tooltip });
|
|
@@ -15,7 +16,8 @@ export const useChartOptions = (args) => {
|
|
|
15
16
|
chart: preparedChart,
|
|
16
17
|
title: preparedTitle,
|
|
17
18
|
tooltip: preparedTooltip,
|
|
19
|
+
colors: colors !== null && colors !== void 0 ? colors : DEFAULT_PALETTE,
|
|
18
20
|
};
|
|
19
|
-
}, [chart, title, tooltip]);
|
|
21
|
+
}, [chart, colors, title, tooltip]);
|
|
20
22
|
return options;
|
|
21
23
|
};
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { group, scaleOrdinal } from 'd3';
|
|
3
|
-
import { DEFAULT_PALETTE } from '../../constants';
|
|
4
3
|
import { getSeriesNames } from '../../utils';
|
|
5
4
|
import { getLegendComponents, getPreparedLegend } from './prepare-legend';
|
|
6
5
|
import { getPreparedOptions } from './prepare-options';
|
|
7
6
|
import { prepareSeries } from './prepareSeries';
|
|
8
7
|
import { getActiveLegendItems, getAllLegendItems } from './utils';
|
|
9
8
|
export const useSeries = (args) => {
|
|
10
|
-
const { chartWidth, chartHeight, chartMargin, legend, preparedYAxis, series: { data: series, options: seriesOptions }, } = args;
|
|
9
|
+
const { chartWidth, chartHeight, chartMargin, legend, preparedYAxis, series: { data: series, options: seriesOptions }, colors, } = args;
|
|
11
10
|
const preparedLegend = React.useMemo(() => getPreparedLegend({ legend, series }), [legend, series]);
|
|
12
11
|
const preparedSeries = React.useMemo(() => {
|
|
13
12
|
const seriesNames = getSeriesNames(series);
|
|
14
|
-
const colorScale = scaleOrdinal(seriesNames,
|
|
13
|
+
const colorScale = scaleOrdinal(seriesNames, colors);
|
|
15
14
|
const groupedSeries = group(series, (item) => item.type);
|
|
16
15
|
return Array.from(groupedSeries).reduce((acc, [seriesType, seriesList]) => {
|
|
17
16
|
acc.push(...prepareSeries({
|
|
@@ -20,6 +19,7 @@ export const useSeries = (args) => {
|
|
|
20
19
|
seriesOptions,
|
|
21
20
|
legend: preparedLegend,
|
|
22
21
|
colorScale,
|
|
22
|
+
colors,
|
|
23
23
|
}));
|
|
24
24
|
return acc;
|
|
25
25
|
}, []);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { scaleOrdinal } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
|
-
import { DEFAULT_DATALABELS_STYLE
|
|
3
|
+
import { DEFAULT_DATALABELS_STYLE } from '../../constants';
|
|
4
4
|
import { getUniqId } from '../../utils';
|
|
5
5
|
import { DEFAULT_DATALABELS_PADDING } from './constants';
|
|
6
6
|
import { prepareLegendSymbol } from './utils';
|
|
7
7
|
export function preparePieSeries(args) {
|
|
8
|
-
const { series, seriesOptions, legend } = args;
|
|
8
|
+
const { series, seriesOptions, legend, colors } = args;
|
|
9
9
|
const dataNames = series.data.map((d) => d.name);
|
|
10
|
-
const colorScale = scaleOrdinal(dataNames,
|
|
10
|
+
const colorScale = scaleOrdinal(dataNames, colors);
|
|
11
11
|
const stackId = getUniqId();
|
|
12
12
|
const seriesHoverState = get(seriesOptions, 'pie.states.hover');
|
|
13
13
|
const preparedSeries = series.data.map((dataItem, i) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { scaleOrdinal } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
3
|
import merge from 'lodash/merge';
|
|
4
|
-
import { DEFAULT_DATALABELS_STYLE
|
|
4
|
+
import { DEFAULT_DATALABELS_STYLE } from '../../constants';
|
|
5
5
|
import { getUniqId } from '../../utils';
|
|
6
6
|
import { DEFAULT_DATALABELS_PADDING, DEFAULT_HALO_OPTIONS, DEFAULT_POINT_MARKER_OPTIONS, } from './constants';
|
|
7
7
|
import { prepareLegendSymbol } from './utils';
|
|
@@ -26,8 +26,8 @@ function prepareMarker(series, seriesOptions) {
|
|
|
26
26
|
}
|
|
27
27
|
export function prepareRadarSeries(args) {
|
|
28
28
|
var _a, _b;
|
|
29
|
-
const { series: radarSeries, seriesOptions, legend } = args;
|
|
30
|
-
const colorScale = scaleOrdinal(radarSeries.map((s, index) => { var _a; return (_a = s.name) !== null && _a !== void 0 ? _a : `Series ${index + 1}`; }),
|
|
29
|
+
const { series: radarSeries, seriesOptions, legend, colors } = args;
|
|
30
|
+
const colorScale = scaleOrdinal(radarSeries.map((s, index) => { var _a; return (_a = s.name) !== null && _a !== void 0 ? _a : `Series ${index + 1}`; }), colors);
|
|
31
31
|
const categories = (_b = (_a = radarSeries.find((s) => s.categories)) === null || _a === void 0 ? void 0 : _a.categories) !== null && _b !== void 0 ? _b : [];
|
|
32
32
|
return radarSeries.map((series, index) => {
|
|
33
33
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -5,6 +5,7 @@ type PrepareWaterfallSeriesArgs = {
|
|
|
5
5
|
colorScale: ScaleOrdinal<string, string>;
|
|
6
6
|
series: WaterfallSeries[];
|
|
7
7
|
legend: PreparedLegend;
|
|
8
|
+
colors: string[];
|
|
8
9
|
};
|
|
9
10
|
export declare function prepareWaterfallSeries(args: PrepareWaterfallSeriesArgs): PreparedSeries[];
|
|
10
11
|
export {};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
|
-
import { DEFAULT_DATALABELS_STYLE
|
|
2
|
+
import { DEFAULT_DATALABELS_STYLE } from '../../constants';
|
|
3
3
|
import { getUniqId } from '../../utils';
|
|
4
4
|
import { DEFAULT_DATALABELS_PADDING } from './constants';
|
|
5
5
|
import { prepareLegendSymbol } from './utils';
|
|
6
6
|
export function prepareWaterfallSeries(args) {
|
|
7
7
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
8
|
-
const { colorScale, series: seriesList, legend } = args;
|
|
9
|
-
const [, negativeColor, positiveColor] =
|
|
8
|
+
const { colorScale, series: seriesList, legend, colors } = args;
|
|
9
|
+
const [, negativeColor, positiveColor] = colors;
|
|
10
10
|
const series = seriesList[0];
|
|
11
11
|
const common = {
|
|
12
12
|
id: '',
|
|
@@ -10,11 +10,16 @@ import { prepareScatterSeries } from './prepare-scatter';
|
|
|
10
10
|
import { prepareTreemap } from './prepare-treemap';
|
|
11
11
|
import { prepareWaterfallSeries } from './prepare-waterfall';
|
|
12
12
|
export function prepareSeries(args) {
|
|
13
|
-
const { type, series, seriesOptions, legend, colorScale } = args;
|
|
13
|
+
const { type, series, seriesOptions, legend, colors, colorScale } = args;
|
|
14
14
|
switch (type) {
|
|
15
15
|
case 'pie': {
|
|
16
16
|
return series.reduce((acc, singleSeries) => {
|
|
17
|
-
acc.push(...preparePieSeries({
|
|
17
|
+
acc.push(...preparePieSeries({
|
|
18
|
+
series: singleSeries,
|
|
19
|
+
seriesOptions,
|
|
20
|
+
legend,
|
|
21
|
+
colors,
|
|
22
|
+
}));
|
|
18
23
|
return acc;
|
|
19
24
|
}, []);
|
|
20
25
|
}
|
|
@@ -66,6 +71,7 @@ export function prepareSeries(args) {
|
|
|
66
71
|
series: series,
|
|
67
72
|
legend,
|
|
68
73
|
colorScale,
|
|
74
|
+
colors,
|
|
69
75
|
});
|
|
70
76
|
}
|
|
71
77
|
case 'sankey': {
|
|
@@ -81,6 +87,7 @@ export function prepareSeries(args) {
|
|
|
81
87
|
series: series,
|
|
82
88
|
seriesOptions,
|
|
83
89
|
legend,
|
|
90
|
+
colors,
|
|
84
91
|
});
|
|
85
92
|
}
|
|
86
93
|
default: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { arc, group, line as lineGenerator } from 'd3';
|
|
1
|
+
import { arc, group, line as lineGenerator, max } from 'd3';
|
|
2
2
|
import merge from 'lodash/merge';
|
|
3
3
|
import { DEFAULT_DATALABELS_STYLE } from '../../../constants';
|
|
4
|
-
import { calculateNumericProperty, getLabelsSize, getLeftPosition, isLabelsOverlapping, } from '../../../utils';
|
|
4
|
+
import { calculateNumericProperty, getLabelsSize, getLeftPosition, getTextSizeFn, isLabelsOverlapping, } from '../../../utils';
|
|
5
5
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
6
6
|
import { getCurveFactory, getInscribedAngle, pieGenerator } from './utils';
|
|
7
7
|
const FULL_CIRCLE = Math.PI * 2;
|
|
@@ -31,7 +31,8 @@ export function preparePieData(args) {
|
|
|
31
31
|
const minRadius = typeof propsMinRadius === 'number' ? propsMinRadius : maxRadius * 0.3;
|
|
32
32
|
const groupedPieSeries = group(preparedSeries, (pieSeries) => pieSeries.stackId);
|
|
33
33
|
const dataLabelsStyle = merge({}, DEFAULT_DATALABELS_STYLE, (_b = (_a = preparedSeries[0]) === null || _a === void 0 ? void 0 : _a.dataLabels) === null || _b === void 0 ? void 0 : _b.style);
|
|
34
|
-
const prepareItem = (stackId, items) => {
|
|
34
|
+
const prepareItem = ({ stackId, items, labels, }) => {
|
|
35
|
+
var _a;
|
|
35
36
|
const series = items[0];
|
|
36
37
|
const { center, borderWidth, borderColor, borderRadius, dataLabels } = series;
|
|
37
38
|
const data = {
|
|
@@ -53,15 +54,13 @@ export function preparePieData(args) {
|
|
|
53
54
|
size: series.states.hover.halo.size,
|
|
54
55
|
},
|
|
55
56
|
};
|
|
56
|
-
const {
|
|
57
|
-
labels: ['Some Label'],
|
|
58
|
-
style: dataLabelsStyle,
|
|
59
|
-
});
|
|
57
|
+
const labelMaxHeight = (_a = max(Object.values(labels).map((l) => { var _a, _b; return (_b = (_a = l.size) === null || _a === void 0 ? void 0 : _a.height) !== null && _b !== void 0 ? _b : 0; }))) !== null && _a !== void 0 ? _a : 0;
|
|
60
58
|
const segments = items.map((item) => {
|
|
61
59
|
var _a;
|
|
62
60
|
let maxSegmentRadius = maxRadius;
|
|
63
61
|
if (dataLabels.enabled) {
|
|
64
|
-
maxSegmentRadius -=
|
|
62
|
+
maxSegmentRadius -=
|
|
63
|
+
dataLabels.distance + dataLabels.connectorPadding + labelMaxHeight;
|
|
65
64
|
}
|
|
66
65
|
const segmentRadius = (_a = calculateNumericProperty({ value: item.radius, base: maxSegmentRadius })) !== null && _a !== void 0 ? _a : maxSegmentRadius;
|
|
67
66
|
return {
|
|
@@ -78,8 +77,40 @@ export function preparePieData(args) {
|
|
|
78
77
|
data.segments = pieGenerator(segments);
|
|
79
78
|
return data;
|
|
80
79
|
};
|
|
80
|
+
const getLabels = ({ series }) => {
|
|
81
|
+
const { dataLabels } = series[0];
|
|
82
|
+
if (!dataLabels.enabled) {
|
|
83
|
+
return {};
|
|
84
|
+
}
|
|
85
|
+
const getTextSize = getTextSizeFn({ style: dataLabelsStyle });
|
|
86
|
+
return series.reduce((acc, d) => {
|
|
87
|
+
const text = getFormattedValue(Object.assign({ value: d.data.label || d.data.value }, d.dataLabels));
|
|
88
|
+
let labelWidth = 0;
|
|
89
|
+
let labelHeight = 0;
|
|
90
|
+
if (dataLabels.html) {
|
|
91
|
+
const size = getLabelsSize({
|
|
92
|
+
labels: [text],
|
|
93
|
+
style: dataLabelsStyle,
|
|
94
|
+
html: true,
|
|
95
|
+
});
|
|
96
|
+
labelWidth = size.maxWidth;
|
|
97
|
+
labelHeight = size.maxHeight;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const size = getTextSize(text);
|
|
101
|
+
labelWidth = size.width;
|
|
102
|
+
labelHeight = size.height;
|
|
103
|
+
}
|
|
104
|
+
const label = {
|
|
105
|
+
text,
|
|
106
|
+
size: { width: labelWidth, height: labelHeight },
|
|
107
|
+
};
|
|
108
|
+
acc[d.id] = label;
|
|
109
|
+
return acc;
|
|
110
|
+
}, {});
|
|
111
|
+
};
|
|
81
112
|
const prepareLabels = (prepareLabelsArgs) => {
|
|
82
|
-
const { data, series, allowOverlow = true } = prepareLabelsArgs;
|
|
113
|
+
const { data, series, labels: labelsData, allowOverlow = true } = prepareLabelsArgs;
|
|
83
114
|
const { dataLabels } = series[0];
|
|
84
115
|
const labels = [];
|
|
85
116
|
const htmlLabels = [];
|
|
@@ -93,12 +124,7 @@ export function preparePieData(args) {
|
|
|
93
124
|
if (curveFactory) {
|
|
94
125
|
line = line.curve(curveFactory);
|
|
95
126
|
}
|
|
96
|
-
const {
|
|
97
|
-
const { maxHeight: labelHeight } = getLabelsSize({
|
|
98
|
-
labels: ['Some Label'],
|
|
99
|
-
style: dataLabelsStyle,
|
|
100
|
-
html: shouldUseHtml,
|
|
101
|
-
});
|
|
127
|
+
const { connectorPadding, distance } = dataLabels;
|
|
102
128
|
const connectorStartPointGenerator = arc()
|
|
103
129
|
.innerRadius((d) => d.data.radius)
|
|
104
130
|
.outerRadius((d) => d.data.radius);
|
|
@@ -114,14 +140,11 @@ export function preparePieData(args) {
|
|
|
114
140
|
let shouldStopLabelPlacement = false;
|
|
115
141
|
// eslint-disable-next-line complexity
|
|
116
142
|
series.forEach((d, index) => {
|
|
143
|
+
var _a, _b;
|
|
117
144
|
const prevLabel = labels[labels.length - 1];
|
|
118
|
-
const text =
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
style: dataLabelsStyle,
|
|
122
|
-
html: shouldUseHtml,
|
|
123
|
-
});
|
|
124
|
-
const labelWidth = labelSize.maxWidth;
|
|
145
|
+
const { text = '', size: labelSize } = labelsData[d.id];
|
|
146
|
+
const labelWidth = (_a = labelSize === null || labelSize === void 0 ? void 0 : labelSize.width) !== null && _a !== void 0 ? _a : 0;
|
|
147
|
+
const labelHeight = (_b = labelSize === null || labelSize === void 0 ? void 0 : labelSize.height) !== null && _b !== void 0 ? _b : 0;
|
|
125
148
|
const relatedSegment = data.segments[index];
|
|
126
149
|
/**
|
|
127
150
|
* Compute the label coordinates on the label arc for a given angle.
|
|
@@ -159,7 +182,7 @@ export function preparePieData(args) {
|
|
|
159
182
|
text,
|
|
160
183
|
x,
|
|
161
184
|
y,
|
|
162
|
-
style,
|
|
185
|
+
style: dataLabelsStyle,
|
|
163
186
|
size: { width: labelWidth, height: labelHeight },
|
|
164
187
|
maxWidth: labelWidth,
|
|
165
188
|
textAnchor: midAngle < Math.PI ? 'start' : 'end',
|
|
@@ -251,9 +274,11 @@ export function preparePieData(args) {
|
|
|
251
274
|
};
|
|
252
275
|
return Array.from(groupedPieSeries).map(([stackId, items]) => {
|
|
253
276
|
var _a;
|
|
254
|
-
const
|
|
277
|
+
const seriesLabels = getLabels({ series: items });
|
|
278
|
+
const data = prepareItem({ stackId, items, labels: seriesLabels });
|
|
255
279
|
const preparedLabels = prepareLabels({
|
|
256
280
|
data,
|
|
281
|
+
labels: seriesLabels,
|
|
257
282
|
series: items,
|
|
258
283
|
});
|
|
259
284
|
let maxLeftRightFreeSpace = Infinity;
|
|
@@ -329,6 +354,7 @@ export function preparePieData(args) {
|
|
|
329
354
|
const { labels, htmlLabels, connectors } = prepareLabels({
|
|
330
355
|
data,
|
|
331
356
|
series: items,
|
|
357
|
+
labels: seriesLabels,
|
|
332
358
|
allowOverlow: false,
|
|
333
359
|
});
|
|
334
360
|
if (typeof ((_a = items[0]) === null || _a === void 0 ? void 0 : _a.innerRadius) !== 'undefined') {
|
|
@@ -71,4 +71,10 @@ export interface ChartData<T = MeaningfulAny> {
|
|
|
71
71
|
* It can be used to visualize related information on multiple charts.
|
|
72
72
|
*/
|
|
73
73
|
split?: ChartSplit;
|
|
74
|
+
/** The color list of palette.
|
|
75
|
+
* If no color is set in series, the colors would be adopted sequentially and circularly from this list as the colors of series.
|
|
76
|
+
*
|
|
77
|
+
* @default ['#4DA2F1', '#FF3D64', '#8AD554', '#FFC636', '#FFB9DD', '#84D1EE', '#FF91A1', '#54A520', '#DB9100', '#BA74B3', '#1F68A9', '#ED65A9', '#0FA08D', '#FF7E00', '#E8B0A4', '#52A6C5', '#BE2443', '#70C1AF', '#FFB46C', '#DCA3D7']
|
|
78
|
+
* */
|
|
79
|
+
colors?: string[];
|
|
74
80
|
}
|
|
@@ -21,7 +21,7 @@ export function axisBottom(args) {
|
|
|
21
21
|
const position = getXTickPosition({ scale, offset });
|
|
22
22
|
const values = getXAxisItems({ scale, count: ticksCount, maxCount: maxTickCount });
|
|
23
23
|
const labelHeight = getLabelsSize({
|
|
24
|
-
labels: values,
|
|
24
|
+
labels: values.map(labelFormat),
|
|
25
25
|
style: labelsStyle,
|
|
26
26
|
}).maxHeight;
|
|
27
27
|
return function (selection) {
|
|
@@ -165,11 +165,27 @@ export function wrapText(args) {
|
|
|
165
165
|
return acc;
|
|
166
166
|
}, []);
|
|
167
167
|
}
|
|
168
|
+
const entityMap = {
|
|
169
|
+
'&': '&',
|
|
170
|
+
'<': '<',
|
|
171
|
+
'>': '>',
|
|
172
|
+
'"': '"',
|
|
173
|
+
"'": ''',
|
|
174
|
+
'/': '/',
|
|
175
|
+
'`': '`',
|
|
176
|
+
'=': '=',
|
|
177
|
+
};
|
|
178
|
+
function unescapeHtml(str) {
|
|
179
|
+
return Object.entries(entityMap).reduce((result, [key, value]) => {
|
|
180
|
+
return result.replace(value, key);
|
|
181
|
+
}, str);
|
|
182
|
+
}
|
|
168
183
|
export function getTextSizeFn({ style }) {
|
|
169
184
|
const map = {};
|
|
170
185
|
const setSymbolSize = (s) => {
|
|
186
|
+
const labels = [s === ' ' ? ' ' : s];
|
|
171
187
|
const size = getLabelsSize({
|
|
172
|
-
labels
|
|
188
|
+
labels,
|
|
173
189
|
style,
|
|
174
190
|
});
|
|
175
191
|
map[s] = { width: size.maxWidth, height: size.maxHeight };
|
|
@@ -177,7 +193,7 @@ export function getTextSizeFn({ style }) {
|
|
|
177
193
|
return (str) => {
|
|
178
194
|
let width = 0;
|
|
179
195
|
let height = 0;
|
|
180
|
-
[...str].forEach((s) => {
|
|
196
|
+
[...unescapeHtml(str)].forEach((s) => {
|
|
181
197
|
if (!map[s]) {
|
|
182
198
|
setSymbolSize(s);
|
|
183
199
|
}
|
|
@@ -187,14 +203,20 @@ export function getTextSizeFn({ style }) {
|
|
|
187
203
|
return { width, height };
|
|
188
204
|
};
|
|
189
205
|
}
|
|
206
|
+
// We ignore an inaccuracy of less than a pixel.
|
|
207
|
+
// To do this, we round the font size down when comparing it, and the size of the allowed space up.
|
|
190
208
|
export function getTextWithElipsis({ text: originalText, getTextWidth, maxWidth, }) {
|
|
191
|
-
let
|
|
192
|
-
|
|
193
|
-
|
|
209
|
+
let textWidth = Math.floor(getTextWidth(originalText));
|
|
210
|
+
const textMaxWidth = Math.ceil(maxWidth);
|
|
211
|
+
if (textWidth <= textMaxWidth) {
|
|
212
|
+
return originalText;
|
|
213
|
+
}
|
|
214
|
+
let text = originalText + '…';
|
|
215
|
+
while (textWidth > textMaxWidth && text.length > 2) {
|
|
194
216
|
text = text.slice(0, -2) + '…';
|
|
195
|
-
|
|
217
|
+
textWidth = Math.floor(getTextWidth(text));
|
|
196
218
|
}
|
|
197
|
-
if (
|
|
219
|
+
if (textWidth > textMaxWidth) {
|
|
198
220
|
text = '';
|
|
199
221
|
}
|
|
200
222
|
return text;
|