@gravity-ui/chartkit 3.2.0 → 3.4.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/renderer/components/AxisX.js +15 -5
- package/build/plugins/d3/renderer/components/AxisY.js +18 -8
- package/build/plugins/d3/renderer/components/Chart.js +3 -3
- package/build/plugins/d3/renderer/hooks/useChartOptions/chart.js +16 -9
- package/build/plugins/d3/renderer/hooks/useChartOptions/legend.js +4 -2
- package/build/plugins/d3/renderer/hooks/useChartOptions/types.d.ts +8 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +8 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +8 -0
- package/build/plugins/d3/renderer/hooks/useScales/index.js +22 -14
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x.d.ts +16 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x.js +75 -0
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +28 -57
- package/build/plugins/d3/renderer/hooks/useShapes/scatter.d.ts +18 -0
- package/build/plugins/d3/renderer/hooks/useShapes/scatter.js +62 -0
- package/build/plugins/highcharts/renderer/helpers/config/utils/index.js +1 -1
- package/build/types/widget-data/axis.d.ts +20 -0
- package/build/types/widget-data/bar-x.d.ts +31 -0
- package/build/types/widget-data/index.d.ts +6 -2
- package/build/types/widget-data/index.js +1 -0
- package/build/types/widget-data/series.d.ts +16 -2
- package/package.json +7 -2
- package/build/libs/chartkit-error/__tests__/chartkit-error.js +0 -27
- package/build/libs/settings/__tests__/settings.test.d.ts +0 -1
- package/build/libs/settings/__tests__/settings.test.js +0 -17
- package/build/plugins/d3/__stories__/LinearCategories.stories.d.ts +0 -4
- package/build/plugins/d3/__stories__/LinearCategories.stories.js +0 -101
- package/build/plugins/d3/__stories__/Timestamp.stories.d.ts +0 -4
- package/build/plugins/d3/__stories__/Timestamp.stories.js +0 -89
- package/build/plugins/d3/__stories__/penguins.json +0 -3098
- package/build/plugins/highcharts/__stories__/Line.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/Line.stories.js +0 -12
- package/build/plugins/highcharts/__stories__/Pie.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/Pie.stories.js +0 -12
- package/build/plugins/highcharts/__stories__/area/Range.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/area/Range.stories.js +0 -12
- package/build/plugins/highcharts/__stories__/area/Stacked.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/area/Stacked.stories.js +0 -12
- package/build/plugins/highcharts/__stories__/column/HorizontalStacked.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/column/HorizontalStacked.stories.js +0 -12
- package/build/plugins/highcharts/__stories__/column/Vertical.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/column/Vertical.stories.js +0 -12
- package/build/plugins/highcharts/__stories__/column/VerticalStacked.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/column/VerticalStacked.stories.js +0 -12
- package/build/plugins/highcharts/__stories__/combined/ComboChartWithSameLegendValues.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/combined/ComboChartWithSameLegendValues.stories.js +0 -12
- package/build/plugins/highcharts/__stories__/complex/TwoAxis.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/complex/TwoAxis.stories.js +0 -12
- package/build/plugins/highcharts/__stories__/components/ChartStory.d.ts +0 -12
- package/build/plugins/highcharts/__stories__/components/ChartStory.js +0 -28
- package/build/plugins/highcharts/__stories__/constants/story-settings.d.ts +0 -42
- package/build/plugins/highcharts/__stories__/constants/story-settings.js +0 -42
- package/build/plugins/highcharts/__stories__/custom-error-render/custom-error-render.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/custom-error-render/custom-error-render.stories.js +0 -41
- package/build/plugins/highcharts/__stories__/no-data/no-data.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/no-data/no-data.stories.js +0 -20
- package/build/plugins/highcharts/__stories__/pie/WithTotals.stories.d.ts +0 -3
- package/build/plugins/highcharts/__stories__/pie/WithTotals.stories.js +0 -12
- package/build/plugins/highcharts/__tests__/prepare-data.test.d.ts +0 -1
- package/build/plugins/highcharts/__tests__/prepare-data.test.js +0 -13
- package/build/plugins/indicator/__stories__/Indicator.stories.d.ts +0 -4
- package/build/plugins/indicator/__stories__/Indicator.stories.js +0 -45
- package/build/plugins/yagr/__stories__/Yagr.stories.d.ts +0 -6
- package/build/plugins/yagr/__stories__/Yagr.stories.js +0 -90
- package/build/plugins/yagr/__stories__/mocks/line10.d.ts +0 -53
- package/build/plugins/yagr/__stories__/mocks/line10.js +0 -78
- package/build/plugins/yagr/__tests__/utils.test.d.ts +0 -1
- package/build/plugins/yagr/__tests__/utils.test.js +0 -26
- package/build/utils/__tests__/common.test.d.ts +0 -1
- package/build/utils/__tests__/common.test.js +0 -9
- /package/build/{libs/chartkit-error/__tests__/chartkit-error.d.ts → types/widget-data/bar-x.js} +0 -0
|
@@ -31,20 +31,30 @@ export const AxisX = ({ axis, width, height, scale }) => {
|
|
|
31
31
|
}
|
|
32
32
|
const svgElement = select(ref.current);
|
|
33
33
|
svgElement.selectAll('*').remove();
|
|
34
|
-
const
|
|
35
|
-
|
|
34
|
+
const tickSize = axis.grid.enabled ? height * -1 : 0;
|
|
35
|
+
let xAxisGenerator = axisBottom(scale)
|
|
36
|
+
.tickSize(tickSize)
|
|
36
37
|
.tickPadding(axis.labels.padding)
|
|
37
38
|
.tickFormat((value) => {
|
|
39
|
+
if (!axis.labels.enabled) {
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
38
42
|
return formatAxisTickLabel({
|
|
39
43
|
axisType: axis.type,
|
|
40
44
|
value,
|
|
41
|
-
dateFormat: axis.labels
|
|
42
|
-
numberFormat: axis.labels
|
|
45
|
+
dateFormat: axis.labels['dateFormat'],
|
|
46
|
+
numberFormat: axis.labels['numberFormat'],
|
|
43
47
|
});
|
|
44
48
|
});
|
|
49
|
+
if (axis.ticks.pixelInterval) {
|
|
50
|
+
const ticksCount = width / axis.ticks.pixelInterval;
|
|
51
|
+
xAxisGenerator = xAxisGenerator.ticks(ticksCount);
|
|
52
|
+
}
|
|
45
53
|
svgElement.call(xAxisGenerator).attr('class', b());
|
|
46
54
|
svgElement.select('.domain').attr('d', `M0,0V0H${width}`);
|
|
47
|
-
|
|
55
|
+
if (axis.labels.enabled) {
|
|
56
|
+
svgElement.selectAll('.tick text').style('font-size', axis.labels.style.fontSize);
|
|
57
|
+
}
|
|
48
58
|
const transformStyle = svgElement.select('.tick').attr('transform');
|
|
49
59
|
const { x } = parseTransformStyle(transformStyle);
|
|
50
60
|
if (x === 0) {
|
|
@@ -32,23 +32,33 @@ export const AxisY = ({ axises, width, height, scale }) => {
|
|
|
32
32
|
const axis = axises[0];
|
|
33
33
|
const svgElement = select(ref.current);
|
|
34
34
|
svgElement.selectAll('*').remove();
|
|
35
|
-
const
|
|
36
|
-
|
|
35
|
+
const tickSize = axis.grid.enabled ? width * -1 : 0;
|
|
36
|
+
let yAxisGenerator = axisLeft(scale)
|
|
37
|
+
.tickSize(tickSize)
|
|
37
38
|
.tickPadding(axis.labels.padding)
|
|
38
39
|
.tickFormat((value) => {
|
|
40
|
+
if (!axis.labels.enabled) {
|
|
41
|
+
return '';
|
|
42
|
+
}
|
|
39
43
|
return formatAxisTickLabel({
|
|
40
44
|
axisType: axis.type,
|
|
41
45
|
value,
|
|
42
|
-
dateFormat: axis.labels
|
|
43
|
-
numberFormat: axis.labels
|
|
46
|
+
dateFormat: axis.labels['dateFormat'],
|
|
47
|
+
numberFormat: axis.labels['numberFormat'],
|
|
44
48
|
});
|
|
45
49
|
});
|
|
50
|
+
if (axis.ticks.pixelInterval) {
|
|
51
|
+
const ticksCount = height / axis.ticks.pixelInterval;
|
|
52
|
+
yAxisGenerator = yAxisGenerator.ticks(ticksCount);
|
|
53
|
+
}
|
|
46
54
|
svgElement.call(yAxisGenerator).attr('class', b());
|
|
47
55
|
svgElement.select('.domain').attr('d', `M0,${height}H0V0`);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
if (axis.labels.enabled) {
|
|
57
|
+
svgElement
|
|
58
|
+
.selectAll('.tick text')
|
|
59
|
+
.style('font-size', axis.labels.style.fontSize)
|
|
60
|
+
.style('transform', 'translateY(-1px)');
|
|
61
|
+
}
|
|
52
62
|
const transformStyle = svgElement.select('.tick').attr('transform');
|
|
53
63
|
const { y } = parseTransformStyle(transformStyle);
|
|
54
64
|
if (y === height) {
|
|
@@ -13,7 +13,7 @@ export const Chart = ({ width, height, data }) => {
|
|
|
13
13
|
// FIXME: add data validation
|
|
14
14
|
const { series } = data;
|
|
15
15
|
const svgRef = React.createRef();
|
|
16
|
-
const hasAxisRelatedSeries = series.some(isAxisRelatedSeries);
|
|
16
|
+
const hasAxisRelatedSeries = series.data.some(isAxisRelatedSeries);
|
|
17
17
|
const { chartHovered, handleMouseEnter, handleMouseLeave } = useChartEvents();
|
|
18
18
|
const { chart, legend, title, tooltip, xAxis, yAxis } = useChartOptions(data);
|
|
19
19
|
const { boundsWidth, boundsHeight, legendHeight } = useChartDimensions({
|
|
@@ -25,8 +25,8 @@ export const Chart = ({ width, height, data }) => {
|
|
|
25
25
|
xAxis,
|
|
26
26
|
yAxis,
|
|
27
27
|
});
|
|
28
|
-
const { activeLegendItems, handleLegendItemClick } = useLegend({ series });
|
|
29
|
-
const { chartSeries } = useSeries({ activeLegendItems, series });
|
|
28
|
+
const { activeLegendItems, handleLegendItemClick } = useLegend({ series: series.data });
|
|
29
|
+
const { chartSeries } = useSeries({ activeLegendItems, series: series.data });
|
|
30
30
|
const { xScale, yScale } = useScales({
|
|
31
31
|
boundsWidth,
|
|
32
32
|
boundsHeight,
|
|
@@ -24,12 +24,15 @@ const getAxisLabelMaxWidth = (args) => {
|
|
|
24
24
|
maxDomainValue = max(domain);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
let formattedValue = '';
|
|
28
|
+
if (axis.labels.enabled) {
|
|
29
|
+
formattedValue = formatAxisTickLabel({
|
|
30
|
+
axisType: axis.type,
|
|
31
|
+
value: maxDomainValue,
|
|
32
|
+
dateFormat: axis.labels['dateFormat'],
|
|
33
|
+
numberFormat: axis.labels['numberFormat'],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
33
36
|
select(document.body)
|
|
34
37
|
.append('text')
|
|
35
38
|
.style('font-size', axis.labels.style.fontSize)
|
|
@@ -47,12 +50,16 @@ export const getPreparedChart = (args) => {
|
|
|
47
50
|
getHorisontalSvgTextDimensions({ text: 'Tmp', style: preparedXAxis.labels.style });
|
|
48
51
|
const marginLeft = get(chart, 'margin.left', AXIS_WIDTH) +
|
|
49
52
|
preparedY1Axis.labels.padding +
|
|
50
|
-
getAxisLabelMaxWidth({ axis: preparedY1Axis, series }) +
|
|
53
|
+
getAxisLabelMaxWidth({ axis: preparedY1Axis, series: series.data }) +
|
|
51
54
|
(preparedY1Axis.title.height || 0);
|
|
55
|
+
const marginTop = get(chart, 'margin.top', 0) +
|
|
56
|
+
getHorisontalSvgTextDimensions({ text: 'Tmp', style: preparedY1Axis.labels.style }) / 2;
|
|
57
|
+
const marginRight = get(chart, 'margin.right', 0) +
|
|
58
|
+
getAxisLabelMaxWidth({ axis: preparedXAxis, series: series.data }) / 2;
|
|
52
59
|
return {
|
|
53
60
|
margin: {
|
|
54
|
-
top:
|
|
55
|
-
right:
|
|
61
|
+
top: marginTop,
|
|
62
|
+
right: marginRight,
|
|
56
63
|
bottom: marginBottom,
|
|
57
64
|
left: marginLeft,
|
|
58
65
|
},
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import get from 'lodash/get';
|
|
2
1
|
export const getPreparedLegend = (args) => {
|
|
3
2
|
const { legend, series } = args;
|
|
4
|
-
|
|
3
|
+
const enabled = legend === null || legend === void 0 ? void 0 : legend.enabled;
|
|
4
|
+
return {
|
|
5
|
+
enabled: typeof enabled === 'boolean' ? enabled : series.data.length > 1,
|
|
6
|
+
};
|
|
5
7
|
};
|
|
@@ -14,6 +14,14 @@ export type PreparedAxis = Omit<ChartKitWidgetAxis, 'type' | 'labels'> & {
|
|
|
14
14
|
text: string;
|
|
15
15
|
style: BaseTextStyle;
|
|
16
16
|
};
|
|
17
|
+
min?: number;
|
|
18
|
+
grid: {
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
};
|
|
21
|
+
maxPadding: number;
|
|
22
|
+
ticks: {
|
|
23
|
+
pixelInterval?: number;
|
|
24
|
+
};
|
|
17
25
|
};
|
|
18
26
|
export type PreparedTitle = ChartKitWidgetData['title'] & {
|
|
19
27
|
height: number;
|
|
@@ -24,6 +24,14 @@ export const getPreparedXAxis = ({ xAxis }) => {
|
|
|
24
24
|
? getHorisontalSvgTextDimensions({ text: titleText, style: titleStyle })
|
|
25
25
|
: 0,
|
|
26
26
|
},
|
|
27
|
+
min: get(xAxis, 'min'),
|
|
28
|
+
maxPadding: get(xAxis, 'maxPadding', 0.01),
|
|
29
|
+
grid: {
|
|
30
|
+
enabled: get(xAxis, 'grid.enabled', true),
|
|
31
|
+
},
|
|
32
|
+
ticks: {
|
|
33
|
+
pixelInterval: get(xAxis, 'ticks.pixelInterval'),
|
|
34
|
+
},
|
|
27
35
|
};
|
|
28
36
|
return preparedXAxis;
|
|
29
37
|
};
|
|
@@ -29,6 +29,14 @@ export const getPreparedYAxis = ({ yAxis }) => {
|
|
|
29
29
|
? getHorisontalSvgTextDimensions({ text: y1TitleText, style: y1TitleStyle })
|
|
30
30
|
: 0,
|
|
31
31
|
},
|
|
32
|
+
min: get(yAxis1, 'min'),
|
|
33
|
+
maxPadding: get(yAxis1, 'maxPadding', 0.05),
|
|
34
|
+
grid: {
|
|
35
|
+
enabled: get(yAxis1, 'grid.enabled', true),
|
|
36
|
+
},
|
|
37
|
+
ticks: {
|
|
38
|
+
pixelInterval: get(yAxis1, 'ticks.pixelInterval'),
|
|
39
|
+
},
|
|
32
40
|
};
|
|
33
41
|
return [preparedY1Axis];
|
|
34
42
|
};
|
|
@@ -15,11 +15,13 @@ const filterCategoriesByVisibleSeries = (categories, series) => {
|
|
|
15
15
|
export const useScales = (args) => {
|
|
16
16
|
const { boundsWidth, boundsHeight, series, xAxis, yAxis } = args;
|
|
17
17
|
const scales = React.useMemo(() => {
|
|
18
|
+
const xMin = get(xAxis, 'min');
|
|
18
19
|
const xType = get(xAxis, 'type', 'linear');
|
|
19
|
-
const
|
|
20
|
+
const xCategories = get(xAxis, 'categories');
|
|
20
21
|
const xTimestamps = get(xAxis, 'timestamps');
|
|
21
22
|
const yType = get(yAxis[0], 'type', 'linear');
|
|
22
|
-
const
|
|
23
|
+
const yMin = get(yAxis[0], 'min');
|
|
24
|
+
const yCategories = get(yAxis[0], 'categories');
|
|
23
25
|
const yTimestamps = get(xAxis, 'timestamps');
|
|
24
26
|
let visibleSeries = getOnlyVisibleSeries(series);
|
|
25
27
|
// Reassign to all series in case of all series unselected,
|
|
@@ -30,29 +32,32 @@ export const useScales = (args) => {
|
|
|
30
32
|
switch (xType) {
|
|
31
33
|
case 'linear': {
|
|
32
34
|
const domain = getDomainDataXBySeries(visibleSeries);
|
|
35
|
+
const range = [0, boundsWidth - boundsWidth * xAxis.maxPadding];
|
|
33
36
|
if (isNumericalArrayData(domain)) {
|
|
34
|
-
const [
|
|
35
|
-
|
|
37
|
+
const [domainXMin, xMax] = extent(domain);
|
|
38
|
+
const xMinValue = typeof xMin === 'number' ? xMin : domainXMin;
|
|
39
|
+
xScale = scaleLinear().domain([xMinValue, xMax]).range(range).nice();
|
|
36
40
|
}
|
|
37
41
|
break;
|
|
38
42
|
}
|
|
39
43
|
case 'category': {
|
|
40
|
-
if (
|
|
41
|
-
const filteredCategories = filterCategoriesByVisibleSeries(
|
|
44
|
+
if (xCategories) {
|
|
45
|
+
const filteredCategories = filterCategoriesByVisibleSeries(xCategories, visibleSeries);
|
|
42
46
|
xScale = scaleBand().domain(filteredCategories).range([0, boundsWidth]);
|
|
43
47
|
}
|
|
44
48
|
break;
|
|
45
49
|
}
|
|
46
50
|
case 'datetime': {
|
|
51
|
+
const range = [0, boundsWidth - boundsWidth * xAxis.maxPadding];
|
|
47
52
|
if (xTimestamps) {
|
|
48
53
|
const [xMin, xMax] = extent(xTimestamps);
|
|
49
|
-
xScale = scaleUtc().domain([xMin, xMax]).range(
|
|
54
|
+
xScale = scaleUtc().domain([xMin, xMax]).range(range).nice();
|
|
50
55
|
}
|
|
51
56
|
else {
|
|
52
57
|
const domain = getDomainDataXBySeries(visibleSeries);
|
|
53
58
|
if (isNumericalArrayData(domain)) {
|
|
54
59
|
const [xMin, xMax] = extent(domain);
|
|
55
|
-
xScale = scaleUtc().domain([xMin, xMax]).range(
|
|
60
|
+
xScale = scaleUtc().domain([xMin, xMax]).range(range).nice();
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
break;
|
|
@@ -64,29 +69,32 @@ export const useScales = (args) => {
|
|
|
64
69
|
switch (yType) {
|
|
65
70
|
case 'linear': {
|
|
66
71
|
const domain = getDomainDataYBySeries(visibleSeries);
|
|
72
|
+
const range = [boundsHeight, boundsHeight * yAxis[0].maxPadding];
|
|
67
73
|
if (isNumericalArrayData(domain)) {
|
|
68
|
-
const [
|
|
69
|
-
|
|
74
|
+
const [domainYMin, yMax] = extent(domain);
|
|
75
|
+
const yMinValue = typeof yMin === 'number' ? yMin : domainYMin;
|
|
76
|
+
yScale = scaleLinear().domain([yMinValue, yMax]).range(range).nice();
|
|
70
77
|
}
|
|
71
78
|
break;
|
|
72
79
|
}
|
|
73
80
|
case 'category': {
|
|
74
|
-
if (
|
|
75
|
-
const filteredCategories = filterCategoriesByVisibleSeries(
|
|
81
|
+
if (yCategories) {
|
|
82
|
+
const filteredCategories = filterCategoriesByVisibleSeries(yCategories, visibleSeries);
|
|
76
83
|
yScale = scaleBand().domain(filteredCategories).range([boundsHeight, 0]);
|
|
77
84
|
}
|
|
78
85
|
break;
|
|
79
86
|
}
|
|
80
87
|
case 'datetime': {
|
|
88
|
+
const range = [boundsHeight, boundsHeight * yAxis[0].maxPadding];
|
|
81
89
|
if (yTimestamps) {
|
|
82
90
|
const [yMin, yMax] = extent(yTimestamps);
|
|
83
|
-
yScale = scaleUtc().domain([yMin, yMax]).range(
|
|
91
|
+
yScale = scaleUtc().domain([yMin, yMax]).range(range).nice();
|
|
84
92
|
}
|
|
85
93
|
else {
|
|
86
94
|
const domain = getDomainDataYBySeries(visibleSeries);
|
|
87
95
|
if (isNumericalArrayData(domain)) {
|
|
88
96
|
const [yMin, yMax] = extent(domain);
|
|
89
|
-
yScale = scaleUtc().domain([yMin, yMax]).range(
|
|
97
|
+
yScale = scaleUtc().domain([yMin, yMax]).range(range).nice();
|
|
90
98
|
}
|
|
91
99
|
}
|
|
92
100
|
break;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ChartOptions } from '../useChartOptions/types';
|
|
3
|
+
import { ChartScale } from '../useScales';
|
|
4
|
+
import { OnSeriesMouseLeave, OnSeriesMouseMove } from '../useTooltip/types';
|
|
5
|
+
import { BarXSeries } from '../../../../../types/widget-data';
|
|
6
|
+
type Args = {
|
|
7
|
+
series: BarXSeries[];
|
|
8
|
+
xAxis: ChartOptions['xAxis'];
|
|
9
|
+
xScale: ChartScale;
|
|
10
|
+
yAxis: ChartOptions['yAxis'];
|
|
11
|
+
yScale: ChartScale;
|
|
12
|
+
onSeriesMouseMove?: OnSeriesMouseMove;
|
|
13
|
+
onSeriesMouseLeave?: OnSeriesMouseLeave;
|
|
14
|
+
};
|
|
15
|
+
export declare function prepareBarXSeries(args: Args): React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import block from 'bem-cn-lite';
|
|
3
|
+
const DEFAULT_BAR_RECT_WIDTH = 50;
|
|
4
|
+
const DEFAULT_LINEAR_BAR_RECT_WIDTH = 20;
|
|
5
|
+
const MIN_RECT_GAP = 1;
|
|
6
|
+
const b = block('chartkit-d3-bar');
|
|
7
|
+
const getRectProperties = (args) => {
|
|
8
|
+
const { point, xAxis, xScale, yAxis, yScale, minPointDistance } = args;
|
|
9
|
+
let cx;
|
|
10
|
+
let cy;
|
|
11
|
+
let width;
|
|
12
|
+
let height;
|
|
13
|
+
if (xAxis.type === 'category') {
|
|
14
|
+
const xBandScale = xScale;
|
|
15
|
+
const maxWidth = xBandScale.bandwidth() - MIN_RECT_GAP;
|
|
16
|
+
width = Math.min(maxWidth, DEFAULT_BAR_RECT_WIDTH);
|
|
17
|
+
cx = (xBandScale(point.category) || 0) + xBandScale.step() / 2 - width / 2;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
const xLinearScale = xScale;
|
|
21
|
+
const [min, max] = xLinearScale.domain();
|
|
22
|
+
const range = xLinearScale.range();
|
|
23
|
+
const maxWidth = ((range[1] - range[0]) * minPointDistance) / (Number(max) - Number(min)) - MIN_RECT_GAP;
|
|
24
|
+
width = Math.min(Math.max(maxWidth, 1), DEFAULT_LINEAR_BAR_RECT_WIDTH);
|
|
25
|
+
cx = xLinearScale(point.x) - width / 2;
|
|
26
|
+
}
|
|
27
|
+
if (yAxis[0].type === 'linear') {
|
|
28
|
+
const yLinearScale = yScale;
|
|
29
|
+
cy = yLinearScale(point.y);
|
|
30
|
+
height = yLinearScale(yLinearScale.domain()[0]) - cy;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
throw Error(`The "${yAxis[0].type}" type for the Y axis is not supported`);
|
|
34
|
+
}
|
|
35
|
+
return { x: cx, y: cy, width, height };
|
|
36
|
+
};
|
|
37
|
+
function minDiff(arr) {
|
|
38
|
+
let result = Infinity;
|
|
39
|
+
for (let i = 0; i < arr.length - 1; i++) {
|
|
40
|
+
for (let j = i + 1; j < arr.length; j++) {
|
|
41
|
+
const diff = Math.abs(arr[i] - arr[j]);
|
|
42
|
+
if (diff < result) {
|
|
43
|
+
result = diff;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
export function prepareBarXSeries(args) {
|
|
50
|
+
const { series, xAxis, xScale, yAxis, yScale, onSeriesMouseMove, onSeriesMouseLeave } = args;
|
|
51
|
+
const seriesData = series.map(({ data }) => data).flat(2);
|
|
52
|
+
const minPointDistance = minDiff(seriesData.map((item) => Number(item.x)));
|
|
53
|
+
return series.reduce((result, item) => {
|
|
54
|
+
const randomKey = Math.random().toString();
|
|
55
|
+
item.data.forEach((point, i) => {
|
|
56
|
+
const rectProps = getRectProperties({
|
|
57
|
+
point,
|
|
58
|
+
xAxis,
|
|
59
|
+
xScale,
|
|
60
|
+
yAxis,
|
|
61
|
+
yScale,
|
|
62
|
+
minPointDistance,
|
|
63
|
+
});
|
|
64
|
+
result.push(React.createElement("rect", Object.assign({ key: `${i}-${randomKey}`, className: b('rect'), fill: item.color }, rectProps, { onMouseMove: function () {
|
|
65
|
+
onSeriesMouseMove === null || onSeriesMouseMove === void 0 ? void 0 : onSeriesMouseMove({
|
|
66
|
+
hovered: {
|
|
67
|
+
data: point,
|
|
68
|
+
series: item,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}, onMouseLeave: onSeriesMouseLeave })));
|
|
72
|
+
});
|
|
73
|
+
return result;
|
|
74
|
+
}, []);
|
|
75
|
+
}
|
|
@@ -1,67 +1,38 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
3
|
-
import { pointer } from 'd3';
|
|
2
|
+
import { group } from 'd3';
|
|
4
3
|
import { getOnlyVisibleSeries } from '../../utils';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const prepareCategoricalScatterData = (data) => {
|
|
8
|
-
return data.filter((d) => typeof d.category === 'string');
|
|
9
|
-
};
|
|
10
|
-
const prepareLinearScatterData = (data) => {
|
|
11
|
-
return data.filter((d) => typeof d.x === 'number' && typeof d.y === 'number');
|
|
12
|
-
};
|
|
13
|
-
const getPointProperties = (args) => {
|
|
14
|
-
const { point, xAxis, xScale, yAxis, yScale } = args;
|
|
15
|
-
const r = point.radius || DEFAULT_SCATTER_POINT_RADIUS;
|
|
16
|
-
let cx;
|
|
17
|
-
let cy;
|
|
18
|
-
if (xAxis.type === 'category') {
|
|
19
|
-
const xBandScale = xScale;
|
|
20
|
-
cx = xBandScale(point.category) + xBandScale.step() / 2;
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
const xLinearScale = xScale;
|
|
24
|
-
cx = xLinearScale(point.x);
|
|
25
|
-
}
|
|
26
|
-
if (yAxis[0].type === 'category') {
|
|
27
|
-
const yBandScale = yScale;
|
|
28
|
-
cy = yBandScale(point.category) + yBandScale.step() / 2;
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
const xLinearScale = yScale;
|
|
32
|
-
cy = xLinearScale(point.y);
|
|
33
|
-
}
|
|
34
|
-
return { r, cx, cy };
|
|
35
|
-
};
|
|
4
|
+
import { prepareBarXSeries } from './bar-x';
|
|
5
|
+
import { prepareScatterSeries } from './scatter';
|
|
36
6
|
export const useShapes = (args) => {
|
|
37
7
|
const { series, xAxis, xScale, yAxis, yScale, svgContainer, onSeriesMouseMove, onSeriesMouseLeave, } = args;
|
|
38
8
|
const shapes = React.useMemo(() => {
|
|
39
9
|
const visibleSeries = getOnlyVisibleSeries(series);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
switch (
|
|
10
|
+
const groupedSeries = group(visibleSeries, (item) => item.type);
|
|
11
|
+
return Array.from(groupedSeries).reduce((acc, item) => {
|
|
12
|
+
const [seriesType, chartSeries] = item;
|
|
13
|
+
switch (seriesType) {
|
|
14
|
+
case 'bar-x': {
|
|
15
|
+
acc.push(...prepareBarXSeries({
|
|
16
|
+
series: chartSeries,
|
|
17
|
+
xAxis,
|
|
18
|
+
xScale,
|
|
19
|
+
yAxis,
|
|
20
|
+
yScale,
|
|
21
|
+
onSeriesMouseMove,
|
|
22
|
+
onSeriesMouseLeave,
|
|
23
|
+
}));
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
44
26
|
case 'scatter': {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
yScale,
|
|
55
|
-
});
|
|
56
|
-
return (React.createElement("circle", Object.assign({ key: `${i}-${randomKey}`, className: b('point'), fill: s.color }, pointProps, { onMouseMove: function (e) {
|
|
57
|
-
onSeriesMouseMove === null || onSeriesMouseMove === void 0 ? void 0 : onSeriesMouseMove({
|
|
58
|
-
hovered: {
|
|
59
|
-
data: point,
|
|
60
|
-
series: s,
|
|
61
|
-
},
|
|
62
|
-
pointerPosition: pointer(e, svgContainer),
|
|
63
|
-
});
|
|
64
|
-
}, onMouseLeave: onSeriesMouseLeave })));
|
|
27
|
+
acc.push(...prepareScatterSeries({
|
|
28
|
+
series: chartSeries,
|
|
29
|
+
xAxis,
|
|
30
|
+
xScale,
|
|
31
|
+
yAxis,
|
|
32
|
+
yScale,
|
|
33
|
+
onSeriesMouseMove,
|
|
34
|
+
onSeriesMouseLeave,
|
|
35
|
+
svgContainer,
|
|
65
36
|
}));
|
|
66
37
|
break;
|
|
67
38
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ChartOptions } from '../useChartOptions/types';
|
|
3
|
+
import { ChartScale } from '../useScales';
|
|
4
|
+
import { OnSeriesMouseLeave, OnSeriesMouseMove } from '../useTooltip/types';
|
|
5
|
+
import { ScatterSeries } from '../../../../../types/widget-data';
|
|
6
|
+
type PrepareScatterSeriesArgs = {
|
|
7
|
+
series: ScatterSeries[];
|
|
8
|
+
xAxis: ChartOptions['xAxis'];
|
|
9
|
+
xScale: ChartScale;
|
|
10
|
+
yAxis: ChartOptions['yAxis'];
|
|
11
|
+
yScale: ChartScale;
|
|
12
|
+
svgContainer: SVGSVGElement | null;
|
|
13
|
+
onSeriesMouseMove?: OnSeriesMouseMove;
|
|
14
|
+
onSeriesMouseLeave?: OnSeriesMouseLeave;
|
|
15
|
+
key?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function prepareScatterSeries(args: PrepareScatterSeriesArgs): React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { pointer } from 'd3';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import block from 'bem-cn-lite';
|
|
4
|
+
const b = block('chartkit-d3-scatter');
|
|
5
|
+
const DEFAULT_SCATTER_POINT_RADIUS = 4;
|
|
6
|
+
const prepareCategoricalScatterData = (data) => {
|
|
7
|
+
return data.filter((d) => typeof d.category === 'string');
|
|
8
|
+
};
|
|
9
|
+
const prepareLinearScatterData = (data) => {
|
|
10
|
+
return data.filter((d) => typeof d.x === 'number' && typeof d.y === 'number');
|
|
11
|
+
};
|
|
12
|
+
const getPointProperties = (args) => {
|
|
13
|
+
const { point, xAxis, xScale, yAxis, yScale } = args;
|
|
14
|
+
const r = point.radius || DEFAULT_SCATTER_POINT_RADIUS;
|
|
15
|
+
let cx;
|
|
16
|
+
let cy;
|
|
17
|
+
if (xAxis.type === 'category') {
|
|
18
|
+
const xBandScale = xScale;
|
|
19
|
+
cx = (xBandScale(point.category) || 0) + xBandScale.step() / 2;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
const xLinearScale = xScale;
|
|
23
|
+
cx = xLinearScale(point.x);
|
|
24
|
+
}
|
|
25
|
+
if (yAxis[0].type === 'category') {
|
|
26
|
+
const yBandScale = yScale;
|
|
27
|
+
cy = (yBandScale(point.category) || 0) + yBandScale.step() / 2;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const yLinearScale = yScale;
|
|
31
|
+
cy = yLinearScale(point.y);
|
|
32
|
+
}
|
|
33
|
+
return { r, cx, cy };
|
|
34
|
+
};
|
|
35
|
+
export function prepareScatterSeries(args) {
|
|
36
|
+
const { series, xAxis, xScale, yAxis, yScale, onSeriesMouseMove, onSeriesMouseLeave, key, svgContainer, } = args;
|
|
37
|
+
return series.reduce((result, s) => {
|
|
38
|
+
var _a;
|
|
39
|
+
const preparedData = xAxis.type === 'category' || ((_a = yAxis[0]) === null || _a === void 0 ? void 0 : _a.type) === 'category'
|
|
40
|
+
? prepareCategoricalScatterData(s.data)
|
|
41
|
+
: prepareLinearScatterData(s.data);
|
|
42
|
+
result.push(...preparedData.map((point, i) => {
|
|
43
|
+
const pointProps = getPointProperties({
|
|
44
|
+
point,
|
|
45
|
+
xAxis,
|
|
46
|
+
xScale,
|
|
47
|
+
yAxis,
|
|
48
|
+
yScale,
|
|
49
|
+
});
|
|
50
|
+
return (React.createElement("circle", Object.assign({ key: `${i}-${key}`, className: b('point'), fill: s.color }, pointProps, { onMouseMove: function (e) {
|
|
51
|
+
onSeriesMouseMove === null || onSeriesMouseMove === void 0 ? void 0 : onSeriesMouseMove({
|
|
52
|
+
hovered: {
|
|
53
|
+
data: point,
|
|
54
|
+
series: s,
|
|
55
|
+
},
|
|
56
|
+
pointerPosition: pointer(e, svgContainer),
|
|
57
|
+
});
|
|
58
|
+
}, onMouseLeave: onSeriesMouseLeave })));
|
|
59
|
+
}));
|
|
60
|
+
return result;
|
|
61
|
+
}, []);
|
|
62
|
+
}
|
|
@@ -13,7 +13,7 @@ export { mergeArrayWithObject } from './mergeArrayWithObject';
|
|
|
13
13
|
export { numberFormat } from './numberFormat';
|
|
14
14
|
export { setNavigatorDefaultPeriod } from './setNavigatorDefaultPeriod';
|
|
15
15
|
export const getSortedData = (data, sort = {}) => {
|
|
16
|
-
const { enabled =
|
|
16
|
+
const { enabled = false, order = 'desc', iteratee = 'y' } = sort;
|
|
17
17
|
if (!enabled) {
|
|
18
18
|
return [...data];
|
|
19
19
|
}
|
|
@@ -19,4 +19,24 @@ export type ChartKitWidgetAxis = {
|
|
|
19
19
|
title?: {
|
|
20
20
|
text?: string;
|
|
21
21
|
};
|
|
22
|
+
/** The minimum value of the axis. If undefined the min value is automatically calculate */
|
|
23
|
+
min?: number;
|
|
24
|
+
grid?: {
|
|
25
|
+
/** Enable or disable the grid lines.
|
|
26
|
+
*
|
|
27
|
+
* Defaults to true.
|
|
28
|
+
* */
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
};
|
|
31
|
+
ticks?: {
|
|
32
|
+
/** Pixel interval of the tick marks. Not applicable to categorized axis.
|
|
33
|
+
* The specified value is only a hint; the interval between ticks can be greater or less depending on the data. */
|
|
34
|
+
pixelInterval?: number;
|
|
35
|
+
};
|
|
36
|
+
/** Padding of the max value relative to the length of the axis.
|
|
37
|
+
* A padding of 0.05 will make a 100px axis 5px longer.
|
|
38
|
+
*
|
|
39
|
+
* Defaults to 0.05 for Y axis and to 0.01 for X axis
|
|
40
|
+
* */
|
|
41
|
+
maxPadding?: number;
|
|
22
42
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { BaseSeries, BaseSeriesData } from './base';
|
|
2
|
+
import type { ChartKitWidgetSeriesOptions } from './series';
|
|
3
|
+
export type BarXSeriesData<T = any> = BaseSeriesData<T> & {
|
|
4
|
+
/** The x value of the point */
|
|
5
|
+
x?: number;
|
|
6
|
+
/** The y value of the point */
|
|
7
|
+
y?: number;
|
|
8
|
+
/** Corresponding value of axis category */
|
|
9
|
+
category?: string;
|
|
10
|
+
};
|
|
11
|
+
export type BarXSeries<T = any> = BaseSeries & {
|
|
12
|
+
type: 'bar-x';
|
|
13
|
+
data: BarXSeriesData<T>[];
|
|
14
|
+
/** The name of the series (used in legend) */
|
|
15
|
+
name: string;
|
|
16
|
+
/** The main color of the series (hex, rgba) */
|
|
17
|
+
color?: string;
|
|
18
|
+
stacking?: 'normal' | 'percent';
|
|
19
|
+
/** This option allows grouping series in a stacked chart */
|
|
20
|
+
stackId?: string;
|
|
21
|
+
/** Whether to group non-stacked columns or to let them render independent of each other.
|
|
22
|
+
* When false columns will be laid out individually and overlap each other.
|
|
23
|
+
*
|
|
24
|
+
* @default true
|
|
25
|
+
* */
|
|
26
|
+
grouping?: boolean;
|
|
27
|
+
dataLabels?: ChartKitWidgetSeriesOptions['dataLabels'] & {
|
|
28
|
+
/** Whether to align the data label inside the box or to the actual value point */
|
|
29
|
+
inside?: boolean;
|
|
30
|
+
};
|
|
31
|
+
};
|