@gravity-ui/chartkit 5.6.0 → 5.8.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/build/plugins/d3/examples/area/NegativeValues.d.ts +2 -0
- package/build/plugins/d3/examples/area/NegativeValues.js +24 -0
- package/build/plugins/d3/examples/bar-x/NegativeValues.d.ts +2 -0
- package/build/plugins/d3/examples/bar-x/NegativeValues.js +41 -0
- package/build/plugins/d3/examples/bar-y/NegativeValues.d.ts +2 -0
- package/build/plugins/d3/examples/bar-y/NegativeValues.js +40 -0
- package/build/plugins/d3/renderer/components/AxisX.d.ts +2 -1
- package/build/plugins/d3/renderer/components/AxisX.js +13 -3
- package/build/plugins/d3/renderer/components/AxisY.d.ts +4 -3
- package/build/plugins/d3/renderer/components/AxisY.js +17 -8
- package/build/plugins/d3/renderer/components/Chart.js +14 -3
- package/build/plugins/d3/renderer/components/PlotTitle.d.ts +7 -0
- package/build/plugins/d3/renderer/components/PlotTitle.js +12 -0
- package/build/plugins/d3/renderer/components/styles.css +7 -1
- package/build/plugins/d3/renderer/hooks/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/index.js +1 -0
- package/build/plugins/d3/renderer/hooks/useAxisScales/index.d.ts +3 -1
- package/build/plugins/d3/renderer/hooks/useAxisScales/index.js +30 -14
- package/build/plugins/d3/renderer/hooks/useChartDimensions/utils.js +13 -2
- package/build/plugins/d3/renderer/hooks/useChartOptions/types.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.d.ts +2 -2
- package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +14 -2
- package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.d.ts +2 -2
- package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +18 -8
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-y.js +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.js +15 -5
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +5 -3
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +5 -3
- package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.d.ts +6 -4
- package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.js +6 -3
- package/build/plugins/d3/renderer/hooks/useSplit/index.d.ts +14 -0
- package/build/plugins/d3/renderer/hooks/useSplit/index.js +57 -0
- package/build/plugins/d3/renderer/hooks/useSplit/types.d.ts +17 -0
- package/build/plugins/d3/renderer/hooks/useSplit/types.js +1 -0
- package/build/plugins/d3/renderer/utils/axis-generators/bottom.d.ts +1 -1
- package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +16 -8
- package/build/plugins/d3/renderer/utils/axis.d.ts +6 -2
- package/build/plugins/d3/renderer/utils/axis.js +7 -0
- package/build/plugins/d3/renderer/utils/index.d.ts +4 -1
- package/build/plugins/d3/renderer/utils/index.js +51 -25
- package/build/plugins/highcharts/renderer/helpers/config/config.js +2 -2
- package/build/types/widget-data/axis.d.ts +10 -0
- package/build/types/widget-data/bar-y.d.ts +2 -1
- package/build/types/widget-data/index.d.ts +8 -3
- package/build/types/widget-data/index.js +1 -0
- package/build/types/widget-data/split.d.ts +13 -0
- package/build/types/widget-data/split.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ChartKit } from '../../../../components/ChartKit';
|
|
3
|
+
import { ExampleWrapper } from '../ExampleWrapper';
|
|
4
|
+
export const NegativeValues = () => {
|
|
5
|
+
const data = [
|
|
6
|
+
{ x: 0, y: 10 },
|
|
7
|
+
{ x: 1, y: 20 },
|
|
8
|
+
{ x: 2, y: -30 },
|
|
9
|
+
{ x: 3, y: 100 },
|
|
10
|
+
];
|
|
11
|
+
const widgetData = {
|
|
12
|
+
series: {
|
|
13
|
+
data: [
|
|
14
|
+
{
|
|
15
|
+
type: 'area',
|
|
16
|
+
data: data,
|
|
17
|
+
name: 'Min temperature',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
return (React.createElement(ExampleWrapper, null,
|
|
23
|
+
React.createElement(ChartKit, { type: "d3", data: widgetData })));
|
|
24
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { dateTime } from '@gravity-ui/date-utils';
|
|
3
|
+
import { ChartKit } from '../../../../components/ChartKit';
|
|
4
|
+
import { ExampleWrapper } from '../ExampleWrapper';
|
|
5
|
+
import marsWeatherData from '../mars-weather';
|
|
6
|
+
export const NegativeValues = () => {
|
|
7
|
+
const data = marsWeatherData.map((d) => ({
|
|
8
|
+
x: dateTime({ input: d.terrestrial_date, format: 'YYYY-MM-DD' }).valueOf(),
|
|
9
|
+
y: d.min_temp,
|
|
10
|
+
}));
|
|
11
|
+
const widgetData = {
|
|
12
|
+
series: {
|
|
13
|
+
data: [
|
|
14
|
+
{
|
|
15
|
+
type: 'bar-x',
|
|
16
|
+
data: data,
|
|
17
|
+
name: 'Min temperature',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
yAxis: [
|
|
22
|
+
{
|
|
23
|
+
title: {
|
|
24
|
+
text: 'Min temperature',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
xAxis: {
|
|
29
|
+
type: 'datetime',
|
|
30
|
+
title: {
|
|
31
|
+
text: 'Terrestrial date',
|
|
32
|
+
},
|
|
33
|
+
ticks: { pixelInterval: 200 },
|
|
34
|
+
},
|
|
35
|
+
title: {
|
|
36
|
+
text: 'Mars weather',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
return (React.createElement(ExampleWrapper, null,
|
|
40
|
+
React.createElement(ChartKit, { type: "d3", data: widgetData })));
|
|
41
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { dateTime } from '@gravity-ui/date-utils';
|
|
3
|
+
import { ChartKit } from '../../../../components/ChartKit';
|
|
4
|
+
import { ExampleWrapper } from '../ExampleWrapper';
|
|
5
|
+
import marsWeatherData from '../mars-weather';
|
|
6
|
+
export const NegativeValues = () => {
|
|
7
|
+
const data = marsWeatherData.map((d) => ({
|
|
8
|
+
y: dateTime({ input: d.terrestrial_date, format: 'YYYY-MM-DD' }).valueOf(),
|
|
9
|
+
x: d.min_temp,
|
|
10
|
+
}));
|
|
11
|
+
const widgetData = {
|
|
12
|
+
series: {
|
|
13
|
+
data: [
|
|
14
|
+
{
|
|
15
|
+
type: 'bar-y',
|
|
16
|
+
data: data,
|
|
17
|
+
name: 'Min temperature',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
xAxis: {
|
|
22
|
+
title: {
|
|
23
|
+
text: 'Min temperature',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
yAxis: [
|
|
27
|
+
{
|
|
28
|
+
type: 'datetime',
|
|
29
|
+
title: {
|
|
30
|
+
text: 'Terrestrial date',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
title: {
|
|
35
|
+
text: 'Mars weather',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
return (React.createElement(ExampleWrapper, null,
|
|
39
|
+
React.createElement(ChartKit, { type: "d3", data: widgetData })));
|
|
40
|
+
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { ChartScale, PreparedAxis } from '../hooks';
|
|
2
|
+
import type { ChartScale, PreparedAxis, PreparedSplit } from '../hooks';
|
|
3
3
|
type Props = {
|
|
4
4
|
axis: PreparedAxis;
|
|
5
5
|
width: number;
|
|
6
6
|
height: number;
|
|
7
7
|
scale: ChartScale;
|
|
8
|
+
split: PreparedSplit;
|
|
8
9
|
};
|
|
9
10
|
export declare const AxisX: React.NamedExoticComponent<Props>;
|
|
10
11
|
export {};
|
|
@@ -18,16 +18,26 @@ function getLabelFormatter({ axis, scale }) {
|
|
|
18
18
|
});
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
-
export const AxisX = React.memo(function AxisX(
|
|
21
|
+
export const AxisX = React.memo(function AxisX(props) {
|
|
22
|
+
const { axis, width, height: totalHeight, scale, split } = props;
|
|
22
23
|
const ref = React.useRef(null);
|
|
23
24
|
React.useEffect(() => {
|
|
24
25
|
if (!ref.current) {
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
28
|
+
let tickItems = [];
|
|
29
|
+
if (axis.grid.enabled) {
|
|
30
|
+
tickItems = new Array(split.plots.length || 1).fill(null).map((_, index) => {
|
|
31
|
+
var _a, _b;
|
|
32
|
+
const top = ((_a = split.plots[index]) === null || _a === void 0 ? void 0 : _a.top) || 0;
|
|
33
|
+
const height = ((_b = split.plots[index]) === null || _b === void 0 ? void 0 : _b.height) || totalHeight;
|
|
34
|
+
return [-top, -(top + height)];
|
|
35
|
+
});
|
|
36
|
+
}
|
|
27
37
|
const xAxisGenerator = axisBottom({
|
|
28
38
|
scale: scale,
|
|
29
39
|
ticks: {
|
|
30
|
-
|
|
40
|
+
items: tickItems,
|
|
31
41
|
labelFormat: getLabelFormatter({ axis, scale }),
|
|
32
42
|
labelsPaddings: axis.labels.padding,
|
|
33
43
|
labelsMargin: axis.labels.margin,
|
|
@@ -59,6 +69,6 @@ export const AxisX = React.memo(function AxisX({ axis, width, height, scale }) {
|
|
|
59
69
|
.text(axis.title.text)
|
|
60
70
|
.call(setEllipsisForOverflowText, width);
|
|
61
71
|
}
|
|
62
|
-
}, [axis, width,
|
|
72
|
+
}, [axis, width, totalHeight, scale, split]);
|
|
63
73
|
return React.createElement("g", { ref: ref });
|
|
64
74
|
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { ChartScale, PreparedAxis } from '../hooks';
|
|
2
|
+
import type { ChartScale, PreparedAxis, PreparedSplit } from '../hooks';
|
|
3
3
|
type Props = {
|
|
4
|
-
|
|
4
|
+
axes: PreparedAxis[];
|
|
5
5
|
scale: ChartScale[];
|
|
6
6
|
width: number;
|
|
7
7
|
height: number;
|
|
8
|
+
split: PreparedSplit;
|
|
8
9
|
};
|
|
9
|
-
export declare const AxisY: (
|
|
10
|
+
export declare const AxisY: (props: Props) => React.JSX.Element;
|
|
10
11
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { axisLeft, axisRight, line, select } from 'd3';
|
|
3
3
|
import { block } from '../../../../utils/cn';
|
|
4
|
-
import { calculateCos, calculateSin, formatAxisTickLabel, getClosestPointsRange, getScaleTicks, getTicksCount, parseTransformStyle, setEllipsisForOverflowText, setEllipsisForOverflowTexts, } from '../utils';
|
|
4
|
+
import { calculateCos, calculateSin, formatAxisTickLabel, getAxisHeight, getClosestPointsRange, getScaleTicks, getTicksCount, parseTransformStyle, setEllipsisForOverflowText, setEllipsisForOverflowTexts, } from '../utils';
|
|
5
5
|
const b = block('d3-axis');
|
|
6
6
|
function transformLabel(args) {
|
|
7
7
|
const { node, axis } = args;
|
|
@@ -51,7 +51,9 @@ function getAxisGenerator(args) {
|
|
|
51
51
|
}
|
|
52
52
|
return axisGenerator;
|
|
53
53
|
}
|
|
54
|
-
export const AxisY = (
|
|
54
|
+
export const AxisY = (props) => {
|
|
55
|
+
const { axes, width, height: totalHeight, scale, split } = props;
|
|
56
|
+
const height = getAxisHeight({ split, boundsHeight: totalHeight });
|
|
55
57
|
const ref = React.useRef(null);
|
|
56
58
|
React.useEffect(() => {
|
|
57
59
|
if (!ref.current) {
|
|
@@ -61,15 +63,22 @@ export const AxisY = ({ axises, width, height, scale }) => {
|
|
|
61
63
|
svgElement.selectAll('*').remove();
|
|
62
64
|
const axisSelection = svgElement
|
|
63
65
|
.selectAll('axis')
|
|
64
|
-
.data(
|
|
66
|
+
.data(axes)
|
|
65
67
|
.join('g')
|
|
66
68
|
.attr('class', b())
|
|
67
|
-
.style('transform', (
|
|
69
|
+
.style('transform', (d) => {
|
|
70
|
+
var _a;
|
|
71
|
+
const top = ((_a = split.plots[d.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
|
|
72
|
+
if (d.position === 'left') {
|
|
73
|
+
return `translate(0, ${top}px)`;
|
|
74
|
+
}
|
|
75
|
+
return `translate(${width}px, 0)`;
|
|
76
|
+
});
|
|
68
77
|
axisSelection.each((d, index, node) => {
|
|
69
78
|
const seriesScale = scale[index];
|
|
70
79
|
const axisItem = select(node[index]);
|
|
71
80
|
const yAxisGenerator = getAxisGenerator({
|
|
72
|
-
axisGenerator:
|
|
81
|
+
axisGenerator: d.position === 'left'
|
|
73
82
|
? axisLeft(seriesScale)
|
|
74
83
|
: axisRight(seriesScale),
|
|
75
84
|
preparedAxis: d,
|
|
@@ -136,13 +145,13 @@ export const AxisY = ({ axises, width, height, scale }) => {
|
|
|
136
145
|
.attr('class', b('title'))
|
|
137
146
|
.attr('text-anchor', 'middle')
|
|
138
147
|
.attr('dy', (d) => -(d.title.margin + d.labels.margin + d.labels.width))
|
|
139
|
-
.attr('dx', (
|
|
148
|
+
.attr('dx', (d) => (d.position === 'left' ? -height / 2 : height / 2))
|
|
140
149
|
.attr('font-size', (d) => d.title.style.fontSize)
|
|
141
|
-
.attr('transform', (
|
|
150
|
+
.attr('transform', (d) => (d.position === 'left' ? 'rotate(-90)' : 'rotate(90)'))
|
|
142
151
|
.text((d) => d.title.text)
|
|
143
152
|
.each((_d, index, node) => {
|
|
144
153
|
return setEllipsisForOverflowText(select(node[index]), height);
|
|
145
154
|
});
|
|
146
|
-
}, [
|
|
155
|
+
}, [axes, width, height, scale, split]);
|
|
147
156
|
return React.createElement("g", { ref: ref, className: b('container') });
|
|
148
157
|
};
|
|
@@ -7,10 +7,12 @@ import { useAxisScales, useChartDimensions, useChartOptions, useSeries, useShape
|
|
|
7
7
|
import { getYAxisWidth } from '../hooks/useChartDimensions/utils';
|
|
8
8
|
import { getPreparedXAxis } from '../hooks/useChartOptions/x-axis';
|
|
9
9
|
import { getPreparedYAxis } from '../hooks/useChartOptions/y-axis';
|
|
10
|
+
import { useSplit } from '../hooks/useSplit';
|
|
10
11
|
import { getClosestPoints } from '../utils/get-closest-data';
|
|
11
12
|
import { AxisX } from './AxisX';
|
|
12
13
|
import { AxisY } from './AxisY';
|
|
13
14
|
import { Legend } from './Legend';
|
|
15
|
+
import { PlotTitle } from './PlotTitle';
|
|
14
16
|
import { Title } from './Title';
|
|
15
17
|
import { Tooltip } from './Tooltip';
|
|
16
18
|
import './styles.css';
|
|
@@ -27,7 +29,10 @@ export const Chart = (props) => {
|
|
|
27
29
|
data,
|
|
28
30
|
});
|
|
29
31
|
const xAxis = React.useMemo(() => getPreparedXAxis({ xAxis: data.xAxis, width, series: data.series.data }), [data, width]);
|
|
30
|
-
const yAxis = React.useMemo(() => getPreparedYAxis({
|
|
32
|
+
const yAxis = React.useMemo(() => getPreparedYAxis({
|
|
33
|
+
series: data.series.data,
|
|
34
|
+
yAxis: data.yAxis,
|
|
35
|
+
}), [data]);
|
|
31
36
|
const { legendItems, legendConfig, preparedSeries, preparedSeriesOptions, preparedLegend, handleLegendItemClick, } = useSeries({
|
|
32
37
|
chartWidth: width,
|
|
33
38
|
chartHeight: height,
|
|
@@ -45,12 +50,14 @@ export const Chart = (props) => {
|
|
|
45
50
|
preparedYAxis: yAxis,
|
|
46
51
|
preparedSeries: preparedSeries,
|
|
47
52
|
});
|
|
53
|
+
const preparedSplit = useSplit({ split: data.split, boundsHeight, chartWidth: width });
|
|
48
54
|
const { xScale, yScale } = useAxisScales({
|
|
49
55
|
boundsWidth,
|
|
50
56
|
boundsHeight,
|
|
51
57
|
series: preparedSeries,
|
|
52
58
|
xAxis,
|
|
53
59
|
yAxis,
|
|
60
|
+
split: preparedSplit,
|
|
54
61
|
});
|
|
55
62
|
const { shapes, shapesData } = useShapes({
|
|
56
63
|
boundsWidth,
|
|
@@ -62,6 +69,7 @@ export const Chart = (props) => {
|
|
|
62
69
|
xScale,
|
|
63
70
|
yAxis,
|
|
64
71
|
yScale,
|
|
72
|
+
split: preparedSplit,
|
|
65
73
|
});
|
|
66
74
|
const clickHandler = (_b = (_a = data.chart) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.click;
|
|
67
75
|
React.useEffect(() => {
|
|
@@ -97,11 +105,14 @@ export const Chart = (props) => {
|
|
|
97
105
|
return (React.createElement(React.Fragment, null,
|
|
98
106
|
React.createElement("svg", { ref: svgRef, className: b(), width: width, height: height, onMouseMove: throttledHandleMouseMove, onMouseLeave: handleMouseLeave },
|
|
99
107
|
title && React.createElement(Title, Object.assign({}, title, { chartWidth: width })),
|
|
108
|
+
React.createElement("g", { transform: `translate(0, ${boundsOffsetTop})` }, preparedSplit.plots.map((plot, index) => {
|
|
109
|
+
return React.createElement(PlotTitle, { key: `plot-${index}`, title: plot.title });
|
|
110
|
+
})),
|
|
100
111
|
React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[boundsOffsetLeft, boundsOffsetTop].join(',')})` },
|
|
101
112
|
xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length) && (React.createElement(React.Fragment, null,
|
|
102
|
-
React.createElement(AxisY, {
|
|
113
|
+
React.createElement(AxisY, { axes: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale, split: preparedSplit }),
|
|
103
114
|
React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
|
|
104
|
-
React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale })))),
|
|
115
|
+
React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale, split: preparedSplit })))),
|
|
105
116
|
shapes),
|
|
106
117
|
preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick }))),
|
|
107
118
|
React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: tooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0] })));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { block } from '../../../../utils/cn';
|
|
3
|
+
const b = block('d3-plot-title');
|
|
4
|
+
export const PlotTitle = (props) => {
|
|
5
|
+
const { title } = props;
|
|
6
|
+
if (!title) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const { x, y, text, style, height } = title;
|
|
10
|
+
return (React.createElement("text", { className: b(), dx: x, dy: y, dominantBaseline: "middle", textAnchor: "middle", style: Object.assign({ lineHeight: `${height}px` }, style) },
|
|
11
|
+
React.createElement("tspan", null, text)));
|
|
12
|
+
};
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
alignment-baseline: after-edge;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
.chartkit-d3-axis .tick line {
|
|
14
|
+
.chartkit-d3-axis .tick line, .chartkit-d3-axis .tick path {
|
|
15
15
|
stroke: var(--g-color-line-generic);
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -79,6 +79,12 @@
|
|
|
79
79
|
fill: var(--g-color-text-primary);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
.chartkit-d3-plot-title {
|
|
83
|
+
font-size: var(--g-text-subheader-3-font-size);
|
|
84
|
+
font-weight: var(--g-text-subheader-font-weight);
|
|
85
|
+
fill: var(--g-color-text-secondary);
|
|
86
|
+
}
|
|
87
|
+
|
|
82
88
|
.chartkit-d3-tooltip[class] {
|
|
83
89
|
--g-popup-border-width: 0;
|
|
84
90
|
pointer-events: none;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ScaleBand, ScaleLinear, ScaleTime } from 'd3';
|
|
2
2
|
import { ChartKitWidgetAxis, ChartKitWidgetSeries } from '../../../../../types';
|
|
3
3
|
import type { PreparedAxis } from '../useChartOptions/types';
|
|
4
|
-
import { PreparedSeries } from '../useSeries/types';
|
|
4
|
+
import type { PreparedSeries } from '../useSeries/types';
|
|
5
|
+
import type { PreparedSplit } from '../useSplit/types';
|
|
5
6
|
export type ChartScale = ScaleLinear<number, number> | ScaleBand<string> | ScaleTime<number, number>;
|
|
6
7
|
type Args = {
|
|
7
8
|
boundsWidth: number;
|
|
@@ -9,6 +10,7 @@ type Args = {
|
|
|
9
10
|
series: PreparedSeries[];
|
|
10
11
|
xAxis: PreparedAxis;
|
|
11
12
|
yAxis: PreparedAxis[];
|
|
13
|
+
split: PreparedSplit;
|
|
12
14
|
};
|
|
13
15
|
type ReturnValue = {
|
|
14
16
|
xScale?: ChartScale;
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { extent, scaleBand, scaleLinear, scaleUtc } from 'd3';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { DEFAULT_AXIS_TYPE } from '../../constants';
|
|
5
|
-
import { getDataCategoryValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
|
|
5
|
+
import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getAxisHeight, getDataCategoryValue, getDefaultMaxXAxisValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
|
|
6
6
|
const isNumericalArrayData = (data) => {
|
|
7
7
|
return data.every((d) => typeof d === 'number' || d === null);
|
|
8
8
|
};
|
|
@@ -28,9 +28,13 @@ export function createYScale(axis, series, boundsHeight) {
|
|
|
28
28
|
const domain = getDomainDataYBySeries(series);
|
|
29
29
|
const range = [boundsHeight, boundsHeight * axis.maxPadding];
|
|
30
30
|
if (isNumericalArrayData(domain)) {
|
|
31
|
-
const [domainYMin,
|
|
31
|
+
const [domainYMin, domainMax] = extent(domain);
|
|
32
32
|
const yMinValue = typeof yMin === 'number' ? yMin : domainYMin;
|
|
33
|
-
|
|
33
|
+
let yMaxValue = domainMax;
|
|
34
|
+
if (series.some((s) => CHART_SERIES_WITH_VOLUME_ON_Y_AXIS.includes(s.type))) {
|
|
35
|
+
yMaxValue = Math.max(yMaxValue, 0);
|
|
36
|
+
}
|
|
37
|
+
return scaleLinear().domain([yMinValue, yMaxValue]).range(range).nice();
|
|
34
38
|
}
|
|
35
39
|
break;
|
|
36
40
|
}
|
|
@@ -69,8 +73,11 @@ function calculateXAxisPadding(series) {
|
|
|
69
73
|
switch (s.type) {
|
|
70
74
|
case 'bar-y': {
|
|
71
75
|
// Since labels can be located to the right of the bar, need to add an additional space
|
|
72
|
-
const
|
|
73
|
-
|
|
76
|
+
const inside = get(s, 'dataLabels.inside');
|
|
77
|
+
if (!inside) {
|
|
78
|
+
const labelsMaxWidth = get(s, 'dataLabels.maxWidth', 0);
|
|
79
|
+
result = Math.max(result, labelsMaxWidth);
|
|
80
|
+
}
|
|
74
81
|
break;
|
|
75
82
|
}
|
|
76
83
|
}
|
|
@@ -79,6 +86,7 @@ function calculateXAxisPadding(series) {
|
|
|
79
86
|
}
|
|
80
87
|
export function createXScale(axis, series, boundsWidth) {
|
|
81
88
|
const xMin = get(axis, 'min');
|
|
89
|
+
const xMax = getDefaultMaxXAxisValue(series);
|
|
82
90
|
const xType = get(axis, 'type', DEFAULT_AXIS_TYPE);
|
|
83
91
|
const xCategories = get(axis, 'categories');
|
|
84
92
|
const xTimestamps = get(axis, 'timestamps');
|
|
@@ -89,9 +97,10 @@ export function createXScale(axis, series, boundsWidth) {
|
|
|
89
97
|
case 'linear': {
|
|
90
98
|
const domain = getDomainDataXBySeries(series);
|
|
91
99
|
if (isNumericalArrayData(domain)) {
|
|
92
|
-
const [domainXMin,
|
|
100
|
+
const [domainXMin, domainXMax] = extent(domain);
|
|
93
101
|
const xMinValue = typeof xMin === 'number' ? xMin : domainXMin;
|
|
94
|
-
|
|
102
|
+
const xMaxValue = typeof xMax === 'number' ? Math.max(xMax, domainXMax) : domainXMax;
|
|
103
|
+
return scaleLinear().domain([xMinValue, xMaxValue]).range(xRange).nice();
|
|
95
104
|
}
|
|
96
105
|
break;
|
|
97
106
|
}
|
|
@@ -128,7 +137,7 @@ export function createXScale(axis, series, boundsWidth) {
|
|
|
128
137
|
throw new Error('Failed to create xScale');
|
|
129
138
|
}
|
|
130
139
|
const createScales = (args) => {
|
|
131
|
-
const { boundsWidth, boundsHeight, series, xAxis, yAxis } = args;
|
|
140
|
+
const { boundsWidth, boundsHeight, series, xAxis, yAxis, split } = args;
|
|
132
141
|
let visibleSeries = getOnlyVisibleSeries(series);
|
|
133
142
|
// Reassign to all series in case of all series unselected,
|
|
134
143
|
// otherwise we will get an empty space without grid
|
|
@@ -141,7 +150,8 @@ const createScales = (args) => {
|
|
|
141
150
|
return seriesAxisIndex === index;
|
|
142
151
|
});
|
|
143
152
|
const visibleAxisSeries = getOnlyVisibleSeries(axisSeries);
|
|
144
|
-
|
|
153
|
+
const axisHeight = getAxisHeight({ boundsHeight, split });
|
|
154
|
+
return createYScale(axis, visibleAxisSeries.length ? visibleAxisSeries : axisSeries, axisHeight);
|
|
145
155
|
}),
|
|
146
156
|
};
|
|
147
157
|
};
|
|
@@ -149,15 +159,21 @@ const createScales = (args) => {
|
|
|
149
159
|
* Uses to create scales for axis related series
|
|
150
160
|
*/
|
|
151
161
|
export const useAxisScales = (args) => {
|
|
152
|
-
const { boundsWidth, boundsHeight, series, xAxis, yAxis } = args;
|
|
153
|
-
|
|
162
|
+
const { boundsWidth, boundsHeight, series, xAxis, yAxis, split } = args;
|
|
163
|
+
return React.useMemo(() => {
|
|
154
164
|
let xScale;
|
|
155
165
|
let yScale;
|
|
156
166
|
const hasAxisRelatedSeries = series.some(isAxisRelatedSeries);
|
|
157
167
|
if (hasAxisRelatedSeries) {
|
|
158
|
-
({ xScale, yScale } = createScales({
|
|
168
|
+
({ xScale, yScale } = createScales({
|
|
169
|
+
boundsWidth,
|
|
170
|
+
boundsHeight,
|
|
171
|
+
series,
|
|
172
|
+
xAxis,
|
|
173
|
+
yAxis,
|
|
174
|
+
split,
|
|
175
|
+
}));
|
|
159
176
|
}
|
|
160
177
|
return { xScale, yScale };
|
|
161
|
-
}, [boundsWidth, boundsHeight, series, xAxis, yAxis]);
|
|
162
|
-
return scales;
|
|
178
|
+
}, [boundsWidth, boundsHeight, series, xAxis, yAxis, split]);
|
|
163
179
|
};
|
|
@@ -16,6 +16,17 @@ export function getYAxisWidth(axis) {
|
|
|
16
16
|
return result;
|
|
17
17
|
}
|
|
18
18
|
export function getWidthOccupiedByYAxis(args) {
|
|
19
|
-
const { preparedAxis
|
|
20
|
-
|
|
19
|
+
const { preparedAxis } = args;
|
|
20
|
+
let leftAxisWidth = 0;
|
|
21
|
+
let rightAxisWidth = 0;
|
|
22
|
+
preparedAxis === null || preparedAxis === void 0 ? void 0 : preparedAxis.forEach((axis) => {
|
|
23
|
+
const axisWidth = getYAxisWidth(axis);
|
|
24
|
+
if (axis.position === 'right') {
|
|
25
|
+
rightAxisWidth = Math.max(rightAxisWidth, axisWidth);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
leftAxisWidth = Math.max(leftAxisWidth, axisWidth);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return leftAxisWidth + rightAxisWidth;
|
|
21
32
|
}
|
|
@@ -28,6 +28,7 @@ export type PreparedAxis = Omit<ChartKitWidgetAxis, 'type' | 'labels'> & {
|
|
|
28
28
|
pixelInterval?: number;
|
|
29
29
|
};
|
|
30
30
|
position: 'left' | 'right' | 'top' | 'bottom';
|
|
31
|
+
plotIndex: number;
|
|
31
32
|
};
|
|
32
33
|
export type PreparedTitle = ChartKitWidgetData['title'] & {
|
|
33
34
|
height: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ChartKitWidgetSeries, ChartKitWidgetXAxis } from '../../../../../types';
|
|
2
2
|
import type { PreparedAxis } from './types';
|
|
3
3
|
export declare const getPreparedXAxis: ({ xAxis, series, width, }: {
|
|
4
|
-
xAxis?: ChartKitWidgetAxis | undefined;
|
|
4
|
+
xAxis?: import("../../../../../types").ChartKitWidgetAxis | undefined;
|
|
5
5
|
series: ChartKitWidgetSeries[];
|
|
6
6
|
width: number;
|
|
7
7
|
}) => PreparedAxis;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import { DEFAULT_AXIS_LABEL_FONT_SIZE, axisLabelsDefaults, xAxisTitleDefaults, } from '../../constants';
|
|
3
|
-
import { calculateCos, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getMaxTickCount, getTicksCount, getXAxisItems, hasOverlappingLabels, } from '../../utils';
|
|
3
|
+
import { CHART_SERIES_WITH_VOLUME_ON_X_AXIS, calculateCos, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getMaxTickCount, getTicksCount, getXAxisItems, hasOverlappingLabels, } from '../../utils';
|
|
4
4
|
import { createXScale } from '../useAxisScales';
|
|
5
5
|
function getLabelSettings({ axis, series, width, autoRotation = true, }) {
|
|
6
6
|
const scale = createXScale(axis, series, width);
|
|
@@ -36,6 +36,17 @@ function getLabelSettings({ axis, series, width, autoRotation = true, }) {
|
|
|
36
36
|
const maxHeight = rotation ? calculateCos(rotation) * axis.labels.maxWidth : labelsHeight;
|
|
37
37
|
return { height: Math.min(maxHeight, labelsHeight), rotation };
|
|
38
38
|
}
|
|
39
|
+
function getAxisMin(axis, series) {
|
|
40
|
+
const min = axis === null || axis === void 0 ? void 0 : axis.min;
|
|
41
|
+
if (typeof min === 'undefined' &&
|
|
42
|
+
(series === null || series === void 0 ? void 0 : series.some((s) => CHART_SERIES_WITH_VOLUME_ON_X_AXIS.includes(s.type)))) {
|
|
43
|
+
return series.reduce((minValue, s) => {
|
|
44
|
+
const minYValue = s.data.reduce((res, d) => Math.min(res, get(d, 'x', 0)), 0);
|
|
45
|
+
return Math.min(minValue, minYValue);
|
|
46
|
+
}, 0);
|
|
47
|
+
}
|
|
48
|
+
return min;
|
|
49
|
+
}
|
|
39
50
|
export const getPreparedXAxis = ({ xAxis, series, width, }) => {
|
|
40
51
|
var _a;
|
|
41
52
|
const titleText = get(xAxis, 'title.text', '');
|
|
@@ -71,7 +82,7 @@ export const getPreparedXAxis = ({ xAxis, series, width, }) => {
|
|
|
71
82
|
? getHorisontalSvgTextHeight({ text: titleText, style: titleStyle })
|
|
72
83
|
: 0,
|
|
73
84
|
},
|
|
74
|
-
min:
|
|
85
|
+
min: getAxisMin(xAxis, series),
|
|
75
86
|
maxPadding: get(xAxis, 'maxPadding', 0.01),
|
|
76
87
|
grid: {
|
|
77
88
|
enabled: get(xAxis, 'grid.enabled', true),
|
|
@@ -80,6 +91,7 @@ export const getPreparedXAxis = ({ xAxis, series, width, }) => {
|
|
|
80
91
|
pixelInterval: get(xAxis, 'ticks.pixelInterval'),
|
|
81
92
|
},
|
|
82
93
|
position: 'bottom',
|
|
94
|
+
plotIndex: 0,
|
|
83
95
|
};
|
|
84
96
|
const { height, rotation } = getLabelSettings({
|
|
85
97
|
axis: preparedXAxis,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ChartKitWidgetSeries, ChartKitWidgetYAxis } from '../../../../../types';
|
|
2
2
|
import type { PreparedAxis } from './types';
|
|
3
3
|
export declare const getPreparedYAxis: ({ series, yAxis, }: {
|
|
4
4
|
series: ChartKitWidgetSeries[];
|
|
5
|
-
yAxis:
|
|
5
|
+
yAxis: ChartKitWidgetYAxis[] | undefined;
|
|
6
6
|
}) => PreparedAxis[];
|