@gravity-ui/charts 1.11.1 → 1.11.3
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/Axis/AxisY.d.ts +1 -0
- package/dist/cjs/components/Axis/AxisY.js +14 -13
- package/dist/cjs/components/ChartInner/index.js +2 -2
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +1 -0
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +44 -13
- package/dist/cjs/components/Legend/index.js +6 -2
- package/dist/cjs/components/Tooltip/DefaultContent.js +4 -4
- package/dist/cjs/hooks/useAxisScales/index.js +15 -6
- package/dist/cjs/hooks/useChartOptions/index.d.ts +6 -2
- package/dist/cjs/hooks/useChartOptions/index.js +4 -4
- package/dist/cjs/hooks/useSeries/index.d.ts +1 -19
- package/dist/cjs/hooks/useSeries/index.js +7 -25
- package/dist/cjs/hooks/useSeries/prepare-area.js +1 -2
- package/dist/cjs/hooks/useSeries/prepare-legend.js +5 -1
- package/dist/cjs/hooks/useSeries/types.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/area/prepare-data.js +2 -1
- package/dist/cjs/hooks/useShapes/line/prepare-data.js +3 -2
- package/dist/cjs/hooks/useShapes/utils.d.ts +6 -0
- package/dist/cjs/hooks/useShapes/utils.js +29 -4
- package/dist/cjs/hooks/useShapes/waterfall/index.js +1 -1
- package/dist/cjs/libs/format-number/index.d.ts +7 -3
- package/dist/cjs/libs/format-number/index.js +8 -3
- package/dist/cjs/libs/format-number/types.d.ts +0 -1
- package/dist/cjs/types/chart/tooltip.d.ts +1 -0
- package/dist/cjs/types/formatter.d.ts +0 -1
- package/dist/cjs/utils/chart/axis-generators/bottom.js +21 -28
- package/dist/cjs/utils/chart/get-closest-data.d.ts +1 -0
- package/dist/cjs/utils/chart/get-closest-data.js +2 -5
- package/dist/cjs/utils/chart/index.d.ts +1 -0
- package/dist/cjs/utils/chart/index.js +3 -2
- package/dist/cjs/utils/chart/series/index.d.ts +1 -0
- package/dist/cjs/utils/chart/series/index.js +1 -0
- package/dist/cjs/utils/chart/series/sorting.d.ts +2 -0
- package/dist/cjs/utils/chart/series/sorting.js +12 -0
- package/dist/{esm/hooks/hooks-utils → cjs/utils/chart}/zoom.d.ts +5 -2
- package/dist/{esm/hooks/hooks-utils → cjs/utils/chart}/zoom.js +36 -9
- package/dist/esm/components/Axis/AxisY.d.ts +1 -0
- package/dist/esm/components/Axis/AxisY.js +14 -13
- package/dist/esm/components/ChartInner/index.js +2 -2
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +1 -0
- package/dist/esm/components/ChartInner/useChartInnerProps.js +44 -13
- package/dist/esm/components/Legend/index.js +6 -2
- package/dist/esm/components/Tooltip/DefaultContent.js +4 -4
- package/dist/esm/hooks/useAxisScales/index.js +15 -6
- package/dist/esm/hooks/useChartOptions/index.d.ts +6 -2
- package/dist/esm/hooks/useChartOptions/index.js +4 -4
- package/dist/esm/hooks/useSeries/index.d.ts +1 -19
- package/dist/esm/hooks/useSeries/index.js +7 -25
- package/dist/esm/hooks/useSeries/prepare-area.js +1 -2
- package/dist/esm/hooks/useSeries/prepare-legend.js +5 -1
- package/dist/esm/hooks/useSeries/types.d.ts +1 -0
- package/dist/esm/hooks/useShapes/area/prepare-data.js +2 -1
- package/dist/esm/hooks/useShapes/line/prepare-data.js +3 -2
- package/dist/esm/hooks/useShapes/utils.d.ts +6 -0
- package/dist/esm/hooks/useShapes/utils.js +29 -4
- package/dist/esm/hooks/useShapes/waterfall/index.js +1 -1
- package/dist/esm/libs/format-number/index.d.ts +7 -3
- package/dist/esm/libs/format-number/index.js +8 -3
- package/dist/esm/libs/format-number/types.d.ts +0 -1
- package/dist/esm/types/chart/tooltip.d.ts +1 -0
- package/dist/esm/types/formatter.d.ts +0 -1
- package/dist/esm/utils/chart/axis-generators/bottom.js +21 -28
- package/dist/esm/utils/chart/get-closest-data.d.ts +1 -0
- package/dist/esm/utils/chart/get-closest-data.js +2 -5
- package/dist/esm/utils/chart/index.d.ts +1 -0
- package/dist/esm/utils/chart/index.js +3 -2
- package/dist/esm/utils/chart/series/index.d.ts +1 -0
- package/dist/esm/utils/chart/series/index.js +1 -0
- package/dist/esm/utils/chart/series/sorting.d.ts +2 -0
- package/dist/esm/utils/chart/series/sorting.js +12 -0
- package/dist/{cjs/hooks/hooks-utils → esm/utils/chart}/zoom.d.ts +5 -2
- package/dist/{cjs/hooks/hooks-utils → esm/utils/chart}/zoom.js +36 -9
- package/package.json +1 -1
- package/dist/cjs/hooks/hooks-utils/index.d.ts +0 -1
- package/dist/cjs/hooks/hooks-utils/index.js +0 -1
- package/dist/esm/hooks/hooks-utils/index.d.ts +0 -1
- package/dist/esm/hooks/hooks-utils/index.js +0 -1
|
@@ -10,6 +10,7 @@ type Props = {
|
|
|
10
10
|
plotBeforeRef?: React.MutableRefObject<SVGGElement | null>;
|
|
11
11
|
plotAfterRef?: React.MutableRefObject<SVGGElement | null>;
|
|
12
12
|
bottomLimit?: number;
|
|
13
|
+
topLimit?: number;
|
|
13
14
|
};
|
|
14
15
|
export declare const AxisY: (props: Props) => React.JSX.Element;
|
|
15
16
|
export {};
|
|
@@ -4,11 +4,8 @@ import { block, calculateCos, calculateSin, formatAxisTickLabel, getAxisHeight,
|
|
|
4
4
|
import './styles.css';
|
|
5
5
|
const b = block('axis');
|
|
6
6
|
function transformLabel(args) {
|
|
7
|
-
const { node, axis,
|
|
8
|
-
let topOffset = axis.labels.lineHeight / 2;
|
|
9
|
-
if (isTopOffsetOverload) {
|
|
10
|
-
topOffset = 0;
|
|
11
|
-
}
|
|
7
|
+
const { node, axis, startTopOffset } = args;
|
|
8
|
+
let topOffset = startTopOffset !== null && startTopOffset !== void 0 ? startTopOffset : axis.labels.lineHeight / 2;
|
|
12
9
|
let leftOffset = axis.labels.margin;
|
|
13
10
|
if (axis.position === 'left') {
|
|
14
11
|
leftOffset = leftOffset * -1;
|
|
@@ -85,7 +82,7 @@ function getTitlePosition(args) {
|
|
|
85
82
|
return { x, y };
|
|
86
83
|
}
|
|
87
84
|
export const AxisY = (props) => {
|
|
88
|
-
const { axes: allAxes, width, height: totalHeight, scale, split, plotBeforeRef, plotAfterRef, bottomLimit = 0, } = props;
|
|
85
|
+
const { axes: allAxes, width, height: totalHeight, scale, split, plotBeforeRef, plotAfterRef, bottomLimit = 0, topLimit = 0, } = props;
|
|
89
86
|
const height = getAxisHeight({ split, boundsHeight: totalHeight });
|
|
90
87
|
const ref = React.useRef(null);
|
|
91
88
|
const lineGenerator = line();
|
|
@@ -140,16 +137,20 @@ export const AxisY = (props) => {
|
|
|
140
137
|
.style('transform', function () {
|
|
141
138
|
return transformLabel({ node: this, axis: d });
|
|
142
139
|
});
|
|
143
|
-
labels.each(function (_d, i) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
140
|
+
labels.each(function (_d, i, nodes) {
|
|
141
|
+
const isFirstNode = i === 0;
|
|
142
|
+
const isLastNode = i === nodes.length - 1;
|
|
143
|
+
if (isFirstNode || isLastNode) {
|
|
144
|
+
const labelNode = this;
|
|
145
|
+
const labelNodeRect = labelNode.getBoundingClientRect();
|
|
146
|
+
const shouldBeTransformed = (isFirstNode && labelNodeRect.bottom > bottomLimit) ||
|
|
147
|
+
(isLastNode && labelNodeRect.top < topLimit);
|
|
148
|
+
if (shouldBeTransformed) {
|
|
149
|
+
const text = select(labelNode);
|
|
149
150
|
const transform = transformLabel({
|
|
150
151
|
node: this,
|
|
151
152
|
axis: d,
|
|
152
|
-
|
|
153
|
+
startTopOffset: isLastNode ? labelNodeRect.height : 0,
|
|
153
154
|
});
|
|
154
155
|
text.style('transform', transform);
|
|
155
156
|
if (d.labels.rotation) {
|
|
@@ -23,7 +23,7 @@ export const ChartInner = (props) => {
|
|
|
23
23
|
const plotAfterRef = React.useRef(null);
|
|
24
24
|
const dispatcher = React.useMemo(() => getDispatcher(), []);
|
|
25
25
|
const clipPathId = useUniqId();
|
|
26
|
-
const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, handleZoomReset, isOutsideBounds, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, prevHeight, prevWidth, shapes, shapesData, title, tooltip, xAxis, xScale, yAxis, yScale, svgXPos, svgBottomPos, } = useChartInnerProps(Object.assign(Object.assign({}, props), { dispatcher,
|
|
26
|
+
const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, handleZoomReset, isOutsideBounds, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, prevHeight, prevWidth, shapes, shapesData, title, tooltip, xAxis, xScale, yAxis, yScale, svgXPos, svgBottomPos, svgTopPos, } = useChartInnerProps(Object.assign(Object.assign({}, props), { dispatcher,
|
|
27
27
|
htmlLayout, svgContainer: svgRef.current, plotNode: plotRef.current, clipPathId }));
|
|
28
28
|
const { tooltipPinned, togglePinTooltip, unpinTooltip } = useChartInnerState({
|
|
29
29
|
dispatcher,
|
|
@@ -94,7 +94,7 @@ export const ChartInner = (props) => {
|
|
|
94
94
|
})),
|
|
95
95
|
React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[boundsOffsetLeft, boundsOffsetTop].join(',')})`, ref: plotRef },
|
|
96
96
|
xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length) && (React.createElement(React.Fragment, null,
|
|
97
|
-
React.createElement(AxisY, { bottomLimit: svgBottomPos, axes: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale, split: preparedSplit, plotBeforeRef: plotBeforeRef, plotAfterRef: plotAfterRef }),
|
|
97
|
+
React.createElement(AxisY, { bottomLimit: svgBottomPos, topLimit: svgTopPos, axes: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale, split: preparedSplit, plotBeforeRef: plotBeforeRef, plotAfterRef: plotAfterRef }),
|
|
98
98
|
xAxis && (React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
|
|
99
99
|
React.createElement(AxisX, { leftmostLimit: svgXPos, axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale, split: preparedSplit, plotBeforeRef: plotBeforeRef, plotAfterRef: plotAfterRef }))))),
|
|
100
100
|
React.createElement("g", { ref: plotBeforeRef }),
|
|
@@ -1,26 +1,37 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useAxisScales, useChartDimensions, useChartOptions, usePrevious, useSeries, useShapes, useSplit, } from '../../hooks';
|
|
3
|
-
import { getZoomedSeriesData } from '../../hooks/hooks-utils';
|
|
4
3
|
import { getYAxisWidth } from '../../hooks/useChartDimensions/utils';
|
|
5
4
|
import { getPreparedXAxis } from '../../hooks/useChartOptions/x-axis';
|
|
6
5
|
import { getPreparedYAxis } from '../../hooks/useChartOptions/y-axis';
|
|
6
|
+
import { getLegendComponents } from '../../hooks/useSeries/prepare-legend';
|
|
7
|
+
import { getPreparedOptions } from '../../hooks/useSeries/prepare-options';
|
|
7
8
|
import { useZoom } from '../../hooks/useZoom';
|
|
9
|
+
import { getSortedSeriesData, getZoomedSeriesData } from '../../utils';
|
|
8
10
|
import { hasAtLeastOneSeriesDataPerPlot } from './utils';
|
|
9
11
|
export function useChartInnerProps(props) {
|
|
10
12
|
var _a;
|
|
11
13
|
const { width, height, data, dispatcher, htmlLayout, svgContainer, plotNode, clipPathId } = props;
|
|
12
14
|
const prevWidth = usePrevious(width);
|
|
13
15
|
const prevHeight = usePrevious(height);
|
|
16
|
+
const { chart, title, tooltip, colors } = useChartOptions({
|
|
17
|
+
seriesData: data.series.data,
|
|
18
|
+
chart: data.chart,
|
|
19
|
+
colors: data.colors,
|
|
20
|
+
title: data.title,
|
|
21
|
+
tooltip: data.tooltip,
|
|
22
|
+
});
|
|
14
23
|
const [zoomState, setZoomState] = React.useState({});
|
|
15
|
-
const
|
|
16
|
-
|
|
24
|
+
const sortedSeriesData = React.useMemo(() => {
|
|
25
|
+
return getSortedSeriesData(data.series.data);
|
|
26
|
+
}, [data.series.data]);
|
|
27
|
+
const { zoomedSeriesData, zoomedShapesSeriesData } = React.useMemo(() => {
|
|
17
28
|
return getZoomedSeriesData({
|
|
18
|
-
seriesData:
|
|
29
|
+
seriesData: sortedSeriesData,
|
|
19
30
|
xAxis: data.xAxis,
|
|
20
31
|
yAxes: data.yAxis,
|
|
21
32
|
zoomState,
|
|
22
33
|
});
|
|
23
|
-
}, [data.
|
|
34
|
+
}, [data.xAxis, data.yAxis, sortedSeriesData, zoomState]);
|
|
24
35
|
const [xAxis, setXAxis] = React.useState(null);
|
|
25
36
|
React.useEffect(() => {
|
|
26
37
|
setXAxis(null);
|
|
@@ -31,17 +42,36 @@ export function useChartInnerProps(props) {
|
|
|
31
42
|
setYAxis([]);
|
|
32
43
|
getPreparedYAxis({ yAxis: data.yAxis, height, seriesData: zoomedSeriesData }).then((val) => setYAxis(val));
|
|
33
44
|
}, [data.yAxis, height, zoomedSeriesData]);
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
45
|
+
const preparedSeriesOptions = React.useMemo(() => {
|
|
46
|
+
return getPreparedOptions(data.series.options);
|
|
47
|
+
}, [data.series.options]);
|
|
48
|
+
const { preparedSeries, preparedLegend, handleLegendItemClick } = useSeries({
|
|
38
49
|
colors,
|
|
39
50
|
legend: data.legend,
|
|
40
51
|
originalSeriesData: data.series.data,
|
|
41
52
|
seriesData: zoomedSeriesData,
|
|
42
53
|
seriesOptions: data.series.options,
|
|
43
|
-
preparedYAxis: yAxis,
|
|
44
54
|
});
|
|
55
|
+
const { preparedSeries: preparedShapesSeries } = useSeries({
|
|
56
|
+
colors,
|
|
57
|
+
legend: data.legend,
|
|
58
|
+
originalSeriesData: data.series.data,
|
|
59
|
+
seriesData: zoomedShapesSeriesData,
|
|
60
|
+
seriesOptions: data.series.options,
|
|
61
|
+
});
|
|
62
|
+
const { legendConfig, legendItems } = React.useMemo(() => {
|
|
63
|
+
if (!preparedLegend) {
|
|
64
|
+
return { legendConfig: undefined, legendItems: [] };
|
|
65
|
+
}
|
|
66
|
+
return getLegendComponents({
|
|
67
|
+
chartWidth: width,
|
|
68
|
+
chartHeight: height,
|
|
69
|
+
chartMargin: chart.margin,
|
|
70
|
+
series: preparedSeries,
|
|
71
|
+
preparedLegend,
|
|
72
|
+
preparedYAxis: yAxis,
|
|
73
|
+
});
|
|
74
|
+
}, [width, height, chart.margin, preparedSeries, preparedLegend, yAxis]);
|
|
45
75
|
const { boundsWidth, boundsHeight } = useChartDimensions({
|
|
46
76
|
width,
|
|
47
77
|
height,
|
|
@@ -69,7 +99,7 @@ export function useChartInnerProps(props) {
|
|
|
69
99
|
boundsWidth,
|
|
70
100
|
boundsHeight,
|
|
71
101
|
dispatcher,
|
|
72
|
-
series:
|
|
102
|
+
series: preparedShapesSeries,
|
|
73
103
|
seriesOptions: preparedSeriesOptions,
|
|
74
104
|
xAxis,
|
|
75
105
|
xScale,
|
|
@@ -81,7 +111,7 @@ export function useChartInnerProps(props) {
|
|
|
81
111
|
isOutsideBounds,
|
|
82
112
|
});
|
|
83
113
|
const handleAttemptToSetZoomState = React.useCallback((nextZoomState) => {
|
|
84
|
-
const nextZoomedSeriesData = getZoomedSeriesData({
|
|
114
|
+
const { zoomedSeriesData: nextZoomedSeriesData } = getZoomedSeriesData({
|
|
85
115
|
seriesData: zoomedSeriesData,
|
|
86
116
|
xAxis: data.xAxis,
|
|
87
117
|
yAxes: data.yAxis,
|
|
@@ -117,12 +147,13 @@ export function useChartInnerProps(props) {
|
|
|
117
147
|
}
|
|
118
148
|
return acc;
|
|
119
149
|
}, 0);
|
|
120
|
-
const {
|
|
150
|
+
const { bottom, top, x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
|
|
121
151
|
const handleZoomReset = React.useCallback(() => {
|
|
122
152
|
setZoomState({});
|
|
123
153
|
}, []);
|
|
124
154
|
return {
|
|
125
155
|
svgBottomPos: bottom,
|
|
156
|
+
svgTopPos: top,
|
|
126
157
|
svgXPos: x,
|
|
127
158
|
boundsHeight,
|
|
128
159
|
boundsOffsetLeft,
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { line as lineGenerator, 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, getLineDashArray, getSymbol, handleOverflowingText, } from '../../utils';
|
|
5
|
+
import { block, createGradientRect, getContinuesColorFn, getLabelsSize, getLineDashArray, getSymbol, getUniqId, handleOverflowingText, } from '../../utils';
|
|
6
6
|
import { axisBottom } from '../../utils/chart/axis-generators';
|
|
7
7
|
import './styles.css';
|
|
8
8
|
const b = block('legend');
|
|
@@ -305,6 +305,7 @@ export const Legend = (props) => {
|
|
|
305
305
|
maxTickCount: 4,
|
|
306
306
|
tickColor: '#fff',
|
|
307
307
|
labelFormat: (value) => formatNumber(value, { unit: 'auto' }),
|
|
308
|
+
labelsStyle: legend.ticks.style,
|
|
308
309
|
},
|
|
309
310
|
domain: {
|
|
310
311
|
size: legend.width,
|
|
@@ -381,5 +382,8 @@ export const Legend = (props) => {
|
|
|
381
382
|
pageIndex,
|
|
382
383
|
htmlLayout,
|
|
383
384
|
]);
|
|
384
|
-
|
|
385
|
+
// due to asynchronous processing, we only need to work with the actual element
|
|
386
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
387
|
+
const key = React.useMemo(() => getUniqId(), [legend, config]);
|
|
388
|
+
return React.createElement("g", { key: key, className: b(), ref: ref, width: boundsWidth, height: legend.height });
|
|
385
389
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
|
-
import { block, getDataCategoryValue
|
|
3
|
+
import { block, getDataCategoryValue } from '../../utils';
|
|
4
4
|
import { getFormattedValue } from '../../utils/chart/format';
|
|
5
5
|
const b = block('tooltip');
|
|
6
6
|
const DEFAULT_DATE_FORMAT = 'DD.MM.YY';
|
|
@@ -62,7 +62,7 @@ export const DefaultContent = ({ hovered, xAxis, yAxis, valueFormat }) => {
|
|
|
62
62
|
measureValue && React.createElement("div", { className: b('series-name') }, measureValue),
|
|
63
63
|
// eslint-disable-next-line complexity
|
|
64
64
|
hovered.map((seriesItem, i) => {
|
|
65
|
-
var _a;
|
|
65
|
+
var _a, _b;
|
|
66
66
|
const { data, series, closest } = seriesItem;
|
|
67
67
|
const id = `${get(series, 'id')}_${i}`;
|
|
68
68
|
const color = get(series, 'color');
|
|
@@ -87,7 +87,7 @@ export const DefaultContent = ({ hovered, xAxis, yAxis, valueFormat }) => {
|
|
|
87
87
|
}
|
|
88
88
|
case 'waterfall': {
|
|
89
89
|
const isTotal = get(data, 'total', false);
|
|
90
|
-
const subTotalValue =
|
|
90
|
+
const subTotalValue = (_a = seriesItem.subTotal) !== null && _a !== void 0 ? _a : 0;
|
|
91
91
|
const format = valueFormat !== null && valueFormat !== void 0 ? valueFormat : getDefaultValueFormat({ axis: yAxis });
|
|
92
92
|
const subTotal = getFormattedValue({
|
|
93
93
|
value: subTotalValue,
|
|
@@ -145,7 +145,7 @@ export const DefaultContent = ({ hovered, xAxis, yAxis, valueFormat }) => {
|
|
|
145
145
|
}
|
|
146
146
|
case 'sankey': {
|
|
147
147
|
const { target, data: source } = seriesItem;
|
|
148
|
-
const value = (
|
|
148
|
+
const value = (_b = source.links.find((d) => d.name === (target === null || target === void 0 ? void 0 : target.name))) === null || _b === void 0 ? void 0 : _b.value;
|
|
149
149
|
const formattedValue = getFormattedValue({
|
|
150
150
|
value,
|
|
151
151
|
format: valueFormat !== null && valueFormat !== void 0 ? valueFormat : { type: 'number' },
|
|
@@ -3,6 +3,7 @@ import { extent, scaleBand, scaleLinear, scaleLog, scaleUtc } from 'd3';
|
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { DEFAULT_AXIS_TYPE } from '../../constants';
|
|
5
5
|
import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getAxisHeight, getDataCategoryValue, getDefaultMaxXAxisValue, getDefaultMinXAxisValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
|
|
6
|
+
const X_AXIS_ZOOM_PADDING = 0.02;
|
|
6
7
|
const isNumericalArrayData = (data) => {
|
|
7
8
|
return data.every((d) => typeof d === 'number' || d === null);
|
|
8
9
|
};
|
|
@@ -103,8 +104,10 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
|
|
|
103
104
|
const xCategories = get(axis, 'categories');
|
|
104
105
|
const xTimestamps = get(axis, 'timestamps');
|
|
105
106
|
const maxPadding = get(axis, 'maxPadding', 0);
|
|
106
|
-
const
|
|
107
|
-
const
|
|
107
|
+
const xAxisMaxPadding = boundsWidth * maxPadding + calculateXAxisPadding(series);
|
|
108
|
+
const xAxisZoomPadding = boundsWidth * X_AXIS_ZOOM_PADDING;
|
|
109
|
+
const xRange = [0, boundsWidth - xAxisMaxPadding];
|
|
110
|
+
const xRangeZoom = [0 + xAxisZoomPadding, boundsWidth - xAxisZoomPadding];
|
|
108
111
|
switch (xType) {
|
|
109
112
|
case 'linear':
|
|
110
113
|
case 'logarithmic': {
|
|
@@ -131,7 +134,9 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
|
|
|
131
134
|
: xMaxDomain;
|
|
132
135
|
}
|
|
133
136
|
const scaleFn = xType === 'logarithmic' ? scaleLog : scaleLinear;
|
|
134
|
-
const scale = scaleFn()
|
|
137
|
+
const scale = scaleFn()
|
|
138
|
+
.domain([xMin, xMax])
|
|
139
|
+
.range(hasZoomX ? xRangeZoom : xRange);
|
|
135
140
|
if (!hasZoomX) {
|
|
136
141
|
scale.nice();
|
|
137
142
|
}
|
|
@@ -147,7 +152,7 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
|
|
|
147
152
|
series: series,
|
|
148
153
|
});
|
|
149
154
|
const xScale = scaleBand().domain(filteredCategories).range([0, boundsWidth]);
|
|
150
|
-
if (xScale.step() / 2 <
|
|
155
|
+
if (xScale.step() / 2 < xAxisMaxPadding) {
|
|
151
156
|
xScale.range(xRange);
|
|
152
157
|
}
|
|
153
158
|
return xScale;
|
|
@@ -159,7 +164,9 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
|
|
|
159
164
|
const [xMinTimestamp, xMaxTimestamp] = extent(xTimestamps);
|
|
160
165
|
const xMin = typeof xMinProps === 'number' ? xMinProps : xMinTimestamp;
|
|
161
166
|
const xMax = typeof xMaxProps === 'number' ? xMaxProps : xMaxTimestamp;
|
|
162
|
-
const scale = scaleUtc()
|
|
167
|
+
const scale = scaleUtc()
|
|
168
|
+
.domain([xMin, xMax])
|
|
169
|
+
.range(hasZoomX ? xRangeZoom : xRange);
|
|
163
170
|
if (!hasZoomX) {
|
|
164
171
|
scale.nice();
|
|
165
172
|
}
|
|
@@ -171,7 +178,9 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
|
|
|
171
178
|
const [xMinTimestamp, xMaxTimestamp] = extent(domain);
|
|
172
179
|
const xMin = typeof xMinProps === 'number' ? xMinProps : xMinTimestamp;
|
|
173
180
|
const xMax = typeof xMaxProps === 'number' ? xMaxProps : xMaxTimestamp;
|
|
174
|
-
const scale = scaleUtc()
|
|
181
|
+
const scale = scaleUtc()
|
|
182
|
+
.domain([xMin, xMax])
|
|
183
|
+
.range(hasZoomX ? xRangeZoom : xRange);
|
|
175
184
|
if (!hasZoomX) {
|
|
176
185
|
scale.nice();
|
|
177
186
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ChartSeries, ChartTitle, ChartTooltip, ChartOptions as GeneralChartOptions } from '../../types';
|
|
2
2
|
import type { ChartOptions } from './types';
|
|
3
3
|
type Args = {
|
|
4
|
-
|
|
4
|
+
seriesData: ChartSeries[];
|
|
5
|
+
chart?: GeneralChartOptions;
|
|
6
|
+
colors?: string[];
|
|
7
|
+
title?: ChartTitle;
|
|
8
|
+
tooltip?: ChartTooltip;
|
|
5
9
|
};
|
|
6
10
|
export declare const useChartOptions: (args: Args) => ChartOptions;
|
|
7
11
|
export {};
|
|
@@ -4,21 +4,21 @@ import { getPreparedChart } from './chart';
|
|
|
4
4
|
import { getPreparedTitle } from './title';
|
|
5
5
|
import { getPreparedTooltip } from './tooltip';
|
|
6
6
|
export const useChartOptions = (args) => {
|
|
7
|
-
const {
|
|
7
|
+
const { chart, colors, seriesData, title, tooltip } = args;
|
|
8
8
|
const options = React.useMemo(() => {
|
|
9
9
|
const preparedTitle = getPreparedTitle({ title });
|
|
10
10
|
const preparedTooltip = getPreparedTooltip({ tooltip });
|
|
11
11
|
const preparedChart = getPreparedChart({
|
|
12
12
|
chart,
|
|
13
13
|
preparedTitle,
|
|
14
|
-
seriesData
|
|
14
|
+
seriesData,
|
|
15
15
|
});
|
|
16
16
|
return {
|
|
17
|
+
colors: colors !== null && colors !== void 0 ? colors : DEFAULT_PALETTE,
|
|
17
18
|
chart: preparedChart,
|
|
18
19
|
title: preparedTitle,
|
|
19
20
|
tooltip: preparedTooltip,
|
|
20
|
-
colors: colors !== null && colors !== void 0 ? colors : DEFAULT_PALETTE,
|
|
21
21
|
};
|
|
22
|
-
}, [chart, colors, title, tooltip
|
|
22
|
+
}, [chart, colors, seriesData, title, tooltip]);
|
|
23
23
|
return options;
|
|
24
24
|
};
|
|
@@ -1,34 +1,16 @@
|
|
|
1
1
|
import type { ChartData } from '../../types';
|
|
2
|
-
import type { PreparedAxis, PreparedChart } from '../useChartOptions/types';
|
|
3
2
|
import type { OnLegendItemClick, PreparedLegend, PreparedSeries } from './types';
|
|
4
3
|
type Args = {
|
|
5
|
-
chartWidth: number;
|
|
6
|
-
chartHeight: number;
|
|
7
|
-
chartMargin: PreparedChart['margin'];
|
|
8
4
|
colors: string[];
|
|
9
5
|
legend: ChartData['legend'];
|
|
10
6
|
originalSeriesData: ChartData['series']['data'];
|
|
11
7
|
seriesData: ChartData['series']['data'];
|
|
12
8
|
seriesOptions: ChartData['series']['options'];
|
|
13
|
-
|
|
9
|
+
preparedLegend?: PreparedLegend;
|
|
14
10
|
};
|
|
15
11
|
export declare const useSeries: (args: Args) => {
|
|
16
|
-
legendItems: never[] | import("./types").LegendItem[][];
|
|
17
|
-
legendConfig: {
|
|
18
|
-
offset: {
|
|
19
|
-
left: number;
|
|
20
|
-
top: number;
|
|
21
|
-
};
|
|
22
|
-
pagination: {
|
|
23
|
-
pages: {
|
|
24
|
-
start: number;
|
|
25
|
-
end: number;
|
|
26
|
-
}[];
|
|
27
|
-
} | undefined;
|
|
28
|
-
} | undefined;
|
|
29
12
|
preparedLegend: PreparedLegend | null;
|
|
30
13
|
preparedSeries: PreparedSeries[];
|
|
31
|
-
preparedSeriesOptions: import("../../constants").SeriesOptionsDefaults;
|
|
32
14
|
handleLegendItemClick: OnLegendItemClick;
|
|
33
15
|
};
|
|
34
16
|
export {};
|
|
@@ -2,16 +2,17 @@ import React from 'react';
|
|
|
2
2
|
import { group, scaleOrdinal } from 'd3';
|
|
3
3
|
import { getSeriesNames } from '../../utils';
|
|
4
4
|
import { usePrevious } from '../usePrevious';
|
|
5
|
-
import {
|
|
6
|
-
import { getPreparedOptions } from './prepare-options';
|
|
5
|
+
import { getPreparedLegend } from './prepare-legend';
|
|
7
6
|
import { prepareSeries } from './prepareSeries';
|
|
8
7
|
import { getActiveLegendItems, getAllLegendItems } from './utils';
|
|
9
8
|
export const useSeries = (args) => {
|
|
10
|
-
const {
|
|
11
|
-
const [preparedLegend, setPreparedLegend] = React.useState(
|
|
9
|
+
const { legend, originalSeriesData, seriesData, seriesOptions, colors, preparedLegend: preparedLegendProps = null, } = args;
|
|
10
|
+
const [preparedLegend, setPreparedLegend] = React.useState(preparedLegendProps);
|
|
12
11
|
React.useEffect(() => {
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
if (!preparedLegendProps) {
|
|
13
|
+
getPreparedLegend({ legend, series: seriesData }).then((value) => setPreparedLegend(value));
|
|
14
|
+
}
|
|
15
|
+
}, [legend, preparedLegendProps, seriesData]);
|
|
15
16
|
const [preparedSeries, setPreparedSeries] = React.useState([]);
|
|
16
17
|
const [activeLegendItems, setActiveLegendItems] = React.useState(getActiveLegendItems(preparedSeries));
|
|
17
18
|
React.useEffect(() => {
|
|
@@ -39,9 +40,6 @@ export const useSeries = (args) => {
|
|
|
39
40
|
setActiveLegendItems(getActiveLegendItems(acc));
|
|
40
41
|
})();
|
|
41
42
|
}, [seriesData, seriesOptions, preparedLegend, colors]);
|
|
42
|
-
const preparedSeriesOptions = React.useMemo(() => {
|
|
43
|
-
return getPreparedOptions(seriesOptions);
|
|
44
|
-
}, [seriesOptions]);
|
|
45
43
|
const prevOriginalSeriesData = usePrevious(originalSeriesData);
|
|
46
44
|
const chartSeries = React.useMemo(() => {
|
|
47
45
|
return preparedSeries.map((singleSeries) => {
|
|
@@ -51,19 +49,6 @@ export const useSeries = (args) => {
|
|
|
51
49
|
return singleSeries;
|
|
52
50
|
});
|
|
53
51
|
}, [preparedSeries, activeLegendItems]);
|
|
54
|
-
const { legendConfig, legendItems } = React.useMemo(() => {
|
|
55
|
-
if (!preparedLegend) {
|
|
56
|
-
return { legendConfig: undefined, legendItems: [] };
|
|
57
|
-
}
|
|
58
|
-
return getLegendComponents({
|
|
59
|
-
chartHeight,
|
|
60
|
-
chartMargin,
|
|
61
|
-
chartWidth,
|
|
62
|
-
series: chartSeries,
|
|
63
|
-
preparedLegend,
|
|
64
|
-
preparedYAxis,
|
|
65
|
-
});
|
|
66
|
-
}, [chartWidth, chartHeight, chartMargin, chartSeries, preparedLegend, preparedYAxis]);
|
|
67
52
|
const handleLegendItemClick = React.useCallback(({ name, metaKey }) => {
|
|
68
53
|
const allItems = getAllLegendItems(preparedSeries);
|
|
69
54
|
const onlyItemSelected = activeLegendItems.length === 1 && activeLegendItems.includes(name);
|
|
@@ -91,11 +76,8 @@ export const useSeries = (args) => {
|
|
|
91
76
|
}
|
|
92
77
|
}, [originalSeriesData, prevOriginalSeriesData, preparedSeries]);
|
|
93
78
|
return {
|
|
94
|
-
legendItems,
|
|
95
|
-
legendConfig,
|
|
96
79
|
preparedLegend,
|
|
97
80
|
preparedSeries: chartSeries,
|
|
98
|
-
preparedSeriesOptions,
|
|
99
81
|
handleLegendItemClick,
|
|
100
82
|
};
|
|
101
83
|
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { sort } from 'd3';
|
|
2
1
|
import get from 'lodash/get';
|
|
3
2
|
import merge from 'lodash/merge';
|
|
4
3
|
import { DEFAULT_DATALABELS_STYLE } from '../../constants';
|
|
@@ -46,7 +45,7 @@ export function prepareArea(args) {
|
|
|
46
45
|
enabled: get(series, 'legend.enabled', legend.enabled),
|
|
47
46
|
symbol: prepareLegendSymbol(series),
|
|
48
47
|
},
|
|
49
|
-
data:
|
|
48
|
+
data: series.data,
|
|
50
49
|
stacking: series.stacking,
|
|
51
50
|
stackId: getSeriesStackId(series),
|
|
52
51
|
dataLabels: {
|
|
@@ -21,9 +21,13 @@ export async function getPreparedLegend(args) {
|
|
|
21
21
|
const titleText = isTitleEnabled ? get(legend, 'title.text', '') : '';
|
|
22
22
|
const titleSize = await getLabelsSize({ labels: [titleText], style: titleStyle });
|
|
23
23
|
const titleHeight = isTitleEnabled ? titleSize.maxHeight : 0;
|
|
24
|
+
const tickStyle = {
|
|
25
|
+
fontSize: '12px',
|
|
26
|
+
};
|
|
24
27
|
const ticks = {
|
|
25
28
|
labelsMargin: 4,
|
|
26
|
-
labelsLineHeight:
|
|
29
|
+
labelsLineHeight: (await getLabelsSize({ labels: ['Tmp'], style: tickStyle })).maxHeight,
|
|
30
|
+
style: tickStyle,
|
|
27
31
|
};
|
|
28
32
|
const colorScale = {
|
|
29
33
|
colors: [],
|
|
@@ -36,7 +36,7 @@ function getXValues(series, xAxis, xScale) {
|
|
|
36
36
|
? getDataCategoryValue({ axisDirection: 'x', categories, data: d })
|
|
37
37
|
: d.x);
|
|
38
38
|
if (!acc.has(key)) {
|
|
39
|
-
acc.set(key, getXValue({ point: d, xAxis, xScale }));
|
|
39
|
+
acc.set(key, getXValue({ point: d, points: s.data, xAxis, xScale }));
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
42
|
return acc;
|
|
@@ -73,6 +73,7 @@ export const prepareAreaData = async (args) => {
|
|
|
73
73
|
const seriesYScale = yScale[yAxisIndex];
|
|
74
74
|
const yMin = getYValue({
|
|
75
75
|
point: { y: 0 },
|
|
76
|
+
points: s.data,
|
|
76
77
|
yAxis: seriesYAxis,
|
|
77
78
|
yScale: seriesYScale,
|
|
78
79
|
});
|
|
@@ -51,8 +51,9 @@ export const prepareLineData = async (args) => {
|
|
|
51
51
|
const yAxisTop = ((_a = split.plots[seriesYAxis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
|
|
52
52
|
const seriesYScale = yScale[s.yAxis];
|
|
53
53
|
const points = s.data.map((d) => ({
|
|
54
|
-
x: getXValue({ point: d, xAxis, xScale }),
|
|
55
|
-
y: yAxisTop +
|
|
54
|
+
x: getXValue({ point: d, points: s.data, xAxis, xScale }),
|
|
55
|
+
y: yAxisTop +
|
|
56
|
+
getYValue({ point: d, points: s.data, yAxis: seriesYAxis, yScale: seriesYScale }),
|
|
56
57
|
active: true,
|
|
57
58
|
data: d,
|
|
58
59
|
series: s,
|
|
@@ -6,6 +6,9 @@ export declare function getXValue(args: {
|
|
|
6
6
|
point: {
|
|
7
7
|
x?: number | string;
|
|
8
8
|
};
|
|
9
|
+
points?: {
|
|
10
|
+
x?: number | string;
|
|
11
|
+
}[];
|
|
9
12
|
xAxis: PreparedAxis;
|
|
10
13
|
xScale: ChartScale;
|
|
11
14
|
}): number;
|
|
@@ -13,6 +16,9 @@ export declare function getYValue(args: {
|
|
|
13
16
|
point: {
|
|
14
17
|
y?: number | string;
|
|
15
18
|
};
|
|
19
|
+
points?: {
|
|
20
|
+
y?: number | string;
|
|
21
|
+
}[];
|
|
16
22
|
yAxis: PreparedAxis;
|
|
17
23
|
yScale: ChartScale;
|
|
18
24
|
}): number;
|
|
@@ -1,26 +1,51 @@
|
|
|
1
1
|
import { path, select } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
3
|
import { getDataCategoryValue } from '../../utils';
|
|
4
|
+
const ONE_POINT_DOMAIN_DATA_CAPACITY = 3;
|
|
4
5
|
export function getXValue(args) {
|
|
5
|
-
const { point, xAxis, xScale } = args;
|
|
6
|
+
const { point, points, xAxis, xScale } = args;
|
|
6
7
|
if (xAxis.type === 'category') {
|
|
7
8
|
const xBandScale = xScale;
|
|
8
9
|
const categories = get(xAxis, 'categories', []);
|
|
9
10
|
const dataCategory = getDataCategoryValue({ axisDirection: 'x', categories, data: point });
|
|
10
11
|
return (xBandScale(dataCategory) || 0) + xBandScale.step() / 2;
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
+
let xLinearScale = xScale;
|
|
14
|
+
const [xMinDomain, xMaxDomain] = xLinearScale.domain();
|
|
15
|
+
if (Number(xMinDomain) === Number(xMaxDomain) &&
|
|
16
|
+
(points === null || points === void 0 ? void 0 : points.length) === ONE_POINT_DOMAIN_DATA_CAPACITY) {
|
|
17
|
+
const x1 = points[0].x;
|
|
18
|
+
const xTarget = points[1].x;
|
|
19
|
+
const x3 = points[2].x;
|
|
20
|
+
const xMin = Math.min(x1, xTarget, x3);
|
|
21
|
+
const xMax = Math.max(x1, xTarget, x3);
|
|
22
|
+
xLinearScale = xLinearScale
|
|
23
|
+
.copy()
|
|
24
|
+
.domain([xMin + (xTarget - xMin) / 2, xMax - (xMax - xTarget) / 2]);
|
|
25
|
+
}
|
|
13
26
|
return xLinearScale(point.x);
|
|
14
27
|
}
|
|
15
28
|
export function getYValue(args) {
|
|
16
|
-
const { point, yAxis, yScale } = args;
|
|
29
|
+
const { point, points, yAxis, yScale } = args;
|
|
17
30
|
if (yAxis.type === 'category') {
|
|
18
31
|
const yBandScale = yScale;
|
|
19
32
|
const categories = get(yAxis, 'categories', []);
|
|
20
33
|
const dataCategory = getDataCategoryValue({ axisDirection: 'y', categories, data: point });
|
|
21
34
|
return (yBandScale(dataCategory) || 0) + yBandScale.step() / 2;
|
|
22
35
|
}
|
|
23
|
-
|
|
36
|
+
let yLinearScale = yScale;
|
|
37
|
+
const [yMinDomain, yMaxDomain] = yLinearScale.domain();
|
|
38
|
+
if (Number(yMinDomain) === Number(yMaxDomain) &&
|
|
39
|
+
(points === null || points === void 0 ? void 0 : points.length) === ONE_POINT_DOMAIN_DATA_CAPACITY) {
|
|
40
|
+
const y1 = points[0].y;
|
|
41
|
+
const yTarget = points[1].y;
|
|
42
|
+
const y2 = points[2].y;
|
|
43
|
+
const yMin = Math.min(y1, yTarget, y2);
|
|
44
|
+
const yMax = Math.max(y1, yTarget, y2);
|
|
45
|
+
yLinearScale = yLinearScale
|
|
46
|
+
.copy()
|
|
47
|
+
.domain([yMin + (yTarget - yMin) / 2, yMax - (yMax - yTarget) / 2]);
|
|
48
|
+
}
|
|
24
49
|
return yLinearScale(point.y);
|
|
25
50
|
}
|
|
26
51
|
export function shapeKey(d) {
|
|
@@ -125,7 +125,7 @@ export const WaterfallSeriesShapes = (args) => {
|
|
|
125
125
|
return () => {
|
|
126
126
|
dispatcher.on('hover-shape.waterfall', null);
|
|
127
127
|
};
|
|
128
|
-
}, [dispatcher, preparedData, seriesOptions]);
|
|
128
|
+
}, [connectorSelector, dispatcher, preparedData, seriesOptions]);
|
|
129
129
|
return (React.createElement(React.Fragment, null,
|
|
130
130
|
React.createElement("g", { ref: ref, className: b(), clipPath: `url(#${clipPathId})` }),
|
|
131
131
|
React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { FormatNumberOptions, FormatOptions } from './types';
|
|
2
|
-
export declare const formatBytes: (value: number, options?: FormatOptions
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
export declare const formatBytes: (value: number, options?: FormatOptions & {
|
|
3
|
+
unitRate?: number;
|
|
4
|
+
}) => string;
|
|
5
|
+
export declare const formatDuration: (value: number, options?: FormatOptions & {
|
|
6
|
+
unitRate?: number;
|
|
7
|
+
}) => string;
|
|
8
|
+
export declare const getDefaultUnit: (value: number) => string | undefined;
|
|
5
9
|
export declare const formatNumber: (value: number | string, options?: FormatNumberOptions) => string;
|