@gravity-ui/charts 1.37.1 → 1.38.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/index.js +3 -2
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +24 -26
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +58 -21
- package/dist/cjs/components/Legend/index.d.ts +2 -1
- package/dist/cjs/components/Legend/index.js +17 -16
- package/dist/cjs/hooks/useAxis/index.d.ts +2 -1
- package/dist/cjs/hooks/useAxis/index.js +9 -2
- package/dist/cjs/hooks/useChartDimensions/index.d.ts +2 -1
- package/dist/cjs/hooks/useChartDimensions/index.js +27 -13
- package/dist/cjs/hooks/useChartOptions/tooltip.d.ts +5 -0
- package/dist/cjs/hooks/useChartOptions/tooltip.js +2 -2
- package/dist/cjs/hooks/useRangeSlider/index.js +2 -1
- package/dist/cjs/hooks/useRangeSlider/types.d.ts +2 -1
- package/dist/cjs/hooks/useSeries/prepare-legend.d.ts +4 -2
- package/dist/cjs/hooks/useSeries/prepare-legend.js +76 -55
- package/dist/cjs/hooks/useSeries/types.d.ts +1 -13
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/types/chart-ui.d.ts +15 -0
- package/dist/cjs/utils/chart/index.js +4 -2
- package/dist/cjs/utils/chart/text.js +4 -6
- package/dist/esm/components/ChartInner/index.js +3 -2
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +17 -19
- package/dist/esm/components/ChartInner/useChartInnerProps.js +58 -21
- package/dist/esm/components/Legend/index.d.ts +2 -1
- package/dist/esm/components/Legend/index.js +17 -16
- package/dist/esm/hooks/useAxis/index.d.ts +2 -1
- package/dist/esm/hooks/useAxis/index.js +9 -2
- package/dist/esm/hooks/useChartDimensions/index.d.ts +2 -1
- package/dist/esm/hooks/useChartDimensions/index.js +27 -13
- package/dist/esm/hooks/useChartOptions/tooltip.d.ts +5 -0
- package/dist/esm/hooks/useChartOptions/tooltip.js +2 -2
- package/dist/esm/hooks/useRangeSlider/index.js +2 -1
- package/dist/esm/hooks/useRangeSlider/types.d.ts +2 -1
- package/dist/esm/hooks/useSeries/prepare-legend.d.ts +4 -2
- package/dist/esm/hooks/useSeries/prepare-legend.js +76 -55
- package/dist/esm/hooks/useSeries/types.d.ts +1 -13
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/types/chart-ui.d.ts +15 -0
- package/dist/esm/utils/chart/index.js +4 -2
- package/dist/esm/utils/chart/text.js +4 -6
- package/package.json +1 -1
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { select } from 'd3';
|
|
2
1
|
import { groupBy } from 'lodash';
|
|
3
2
|
import clone from 'lodash/clone';
|
|
4
3
|
import get from 'lodash/get';
|
|
5
4
|
import merge from 'lodash/merge';
|
|
6
5
|
import { CONTINUOUS_LEGEND_SIZE, legendDefaults } from '../../constants';
|
|
7
|
-
import { getDefaultColorStops, getDomainForContinuousColorScale, getLabelsSize } from '../../utils';
|
|
6
|
+
import { getDefaultColorStops, getDomainForContinuousColorScale, getLabelsSize, getTextSizeFn, getTextWithElipsis, } from '../../utils';
|
|
8
7
|
export async function getPreparedLegend(args) {
|
|
9
8
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
10
9
|
const { legend, series } = args;
|
|
@@ -87,68 +86,81 @@ function getFlattenLegendItems(series, preparedLegend) {
|
|
|
87
86
|
return Object.values(grouped).reduce((acc, items) => {
|
|
88
87
|
const s = items.find((item) => item.legend.enabled);
|
|
89
88
|
if (s) {
|
|
90
|
-
acc.push(Object.assign(Object.assign({}, s), { id: s.legend.groupId, name: s.legend.itemText, height: preparedLegend.lineHeight, symbol: s.legend.symbol }));
|
|
89
|
+
acc.push(Object.assign(Object.assign({}, s), { id: s.legend.groupId, name: s.legend.itemText, text: s.legend.itemText, height: preparedLegend.lineHeight, symbol: s.legend.symbol }));
|
|
91
90
|
}
|
|
92
91
|
return acc;
|
|
93
92
|
}, []);
|
|
94
93
|
}
|
|
95
|
-
function getGroupedLegendItems(args) {
|
|
94
|
+
async function getGroupedLegendItems(args) {
|
|
96
95
|
const { maxLegendWidth, items, preparedLegend } = args;
|
|
97
96
|
const result = [[]];
|
|
98
|
-
const bodySelection = select(document.body);
|
|
99
97
|
let textWidthsInLine = [0];
|
|
100
98
|
let lineIndex = 0;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
99
|
+
const getLegendItemTextSize = getTextSizeFn({ style: preparedLegend.itemStyle });
|
|
100
|
+
for (let i = 0; i < items.length; i++) {
|
|
101
|
+
const item = items[i];
|
|
102
|
+
const resultItem = clone(item);
|
|
103
|
+
resultItem.text = item.name;
|
|
104
|
+
const maxTextWidth = maxLegendWidth - resultItem.symbol.width - resultItem.symbol.padding;
|
|
105
|
+
let textHeight = 0;
|
|
106
|
+
let textWidth = 0;
|
|
107
|
+
if (preparedLegend.html) {
|
|
108
|
+
const textSize = await getLabelsSize({
|
|
109
|
+
labels: [resultItem.text],
|
|
110
|
+
html: true,
|
|
111
|
+
style: preparedLegend.itemStyle,
|
|
112
|
+
});
|
|
113
|
+
textHeight = textSize.maxHeight;
|
|
114
|
+
textWidth = textSize.maxWidth;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const textSize = await getLegendItemTextSize(resultItem.text);
|
|
118
|
+
textHeight = textSize.height;
|
|
119
|
+
textWidth = textSize.width;
|
|
120
|
+
}
|
|
121
|
+
resultItem.height = textHeight;
|
|
122
|
+
if (textWidth > maxTextWidth) {
|
|
123
|
+
if (preparedLegend.html) {
|
|
118
124
|
resultItem.overflowed = true;
|
|
119
|
-
resultItem.textWidth =
|
|
120
|
-
maxLegendWidth - resultItem.symbol.width - resultItem.symbol.padding;
|
|
125
|
+
resultItem.textWidth = maxTextWidth;
|
|
121
126
|
}
|
|
122
127
|
else {
|
|
123
|
-
resultItem.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
resultItem.text = await getTextWithElipsis({
|
|
129
|
+
text: resultItem.text,
|
|
130
|
+
getTextWidth: async (s) => (await getLegendItemTextSize(s)).width,
|
|
131
|
+
maxWidth: maxTextWidth,
|
|
132
|
+
});
|
|
133
|
+
resultItem.textWidth = (await getLegendItemTextSize(resultItem.text)).width;
|
|
129
134
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
resultItem.textWidth = textWidth;
|
|
138
|
+
}
|
|
139
|
+
textWidthsInLine.push(textWidth);
|
|
140
|
+
const textsWidth = textWidthsInLine.reduce((acc, width) => acc + width, 0);
|
|
141
|
+
if (!result[lineIndex]) {
|
|
142
|
+
result[lineIndex] = [];
|
|
143
|
+
}
|
|
144
|
+
result[lineIndex].push(resultItem);
|
|
145
|
+
const symbolsWidth = result[lineIndex].reduce((acc, { symbol }) => {
|
|
146
|
+
return acc + symbol.width + symbol.padding;
|
|
147
|
+
}, 0);
|
|
148
|
+
const distancesWidth = (result[lineIndex].length - 1) * preparedLegend.itemDistance;
|
|
149
|
+
const isOverflowedAsOnlyItemInLine = resultItem.overflowed && result[lineIndex].length === 1;
|
|
150
|
+
const isCurrentLineOverMaxWidth = maxLegendWidth < textsWidth + symbolsWidth + distancesWidth;
|
|
151
|
+
if (isOverflowedAsOnlyItemInLine) {
|
|
152
|
+
lineIndex += 1;
|
|
153
|
+
textWidthsInLine = [];
|
|
154
|
+
}
|
|
155
|
+
else if (isCurrentLineOverMaxWidth) {
|
|
156
|
+
result[lineIndex].pop();
|
|
157
|
+
lineIndex += 1;
|
|
158
|
+
textWidthsInLine = [textWidth];
|
|
159
|
+
const nextLineIndex = lineIndex;
|
|
160
|
+
result[nextLineIndex] = [];
|
|
161
|
+
result[nextLineIndex].push(resultItem);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
152
164
|
return result;
|
|
153
165
|
}
|
|
154
166
|
function getPagination(args) {
|
|
@@ -227,7 +239,7 @@ function getMaxLegendHeight(args) {
|
|
|
227
239
|
}
|
|
228
240
|
return (chartHeight - chartMargin.top - chartMargin.bottom - preparedLegend.margin) / 2;
|
|
229
241
|
}
|
|
230
|
-
export function getLegendComponents(args) {
|
|
242
|
+
export async function getLegendComponents(args) {
|
|
231
243
|
const { chartWidth, chartHeight, chartMargin, series, preparedLegend } = args;
|
|
232
244
|
const isVerticalPosition = preparedLegend.position === 'right' || preparedLegend.position === 'left';
|
|
233
245
|
const maxLegendWidth = getMaxLegendWidth({
|
|
@@ -243,7 +255,7 @@ export function getLegendComponents(args) {
|
|
|
243
255
|
isVerticalPosition,
|
|
244
256
|
});
|
|
245
257
|
const flattenLegendItems = getFlattenLegendItems(series, preparedLegend);
|
|
246
|
-
const items = getGroupedLegendItems({
|
|
258
|
+
const items = await getGroupedLegendItems({
|
|
247
259
|
maxLegendWidth,
|
|
248
260
|
items: flattenLegendItems,
|
|
249
261
|
preparedLegend,
|
|
@@ -278,5 +290,14 @@ export function getLegendComponents(args) {
|
|
|
278
290
|
legendWidth: preparedLegend.width,
|
|
279
291
|
legendHeight: preparedLegend.height,
|
|
280
292
|
});
|
|
281
|
-
return {
|
|
293
|
+
return {
|
|
294
|
+
legendConfig: {
|
|
295
|
+
offset,
|
|
296
|
+
pagination,
|
|
297
|
+
maxWidth: maxLegendWidth,
|
|
298
|
+
height: preparedLegend.height,
|
|
299
|
+
width: preparedLegend.width,
|
|
300
|
+
},
|
|
301
|
+
legendItems: items,
|
|
302
|
+
};
|
|
282
303
|
}
|
|
@@ -44,25 +44,13 @@ export type LegendItem = {
|
|
|
44
44
|
color: string;
|
|
45
45
|
height: number;
|
|
46
46
|
name: string;
|
|
47
|
+
text: string;
|
|
47
48
|
symbol: PreparedLegendSymbol;
|
|
48
49
|
textWidth: number;
|
|
49
50
|
dashStyle?: DashStyle;
|
|
50
51
|
overflowed?: boolean;
|
|
51
52
|
visible?: boolean;
|
|
52
53
|
};
|
|
53
|
-
export type LegendConfig = {
|
|
54
|
-
offset: {
|
|
55
|
-
left: number;
|
|
56
|
-
top: number;
|
|
57
|
-
};
|
|
58
|
-
maxWidth: number;
|
|
59
|
-
pagination?: {
|
|
60
|
-
pages: {
|
|
61
|
-
start: number;
|
|
62
|
-
end: number;
|
|
63
|
-
}[];
|
|
64
|
-
};
|
|
65
|
-
};
|
|
66
54
|
export type PreparedHaloOptions = {
|
|
67
55
|
enabled: boolean;
|
|
68
56
|
opacity: number;
|
package/dist/cjs/index.d.ts
CHANGED
package/dist/cjs/index.js
CHANGED
|
@@ -27,3 +27,18 @@ export interface HtmlItem {
|
|
|
27
27
|
export interface ShapeDataWithHtmlItems {
|
|
28
28
|
htmlElements: HtmlItem[];
|
|
29
29
|
}
|
|
30
|
+
export type LegendConfig = {
|
|
31
|
+
offset: {
|
|
32
|
+
left: number;
|
|
33
|
+
top: number;
|
|
34
|
+
};
|
|
35
|
+
pagination?: {
|
|
36
|
+
pages: {
|
|
37
|
+
start: number;
|
|
38
|
+
end: number;
|
|
39
|
+
}[];
|
|
40
|
+
};
|
|
41
|
+
maxWidth: number;
|
|
42
|
+
height: number;
|
|
43
|
+
width: number;
|
|
44
|
+
};
|
|
@@ -107,8 +107,10 @@ export const getDomainDataYBySeries = (series) => {
|
|
|
107
107
|
let yValue = 0;
|
|
108
108
|
const points = seriesList.map((s) => s.data).flat();
|
|
109
109
|
sortBy(points, (p) => p.index).forEach((d) => {
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
if (!d.total) {
|
|
111
|
+
yValue += Number(d.y) || 0;
|
|
112
|
+
acc.push(yValue);
|
|
113
|
+
}
|
|
112
114
|
});
|
|
113
115
|
break;
|
|
114
116
|
}
|
|
@@ -176,9 +176,6 @@ function unescapeHtml(str) {
|
|
|
176
176
|
return result.replace(value, key);
|
|
177
177
|
}, str);
|
|
178
178
|
}
|
|
179
|
-
function getCssStyle(prop, el = document.body) {
|
|
180
|
-
return window.getComputedStyle(el, null).getPropertyValue(prop);
|
|
181
|
-
}
|
|
182
179
|
let measureCanvas = null;
|
|
183
180
|
export function getTextSizeFn({ style }) {
|
|
184
181
|
var _a;
|
|
@@ -188,9 +185,10 @@ export function getTextSizeFn({ style }) {
|
|
|
188
185
|
throw new Error("Couldn't get canvas context");
|
|
189
186
|
}
|
|
190
187
|
const element = (_a = document.getElementsByClassName(b())[0]) !== null && _a !== void 0 ? _a : document.body;
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
const
|
|
188
|
+
const computedStyle = window.getComputedStyle(element, null);
|
|
189
|
+
const defaultFontFamily = computedStyle.getPropertyValue('font-family');
|
|
190
|
+
const defaultFontSize = computedStyle.getPropertyValue('font-size');
|
|
191
|
+
const defaultFontWeight = computedStyle.getPropertyValue('font-weight');
|
|
194
192
|
return async (str) => {
|
|
195
193
|
var _a, _b;
|
|
196
194
|
await document.fonts.ready;
|
|
@@ -66,7 +66,8 @@ export const ChartInner = (props) => {
|
|
|
66
66
|
const { allPreparedSeries, boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, isOutsideBounds, legendConfig, legendItems, preparedLegend, preparedSeries, preparedSeriesOptions, preparedSplit, prevHeight, prevWidth, shapes, shapesData, shapesReady, xAxis, xScale, yAxis, yScale, } = useChartInnerProps(Object.assign(Object.assign({}, props), { clipPathId,
|
|
67
67
|
dispatcher,
|
|
68
68
|
htmlLayout, plotNode: plotRef.current, preparedChart,
|
|
69
|
-
rangeSliderState,
|
|
69
|
+
rangeSliderState,
|
|
70
|
+
updateZoomState,
|
|
70
71
|
zoomState }));
|
|
71
72
|
const debouncedBoundsWidth = useDebouncedValue({
|
|
72
73
|
value: boundsWidth,
|
|
@@ -248,7 +249,7 @@ export const ChartInner = (props) => {
|
|
|
248
249
|
React.createElement("g", { ref: plotBeforeRef }),
|
|
249
250
|
shapes,
|
|
250
251
|
React.createElement("g", { ref: plotAfterRef })),
|
|
251
|
-
((_e = xAxis === null || xAxis === void 0 ? void 0 : xAxis.rangeSlider) === null || _e === void 0 ? void 0 : _e.enabled) && (React.createElement(RangeSlider, { boundsOffsetLeft: debouncedOffsetLeft, boundsWidth: debouncedBoundsWidth, height: height, htmlLayout: htmlLayout, onUpdate: updateRangeSliderState, preparedChart: preparedChart, preparedLegend: preparedLegend, preparedSeries: debouncedAllPreparedSeries, preparedSeriesOptions: preparedSeriesOptions, preparedRangeSlider: xAxis.rangeSlider, rangeSliderState: rangeSliderState, ref: rangeSliderRef, width: width, xAxis: data.xAxis, yAxis: data.yAxis })),
|
|
252
|
+
((_e = xAxis === null || xAxis === void 0 ? void 0 : xAxis.rangeSlider) === null || _e === void 0 ? void 0 : _e.enabled) && (React.createElement(RangeSlider, { boundsOffsetLeft: debouncedOffsetLeft, boundsWidth: debouncedBoundsWidth, height: height, htmlLayout: htmlLayout, onUpdate: updateRangeSliderState, preparedChart: preparedChart, preparedLegend: preparedLegend, preparedSeries: debouncedAllPreparedSeries, preparedSeriesOptions: preparedSeriesOptions, preparedRangeSlider: xAxis.rangeSlider, rangeSliderState: rangeSliderState, ref: rangeSliderRef, width: width, xAxis: data.xAxis, yAxis: data.yAxis, legendConfig: legendConfig })),
|
|
252
253
|
(preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && legendConfig && (React.createElement(Legend, { chartSeries: preparedSeries, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick, onUpdate: unpinTooltip, htmlLayout: htmlLayout }))));
|
|
253
254
|
return (React.createElement("div", { className: b() },
|
|
254
255
|
React.createElement("svg", { ref: svgRef, width: width, height: height,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Dispatch } from 'd3';
|
|
3
|
-
import type { PreparedChart, PreparedLegend, RangeSliderState, ZoomState } from '../../hooks';
|
|
3
|
+
import type { LegendItem, PreparedChart, PreparedLegend, PreparedSeries, RangeSliderState, ZoomState } from '../../hooks';
|
|
4
|
+
import type { LegendConfig } from '../../types';
|
|
4
5
|
import type { ChartInnerProps } from './types';
|
|
5
6
|
type Props = ChartInnerProps & {
|
|
6
7
|
clipPathId: string;
|
|
@@ -8,35 +9,33 @@ type Props = ChartInnerProps & {
|
|
|
8
9
|
htmlLayout: HTMLElement | null;
|
|
9
10
|
plotNode: SVGGElement | null;
|
|
10
11
|
preparedChart: PreparedChart;
|
|
11
|
-
svgContainer: SVGGElement | null;
|
|
12
12
|
updateZoomState: (nextZoomState: Partial<ZoomState>) => void;
|
|
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;
|
|
16
27
|
export declare function useChartInnerProps(props: Props): {
|
|
17
|
-
allPreparedSeries:
|
|
28
|
+
allPreparedSeries: PreparedSeries[];
|
|
18
29
|
boundsHeight: number;
|
|
19
30
|
boundsOffsetLeft: number;
|
|
20
31
|
boundsOffsetTop: number;
|
|
21
32
|
boundsWidth: number;
|
|
22
33
|
handleLegendItemClick: import("../../hooks").OnLegendItemClick;
|
|
23
34
|
isOutsideBounds: (x: number, y: number) => boolean;
|
|
24
|
-
legendConfig:
|
|
25
|
-
|
|
26
|
-
left: number;
|
|
27
|
-
top: number;
|
|
28
|
-
};
|
|
29
|
-
pagination: {
|
|
30
|
-
pages: {
|
|
31
|
-
start: number;
|
|
32
|
-
end: number;
|
|
33
|
-
}[];
|
|
34
|
-
} | undefined;
|
|
35
|
-
maxWidth: number;
|
|
36
|
-
} | undefined;
|
|
37
|
-
legendItems: never[] | import("../../hooks").LegendItem[][];
|
|
35
|
+
legendConfig: LegendConfig | undefined;
|
|
36
|
+
legendItems: LegendItem[][];
|
|
38
37
|
preparedLegend: PreparedLegend | null;
|
|
39
|
-
preparedSeries:
|
|
38
|
+
preparedSeries: PreparedSeries[];
|
|
40
39
|
preparedSeriesOptions: import("../../constants").SeriesOptionsDefaults;
|
|
41
40
|
preparedSplit: import("../../hooks").PreparedSplit;
|
|
42
41
|
prevHeight: number | undefined;
|
|
@@ -44,7 +43,6 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
44
43
|
shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|
|
45
44
|
shapesData: import("../../hooks").ShapeData[];
|
|
46
45
|
shapesReady: boolean;
|
|
47
|
-
svgXPos: number | undefined;
|
|
48
46
|
xAxis: import("../../hooks").PreparedXAxis | null;
|
|
49
47
|
xScale: import("../../hooks").ChartScale | undefined;
|
|
50
48
|
yAxis: (Omit<import("../..").ChartAxis, "type" | "labels" | "plotLines" | "plotBands"> & {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
2
3
|
import { DEFAULT_PALETTE, SERIES_TYPE } from '../../constants';
|
|
3
4
|
import { useAxis, useAxisScales, useChartDimensions, useNormalizedOriginalData, usePrevious, useSeries, useShapes, useSplit, useYAxisLabelWidth, useZoom, } from '../../hooks';
|
|
4
5
|
import { getYAxisWidth } from '../../hooks/useChartDimensions/utils';
|
|
@@ -9,17 +10,18 @@ import { hasAtLeastOneSeriesDataPerPlot } from './utils';
|
|
|
9
10
|
const CLIP_PATH_BY_SERIES_TYPE = {
|
|
10
11
|
[SERIES_TYPE.Scatter]: false,
|
|
11
12
|
};
|
|
12
|
-
function getBoundsOffsetTop(
|
|
13
|
-
|
|
13
|
+
function getBoundsOffsetTop({ chartMarginTop, preparedLegend, legendConfig, }) {
|
|
14
|
+
var _a;
|
|
14
15
|
return (chartMarginTop +
|
|
15
16
|
((preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && preparedLegend.position === 'top'
|
|
16
|
-
?
|
|
17
|
+
? ((_a = legendConfig === null || legendConfig === void 0 ? void 0 : legendConfig.height) !== null && _a !== void 0 ? _a : 0) + preparedLegend.margin
|
|
17
18
|
: 0));
|
|
18
19
|
}
|
|
19
20
|
function getBoundsOffsetLeft(args) {
|
|
20
|
-
|
|
21
|
+
var _a;
|
|
22
|
+
const { chartMarginLeft, preparedLegend, yAxis, getYAxisWidth: getAxisWidth, legendConfig, } = args;
|
|
21
23
|
const legendOffset = (preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && preparedLegend.position === 'left'
|
|
22
|
-
?
|
|
24
|
+
? ((_a = legendConfig === null || legendConfig === void 0 ? void 0 : legendConfig.width) !== null && _a !== void 0 ? _a : 0) + preparedLegend.margin
|
|
23
25
|
: 0;
|
|
24
26
|
const leftAxisWidth = yAxis.reduce((acc, axis) => {
|
|
25
27
|
if (axis.position !== 'left') {
|
|
@@ -33,9 +35,47 @@ function getBoundsOffsetLeft(args) {
|
|
|
33
35
|
}, 0);
|
|
34
36
|
return chartMarginLeft + legendOffset + leftAxisWidth;
|
|
35
37
|
}
|
|
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
|
+
}
|
|
36
77
|
export function useChartInnerProps(props) {
|
|
37
|
-
|
|
38
|
-
const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, preparedChart, rangeSliderState, svgContainer, width, updateZoomState, zoomState, } = props;
|
|
78
|
+
const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, preparedChart, rangeSliderState, width, updateZoomState, zoomState, } = props;
|
|
39
79
|
const prevWidth = usePrevious(width);
|
|
40
80
|
const prevHeight = usePrevious(height);
|
|
41
81
|
const colors = React.useMemo(() => {
|
|
@@ -76,21 +116,17 @@ export function useChartInnerProps(props) {
|
|
|
76
116
|
zoomState: effectiveZoomState,
|
|
77
117
|
});
|
|
78
118
|
}, [allPreparedSeries, normalizedXAxis, normalizedYAxis, effectiveZoomState]);
|
|
79
|
-
const { legendConfig, legendItems } =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
chartMargin: preparedChart.margin,
|
|
87
|
-
series: preparedSeries,
|
|
88
|
-
preparedLegend,
|
|
89
|
-
});
|
|
90
|
-
}, [width, height, preparedChart.margin, preparedSeries, preparedLegend]);
|
|
119
|
+
const { legendConfig, legendItems } = useLegend({
|
|
120
|
+
width,
|
|
121
|
+
height,
|
|
122
|
+
preparedChart,
|
|
123
|
+
preparedSeries,
|
|
124
|
+
preparedLegend,
|
|
125
|
+
});
|
|
91
126
|
const { xAxis, yAxis, setAxes } = useAxis({
|
|
92
127
|
height,
|
|
93
128
|
preparedChart,
|
|
129
|
+
legendConfig,
|
|
94
130
|
preparedLegend,
|
|
95
131
|
preparedSeries,
|
|
96
132
|
preparedSeriesOptions,
|
|
@@ -106,6 +142,7 @@ export function useChartInnerProps(props) {
|
|
|
106
142
|
preparedYAxis: yAxis,
|
|
107
143
|
preparedXAxis: xAxis,
|
|
108
144
|
width,
|
|
145
|
+
legendConfig,
|
|
109
146
|
});
|
|
110
147
|
const preparedSplit = useSplit({ split: data.split, boundsHeight, chartWidth: width });
|
|
111
148
|
const { xScale, yScale } = useAxisScales({
|
|
@@ -166,6 +203,7 @@ export function useChartInnerProps(props) {
|
|
|
166
203
|
const boundsOffsetTop = getBoundsOffsetTop({
|
|
167
204
|
chartMarginTop: preparedChart.margin.top,
|
|
168
205
|
preparedLegend,
|
|
206
|
+
legendConfig,
|
|
169
207
|
});
|
|
170
208
|
// We need to calculate the width of each left axis because the first axis can be hidden
|
|
171
209
|
const boundsOffsetLeft = getBoundsOffsetLeft({
|
|
@@ -173,8 +211,8 @@ export function useChartInnerProps(props) {
|
|
|
173
211
|
preparedLegend,
|
|
174
212
|
yAxis,
|
|
175
213
|
getYAxisWidth,
|
|
214
|
+
legendConfig,
|
|
176
215
|
});
|
|
177
|
-
const { x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
|
|
178
216
|
return {
|
|
179
217
|
allPreparedSeries,
|
|
180
218
|
boundsHeight,
|
|
@@ -194,7 +232,6 @@ export function useChartInnerProps(props) {
|
|
|
194
232
|
shapes,
|
|
195
233
|
shapesData,
|
|
196
234
|
shapesReady,
|
|
197
|
-
svgXPos: x,
|
|
198
235
|
xAxis,
|
|
199
236
|
xScale,
|
|
200
237
|
yAxis,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { LegendItem, OnLegendItemClick, PreparedLegend, PreparedSeries } from '../../hooks';
|
|
3
|
+
import type { LegendConfig } from '../../types';
|
|
3
4
|
import './styles.css';
|
|
4
5
|
type Props = {
|
|
5
6
|
chartSeries: PreparedSeries[];
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { scaleLinear, select, symbol } from 'd3';
|
|
3
3
|
import { CONTINUOUS_LEGEND_SIZE } from '../../constants';
|
|
4
4
|
import { formatNumber } from '../../libs';
|
|
5
|
-
import { block, createGradientRect, getContinuesColorFn, getLabelsSize, getSymbol, getUniqId,
|
|
5
|
+
import { block, createGradientRect, getContinuesColorFn, getLabelsSize, getSymbol, getUniqId, } from '../../utils';
|
|
6
6
|
import { axisBottom } from '../../utils/chart/axis-generators';
|
|
7
7
|
import { appendLinePathElement } from '../utils';
|
|
8
8
|
import './styles.css';
|
|
@@ -163,7 +163,6 @@ export const Legend = (props) => {
|
|
|
163
163
|
: items;
|
|
164
164
|
const legendLineHeights = [];
|
|
165
165
|
pageItems.forEach((line) => {
|
|
166
|
-
var _a;
|
|
167
166
|
const legendLine = svgElement.append('g').attr('class', b('line'));
|
|
168
167
|
const htmlLegendLine = htmlContainer === null || htmlContainer === void 0 ? void 0 : htmlContainer.append('div').style('position', 'absolute');
|
|
169
168
|
const legendItemTemplate = legendLine
|
|
@@ -214,9 +213,8 @@ export const Legend = (props) => {
|
|
|
214
213
|
.on('click', function (e, d) {
|
|
215
214
|
onItemClick({ id: d.id, name: d.name, metaKey: e.metaKey });
|
|
216
215
|
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate();
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
});
|
|
216
|
+
})
|
|
217
|
+
.html((d) => d.text);
|
|
220
218
|
}
|
|
221
219
|
else {
|
|
222
220
|
legendItemTemplate
|
|
@@ -231,19 +229,22 @@ export const Legend = (props) => {
|
|
|
231
229
|
const mods = { selected: d.visible, unselected: !d.visible };
|
|
232
230
|
return b('item-text', mods);
|
|
233
231
|
})
|
|
234
|
-
.html(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
232
|
+
.html((d) => d.text)
|
|
233
|
+
.style('font-size', legend.itemStyle.fontSize);
|
|
234
|
+
}
|
|
235
|
+
let contentWidth = 0;
|
|
236
|
+
if (legend.html) {
|
|
237
|
+
contentWidth = getXPosition(line.length) - legend.itemDistance;
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
contentWidth = line.reduce((sum, l, index) => {
|
|
241
|
+
sum += l.textWidth + l.symbol.width + l.symbol.padding;
|
|
242
|
+
if (index > 0) {
|
|
243
|
+
sum += legend.itemDistance;
|
|
241
244
|
}
|
|
242
|
-
|
|
245
|
+
return sum;
|
|
246
|
+
}, 0);
|
|
243
247
|
}
|
|
244
|
-
const contentWidth = (legend.html
|
|
245
|
-
? getXPosition(line.length) - legend.itemDistance
|
|
246
|
-
: (_a = legendLine.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) || 0;
|
|
247
248
|
let left = 0;
|
|
248
249
|
switch (legend.justifyContent) {
|
|
249
250
|
case 'center': {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { ChartXAxis, ChartYAxis } from '../../types';
|
|
2
|
+
import type { ChartXAxis, ChartYAxis, LegendConfig } from '../../types';
|
|
3
3
|
import type { PreparedChart } from '../useChartOptions/types';
|
|
4
4
|
import type { PreparedLegend, PreparedSeries, PreparedSeriesOptions } from '../useSeries/types';
|
|
5
5
|
import type { AxesState } from './types';
|
|
6
6
|
interface UseAxesProps {
|
|
7
7
|
height: number;
|
|
8
8
|
preparedChart: PreparedChart;
|
|
9
|
+
legendConfig: LegendConfig | undefined;
|
|
9
10
|
preparedLegend: PreparedLegend | null;
|
|
10
11
|
preparedSeries: PreparedSeries[];
|
|
11
12
|
preparedSeriesOptions: PreparedSeriesOptions;
|
|
@@ -4,15 +4,20 @@ import { getWidthOccupiedByYAxis } from '../useChartDimensions/utils';
|
|
|
4
4
|
import { getPreparedXAxis } from './x-axis';
|
|
5
5
|
import { getPreparedYAxis } from './y-axis';
|
|
6
6
|
export function useAxis(props) {
|
|
7
|
-
const { boundsHeight, height, preparedChart, preparedLegend, preparedSeries, preparedSeriesOptions, width, xAxis, yAxis, } = props;
|
|
7
|
+
const { boundsHeight, height, preparedChart, legendConfig, preparedLegend, preparedSeries, preparedSeriesOptions, width, xAxis, yAxis, } = props;
|
|
8
8
|
const [axesState, setAxes] = React.useState({ xAxis: null, yAxis: [] });
|
|
9
9
|
const axesStateRunRef = React.useRef(0);
|
|
10
10
|
const prevAxesStateValue = React.useRef(axesState);
|
|
11
11
|
const axesStateReady = React.useRef(false);
|
|
12
12
|
React.useEffect(() => {
|
|
13
|
+
const shouldWaitForLegendReady = !preparedLegend || ((preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && !legendConfig);
|
|
14
|
+
if (shouldWaitForLegendReady) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
13
17
|
axesStateRunRef.current++;
|
|
14
18
|
axesStateReady.current = false;
|
|
15
19
|
(async function () {
|
|
20
|
+
var _a, _b;
|
|
16
21
|
const currentRun = axesStateRunRef.current;
|
|
17
22
|
const seriesData = preparedSeries.filter((s) => s.visible);
|
|
18
23
|
const estimatedPreparedYAxis = await getPreparedYAxis({
|
|
@@ -41,7 +46,8 @@ export function useAxis(props) {
|
|
|
41
46
|
(preparedXAxis.rangeSlider.enabled
|
|
42
47
|
? preparedXAxis.rangeSlider.height + preparedXAxis.rangeSlider.margin
|
|
43
48
|
: 0) +
|
|
44
|
-
(
|
|
49
|
+
((_a = legendConfig === null || legendConfig === void 0 ? void 0 : legendConfig.height) !== null && _a !== void 0 ? _a : 0) +
|
|
50
|
+
((_b = preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.margin) !== null && _b !== void 0 ? _b : 0) +
|
|
45
51
|
preparedChart.margin.top +
|
|
46
52
|
preparedChart.margin.bottom);
|
|
47
53
|
}
|
|
@@ -65,6 +71,7 @@ export function useAxis(props) {
|
|
|
65
71
|
boundsHeight,
|
|
66
72
|
height,
|
|
67
73
|
preparedChart.margin,
|
|
74
|
+
legendConfig,
|
|
68
75
|
preparedLegend,
|
|
69
76
|
preparedSeries,
|
|
70
77
|
preparedSeriesOptions,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PreparedLegend, PreparedSeries, PreparedXAxis, PreparedYAxis } from '../../hooks';
|
|
2
|
-
import type { ChartMargin } from '../../types';
|
|
2
|
+
import type { ChartMargin, LegendConfig } from '../../types';
|
|
3
3
|
export { getBoundsWidth } from './utils';
|
|
4
4
|
type Args = {
|
|
5
5
|
height: number;
|
|
@@ -9,6 +9,7 @@ type Args = {
|
|
|
9
9
|
preparedXAxis: PreparedXAxis | null;
|
|
10
10
|
preparedYAxis: PreparedYAxis[] | null;
|
|
11
11
|
width: number;
|
|
12
|
+
legendConfig: LegendConfig | undefined;
|
|
12
13
|
};
|
|
13
14
|
export declare const useChartDimensions: (args: Args) => {
|
|
14
15
|
boundsWidth: number;
|