@gravity-ui/charts 1.25.0 → 1.26.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.d.ts +2 -2
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +34 -15
- package/dist/cjs/components/Legend/index.js +47 -18
- package/dist/cjs/hooks/useChartDimensions/index.js +16 -1
- package/dist/cjs/hooks/useSeries/prepare-legend.js +69 -11
- package/dist/cjs/types/chart/legend.d.ts +1 -1
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +2 -2
- package/dist/esm/components/ChartInner/useChartInnerProps.js +34 -15
- package/dist/esm/components/Legend/index.js +47 -18
- package/dist/esm/hooks/useChartDimensions/index.js +16 -1
- package/dist/esm/hooks/useSeries/prepare-legend.js +69 -11
- package/dist/esm/types/chart/legend.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Dispatch } from 'd3';
|
|
3
|
-
import type { RangeSliderState, ZoomState } from '../../hooks';
|
|
3
|
+
import type { PreparedLegend, RangeSliderState, ZoomState } from '../../hooks';
|
|
4
4
|
import type { ChartInnerProps } from './types';
|
|
5
5
|
type Props = ChartInnerProps & {
|
|
6
6
|
clipPathId: string;
|
|
@@ -35,7 +35,7 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
35
35
|
} | undefined;
|
|
36
36
|
legendItems: never[] | import("../../hooks").LegendItem[][];
|
|
37
37
|
preparedChart: import("../../hooks").PreparedChart;
|
|
38
|
-
preparedLegend:
|
|
38
|
+
preparedLegend: PreparedLegend | null;
|
|
39
39
|
preparedSeries: import("../../hooks").PreparedSeries[];
|
|
40
40
|
preparedSeriesOptions: import("../../constants").SeriesOptionsDefaults;
|
|
41
41
|
preparedSplit: import("../../hooks").PreparedSplit;
|
|
@@ -9,6 +9,30 @@ import { hasAtLeastOneSeriesDataPerPlot } from './utils';
|
|
|
9
9
|
const CLIP_PATH_BY_SERIES_TYPE = {
|
|
10
10
|
[SERIES_TYPE.Scatter]: false,
|
|
11
11
|
};
|
|
12
|
+
function getBoundsOffsetTop(args) {
|
|
13
|
+
const { chartMarginTop, preparedLegend } = args;
|
|
14
|
+
return (chartMarginTop +
|
|
15
|
+
((preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && preparedLegend.position === 'top'
|
|
16
|
+
? preparedLegend.height + preparedLegend.margin
|
|
17
|
+
: 0));
|
|
18
|
+
}
|
|
19
|
+
function getBoundsOffsetLeft(args) {
|
|
20
|
+
const { chartMarginLeft, preparedLegend, yAxis, getYAxisWidth: getAxisWidth } = args;
|
|
21
|
+
const legendOffset = (preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && preparedLegend.position === 'left'
|
|
22
|
+
? preparedLegend.width + preparedLegend.margin
|
|
23
|
+
: 0;
|
|
24
|
+
const leftAxisWidth = yAxis.reduce((acc, axis) => {
|
|
25
|
+
if (axis.position !== 'left') {
|
|
26
|
+
return acc;
|
|
27
|
+
}
|
|
28
|
+
const axisWidth = getAxisWidth(axis);
|
|
29
|
+
if (acc < axisWidth) {
|
|
30
|
+
acc = axisWidth;
|
|
31
|
+
}
|
|
32
|
+
return acc;
|
|
33
|
+
}, 0);
|
|
34
|
+
return chartMarginLeft + legendOffset + leftAxisWidth;
|
|
35
|
+
}
|
|
12
36
|
export function useChartInnerProps(props) {
|
|
13
37
|
var _a;
|
|
14
38
|
const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, rangeSliderState, svgContainer, width, updateZoomState, zoomState, } = props;
|
|
@@ -129,22 +153,17 @@ export function useChartInnerProps(props) {
|
|
|
129
153
|
yAxis,
|
|
130
154
|
yScale,
|
|
131
155
|
});
|
|
132
|
-
const boundsOffsetTop =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
156
|
+
const boundsOffsetTop = getBoundsOffsetTop({
|
|
157
|
+
chartMarginTop: chart.margin.top,
|
|
158
|
+
preparedLegend,
|
|
159
|
+
});
|
|
136
160
|
// We need to calculate the width of each left axis because the first axis can be hidden
|
|
137
|
-
const boundsOffsetLeft =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (acc < axisWidth) {
|
|
144
|
-
acc = axisWidth;
|
|
145
|
-
}
|
|
146
|
-
return acc;
|
|
147
|
-
}, 0);
|
|
161
|
+
const boundsOffsetLeft = getBoundsOffsetLeft({
|
|
162
|
+
chartMarginLeft: chart.margin.left,
|
|
163
|
+
preparedLegend,
|
|
164
|
+
yAxis,
|
|
165
|
+
getYAxisWidth,
|
|
166
|
+
});
|
|
148
167
|
const { x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
|
|
149
168
|
return {
|
|
150
169
|
allPreparedSeries,
|
|
@@ -6,16 +6,19 @@ import { block, createGradientRect, getContinuesColorFn, getLabelsSize, getLineD
|
|
|
6
6
|
import { axisBottom } from '../../utils/chart/axis-generators';
|
|
7
7
|
import './styles.css';
|
|
8
8
|
const b = block('legend');
|
|
9
|
-
const
|
|
10
|
-
const { align,
|
|
11
|
-
const top = 0;
|
|
12
|
-
if (align === 'left') {
|
|
13
|
-
return { top, left: offsetLeft };
|
|
14
|
-
}
|
|
9
|
+
const getLegendItemLeftPosition = (args) => {
|
|
10
|
+
const { align, width, contentWidth } = args;
|
|
15
11
|
if (align === 'right') {
|
|
16
|
-
return
|
|
12
|
+
return width - contentWidth;
|
|
13
|
+
}
|
|
14
|
+
if (align === 'left') {
|
|
15
|
+
return 0;
|
|
17
16
|
}
|
|
18
|
-
return
|
|
17
|
+
return width / 2 - contentWidth / 2;
|
|
18
|
+
};
|
|
19
|
+
const getLegendPosition = (args) => {
|
|
20
|
+
const { offsetLeft, offsetTop, contentWidth, width } = args;
|
|
21
|
+
return { top: offsetTop, left: offsetLeft + width / 2 - contentWidth / 2 };
|
|
19
22
|
};
|
|
20
23
|
const appendPaginator = (args) => {
|
|
21
24
|
const { container, pageIndex, legend, transform, pages, onArrowClick } = args;
|
|
@@ -157,6 +160,8 @@ export const Legend = (props) => {
|
|
|
157
160
|
? htmlElement.append('div').attr('data-legend', 1).style('position', 'absolute')
|
|
158
161
|
: null;
|
|
159
162
|
let legendWidth = 0;
|
|
163
|
+
let legendLeft = 0;
|
|
164
|
+
let legendTop = 0;
|
|
160
165
|
if (legend.type === 'discrete') {
|
|
161
166
|
const start = (_b = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.pages[pageIndex]) === null || _b === void 0 ? void 0 : _b.start;
|
|
162
167
|
const end = (_e = (_c = config.pagination) === null || _c === void 0 ? void 0 : _c.pages[pageIndex]) === null || _e === void 0 ? void 0 : _e.end;
|
|
@@ -249,13 +254,11 @@ export const Legend = (props) => {
|
|
|
249
254
|
let left = 0;
|
|
250
255
|
switch (legend.justifyContent) {
|
|
251
256
|
case 'center': {
|
|
252
|
-
|
|
257
|
+
left = getLegendItemLeftPosition({
|
|
253
258
|
align: legend.align,
|
|
254
259
|
width: config.maxWidth,
|
|
255
260
|
contentWidth,
|
|
256
|
-
offsetLeft: config.offset.left,
|
|
257
261
|
});
|
|
258
|
-
left = legendLinePostion.left;
|
|
259
262
|
legendWidth = config.maxWidth;
|
|
260
263
|
break;
|
|
261
264
|
}
|
|
@@ -280,8 +283,39 @@ export const Legend = (props) => {
|
|
|
280
283
|
onArrowClick: setPageIndex,
|
|
281
284
|
});
|
|
282
285
|
}
|
|
286
|
+
const { left, top } = getLegendPosition({
|
|
287
|
+
width: config.maxWidth,
|
|
288
|
+
contentWidth: legendWidth,
|
|
289
|
+
offsetLeft: config.offset.left,
|
|
290
|
+
offsetTop: config.offset.top,
|
|
291
|
+
});
|
|
292
|
+
legendLeft = left;
|
|
293
|
+
legendTop = top;
|
|
283
294
|
}
|
|
284
295
|
else {
|
|
296
|
+
let left = 0;
|
|
297
|
+
switch (legend.align) {
|
|
298
|
+
case 'right': {
|
|
299
|
+
left = config.offset.left + config.maxWidth - legend.width;
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
case 'left': {
|
|
303
|
+
left = config.offset.left;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
case 'center': {
|
|
307
|
+
left = config.offset.left + config.maxWidth / 2 - legend.width / 2;
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const { top } = getLegendPosition({
|
|
312
|
+
width: config.maxWidth,
|
|
313
|
+
contentWidth: legendWidth,
|
|
314
|
+
offsetLeft: config.offset.left,
|
|
315
|
+
offsetTop: config.offset.top,
|
|
316
|
+
});
|
|
317
|
+
legendLeft = left;
|
|
318
|
+
legendTop = top;
|
|
285
319
|
// gradient rect
|
|
286
320
|
const domain = (_f = legend.colorScale.domain) !== null && _f !== void 0 ? _f : [];
|
|
287
321
|
const rectHeight = CONTINUOUS_LEGEND_SIZE.height;
|
|
@@ -361,15 +395,10 @@ export const Legend = (props) => {
|
|
|
361
395
|
else {
|
|
362
396
|
svgElement.selectAll(`.${legendTitleClassname}`).remove();
|
|
363
397
|
}
|
|
364
|
-
const { left } = getLegendPosition({
|
|
365
|
-
align: legend.align,
|
|
366
|
-
width: config.maxWidth,
|
|
367
|
-
contentWidth: legendWidth,
|
|
368
|
-
});
|
|
369
398
|
svgElement
|
|
370
|
-
.attr('transform', `translate(${[
|
|
399
|
+
.attr('transform', `translate(${[legendLeft, legendTop].join(',')})`)
|
|
371
400
|
.style('opacity', 1);
|
|
372
|
-
htmlContainer === null || htmlContainer === void 0 ? void 0 : htmlContainer.style('transform', `translate(${
|
|
401
|
+
htmlContainer === null || htmlContainer === void 0 ? void 0 : htmlContainer.style('transform', `translate(${legendLeft}px, ${legendTop}px)`);
|
|
373
402
|
}
|
|
374
403
|
prepareLegend();
|
|
375
404
|
}, [chartSeries, onItemClick, onUpdate, legend, items, config, pageIndex, htmlLayout]);
|
|
@@ -30,6 +30,18 @@ const getTopOffset = ({ preparedLegend }) => {
|
|
|
30
30
|
}
|
|
31
31
|
return 0;
|
|
32
32
|
};
|
|
33
|
+
const getRightOffset = ({ preparedLegend }) => {
|
|
34
|
+
if ((preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && preparedLegend.position === 'right') {
|
|
35
|
+
return preparedLegend.width + preparedLegend.margin;
|
|
36
|
+
}
|
|
37
|
+
return 0;
|
|
38
|
+
};
|
|
39
|
+
const getLeftOffset = ({ preparedLegend }) => {
|
|
40
|
+
if ((preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && preparedLegend.position === 'left') {
|
|
41
|
+
return preparedLegend.width + preparedLegend.margin;
|
|
42
|
+
}
|
|
43
|
+
return 0;
|
|
44
|
+
};
|
|
33
45
|
export const useChartDimensions = (args) => {
|
|
34
46
|
const { height, margin, preparedLegend, preparedSeries, preparedXAxis, preparedYAxis, width } = args;
|
|
35
47
|
return React.useMemo(() => {
|
|
@@ -41,7 +53,10 @@ export const useChartDimensions = (args) => {
|
|
|
41
53
|
preparedXAxis,
|
|
42
54
|
});
|
|
43
55
|
const topOffset = getTopOffset({ preparedLegend });
|
|
56
|
+
const rightOffset = getRightOffset({ preparedLegend });
|
|
57
|
+
const leftOffset = getLeftOffset({ preparedLegend });
|
|
44
58
|
const boundsHeight = height - margin.top - margin.bottom - bottomOffset - topOffset;
|
|
45
|
-
|
|
59
|
+
const adjustedBoundsWidth = boundsWidth - rightOffset - leftOffset;
|
|
60
|
+
return { boundsWidth: adjustedBoundsWidth, boundsHeight };
|
|
46
61
|
}, [height, margin, preparedLegend, preparedSeries, preparedXAxis, preparedYAxis, width]);
|
|
47
62
|
};
|
|
@@ -12,7 +12,10 @@ export async function getPreparedLegend(args) {
|
|
|
12
12
|
const defaultItemStyle = clone(legendDefaults.itemStyle);
|
|
13
13
|
const itemStyle = get(legend, 'itemStyle');
|
|
14
14
|
const computedItemStyle = merge(defaultItemStyle, itemStyle);
|
|
15
|
-
const
|
|
15
|
+
const { maxHeight: lineHeight, maxWidth: lineWidth } = await getLabelsSize({
|
|
16
|
+
labels: ['Tmp'],
|
|
17
|
+
style: computedItemStyle,
|
|
18
|
+
});
|
|
16
19
|
const legendType = get(legend, 'type', 'discrete');
|
|
17
20
|
const isTitleEnabled = Boolean((_a = legend === null || legend === void 0 ? void 0 : legend.title) === null || _a === void 0 ? void 0 : _a.text);
|
|
18
21
|
const titleMargin = isTitleEnabled ? get(legend, 'title.margin', 4) : 0;
|
|
@@ -34,9 +37,11 @@ export async function getPreparedLegend(args) {
|
|
|
34
37
|
stops: [],
|
|
35
38
|
};
|
|
36
39
|
let height = 0;
|
|
40
|
+
let legendWidth = 0;
|
|
37
41
|
if (enabled) {
|
|
38
42
|
height += titleHeight + titleMargin;
|
|
39
43
|
if (legendType === 'continuous') {
|
|
44
|
+
legendWidth = get(legend, 'width', CONTINUOUS_LEGEND_SIZE.width);
|
|
40
45
|
height += CONTINUOUS_LEGEND_SIZE.height;
|
|
41
46
|
height += ticks.labelsLineHeight + ticks.labelsMargin;
|
|
42
47
|
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 : [];
|
|
@@ -47,9 +52,9 @@ export async function getPreparedLegend(args) {
|
|
|
47
52
|
}
|
|
48
53
|
else {
|
|
49
54
|
height += lineHeight;
|
|
55
|
+
legendWidth = get(legend, 'width', lineWidth);
|
|
50
56
|
}
|
|
51
57
|
}
|
|
52
|
-
const legendWidth = get(legend, 'width', CONTINUOUS_LEGEND_SIZE.width);
|
|
53
58
|
return {
|
|
54
59
|
align: get(legend, 'align', legendDefaults.align),
|
|
55
60
|
justifyContent: get(legend, 'justifyContent', legendDefaults.justifyContent),
|
|
@@ -168,10 +173,61 @@ function getPagination(args) {
|
|
|
168
173
|
});
|
|
169
174
|
return { pages };
|
|
170
175
|
}
|
|
176
|
+
function getLegendOffset(args) {
|
|
177
|
+
const { position, chartWidth, chartHeight, chartMargin, legendWidth, legendHeight } = args;
|
|
178
|
+
switch (position) {
|
|
179
|
+
case 'top':
|
|
180
|
+
return {
|
|
181
|
+
top: chartMargin.top,
|
|
182
|
+
left: chartMargin.left,
|
|
183
|
+
};
|
|
184
|
+
case 'right':
|
|
185
|
+
return {
|
|
186
|
+
top: chartMargin.top,
|
|
187
|
+
left: chartWidth - chartMargin.right - legendWidth,
|
|
188
|
+
};
|
|
189
|
+
case 'left':
|
|
190
|
+
return {
|
|
191
|
+
top: chartMargin.top,
|
|
192
|
+
left: chartMargin.left,
|
|
193
|
+
};
|
|
194
|
+
case 'bottom':
|
|
195
|
+
default:
|
|
196
|
+
return {
|
|
197
|
+
top: chartHeight - chartMargin.bottom - legendHeight,
|
|
198
|
+
left: chartMargin.left,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function getMaxLegendWidth(args) {
|
|
203
|
+
const { chartWidth, chartMargin, preparedLegend, isVerticalPosition } = args;
|
|
204
|
+
if (isVerticalPosition) {
|
|
205
|
+
return (chartWidth - chartMargin.right - chartMargin.left - preparedLegend.margin) / 2;
|
|
206
|
+
}
|
|
207
|
+
return chartWidth - chartMargin.right - chartMargin.left;
|
|
208
|
+
}
|
|
209
|
+
function getMaxLegendHeight(args) {
|
|
210
|
+
const { chartHeight, chartMargin, preparedLegend, isVerticalPosition } = args;
|
|
211
|
+
if (isVerticalPosition) {
|
|
212
|
+
return chartHeight - chartMargin.top - chartMargin.bottom;
|
|
213
|
+
}
|
|
214
|
+
return (chartHeight - chartMargin.top - chartMargin.bottom - preparedLegend.margin) / 2;
|
|
215
|
+
}
|
|
171
216
|
export function getLegendComponents(args) {
|
|
172
217
|
const { chartWidth, chartHeight, chartMargin, series, preparedLegend } = args;
|
|
173
|
-
const
|
|
174
|
-
const
|
|
218
|
+
const isVerticalPosition = preparedLegend.position === 'right' || preparedLegend.position === 'left';
|
|
219
|
+
const maxLegendWidth = getMaxLegendWidth({
|
|
220
|
+
chartWidth,
|
|
221
|
+
chartMargin,
|
|
222
|
+
preparedLegend,
|
|
223
|
+
isVerticalPosition,
|
|
224
|
+
});
|
|
225
|
+
const maxLegendHeight = getMaxLegendHeight({
|
|
226
|
+
chartHeight,
|
|
227
|
+
chartMargin,
|
|
228
|
+
preparedLegend,
|
|
229
|
+
isVerticalPosition,
|
|
230
|
+
});
|
|
175
231
|
const flattenLegendItems = getFlattenLegendItems(series, preparedLegend);
|
|
176
232
|
const items = getGroupedLegendItems({
|
|
177
233
|
maxLegendWidth,
|
|
@@ -197,13 +253,15 @@ export function getLegendComponents(args) {
|
|
|
197
253
|
});
|
|
198
254
|
}
|
|
199
255
|
preparedLegend.height = legendHeight;
|
|
256
|
+
preparedLegend.width = Math.max(maxLegendWidth, preparedLegend.width);
|
|
200
257
|
}
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
258
|
+
const offset = getLegendOffset({
|
|
259
|
+
position: preparedLegend.position,
|
|
260
|
+
chartWidth,
|
|
261
|
+
chartHeight,
|
|
262
|
+
chartMargin,
|
|
263
|
+
legendWidth: preparedLegend.width,
|
|
264
|
+
legendHeight: preparedLegend.height,
|
|
265
|
+
});
|
|
208
266
|
return { legendConfig: { offset, pagination, maxWidth: maxLegendWidth }, legendItems: items };
|
|
209
267
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Dispatch } from 'd3';
|
|
3
|
-
import type { RangeSliderState, ZoomState } from '../../hooks';
|
|
3
|
+
import type { PreparedLegend, RangeSliderState, ZoomState } from '../../hooks';
|
|
4
4
|
import type { ChartInnerProps } from './types';
|
|
5
5
|
type Props = ChartInnerProps & {
|
|
6
6
|
clipPathId: string;
|
|
@@ -35,7 +35,7 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
35
35
|
} | undefined;
|
|
36
36
|
legendItems: never[] | import("../../hooks").LegendItem[][];
|
|
37
37
|
preparedChart: import("../../hooks").PreparedChart;
|
|
38
|
-
preparedLegend:
|
|
38
|
+
preparedLegend: PreparedLegend | null;
|
|
39
39
|
preparedSeries: import("../../hooks").PreparedSeries[];
|
|
40
40
|
preparedSeriesOptions: import("../../constants").SeriesOptionsDefaults;
|
|
41
41
|
preparedSplit: import("../../hooks").PreparedSplit;
|
|
@@ -9,6 +9,30 @@ import { hasAtLeastOneSeriesDataPerPlot } from './utils';
|
|
|
9
9
|
const CLIP_PATH_BY_SERIES_TYPE = {
|
|
10
10
|
[SERIES_TYPE.Scatter]: false,
|
|
11
11
|
};
|
|
12
|
+
function getBoundsOffsetTop(args) {
|
|
13
|
+
const { chartMarginTop, preparedLegend } = args;
|
|
14
|
+
return (chartMarginTop +
|
|
15
|
+
((preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && preparedLegend.position === 'top'
|
|
16
|
+
? preparedLegend.height + preparedLegend.margin
|
|
17
|
+
: 0));
|
|
18
|
+
}
|
|
19
|
+
function getBoundsOffsetLeft(args) {
|
|
20
|
+
const { chartMarginLeft, preparedLegend, yAxis, getYAxisWidth: getAxisWidth } = args;
|
|
21
|
+
const legendOffset = (preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && preparedLegend.position === 'left'
|
|
22
|
+
? preparedLegend.width + preparedLegend.margin
|
|
23
|
+
: 0;
|
|
24
|
+
const leftAxisWidth = yAxis.reduce((acc, axis) => {
|
|
25
|
+
if (axis.position !== 'left') {
|
|
26
|
+
return acc;
|
|
27
|
+
}
|
|
28
|
+
const axisWidth = getAxisWidth(axis);
|
|
29
|
+
if (acc < axisWidth) {
|
|
30
|
+
acc = axisWidth;
|
|
31
|
+
}
|
|
32
|
+
return acc;
|
|
33
|
+
}, 0);
|
|
34
|
+
return chartMarginLeft + legendOffset + leftAxisWidth;
|
|
35
|
+
}
|
|
12
36
|
export function useChartInnerProps(props) {
|
|
13
37
|
var _a;
|
|
14
38
|
const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, rangeSliderState, svgContainer, width, updateZoomState, zoomState, } = props;
|
|
@@ -129,22 +153,17 @@ export function useChartInnerProps(props) {
|
|
|
129
153
|
yAxis,
|
|
130
154
|
yScale,
|
|
131
155
|
});
|
|
132
|
-
const boundsOffsetTop =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
156
|
+
const boundsOffsetTop = getBoundsOffsetTop({
|
|
157
|
+
chartMarginTop: chart.margin.top,
|
|
158
|
+
preparedLegend,
|
|
159
|
+
});
|
|
136
160
|
// We need to calculate the width of each left axis because the first axis can be hidden
|
|
137
|
-
const boundsOffsetLeft =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (acc < axisWidth) {
|
|
144
|
-
acc = axisWidth;
|
|
145
|
-
}
|
|
146
|
-
return acc;
|
|
147
|
-
}, 0);
|
|
161
|
+
const boundsOffsetLeft = getBoundsOffsetLeft({
|
|
162
|
+
chartMarginLeft: chart.margin.left,
|
|
163
|
+
preparedLegend,
|
|
164
|
+
yAxis,
|
|
165
|
+
getYAxisWidth,
|
|
166
|
+
});
|
|
148
167
|
const { x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
|
|
149
168
|
return {
|
|
150
169
|
allPreparedSeries,
|
|
@@ -6,16 +6,19 @@ import { block, createGradientRect, getContinuesColorFn, getLabelsSize, getLineD
|
|
|
6
6
|
import { axisBottom } from '../../utils/chart/axis-generators';
|
|
7
7
|
import './styles.css';
|
|
8
8
|
const b = block('legend');
|
|
9
|
-
const
|
|
10
|
-
const { align,
|
|
11
|
-
const top = 0;
|
|
12
|
-
if (align === 'left') {
|
|
13
|
-
return { top, left: offsetLeft };
|
|
14
|
-
}
|
|
9
|
+
const getLegendItemLeftPosition = (args) => {
|
|
10
|
+
const { align, width, contentWidth } = args;
|
|
15
11
|
if (align === 'right') {
|
|
16
|
-
return
|
|
12
|
+
return width - contentWidth;
|
|
13
|
+
}
|
|
14
|
+
if (align === 'left') {
|
|
15
|
+
return 0;
|
|
17
16
|
}
|
|
18
|
-
return
|
|
17
|
+
return width / 2 - contentWidth / 2;
|
|
18
|
+
};
|
|
19
|
+
const getLegendPosition = (args) => {
|
|
20
|
+
const { offsetLeft, offsetTop, contentWidth, width } = args;
|
|
21
|
+
return { top: offsetTop, left: offsetLeft + width / 2 - contentWidth / 2 };
|
|
19
22
|
};
|
|
20
23
|
const appendPaginator = (args) => {
|
|
21
24
|
const { container, pageIndex, legend, transform, pages, onArrowClick } = args;
|
|
@@ -157,6 +160,8 @@ export const Legend = (props) => {
|
|
|
157
160
|
? htmlElement.append('div').attr('data-legend', 1).style('position', 'absolute')
|
|
158
161
|
: null;
|
|
159
162
|
let legendWidth = 0;
|
|
163
|
+
let legendLeft = 0;
|
|
164
|
+
let legendTop = 0;
|
|
160
165
|
if (legend.type === 'discrete') {
|
|
161
166
|
const start = (_b = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.pages[pageIndex]) === null || _b === void 0 ? void 0 : _b.start;
|
|
162
167
|
const end = (_e = (_c = config.pagination) === null || _c === void 0 ? void 0 : _c.pages[pageIndex]) === null || _e === void 0 ? void 0 : _e.end;
|
|
@@ -249,13 +254,11 @@ export const Legend = (props) => {
|
|
|
249
254
|
let left = 0;
|
|
250
255
|
switch (legend.justifyContent) {
|
|
251
256
|
case 'center': {
|
|
252
|
-
|
|
257
|
+
left = getLegendItemLeftPosition({
|
|
253
258
|
align: legend.align,
|
|
254
259
|
width: config.maxWidth,
|
|
255
260
|
contentWidth,
|
|
256
|
-
offsetLeft: config.offset.left,
|
|
257
261
|
});
|
|
258
|
-
left = legendLinePostion.left;
|
|
259
262
|
legendWidth = config.maxWidth;
|
|
260
263
|
break;
|
|
261
264
|
}
|
|
@@ -280,8 +283,39 @@ export const Legend = (props) => {
|
|
|
280
283
|
onArrowClick: setPageIndex,
|
|
281
284
|
});
|
|
282
285
|
}
|
|
286
|
+
const { left, top } = getLegendPosition({
|
|
287
|
+
width: config.maxWidth,
|
|
288
|
+
contentWidth: legendWidth,
|
|
289
|
+
offsetLeft: config.offset.left,
|
|
290
|
+
offsetTop: config.offset.top,
|
|
291
|
+
});
|
|
292
|
+
legendLeft = left;
|
|
293
|
+
legendTop = top;
|
|
283
294
|
}
|
|
284
295
|
else {
|
|
296
|
+
let left = 0;
|
|
297
|
+
switch (legend.align) {
|
|
298
|
+
case 'right': {
|
|
299
|
+
left = config.offset.left + config.maxWidth - legend.width;
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
case 'left': {
|
|
303
|
+
left = config.offset.left;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
case 'center': {
|
|
307
|
+
left = config.offset.left + config.maxWidth / 2 - legend.width / 2;
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const { top } = getLegendPosition({
|
|
312
|
+
width: config.maxWidth,
|
|
313
|
+
contentWidth: legendWidth,
|
|
314
|
+
offsetLeft: config.offset.left,
|
|
315
|
+
offsetTop: config.offset.top,
|
|
316
|
+
});
|
|
317
|
+
legendLeft = left;
|
|
318
|
+
legendTop = top;
|
|
285
319
|
// gradient rect
|
|
286
320
|
const domain = (_f = legend.colorScale.domain) !== null && _f !== void 0 ? _f : [];
|
|
287
321
|
const rectHeight = CONTINUOUS_LEGEND_SIZE.height;
|
|
@@ -361,15 +395,10 @@ export const Legend = (props) => {
|
|
|
361
395
|
else {
|
|
362
396
|
svgElement.selectAll(`.${legendTitleClassname}`).remove();
|
|
363
397
|
}
|
|
364
|
-
const { left } = getLegendPosition({
|
|
365
|
-
align: legend.align,
|
|
366
|
-
width: config.maxWidth,
|
|
367
|
-
contentWidth: legendWidth,
|
|
368
|
-
});
|
|
369
398
|
svgElement
|
|
370
|
-
.attr('transform', `translate(${[
|
|
399
|
+
.attr('transform', `translate(${[legendLeft, legendTop].join(',')})`)
|
|
371
400
|
.style('opacity', 1);
|
|
372
|
-
htmlContainer === null || htmlContainer === void 0 ? void 0 : htmlContainer.style('transform', `translate(${
|
|
401
|
+
htmlContainer === null || htmlContainer === void 0 ? void 0 : htmlContainer.style('transform', `translate(${legendLeft}px, ${legendTop}px)`);
|
|
373
402
|
}
|
|
374
403
|
prepareLegend();
|
|
375
404
|
}, [chartSeries, onItemClick, onUpdate, legend, items, config, pageIndex, htmlLayout]);
|
|
@@ -30,6 +30,18 @@ const getTopOffset = ({ preparedLegend }) => {
|
|
|
30
30
|
}
|
|
31
31
|
return 0;
|
|
32
32
|
};
|
|
33
|
+
const getRightOffset = ({ preparedLegend }) => {
|
|
34
|
+
if ((preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && preparedLegend.position === 'right') {
|
|
35
|
+
return preparedLegend.width + preparedLegend.margin;
|
|
36
|
+
}
|
|
37
|
+
return 0;
|
|
38
|
+
};
|
|
39
|
+
const getLeftOffset = ({ preparedLegend }) => {
|
|
40
|
+
if ((preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && preparedLegend.position === 'left') {
|
|
41
|
+
return preparedLegend.width + preparedLegend.margin;
|
|
42
|
+
}
|
|
43
|
+
return 0;
|
|
44
|
+
};
|
|
33
45
|
export const useChartDimensions = (args) => {
|
|
34
46
|
const { height, margin, preparedLegend, preparedSeries, preparedXAxis, preparedYAxis, width } = args;
|
|
35
47
|
return React.useMemo(() => {
|
|
@@ -41,7 +53,10 @@ export const useChartDimensions = (args) => {
|
|
|
41
53
|
preparedXAxis,
|
|
42
54
|
});
|
|
43
55
|
const topOffset = getTopOffset({ preparedLegend });
|
|
56
|
+
const rightOffset = getRightOffset({ preparedLegend });
|
|
57
|
+
const leftOffset = getLeftOffset({ preparedLegend });
|
|
44
58
|
const boundsHeight = height - margin.top - margin.bottom - bottomOffset - topOffset;
|
|
45
|
-
|
|
59
|
+
const adjustedBoundsWidth = boundsWidth - rightOffset - leftOffset;
|
|
60
|
+
return { boundsWidth: adjustedBoundsWidth, boundsHeight };
|
|
46
61
|
}, [height, margin, preparedLegend, preparedSeries, preparedXAxis, preparedYAxis, width]);
|
|
47
62
|
};
|
|
@@ -12,7 +12,10 @@ export async function getPreparedLegend(args) {
|
|
|
12
12
|
const defaultItemStyle = clone(legendDefaults.itemStyle);
|
|
13
13
|
const itemStyle = get(legend, 'itemStyle');
|
|
14
14
|
const computedItemStyle = merge(defaultItemStyle, itemStyle);
|
|
15
|
-
const
|
|
15
|
+
const { maxHeight: lineHeight, maxWidth: lineWidth } = await getLabelsSize({
|
|
16
|
+
labels: ['Tmp'],
|
|
17
|
+
style: computedItemStyle,
|
|
18
|
+
});
|
|
16
19
|
const legendType = get(legend, 'type', 'discrete');
|
|
17
20
|
const isTitleEnabled = Boolean((_a = legend === null || legend === void 0 ? void 0 : legend.title) === null || _a === void 0 ? void 0 : _a.text);
|
|
18
21
|
const titleMargin = isTitleEnabled ? get(legend, 'title.margin', 4) : 0;
|
|
@@ -34,9 +37,11 @@ export async function getPreparedLegend(args) {
|
|
|
34
37
|
stops: [],
|
|
35
38
|
};
|
|
36
39
|
let height = 0;
|
|
40
|
+
let legendWidth = 0;
|
|
37
41
|
if (enabled) {
|
|
38
42
|
height += titleHeight + titleMargin;
|
|
39
43
|
if (legendType === 'continuous') {
|
|
44
|
+
legendWidth = get(legend, 'width', CONTINUOUS_LEGEND_SIZE.width);
|
|
40
45
|
height += CONTINUOUS_LEGEND_SIZE.height;
|
|
41
46
|
height += ticks.labelsLineHeight + ticks.labelsMargin;
|
|
42
47
|
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 : [];
|
|
@@ -47,9 +52,9 @@ export async function getPreparedLegend(args) {
|
|
|
47
52
|
}
|
|
48
53
|
else {
|
|
49
54
|
height += lineHeight;
|
|
55
|
+
legendWidth = get(legend, 'width', lineWidth);
|
|
50
56
|
}
|
|
51
57
|
}
|
|
52
|
-
const legendWidth = get(legend, 'width', CONTINUOUS_LEGEND_SIZE.width);
|
|
53
58
|
return {
|
|
54
59
|
align: get(legend, 'align', legendDefaults.align),
|
|
55
60
|
justifyContent: get(legend, 'justifyContent', legendDefaults.justifyContent),
|
|
@@ -168,10 +173,61 @@ function getPagination(args) {
|
|
|
168
173
|
});
|
|
169
174
|
return { pages };
|
|
170
175
|
}
|
|
176
|
+
function getLegendOffset(args) {
|
|
177
|
+
const { position, chartWidth, chartHeight, chartMargin, legendWidth, legendHeight } = args;
|
|
178
|
+
switch (position) {
|
|
179
|
+
case 'top':
|
|
180
|
+
return {
|
|
181
|
+
top: chartMargin.top,
|
|
182
|
+
left: chartMargin.left,
|
|
183
|
+
};
|
|
184
|
+
case 'right':
|
|
185
|
+
return {
|
|
186
|
+
top: chartMargin.top,
|
|
187
|
+
left: chartWidth - chartMargin.right - legendWidth,
|
|
188
|
+
};
|
|
189
|
+
case 'left':
|
|
190
|
+
return {
|
|
191
|
+
top: chartMargin.top,
|
|
192
|
+
left: chartMargin.left,
|
|
193
|
+
};
|
|
194
|
+
case 'bottom':
|
|
195
|
+
default:
|
|
196
|
+
return {
|
|
197
|
+
top: chartHeight - chartMargin.bottom - legendHeight,
|
|
198
|
+
left: chartMargin.left,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function getMaxLegendWidth(args) {
|
|
203
|
+
const { chartWidth, chartMargin, preparedLegend, isVerticalPosition } = args;
|
|
204
|
+
if (isVerticalPosition) {
|
|
205
|
+
return (chartWidth - chartMargin.right - chartMargin.left - preparedLegend.margin) / 2;
|
|
206
|
+
}
|
|
207
|
+
return chartWidth - chartMargin.right - chartMargin.left;
|
|
208
|
+
}
|
|
209
|
+
function getMaxLegendHeight(args) {
|
|
210
|
+
const { chartHeight, chartMargin, preparedLegend, isVerticalPosition } = args;
|
|
211
|
+
if (isVerticalPosition) {
|
|
212
|
+
return chartHeight - chartMargin.top - chartMargin.bottom;
|
|
213
|
+
}
|
|
214
|
+
return (chartHeight - chartMargin.top - chartMargin.bottom - preparedLegend.margin) / 2;
|
|
215
|
+
}
|
|
171
216
|
export function getLegendComponents(args) {
|
|
172
217
|
const { chartWidth, chartHeight, chartMargin, series, preparedLegend } = args;
|
|
173
|
-
const
|
|
174
|
-
const
|
|
218
|
+
const isVerticalPosition = preparedLegend.position === 'right' || preparedLegend.position === 'left';
|
|
219
|
+
const maxLegendWidth = getMaxLegendWidth({
|
|
220
|
+
chartWidth,
|
|
221
|
+
chartMargin,
|
|
222
|
+
preparedLegend,
|
|
223
|
+
isVerticalPosition,
|
|
224
|
+
});
|
|
225
|
+
const maxLegendHeight = getMaxLegendHeight({
|
|
226
|
+
chartHeight,
|
|
227
|
+
chartMargin,
|
|
228
|
+
preparedLegend,
|
|
229
|
+
isVerticalPosition,
|
|
230
|
+
});
|
|
175
231
|
const flattenLegendItems = getFlattenLegendItems(series, preparedLegend);
|
|
176
232
|
const items = getGroupedLegendItems({
|
|
177
233
|
maxLegendWidth,
|
|
@@ -197,13 +253,15 @@ export function getLegendComponents(args) {
|
|
|
197
253
|
});
|
|
198
254
|
}
|
|
199
255
|
preparedLegend.height = legendHeight;
|
|
256
|
+
preparedLegend.width = Math.max(maxLegendWidth, preparedLegend.width);
|
|
200
257
|
}
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
258
|
+
const offset = getLegendOffset({
|
|
259
|
+
position: preparedLegend.position,
|
|
260
|
+
chartWidth,
|
|
261
|
+
chartHeight,
|
|
262
|
+
chartMargin,
|
|
263
|
+
legendWidth: preparedLegend.width,
|
|
264
|
+
legendHeight: preparedLegend.height,
|
|
265
|
+
});
|
|
208
266
|
return { legendConfig: { offset, pagination, maxWidth: maxLegendWidth }, legendItems: items };
|
|
209
267
|
}
|