@gravity-ui/chartkit 3.4.2 → 3.5.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/components/ChartKit.js +2 -2
- package/build/components/Loader/Loader.js +2 -2
- package/build/plugins/d3/renderer/D3Widget.js +3 -3
- package/build/plugins/d3/renderer/components/AxisX.js +2 -2
- package/build/plugins/d3/renderer/components/AxisY.js +2 -2
- package/build/plugins/d3/renderer/components/Chart.d.ts +3 -1
- package/build/plugins/d3/renderer/components/Chart.js +11 -6
- package/build/plugins/d3/renderer/components/Legend.js +80 -61
- package/build/plugins/d3/renderer/components/Title.js +2 -2
- package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.js +12 -0
- package/build/plugins/d3/renderer/components/Tooltip/index.js +2 -2
- package/build/plugins/d3/renderer/components/styles.css +4 -13
- package/build/plugins/d3/renderer/hooks/index.d.ts +1 -1
- package/build/plugins/d3/renderer/hooks/index.js +1 -1
- package/build/plugins/d3/renderer/hooks/{useScales → useAxisScales}/index.d.ts +6 -3
- package/build/plugins/d3/renderer/hooks/useAxisScales/index.js +122 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/chart.js +19 -12
- package/build/plugins/d3/renderer/hooks/useChartOptions/index.d.ts +3 -1
- package/build/plugins/d3/renderer/hooks/useLegend/index.js +35 -4
- package/build/plugins/d3/renderer/hooks/useSeries/index.js +48 -10
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x.d.ts +4 -1
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x.js +7 -4
- package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +8 -3
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +46 -21
- package/build/plugins/d3/renderer/hooks/useShapes/pie.d.ts +13 -0
- package/build/plugins/d3/renderer/hooks/useShapes/pie.js +51 -0
- package/build/plugins/d3/renderer/hooks/useShapes/scatter.d.ts +3 -1
- package/build/plugins/d3/renderer/hooks/useShapes/scatter.js +5 -4
- package/build/plugins/d3/renderer/hooks/useShapes/styles.css +16 -0
- package/build/plugins/d3/renderer/utils/index.d.ts +7 -1
- package/build/plugins/d3/renderer/utils/index.js +7 -12
- package/build/plugins/d3/renderer/utils/math.d.ts +23 -0
- package/build/plugins/d3/renderer/utils/math.js +43 -0
- package/build/plugins/highcharts/renderer/components/HighchartsComponent.js +1 -1
- package/build/plugins/highcharts/renderer/components/HighchartsReact.d.ts +18 -0
- package/build/plugins/highcharts/renderer/components/HighchartsReact.js +46 -0
- package/build/plugins/highcharts/renderer/components/StyledSplitPane/StyledSplitPane.js +2 -2
- package/build/plugins/highcharts/renderer/components/withSplitPane/withSplitPane.js +2 -2
- package/build/plugins/highcharts/renderer/helpers/config/config.js +2 -2
- package/build/plugins/highcharts/renderer/helpers/config/options.js +2 -2
- package/build/plugins/index.d.ts +1 -0
- package/build/plugins/index.js +1 -0
- package/build/plugins/indicator/renderer/IndicatorItem.js +2 -2
- package/build/plugins/indicator/renderer/IndicatorWidget.js +2 -2
- package/build/types/widget-data/bar-x.d.ts +1 -1
- package/build/types/widget-data/base.d.ts +3 -0
- package/build/types/widget-data/pie.d.ts +18 -1
- package/build/types/widget-data/scatter.d.ts +1 -1
- package/build/utils/cn.d.ts +3 -0
- package/build/utils/cn.js +4 -0
- package/package.json +4 -6
- package/build/plugins/d3/renderer/hooks/useScales/index.js +0 -109
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { __rest } from "tslib";
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import block from 'bem-cn-lite';
|
|
4
3
|
import { i18n } from '../i18n';
|
|
5
4
|
import { CHARTKIT_ERROR_CODE, ChartKitError, settings } from '../libs';
|
|
6
5
|
import { getRandomCKId, typedMemo } from '../utils';
|
|
6
|
+
import { cn } from '../utils/cn';
|
|
7
7
|
import { ErrorBoundary } from './ErrorBoundary/ErrorBoundary';
|
|
8
8
|
import { Loader } from './Loader/Loader';
|
|
9
9
|
import './ChartKit.css';
|
|
10
|
-
const b =
|
|
10
|
+
const b = cn('chartkit');
|
|
11
11
|
const ChartKitComponent = (props) => {
|
|
12
12
|
const widgetRef = React.useRef();
|
|
13
13
|
const { instanceRef, id: propsId, type, isMobile, renderPluginLoader } = props, restProps = __rest(props, ["instanceRef", "id", "type", "isMobile", "renderPluginLoader"]);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import block from 'bem-cn-lite';
|
|
3
2
|
import { Loader as BaseLoader } from '@gravity-ui/uikit';
|
|
3
|
+
import { block } from '../../utils/cn';
|
|
4
4
|
import './Loader.css';
|
|
5
|
-
const b = block('
|
|
5
|
+
const b = block('loader');
|
|
6
6
|
export const Loader = (props) => {
|
|
7
7
|
return (React.createElement("div", { className: b() },
|
|
8
8
|
React.createElement(BaseLoader, Object.assign({}, props))));
|
|
@@ -8,8 +8,8 @@ const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
|
|
|
8
8
|
const [dimensions, setDimensions] = React.useState();
|
|
9
9
|
const handleResize = React.useCallback(() => {
|
|
10
10
|
if (ref.current) {
|
|
11
|
-
const { width, height } = ref.current.getBoundingClientRect();
|
|
12
|
-
setDimensions({ width, height });
|
|
11
|
+
const { top, left, width, height } = ref.current.getBoundingClientRect();
|
|
12
|
+
setDimensions({ top, left, width, height });
|
|
13
13
|
}
|
|
14
14
|
}, []);
|
|
15
15
|
const debuncedHandleResize = React.useMemo(() => {
|
|
@@ -35,6 +35,6 @@ const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
|
|
|
35
35
|
// dimensions initialize
|
|
36
36
|
handleResize();
|
|
37
37
|
}, [handleResize]);
|
|
38
|
-
return (React.createElement("div", { ref: ref, style: { width: '100%', height: '100%' } }, (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) && (React.createElement(Chart, { width: dimensions.width, height: dimensions.height, data: props.data }))));
|
|
38
|
+
return (React.createElement("div", { ref: ref, style: { width: '100%', height: '100%', position: 'relative' } }, (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) && (React.createElement(Chart, { top: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.top) || 0, left: dimensions.left || 0, width: dimensions.width, height: dimensions.height, data: props.data }))));
|
|
39
39
|
});
|
|
40
40
|
export default D3Widget;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import block from 'bem-cn-lite';
|
|
3
2
|
import { axisBottom, select } from 'd3';
|
|
3
|
+
import { block } from '../../../../utils/cn';
|
|
4
4
|
import { formatAxisTickLabel, parseTransformStyle } from '../utils';
|
|
5
|
-
const b = block('
|
|
5
|
+
const b = block('d3-axis');
|
|
6
6
|
const EMPTY_SPACE_BETWEEN_LABELS = 10;
|
|
7
7
|
// Note: this method do not prepared for rotated labels
|
|
8
8
|
const removeOverlappingXTicks = (axis) => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import block from 'bem-cn-lite';
|
|
3
2
|
import { axisLeft, select } from 'd3';
|
|
3
|
+
import { block } from '../../../../utils/cn';
|
|
4
4
|
import { formatAxisTickLabel, parseTransformStyle } from '../utils';
|
|
5
|
-
const b = block('
|
|
5
|
+
const b = block('d3-axis');
|
|
6
6
|
const EMPTY_SPACE_BETWEEN_LABELS = 10;
|
|
7
7
|
// Note: this method do not prepared for rotated labels
|
|
8
8
|
const removeOverlappingYTicks = (axis) => {
|
|
@@ -2,9 +2,11 @@ import React from 'react';
|
|
|
2
2
|
import type { ChartKitWidgetData } from '../../../../types/widget-data';
|
|
3
3
|
import './styles.css';
|
|
4
4
|
type Props = {
|
|
5
|
+
top: number;
|
|
6
|
+
left: number;
|
|
5
7
|
width: number;
|
|
6
8
|
height: number;
|
|
7
9
|
data: ChartKitWidgetData;
|
|
8
10
|
};
|
|
9
|
-
export declare const Chart: (
|
|
11
|
+
export declare const Chart: (props: Props) => React.JSX.Element;
|
|
10
12
|
export {};
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import block from '
|
|
2
|
+
import { block } from '../../../../utils/cn';
|
|
3
3
|
import { AxisY } from './AxisY';
|
|
4
4
|
import { AxisX } from './AxisX';
|
|
5
5
|
import { Legend } from './Legend';
|
|
6
6
|
import { Title } from './Title';
|
|
7
7
|
import { Tooltip } from './Tooltip';
|
|
8
|
-
import { useChartDimensions, useChartEvents, useChartOptions, useLegend,
|
|
8
|
+
import { useChartDimensions, useChartEvents, useChartOptions, useLegend, useAxisScales, useSeries, useShapes, useTooltip, } from '../hooks';
|
|
9
9
|
import { isAxisRelatedSeries } from '../utils';
|
|
10
10
|
import './styles.css';
|
|
11
|
-
const b = block('
|
|
12
|
-
export const Chart = (
|
|
11
|
+
const b = block('d3');
|
|
12
|
+
export const Chart = (props) => {
|
|
13
|
+
const { top, left, width, height, data } = props;
|
|
13
14
|
// FIXME: add data validation
|
|
14
15
|
const { series } = data;
|
|
15
16
|
const svgRef = React.createRef();
|
|
@@ -27,7 +28,7 @@ export const Chart = ({ width, height, data }) => {
|
|
|
27
28
|
});
|
|
28
29
|
const { activeLegendItems, handleLegendItemClick } = useLegend({ series: series.data });
|
|
29
30
|
const { chartSeries } = useSeries({ activeLegendItems, series: series.data });
|
|
30
|
-
const { xScale, yScale } =
|
|
31
|
+
const { xScale, yScale } = useAxisScales({
|
|
31
32
|
boundsWidth,
|
|
32
33
|
boundsHeight,
|
|
33
34
|
series: chartSeries,
|
|
@@ -38,6 +39,10 @@ export const Chart = ({ width, height, data }) => {
|
|
|
38
39
|
tooltip,
|
|
39
40
|
});
|
|
40
41
|
const { shapes } = useShapes({
|
|
42
|
+
top,
|
|
43
|
+
left,
|
|
44
|
+
boundsWidth,
|
|
45
|
+
boundsHeight,
|
|
41
46
|
series: chartSeries,
|
|
42
47
|
xAxis,
|
|
43
48
|
xScale,
|
|
@@ -54,7 +59,7 @@ export const Chart = ({ width, height, data }) => {
|
|
|
54
59
|
chart.margin.left,
|
|
55
60
|
chart.margin.top + ((title === null || title === void 0 ? void 0 : title.height) || 0),
|
|
56
61
|
].join(',')})` },
|
|
57
|
-
hasAxisRelatedSeries && (React.createElement(React.Fragment, null,
|
|
62
|
+
hasAxisRelatedSeries && xScale && yScale && (React.createElement(React.Fragment, null,
|
|
58
63
|
React.createElement(AxisY, { axises: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale }),
|
|
59
64
|
React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
|
|
60
65
|
React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale })))),
|
|
@@ -1,66 +1,85 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import block from 'bem-cn-lite';
|
|
3
2
|
import { select } from 'd3';
|
|
4
|
-
|
|
3
|
+
import get from 'lodash/get';
|
|
4
|
+
import { block } from '../../../../utils/cn';
|
|
5
|
+
import { isAxisRelatedSeries } from '../utils';
|
|
6
|
+
const b = block('d3-legend');
|
|
7
|
+
const getLegendItems = (series) => {
|
|
8
|
+
return series.reduce((acc, s) => {
|
|
9
|
+
const isAxisRelated = isAxisRelatedSeries(s);
|
|
10
|
+
const legendEnabled = get(s, 'legend.enabled', true);
|
|
11
|
+
if (isAxisRelated) {
|
|
12
|
+
acc.push(s);
|
|
13
|
+
}
|
|
14
|
+
else if (!isAxisRelated && legendEnabled) {
|
|
15
|
+
acc.push(...s.data);
|
|
16
|
+
}
|
|
17
|
+
return acc;
|
|
18
|
+
}, []);
|
|
19
|
+
};
|
|
5
20
|
export const Legend = (props) => {
|
|
6
21
|
const { width, offsetWidth, height, offsetHeight, chartSeries, onItemClick } = props;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
22
|
+
const ref = React.useRef(null);
|
|
23
|
+
React.useEffect(() => {
|
|
24
|
+
if (!ref.current) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const legendItems = getLegendItems(chartSeries);
|
|
28
|
+
const size = 10;
|
|
29
|
+
const textWidths = [0];
|
|
30
|
+
const svgElement = select(ref.current);
|
|
31
|
+
svgElement.selectAll('*').remove();
|
|
32
|
+
const legendItemTemplate = svgElement
|
|
33
|
+
.selectAll('legend-history')
|
|
34
|
+
.data(legendItems)
|
|
35
|
+
.enter()
|
|
36
|
+
.append('g')
|
|
37
|
+
.attr('class', b('item'))
|
|
38
|
+
.on('click', function (e, d) {
|
|
39
|
+
onItemClick({ name: d.name, metaKey: e.metaKey });
|
|
40
|
+
});
|
|
41
|
+
svgElement
|
|
42
|
+
.selectAll('*')
|
|
43
|
+
.data(legendItems)
|
|
44
|
+
.append('text')
|
|
45
|
+
.text(function (d) {
|
|
46
|
+
return d.name;
|
|
47
|
+
})
|
|
48
|
+
.each(function () {
|
|
49
|
+
textWidths.push(this.getComputedTextLength());
|
|
50
|
+
})
|
|
51
|
+
.remove();
|
|
52
|
+
legendItemTemplate
|
|
53
|
+
.append('rect')
|
|
54
|
+
.attr('x', function (_d, i) {
|
|
55
|
+
return (offsetWidth +
|
|
56
|
+
i * size +
|
|
57
|
+
textWidths.slice(0, i + 1).reduce((acc, tw) => acc + tw, 0));
|
|
58
|
+
})
|
|
59
|
+
.attr('y', offsetHeight - size / 2)
|
|
60
|
+
.attr('width', size)
|
|
61
|
+
.attr('height', size)
|
|
62
|
+
.attr('class', b('item-shape'))
|
|
63
|
+
.style('fill', function (d) {
|
|
64
|
+
return d.color;
|
|
65
|
+
});
|
|
66
|
+
legendItemTemplate
|
|
67
|
+
.append('text')
|
|
68
|
+
.attr('x', function (_d, i) {
|
|
69
|
+
return (offsetWidth +
|
|
70
|
+
i * size +
|
|
71
|
+
size +
|
|
72
|
+
textWidths.slice(0, i + 1).reduce((acc, tw) => acc + tw, 0));
|
|
73
|
+
})
|
|
74
|
+
.attr('y', offsetHeight)
|
|
75
|
+
.attr('class', function (d) {
|
|
76
|
+
const mods = { selected: d.visible, unselected: !d.visible };
|
|
77
|
+
return b('item-text', mods);
|
|
78
|
+
})
|
|
79
|
+
.text(function (d) {
|
|
80
|
+
return ('name' in d && d.name);
|
|
81
|
+
})
|
|
82
|
+
.style('alignment-baseline', 'middle');
|
|
83
|
+
}, [width, offsetWidth, height, offsetHeight, chartSeries, onItemClick]);
|
|
84
|
+
return React.createElement("g", { ref: ref, width: width, height: height });
|
|
66
85
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import block from '
|
|
3
|
-
const b = block('
|
|
2
|
+
import { block } from '../../../../utils/cn';
|
|
3
|
+
const b = block('d3-title');
|
|
4
4
|
export const Title = (props) => {
|
|
5
5
|
const { chartWidth, text, height, style } = props;
|
|
6
6
|
return (React.createElement("text", { className: b(), dx: chartWidth / 2, dy: height / 2, dominantBaseline: "middle", textAnchor: "middle", style: { fontSize: style === null || style === void 0 ? void 0 : style.fontSize, lineHeight: `${height}px` } },
|
|
@@ -14,6 +14,18 @@ export const DefaultContent = ({ hovered, xAxis, yAxis }) => {
|
|
|
14
14
|
React.createElement("span", null, "Y:\u00A0"),
|
|
15
15
|
React.createElement("b", null, yRow))));
|
|
16
16
|
}
|
|
17
|
+
case 'bar-x': {
|
|
18
|
+
const barXData = data;
|
|
19
|
+
const xRow = xAxis.type === 'category' ? barXData.category : barXData.x;
|
|
20
|
+
const yRow = yAxis.type === 'category' ? barXData.category : barXData.y;
|
|
21
|
+
return (React.createElement("div", null,
|
|
22
|
+
React.createElement("div", null, xRow),
|
|
23
|
+
React.createElement("div", null,
|
|
24
|
+
React.createElement("span", null,
|
|
25
|
+
React.createElement("b", null, series.name),
|
|
26
|
+
": ",
|
|
27
|
+
yRow))));
|
|
28
|
+
}
|
|
17
29
|
default: {
|
|
18
30
|
return null;
|
|
19
31
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import block from '
|
|
2
|
+
import { block } from '../../../../../utils/cn';
|
|
3
3
|
import { DefaultContent } from './DefaultContent';
|
|
4
|
-
const b = block('
|
|
4
|
+
const b = block('d3-tooltip');
|
|
5
5
|
const POINTER_OFFSET_X = 20;
|
|
6
6
|
export const Tooltip = (props) => {
|
|
7
7
|
const { hovered, pointerPosition, tooltip, xAxis, yAxis } = props;
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
user-select: none;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
.chartkit-d3-legend__item-shape {
|
|
23
|
+
fill: var(--g-color-base-misc-medium);
|
|
24
|
+
}
|
|
25
|
+
|
|
22
26
|
.chartkit-d3-legend__item-text {
|
|
23
27
|
fill: var(--g-color-text-secondary);
|
|
24
28
|
}
|
|
@@ -31,19 +35,6 @@
|
|
|
31
35
|
fill: var(--g-color-text-complementary);
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
.chartkit-d3-scatter__point {
|
|
35
|
-
stroke-width: 1px;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.chartkit-d3_hovered .chartkit-d3-scatter__point {
|
|
39
|
-
opacity: 0.5;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.chartkit-d3-scatter__point:hover {
|
|
43
|
-
stroke: #fff;
|
|
44
|
-
opacity: 1;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
38
|
.chartkit-d3-title {
|
|
48
39
|
font-size: var(--g-text-subheader-2-font-size);
|
|
49
40
|
font-weight: var(--g-text-subheader-font-weight);
|
|
@@ -3,7 +3,7 @@ export * from './useChartEvents';
|
|
|
3
3
|
export * from './useChartOptions';
|
|
4
4
|
export * from './useChartOptions/types';
|
|
5
5
|
export * from './useLegend';
|
|
6
|
-
export * from './
|
|
6
|
+
export * from './useAxisScales';
|
|
7
7
|
export * from './useSeries';
|
|
8
8
|
export * from './useShapes';
|
|
9
9
|
export * from './useTooltip';
|
|
@@ -3,7 +3,7 @@ export * from './useChartEvents';
|
|
|
3
3
|
export * from './useChartOptions';
|
|
4
4
|
export * from './useChartOptions/types';
|
|
5
5
|
export * from './useLegend';
|
|
6
|
-
export * from './
|
|
6
|
+
export * from './useAxisScales';
|
|
7
7
|
export * from './useSeries';
|
|
8
8
|
export * from './useShapes';
|
|
9
9
|
export * from './useTooltip';
|
|
@@ -10,8 +10,11 @@ type Args = {
|
|
|
10
10
|
yAxis: ChartOptions['yAxis'];
|
|
11
11
|
};
|
|
12
12
|
type ReturnValue = {
|
|
13
|
-
xScale
|
|
14
|
-
yScale
|
|
13
|
+
xScale?: ChartScale;
|
|
14
|
+
yScale?: ChartScale;
|
|
15
15
|
};
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Uses to create scales for axis related series
|
|
18
|
+
*/
|
|
19
|
+
export declare const useAxisScales: (args: Args) => ReturnValue;
|
|
17
20
|
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { scaleBand, scaleLinear, scaleUtc, extent } from 'd3';
|
|
3
|
+
import get from 'lodash/get';
|
|
4
|
+
import { getOnlyVisibleSeries, getDomainDataXBySeries, getDomainDataYBySeries, isAxisRelatedSeries, } from '../../utils';
|
|
5
|
+
const isNumericalArrayData = (data) => {
|
|
6
|
+
return data.every((d) => typeof d === 'number' || d === null);
|
|
7
|
+
};
|
|
8
|
+
const filterCategoriesByVisibleSeries = (categories, series) => {
|
|
9
|
+
return categories.filter((category) => {
|
|
10
|
+
return series.some((s) => {
|
|
11
|
+
return s.data.some((d) => 'category' in d && d.category === category);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
const createScales = (args) => {
|
|
16
|
+
const { boundsWidth, boundsHeight, series, xAxis, yAxis } = args;
|
|
17
|
+
const xMin = get(xAxis, 'min');
|
|
18
|
+
const xType = get(xAxis, 'type', 'linear');
|
|
19
|
+
const xCategories = get(xAxis, 'categories');
|
|
20
|
+
const xTimestamps = get(xAxis, 'timestamps');
|
|
21
|
+
const yType = get(yAxis[0], 'type', 'linear');
|
|
22
|
+
const yMin = get(yAxis[0], 'min');
|
|
23
|
+
const yCategories = get(yAxis[0], 'categories');
|
|
24
|
+
const yTimestamps = get(xAxis, 'timestamps');
|
|
25
|
+
let visibleSeries = getOnlyVisibleSeries(series);
|
|
26
|
+
// Reassign to all series in case of all series unselected,
|
|
27
|
+
// otherwise we will get an empty space without grid
|
|
28
|
+
visibleSeries = visibleSeries.length === 0 ? series : visibleSeries;
|
|
29
|
+
let xScale;
|
|
30
|
+
let yScale;
|
|
31
|
+
switch (xType) {
|
|
32
|
+
case 'linear': {
|
|
33
|
+
const domain = getDomainDataXBySeries(visibleSeries);
|
|
34
|
+
const range = [0, boundsWidth - boundsWidth * xAxis.maxPadding];
|
|
35
|
+
if (isNumericalArrayData(domain)) {
|
|
36
|
+
const [domainXMin, xMax] = extent(domain);
|
|
37
|
+
const xMinValue = typeof xMin === 'number' ? xMin : domainXMin;
|
|
38
|
+
xScale = scaleLinear().domain([xMinValue, xMax]).range(range).nice();
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case 'category': {
|
|
43
|
+
if (xCategories) {
|
|
44
|
+
const filteredCategories = filterCategoriesByVisibleSeries(xCategories, visibleSeries);
|
|
45
|
+
xScale = scaleBand().domain(filteredCategories).range([0, boundsWidth]);
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case 'datetime': {
|
|
50
|
+
const range = [0, boundsWidth - boundsWidth * xAxis.maxPadding];
|
|
51
|
+
if (xTimestamps) {
|
|
52
|
+
const [xMin, xMax] = extent(xTimestamps);
|
|
53
|
+
xScale = scaleUtc().domain([xMin, xMax]).range(range).nice();
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const domain = getDomainDataXBySeries(visibleSeries);
|
|
57
|
+
if (isNumericalArrayData(domain)) {
|
|
58
|
+
const [xMin, xMax] = extent(domain);
|
|
59
|
+
xScale = scaleUtc().domain([xMin, xMax]).range(range).nice();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!xScale) {
|
|
66
|
+
throw new Error('Failed to create xScale');
|
|
67
|
+
}
|
|
68
|
+
switch (yType) {
|
|
69
|
+
case 'linear': {
|
|
70
|
+
const domain = getDomainDataYBySeries(visibleSeries);
|
|
71
|
+
const range = [boundsHeight, boundsHeight * yAxis[0].maxPadding];
|
|
72
|
+
if (isNumericalArrayData(domain)) {
|
|
73
|
+
const [domainYMin, yMax] = extent(domain);
|
|
74
|
+
const yMinValue = typeof yMin === 'number' ? yMin : domainYMin;
|
|
75
|
+
yScale = scaleLinear().domain([yMinValue, yMax]).range(range).nice();
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case 'category': {
|
|
80
|
+
if (yCategories) {
|
|
81
|
+
const filteredCategories = filterCategoriesByVisibleSeries(yCategories, visibleSeries);
|
|
82
|
+
yScale = scaleBand().domain(filteredCategories).range([boundsHeight, 0]);
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case 'datetime': {
|
|
87
|
+
const range = [boundsHeight, boundsHeight * yAxis[0].maxPadding];
|
|
88
|
+
if (yTimestamps) {
|
|
89
|
+
const [yMin, yMax] = extent(yTimestamps);
|
|
90
|
+
yScale = scaleUtc().domain([yMin, yMax]).range(range).nice();
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
const domain = getDomainDataYBySeries(visibleSeries);
|
|
94
|
+
if (isNumericalArrayData(domain)) {
|
|
95
|
+
const [yMin, yMax] = extent(domain);
|
|
96
|
+
yScale = scaleUtc().domain([yMin, yMax]).range(range).nice();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (!yScale) {
|
|
103
|
+
throw new Error('Failed to create yScale');
|
|
104
|
+
}
|
|
105
|
+
return { xScale, yScale };
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Uses to create scales for axis related series
|
|
109
|
+
*/
|
|
110
|
+
export const useAxisScales = (args) => {
|
|
111
|
+
const { boundsWidth, boundsHeight, series, xAxis, yAxis } = args;
|
|
112
|
+
const scales = React.useMemo(() => {
|
|
113
|
+
let xScale;
|
|
114
|
+
let yScale;
|
|
115
|
+
const hasAxisRelatedSeries = series.some(isAxisRelatedSeries);
|
|
116
|
+
if (hasAxisRelatedSeries) {
|
|
117
|
+
({ xScale, yScale } = createScales({ boundsWidth, boundsHeight, series, xAxis, yAxis }));
|
|
118
|
+
}
|
|
119
|
+
return { xScale, yScale };
|
|
120
|
+
}, [boundsWidth, boundsHeight, series, xAxis, yAxis]);
|
|
121
|
+
return scales;
|
|
122
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { select, max } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
|
-
import { formatAxisTickLabel, getDomainDataYBySeries } from '../../utils';
|
|
3
|
+
import { formatAxisTickLabel, getDomainDataYBySeries, isAxisRelatedSeries } from '../../utils';
|
|
4
4
|
import { getHorisontalSvgTextDimensions } from './utils';
|
|
5
5
|
const AXIS_WIDTH = 1;
|
|
6
6
|
const getAxisLabelMaxWidth = (args) => {
|
|
@@ -45,17 +45,24 @@ const getAxisLabelMaxWidth = (args) => {
|
|
|
45
45
|
};
|
|
46
46
|
export const getPreparedChart = (args) => {
|
|
47
47
|
const { chart, series, preparedXAxis, preparedY1Axis } = args;
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
const hasAxisRelatedSeries = series.data.some(isAxisRelatedSeries);
|
|
49
|
+
let marginBottom = get(chart, 'margin.bottom', 0);
|
|
50
|
+
let marginLeft = get(chart, 'margin.left', 0);
|
|
51
|
+
let marginTop = get(chart, 'margin.top', 0);
|
|
52
|
+
let marginRight = get(chart, 'margin.right', 0);
|
|
53
|
+
if (hasAxisRelatedSeries) {
|
|
54
|
+
marginBottom +=
|
|
55
|
+
preparedXAxis.labels.padding +
|
|
56
|
+
getHorisontalSvgTextDimensions({ text: 'Tmp', style: preparedXAxis.labels.style });
|
|
57
|
+
marginLeft +=
|
|
58
|
+
AXIS_WIDTH +
|
|
59
|
+
preparedY1Axis.labels.padding +
|
|
60
|
+
getAxisLabelMaxWidth({ axis: preparedY1Axis, series: series.data }) +
|
|
61
|
+
(preparedY1Axis.title.height || 0);
|
|
62
|
+
marginTop +=
|
|
63
|
+
getHorisontalSvgTextDimensions({ text: 'Tmp', style: preparedY1Axis.labels.style }) / 2;
|
|
64
|
+
marginRight += getAxisLabelMaxWidth({ axis: preparedXAxis, series: series.data }) / 2;
|
|
65
|
+
}
|
|
59
66
|
return {
|
|
60
67
|
margin: {
|
|
61
68
|
top: marginTop,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { ChartKitWidgetData } from '../../../../../types/widget-data';
|
|
2
2
|
import type { ChartOptions } from './types';
|
|
3
|
-
|
|
3
|
+
type Args = ChartKitWidgetData;
|
|
4
|
+
export declare const useChartOptions: (args: Args) => ChartOptions;
|
|
5
|
+
export {};
|
|
@@ -1,8 +1,39 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
2
|
+
import get from 'lodash/get';
|
|
3
|
+
import { isAxisRelatedSeries } from '../../utils';
|
|
4
|
+
const getActiveLegendItems = (series) => {
|
|
5
|
+
return series.reduce((acc, s) => {
|
|
6
|
+
const isAxisRelated = isAxisRelatedSeries(s);
|
|
7
|
+
const isLegendEnabled = get(s, 'legend.enabled', true);
|
|
8
|
+
const isSeriesVisible = get(s, 'visible', true);
|
|
9
|
+
if (isLegendEnabled && isAxisRelated && isSeriesVisible && 'name' in s) {
|
|
10
|
+
acc.push(s.name);
|
|
11
|
+
}
|
|
12
|
+
else if (isLegendEnabled && !isAxisRelated) {
|
|
13
|
+
s.data.forEach((d) => {
|
|
14
|
+
const isDataVisible = get(d, 'visible', true);
|
|
15
|
+
if (isDataVisible && 'name' in d) {
|
|
16
|
+
acc.push(d.name);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return acc;
|
|
21
|
+
}, []);
|
|
22
|
+
};
|
|
23
|
+
const getAllLegendItems = (series) => {
|
|
24
|
+
return series.reduce((acc, s) => {
|
|
25
|
+
if (isAxisRelatedSeries(s) && 'name' in s) {
|
|
26
|
+
acc.push(s.name);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
acc.push(...s.data.map((d) => ('name' in d && d.name) || ''));
|
|
30
|
+
}
|
|
31
|
+
return acc;
|
|
32
|
+
}, []);
|
|
33
|
+
};
|
|
3
34
|
export const useLegend = (args) => {
|
|
4
35
|
const { series } = args;
|
|
5
|
-
const [activeLegendItems, setActiveLegendItems] = React.useState(
|
|
36
|
+
const [activeLegendItems, setActiveLegendItems] = React.useState(getActiveLegendItems(series));
|
|
6
37
|
const handleLegendItemClick = React.useCallback(({ name, metaKey }) => {
|
|
7
38
|
const onlyItemSelected = activeLegendItems.length === 1 && activeLegendItems.includes(name);
|
|
8
39
|
let nextActiveLegendItems;
|
|
@@ -13,7 +44,7 @@ export const useLegend = (args) => {
|
|
|
13
44
|
nextActiveLegendItems = activeLegendItems.concat(name);
|
|
14
45
|
}
|
|
15
46
|
else if (onlyItemSelected) {
|
|
16
|
-
nextActiveLegendItems =
|
|
47
|
+
nextActiveLegendItems = getAllLegendItems(series);
|
|
17
48
|
}
|
|
18
49
|
else {
|
|
19
50
|
nextActiveLegendItems = [name];
|
|
@@ -22,7 +53,7 @@ export const useLegend = (args) => {
|
|
|
22
53
|
}, [series, activeLegendItems]);
|
|
23
54
|
// FIXME: remove effect. It initiates extra rerender
|
|
24
55
|
React.useEffect(() => {
|
|
25
|
-
setActiveLegendItems(
|
|
56
|
+
setActiveLegendItems(getActiveLegendItems(series));
|
|
26
57
|
}, [series]);
|
|
27
58
|
return { activeLegendItems, handleLegendItemClick };
|
|
28
59
|
};
|