@gravity-ui/charts 1.25.0 → 1.26.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/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 +39 -17
- 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 +39 -17
- 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,21 @@ 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 getLegendItemPosition = (args) => {
|
|
10
|
+
const { align, width, contentWidth } = args;
|
|
15
11
|
if (align === 'right') {
|
|
16
|
-
return {
|
|
12
|
+
return { left: width - contentWidth };
|
|
13
|
+
}
|
|
14
|
+
else if (align === 'left') {
|
|
15
|
+
return { left: 0 };
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
return { left: width / 2 - contentWidth / 2 };
|
|
17
19
|
}
|
|
18
|
-
|
|
20
|
+
};
|
|
21
|
+
const getLegendPosition = (args) => {
|
|
22
|
+
const { offsetLeft, offsetTop, contentWidth, width } = args;
|
|
23
|
+
return { top: offsetTop, left: offsetLeft + width / 2 - contentWidth / 2 };
|
|
19
24
|
};
|
|
20
25
|
const appendPaginator = (args) => {
|
|
21
26
|
const { container, pageIndex, legend, transform, pages, onArrowClick } = args;
|
|
@@ -157,6 +162,8 @@ export const Legend = (props) => {
|
|
|
157
162
|
? htmlElement.append('div').attr('data-legend', 1).style('position', 'absolute')
|
|
158
163
|
: null;
|
|
159
164
|
let legendWidth = 0;
|
|
165
|
+
let legendLeft = 0;
|
|
166
|
+
let legendTop = 0;
|
|
160
167
|
if (legend.type === 'discrete') {
|
|
161
168
|
const start = (_b = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.pages[pageIndex]) === null || _b === void 0 ? void 0 : _b.start;
|
|
162
169
|
const end = (_e = (_c = config.pagination) === null || _c === void 0 ? void 0 : _c.pages[pageIndex]) === null || _e === void 0 ? void 0 : _e.end;
|
|
@@ -249,11 +256,10 @@ export const Legend = (props) => {
|
|
|
249
256
|
let left = 0;
|
|
250
257
|
switch (legend.justifyContent) {
|
|
251
258
|
case 'center': {
|
|
252
|
-
const legendLinePostion =
|
|
259
|
+
const legendLinePostion = getLegendItemPosition({
|
|
253
260
|
align: legend.align,
|
|
254
261
|
width: config.maxWidth,
|
|
255
262
|
contentWidth,
|
|
256
|
-
offsetLeft: config.offset.left,
|
|
257
263
|
});
|
|
258
264
|
left = legendLinePostion.left;
|
|
259
265
|
legendWidth = config.maxWidth;
|
|
@@ -280,8 +286,29 @@ export const Legend = (props) => {
|
|
|
280
286
|
onArrowClick: setPageIndex,
|
|
281
287
|
});
|
|
282
288
|
}
|
|
289
|
+
const { left, top } = getLegendPosition({
|
|
290
|
+
width: config.maxWidth,
|
|
291
|
+
contentWidth: legendWidth,
|
|
292
|
+
offsetLeft: config.offset.left,
|
|
293
|
+
offsetTop: config.offset.top,
|
|
294
|
+
});
|
|
295
|
+
legendLeft = left;
|
|
296
|
+
legendTop = top;
|
|
283
297
|
}
|
|
284
298
|
else {
|
|
299
|
+
const { left } = getLegendItemPosition({
|
|
300
|
+
align: legend.align,
|
|
301
|
+
width: config.maxWidth,
|
|
302
|
+
contentWidth: legend.width,
|
|
303
|
+
});
|
|
304
|
+
const { top } = getLegendPosition({
|
|
305
|
+
width: config.maxWidth,
|
|
306
|
+
contentWidth: legendWidth,
|
|
307
|
+
offsetLeft: config.offset.left,
|
|
308
|
+
offsetTop: config.offset.top,
|
|
309
|
+
});
|
|
310
|
+
legendLeft = left;
|
|
311
|
+
legendTop = top;
|
|
285
312
|
// gradient rect
|
|
286
313
|
const domain = (_f = legend.colorScale.domain) !== null && _f !== void 0 ? _f : [];
|
|
287
314
|
const rectHeight = CONTINUOUS_LEGEND_SIZE.height;
|
|
@@ -361,15 +388,10 @@ export const Legend = (props) => {
|
|
|
361
388
|
else {
|
|
362
389
|
svgElement.selectAll(`.${legendTitleClassname}`).remove();
|
|
363
390
|
}
|
|
364
|
-
const { left } = getLegendPosition({
|
|
365
|
-
align: legend.align,
|
|
366
|
-
width: config.maxWidth,
|
|
367
|
-
contentWidth: legendWidth,
|
|
368
|
-
});
|
|
369
391
|
svgElement
|
|
370
|
-
.attr('transform', `translate(${[
|
|
392
|
+
.attr('transform', `translate(${[legendLeft, legendTop].join(',')})`)
|
|
371
393
|
.style('opacity', 1);
|
|
372
|
-
htmlContainer === null || htmlContainer === void 0 ? void 0 : htmlContainer.style('transform', `translate(${
|
|
394
|
+
htmlContainer === null || htmlContainer === void 0 ? void 0 : htmlContainer.style('transform', `translate(${legendLeft}px, ${legendTop}px)`);
|
|
373
395
|
}
|
|
374
396
|
prepareLegend();
|
|
375
397
|
}, [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,21 @@ 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 getLegendItemPosition = (args) => {
|
|
10
|
+
const { align, width, contentWidth } = args;
|
|
15
11
|
if (align === 'right') {
|
|
16
|
-
return {
|
|
12
|
+
return { left: width - contentWidth };
|
|
13
|
+
}
|
|
14
|
+
else if (align === 'left') {
|
|
15
|
+
return { left: 0 };
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
return { left: width / 2 - contentWidth / 2 };
|
|
17
19
|
}
|
|
18
|
-
|
|
20
|
+
};
|
|
21
|
+
const getLegendPosition = (args) => {
|
|
22
|
+
const { offsetLeft, offsetTop, contentWidth, width } = args;
|
|
23
|
+
return { top: offsetTop, left: offsetLeft + width / 2 - contentWidth / 2 };
|
|
19
24
|
};
|
|
20
25
|
const appendPaginator = (args) => {
|
|
21
26
|
const { container, pageIndex, legend, transform, pages, onArrowClick } = args;
|
|
@@ -157,6 +162,8 @@ export const Legend = (props) => {
|
|
|
157
162
|
? htmlElement.append('div').attr('data-legend', 1).style('position', 'absolute')
|
|
158
163
|
: null;
|
|
159
164
|
let legendWidth = 0;
|
|
165
|
+
let legendLeft = 0;
|
|
166
|
+
let legendTop = 0;
|
|
160
167
|
if (legend.type === 'discrete') {
|
|
161
168
|
const start = (_b = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.pages[pageIndex]) === null || _b === void 0 ? void 0 : _b.start;
|
|
162
169
|
const end = (_e = (_c = config.pagination) === null || _c === void 0 ? void 0 : _c.pages[pageIndex]) === null || _e === void 0 ? void 0 : _e.end;
|
|
@@ -249,11 +256,10 @@ export const Legend = (props) => {
|
|
|
249
256
|
let left = 0;
|
|
250
257
|
switch (legend.justifyContent) {
|
|
251
258
|
case 'center': {
|
|
252
|
-
const legendLinePostion =
|
|
259
|
+
const legendLinePostion = getLegendItemPosition({
|
|
253
260
|
align: legend.align,
|
|
254
261
|
width: config.maxWidth,
|
|
255
262
|
contentWidth,
|
|
256
|
-
offsetLeft: config.offset.left,
|
|
257
263
|
});
|
|
258
264
|
left = legendLinePostion.left;
|
|
259
265
|
legendWidth = config.maxWidth;
|
|
@@ -280,8 +286,29 @@ export const Legend = (props) => {
|
|
|
280
286
|
onArrowClick: setPageIndex,
|
|
281
287
|
});
|
|
282
288
|
}
|
|
289
|
+
const { left, top } = getLegendPosition({
|
|
290
|
+
width: config.maxWidth,
|
|
291
|
+
contentWidth: legendWidth,
|
|
292
|
+
offsetLeft: config.offset.left,
|
|
293
|
+
offsetTop: config.offset.top,
|
|
294
|
+
});
|
|
295
|
+
legendLeft = left;
|
|
296
|
+
legendTop = top;
|
|
283
297
|
}
|
|
284
298
|
else {
|
|
299
|
+
const { left } = getLegendItemPosition({
|
|
300
|
+
align: legend.align,
|
|
301
|
+
width: config.maxWidth,
|
|
302
|
+
contentWidth: legend.width,
|
|
303
|
+
});
|
|
304
|
+
const { top } = getLegendPosition({
|
|
305
|
+
width: config.maxWidth,
|
|
306
|
+
contentWidth: legendWidth,
|
|
307
|
+
offsetLeft: config.offset.left,
|
|
308
|
+
offsetTop: config.offset.top,
|
|
309
|
+
});
|
|
310
|
+
legendLeft = left;
|
|
311
|
+
legendTop = top;
|
|
285
312
|
// gradient rect
|
|
286
313
|
const domain = (_f = legend.colorScale.domain) !== null && _f !== void 0 ? _f : [];
|
|
287
314
|
const rectHeight = CONTINUOUS_LEGEND_SIZE.height;
|
|
@@ -361,15 +388,10 @@ export const Legend = (props) => {
|
|
|
361
388
|
else {
|
|
362
389
|
svgElement.selectAll(`.${legendTitleClassname}`).remove();
|
|
363
390
|
}
|
|
364
|
-
const { left } = getLegendPosition({
|
|
365
|
-
align: legend.align,
|
|
366
|
-
width: config.maxWidth,
|
|
367
|
-
contentWidth: legendWidth,
|
|
368
|
-
});
|
|
369
391
|
svgElement
|
|
370
|
-
.attr('transform', `translate(${[
|
|
392
|
+
.attr('transform', `translate(${[legendLeft, legendTop].join(',')})`)
|
|
371
393
|
.style('opacity', 1);
|
|
372
|
-
htmlContainer === null || htmlContainer === void 0 ? void 0 : htmlContainer.style('transform', `translate(${
|
|
394
|
+
htmlContainer === null || htmlContainer === void 0 ? void 0 : htmlContainer.style('transform', `translate(${legendLeft}px, ${legendTop}px)`);
|
|
373
395
|
}
|
|
374
396
|
prepareLegend();
|
|
375
397
|
}, [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
|
}
|