@gravity-ui/chartkit 5.15.0 → 5.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/plugins/d3/renderer/components/Legend.js +129 -66
- package/build/plugins/d3/renderer/components/styles.css +8 -0
- package/build/plugins/d3/renderer/constants/defaults/legend.d.ts +12 -4
- package/build/plugins/d3/renderer/constants/defaults/legend.js +4 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-area.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-x.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-y.js +8 -6
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-legend.js +58 -11
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-line.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-treemap.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-waterfall.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +23 -1
- package/build/plugins/d3/renderer/hooks/useShapes/HtmlLayer.d.ts +8 -0
- package/build/plugins/d3/renderer/hooks/useShapes/HtmlLayer.js +22 -0
- package/build/plugins/d3/renderer/hooks/useShapes/area/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/area/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.js +18 -3
- package/build/plugins/d3/renderer/hooks/useShapes/area/types.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +21 -4
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/types.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.js +18 -23
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +44 -3
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/types.d.ts +3 -0
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +7 -7
- package/build/plugins/d3/renderer/hooks/useShapes/line/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/line/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.js +17 -1
- package/build/plugins/d3/renderer/hooks/useShapes/line/types.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/pie/index.js +2 -10
- package/build/plugins/d3/renderer/hooks/useShapes/pie/prepare-data.js +0 -1
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.js +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/types.d.ts +2 -0
- package/build/plugins/d3/renderer/hooks/useShapes/treemap/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/treemap/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/treemap/prepare-data.js +1 -1
- package/build/plugins/d3/renderer/hooks/useShapes/treemap/types.d.ts +2 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/prepare-data.js +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/types.d.ts +2 -1
- package/build/plugins/d3/renderer/types/index.d.ts +3 -1
- package/build/plugins/d3/renderer/utils/axis-generators/bottom.d.ts +5 -4
- package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +11 -7
- package/build/plugins/d3/renderer/utils/axis.d.ts +1 -1
- package/build/plugins/d3/renderer/utils/axis.js +1 -1
- package/build/plugins/d3/renderer/utils/color.d.ts +10 -0
- package/build/plugins/d3/renderer/utils/color.js +43 -0
- package/build/plugins/d3/renderer/utils/index.d.ts +2 -0
- package/build/plugins/d3/renderer/utils/index.js +2 -0
- package/build/plugins/d3/renderer/utils/legend.d.ts +8 -0
- package/build/plugins/d3/renderer/utils/legend.js +23 -0
- package/build/plugins/d3/renderer/utils/text.js +17 -10
- package/build/types/widget-data/bar-x.d.ts +1 -1
- package/build/types/widget-data/legend.d.ts +24 -0
- package/package.json +1 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { line as lineGenerator, select, symbol } from 'd3';
|
|
2
|
+
import { line as lineGenerator, scaleLinear, select, symbol } from 'd3';
|
|
3
3
|
import { block } from '../../../../utils/cn';
|
|
4
|
+
import { CONTINUOUS_LEGEND_SIZE } from '../constants';
|
|
4
5
|
import { getLineDashArray } from '../hooks/useShapes/utils';
|
|
5
|
-
import { getSymbol } from '../utils';
|
|
6
|
+
import { createGradientRect, getContinuesColorFn, getLabelsSize, getSymbol } from '../utils';
|
|
7
|
+
import { axisBottom } from '../utils/axis-generators';
|
|
6
8
|
const b = block('d3-legend');
|
|
7
9
|
const getLegendPosition = (args) => {
|
|
8
10
|
const { align, offsetWidth, width, contentWidth } = args;
|
|
@@ -138,78 +140,139 @@ export const Legend = (props) => {
|
|
|
138
140
|
setPaginationOffset(0);
|
|
139
141
|
}, [boundsWidth]);
|
|
140
142
|
React.useEffect(() => {
|
|
141
|
-
var _a;
|
|
143
|
+
var _a, _b, _c, _d, _e;
|
|
142
144
|
if (!ref.current) {
|
|
143
145
|
return;
|
|
144
146
|
}
|
|
145
147
|
const svgElement = select(ref.current);
|
|
146
148
|
svgElement.selectAll('*').remove();
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
149
|
+
let legendWidth = 0;
|
|
150
|
+
if (legend.type === 'discrete') {
|
|
151
|
+
const limit = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.limit;
|
|
152
|
+
const pageItems = typeof limit === 'number'
|
|
153
|
+
? items.slice(paginationOffset * limit, paginationOffset * limit + limit)
|
|
154
|
+
: items;
|
|
155
|
+
pageItems.forEach((line, lineIndex) => {
|
|
156
|
+
var _a;
|
|
157
|
+
const legendLine = svgElement.append('g').attr('class', b('line'));
|
|
158
|
+
const legendItemTemplate = legendLine
|
|
159
|
+
.selectAll('legend-history')
|
|
160
|
+
.data(line)
|
|
161
|
+
.enter()
|
|
162
|
+
.append('g')
|
|
163
|
+
.attr('class', b('item'))
|
|
164
|
+
.on('click', function (e, d) {
|
|
165
|
+
onItemClick({ name: d.name, metaKey: e.metaKey });
|
|
166
|
+
});
|
|
167
|
+
const getXPosition = (i) => {
|
|
168
|
+
return line.slice(0, i).reduce((acc, legendItem) => {
|
|
169
|
+
return (acc +
|
|
170
|
+
legendItem.symbol.width +
|
|
171
|
+
legendItem.symbol.padding +
|
|
172
|
+
legendItem.textWidth +
|
|
173
|
+
legend.itemDistance);
|
|
174
|
+
}, 0);
|
|
175
|
+
};
|
|
176
|
+
renderLegendSymbol({ selection: legendItemTemplate, legend });
|
|
177
|
+
legendItemTemplate
|
|
178
|
+
.append('text')
|
|
179
|
+
.attr('x', function (legendItem, i) {
|
|
180
|
+
return (getXPosition(i) + legendItem.symbol.width + legendItem.symbol.padding);
|
|
181
|
+
})
|
|
182
|
+
.attr('height', legend.lineHeight)
|
|
183
|
+
.attr('class', function (d) {
|
|
184
|
+
const mods = { selected: d.visible, unselected: !d.visible };
|
|
185
|
+
return b('item-text', mods);
|
|
186
|
+
})
|
|
187
|
+
.text(function (d) {
|
|
188
|
+
return ('name' in d && d.name);
|
|
189
|
+
})
|
|
190
|
+
.style('font-size', legend.itemStyle.fontSize);
|
|
191
|
+
const contentWidth = ((_a = legendLine.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) || 0;
|
|
192
|
+
const { left } = getLegendPosition({
|
|
193
|
+
align: legend.align,
|
|
194
|
+
width: boundsWidth,
|
|
195
|
+
offsetWidth: 0,
|
|
196
|
+
contentWidth,
|
|
197
|
+
});
|
|
198
|
+
const top = legend.lineHeight * lineIndex;
|
|
199
|
+
legendLine.attr('transform', `translate(${[left, top].join(',')})`);
|
|
162
200
|
});
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
contentWidth,
|
|
201
|
+
legendWidth = boundsWidth;
|
|
202
|
+
if (config.pagination) {
|
|
203
|
+
const transform = `translate(${[
|
|
204
|
+
0,
|
|
205
|
+
legend.lineHeight * config.pagination.limit + legend.lineHeight / 2,
|
|
206
|
+
].join(',')})`;
|
|
207
|
+
appendPaginator({
|
|
208
|
+
container: svgElement,
|
|
209
|
+
offset: paginationOffset,
|
|
210
|
+
maxPage: config.pagination.maxPage,
|
|
211
|
+
legend,
|
|
212
|
+
transform,
|
|
213
|
+
onArrowClick: setPaginationOffset,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
// gradient rect
|
|
219
|
+
const domain = (_b = legend.colorScale.domain) !== null && _b !== void 0 ? _b : [];
|
|
220
|
+
const rectHeight = CONTINUOUS_LEGEND_SIZE.height;
|
|
221
|
+
svgElement.call(createGradientRect, {
|
|
222
|
+
y: legend.title.height + legend.title.margin,
|
|
223
|
+
height: rectHeight,
|
|
224
|
+
width: legend.width,
|
|
225
|
+
interpolator: getContinuesColorFn({
|
|
226
|
+
values: [0, 1],
|
|
227
|
+
colors: legend.colorScale.colors,
|
|
228
|
+
stops: legend.colorScale.stops,
|
|
229
|
+
}),
|
|
193
230
|
});
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
legend,
|
|
209
|
-
transform,
|
|
210
|
-
onArrowClick: setPaginationOffset,
|
|
231
|
+
// ticks
|
|
232
|
+
const xAxisGenerator = axisBottom({
|
|
233
|
+
scale: scaleLinear(domain, [0, legend.width]),
|
|
234
|
+
ticks: {
|
|
235
|
+
items: [[0, -rectHeight]],
|
|
236
|
+
labelsMargin: legend.ticks.labelsMargin,
|
|
237
|
+
labelsLineHeight: legend.ticks.labelsLineHeight,
|
|
238
|
+
maxTickCount: 4,
|
|
239
|
+
tickColor: '#fff',
|
|
240
|
+
},
|
|
241
|
+
domain: {
|
|
242
|
+
size: legend.width,
|
|
243
|
+
color: 'transparent',
|
|
244
|
+
},
|
|
211
245
|
});
|
|
246
|
+
const tickTop = legend.title.height + legend.title.margin + rectHeight;
|
|
247
|
+
svgElement
|
|
248
|
+
.append('g')
|
|
249
|
+
.attr('transform', `translate(0, ${tickTop})`)
|
|
250
|
+
.call(xAxisGenerator);
|
|
251
|
+
legendWidth = legend.width;
|
|
212
252
|
}
|
|
253
|
+
if (legend.title.enable) {
|
|
254
|
+
const { maxWidth: labelWidth } = getLabelsSize({
|
|
255
|
+
labels: [legend.title.text],
|
|
256
|
+
style: legend.title.style,
|
|
257
|
+
});
|
|
258
|
+
svgElement
|
|
259
|
+
.append('g')
|
|
260
|
+
.attr('class', b('title'))
|
|
261
|
+
.append('text')
|
|
262
|
+
.attr('dx', legend.width / 2 - labelWidth / 2)
|
|
263
|
+
.attr('font-weight', (_c = legend.title.style.fontWeight) !== null && _c !== void 0 ? _c : null)
|
|
264
|
+
.attr('font-size', (_d = legend.title.style.fontSize) !== null && _d !== void 0 ? _d : null)
|
|
265
|
+
.attr('fill', (_e = legend.title.style.fontColor) !== null && _e !== void 0 ? _e : null)
|
|
266
|
+
.style('alignment-baseline', 'before-edge')
|
|
267
|
+
.text(legend.title.text);
|
|
268
|
+
}
|
|
269
|
+
const { left } = getLegendPosition({
|
|
270
|
+
align: legend.align,
|
|
271
|
+
width: boundsWidth,
|
|
272
|
+
offsetWidth: config.offset.left,
|
|
273
|
+
contentWidth: legendWidth,
|
|
274
|
+
});
|
|
275
|
+
svgElement.attr('transform', `translate(${[left, config.offset.top].join(',')})`);
|
|
213
276
|
}, [boundsWidth, chartSeries, onItemClick, legend, items, config, paginationOffset]);
|
|
214
|
-
return React.createElement("g", { ref: ref, width: boundsWidth, height: legend.height });
|
|
277
|
+
return React.createElement("g", { className: b(), ref: ref, width: boundsWidth, height: legend.height });
|
|
215
278
|
};
|
|
@@ -32,6 +32,14 @@
|
|
|
32
32
|
alignment-baseline: after-edge;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
.chartkit-d3-legend {
|
|
36
|
+
color: var(--g-color-text-secondary);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.chartkit-d3-legend__title {
|
|
40
|
+
fill: var(--g-color-text-secondary);
|
|
41
|
+
}
|
|
42
|
+
|
|
35
43
|
.chartkit-d3-legend__item {
|
|
36
44
|
cursor: pointer;
|
|
37
45
|
user-select: none;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export declare const legendDefaults: {
|
|
2
|
+
align: "left" | "center" | "right";
|
|
3
|
+
itemDistance: number;
|
|
4
|
+
margin: number;
|
|
5
|
+
itemStyle: {
|
|
6
|
+
fontSize: string;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export declare const CONTINUOUS_LEGEND_SIZE: {
|
|
10
|
+
height: number;
|
|
11
|
+
width: number;
|
|
12
|
+
};
|
|
@@ -52,6 +52,7 @@ export function prepareArea(args) {
|
|
|
52
52
|
style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_b = series.dataLabels) === null || _b === void 0 ? void 0 : _b.style),
|
|
53
53
|
padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING),
|
|
54
54
|
allowOverlap: get(series, 'dataLabels.allowOverlap', false),
|
|
55
|
+
html: get(series, 'dataLabels.html', false),
|
|
55
56
|
},
|
|
56
57
|
marker: prepareMarker(series, seriesOptions),
|
|
57
58
|
cursor: get(series, 'cursor', null),
|
|
@@ -29,6 +29,7 @@ export function prepareBarXSeries(args) {
|
|
|
29
29
|
style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_d = series.dataLabels) === null || _d === void 0 ? void 0 : _d.style),
|
|
30
30
|
allowOverlap: ((_e = series.dataLabels) === null || _e === void 0 ? void 0 : _e.allowOverlap) || false,
|
|
31
31
|
padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING),
|
|
32
|
+
html: get(series, 'dataLabels.html', false),
|
|
32
33
|
},
|
|
33
34
|
cursor: get(series, 'cursor', null),
|
|
34
35
|
yAxis: get(series, 'yAxis', 0),
|
|
@@ -7,12 +7,13 @@ function prepareDataLabels(series) {
|
|
|
7
7
|
var _a;
|
|
8
8
|
const enabled = get(series, 'dataLabels.enabled', false);
|
|
9
9
|
const style = Object.assign({}, DEFAULT_DATALABELS_STYLE, (_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.style);
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
const html = get(series, 'dataLabels.html', false);
|
|
11
|
+
const labels = enabled ? series.data.map((d) => String(d.label || d.x)) : [];
|
|
12
|
+
const { maxHeight = 0, maxWidth = 0 } = getLabelsSize({
|
|
13
|
+
labels,
|
|
14
|
+
style,
|
|
15
|
+
html,
|
|
16
|
+
});
|
|
16
17
|
const inside = series.stacking === 'percent' ? true : get(series, 'dataLabels.inside', false);
|
|
17
18
|
return {
|
|
18
19
|
enabled,
|
|
@@ -20,6 +21,7 @@ function prepareDataLabels(series) {
|
|
|
20
21
|
style,
|
|
21
22
|
maxHeight,
|
|
22
23
|
maxWidth,
|
|
24
|
+
html,
|
|
23
25
|
};
|
|
24
26
|
}
|
|
25
27
|
export function prepareBarYSeries(args) {
|
|
@@ -2,18 +2,52 @@ import { select } from 'd3';
|
|
|
2
2
|
import clone from 'lodash/clone';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import merge from 'lodash/merge';
|
|
5
|
-
import { legendDefaults } from '../../constants';
|
|
6
|
-
import { getHorisontalSvgTextHeight } from '../../utils';
|
|
5
|
+
import { CONTINUOUS_LEGEND_SIZE, legendDefaults } from '../../constants';
|
|
6
|
+
import { getDefaultColorStops, getDomainForContinuousColorScale, getHorisontalSvgTextHeight, getLabelsSize, } from '../../utils';
|
|
7
7
|
import { getBoundsWidth } from '../useChartDimensions';
|
|
8
8
|
import { getYAxisWidth } from '../useChartDimensions/utils';
|
|
9
9
|
export const getPreparedLegend = (args) => {
|
|
10
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
10
11
|
const { legend, series } = args;
|
|
11
12
|
const enabled = Boolean(typeof (legend === null || legend === void 0 ? void 0 : legend.enabled) === 'boolean' ? legend === null || legend === void 0 ? void 0 : legend.enabled : series.length > 1);
|
|
12
13
|
const defaultItemStyle = clone(legendDefaults.itemStyle);
|
|
13
14
|
const itemStyle = get(legend, 'itemStyle');
|
|
14
15
|
const computedItemStyle = merge(defaultItemStyle, itemStyle);
|
|
15
16
|
const lineHeight = getHorisontalSvgTextHeight({ text: 'Tmp', style: computedItemStyle });
|
|
16
|
-
const
|
|
17
|
+
const legendType = get(legend, 'type', 'discrete');
|
|
18
|
+
const isTitleEnabled = Boolean((_a = legend === null || legend === void 0 ? void 0 : legend.title) === null || _a === void 0 ? void 0 : _a.text);
|
|
19
|
+
const titleMargin = isTitleEnabled ? get(legend, 'title.margin', 4) : 0;
|
|
20
|
+
const titleStyle = Object.assign({ fontSize: '12px', fontWeight: 'bold' }, get(legend, 'title.style'));
|
|
21
|
+
const titleText = isTitleEnabled ? get(legend, 'title.text', '') : '';
|
|
22
|
+
const titleHeight = isTitleEnabled
|
|
23
|
+
? getLabelsSize({ labels: [titleText], style: titleStyle }).maxHeight
|
|
24
|
+
: 0;
|
|
25
|
+
const ticks = {
|
|
26
|
+
labelsMargin: 4,
|
|
27
|
+
labelsLineHeight: 12,
|
|
28
|
+
};
|
|
29
|
+
const colorScale = {
|
|
30
|
+
colors: [],
|
|
31
|
+
domain: [],
|
|
32
|
+
stops: [],
|
|
33
|
+
};
|
|
34
|
+
let height = 0;
|
|
35
|
+
if (enabled) {
|
|
36
|
+
height += titleHeight + titleMargin;
|
|
37
|
+
if (legendType === 'continuous') {
|
|
38
|
+
height += CONTINUOUS_LEGEND_SIZE.height;
|
|
39
|
+
height += ticks.labelsLineHeight + ticks.labelsMargin;
|
|
40
|
+
colorScale.colors = (_c = (_b = legend === null || legend === void 0 ? void 0 : legend.colorScale) === null || _b === void 0 ? void 0 : _b.colors) !== null && _c !== void 0 ? _c : [];
|
|
41
|
+
colorScale.stops =
|
|
42
|
+
(_e = (_d = legend === null || legend === void 0 ? void 0 : legend.colorScale) === null || _d === void 0 ? void 0 : _d.stops) !== null && _e !== void 0 ? _e : getDefaultColorStops(colorScale.colors.length);
|
|
43
|
+
colorScale.domain =
|
|
44
|
+
(_g = (_f = legend === null || legend === void 0 ? void 0 : legend.colorScale) === null || _f === void 0 ? void 0 : _f.domain) !== null && _g !== void 0 ? _g : getDomainForContinuousColorScale({ series });
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
height += lineHeight;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const legendWidth = get(legend, 'width', CONTINUOUS_LEGEND_SIZE.width);
|
|
17
51
|
return {
|
|
18
52
|
align: get(legend, 'align', legendDefaults.align),
|
|
19
53
|
enabled,
|
|
@@ -22,6 +56,17 @@ export const getPreparedLegend = (args) => {
|
|
|
22
56
|
itemStyle: computedItemStyle,
|
|
23
57
|
lineHeight,
|
|
24
58
|
margin: get(legend, 'margin', legendDefaults.margin),
|
|
59
|
+
type: legendType,
|
|
60
|
+
title: {
|
|
61
|
+
enable: isTitleEnabled,
|
|
62
|
+
text: titleText,
|
|
63
|
+
margin: titleMargin,
|
|
64
|
+
style: titleStyle,
|
|
65
|
+
height: titleHeight,
|
|
66
|
+
},
|
|
67
|
+
width: legendWidth,
|
|
68
|
+
ticks,
|
|
69
|
+
colorScale,
|
|
25
70
|
};
|
|
26
71
|
};
|
|
27
72
|
const getFlattenLegendItems = (series) => {
|
|
@@ -78,16 +123,18 @@ export const getLegendComponents = (args) => {
|
|
|
78
123
|
items: flattenLegendItems,
|
|
79
124
|
preparedLegend,
|
|
80
125
|
});
|
|
81
|
-
let legendHeight = preparedLegend.lineHeight * items.length;
|
|
82
126
|
let pagination;
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
127
|
+
if (preparedLegend.type === 'discrete') {
|
|
128
|
+
let legendHeight = preparedLegend.lineHeight * items.length;
|
|
129
|
+
if (maxLegendHeight < legendHeight) {
|
|
130
|
+
// extra line for paginator
|
|
131
|
+
const limit = Math.floor(maxLegendHeight / preparedLegend.lineHeight) - 1;
|
|
132
|
+
const maxPage = Math.ceil(items.length / limit);
|
|
133
|
+
pagination = { limit, maxPage };
|
|
134
|
+
legendHeight = maxLegendHeight;
|
|
135
|
+
}
|
|
136
|
+
preparedLegend.height = legendHeight;
|
|
89
137
|
}
|
|
90
|
-
preparedLegend.height = legendHeight;
|
|
91
138
|
const top = chartHeight - chartMargin.bottom - preparedLegend.height;
|
|
92
139
|
const offset = {
|
|
93
140
|
left: chartMargin.left + getYAxisWidth(preparedYAxis[0]),
|
|
@@ -68,6 +68,7 @@ export function prepareLineSeries(args) {
|
|
|
68
68
|
style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_b = series.dataLabels) === null || _b === void 0 ? void 0 : _b.style),
|
|
69
69
|
padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING),
|
|
70
70
|
allowOverlap: get(series, 'dataLabels.allowOverlap', false),
|
|
71
|
+
html: get(series, 'dataLabels.html', false),
|
|
71
72
|
},
|
|
72
73
|
marker: prepareMarker(series, seriesOptions),
|
|
73
74
|
dashStyle: dashStyle,
|
|
@@ -18,6 +18,7 @@ export function prepareTreemap(args) {
|
|
|
18
18
|
style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_a = s.dataLabels) === null || _a === void 0 ? void 0 : _a.style),
|
|
19
19
|
padding: get(s, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING),
|
|
20
20
|
allowOverlap: get(s, 'dataLabels.allowOverlap', false),
|
|
21
|
+
html: get(series, 'dataLabels.html', false),
|
|
21
22
|
},
|
|
22
23
|
id,
|
|
23
24
|
type: s.type,
|
|
@@ -28,6 +28,7 @@ export function prepareWaterfallSeries(args) {
|
|
|
28
28
|
style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_b = series.dataLabels) === null || _b === void 0 ? void 0 : _b.style),
|
|
29
29
|
allowOverlap: ((_c = series.dataLabels) === null || _c === void 0 ? void 0 : _c.allowOverlap) || false,
|
|
30
30
|
padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING),
|
|
31
|
+
html: get(series, 'dataLabels.html', false),
|
|
31
32
|
},
|
|
32
33
|
cursor: get(series, 'cursor', null),
|
|
33
34
|
};
|
|
@@ -13,9 +13,25 @@ export type SymbolLegendSymbol = {
|
|
|
13
13
|
symbolType: `${SymbolType}`;
|
|
14
14
|
} & Required<SymbolLegendSymbolOptions>;
|
|
15
15
|
export type PreparedLegendSymbol = RectLegendSymbol | PathLegendSymbol | SymbolLegendSymbol;
|
|
16
|
-
export type PreparedLegend = Required<ChartKitWidgetLegend
|
|
16
|
+
export type PreparedLegend = Required<Omit<ChartKitWidgetLegend, 'title' | 'colorScale'>> & {
|
|
17
17
|
height: number;
|
|
18
18
|
lineHeight: number;
|
|
19
|
+
title: {
|
|
20
|
+
enable: boolean;
|
|
21
|
+
text: string;
|
|
22
|
+
margin: number;
|
|
23
|
+
style: BaseTextStyle;
|
|
24
|
+
height: number;
|
|
25
|
+
};
|
|
26
|
+
ticks: {
|
|
27
|
+
labelsMargin: number;
|
|
28
|
+
labelsLineHeight: number;
|
|
29
|
+
};
|
|
30
|
+
colorScale: {
|
|
31
|
+
colors: string[];
|
|
32
|
+
domain: number[];
|
|
33
|
+
stops: number[];
|
|
34
|
+
};
|
|
19
35
|
};
|
|
20
36
|
export type OnLegendItemClick = (data: {
|
|
21
37
|
name: string;
|
|
@@ -89,6 +105,7 @@ export type PreparedBarXSeries = {
|
|
|
89
105
|
style: BaseTextStyle;
|
|
90
106
|
allowOverlap: boolean;
|
|
91
107
|
padding: number;
|
|
108
|
+
html: boolean;
|
|
92
109
|
};
|
|
93
110
|
yAxis: number;
|
|
94
111
|
} & BasePreparedSeries;
|
|
@@ -103,6 +120,7 @@ export type PreparedBarYSeries = {
|
|
|
103
120
|
style: BaseTextStyle;
|
|
104
121
|
maxHeight: number;
|
|
105
122
|
maxWidth: number;
|
|
123
|
+
html: boolean;
|
|
106
124
|
};
|
|
107
125
|
} & BasePreparedSeries;
|
|
108
126
|
export type PreparedPieSeries = {
|
|
@@ -145,6 +163,7 @@ export type PreparedLineSeries = {
|
|
|
145
163
|
style: BaseTextStyle;
|
|
146
164
|
padding: number;
|
|
147
165
|
allowOverlap: boolean;
|
|
166
|
+
html: boolean;
|
|
148
167
|
};
|
|
149
168
|
marker: {
|
|
150
169
|
states: {
|
|
@@ -181,6 +200,7 @@ export type PreparedAreaSeries = {
|
|
|
181
200
|
style: BaseTextStyle;
|
|
182
201
|
padding: number;
|
|
183
202
|
allowOverlap: boolean;
|
|
203
|
+
html: boolean;
|
|
184
204
|
};
|
|
185
205
|
marker: {
|
|
186
206
|
states: {
|
|
@@ -210,6 +230,7 @@ export type PreparedTreemapSeries = {
|
|
|
210
230
|
style: BaseTextStyle;
|
|
211
231
|
padding: number;
|
|
212
232
|
allowOverlap: boolean;
|
|
233
|
+
html: boolean;
|
|
213
234
|
};
|
|
214
235
|
layoutAlgorithm: `${LayoutAlgorithm}`;
|
|
215
236
|
} & BasePreparedSeries & Omit<TreemapSeries, keyof BasePreparedSeries>;
|
|
@@ -221,6 +242,7 @@ export type PreparedWaterfallSeries = {
|
|
|
221
242
|
style: BaseTextStyle;
|
|
222
243
|
allowOverlap: boolean;
|
|
223
244
|
padding: number;
|
|
245
|
+
html: boolean;
|
|
224
246
|
};
|
|
225
247
|
positiveColor: string;
|
|
226
248
|
negativeColor: string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ShapeDataWithHtmlItems } from '../../types';
|
|
3
|
+
type Props = {
|
|
4
|
+
htmlLayout: HTMLElement | null;
|
|
5
|
+
preparedData: ShapeDataWithHtmlItems | ShapeDataWithHtmlItems[];
|
|
6
|
+
};
|
|
7
|
+
export declare const HtmlLayer: (props: Props) => React.JSX.Element | null;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Portal } from '@gravity-ui/uikit';
|
|
3
|
+
export const HtmlLayer = (props) => {
|
|
4
|
+
const { htmlLayout, preparedData } = props;
|
|
5
|
+
const items = React.useMemo(() => {
|
|
6
|
+
if (Array.isArray(preparedData)) {
|
|
7
|
+
return preparedData.reduce((result, d) => {
|
|
8
|
+
result.push(...d.htmlElements);
|
|
9
|
+
return result;
|
|
10
|
+
}, []);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
return preparedData.htmlElements;
|
|
14
|
+
}
|
|
15
|
+
}, [preparedData]);
|
|
16
|
+
if (!htmlLayout) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return (React.createElement(Portal, { container: htmlLayout }, items.map((item, index) => {
|
|
20
|
+
return (React.createElement("div", { key: index, dangerouslySetInnerHTML: { __html: item.content }, style: { position: 'absolute', left: item.x, top: item.y } }));
|
|
21
|
+
})));
|
|
22
|
+
};
|
|
@@ -3,11 +3,12 @@ import { area as areaGenerator, color, line as lineGenerator, select } from 'd3'
|
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { block } from '../../../../../../utils/cn';
|
|
5
5
|
import { filterOverlappingLabels } from '../../../utils';
|
|
6
|
+
import { HtmlLayer } from '../HtmlLayer';
|
|
6
7
|
import { getMarkerHaloVisibility, getMarkerVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
|
|
7
8
|
import { setActiveState } from '../utils';
|
|
8
9
|
const b = block('d3-area');
|
|
9
10
|
export const AreaSeriesShapes = (args) => {
|
|
10
|
-
const { dispatcher, preparedData, seriesOptions } = args;
|
|
11
|
+
const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
|
|
11
12
|
const ref = React.useRef(null);
|
|
12
13
|
React.useEffect(() => {
|
|
13
14
|
var _a;
|
|
@@ -137,5 +138,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
137
138
|
dispatcher.on('hover-shape.area', null);
|
|
138
139
|
};
|
|
139
140
|
}, [dispatcher, preparedData, seriesOptions]);
|
|
140
|
-
return React.createElement(
|
|
141
|
+
return (React.createElement(React.Fragment, null,
|
|
142
|
+
React.createElement("g", { ref: ref, className: b() }),
|
|
143
|
+
React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
|
|
141
144
|
};
|
|
@@ -4,7 +4,7 @@ import { getXValue, getYValue } from '../utils';
|
|
|
4
4
|
function getLabelData(point, series, xMax) {
|
|
5
5
|
const text = String(point.data.label || point.data.y);
|
|
6
6
|
const style = series.dataLabels.style;
|
|
7
|
-
const size = getLabelsSize({ labels: [text], style });
|
|
7
|
+
const size = getLabelsSize({ labels: [text], style, html: series.dataLabels.html });
|
|
8
8
|
const labelData = {
|
|
9
9
|
text,
|
|
10
10
|
x: point.x,
|
|
@@ -22,7 +22,7 @@ function getLabelData(point, series, xMax) {
|
|
|
22
22
|
else {
|
|
23
23
|
const right = left + labelData.size.width;
|
|
24
24
|
if (right > xMax) {
|
|
25
|
-
labelData.x = labelData.x -
|
|
25
|
+
labelData.x = labelData.x - (right - xMax);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
return labelData;
|
|
@@ -97,8 +97,22 @@ export const prepareAreaData = (args) => {
|
|
|
97
97
|
return pointsAcc;
|
|
98
98
|
}, []);
|
|
99
99
|
let labels = [];
|
|
100
|
+
const htmlElements = [];
|
|
100
101
|
if (s.dataLabels.enabled) {
|
|
101
|
-
|
|
102
|
+
const labelItems = points.map((p) => getLabelData(p, s, xMax));
|
|
103
|
+
if (s.dataLabels.html) {
|
|
104
|
+
const htmlLabels = labelItems.map((l) => {
|
|
105
|
+
return {
|
|
106
|
+
x: l.x - l.size.width / 2,
|
|
107
|
+
y: l.y,
|
|
108
|
+
content: l.text,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
htmlElements.push(...htmlLabels);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
labels = labelItems;
|
|
115
|
+
}
|
|
102
116
|
}
|
|
103
117
|
let markers = [];
|
|
104
118
|
if (s.marker.states.normal.enabled || s.marker.states.hover.enabled) {
|
|
@@ -119,6 +133,7 @@ export const prepareAreaData = (args) => {
|
|
|
119
133
|
hovered: false,
|
|
120
134
|
active: true,
|
|
121
135
|
id: s.id,
|
|
136
|
+
htmlElements,
|
|
122
137
|
});
|
|
123
138
|
return acc;
|
|
124
139
|
}, []);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AreaSeriesData } from '../../../../../../types';
|
|
2
|
-
import { LabelData } from '../../../types';
|
|
2
|
+
import { HtmlItem, LabelData } from '../../../types';
|
|
3
3
|
import { PreparedAreaSeries } from '../../useSeries/types';
|
|
4
4
|
export type PointData = {
|
|
5
5
|
y0: number;
|
|
@@ -24,4 +24,5 @@ export type PreparedAreaData = {
|
|
|
24
24
|
hovered: boolean;
|
|
25
25
|
active: boolean;
|
|
26
26
|
labels: LabelData[];
|
|
27
|
+
htmlElements: HtmlItem[];
|
|
27
28
|
};
|