@gravity-ui/charts 1.18.1 → 1.19.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/AxisY/AxisY.js +7 -5
- package/dist/cjs/components/AxisY/prepare-axis-data.js +9 -6
- package/dist/cjs/components/AxisY/types.d.ts +1 -1
- package/dist/cjs/components/AxisY/utils.js +1 -1
- package/dist/cjs/components/ChartInner/index.js +20 -26
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +2 -2
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +38 -30
- package/dist/cjs/components/ChartInner/utils.d.ts +1 -0
- package/dist/cjs/components/ChartInner/utils.js +21 -0
- package/dist/cjs/components/Legend/index.js +1 -1
- package/dist/cjs/components/Tooltip/ChartTooltipContent.d.ts +1 -1
- package/dist/cjs/components/Tooltip/ChartTooltipContent.js +3 -2
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/index.js +1 -0
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/utils.js +2 -1
- package/dist/cjs/constants/chart-types.d.ts +1 -0
- package/dist/cjs/constants/chart-types.js +1 -0
- package/dist/cjs/constants/defaults/series-options.js +8 -0
- package/dist/cjs/hooks/useAxisScales/index.js +47 -8
- package/dist/cjs/hooks/useChartOptions/tooltip.js +1 -1
- package/dist/cjs/hooks/useChartOptions/x-axis.d.ts +1 -1
- package/dist/cjs/hooks/useChartOptions/x-axis.js +15 -4
- package/dist/cjs/hooks/useChartOptions/y-axis.js +15 -7
- package/dist/cjs/hooks/useSeries/prepare-heatmap.d.ts +11 -0
- package/dist/cjs/hooks/useSeries/prepare-heatmap.js +37 -0
- package/dist/cjs/hooks/useSeries/prepareSeries.js +9 -0
- package/dist/cjs/hooks/useSeries/types.d.ts +14 -2
- package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +7 -4
- package/dist/cjs/hooks/useShapes/heatmap/index.d.ts +13 -0
- package/dist/cjs/hooks/useShapes/heatmap/index.js +74 -0
- package/dist/cjs/hooks/useShapes/heatmap/prepare-data.d.ts +13 -0
- package/dist/cjs/hooks/useShapes/heatmap/prepare-data.js +97 -0
- package/dist/cjs/hooks/useShapes/heatmap/types.d.ts +24 -0
- package/dist/cjs/hooks/useShapes/heatmap/types.js +1 -0
- package/dist/cjs/hooks/useShapes/index.d.ts +2 -1
- package/dist/cjs/hooks/useShapes/index.js +15 -0
- package/dist/cjs/hooks/useShapes/styles.css +4 -0
- package/dist/cjs/hooks/useTooltip/index.js +11 -1
- package/dist/cjs/hooks/utils/bar-y.d.ts +0 -5
- package/dist/cjs/hooks/utils/bar-y.js +2 -29
- package/dist/cjs/hooks/utils/get-band-size.d.ts +5 -0
- package/dist/cjs/hooks/utils/get-band-size.js +29 -0
- package/dist/cjs/types/chart/axis.d.ts +3 -1
- package/dist/cjs/types/chart/heatmap.d.ts +47 -0
- package/dist/cjs/types/chart/heatmap.js +1 -0
- package/dist/cjs/types/chart/series.d.ts +19 -2
- package/dist/cjs/types/chart/tooltip.d.ts +7 -1
- package/dist/cjs/types/index.d.ts +1 -0
- package/dist/cjs/types/index.js +1 -0
- package/dist/cjs/utils/chart/color.js +3 -2
- package/dist/cjs/utils/chart/get-closest-data.js +18 -1
- package/dist/cjs/utils/chart/index.js +10 -13
- package/dist/cjs/utils/chart/series/waterfall.d.ts +1 -1
- package/dist/cjs/utils/chart/series/waterfall.js +3 -3
- package/dist/cjs/validation/index.js +4 -1
- package/dist/esm/components/AxisY/AxisY.js +7 -5
- package/dist/esm/components/AxisY/prepare-axis-data.js +9 -6
- package/dist/esm/components/AxisY/types.d.ts +1 -1
- package/dist/esm/components/AxisY/utils.js +1 -1
- package/dist/esm/components/ChartInner/index.js +20 -26
- package/dist/esm/components/ChartInner/useChartInnerProps.js +38 -30
- package/dist/esm/components/ChartInner/utils.d.ts +1 -0
- package/dist/esm/components/ChartInner/utils.js +21 -0
- package/dist/esm/components/Legend/index.js +1 -1
- package/dist/esm/components/Tooltip/ChartTooltipContent.d.ts +1 -1
- package/dist/esm/components/Tooltip/ChartTooltipContent.js +3 -2
- package/dist/esm/components/Tooltip/DefaultTooltipContent/index.js +1 -0
- package/dist/esm/components/Tooltip/DefaultTooltipContent/utils.js +2 -1
- package/dist/esm/constants/chart-types.d.ts +1 -0
- package/dist/esm/constants/chart-types.js +1 -0
- package/dist/esm/constants/defaults/series-options.js +8 -0
- package/dist/esm/hooks/useAxisScales/index.js +47 -8
- package/dist/esm/hooks/useChartOptions/tooltip.js +1 -1
- package/dist/esm/hooks/useChartOptions/x-axis.d.ts +1 -1
- package/dist/esm/hooks/useChartOptions/x-axis.js +15 -4
- package/dist/esm/hooks/useChartOptions/y-axis.js +15 -7
- package/dist/esm/hooks/useSeries/prepare-heatmap.d.ts +11 -0
- package/dist/esm/hooks/useSeries/prepare-heatmap.js +37 -0
- package/dist/esm/hooks/useSeries/prepareSeries.js +9 -0
- package/dist/esm/hooks/useSeries/types.d.ts +14 -2
- package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +7 -4
- package/dist/esm/hooks/useShapes/heatmap/index.d.ts +13 -0
- package/dist/esm/hooks/useShapes/heatmap/index.js +74 -0
- package/dist/esm/hooks/useShapes/heatmap/prepare-data.d.ts +13 -0
- package/dist/esm/hooks/useShapes/heatmap/prepare-data.js +97 -0
- package/dist/esm/hooks/useShapes/heatmap/types.d.ts +24 -0
- package/dist/esm/hooks/useShapes/heatmap/types.js +1 -0
- package/dist/esm/hooks/useShapes/index.d.ts +2 -1
- package/dist/esm/hooks/useShapes/index.js +15 -0
- package/dist/esm/hooks/useShapes/styles.css +4 -0
- package/dist/esm/hooks/useTooltip/index.js +11 -1
- package/dist/esm/hooks/utils/bar-y.d.ts +0 -5
- package/dist/esm/hooks/utils/bar-y.js +2 -29
- package/dist/esm/hooks/utils/get-band-size.d.ts +5 -0
- package/dist/esm/hooks/utils/get-band-size.js +29 -0
- package/dist/esm/types/chart/axis.d.ts +3 -1
- package/dist/esm/types/chart/heatmap.d.ts +47 -0
- package/dist/esm/types/chart/heatmap.js +1 -0
- package/dist/esm/types/chart/series.d.ts +19 -2
- package/dist/esm/types/chart/tooltip.d.ts +7 -1
- package/dist/esm/types/index.d.ts +1 -0
- package/dist/esm/types/index.js +1 -0
- package/dist/esm/utils/chart/color.js +3 -2
- package/dist/esm/utils/chart/get-closest-data.js +18 -1
- package/dist/esm/utils/chart/index.js +10 -13
- package/dist/esm/utils/chart/series/waterfall.d.ts +1 -1
- package/dist/esm/utils/chart/series/waterfall.js +3 -3
- package/dist/esm/validation/index.js +4 -1
- package/package.json +1 -1
|
@@ -43,11 +43,13 @@ export const AxisY = (props) => {
|
|
|
43
43
|
.attr('y', (d) => d.y)
|
|
44
44
|
.attr('text-anchor', 'start');
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
if (preparedAxisData.domain) {
|
|
47
|
+
svgElement
|
|
48
|
+
.append('path')
|
|
49
|
+
.attr('class', b('domain'))
|
|
50
|
+
.attr('d', lineGenerator([preparedAxisData.domain.start, preparedAxisData.domain.end]))
|
|
51
|
+
.style('stroke', preparedAxisData.domain.lineColor);
|
|
52
|
+
}
|
|
51
53
|
const tickClassName = b('tick');
|
|
52
54
|
const ticks = svgElement
|
|
53
55
|
.selectAll(`.${tickClassName}`)
|
|
@@ -86,11 +86,14 @@ export async function prepareAxisData({ axis, split, scale, top: topOffset, widt
|
|
|
86
86
|
const axisPlotTopPosition = ((_a = split.plots[axis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
|
|
87
87
|
const axisHeight = ((_b = split.plots[axis.plotIndex]) === null || _b === void 0 ? void 0 : _b.height) || height;
|
|
88
88
|
const domainX = axis.position === 'left' ? 0 : width;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
let domain = null;
|
|
90
|
+
if (axis.visible) {
|
|
91
|
+
domain = {
|
|
92
|
+
start: [domainX, axisPlotTopPosition],
|
|
93
|
+
end: [domainX, axisPlotTopPosition + axisHeight],
|
|
94
|
+
lineColor: (_c = axis.lineColor) !== null && _c !== void 0 ? _c : '',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
94
97
|
const ticks = [];
|
|
95
98
|
const getTextSize = getTextSizeFn({ style: axis.labels.style });
|
|
96
99
|
const labelLineHeight = (await getTextSize('Tmp')).height;
|
|
@@ -226,7 +229,7 @@ export async function prepareAxisData({ axis, split, scale, top: topOffset, widt
|
|
|
226
229
|
content: titleContent,
|
|
227
230
|
style: axis.title.style,
|
|
228
231
|
size: rotatedTitleSize,
|
|
229
|
-
x
|
|
232
|
+
x,
|
|
230
233
|
y: axisPlotTopPosition + y,
|
|
231
234
|
rotate: rotateAngle,
|
|
232
235
|
offset: -(originalTextSize.height / titleContent.length) * (titleContent.length - 1),
|
|
@@ -74,7 +74,7 @@ export type AxisDomainData = {
|
|
|
74
74
|
export type AxisYData = {
|
|
75
75
|
id: string;
|
|
76
76
|
title: AxisTitleData | null;
|
|
77
|
-
domain: AxisDomainData;
|
|
77
|
+
domain: AxisDomainData | null;
|
|
78
78
|
ticks: AxisTickData[];
|
|
79
79
|
plotLines: AxisPlotLineData[];
|
|
80
80
|
plotBands: AxisPlotBandData[];
|
|
@@ -15,8 +15,8 @@ export function getTickValues({ scale, axis, labelLineHeight, series, }) {
|
|
|
15
15
|
}
|
|
16
16
|
const getScaleTicks = () => {
|
|
17
17
|
var _a;
|
|
18
|
+
const domainData = getDomainDataYBySeries(series);
|
|
18
19
|
if (series.some((s) => s.type === 'bar-y')) {
|
|
19
|
-
const domainData = getDomainDataYBySeries(series);
|
|
20
20
|
if (domainData.length < 3) {
|
|
21
21
|
return domainData;
|
|
22
22
|
}
|
|
@@ -13,6 +13,7 @@ import { Tooltip } from '../Tooltip';
|
|
|
13
13
|
import { useChartInnerHandlers } from './useChartInnerHandlers';
|
|
14
14
|
import { useChartInnerProps } from './useChartInnerProps';
|
|
15
15
|
import { useChartInnerState } from './useChartInnerState';
|
|
16
|
+
import { useAsyncState } from './utils';
|
|
16
17
|
import './styles.css';
|
|
17
18
|
const b = block('chart');
|
|
18
19
|
export const ChartInner = (props) => {
|
|
@@ -82,34 +83,27 @@ export const ChartInner = (props) => {
|
|
|
82
83
|
unpinTooltip === null || unpinTooltip === void 0 ? void 0 : unpinTooltip();
|
|
83
84
|
}
|
|
84
85
|
}, [prevWidth, width, prevHeight, height, tooltipPinned, unpinTooltip]);
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
height: boundsHeight,
|
|
102
|
-
split: preparedSplit,
|
|
103
|
-
series: preparedSeries,
|
|
104
|
-
});
|
|
105
|
-
items.push(axisData);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (countedRef.current === currentRun) {
|
|
109
|
-
setYAxisDataItems(items);
|
|
86
|
+
const setYAxisDataItems = React.useCallback(async () => {
|
|
87
|
+
const items = [];
|
|
88
|
+
for (let i = 0; i < yAxis.length; i++) {
|
|
89
|
+
const axis = yAxis[i];
|
|
90
|
+
const scale = yScale === null || yScale === void 0 ? void 0 : yScale[i];
|
|
91
|
+
if (scale) {
|
|
92
|
+
const axisData = await prepareAxisData({
|
|
93
|
+
axis,
|
|
94
|
+
scale,
|
|
95
|
+
top: boundsOffsetTop,
|
|
96
|
+
width: boundsWidth,
|
|
97
|
+
height: boundsHeight,
|
|
98
|
+
split: preparedSplit,
|
|
99
|
+
series: preparedSeries.filter((s) => s.visible),
|
|
100
|
+
});
|
|
101
|
+
items.push(axisData);
|
|
110
102
|
}
|
|
111
|
-
}
|
|
103
|
+
}
|
|
104
|
+
return items;
|
|
112
105
|
}, [boundsHeight, boundsOffsetTop, boundsWidth, preparedSeries, preparedSplit, yAxis, yScale]);
|
|
106
|
+
const yAxisDataItems = useAsyncState([], setYAxisDataItems);
|
|
113
107
|
return (React.createElement("div", { className: b() },
|
|
114
108
|
React.createElement("svg", { ref: svgRef, width: width, height: height,
|
|
115
109
|
// We use onPointerMove here because onMouseMove works incorrectly when the zoom setting is enabled:
|
|
@@ -41,10 +41,10 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
41
41
|
prevWidth: number | undefined;
|
|
42
42
|
shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|
|
43
43
|
shapesData: import("../../hooks").ShapeData[];
|
|
44
|
-
title: (import("
|
|
44
|
+
title: (import("../../types").ChartTitle & {
|
|
45
45
|
height: number;
|
|
46
46
|
}) | undefined;
|
|
47
|
-
tooltip: import("
|
|
47
|
+
tooltip: import("../../types").ChartTooltip<any> & {
|
|
48
48
|
enabled: boolean;
|
|
49
49
|
throttle: number;
|
|
50
50
|
};
|
|
@@ -8,7 +8,7 @@ import { getPreparedOptions } from '../../hooks/useSeries/prepare-options';
|
|
|
8
8
|
import { getActiveLegendItems } from '../../hooks/useSeries/utils';
|
|
9
9
|
import { useZoom } from '../../hooks/useZoom';
|
|
10
10
|
import { getSortedSeriesData, getZoomedSeriesData } from '../../utils';
|
|
11
|
-
import { hasAtLeastOneSeriesDataPerPlot } from './utils';
|
|
11
|
+
import { hasAtLeastOneSeriesDataPerPlot, useAsyncState } from './utils';
|
|
12
12
|
export function useChartInnerProps(props) {
|
|
13
13
|
var _a;
|
|
14
14
|
const { width, height, data, dispatcher, htmlLayout, svgContainer, plotNode, clipPathId } = props;
|
|
@@ -38,44 +38,52 @@ export function useChartInnerProps(props) {
|
|
|
38
38
|
zoomState,
|
|
39
39
|
});
|
|
40
40
|
}, [data.xAxis, data.yAxis, sortedSeriesData, zoomState]);
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
const { preparedSeries, preparedLegend, handleLegendItemClick } = useSeries({
|
|
42
|
+
colors,
|
|
43
|
+
legend: data.legend,
|
|
44
|
+
originalSeriesData: sortedSeriesData,
|
|
45
|
+
seriesData: zoomedSeriesData,
|
|
46
|
+
seriesOptions: data.series.options,
|
|
47
|
+
});
|
|
48
|
+
const setAxes = React.useCallback(async () => {
|
|
49
|
+
const seriesData = preparedSeries.filter((s) => s.visible);
|
|
50
|
+
const xAxis = await getPreparedXAxis({
|
|
45
51
|
xAxis: data.xAxis,
|
|
46
52
|
width,
|
|
47
|
-
seriesData
|
|
53
|
+
seriesData,
|
|
48
54
|
seriesOptions: preparedSeriesOptions,
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
const estimatedBoundsHeight = React.useMemo(() => {
|
|
55
|
+
});
|
|
56
|
+
let estimatedBoundsHeight = height;
|
|
52
57
|
if (xAxis) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
estimatedBoundsHeight =
|
|
59
|
+
height -
|
|
60
|
+
(xAxis.title.height +
|
|
61
|
+
xAxis.title.margin +
|
|
62
|
+
xAxis.labels.margin +
|
|
63
|
+
xAxis.labels.height +
|
|
64
|
+
(preparedLegend ? preparedLegend.height + preparedLegend.margin : 0) +
|
|
65
|
+
chart.margin.top +
|
|
66
|
+
chart.margin.bottom);
|
|
58
67
|
}
|
|
59
|
-
|
|
60
|
-
}, [height, xAxis]);
|
|
61
|
-
const [yAxis, setYAxis] = React.useState([]);
|
|
62
|
-
React.useEffect(() => {
|
|
63
|
-
setYAxis([]);
|
|
64
|
-
getPreparedYAxis({
|
|
68
|
+
const yAxis = await getPreparedYAxis({
|
|
65
69
|
height,
|
|
66
70
|
boundsHeight: estimatedBoundsHeight,
|
|
67
71
|
width,
|
|
68
|
-
seriesData
|
|
72
|
+
seriesData,
|
|
69
73
|
yAxis: data.yAxis,
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
});
|
|
75
|
+
return { xAxis, yAxis };
|
|
76
|
+
}, [
|
|
77
|
+
chart.margin,
|
|
78
|
+
data.xAxis,
|
|
79
|
+
data.yAxis,
|
|
80
|
+
height,
|
|
81
|
+
preparedLegend,
|
|
82
|
+
preparedSeries,
|
|
83
|
+
preparedSeriesOptions,
|
|
84
|
+
width,
|
|
85
|
+
]);
|
|
86
|
+
const { xAxis, yAxis } = useAsyncState({ xAxis: null, yAxis: [] }, setAxes);
|
|
79
87
|
const activeLegendItems = React.useMemo(() => getActiveLegendItems(preparedSeries), [preparedSeries]);
|
|
80
88
|
const { preparedSeries: preparedShapesSeries } = useShapeSeries({
|
|
81
89
|
colors,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { PreparedAxis } from '../../hooks/useChartOptions/types';
|
|
2
2
|
import type { ChartSeries } from '../../types';
|
|
3
3
|
export declare function hasAtLeastOneSeriesDataPerPlot(seriesData: ChartSeries[], yAxes?: PreparedAxis[]): boolean;
|
|
4
|
+
export declare function useAsyncState<T>(value: T, setState: () => Promise<T>): T;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
1
3
|
export function hasAtLeastOneSeriesDataPerPlot(seriesData, yAxes = []) {
|
|
2
4
|
const hasDataMap = new Map();
|
|
3
5
|
yAxes.forEach((yAxis) => {
|
|
@@ -26,3 +28,22 @@ export function hasAtLeastOneSeriesDataPerPlot(seriesData, yAxes = []) {
|
|
|
26
28
|
});
|
|
27
29
|
return [...hasDataMap.values()].every((hasData) => hasData);
|
|
28
30
|
}
|
|
31
|
+
export function useAsyncState(value, setState) {
|
|
32
|
+
const [stateValue, setValue] = React.useState(value);
|
|
33
|
+
const countedRef = React.useRef(0);
|
|
34
|
+
const prevValue = React.useRef(value);
|
|
35
|
+
const ready = React.useRef(false);
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
countedRef.current++;
|
|
38
|
+
(async function () {
|
|
39
|
+
const currentRun = countedRef.current;
|
|
40
|
+
const newValue = await setState();
|
|
41
|
+
ready.current = true;
|
|
42
|
+
if (countedRef.current === currentRun && !isEqual(prevValue.current, newValue)) {
|
|
43
|
+
setValue(newValue);
|
|
44
|
+
prevValue.current = newValue;
|
|
45
|
+
}
|
|
46
|
+
})();
|
|
47
|
+
}, [setState]);
|
|
48
|
+
return stateValue;
|
|
49
|
+
}
|
|
@@ -12,4 +12,4 @@ export interface ChartTooltipContentProps {
|
|
|
12
12
|
yAxis?: ChartYAxis;
|
|
13
13
|
qa?: string;
|
|
14
14
|
}
|
|
15
|
-
export declare const ChartTooltipContent: (props: ChartTooltipContentProps) => React.JSX.Element | null
|
|
15
|
+
export declare const ChartTooltipContent: React.MemoExoticComponent<(props: ChartTooltipContentProps) => React.JSX.Element | null>;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import isNil from 'lodash/isNil';
|
|
3
3
|
import { DefaultTooltipContent } from './DefaultTooltipContent';
|
|
4
|
-
export const ChartTooltipContent = (props) => {
|
|
4
|
+
export const ChartTooltipContent = React.memo((props) => {
|
|
5
5
|
const { hovered, xAxis, yAxis, renderer, rowRenderer, valueFormat, headerFormat, totals, pinned, qa, } = props;
|
|
6
6
|
if (!hovered) {
|
|
7
7
|
return null;
|
|
8
8
|
}
|
|
9
9
|
const customTooltip = renderer === null || renderer === void 0 ? void 0 : renderer({ hovered, xAxis, yAxis });
|
|
10
10
|
return isNil(customTooltip) ? (React.createElement(DefaultTooltipContent, { hovered: hovered, pinned: pinned, rowRenderer: rowRenderer, totals: totals, valueFormat: valueFormat, headerFormat: headerFormat, xAxis: xAxis, yAxis: yAxis, qa: qa })) : (customTooltip);
|
|
11
|
-
};
|
|
11
|
+
});
|
|
12
|
+
ChartTooltipContent.displayName = 'ChartTooltipContent';
|
|
@@ -39,7 +39,7 @@ export function getDefaultValueFormat({ axis, closestPointsRange, }) {
|
|
|
39
39
|
}
|
|
40
40
|
export const getMeasureValue = ({ data, xAxis, yAxis, headerFormat, }) => {
|
|
41
41
|
var _a, _b, _c, _d, _e, _f;
|
|
42
|
-
if (data.every((item) => ['pie', 'treemap', 'waterfall', 'sankey'].includes(item.series.type))) {
|
|
42
|
+
if (data.every((item) => ['pie', 'treemap', 'waterfall', 'sankey', 'heatmap'].includes(item.series.type))) {
|
|
43
43
|
return null;
|
|
44
44
|
}
|
|
45
45
|
if (data.some((item) => item.series.type === 'radar')) {
|
|
@@ -78,6 +78,7 @@ export function getHoveredValues(args) {
|
|
|
78
78
|
}
|
|
79
79
|
case 'pie':
|
|
80
80
|
case 'radar':
|
|
81
|
+
case 'heatmap':
|
|
81
82
|
case 'treemap': {
|
|
82
83
|
const seriesData = data;
|
|
83
84
|
return seriesData.value;
|
|
@@ -3,8 +3,8 @@ import { extent, scaleBand, scaleLinear, scaleLog, scaleUtc } from 'd3';
|
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { DEFAULT_AXIS_TYPE, SeriesType } from '../../constants';
|
|
5
5
|
import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getAxisHeight, getDataCategoryValue, getDefaultMaxXAxisValue, getDefaultMinXAxisValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
|
|
6
|
-
import { getBandSize } from '../utils';
|
|
7
6
|
import { getBarXLayoutForNumericScale, groupBarXDataByXValue } from '../utils/bar-x';
|
|
7
|
+
import { getBandSize } from '../utils/get-band-size';
|
|
8
8
|
const X_AXIS_ZOOM_PADDING = 0.02;
|
|
9
9
|
function validateArrayData(data) {
|
|
10
10
|
let hasNumberAndNullValues;
|
|
@@ -59,6 +59,10 @@ function getYScaleRange(args) {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
function isSeriesWithYAxisOffset(series) {
|
|
63
|
+
const types = [SeriesType.BarY, SeriesType.Heatmap];
|
|
64
|
+
return series.some((s) => types.includes(s.type));
|
|
65
|
+
}
|
|
62
66
|
// eslint-disable-next-line complexity
|
|
63
67
|
export function createYScale(args) {
|
|
64
68
|
const { axis, boundsHeight, series } = args;
|
|
@@ -89,9 +93,9 @@ export function createYScale(args) {
|
|
|
89
93
|
const scaleFn = axis.type === 'logarithmic' ? scaleLog : scaleLinear;
|
|
90
94
|
const scale = scaleFn().domain([yMin, yMax]).range(range);
|
|
91
95
|
let offsetMin = 0;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (
|
|
96
|
+
// We should ignore padding if we are drawing only one point on the plot.
|
|
97
|
+
let offsetMax = yMin === yMax ? 0 : boundsHeight * axis.maxPadding;
|
|
98
|
+
if (isSeriesWithYAxisOffset(series)) {
|
|
95
99
|
if (domain.length > 1) {
|
|
96
100
|
const bandWidth = getBandSize({
|
|
97
101
|
scale: scale,
|
|
@@ -138,8 +142,7 @@ export function createYScale(args) {
|
|
|
138
142
|
const scale = scaleUtc().domain([yMin, yMax]).range(range);
|
|
139
143
|
let offsetMin = 0;
|
|
140
144
|
let offsetMax = boundsHeight * axis.maxPadding;
|
|
141
|
-
|
|
142
|
-
if (barYSeries.length) {
|
|
145
|
+
if (isSeriesWithYAxisOffset(series)) {
|
|
143
146
|
if (Object.keys(domain).length > 1) {
|
|
144
147
|
const bandWidth = getBandSize({
|
|
145
148
|
scale: scale,
|
|
@@ -175,6 +178,10 @@ function calculateXAxisPadding(series) {
|
|
|
175
178
|
});
|
|
176
179
|
return result;
|
|
177
180
|
}
|
|
181
|
+
function isSeriesWithXAxisOffset(series) {
|
|
182
|
+
const types = [SeriesType.Heatmap];
|
|
183
|
+
return series.some((s) => types.includes(s.type));
|
|
184
|
+
}
|
|
178
185
|
function getXScaleRange({ boundsWidth, series, seriesOptions, hasZoomX, axis, maxPadding, }) {
|
|
179
186
|
const xAxisZoomPadding = boundsWidth * X_AXIS_ZOOM_PADDING;
|
|
180
187
|
const xRange = [0, boundsWidth - maxPadding];
|
|
@@ -252,7 +259,23 @@ export function createXScale(args) {
|
|
|
252
259
|
}
|
|
253
260
|
const scaleFn = xType === 'logarithmic' ? scaleLog : scaleLinear;
|
|
254
261
|
const scale = scaleFn().domain([xMin, xMax]).range(range);
|
|
255
|
-
|
|
262
|
+
let offsetMin = 0;
|
|
263
|
+
let offsetMax = 0;
|
|
264
|
+
const hasOffset = isSeriesWithXAxisOffset(series);
|
|
265
|
+
if (hasOffset) {
|
|
266
|
+
if (domainData.length > 1) {
|
|
267
|
+
const bandWidth = getBandSize({
|
|
268
|
+
scale: scale,
|
|
269
|
+
domain: domainData,
|
|
270
|
+
});
|
|
271
|
+
offsetMin += bandWidth / 2;
|
|
272
|
+
offsetMax += bandWidth / 2;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const domainOffsetMin = Math.abs(scale.invert(offsetMin) - scale.invert(0));
|
|
276
|
+
const domainOffsetMax = Math.abs(scale.invert(offsetMax) - scale.invert(0));
|
|
277
|
+
scale.domain([xMin - domainOffsetMin, xMax + domainOffsetMax]);
|
|
278
|
+
if (!hasZoomX && !hasOffset) {
|
|
256
279
|
// 10 is the default value for the number of ticks. Here, to preserve the appearance of a series with a small number of points
|
|
257
280
|
scale.nice(Math.max(10, domainData.length));
|
|
258
281
|
}
|
|
@@ -288,7 +311,23 @@ export function createXScale(args) {
|
|
|
288
311
|
const xMax = typeof xMaxProps === 'number' ? xMaxProps : xMaxTimestamp;
|
|
289
312
|
domain = [xMin, xMax];
|
|
290
313
|
const scale = scaleUtc().domain(domain).range(range);
|
|
291
|
-
|
|
314
|
+
let offsetMin = 0;
|
|
315
|
+
let offsetMax = 0;
|
|
316
|
+
const hasOffset = isSeriesWithXAxisOffset(series);
|
|
317
|
+
if (hasOffset) {
|
|
318
|
+
if (domainData.length > 1) {
|
|
319
|
+
const bandWidth = getBandSize({
|
|
320
|
+
scale: scale,
|
|
321
|
+
domain: domainData,
|
|
322
|
+
});
|
|
323
|
+
offsetMin += bandWidth / 2;
|
|
324
|
+
offsetMax += bandWidth / 2;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
const domainOffsetMin = Math.abs(scale.invert(offsetMin).getTime() - scale.invert(0).getTime());
|
|
328
|
+
const domainOffsetMax = Math.abs(scale.invert(offsetMax).getTime() - scale.invert(0).getTime());
|
|
329
|
+
scale.domain([xMin - domainOffsetMin, xMax + domainOffsetMax]);
|
|
330
|
+
if (!hasZoomX && !hasOffset) {
|
|
292
331
|
// 10 is the default value for the number of ticks. Here, to preserve the appearance of a series with a small number of points
|
|
293
332
|
scale.nice(Math.max(10, domainData.length));
|
|
294
333
|
}
|
|
@@ -2,7 +2,7 @@ import get from 'lodash/get';
|
|
|
2
2
|
import { getDefaultValueFormat } from '../../components/Tooltip/DefaultTooltipContent/utils';
|
|
3
3
|
import { getDomainDataXBySeries, getDomainDataYBySeries, getMinSpaceBetween } from '../../utils';
|
|
4
4
|
function getDefaultHeaderFormat({ seriesData, yAxes, xAxis, }) {
|
|
5
|
-
if (seriesData.every((item) => ['pie', 'treemap', 'waterfall', 'sankey', 'radar'].includes(item.type))) {
|
|
5
|
+
if (seriesData.every((item) => ['pie', 'treemap', 'waterfall', 'sankey', 'radar', 'heatmap'].includes(item.type))) {
|
|
6
6
|
return undefined;
|
|
7
7
|
}
|
|
8
8
|
if (seriesData.some((item) => item.type === 'bar-y')) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
|
-
import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, axisCrosshairDefaults, axisLabelsDefaults, xAxisTitleDefaults, } from '../../constants';
|
|
3
|
-
import { calculateCos, calculateNumericProperty, formatAxisTickLabel, getAxisItems, getClosestPointsRange, getDefaultDateFormat, getHorizontalHtmlTextHeight, getHorizontalSvgTextHeight, getLabelsSize, getMaxTickCount, getTicksCount, hasOverlappingLabels, wrapText, } from '../../utils';
|
|
2
|
+
import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, SeriesType, axisCrosshairDefaults, axisLabelsDefaults, xAxisTitleDefaults, } from '../../constants';
|
|
3
|
+
import { calculateCos, calculateNumericProperty, formatAxisTickLabel, getAxisItems, getClosestPointsRange, getDefaultDateFormat, getHorizontalHtmlTextHeight, getHorizontalSvgTextHeight, getLabelsSize, getMaxTickCount, getTicksCount, hasOverlappingLabels, isAxisRelatedSeries, wrapText, } from '../../utils';
|
|
4
4
|
import { createXScale } from '../useAxisScales';
|
|
5
5
|
import { getAxisCategories, prepareAxisPlotLabel } from './utils';
|
|
6
6
|
async function setLabelSettings({ axis, seriesData, seriesOptions, width, autoRotation = true, }) {
|
|
@@ -49,8 +49,18 @@ async function setLabelSettings({ axis, seriesData, seriesOptions, width, autoRo
|
|
|
49
49
|
axis.labels.height = Math.min(maxHeight, labelsHeight);
|
|
50
50
|
axis.labels.rotation = rotation;
|
|
51
51
|
}
|
|
52
|
+
function getMaxPaddingBySeries({ series }) {
|
|
53
|
+
if (series.some((s) => s.type === SeriesType.Heatmap)) {
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
return 0.01;
|
|
57
|
+
}
|
|
52
58
|
export const getPreparedXAxis = async ({ xAxis, seriesData, seriesOptions, width, }) => {
|
|
53
59
|
var _a, _b, _c, _d, _e;
|
|
60
|
+
const hasAxisRelatedSeries = seriesData.some(isAxisRelatedSeries);
|
|
61
|
+
if (!hasAxisRelatedSeries) {
|
|
62
|
+
return Promise.resolve(null);
|
|
63
|
+
}
|
|
54
64
|
const titleText = get(xAxis, 'title.text', '');
|
|
55
65
|
const titleStyle = Object.assign(Object.assign({}, xAxisTitleDefaults.style), get(xAxis, 'title.style'));
|
|
56
66
|
const titleMaxRowsCount = get(xAxis, 'title.maxRowCount', xAxisTitleDefaults.maxRowCount);
|
|
@@ -70,6 +80,7 @@ export const getPreparedXAxis = async ({ xAxis, seriesData, seriesOptions, width
|
|
|
70
80
|
const labelsLineHeight = labelsHtml
|
|
71
81
|
? getHorizontalHtmlTextHeight({ text: 'Tmp', style: labelsStyle })
|
|
72
82
|
: getHorizontalSvgTextHeight({ text: 'Tmp', style: labelsStyle });
|
|
83
|
+
const shouldHideGrid = seriesData.some((s) => s.type === SeriesType.Heatmap);
|
|
73
84
|
const preparedXAxis = {
|
|
74
85
|
type: get(xAxis, 'type', 'linear'),
|
|
75
86
|
labels: {
|
|
@@ -100,9 +111,9 @@ export const getPreparedXAxis = async ({ xAxis, seriesData, seriesOptions, width
|
|
|
100
111
|
},
|
|
101
112
|
min: get(xAxis, 'min'),
|
|
102
113
|
max: get(xAxis, 'max'),
|
|
103
|
-
maxPadding: get(xAxis, 'maxPadding',
|
|
114
|
+
maxPadding: get(xAxis, 'maxPadding', getMaxPaddingBySeries({ series: seriesData })),
|
|
104
115
|
grid: {
|
|
105
|
-
enabled: get(xAxis, 'grid.enabled', true),
|
|
116
|
+
enabled: shouldHideGrid ? false : get(xAxis, 'grid.enabled', true),
|
|
106
117
|
},
|
|
107
118
|
ticks: {
|
|
108
119
|
pixelInterval: ((_c = xAxis === null || xAxis === void 0 ? void 0 : xAxis.ticks) === null || _c === void 0 ? void 0 : _c.interval)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import { getTickValues } from '../../components/AxisY/utils';
|
|
3
|
-
import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, DEFAULT_AXIS_TYPE, axisCrosshairDefaults, axisLabelsDefaults, yAxisTitleDefaults, } from '../../constants';
|
|
4
|
-
import { calculateNumericProperty, formatAxisTickLabel,
|
|
3
|
+
import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, DEFAULT_AXIS_TYPE, SeriesType, axisCrosshairDefaults, axisLabelsDefaults, yAxisTitleDefaults, } from '../../constants';
|
|
4
|
+
import { calculateNumericProperty, formatAxisTickLabel, getDefaultDateFormat, getDefaultMinYAxisValue, getHorizontalHtmlTextHeight, getHorizontalSvgTextHeight, getLabelsSize, getMinSpaceBetween, getTextSizeFn, isAxisRelatedSeries, wrapText, } from '../../utils';
|
|
5
5
|
import { createYScale } from '../useAxisScales';
|
|
6
6
|
import { getAxisCategories, prepareAxisPlotLabel } from './utils';
|
|
7
7
|
const getAxisLabelMaxWidth = async (args) => {
|
|
@@ -20,8 +20,7 @@ const getAxisLabelMaxWidth = async (args) => {
|
|
|
20
20
|
const getTextSize = getTextSizeFn({ style: axis.labels.style });
|
|
21
21
|
const labelLineHeight = (await getTextSize('Tmp')).height;
|
|
22
22
|
const tickValues = getTickValues({ axis, scale, labelLineHeight, series: seriesData });
|
|
23
|
-
const
|
|
24
|
-
const tickStep = getClosestPointsRange(axis, ticks);
|
|
23
|
+
const tickStep = getMinSpaceBetween(tickValues, (d) => Number(d.value));
|
|
25
24
|
if (axis.type === 'datetime' && !axis.labels.dateFormat) {
|
|
26
25
|
axis.labels.dateFormat = getDefaultDateFormat(tickStep);
|
|
27
26
|
}
|
|
@@ -38,6 +37,12 @@ const getAxisLabelMaxWidth = async (args) => {
|
|
|
38
37
|
});
|
|
39
38
|
return { height: size.maxHeight, width: size.maxWidth };
|
|
40
39
|
};
|
|
40
|
+
function getMaxPaddingBySeries({ series }) {
|
|
41
|
+
if (series.some((s) => s.type === SeriesType.Heatmap)) {
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
return 0.05;
|
|
45
|
+
}
|
|
41
46
|
export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxis, }) => {
|
|
42
47
|
const axisByPlot = [];
|
|
43
48
|
const axisItems = yAxis || [{}];
|
|
@@ -72,6 +77,7 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
72
77
|
})).slice(0, titleMaxRowsCount);
|
|
73
78
|
const titleSize = await getLabelsSize({ labels: [titleText], style: titleStyle });
|
|
74
79
|
const axisType = get(axisItem, 'type', DEFAULT_AXIS_TYPE);
|
|
80
|
+
const shouldHideGrid = axisItem.visible === false || seriesData.some((s) => s.type === SeriesType.Heatmap);
|
|
75
81
|
const preparedAxis = {
|
|
76
82
|
type: axisType,
|
|
77
83
|
labels: {
|
|
@@ -104,10 +110,12 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
104
110
|
},
|
|
105
111
|
min: (_c = get(axisItem, 'min')) !== null && _c !== void 0 ? _c : getDefaultMinYAxisValue(seriesData),
|
|
106
112
|
max: get(axisItem, 'max'),
|
|
107
|
-
maxPadding: get(axisItem, 'maxPadding',
|
|
113
|
+
maxPadding: get(axisItem, 'maxPadding', getMaxPaddingBySeries({ series: seriesData })),
|
|
108
114
|
grid: {
|
|
109
|
-
enabled:
|
|
110
|
-
|
|
115
|
+
enabled: shouldHideGrid
|
|
116
|
+
? false
|
|
117
|
+
: get(axisItem, 'grid.enabled', firstPlotAxis ||
|
|
118
|
+
(!firstPlotAxis && !((_d = axisByPlot[plotIndex][0].visible) !== null && _d !== void 0 ? _d : true))),
|
|
111
119
|
},
|
|
112
120
|
ticks: {
|
|
113
121
|
pixelInterval: ((_e = axisItem.ticks) === null || _e === void 0 ? void 0 : _e.interval)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ScaleOrdinal } from 'd3';
|
|
2
|
+
import type { ChartSeriesOptions, HeatmapSeries } from '../../types';
|
|
3
|
+
import type { PreparedLegend, PreparedSeries } from './types';
|
|
4
|
+
type PrepareHeatmapSeriesArgs = {
|
|
5
|
+
colorScale: ScaleOrdinal<string, string>;
|
|
6
|
+
series: HeatmapSeries[];
|
|
7
|
+
legend: PreparedLegend;
|
|
8
|
+
seriesOptions?: ChartSeriesOptions;
|
|
9
|
+
};
|
|
10
|
+
export declare function prepareHeatmapSeries(args: PrepareHeatmapSeriesArgs): PreparedSeries[];
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import get from 'lodash/get';
|
|
2
|
+
import { DEFAULT_DATALABELS_STYLE } from '../../constants';
|
|
3
|
+
import { getUniqId } from '../../utils';
|
|
4
|
+
import { DEFAULT_DATALABELS_PADDING } from './constants';
|
|
5
|
+
import { prepareLegendSymbol } from './utils';
|
|
6
|
+
export function prepareHeatmapSeries(args) {
|
|
7
|
+
const { colorScale, series: seriesList, seriesOptions, legend } = args;
|
|
8
|
+
return seriesList.map((series) => {
|
|
9
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
10
|
+
const name = series.name || '';
|
|
11
|
+
const color = series.color || colorScale(name);
|
|
12
|
+
return {
|
|
13
|
+
type: series.type,
|
|
14
|
+
color,
|
|
15
|
+
name,
|
|
16
|
+
id: getUniqId(),
|
|
17
|
+
visible: get(series, 'visible', true),
|
|
18
|
+
legend: {
|
|
19
|
+
enabled: get(series, 'legend.enabled', legend.enabled),
|
|
20
|
+
symbol: prepareLegendSymbol(series),
|
|
21
|
+
},
|
|
22
|
+
data: series.data,
|
|
23
|
+
dataLabels: {
|
|
24
|
+
enabled: ((_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.enabled) || false,
|
|
25
|
+
style: Object.assign({}, DEFAULT_DATALABELS_STYLE, (_b = series.dataLabels) === null || _b === void 0 ? void 0 : _b.style),
|
|
26
|
+
padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING),
|
|
27
|
+
html: (_d = (_c = series.dataLabels) === null || _c === void 0 ? void 0 : _c.html) !== null && _d !== void 0 ? _d : false,
|
|
28
|
+
format: (_e = series.dataLabels) === null || _e === void 0 ? void 0 : _e.format,
|
|
29
|
+
},
|
|
30
|
+
cursor: get(series, 'cursor', null),
|
|
31
|
+
yAxis: get(series, 'yAxis', 0),
|
|
32
|
+
tooltip: series.tooltip,
|
|
33
|
+
borderColor: (_h = (_f = series.borderColor) !== null && _f !== void 0 ? _f : (_g = seriesOptions === null || seriesOptions === void 0 ? void 0 : seriesOptions.heatmap) === null || _g === void 0 ? void 0 : _g.borderColor) !== null && _h !== void 0 ? _h : 'var(--gcharts-shape-border-color)',
|
|
34
|
+
borderWidth: (_l = (_j = series.borderWidth) !== null && _j !== void 0 ? _j : (_k = seriesOptions === null || seriesOptions === void 0 ? void 0 : seriesOptions.heatmap) === null || _k === void 0 ? void 0 : _k.borderWidth) !== null && _l !== void 0 ? _l : 0,
|
|
35
|
+
};
|
|
36
|
+
}, []);
|
|
37
|
+
}
|