@gravity-ui/chartkit 5.14.1 → 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/Chart.js +5 -0
- package/build/plugins/d3/renderer/components/Legend.js +129 -66
- package/build/plugins/d3/renderer/components/styles.css +16 -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-pie.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 +24 -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.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +9 -9
- 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.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/pie/index.js +10 -14
- package/build/plugins/d3/renderer/hooks/useShapes/pie/prepare-data.js +30 -12
- package/build/plugins/d3/renderer/hooks/useShapes/pie/types.d.ts +7 -5
- 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 +8 -0
- 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.d.ts +2 -1
- package/build/plugins/d3/renderer/utils/text.js +32 -10
- package/build/types/widget-data/bar-x.d.ts +1 -1
- package/build/types/widget-data/base.d.ts +7 -0
- package/build/types/widget-data/legend.d.ts +24 -0
- package/package.json +2 -2
|
@@ -22,6 +22,7 @@ export const Chart = (props) => {
|
|
|
22
22
|
var _a, _b;
|
|
23
23
|
const { width, height, data } = props;
|
|
24
24
|
const svgRef = React.useRef(null);
|
|
25
|
+
const htmlLayerRef = React.useRef(null);
|
|
25
26
|
const dispatcher = React.useMemo(() => {
|
|
26
27
|
return getD3Dispatcher();
|
|
27
28
|
}, []);
|
|
@@ -71,6 +72,7 @@ export const Chart = (props) => {
|
|
|
71
72
|
yAxis,
|
|
72
73
|
yScale,
|
|
73
74
|
split: preparedSplit,
|
|
75
|
+
htmlLayout: htmlLayerRef.current,
|
|
74
76
|
});
|
|
75
77
|
const clickHandler = (_b = (_a = data.chart) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.click;
|
|
76
78
|
React.useEffect(() => {
|
|
@@ -136,5 +138,8 @@ export const Chart = (props) => {
|
|
|
136
138
|
React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale, split: preparedSplit })))),
|
|
137
139
|
shapes),
|
|
138
140
|
preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick }))),
|
|
141
|
+
React.createElement("div", { className: b('html-layer'), ref: htmlLayerRef, style: {
|
|
142
|
+
transform: `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
|
|
143
|
+
} }),
|
|
139
144
|
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: tooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0] })));
|
|
140
145
|
};
|
|
@@ -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
|
};
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
position: absolute;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
.chartkit-d3__html-layer {
|
|
6
|
+
display: contents;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.chartkit-d3__html-layer > * {
|
|
10
|
+
transform: inherit;
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
.chartkit-d3-axis .domain {
|
|
6
14
|
stroke: var(--g-color-line-generic-active);
|
|
7
15
|
}
|
|
@@ -24,6 +32,14 @@
|
|
|
24
32
|
alignment-baseline: after-edge;
|
|
25
33
|
}
|
|
26
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
|
+
|
|
27
43
|
.chartkit-d3-legend__item {
|
|
28
44
|
cursor: pointer;
|
|
29
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,
|
|
@@ -24,6 +24,7 @@ export function preparePieSeries(args) {
|
|
|
24
24
|
connectorShape: get(series, 'dataLabels.connectorShape', 'polyline'),
|
|
25
25
|
distance: get(series, 'dataLabels.distance', 25),
|
|
26
26
|
connectorCurve: get(series, 'dataLabels.connectorCurve', 'basic'),
|
|
27
|
+
html: get(series, 'dataLabels.html', false),
|
|
27
28
|
},
|
|
28
29
|
label: dataItem.label,
|
|
29
30
|
value: dataItem.value,
|
|
@@ -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 = {
|
|
@@ -126,6 +144,7 @@ export type PreparedPieSeries = {
|
|
|
126
144
|
connectorShape: ConnectorShape;
|
|
127
145
|
distance: number;
|
|
128
146
|
connectorCurve: ConnectorCurve;
|
|
147
|
+
html: boolean;
|
|
129
148
|
};
|
|
130
149
|
states: {
|
|
131
150
|
hover: {
|
|
@@ -144,6 +163,7 @@ export type PreparedLineSeries = {
|
|
|
144
163
|
style: BaseTextStyle;
|
|
145
164
|
padding: number;
|
|
146
165
|
allowOverlap: boolean;
|
|
166
|
+
html: boolean;
|
|
147
167
|
};
|
|
148
168
|
marker: {
|
|
149
169
|
states: {
|
|
@@ -180,6 +200,7 @@ export type PreparedAreaSeries = {
|
|
|
180
200
|
style: BaseTextStyle;
|
|
181
201
|
padding: number;
|
|
182
202
|
allowOverlap: boolean;
|
|
203
|
+
html: boolean;
|
|
183
204
|
};
|
|
184
205
|
marker: {
|
|
185
206
|
states: {
|
|
@@ -209,6 +230,7 @@ export type PreparedTreemapSeries = {
|
|
|
209
230
|
style: BaseTextStyle;
|
|
210
231
|
padding: number;
|
|
211
232
|
allowOverlap: boolean;
|
|
233
|
+
html: boolean;
|
|
212
234
|
};
|
|
213
235
|
layoutAlgorithm: `${LayoutAlgorithm}`;
|
|
214
236
|
} & BasePreparedSeries & Omit<TreemapSeries, keyof BasePreparedSeries>;
|
|
@@ -220,6 +242,7 @@ export type PreparedWaterfallSeries = {
|
|
|
220
242
|
style: BaseTextStyle;
|
|
221
243
|
allowOverlap: boolean;
|
|
222
244
|
padding: number;
|
|
245
|
+
html: boolean;
|
|
223
246
|
};
|
|
224
247
|
positiveColor: string;
|
|
225
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
|
};
|