@gravity-ui/charts 1.38.0 → 1.38.2
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 +4 -15
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +1 -41
- package/dist/cjs/components/ChartInner/useLegend.d.ts +14 -0
- package/dist/cjs/components/ChartInner/useLegend.js +37 -0
- package/dist/cjs/components/Legend/index.js +20 -22
- package/dist/cjs/hooks/useAxis/x-axis.js +1 -1
- package/dist/cjs/hooks/useAxis/y-axis.js +18 -17
- package/dist/cjs/hooks/useSeries/prepare-legend.js +2 -2
- package/dist/cjs/hooks/useSeries/prepare-line.js +3 -1
- package/dist/cjs/hooks/useSeries/types.d.ts +3 -1
- package/dist/cjs/hooks/useSeries/utils.js +6 -2
- package/dist/cjs/types/chart/axis.d.ts +8 -2
- package/dist/cjs/utils/chart/symbol.d.ts +4 -0
- package/dist/cjs/utils/chart/symbol.js +15 -0
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +4 -15
- package/dist/esm/components/ChartInner/useChartInnerProps.js +1 -41
- package/dist/esm/components/ChartInner/useLegend.d.ts +14 -0
- package/dist/esm/components/ChartInner/useLegend.js +37 -0
- package/dist/esm/components/Legend/index.js +20 -22
- package/dist/esm/hooks/useAxis/x-axis.js +1 -1
- package/dist/esm/hooks/useAxis/y-axis.js +18 -17
- package/dist/esm/hooks/useSeries/prepare-legend.js +2 -2
- package/dist/esm/hooks/useSeries/prepare-line.js +3 -1
- package/dist/esm/hooks/useSeries/types.d.ts +3 -1
- package/dist/esm/hooks/useSeries/utils.js +6 -2
- package/dist/esm/types/chart/axis.d.ts +8 -2
- package/dist/esm/utils/chart/symbol.d.ts +4 -0
- package/dist/esm/utils/chart/symbol.js +15 -0
- 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 {
|
|
3
|
+
import type { PreparedChart, PreparedLegend, RangeSliderState, ZoomState } from '../../hooks';
|
|
4
4
|
import type { LegendConfig } from '../../types';
|
|
5
5
|
import type { ChartInnerProps } from './types';
|
|
6
6
|
type Props = ChartInnerProps & {
|
|
@@ -13,19 +13,8 @@ type Props = ChartInnerProps & {
|
|
|
13
13
|
zoomState: Partial<ZoomState>;
|
|
14
14
|
rangeSliderState?: RangeSliderState;
|
|
15
15
|
};
|
|
16
|
-
type LegendState = {
|
|
17
|
-
legendConfig?: LegendConfig;
|
|
18
|
-
legendItems: LegendItem[][];
|
|
19
|
-
};
|
|
20
|
-
export declare function useLegend({ preparedLegend, preparedChart, preparedSeries, width, height, }: {
|
|
21
|
-
preparedLegend: PreparedLegend | null;
|
|
22
|
-
preparedChart: PreparedChart;
|
|
23
|
-
preparedSeries: PreparedSeries[];
|
|
24
|
-
width: number;
|
|
25
|
-
height: number;
|
|
26
|
-
}): LegendState;
|
|
27
16
|
export declare function useChartInnerProps(props: Props): {
|
|
28
|
-
allPreparedSeries: PreparedSeries[];
|
|
17
|
+
allPreparedSeries: import("../../hooks").PreparedSeries[];
|
|
29
18
|
boundsHeight: number;
|
|
30
19
|
boundsOffsetLeft: number;
|
|
31
20
|
boundsOffsetTop: number;
|
|
@@ -33,9 +22,9 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
33
22
|
handleLegendItemClick: import("../../hooks").OnLegendItemClick;
|
|
34
23
|
isOutsideBounds: (x: number, y: number) => boolean;
|
|
35
24
|
legendConfig: LegendConfig | undefined;
|
|
36
|
-
legendItems: LegendItem[][];
|
|
25
|
+
legendItems: import("../../hooks").LegendItem[][];
|
|
37
26
|
preparedLegend: PreparedLegend | null;
|
|
38
|
-
preparedSeries: PreparedSeries[];
|
|
27
|
+
preparedSeries: import("../../hooks").PreparedSeries[];
|
|
39
28
|
preparedSeriesOptions: import("../../constants").SeriesOptionsDefaults;
|
|
40
29
|
preparedSplit: import("../../hooks").PreparedSplit;
|
|
41
30
|
prevHeight: number | undefined;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import isEqual from 'lodash/isEqual';
|
|
3
2
|
import { DEFAULT_PALETTE, SERIES_TYPE } from '../../constants';
|
|
4
3
|
import { useAxis, useAxisScales, useChartDimensions, useNormalizedOriginalData, usePrevious, useSeries, useShapes, useSplit, useYAxisLabelWidth, useZoom, } from '../../hooks';
|
|
5
4
|
import { getYAxisWidth } from '../../hooks/useChartDimensions/utils';
|
|
6
|
-
import { getLegendComponents } from '../../hooks/useSeries/prepare-legend';
|
|
7
5
|
import { getPreparedOptions } from '../../hooks/useSeries/prepare-options';
|
|
8
6
|
import { getEffectiveXRange, getZoomedSeriesData } from '../../utils';
|
|
7
|
+
import { useLegend } from './useLegend';
|
|
9
8
|
import { hasAtLeastOneSeriesDataPerPlot } from './utils';
|
|
10
9
|
const CLIP_PATH_BY_SERIES_TYPE = {
|
|
11
10
|
[SERIES_TYPE.Scatter]: false,
|
|
@@ -35,45 +34,6 @@ function getBoundsOffsetLeft(args) {
|
|
|
35
34
|
}, 0);
|
|
36
35
|
return chartMarginLeft + legendOffset + leftAxisWidth;
|
|
37
36
|
}
|
|
38
|
-
export function useLegend({ preparedLegend, preparedChart, preparedSeries, width, height, }) {
|
|
39
|
-
const [legendState, setLegend] = React.useState({
|
|
40
|
-
legendConfig: undefined,
|
|
41
|
-
legendItems: [],
|
|
42
|
-
});
|
|
43
|
-
const legendStateRunRef = React.useRef(0);
|
|
44
|
-
const prevLegendStateValue = React.useRef(legendState);
|
|
45
|
-
const legendStateReady = React.useRef(false);
|
|
46
|
-
React.useEffect(() => {
|
|
47
|
-
legendStateRunRef.current++;
|
|
48
|
-
legendStateReady.current = false;
|
|
49
|
-
(async function () {
|
|
50
|
-
const currentRun = legendStateRunRef.current;
|
|
51
|
-
if (!preparedLegend) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const newStateValue = await getLegendComponents({
|
|
55
|
-
chartWidth: width,
|
|
56
|
-
chartHeight: height,
|
|
57
|
-
chartMargin: preparedChart.margin,
|
|
58
|
-
series: preparedSeries,
|
|
59
|
-
preparedLegend,
|
|
60
|
-
});
|
|
61
|
-
if (legendStateRunRef.current === currentRun) {
|
|
62
|
-
if (!isEqual(prevLegendStateValue.current, newStateValue)) {
|
|
63
|
-
setLegend(newStateValue);
|
|
64
|
-
prevLegendStateValue.current = newStateValue;
|
|
65
|
-
}
|
|
66
|
-
legendStateReady.current = true;
|
|
67
|
-
}
|
|
68
|
-
})();
|
|
69
|
-
}, [height, preparedChart.margin, preparedLegend, preparedSeries, width]);
|
|
70
|
-
return legendStateReady.current
|
|
71
|
-
? legendState
|
|
72
|
-
: {
|
|
73
|
-
legendConfig: undefined,
|
|
74
|
-
legendItems: [],
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
37
|
export function useChartInnerProps(props) {
|
|
78
38
|
const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, preparedChart, rangeSliderState, width, updateZoomState, zoomState, } = props;
|
|
79
39
|
const prevWidth = usePrevious(width);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LegendItem, PreparedChart, PreparedLegend, PreparedSeries } from '../../hooks';
|
|
2
|
+
import type { LegendConfig } from '../../types';
|
|
3
|
+
type LegendState = {
|
|
4
|
+
legendConfig?: LegendConfig;
|
|
5
|
+
legendItems: LegendItem[][];
|
|
6
|
+
};
|
|
7
|
+
export declare function useLegend({ preparedLegend, preparedChart, preparedSeries, width, height, }: {
|
|
8
|
+
preparedLegend: PreparedLegend | null;
|
|
9
|
+
preparedChart: PreparedChart;
|
|
10
|
+
preparedSeries: PreparedSeries[];
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
}): LegendState;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
3
|
+
import { getLegendComponents } from '../../hooks/useSeries/prepare-legend';
|
|
4
|
+
export function useLegend({ preparedLegend, preparedChart, preparedSeries, width, height, }) {
|
|
5
|
+
const [legendState, setLegend] = React.useState({
|
|
6
|
+
legendConfig: undefined,
|
|
7
|
+
legendItems: [],
|
|
8
|
+
});
|
|
9
|
+
const legendStateRunRef = React.useRef(0);
|
|
10
|
+
const prevLegendStateValue = React.useRef(legendState);
|
|
11
|
+
const legendStateReady = React.useRef(false);
|
|
12
|
+
React.useEffect(() => {
|
|
13
|
+
legendStateRunRef.current++;
|
|
14
|
+
legendStateReady.current = false;
|
|
15
|
+
(async function () {
|
|
16
|
+
const currentRun = legendStateRunRef.current;
|
|
17
|
+
if (!preparedLegend) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const newStateValue = await getLegendComponents({
|
|
21
|
+
chartWidth: width,
|
|
22
|
+
chartHeight: height,
|
|
23
|
+
chartMargin: preparedChart.margin,
|
|
24
|
+
series: preparedSeries,
|
|
25
|
+
preparedLegend,
|
|
26
|
+
});
|
|
27
|
+
if (legendStateRunRef.current === currentRun) {
|
|
28
|
+
if (!isEqual(prevLegendStateValue.current, newStateValue)) {
|
|
29
|
+
setLegend(newStateValue);
|
|
30
|
+
prevLegendStateValue.current = newStateValue;
|
|
31
|
+
}
|
|
32
|
+
legendStateReady.current = true;
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
35
|
+
}, [height, preparedChart.margin, preparedLegend, preparedSeries, width]);
|
|
36
|
+
return legendState;
|
|
37
|
+
}
|
|
@@ -71,7 +71,7 @@ function renderLegendSymbol(args) {
|
|
|
71
71
|
const getXPosition = (i) => {
|
|
72
72
|
return line.slice(0, i).reduce((acc, legendItem) => {
|
|
73
73
|
return (acc +
|
|
74
|
-
legendItem.symbol.
|
|
74
|
+
legendItem.symbol.bboxWidth +
|
|
75
75
|
legendItem.symbol.padding +
|
|
76
76
|
legendItem.textWidth +
|
|
77
77
|
legend.itemDistance);
|
|
@@ -82,13 +82,15 @@ function renderLegendSymbol(args) {
|
|
|
82
82
|
const x = getXPosition(i);
|
|
83
83
|
const className = b('item-symbol', { shape: d.symbol.shape, unselected: !d.visible });
|
|
84
84
|
const color = d.visible ? d.color : '';
|
|
85
|
+
const symbolType = d.symbol.symbolType;
|
|
86
|
+
const scatterSymbol = getSymbol(symbolType);
|
|
85
87
|
switch (d.symbol.shape) {
|
|
86
88
|
case 'path': {
|
|
87
89
|
appendLinePathElement({
|
|
88
90
|
svgRootElement: element.node(),
|
|
89
91
|
x,
|
|
90
92
|
height: legendLineHeight,
|
|
91
|
-
width: d.symbol.
|
|
93
|
+
width: d.symbol.bboxWidth,
|
|
92
94
|
color,
|
|
93
95
|
className,
|
|
94
96
|
dashStyle: d.dashStyle,
|
|
@@ -102,7 +104,7 @@ function renderLegendSymbol(args) {
|
|
|
102
104
|
.append('rect')
|
|
103
105
|
.attr('x', x)
|
|
104
106
|
.attr('y', y)
|
|
105
|
-
.attr('width', d.symbol.
|
|
107
|
+
.attr('width', d.symbol.bboxWidth)
|
|
106
108
|
.attr('height', d.symbol.height)
|
|
107
109
|
.attr('rx', d.symbol.radius)
|
|
108
110
|
.attr('class', className)
|
|
@@ -110,18 +112,14 @@ function renderLegendSymbol(args) {
|
|
|
110
112
|
break;
|
|
111
113
|
}
|
|
112
114
|
case 'symbol': {
|
|
115
|
+
const symbolAreaSize = Math.pow(d.symbol.width, 2);
|
|
113
116
|
const y = legendLineHeight / 2;
|
|
117
|
+
const bboxWidth = d.symbol.bboxWidth;
|
|
114
118
|
element
|
|
115
119
|
.append('svg:path')
|
|
116
|
-
.attr('d', () =>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// https://d3js.org/d3-shape/symbol#symbol
|
|
120
|
-
return symbol(scatterSymbol, d.symbol.width * d.symbol.width)();
|
|
121
|
-
})
|
|
122
|
-
.attr('transform', (_d, _index, elements) => {
|
|
123
|
-
var _a, _b;
|
|
124
|
-
const translateX = x + ((_b = (_a = elements[0]) === null || _a === void 0 ? void 0 : _a.getBBox()) === null || _b === void 0 ? void 0 : _b.width) / 2;
|
|
120
|
+
.attr('d', () => symbol(scatterSymbol, symbolAreaSize)())
|
|
121
|
+
.attr('transform', () => {
|
|
122
|
+
const translateX = x + bboxWidth / 2;
|
|
125
123
|
return 'translate(' + translateX + ',' + y + ')';
|
|
126
124
|
})
|
|
127
125
|
.attr('class', className)
|
|
@@ -140,7 +138,7 @@ export const Legend = (props) => {
|
|
|
140
138
|
}, [config.maxWidth]);
|
|
141
139
|
React.useEffect(() => {
|
|
142
140
|
async function prepareLegend() {
|
|
143
|
-
var _a, _b, _c, _e, _f, _g, _h
|
|
141
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
144
142
|
if (!ref.current || !htmlLayout) {
|
|
145
143
|
return;
|
|
146
144
|
}
|
|
@@ -157,7 +155,7 @@ export const Legend = (props) => {
|
|
|
157
155
|
let legendTop = 0;
|
|
158
156
|
if (legend.type === 'discrete') {
|
|
159
157
|
const start = (_b = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.pages[pageIndex]) === null || _b === void 0 ? void 0 : _b.start;
|
|
160
|
-
const end = (
|
|
158
|
+
const end = (_d = (_c = config.pagination) === null || _c === void 0 ? void 0 : _c.pages[pageIndex]) === null || _d === void 0 ? void 0 : _d.end;
|
|
161
159
|
const pageItems = typeof start === 'number' && typeof end === 'number'
|
|
162
160
|
? items.slice(start, end)
|
|
163
161
|
: items;
|
|
@@ -178,7 +176,7 @@ export const Legend = (props) => {
|
|
|
178
176
|
const getXPosition = (i) => {
|
|
179
177
|
return line.slice(0, i).reduce((acc, legendItem) => {
|
|
180
178
|
return (acc +
|
|
181
|
-
legendItem.symbol.
|
|
179
|
+
legendItem.symbol.bboxWidth +
|
|
182
180
|
legendItem.symbol.padding +
|
|
183
181
|
legendItem.textWidth +
|
|
184
182
|
legend.itemDistance);
|
|
@@ -202,7 +200,7 @@ export const Legend = (props) => {
|
|
|
202
200
|
return `${d.textWidth}px`;
|
|
203
201
|
})
|
|
204
202
|
.style('left', function (d, i) {
|
|
205
|
-
return `${getXPosition(i) + d.symbol.
|
|
203
|
+
return `${getXPosition(i) + d.symbol.bboxWidth + d.symbol.padding}px`;
|
|
206
204
|
})
|
|
207
205
|
.style('top', function (d) {
|
|
208
206
|
if (d.height < legendLineHeight) {
|
|
@@ -221,7 +219,7 @@ export const Legend = (props) => {
|
|
|
221
219
|
.append('text')
|
|
222
220
|
.attr('x', function (legendItem, i) {
|
|
223
221
|
return (getXPosition(i) +
|
|
224
|
-
legendItem.symbol.
|
|
222
|
+
legendItem.symbol.bboxWidth +
|
|
225
223
|
legendItem.symbol.padding);
|
|
226
224
|
})
|
|
227
225
|
.attr('height', legend.height)
|
|
@@ -238,7 +236,7 @@ export const Legend = (props) => {
|
|
|
238
236
|
}
|
|
239
237
|
else {
|
|
240
238
|
contentWidth = line.reduce((sum, l, index) => {
|
|
241
|
-
sum += l.textWidth + l.symbol.
|
|
239
|
+
sum += l.textWidth + l.symbol.bboxWidth + l.symbol.padding;
|
|
242
240
|
if (index > 0) {
|
|
243
241
|
sum += legend.itemDistance;
|
|
244
242
|
}
|
|
@@ -311,7 +309,7 @@ export const Legend = (props) => {
|
|
|
311
309
|
legendLeft = left;
|
|
312
310
|
legendTop = top;
|
|
313
311
|
// gradient rect
|
|
314
|
-
const domain = (
|
|
312
|
+
const domain = (_e = legend.colorScale.domain) !== null && _e !== void 0 ? _e : [];
|
|
315
313
|
const rectHeight = CONTINUOUS_LEGEND_SIZE.height;
|
|
316
314
|
svgElement.call(createGradientRect, {
|
|
317
315
|
y: legend.title.height + legend.title.margin,
|
|
@@ -380,9 +378,9 @@ export const Legend = (props) => {
|
|
|
380
378
|
.attr('class', legendTitleClassname)
|
|
381
379
|
.append('text')
|
|
382
380
|
.attr('dx', dx)
|
|
383
|
-
.attr('font-weight', (
|
|
384
|
-
.attr('font-size', (
|
|
385
|
-
.attr('fill', (
|
|
381
|
+
.attr('font-weight', (_f = legend.title.style.fontWeight) !== null && _f !== void 0 ? _f : null)
|
|
382
|
+
.attr('font-size', (_g = legend.title.style.fontSize) !== null && _g !== void 0 ? _g : null)
|
|
383
|
+
.attr('fill', (_h = legend.title.style.fontColor) !== null && _h !== void 0 ? _h : null)
|
|
386
384
|
.style('dominant-baseline', 'text-before-edge')
|
|
387
385
|
.html(legend.title.text);
|
|
388
386
|
}
|
|
@@ -142,7 +142,7 @@ export const getPreparedXAxis = async ({ xAxis, seriesData, width, boundsWidth,
|
|
|
142
142
|
: (_j = xAxis === null || xAxis === void 0 ? void 0 : xAxis.ticks) === null || _j === void 0 ? void 0 : _j.pixelInterval,
|
|
143
143
|
},
|
|
144
144
|
tickMarks: {
|
|
145
|
-
enabled: get(xAxis, 'tickMarks.enabled', axisTickMarksDefaults.enabled),
|
|
145
|
+
enabled: isAxisVisible && get(xAxis, 'tickMarks.enabled', axisTickMarksDefaults.enabled),
|
|
146
146
|
length: get(xAxis, 'tickMarks.length', axisTickMarksDefaults.length),
|
|
147
147
|
},
|
|
148
148
|
position: 'bottom',
|
|
@@ -52,7 +52,7 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
52
52
|
return Promise.all(
|
|
53
53
|
// eslint-disable-next-line complexity
|
|
54
54
|
axisItems.map(async (axisItem, axisIndex) => {
|
|
55
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
55
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
56
56
|
const plotIndex = get(axisItem, 'plotIndex', 0);
|
|
57
57
|
const firstPlotAxis = !axisByPlot[plotIndex];
|
|
58
58
|
if (firstPlotAxis) {
|
|
@@ -62,7 +62,8 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
62
62
|
const defaultAxisPosition = firstPlotAxis ? 'left' : 'right';
|
|
63
63
|
const axisPosition = get(axisItem, 'position', defaultAxisPosition);
|
|
64
64
|
const axisSeriesData = seriesData.filter((s) => get(s, 'yAxis', 0) === axisIndex);
|
|
65
|
-
const
|
|
65
|
+
const isAxisVisible = (_a = axisItem.visible) !== null && _a !== void 0 ? _a : true;
|
|
66
|
+
const labelsEnabled = isAxisVisible && get(axisItem, 'labels.enabled', true);
|
|
66
67
|
const labelsStyle = {
|
|
67
68
|
fontSize: get(axisItem, 'labels.style.fontSize', DEFAULT_AXIS_LABEL_FONT_SIZE),
|
|
68
69
|
};
|
|
@@ -70,7 +71,7 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
70
71
|
const labelsLineHeight = labelsHtml
|
|
71
72
|
? getHorizontalHtmlTextHeight({ text: 'Tmp', style: labelsStyle })
|
|
72
73
|
: getHorizontalSvgTextHeight({ text: 'Tmp', style: labelsStyle });
|
|
73
|
-
const titleText = get(axisItem, 'title.text', '');
|
|
74
|
+
const titleText = isAxisVisible ? get(axisItem, 'title.text', '') : '';
|
|
74
75
|
const titleStyle = Object.assign(Object.assign({}, yAxisTitleDefaults.style), get(axisItem, 'title.style'));
|
|
75
76
|
const titleMaxRowsCount = get(axisItem, 'title.maxRowCount', yAxisTitleDefaults.maxRowCount);
|
|
76
77
|
const estimatedTitleRows = (await wrapText({
|
|
@@ -78,24 +79,23 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
78
79
|
style: titleStyle,
|
|
79
80
|
width: height,
|
|
80
81
|
})).slice(0, titleMaxRowsCount);
|
|
81
|
-
const titleRotation = getAxisTitleRotation((
|
|
82
|
+
const titleRotation = getAxisTitleRotation((_b = axisItem.title) === null || _b === void 0 ? void 0 : _b.rotation, axisPosition);
|
|
82
83
|
const titleMaxWidth = titleRotation === 0
|
|
83
84
|
? calculateNumericProperty({
|
|
84
|
-
value: (
|
|
85
|
+
value: (_d = (_c = axisItem.title) === null || _c === void 0 ? void 0 : _c.maxWidth) !== null && _d !== void 0 ? _d : '20%',
|
|
85
86
|
base: width,
|
|
86
87
|
})
|
|
87
88
|
: calculateNumericProperty({
|
|
88
|
-
value: (
|
|
89
|
+
value: (_f = (_e = axisItem.title) === null || _e === void 0 ? void 0 : _e.maxWidth) !== null && _f !== void 0 ? _f : '100%',
|
|
89
90
|
base: height,
|
|
90
91
|
});
|
|
91
92
|
const titleSize = await getLabelsSize({
|
|
92
93
|
labels: [titleText],
|
|
93
94
|
style: titleStyle,
|
|
94
|
-
html: (
|
|
95
|
+
html: (_g = axisItem.title) === null || _g === void 0 ? void 0 : _g.html,
|
|
95
96
|
});
|
|
96
97
|
const axisType = get(axisItem, 'type', DEFAULT_AXIS_TYPE);
|
|
97
|
-
const shouldHideGrid =
|
|
98
|
-
axisSeriesData.some((s) => s.type === SERIES_TYPE.Heatmap);
|
|
98
|
+
const shouldHideGrid = !isAxisVisible || axisSeriesData.some((s) => s.type === SERIES_TYPE.Heatmap);
|
|
99
99
|
let gridEnabled;
|
|
100
100
|
if (shouldHideGrid) {
|
|
101
101
|
gridEnabled = false;
|
|
@@ -108,7 +108,7 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
108
108
|
else {
|
|
109
109
|
gridEnabled = shouldSyncAxisWithPrimary(axisItem, axisByPlot[plotIndex][0])
|
|
110
110
|
? false
|
|
111
|
-
: !((
|
|
111
|
+
: !((_h = axisByPlot[plotIndex][0].visible) !== null && _h !== void 0 ? _h : true);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
const preparedAxis = {
|
|
@@ -126,7 +126,7 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
126
126
|
width: 0,
|
|
127
127
|
height: 0,
|
|
128
128
|
lineHeight: labelsLineHeight,
|
|
129
|
-
maxWidth: (
|
|
129
|
+
maxWidth: (_k = calculateNumericProperty({ base: width, value: (_j = axisItem.labels) === null || _j === void 0 ? void 0 : _j.maxWidth })) !== null && _k !== void 0 ? _k : axisLabelsDefaults.maxWidth,
|
|
130
130
|
html: labelsHtml,
|
|
131
131
|
},
|
|
132
132
|
lineColor: get(axisItem, 'lineColor'),
|
|
@@ -140,11 +140,11 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
140
140
|
height: titleSize.maxHeight * estimatedTitleRows.length,
|
|
141
141
|
align: get(axisItem, 'title.align', yAxisTitleDefaults.align),
|
|
142
142
|
maxRowCount: titleMaxRowsCount,
|
|
143
|
-
html: (
|
|
143
|
+
html: (_m = (_l = axisItem.title) === null || _l === void 0 ? void 0 : _l.html) !== null && _m !== void 0 ? _m : false,
|
|
144
144
|
maxWidth: titleMaxWidth !== null && titleMaxWidth !== void 0 ? titleMaxWidth : Infinity,
|
|
145
145
|
rotation: titleRotation,
|
|
146
146
|
},
|
|
147
|
-
min: (
|
|
147
|
+
min: (_o = get(axisItem, 'min')) !== null && _o !== void 0 ? _o : getDefaultMinYAxisValue(axisSeriesData),
|
|
148
148
|
max: get(axisItem, 'max'),
|
|
149
149
|
startOnTick: get(axisItem, 'startOnTick'),
|
|
150
150
|
endOnTick: get(axisItem, 'endOnTick'),
|
|
@@ -153,15 +153,16 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
153
153
|
enabled: gridEnabled,
|
|
154
154
|
},
|
|
155
155
|
ticks: {
|
|
156
|
-
pixelInterval: ((
|
|
156
|
+
pixelInterval: ((_p = axisItem.ticks) === null || _p === void 0 ? void 0 : _p.interval)
|
|
157
157
|
? calculateNumericProperty({
|
|
158
158
|
base: height,
|
|
159
|
-
value: (
|
|
159
|
+
value: (_q = axisItem.ticks) === null || _q === void 0 ? void 0 : _q.interval,
|
|
160
160
|
})
|
|
161
|
-
: (
|
|
161
|
+
: (_r = axisItem.ticks) === null || _r === void 0 ? void 0 : _r.pixelInterval,
|
|
162
162
|
},
|
|
163
163
|
tickMarks: {
|
|
164
|
-
enabled:
|
|
164
|
+
enabled: isAxisVisible &&
|
|
165
|
+
get(axisItem, 'tickMarks.enabled', axisTickMarksDefaults.enabled),
|
|
165
166
|
length: get(axisItem, 'tickMarks.length', axisTickMarksDefaults.length),
|
|
166
167
|
},
|
|
167
168
|
position: axisPosition,
|
|
@@ -101,7 +101,7 @@ async function getGroupedLegendItems(args) {
|
|
|
101
101
|
const item = items[i];
|
|
102
102
|
const resultItem = clone(item);
|
|
103
103
|
resultItem.text = item.name;
|
|
104
|
-
const maxTextWidth = maxLegendWidth - resultItem.symbol.
|
|
104
|
+
const maxTextWidth = maxLegendWidth - resultItem.symbol.bboxWidth - resultItem.symbol.padding;
|
|
105
105
|
let textHeight = 0;
|
|
106
106
|
let textWidth = 0;
|
|
107
107
|
if (preparedLegend.html) {
|
|
@@ -143,7 +143,7 @@ async function getGroupedLegendItems(args) {
|
|
|
143
143
|
}
|
|
144
144
|
result[lineIndex].push(resultItem);
|
|
145
145
|
const symbolsWidth = result[lineIndex].reduce((acc, { symbol }) => {
|
|
146
|
-
return acc + symbol.
|
|
146
|
+
return acc + symbol.bboxWidth + symbol.padding;
|
|
147
147
|
}, 0);
|
|
148
148
|
const distancesWidth = (result[lineIndex].length - 1) * preparedLegend.itemDistance;
|
|
149
149
|
const isOverflowedAsOnlyItemInLine = resultItem.overflowed && result[lineIndex].length === 1;
|
|
@@ -22,9 +22,11 @@ function prepareLineLegendSymbol(series, seriesOptions) {
|
|
|
22
22
|
var _a;
|
|
23
23
|
const symbolOptions = ((_a = series.legend) === null || _a === void 0 ? void 0 : _a.symbol) || {};
|
|
24
24
|
const defaultLineWidth = get(seriesOptions, 'line.lineWidth', DEFAULT_LINE_WIDTH);
|
|
25
|
+
const width = (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE;
|
|
25
26
|
return {
|
|
26
27
|
shape: 'path',
|
|
27
|
-
width:
|
|
28
|
+
width: width,
|
|
29
|
+
bboxWidth: width,
|
|
28
30
|
padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) || DEFAULT_LEGEND_SYMBOL_PADDING,
|
|
29
31
|
strokeWidth: get(series, 'lineWidth', defaultLineWidth),
|
|
30
32
|
};
|
|
@@ -11,7 +11,9 @@ export type SymbolLegendSymbol = {
|
|
|
11
11
|
shape: 'symbol';
|
|
12
12
|
symbolType: `${SymbolType}`;
|
|
13
13
|
} & Required<SymbolLegendSymbolOptions>;
|
|
14
|
-
export type PreparedLegendSymbol = RectLegendSymbol | PathLegendSymbol | SymbolLegendSymbol
|
|
14
|
+
export type PreparedLegendSymbol = (RectLegendSymbol | PathLegendSymbol | SymbolLegendSymbol) & {
|
|
15
|
+
bboxWidth: number;
|
|
16
|
+
};
|
|
15
17
|
export type PreparedLegend = Required<Omit<ChartLegend, 'title' | 'colorScale'>> & {
|
|
16
18
|
height: number;
|
|
17
19
|
lineHeight: number;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import memoize from 'lodash/memoize';
|
|
2
2
|
import { SymbolType } from '../../constants';
|
|
3
|
+
import { getSymbolBBoxWidth } from '../../utils';
|
|
3
4
|
import { getUniqId } from '../../utils/misc';
|
|
4
5
|
import { DEFAULT_LEGEND_SYMBOL_PADDING, DEFAULT_LEGEND_SYMBOL_SIZE } from './constants';
|
|
5
6
|
export const getActiveLegendItems = (series) => {
|
|
@@ -16,10 +17,13 @@ export const getAllLegendItems = (series) => {
|
|
|
16
17
|
export function prepareLegendSymbol(series, symbolType) {
|
|
17
18
|
var _a;
|
|
18
19
|
const symbolOptions = ((_a = series.legend) === null || _a === void 0 ? void 0 : _a.symbol) || {};
|
|
20
|
+
const width = (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE;
|
|
21
|
+
const type = symbolType || SymbolType.Circle;
|
|
19
22
|
return {
|
|
20
23
|
shape: 'symbol',
|
|
21
|
-
symbolType:
|
|
22
|
-
width
|
|
24
|
+
symbolType: type,
|
|
25
|
+
width,
|
|
26
|
+
bboxWidth: getSymbolBBoxWidth({ symbolSize: Math.pow(width, 2), symbolType: type }),
|
|
23
27
|
padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) || DEFAULT_LEGEND_SYMBOL_PADDING,
|
|
24
28
|
};
|
|
25
29
|
}
|
|
@@ -97,6 +97,9 @@ export interface ChartAxisTitle {
|
|
|
97
97
|
}
|
|
98
98
|
export interface ChartAxisTickMarks {
|
|
99
99
|
/** Enable or disable the tick marks on the axis.
|
|
100
|
+
*
|
|
101
|
+
* Note: tick marks are always hidden when the axis `visible` is set to `false`.
|
|
102
|
+
*
|
|
100
103
|
* @default false
|
|
101
104
|
*/
|
|
102
105
|
enabled?: boolean;
|
|
@@ -172,9 +175,12 @@ export interface ChartAxis {
|
|
|
172
175
|
plotLines?: AxisPlotLine[];
|
|
173
176
|
/** An array of colored bands stretching across the plot area marking an interval on the axis. */
|
|
174
177
|
plotBands?: AxisPlotBand[];
|
|
175
|
-
/** Small perpendicular marks on the axis line at each tick position.
|
|
178
|
+
/** Small perpendicular marks on the axis line at each tick position.
|
|
179
|
+
*
|
|
180
|
+
* Hidden when the axis `visible` is set to `false`.
|
|
181
|
+
*/
|
|
176
182
|
tickMarks?: ChartAxisTickMarks;
|
|
177
|
-
/** Whether axis, including axis title, line,
|
|
183
|
+
/** Whether axis, including axis title, line, tick marks, and labels, should be visible. */
|
|
178
184
|
visible?: boolean;
|
|
179
185
|
/** Setting the order of the axis values. It is not applied by default.
|
|
180
186
|
* the "reverse" value is needed to use the reverse order without sorting
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { SymbolType } from '../../constants';
|
|
2
2
|
export declare const getSymbolType: (index: number) => SymbolType;
|
|
3
3
|
export declare const getSymbol: (symbolType: `${SymbolType}`) => import("d3-shape").SymbolType;
|
|
4
|
+
export declare function getSymbolBBoxWidth({ symbolSize, symbolType, }: {
|
|
5
|
+
symbolSize: number;
|
|
6
|
+
symbolType: `${SymbolType}`;
|
|
7
|
+
}): number;
|
|
@@ -34,3 +34,18 @@ export const getSymbol = (symbolType) => {
|
|
|
34
34
|
return symbolCircle;
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
+
export function getSymbolBBoxWidth({ symbolSize, symbolType, }) {
|
|
38
|
+
switch (symbolType) {
|
|
39
|
+
case SymbolType.Diamond:
|
|
40
|
+
return Math.sqrt(symbolSize * 2);
|
|
41
|
+
case SymbolType.Circle:
|
|
42
|
+
return Math.sqrt(symbolSize / Math.PI) * 2;
|
|
43
|
+
case SymbolType.Square:
|
|
44
|
+
return Math.sqrt(symbolSize);
|
|
45
|
+
case SymbolType.Triangle:
|
|
46
|
+
case SymbolType.TriangleDown:
|
|
47
|
+
return Math.sqrt((4 * symbolSize * Math.sqrt(3)) / 3);
|
|
48
|
+
default:
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Dispatch } from 'd3';
|
|
3
|
-
import type {
|
|
3
|
+
import type { PreparedChart, PreparedLegend, RangeSliderState, ZoomState } from '../../hooks';
|
|
4
4
|
import type { LegendConfig } from '../../types';
|
|
5
5
|
import type { ChartInnerProps } from './types';
|
|
6
6
|
type Props = ChartInnerProps & {
|
|
@@ -13,19 +13,8 @@ type Props = ChartInnerProps & {
|
|
|
13
13
|
zoomState: Partial<ZoomState>;
|
|
14
14
|
rangeSliderState?: RangeSliderState;
|
|
15
15
|
};
|
|
16
|
-
type LegendState = {
|
|
17
|
-
legendConfig?: LegendConfig;
|
|
18
|
-
legendItems: LegendItem[][];
|
|
19
|
-
};
|
|
20
|
-
export declare function useLegend({ preparedLegend, preparedChart, preparedSeries, width, height, }: {
|
|
21
|
-
preparedLegend: PreparedLegend | null;
|
|
22
|
-
preparedChart: PreparedChart;
|
|
23
|
-
preparedSeries: PreparedSeries[];
|
|
24
|
-
width: number;
|
|
25
|
-
height: number;
|
|
26
|
-
}): LegendState;
|
|
27
16
|
export declare function useChartInnerProps(props: Props): {
|
|
28
|
-
allPreparedSeries: PreparedSeries[];
|
|
17
|
+
allPreparedSeries: import("../../hooks").PreparedSeries[];
|
|
29
18
|
boundsHeight: number;
|
|
30
19
|
boundsOffsetLeft: number;
|
|
31
20
|
boundsOffsetTop: number;
|
|
@@ -33,9 +22,9 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
33
22
|
handleLegendItemClick: import("../../hooks").OnLegendItemClick;
|
|
34
23
|
isOutsideBounds: (x: number, y: number) => boolean;
|
|
35
24
|
legendConfig: LegendConfig | undefined;
|
|
36
|
-
legendItems: LegendItem[][];
|
|
25
|
+
legendItems: import("../../hooks").LegendItem[][];
|
|
37
26
|
preparedLegend: PreparedLegend | null;
|
|
38
|
-
preparedSeries: PreparedSeries[];
|
|
27
|
+
preparedSeries: import("../../hooks").PreparedSeries[];
|
|
39
28
|
preparedSeriesOptions: import("../../constants").SeriesOptionsDefaults;
|
|
40
29
|
preparedSplit: import("../../hooks").PreparedSplit;
|
|
41
30
|
prevHeight: number | undefined;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import isEqual from 'lodash/isEqual';
|
|
3
2
|
import { DEFAULT_PALETTE, SERIES_TYPE } from '../../constants';
|
|
4
3
|
import { useAxis, useAxisScales, useChartDimensions, useNormalizedOriginalData, usePrevious, useSeries, useShapes, useSplit, useYAxisLabelWidth, useZoom, } from '../../hooks';
|
|
5
4
|
import { getYAxisWidth } from '../../hooks/useChartDimensions/utils';
|
|
6
|
-
import { getLegendComponents } from '../../hooks/useSeries/prepare-legend';
|
|
7
5
|
import { getPreparedOptions } from '../../hooks/useSeries/prepare-options';
|
|
8
6
|
import { getEffectiveXRange, getZoomedSeriesData } from '../../utils';
|
|
7
|
+
import { useLegend } from './useLegend';
|
|
9
8
|
import { hasAtLeastOneSeriesDataPerPlot } from './utils';
|
|
10
9
|
const CLIP_PATH_BY_SERIES_TYPE = {
|
|
11
10
|
[SERIES_TYPE.Scatter]: false,
|
|
@@ -35,45 +34,6 @@ function getBoundsOffsetLeft(args) {
|
|
|
35
34
|
}, 0);
|
|
36
35
|
return chartMarginLeft + legendOffset + leftAxisWidth;
|
|
37
36
|
}
|
|
38
|
-
export function useLegend({ preparedLegend, preparedChart, preparedSeries, width, height, }) {
|
|
39
|
-
const [legendState, setLegend] = React.useState({
|
|
40
|
-
legendConfig: undefined,
|
|
41
|
-
legendItems: [],
|
|
42
|
-
});
|
|
43
|
-
const legendStateRunRef = React.useRef(0);
|
|
44
|
-
const prevLegendStateValue = React.useRef(legendState);
|
|
45
|
-
const legendStateReady = React.useRef(false);
|
|
46
|
-
React.useEffect(() => {
|
|
47
|
-
legendStateRunRef.current++;
|
|
48
|
-
legendStateReady.current = false;
|
|
49
|
-
(async function () {
|
|
50
|
-
const currentRun = legendStateRunRef.current;
|
|
51
|
-
if (!preparedLegend) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const newStateValue = await getLegendComponents({
|
|
55
|
-
chartWidth: width,
|
|
56
|
-
chartHeight: height,
|
|
57
|
-
chartMargin: preparedChart.margin,
|
|
58
|
-
series: preparedSeries,
|
|
59
|
-
preparedLegend,
|
|
60
|
-
});
|
|
61
|
-
if (legendStateRunRef.current === currentRun) {
|
|
62
|
-
if (!isEqual(prevLegendStateValue.current, newStateValue)) {
|
|
63
|
-
setLegend(newStateValue);
|
|
64
|
-
prevLegendStateValue.current = newStateValue;
|
|
65
|
-
}
|
|
66
|
-
legendStateReady.current = true;
|
|
67
|
-
}
|
|
68
|
-
})();
|
|
69
|
-
}, [height, preparedChart.margin, preparedLegend, preparedSeries, width]);
|
|
70
|
-
return legendStateReady.current
|
|
71
|
-
? legendState
|
|
72
|
-
: {
|
|
73
|
-
legendConfig: undefined,
|
|
74
|
-
legendItems: [],
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
37
|
export function useChartInnerProps(props) {
|
|
78
38
|
const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, preparedChart, rangeSliderState, width, updateZoomState, zoomState, } = props;
|
|
79
39
|
const prevWidth = usePrevious(width);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LegendItem, PreparedChart, PreparedLegend, PreparedSeries } from '../../hooks';
|
|
2
|
+
import type { LegendConfig } from '../../types';
|
|
3
|
+
type LegendState = {
|
|
4
|
+
legendConfig?: LegendConfig;
|
|
5
|
+
legendItems: LegendItem[][];
|
|
6
|
+
};
|
|
7
|
+
export declare function useLegend({ preparedLegend, preparedChart, preparedSeries, width, height, }: {
|
|
8
|
+
preparedLegend: PreparedLegend | null;
|
|
9
|
+
preparedChart: PreparedChart;
|
|
10
|
+
preparedSeries: PreparedSeries[];
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
}): LegendState;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
3
|
+
import { getLegendComponents } from '../../hooks/useSeries/prepare-legend';
|
|
4
|
+
export function useLegend({ preparedLegend, preparedChart, preparedSeries, width, height, }) {
|
|
5
|
+
const [legendState, setLegend] = React.useState({
|
|
6
|
+
legendConfig: undefined,
|
|
7
|
+
legendItems: [],
|
|
8
|
+
});
|
|
9
|
+
const legendStateRunRef = React.useRef(0);
|
|
10
|
+
const prevLegendStateValue = React.useRef(legendState);
|
|
11
|
+
const legendStateReady = React.useRef(false);
|
|
12
|
+
React.useEffect(() => {
|
|
13
|
+
legendStateRunRef.current++;
|
|
14
|
+
legendStateReady.current = false;
|
|
15
|
+
(async function () {
|
|
16
|
+
const currentRun = legendStateRunRef.current;
|
|
17
|
+
if (!preparedLegend) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const newStateValue = await getLegendComponents({
|
|
21
|
+
chartWidth: width,
|
|
22
|
+
chartHeight: height,
|
|
23
|
+
chartMargin: preparedChart.margin,
|
|
24
|
+
series: preparedSeries,
|
|
25
|
+
preparedLegend,
|
|
26
|
+
});
|
|
27
|
+
if (legendStateRunRef.current === currentRun) {
|
|
28
|
+
if (!isEqual(prevLegendStateValue.current, newStateValue)) {
|
|
29
|
+
setLegend(newStateValue);
|
|
30
|
+
prevLegendStateValue.current = newStateValue;
|
|
31
|
+
}
|
|
32
|
+
legendStateReady.current = true;
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
35
|
+
}, [height, preparedChart.margin, preparedLegend, preparedSeries, width]);
|
|
36
|
+
return legendState;
|
|
37
|
+
}
|
|
@@ -71,7 +71,7 @@ function renderLegendSymbol(args) {
|
|
|
71
71
|
const getXPosition = (i) => {
|
|
72
72
|
return line.slice(0, i).reduce((acc, legendItem) => {
|
|
73
73
|
return (acc +
|
|
74
|
-
legendItem.symbol.
|
|
74
|
+
legendItem.symbol.bboxWidth +
|
|
75
75
|
legendItem.symbol.padding +
|
|
76
76
|
legendItem.textWidth +
|
|
77
77
|
legend.itemDistance);
|
|
@@ -82,13 +82,15 @@ function renderLegendSymbol(args) {
|
|
|
82
82
|
const x = getXPosition(i);
|
|
83
83
|
const className = b('item-symbol', { shape: d.symbol.shape, unselected: !d.visible });
|
|
84
84
|
const color = d.visible ? d.color : '';
|
|
85
|
+
const symbolType = d.symbol.symbolType;
|
|
86
|
+
const scatterSymbol = getSymbol(symbolType);
|
|
85
87
|
switch (d.symbol.shape) {
|
|
86
88
|
case 'path': {
|
|
87
89
|
appendLinePathElement({
|
|
88
90
|
svgRootElement: element.node(),
|
|
89
91
|
x,
|
|
90
92
|
height: legendLineHeight,
|
|
91
|
-
width: d.symbol.
|
|
93
|
+
width: d.symbol.bboxWidth,
|
|
92
94
|
color,
|
|
93
95
|
className,
|
|
94
96
|
dashStyle: d.dashStyle,
|
|
@@ -102,7 +104,7 @@ function renderLegendSymbol(args) {
|
|
|
102
104
|
.append('rect')
|
|
103
105
|
.attr('x', x)
|
|
104
106
|
.attr('y', y)
|
|
105
|
-
.attr('width', d.symbol.
|
|
107
|
+
.attr('width', d.symbol.bboxWidth)
|
|
106
108
|
.attr('height', d.symbol.height)
|
|
107
109
|
.attr('rx', d.symbol.radius)
|
|
108
110
|
.attr('class', className)
|
|
@@ -110,18 +112,14 @@ function renderLegendSymbol(args) {
|
|
|
110
112
|
break;
|
|
111
113
|
}
|
|
112
114
|
case 'symbol': {
|
|
115
|
+
const symbolAreaSize = Math.pow(d.symbol.width, 2);
|
|
113
116
|
const y = legendLineHeight / 2;
|
|
117
|
+
const bboxWidth = d.symbol.bboxWidth;
|
|
114
118
|
element
|
|
115
119
|
.append('svg:path')
|
|
116
|
-
.attr('d', () =>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// https://d3js.org/d3-shape/symbol#symbol
|
|
120
|
-
return symbol(scatterSymbol, d.symbol.width * d.symbol.width)();
|
|
121
|
-
})
|
|
122
|
-
.attr('transform', (_d, _index, elements) => {
|
|
123
|
-
var _a, _b;
|
|
124
|
-
const translateX = x + ((_b = (_a = elements[0]) === null || _a === void 0 ? void 0 : _a.getBBox()) === null || _b === void 0 ? void 0 : _b.width) / 2;
|
|
120
|
+
.attr('d', () => symbol(scatterSymbol, symbolAreaSize)())
|
|
121
|
+
.attr('transform', () => {
|
|
122
|
+
const translateX = x + bboxWidth / 2;
|
|
125
123
|
return 'translate(' + translateX + ',' + y + ')';
|
|
126
124
|
})
|
|
127
125
|
.attr('class', className)
|
|
@@ -140,7 +138,7 @@ export const Legend = (props) => {
|
|
|
140
138
|
}, [config.maxWidth]);
|
|
141
139
|
React.useEffect(() => {
|
|
142
140
|
async function prepareLegend() {
|
|
143
|
-
var _a, _b, _c, _e, _f, _g, _h
|
|
141
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
144
142
|
if (!ref.current || !htmlLayout) {
|
|
145
143
|
return;
|
|
146
144
|
}
|
|
@@ -157,7 +155,7 @@ export const Legend = (props) => {
|
|
|
157
155
|
let legendTop = 0;
|
|
158
156
|
if (legend.type === 'discrete') {
|
|
159
157
|
const start = (_b = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.pages[pageIndex]) === null || _b === void 0 ? void 0 : _b.start;
|
|
160
|
-
const end = (
|
|
158
|
+
const end = (_d = (_c = config.pagination) === null || _c === void 0 ? void 0 : _c.pages[pageIndex]) === null || _d === void 0 ? void 0 : _d.end;
|
|
161
159
|
const pageItems = typeof start === 'number' && typeof end === 'number'
|
|
162
160
|
? items.slice(start, end)
|
|
163
161
|
: items;
|
|
@@ -178,7 +176,7 @@ export const Legend = (props) => {
|
|
|
178
176
|
const getXPosition = (i) => {
|
|
179
177
|
return line.slice(0, i).reduce((acc, legendItem) => {
|
|
180
178
|
return (acc +
|
|
181
|
-
legendItem.symbol.
|
|
179
|
+
legendItem.symbol.bboxWidth +
|
|
182
180
|
legendItem.symbol.padding +
|
|
183
181
|
legendItem.textWidth +
|
|
184
182
|
legend.itemDistance);
|
|
@@ -202,7 +200,7 @@ export const Legend = (props) => {
|
|
|
202
200
|
return `${d.textWidth}px`;
|
|
203
201
|
})
|
|
204
202
|
.style('left', function (d, i) {
|
|
205
|
-
return `${getXPosition(i) + d.symbol.
|
|
203
|
+
return `${getXPosition(i) + d.symbol.bboxWidth + d.symbol.padding}px`;
|
|
206
204
|
})
|
|
207
205
|
.style('top', function (d) {
|
|
208
206
|
if (d.height < legendLineHeight) {
|
|
@@ -221,7 +219,7 @@ export const Legend = (props) => {
|
|
|
221
219
|
.append('text')
|
|
222
220
|
.attr('x', function (legendItem, i) {
|
|
223
221
|
return (getXPosition(i) +
|
|
224
|
-
legendItem.symbol.
|
|
222
|
+
legendItem.symbol.bboxWidth +
|
|
225
223
|
legendItem.symbol.padding);
|
|
226
224
|
})
|
|
227
225
|
.attr('height', legend.height)
|
|
@@ -238,7 +236,7 @@ export const Legend = (props) => {
|
|
|
238
236
|
}
|
|
239
237
|
else {
|
|
240
238
|
contentWidth = line.reduce((sum, l, index) => {
|
|
241
|
-
sum += l.textWidth + l.symbol.
|
|
239
|
+
sum += l.textWidth + l.symbol.bboxWidth + l.symbol.padding;
|
|
242
240
|
if (index > 0) {
|
|
243
241
|
sum += legend.itemDistance;
|
|
244
242
|
}
|
|
@@ -311,7 +309,7 @@ export const Legend = (props) => {
|
|
|
311
309
|
legendLeft = left;
|
|
312
310
|
legendTop = top;
|
|
313
311
|
// gradient rect
|
|
314
|
-
const domain = (
|
|
312
|
+
const domain = (_e = legend.colorScale.domain) !== null && _e !== void 0 ? _e : [];
|
|
315
313
|
const rectHeight = CONTINUOUS_LEGEND_SIZE.height;
|
|
316
314
|
svgElement.call(createGradientRect, {
|
|
317
315
|
y: legend.title.height + legend.title.margin,
|
|
@@ -380,9 +378,9 @@ export const Legend = (props) => {
|
|
|
380
378
|
.attr('class', legendTitleClassname)
|
|
381
379
|
.append('text')
|
|
382
380
|
.attr('dx', dx)
|
|
383
|
-
.attr('font-weight', (
|
|
384
|
-
.attr('font-size', (
|
|
385
|
-
.attr('fill', (
|
|
381
|
+
.attr('font-weight', (_f = legend.title.style.fontWeight) !== null && _f !== void 0 ? _f : null)
|
|
382
|
+
.attr('font-size', (_g = legend.title.style.fontSize) !== null && _g !== void 0 ? _g : null)
|
|
383
|
+
.attr('fill', (_h = legend.title.style.fontColor) !== null && _h !== void 0 ? _h : null)
|
|
386
384
|
.style('dominant-baseline', 'text-before-edge')
|
|
387
385
|
.html(legend.title.text);
|
|
388
386
|
}
|
|
@@ -142,7 +142,7 @@ export const getPreparedXAxis = async ({ xAxis, seriesData, width, boundsWidth,
|
|
|
142
142
|
: (_j = xAxis === null || xAxis === void 0 ? void 0 : xAxis.ticks) === null || _j === void 0 ? void 0 : _j.pixelInterval,
|
|
143
143
|
},
|
|
144
144
|
tickMarks: {
|
|
145
|
-
enabled: get(xAxis, 'tickMarks.enabled', axisTickMarksDefaults.enabled),
|
|
145
|
+
enabled: isAxisVisible && get(xAxis, 'tickMarks.enabled', axisTickMarksDefaults.enabled),
|
|
146
146
|
length: get(xAxis, 'tickMarks.length', axisTickMarksDefaults.length),
|
|
147
147
|
},
|
|
148
148
|
position: 'bottom',
|
|
@@ -52,7 +52,7 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
52
52
|
return Promise.all(
|
|
53
53
|
// eslint-disable-next-line complexity
|
|
54
54
|
axisItems.map(async (axisItem, axisIndex) => {
|
|
55
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
55
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
56
56
|
const plotIndex = get(axisItem, 'plotIndex', 0);
|
|
57
57
|
const firstPlotAxis = !axisByPlot[plotIndex];
|
|
58
58
|
if (firstPlotAxis) {
|
|
@@ -62,7 +62,8 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
62
62
|
const defaultAxisPosition = firstPlotAxis ? 'left' : 'right';
|
|
63
63
|
const axisPosition = get(axisItem, 'position', defaultAxisPosition);
|
|
64
64
|
const axisSeriesData = seriesData.filter((s) => get(s, 'yAxis', 0) === axisIndex);
|
|
65
|
-
const
|
|
65
|
+
const isAxisVisible = (_a = axisItem.visible) !== null && _a !== void 0 ? _a : true;
|
|
66
|
+
const labelsEnabled = isAxisVisible && get(axisItem, 'labels.enabled', true);
|
|
66
67
|
const labelsStyle = {
|
|
67
68
|
fontSize: get(axisItem, 'labels.style.fontSize', DEFAULT_AXIS_LABEL_FONT_SIZE),
|
|
68
69
|
};
|
|
@@ -70,7 +71,7 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
70
71
|
const labelsLineHeight = labelsHtml
|
|
71
72
|
? getHorizontalHtmlTextHeight({ text: 'Tmp', style: labelsStyle })
|
|
72
73
|
: getHorizontalSvgTextHeight({ text: 'Tmp', style: labelsStyle });
|
|
73
|
-
const titleText = get(axisItem, 'title.text', '');
|
|
74
|
+
const titleText = isAxisVisible ? get(axisItem, 'title.text', '') : '';
|
|
74
75
|
const titleStyle = Object.assign(Object.assign({}, yAxisTitleDefaults.style), get(axisItem, 'title.style'));
|
|
75
76
|
const titleMaxRowsCount = get(axisItem, 'title.maxRowCount', yAxisTitleDefaults.maxRowCount);
|
|
76
77
|
const estimatedTitleRows = (await wrapText({
|
|
@@ -78,24 +79,23 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
78
79
|
style: titleStyle,
|
|
79
80
|
width: height,
|
|
80
81
|
})).slice(0, titleMaxRowsCount);
|
|
81
|
-
const titleRotation = getAxisTitleRotation((
|
|
82
|
+
const titleRotation = getAxisTitleRotation((_b = axisItem.title) === null || _b === void 0 ? void 0 : _b.rotation, axisPosition);
|
|
82
83
|
const titleMaxWidth = titleRotation === 0
|
|
83
84
|
? calculateNumericProperty({
|
|
84
|
-
value: (
|
|
85
|
+
value: (_d = (_c = axisItem.title) === null || _c === void 0 ? void 0 : _c.maxWidth) !== null && _d !== void 0 ? _d : '20%',
|
|
85
86
|
base: width,
|
|
86
87
|
})
|
|
87
88
|
: calculateNumericProperty({
|
|
88
|
-
value: (
|
|
89
|
+
value: (_f = (_e = axisItem.title) === null || _e === void 0 ? void 0 : _e.maxWidth) !== null && _f !== void 0 ? _f : '100%',
|
|
89
90
|
base: height,
|
|
90
91
|
});
|
|
91
92
|
const titleSize = await getLabelsSize({
|
|
92
93
|
labels: [titleText],
|
|
93
94
|
style: titleStyle,
|
|
94
|
-
html: (
|
|
95
|
+
html: (_g = axisItem.title) === null || _g === void 0 ? void 0 : _g.html,
|
|
95
96
|
});
|
|
96
97
|
const axisType = get(axisItem, 'type', DEFAULT_AXIS_TYPE);
|
|
97
|
-
const shouldHideGrid =
|
|
98
|
-
axisSeriesData.some((s) => s.type === SERIES_TYPE.Heatmap);
|
|
98
|
+
const shouldHideGrid = !isAxisVisible || axisSeriesData.some((s) => s.type === SERIES_TYPE.Heatmap);
|
|
99
99
|
let gridEnabled;
|
|
100
100
|
if (shouldHideGrid) {
|
|
101
101
|
gridEnabled = false;
|
|
@@ -108,7 +108,7 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
108
108
|
else {
|
|
109
109
|
gridEnabled = shouldSyncAxisWithPrimary(axisItem, axisByPlot[plotIndex][0])
|
|
110
110
|
? false
|
|
111
|
-
: !((
|
|
111
|
+
: !((_h = axisByPlot[plotIndex][0].visible) !== null && _h !== void 0 ? _h : true);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
const preparedAxis = {
|
|
@@ -126,7 +126,7 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
126
126
|
width: 0,
|
|
127
127
|
height: 0,
|
|
128
128
|
lineHeight: labelsLineHeight,
|
|
129
|
-
maxWidth: (
|
|
129
|
+
maxWidth: (_k = calculateNumericProperty({ base: width, value: (_j = axisItem.labels) === null || _j === void 0 ? void 0 : _j.maxWidth })) !== null && _k !== void 0 ? _k : axisLabelsDefaults.maxWidth,
|
|
130
130
|
html: labelsHtml,
|
|
131
131
|
},
|
|
132
132
|
lineColor: get(axisItem, 'lineColor'),
|
|
@@ -140,11 +140,11 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
140
140
|
height: titleSize.maxHeight * estimatedTitleRows.length,
|
|
141
141
|
align: get(axisItem, 'title.align', yAxisTitleDefaults.align),
|
|
142
142
|
maxRowCount: titleMaxRowsCount,
|
|
143
|
-
html: (
|
|
143
|
+
html: (_m = (_l = axisItem.title) === null || _l === void 0 ? void 0 : _l.html) !== null && _m !== void 0 ? _m : false,
|
|
144
144
|
maxWidth: titleMaxWidth !== null && titleMaxWidth !== void 0 ? titleMaxWidth : Infinity,
|
|
145
145
|
rotation: titleRotation,
|
|
146
146
|
},
|
|
147
|
-
min: (
|
|
147
|
+
min: (_o = get(axisItem, 'min')) !== null && _o !== void 0 ? _o : getDefaultMinYAxisValue(axisSeriesData),
|
|
148
148
|
max: get(axisItem, 'max'),
|
|
149
149
|
startOnTick: get(axisItem, 'startOnTick'),
|
|
150
150
|
endOnTick: get(axisItem, 'endOnTick'),
|
|
@@ -153,15 +153,16 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
153
153
|
enabled: gridEnabled,
|
|
154
154
|
},
|
|
155
155
|
ticks: {
|
|
156
|
-
pixelInterval: ((
|
|
156
|
+
pixelInterval: ((_p = axisItem.ticks) === null || _p === void 0 ? void 0 : _p.interval)
|
|
157
157
|
? calculateNumericProperty({
|
|
158
158
|
base: height,
|
|
159
|
-
value: (
|
|
159
|
+
value: (_q = axisItem.ticks) === null || _q === void 0 ? void 0 : _q.interval,
|
|
160
160
|
})
|
|
161
|
-
: (
|
|
161
|
+
: (_r = axisItem.ticks) === null || _r === void 0 ? void 0 : _r.pixelInterval,
|
|
162
162
|
},
|
|
163
163
|
tickMarks: {
|
|
164
|
-
enabled:
|
|
164
|
+
enabled: isAxisVisible &&
|
|
165
|
+
get(axisItem, 'tickMarks.enabled', axisTickMarksDefaults.enabled),
|
|
165
166
|
length: get(axisItem, 'tickMarks.length', axisTickMarksDefaults.length),
|
|
166
167
|
},
|
|
167
168
|
position: axisPosition,
|
|
@@ -101,7 +101,7 @@ async function getGroupedLegendItems(args) {
|
|
|
101
101
|
const item = items[i];
|
|
102
102
|
const resultItem = clone(item);
|
|
103
103
|
resultItem.text = item.name;
|
|
104
|
-
const maxTextWidth = maxLegendWidth - resultItem.symbol.
|
|
104
|
+
const maxTextWidth = maxLegendWidth - resultItem.symbol.bboxWidth - resultItem.symbol.padding;
|
|
105
105
|
let textHeight = 0;
|
|
106
106
|
let textWidth = 0;
|
|
107
107
|
if (preparedLegend.html) {
|
|
@@ -143,7 +143,7 @@ async function getGroupedLegendItems(args) {
|
|
|
143
143
|
}
|
|
144
144
|
result[lineIndex].push(resultItem);
|
|
145
145
|
const symbolsWidth = result[lineIndex].reduce((acc, { symbol }) => {
|
|
146
|
-
return acc + symbol.
|
|
146
|
+
return acc + symbol.bboxWidth + symbol.padding;
|
|
147
147
|
}, 0);
|
|
148
148
|
const distancesWidth = (result[lineIndex].length - 1) * preparedLegend.itemDistance;
|
|
149
149
|
const isOverflowedAsOnlyItemInLine = resultItem.overflowed && result[lineIndex].length === 1;
|
|
@@ -22,9 +22,11 @@ function prepareLineLegendSymbol(series, seriesOptions) {
|
|
|
22
22
|
var _a;
|
|
23
23
|
const symbolOptions = ((_a = series.legend) === null || _a === void 0 ? void 0 : _a.symbol) || {};
|
|
24
24
|
const defaultLineWidth = get(seriesOptions, 'line.lineWidth', DEFAULT_LINE_WIDTH);
|
|
25
|
+
const width = (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE;
|
|
25
26
|
return {
|
|
26
27
|
shape: 'path',
|
|
27
|
-
width:
|
|
28
|
+
width: width,
|
|
29
|
+
bboxWidth: width,
|
|
28
30
|
padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) || DEFAULT_LEGEND_SYMBOL_PADDING,
|
|
29
31
|
strokeWidth: get(series, 'lineWidth', defaultLineWidth),
|
|
30
32
|
};
|
|
@@ -11,7 +11,9 @@ export type SymbolLegendSymbol = {
|
|
|
11
11
|
shape: 'symbol';
|
|
12
12
|
symbolType: `${SymbolType}`;
|
|
13
13
|
} & Required<SymbolLegendSymbolOptions>;
|
|
14
|
-
export type PreparedLegendSymbol = RectLegendSymbol | PathLegendSymbol | SymbolLegendSymbol
|
|
14
|
+
export type PreparedLegendSymbol = (RectLegendSymbol | PathLegendSymbol | SymbolLegendSymbol) & {
|
|
15
|
+
bboxWidth: number;
|
|
16
|
+
};
|
|
15
17
|
export type PreparedLegend = Required<Omit<ChartLegend, 'title' | 'colorScale'>> & {
|
|
16
18
|
height: number;
|
|
17
19
|
lineHeight: number;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import memoize from 'lodash/memoize';
|
|
2
2
|
import { SymbolType } from '../../constants';
|
|
3
|
+
import { getSymbolBBoxWidth } from '../../utils';
|
|
3
4
|
import { getUniqId } from '../../utils/misc';
|
|
4
5
|
import { DEFAULT_LEGEND_SYMBOL_PADDING, DEFAULT_LEGEND_SYMBOL_SIZE } from './constants';
|
|
5
6
|
export const getActiveLegendItems = (series) => {
|
|
@@ -16,10 +17,13 @@ export const getAllLegendItems = (series) => {
|
|
|
16
17
|
export function prepareLegendSymbol(series, symbolType) {
|
|
17
18
|
var _a;
|
|
18
19
|
const symbolOptions = ((_a = series.legend) === null || _a === void 0 ? void 0 : _a.symbol) || {};
|
|
20
|
+
const width = (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE;
|
|
21
|
+
const type = symbolType || SymbolType.Circle;
|
|
19
22
|
return {
|
|
20
23
|
shape: 'symbol',
|
|
21
|
-
symbolType:
|
|
22
|
-
width
|
|
24
|
+
symbolType: type,
|
|
25
|
+
width,
|
|
26
|
+
bboxWidth: getSymbolBBoxWidth({ symbolSize: Math.pow(width, 2), symbolType: type }),
|
|
23
27
|
padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) || DEFAULT_LEGEND_SYMBOL_PADDING,
|
|
24
28
|
};
|
|
25
29
|
}
|
|
@@ -97,6 +97,9 @@ export interface ChartAxisTitle {
|
|
|
97
97
|
}
|
|
98
98
|
export interface ChartAxisTickMarks {
|
|
99
99
|
/** Enable or disable the tick marks on the axis.
|
|
100
|
+
*
|
|
101
|
+
* Note: tick marks are always hidden when the axis `visible` is set to `false`.
|
|
102
|
+
*
|
|
100
103
|
* @default false
|
|
101
104
|
*/
|
|
102
105
|
enabled?: boolean;
|
|
@@ -172,9 +175,12 @@ export interface ChartAxis {
|
|
|
172
175
|
plotLines?: AxisPlotLine[];
|
|
173
176
|
/** An array of colored bands stretching across the plot area marking an interval on the axis. */
|
|
174
177
|
plotBands?: AxisPlotBand[];
|
|
175
|
-
/** Small perpendicular marks on the axis line at each tick position.
|
|
178
|
+
/** Small perpendicular marks on the axis line at each tick position.
|
|
179
|
+
*
|
|
180
|
+
* Hidden when the axis `visible` is set to `false`.
|
|
181
|
+
*/
|
|
176
182
|
tickMarks?: ChartAxisTickMarks;
|
|
177
|
-
/** Whether axis, including axis title, line,
|
|
183
|
+
/** Whether axis, including axis title, line, tick marks, and labels, should be visible. */
|
|
178
184
|
visible?: boolean;
|
|
179
185
|
/** Setting the order of the axis values. It is not applied by default.
|
|
180
186
|
* the "reverse" value is needed to use the reverse order without sorting
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { SymbolType } from '../../constants';
|
|
2
2
|
export declare const getSymbolType: (index: number) => SymbolType;
|
|
3
3
|
export declare const getSymbol: (symbolType: `${SymbolType}`) => import("d3-shape").SymbolType;
|
|
4
|
+
export declare function getSymbolBBoxWidth({ symbolSize, symbolType, }: {
|
|
5
|
+
symbolSize: number;
|
|
6
|
+
symbolType: `${SymbolType}`;
|
|
7
|
+
}): number;
|
|
@@ -34,3 +34,18 @@ export const getSymbol = (symbolType) => {
|
|
|
34
34
|
return symbolCircle;
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
+
export function getSymbolBBoxWidth({ symbolSize, symbolType, }) {
|
|
38
|
+
switch (symbolType) {
|
|
39
|
+
case SymbolType.Diamond:
|
|
40
|
+
return Math.sqrt(symbolSize * 2);
|
|
41
|
+
case SymbolType.Circle:
|
|
42
|
+
return Math.sqrt(symbolSize / Math.PI) * 2;
|
|
43
|
+
case SymbolType.Square:
|
|
44
|
+
return Math.sqrt(symbolSize);
|
|
45
|
+
case SymbolType.Triangle:
|
|
46
|
+
case SymbolType.TriangleDown:
|
|
47
|
+
return Math.sqrt((4 * symbolSize * Math.sqrt(3)) / 3);
|
|
48
|
+
default:
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
}
|