@gravity-ui/chartkit 3.1.3 → 3.2.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/__stories__/LinearCategories.stories.d.ts +4 -0
- package/build/plugins/d3/__stories__/LinearCategories.stories.js +101 -0
- package/build/plugins/d3/__stories__/Timestamp.stories.d.ts +4 -0
- package/build/plugins/d3/__stories__/Timestamp.stories.js +89 -0
- package/build/plugins/d3/__stories__/penguins.json +3098 -0
- package/build/plugins/d3/index.d.ts +7 -0
- package/build/plugins/d3/index.js +10 -0
- package/build/plugins/d3/renderer/D3Widget.d.ts +15 -0
- package/build/plugins/d3/renderer/D3Widget.js +40 -0
- package/build/plugins/d3/renderer/components/AxisX.d.ts +10 -0
- package/build/plugins/d3/renderer/components/AxisX.js +68 -0
- package/build/plugins/d3/renderer/components/AxisY.d.ts +10 -0
- package/build/plugins/d3/renderer/components/AxisY.js +73 -0
- package/build/plugins/d3/renderer/components/Chart.d.ts +10 -0
- package/build/plugins/d3/renderer/components/Chart.js +64 -0
- package/build/plugins/d3/renderer/components/Legend.d.ts +12 -0
- package/build/plugins/d3/renderer/components/Legend.js +66 -0
- package/build/plugins/d3/renderer/components/Title.d.ts +7 -0
- package/build/plugins/d3/renderer/components/Title.js +8 -0
- package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.d.ts +10 -0
- package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.js +21 -0
- package/build/plugins/d3/renderer/components/Tooltip/index.d.ts +12 -0
- package/build/plugins/d3/renderer/components/Tooltip/index.js +53 -0
- package/build/plugins/d3/renderer/components/index.d.ts +1 -0
- package/build/plugins/d3/renderer/components/index.js +1 -0
- package/build/plugins/d3/renderer/components/styles.css +61 -0
- package/build/plugins/d3/renderer/constants.d.ts +1 -0
- package/build/plugins/d3/renderer/constants.js +22 -0
- package/build/plugins/d3/renderer/hooks/index.d.ts +10 -0
- package/build/plugins/d3/renderer/hooks/index.js +10 -0
- package/build/plugins/d3/renderer/hooks/useChartDimensions/index.d.ts +17 -0
- package/build/plugins/d3/renderer/hooks/useChartDimensions/index.js +13 -0
- package/build/plugins/d3/renderer/hooks/useChartEvents/index.d.ts +5 -0
- package/build/plugins/d3/renderer/hooks/useChartEvents/index.js +15 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/chart.d.ts +8 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/chart.js +60 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/constants.d.ts +3 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/constants.js +3 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/index.d.ts +3 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/index.js +32 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/legend.d.ts +6 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/legend.js +5 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/title.d.ts +5 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/title.js +17 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/tooltip.d.ts +5 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/tooltip.js +5 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/types.d.ts +32 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/types.js +1 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/utils.d.ts +5 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/utils.js +18 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.d.ts +5 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +29 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.d.ts +5 -0
- package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +34 -0
- package/build/plugins/d3/renderer/hooks/useLegend/index.d.ts +13 -0
- package/build/plugins/d3/renderer/hooks/useLegend/index.js +28 -0
- package/build/plugins/d3/renderer/hooks/useScales/index.d.ts +17 -0
- package/build/plugins/d3/renderer/hooks/useScales/index.js +101 -0
- package/build/plugins/d3/renderer/hooks/useSeries/index.d.ts +14 -0
- package/build/plugins/d3/renderer/hooks/useSeries/index.js +23 -0
- package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +19 -0
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +73 -0
- package/build/plugins/d3/renderer/hooks/useTooltip/index.d.ts +13 -0
- package/build/plugins/d3/renderer/hooks/useTooltip/index.js +19 -0
- package/build/plugins/d3/renderer/hooks/useTooltip/types.d.ts +7 -0
- package/build/plugins/d3/renderer/hooks/useTooltip/types.js +1 -0
- package/build/plugins/d3/renderer/utils/index.d.ts +18 -0
- package/build/plugins/d3/renderer/utils/index.js +71 -0
- package/build/plugins/d3/types.d.ts +4 -0
- package/build/plugins/d3/types.js +1 -0
- package/build/types/widget-data/axis.d.ts +22 -0
- package/build/types/widget-data/axis.js +1 -0
- package/build/types/widget-data/base.d.ts +15 -0
- package/build/types/widget-data/base.js +1 -0
- package/build/types/widget-data/chart.d.ts +9 -0
- package/build/types/widget-data/chart.js +1 -0
- package/build/types/widget-data/index.d.ts +24 -0
- package/build/types/widget-data/index.js +9 -0
- package/build/types/widget-data/legend.d.ts +3 -0
- package/build/types/widget-data/legend.js +1 -0
- package/build/types/widget-data/pie.d.ts +10 -0
- package/build/types/widget-data/pie.js +1 -0
- package/build/types/widget-data/scatter.d.ts +20 -0
- package/build/types/widget-data/scatter.js +1 -0
- package/build/types/widget-data/series.d.ts +4 -0
- package/build/types/widget-data/series.js +1 -0
- package/build/types/widget-data/title.d.ts +5 -0
- package/build/types/widget-data/title.js +1 -0
- package/build/types/widget-data/tooltip.d.ts +12 -0
- package/build/types/widget-data/tooltip.js +1 -0
- package/build/types/widget.d.ts +5 -0
- package/package.json +4 -2
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ChartKitWidgetRef } from '../../../types';
|
|
3
|
+
declare const D3Widget: React.ForwardRefExoticComponent<{
|
|
4
|
+
type: "d3";
|
|
5
|
+
data: import("../../../types/widget-data").ChartKitWidgetData<any>;
|
|
6
|
+
id?: string | undefined;
|
|
7
|
+
isMobile?: boolean | undefined;
|
|
8
|
+
onLoad?: ((data?: import("../../../types").ChartKitOnLoadData<"d3"> | undefined) => void) | undefined;
|
|
9
|
+
onRender?: ((data: import("../../../types").ChartKitOnRenderData) => void) | undefined;
|
|
10
|
+
onChartLoad?: ((data: import("../../../types").ChartKitOnChartLoad<"d3">) => void) | undefined;
|
|
11
|
+
onError?: import("../../../types").ChartKitOnError | undefined;
|
|
12
|
+
renderError?: import("../../../types").RenderError | undefined;
|
|
13
|
+
renderPluginLoader?: (() => React.ReactNode) | undefined;
|
|
14
|
+
} & {} & React.RefAttributes<ChartKitWidgetRef | undefined>>;
|
|
15
|
+
export default D3Widget;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { select } from 'd3';
|
|
3
|
+
import debounce from 'lodash/debounce';
|
|
4
|
+
import { Chart } from './components';
|
|
5
|
+
const D3Widget = React.forwardRef(function D3Widget(props, forwardedRef) {
|
|
6
|
+
const ref = React.useRef(null);
|
|
7
|
+
const debounced = React.useRef();
|
|
8
|
+
const [dimensions, setDimensions] = React.useState();
|
|
9
|
+
const handleResize = React.useCallback(() => {
|
|
10
|
+
if (ref.current) {
|
|
11
|
+
const { width, height } = ref.current.getBoundingClientRect();
|
|
12
|
+
setDimensions({ width, height });
|
|
13
|
+
}
|
|
14
|
+
}, []);
|
|
15
|
+
const debuncedHandleResize = React.useMemo(() => {
|
|
16
|
+
var _a;
|
|
17
|
+
(_a = debounced.current) === null || _a === void 0 ? void 0 : _a.cancel();
|
|
18
|
+
debounced.current = debounce(handleResize, 200);
|
|
19
|
+
return debounced.current;
|
|
20
|
+
}, [handleResize]);
|
|
21
|
+
React.useImperativeHandle(forwardedRef, () => ({
|
|
22
|
+
reflow() {
|
|
23
|
+
handleResize();
|
|
24
|
+
},
|
|
25
|
+
}), [handleResize]);
|
|
26
|
+
React.useEffect(() => {
|
|
27
|
+
const selection = select(window);
|
|
28
|
+
selection.on('resize', debuncedHandleResize);
|
|
29
|
+
return () => {
|
|
30
|
+
// https://d3js.org/d3-selection/events#selection_on
|
|
31
|
+
selection.on('resize', null);
|
|
32
|
+
};
|
|
33
|
+
}, [debuncedHandleResize]);
|
|
34
|
+
React.useEffect(() => {
|
|
35
|
+
// dimensions initialize
|
|
36
|
+
handleResize();
|
|
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 }))));
|
|
39
|
+
});
|
|
40
|
+
export default D3Widget;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ChartOptions, ChartScale } from '../hooks';
|
|
3
|
+
type Props = {
|
|
4
|
+
axis: ChartOptions['xAxis'];
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
scale: ChartScale;
|
|
8
|
+
};
|
|
9
|
+
export declare const AxisX: ({ axis, width, height, scale }: Props) => React.JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import block from 'bem-cn-lite';
|
|
3
|
+
import { axisBottom, select } from 'd3';
|
|
4
|
+
import { formatAxisTickLabel, parseTransformStyle } from '../utils';
|
|
5
|
+
const b = block('chartkit-d3-axis');
|
|
6
|
+
const EMPTY_SPACE_BETWEEN_LABELS = 10;
|
|
7
|
+
// Note: this method do not prepared for rotated labels
|
|
8
|
+
const removeOverlappingXTicks = (axis) => {
|
|
9
|
+
var _a;
|
|
10
|
+
const a = axis.selectAll('g.tick').nodes();
|
|
11
|
+
if (a.length <= 1) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
for (let i = 0, x = 0; i < a.length; i++) {
|
|
15
|
+
const node = a[i];
|
|
16
|
+
const r = node.getBoundingClientRect();
|
|
17
|
+
if (r.left < x) {
|
|
18
|
+
(_a = node === null || node === void 0 ? void 0 : node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
x = r.right + EMPTY_SPACE_BETWEEN_LABELS;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
// FIXME: add overflow ellipsis for the labels that out of boundaries
|
|
26
|
+
export const AxisX = ({ axis, width, height, scale }) => {
|
|
27
|
+
const ref = React.useRef(null);
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
if (!ref.current) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const svgElement = select(ref.current);
|
|
33
|
+
svgElement.selectAll('*').remove();
|
|
34
|
+
const xAxisGenerator = axisBottom(scale)
|
|
35
|
+
.tickSize(height * -1)
|
|
36
|
+
.tickPadding(axis.labels.padding)
|
|
37
|
+
.tickFormat((value) => {
|
|
38
|
+
return formatAxisTickLabel({
|
|
39
|
+
axisType: axis.type,
|
|
40
|
+
value,
|
|
41
|
+
dateFormat: axis.labels.dateFormat,
|
|
42
|
+
numberFormat: axis.labels.numberFormat,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
svgElement.call(xAxisGenerator).attr('class', b());
|
|
46
|
+
svgElement.select('.domain').attr('d', `M0,0V0H${width}`);
|
|
47
|
+
svgElement.selectAll('.tick text').style('font-size', axis.labels.style.fontSize);
|
|
48
|
+
const transformStyle = svgElement.select('.tick').attr('transform');
|
|
49
|
+
const { x } = parseTransformStyle(transformStyle);
|
|
50
|
+
if (x === 0) {
|
|
51
|
+
// Remove tick that has the same x coordinate like domain
|
|
52
|
+
svgElement.select('.tick').remove();
|
|
53
|
+
}
|
|
54
|
+
if (axis.title.text) {
|
|
55
|
+
const textY = axis.title.height + parseInt(axis.labels.style.fontSize) + axis.labels.padding;
|
|
56
|
+
svgElement
|
|
57
|
+
.append('text')
|
|
58
|
+
.attr('class', b('title'))
|
|
59
|
+
.attr('text-anchor', 'middle')
|
|
60
|
+
.attr('x', width / 2)
|
|
61
|
+
.attr('y', textY)
|
|
62
|
+
.attr('font-size', axis.title.style.fontSize)
|
|
63
|
+
.text(axis.title.text);
|
|
64
|
+
}
|
|
65
|
+
removeOverlappingXTicks(svgElement);
|
|
66
|
+
}, [axis, width, height, scale]);
|
|
67
|
+
return React.createElement("g", { ref: ref });
|
|
68
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ChartOptions, ChartScale } from '../hooks';
|
|
3
|
+
type Props = {
|
|
4
|
+
axises: ChartOptions['yAxis'];
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
scale: ChartScale;
|
|
8
|
+
};
|
|
9
|
+
export declare const AxisY: ({ axises, width, height, scale }: Props) => React.JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import block from 'bem-cn-lite';
|
|
3
|
+
import { axisLeft, select } from 'd3';
|
|
4
|
+
import { formatAxisTickLabel, parseTransformStyle } from '../utils';
|
|
5
|
+
const b = block('chartkit-d3-axis');
|
|
6
|
+
const EMPTY_SPACE_BETWEEN_LABELS = 10;
|
|
7
|
+
// Note: this method do not prepared for rotated labels
|
|
8
|
+
const removeOverlappingYTicks = (axis) => {
|
|
9
|
+
var _a;
|
|
10
|
+
const a = axis.selectAll('g.tick').nodes();
|
|
11
|
+
if (a.length <= 1) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
for (let i = 0, x = 0; i < a.length; i++) {
|
|
15
|
+
const node = a[i];
|
|
16
|
+
const r = node.getBoundingClientRect();
|
|
17
|
+
if (r.bottom > x && i !== 0) {
|
|
18
|
+
(_a = node === null || node === void 0 ? void 0 : node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
x = r.top - EMPTY_SPACE_BETWEEN_LABELS;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
// FIXME: add overflow ellipsis for the labels that out of boundaries
|
|
26
|
+
export const AxisY = ({ axises, width, height, scale }) => {
|
|
27
|
+
const ref = React.useRef(null);
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
if (!ref.current) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const axis = axises[0];
|
|
33
|
+
const svgElement = select(ref.current);
|
|
34
|
+
svgElement.selectAll('*').remove();
|
|
35
|
+
const yAxisGenerator = axisLeft(scale)
|
|
36
|
+
.tickSize(width * -1)
|
|
37
|
+
.tickPadding(axis.labels.padding)
|
|
38
|
+
.tickFormat((value) => {
|
|
39
|
+
return formatAxisTickLabel({
|
|
40
|
+
axisType: axis.type,
|
|
41
|
+
value,
|
|
42
|
+
dateFormat: axis.labels.dateFormat,
|
|
43
|
+
numberFormat: axis.labels.numberFormat,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
svgElement.call(yAxisGenerator).attr('class', b());
|
|
47
|
+
svgElement.select('.domain').attr('d', `M0,${height}H0V0`);
|
|
48
|
+
svgElement
|
|
49
|
+
.selectAll('.tick text')
|
|
50
|
+
.style('font-size', axis.labels.style.fontSize)
|
|
51
|
+
.style('transform', 'translateY(-1px)');
|
|
52
|
+
const transformStyle = svgElement.select('.tick').attr('transform');
|
|
53
|
+
const { y } = parseTransformStyle(transformStyle);
|
|
54
|
+
if (y === height) {
|
|
55
|
+
// Remove stroke from tick that has the same y coordinate like domain
|
|
56
|
+
svgElement.select('.tick line').style('stroke', 'none');
|
|
57
|
+
}
|
|
58
|
+
if (axis.title.text) {
|
|
59
|
+
const textY = axis.title.height + axis.labels.padding;
|
|
60
|
+
svgElement
|
|
61
|
+
.append('text')
|
|
62
|
+
.attr('class', b('title'))
|
|
63
|
+
.attr('text-anchor', 'middle')
|
|
64
|
+
.attr('dy', -textY)
|
|
65
|
+
.attr('dx', -height / 2)
|
|
66
|
+
.attr('font-size', axis.title.style.fontSize)
|
|
67
|
+
.attr('transform', 'rotate(-90)')
|
|
68
|
+
.text(axis.title.text);
|
|
69
|
+
}
|
|
70
|
+
removeOverlappingYTicks(svgElement);
|
|
71
|
+
}, [axises, width, height, scale]);
|
|
72
|
+
return React.createElement("g", { ref: ref });
|
|
73
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ChartKitWidgetData } from '../../../../types/widget-data';
|
|
3
|
+
import './styles.css';
|
|
4
|
+
type Props = {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
data: ChartKitWidgetData;
|
|
8
|
+
};
|
|
9
|
+
export declare const Chart: ({ width, height, data }: Props) => React.JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import block from 'bem-cn-lite';
|
|
3
|
+
import { AxisY } from './AxisY';
|
|
4
|
+
import { AxisX } from './AxisX';
|
|
5
|
+
import { Legend } from './Legend';
|
|
6
|
+
import { Title } from './Title';
|
|
7
|
+
import { Tooltip } from './Tooltip';
|
|
8
|
+
import { useChartDimensions, useChartEvents, useChartOptions, useLegend, useScales, useSeries, useShapes, useTooltip, } from '../hooks';
|
|
9
|
+
import { isAxisRelatedSeries } from '../utils';
|
|
10
|
+
import './styles.css';
|
|
11
|
+
const b = block('chartkit-d3');
|
|
12
|
+
export const Chart = ({ width, height, data }) => {
|
|
13
|
+
// FIXME: add data validation
|
|
14
|
+
const { series } = data;
|
|
15
|
+
const svgRef = React.createRef();
|
|
16
|
+
const hasAxisRelatedSeries = series.some(isAxisRelatedSeries);
|
|
17
|
+
const { chartHovered, handleMouseEnter, handleMouseLeave } = useChartEvents();
|
|
18
|
+
const { chart, legend, title, tooltip, xAxis, yAxis } = useChartOptions(data);
|
|
19
|
+
const { boundsWidth, boundsHeight, legendHeight } = useChartDimensions({
|
|
20
|
+
width,
|
|
21
|
+
height,
|
|
22
|
+
margin: chart.margin,
|
|
23
|
+
legend,
|
|
24
|
+
title,
|
|
25
|
+
xAxis,
|
|
26
|
+
yAxis,
|
|
27
|
+
});
|
|
28
|
+
const { activeLegendItems, handleLegendItemClick } = useLegend({ series });
|
|
29
|
+
const { chartSeries } = useSeries({ activeLegendItems, series });
|
|
30
|
+
const { xScale, yScale } = useScales({
|
|
31
|
+
boundsWidth,
|
|
32
|
+
boundsHeight,
|
|
33
|
+
series: chartSeries,
|
|
34
|
+
xAxis,
|
|
35
|
+
yAxis,
|
|
36
|
+
});
|
|
37
|
+
const { hovered, pointerPosition, handleSeriesMouseMove, handleSeriesMouseLeave } = useTooltip({
|
|
38
|
+
tooltip,
|
|
39
|
+
});
|
|
40
|
+
const { shapes } = useShapes({
|
|
41
|
+
series: chartSeries,
|
|
42
|
+
xAxis,
|
|
43
|
+
xScale,
|
|
44
|
+
yAxis,
|
|
45
|
+
yScale,
|
|
46
|
+
svgContainer: svgRef.current,
|
|
47
|
+
onSeriesMouseMove: handleSeriesMouseMove,
|
|
48
|
+
onSeriesMouseLeave: handleSeriesMouseLeave,
|
|
49
|
+
});
|
|
50
|
+
return (React.createElement(React.Fragment, null,
|
|
51
|
+
React.createElement("svg", { ref: svgRef, className: b({ hovered: chartHovered }), width: width, height: height, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave },
|
|
52
|
+
title && React.createElement(Title, Object.assign({}, title, { chartWidth: width })),
|
|
53
|
+
React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[
|
|
54
|
+
chart.margin.left,
|
|
55
|
+
chart.margin.top + ((title === null || title === void 0 ? void 0 : title.height) || 0),
|
|
56
|
+
].join(',')})` },
|
|
57
|
+
hasAxisRelatedSeries && (React.createElement(React.Fragment, null,
|
|
58
|
+
React.createElement(AxisY, { axises: yAxis, width: boundsWidth, height: boundsHeight, scale: yScale }),
|
|
59
|
+
React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
|
|
60
|
+
React.createElement(AxisX, { axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale })))),
|
|
61
|
+
shapes),
|
|
62
|
+
legend.enabled && (React.createElement(Legend, { width: boundsWidth, offsetWidth: chart.margin.left, height: legendHeight, offsetHeight: height - legendHeight / 2, chartSeries: chartSeries, onItemClick: handleLegendItemClick }))),
|
|
63
|
+
React.createElement(Tooltip, { hovered: hovered, pointerPosition: pointerPosition, tooltip: tooltip, xAxis: xAxis, yAxis: yAxis[0] })));
|
|
64
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ChartSeries, OnLegendItemClick } from '../hooks';
|
|
3
|
+
type Props = {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
offsetWidth: number;
|
|
7
|
+
offsetHeight: number;
|
|
8
|
+
chartSeries: ChartSeries[];
|
|
9
|
+
onItemClick: OnLegendItemClick;
|
|
10
|
+
};
|
|
11
|
+
export declare const Legend: (props: Props) => React.JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import block from 'bem-cn-lite';
|
|
3
|
+
import { select } from 'd3';
|
|
4
|
+
const b = block('chartkit-d3-legend');
|
|
5
|
+
export const Legend = (props) => {
|
|
6
|
+
const { width, offsetWidth, height, offsetHeight, chartSeries, onItemClick } = props;
|
|
7
|
+
return (React.createElement("g", { width: width, height: height, ref: (node) => {
|
|
8
|
+
if (!node) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const size = 10;
|
|
12
|
+
const textWidths = [0];
|
|
13
|
+
const svgElement = select(node);
|
|
14
|
+
svgElement.selectAll('*').remove();
|
|
15
|
+
const legendItemTemplate = svgElement
|
|
16
|
+
.selectAll('legend-history')
|
|
17
|
+
.data(chartSeries)
|
|
18
|
+
.enter()
|
|
19
|
+
.append('g')
|
|
20
|
+
.attr('class', b('item'))
|
|
21
|
+
.on('click', function (e, d) {
|
|
22
|
+
onItemClick({ name: d.name, metaKey: e.metaKey });
|
|
23
|
+
});
|
|
24
|
+
svgElement
|
|
25
|
+
.selectAll('*')
|
|
26
|
+
.data(chartSeries)
|
|
27
|
+
.append('text')
|
|
28
|
+
.text(function (d) {
|
|
29
|
+
return d.name;
|
|
30
|
+
})
|
|
31
|
+
.each(function () {
|
|
32
|
+
textWidths.push(this.getComputedTextLength());
|
|
33
|
+
})
|
|
34
|
+
.remove();
|
|
35
|
+
legendItemTemplate
|
|
36
|
+
.append('rect')
|
|
37
|
+
.attr('x', function (_d, i) {
|
|
38
|
+
return (offsetWidth +
|
|
39
|
+
i * size +
|
|
40
|
+
textWidths.slice(0, i + 1).reduce((acc, tw) => acc + tw, 0));
|
|
41
|
+
})
|
|
42
|
+
.attr('y', offsetHeight - size / 2)
|
|
43
|
+
.attr('width', size)
|
|
44
|
+
.attr('height', size)
|
|
45
|
+
.style('fill', function (d) {
|
|
46
|
+
return d.color;
|
|
47
|
+
});
|
|
48
|
+
legendItemTemplate
|
|
49
|
+
.append('text')
|
|
50
|
+
.attr('x', function (_d, i) {
|
|
51
|
+
return (offsetWidth +
|
|
52
|
+
i * size +
|
|
53
|
+
size +
|
|
54
|
+
textWidths.slice(0, i + 1).reduce((acc, tw) => acc + tw, 0));
|
|
55
|
+
})
|
|
56
|
+
.attr('y', offsetHeight)
|
|
57
|
+
.attr('class', function (d) {
|
|
58
|
+
const mods = { selected: d.visible, unselected: !d.visible };
|
|
59
|
+
return b('item-text', mods);
|
|
60
|
+
})
|
|
61
|
+
.text(function (d) {
|
|
62
|
+
return ('name' in d && d.name);
|
|
63
|
+
})
|
|
64
|
+
.style('alignment-baseline', 'middle');
|
|
65
|
+
} }));
|
|
66
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import block from 'bem-cn-lite';
|
|
3
|
+
const b = block('chartkit-d3-title');
|
|
4
|
+
export const Title = (props) => {
|
|
5
|
+
const { chartWidth, text, height, style } = props;
|
|
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` } },
|
|
7
|
+
React.createElement("tspan", null, text)));
|
|
8
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { TooltipHoveredData } from '../../../../../types/widget-data';
|
|
3
|
+
import type { PreparedAxis } from '../../hooks';
|
|
4
|
+
type Props = {
|
|
5
|
+
hovered: TooltipHoveredData;
|
|
6
|
+
xAxis: PreparedAxis;
|
|
7
|
+
yAxis: PreparedAxis;
|
|
8
|
+
};
|
|
9
|
+
export declare const DefaultContent: ({ hovered, xAxis, yAxis }: Props) => React.JSX.Element | null;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export const DefaultContent = ({ hovered, xAxis, yAxis }) => {
|
|
3
|
+
const { data, series } = hovered;
|
|
4
|
+
switch (series.type) {
|
|
5
|
+
case 'scatter': {
|
|
6
|
+
const scatterData = data;
|
|
7
|
+
const xRow = xAxis.type === 'category' ? scatterData.category : scatterData.x;
|
|
8
|
+
const yRow = yAxis.type === 'category' ? scatterData.category : scatterData.y;
|
|
9
|
+
return (React.createElement("div", null,
|
|
10
|
+
React.createElement("div", null,
|
|
11
|
+
React.createElement("span", null, "X:\u00A0"),
|
|
12
|
+
React.createElement("b", null, xRow)),
|
|
13
|
+
React.createElement("div", null,
|
|
14
|
+
React.createElement("span", null, "Y:\u00A0"),
|
|
15
|
+
React.createElement("b", null, yRow))));
|
|
16
|
+
}
|
|
17
|
+
default: {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { TooltipHoveredData } from '../../../../../types/widget-data';
|
|
3
|
+
import type { PointerPosition, PreparedAxis, PreparedTooltip } from '../../hooks';
|
|
4
|
+
type TooltipProps = {
|
|
5
|
+
tooltip: PreparedTooltip;
|
|
6
|
+
xAxis: PreparedAxis;
|
|
7
|
+
yAxis: PreparedAxis;
|
|
8
|
+
hovered?: TooltipHoveredData;
|
|
9
|
+
pointerPosition?: PointerPosition;
|
|
10
|
+
};
|
|
11
|
+
export declare const Tooltip: (props: TooltipProps) => React.JSX.Element | null;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import block from 'bem-cn-lite';
|
|
3
|
+
import { DefaultContent } from './DefaultContent';
|
|
4
|
+
const b = block('chartkit-d3-tooltip');
|
|
5
|
+
const POINTER_OFFSET_X = 20;
|
|
6
|
+
export const Tooltip = (props) => {
|
|
7
|
+
const { hovered, pointerPosition, tooltip, xAxis, yAxis } = props;
|
|
8
|
+
const ref = React.useRef(null);
|
|
9
|
+
const size = React.useMemo(() => {
|
|
10
|
+
if (ref.current && hovered) {
|
|
11
|
+
const { width, height } = ref.current.getBoundingClientRect();
|
|
12
|
+
return { width, height };
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}, [hovered, pointerPosition]);
|
|
16
|
+
const position = React.useMemo(() => {
|
|
17
|
+
if (hovered && pointerPosition && size) {
|
|
18
|
+
const { clientWidth } = document.documentElement;
|
|
19
|
+
const { width, height } = size;
|
|
20
|
+
const [pointerLeft, pointetTop] = pointerPosition;
|
|
21
|
+
const outOfRightBoudary = pointerLeft + width + POINTER_OFFSET_X >= clientWidth;
|
|
22
|
+
const outOfTopBoundary = pointetTop - height / 2 <= 0;
|
|
23
|
+
const left = outOfRightBoudary
|
|
24
|
+
? pointerLeft - width - POINTER_OFFSET_X
|
|
25
|
+
: pointerLeft + POINTER_OFFSET_X;
|
|
26
|
+
const top = outOfTopBoundary ? 0 : pointetTop - height / 2;
|
|
27
|
+
return { left, top };
|
|
28
|
+
}
|
|
29
|
+
else if (hovered && pointerPosition) {
|
|
30
|
+
return { left: -1000, top: -1000 };
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}, [hovered, pointerPosition, size]);
|
|
34
|
+
const content = React.useMemo(() => {
|
|
35
|
+
if (tooltip.renderer && hovered) {
|
|
36
|
+
return tooltip.renderer({ hovered });
|
|
37
|
+
}
|
|
38
|
+
else if (hovered) {
|
|
39
|
+
return React.createElement(DefaultContent, { hovered: hovered, xAxis: xAxis, yAxis: yAxis });
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}, [hovered, tooltip, xAxis, yAxis]);
|
|
43
|
+
if (!position || !hovered) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const { left, top } = position;
|
|
47
|
+
const style = {
|
|
48
|
+
position: 'absolute',
|
|
49
|
+
top,
|
|
50
|
+
left: left,
|
|
51
|
+
};
|
|
52
|
+
return (React.createElement("div", { ref: ref, className: b(), style: style }, content));
|
|
53
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Chart } from './Chart';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Chart } from './Chart';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
.chartkit-d3-axis .domain {
|
|
2
|
+
stroke: var(--g-color-line-generic-active);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.chartkit-d3-axis .tick text {
|
|
6
|
+
color: var(--g-color-text-secondary);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.chartkit-d3-axis .tick line {
|
|
10
|
+
stroke: var(--g-color-line-generic);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.chartkit-d3-axis__title {
|
|
14
|
+
fill: var(--g-color-text-secondary);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.chartkit-d3-legend__item {
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
user-select: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.chartkit-d3-legend__item-text {
|
|
23
|
+
fill: var(--g-color-text-secondary);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.chartkit-d3-legend__item-text_unselected {
|
|
27
|
+
fill: var(--g-color-text-hint);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.chartkit-d3-legend__item-text:hover {
|
|
31
|
+
fill: var(--g-color-text-complementary);
|
|
32
|
+
}
|
|
33
|
+
|
|
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
|
+
.chartkit-d3-title {
|
|
48
|
+
font-size: var(--g-text-subheader-2-font-size);
|
|
49
|
+
font-weight: var(--g-text-subheader-font-weight);
|
|
50
|
+
fill: var(--g-color-text-primary);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.chartkit-d3-tooltip {
|
|
54
|
+
position: absolute;
|
|
55
|
+
padding: 10px 14px;
|
|
56
|
+
background-color: var(--g-color-infographics-tooltip-bg);
|
|
57
|
+
border: 1px solid var(--g-color-line-generic);
|
|
58
|
+
border-radius: 3px;
|
|
59
|
+
box-shadow: 0 2px 12px var(--g-color-sfx-shadow);
|
|
60
|
+
z-index: 100001;
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const DEFAULT_PALETTE: string[];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const DEFAULT_PALETTE = [
|
|
2
|
+
'#4DA2F1',
|
|
3
|
+
'#FF3D64',
|
|
4
|
+
'#8AD554',
|
|
5
|
+
'#FFC636',
|
|
6
|
+
'#FFB9DD',
|
|
7
|
+
'#84D1EE',
|
|
8
|
+
'#FF91A1',
|
|
9
|
+
'#54A520',
|
|
10
|
+
'#DB9100',
|
|
11
|
+
'#BA74B3',
|
|
12
|
+
'#1F68A9',
|
|
13
|
+
'#ED65A9',
|
|
14
|
+
'#0FA08D',
|
|
15
|
+
'#FF7E00',
|
|
16
|
+
'#E8B0A4',
|
|
17
|
+
'#52A6C5',
|
|
18
|
+
'#BE2443',
|
|
19
|
+
'#70C1AF',
|
|
20
|
+
'#FFB46C',
|
|
21
|
+
'#DCA3D7',
|
|
22
|
+
];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './useChartDimensions';
|
|
2
|
+
export * from './useChartEvents';
|
|
3
|
+
export * from './useChartOptions';
|
|
4
|
+
export * from './useChartOptions/types';
|
|
5
|
+
export * from './useLegend';
|
|
6
|
+
export * from './useScales';
|
|
7
|
+
export * from './useSeries';
|
|
8
|
+
export * from './useShapes';
|
|
9
|
+
export * from './useTooltip';
|
|
10
|
+
export * from './useTooltip/types';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './useChartDimensions';
|
|
2
|
+
export * from './useChartEvents';
|
|
3
|
+
export * from './useChartOptions';
|
|
4
|
+
export * from './useChartOptions/types';
|
|
5
|
+
export * from './useLegend';
|
|
6
|
+
export * from './useScales';
|
|
7
|
+
export * from './useSeries';
|
|
8
|
+
export * from './useShapes';
|
|
9
|
+
export * from './useTooltip';
|
|
10
|
+
export * from './useTooltip/types';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ChartMargin } from '../../../../../types/widget-data';
|
|
2
|
+
import type { ChartOptions, PreparedAxis, PreparedTitle } from '../useChartOptions/types';
|
|
3
|
+
type Args = {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
margin: ChartMargin;
|
|
7
|
+
legend: ChartOptions['legend'];
|
|
8
|
+
title?: PreparedTitle;
|
|
9
|
+
xAxis?: PreparedAxis;
|
|
10
|
+
yAxis?: PreparedAxis[];
|
|
11
|
+
};
|
|
12
|
+
export declare const useChartDimensions: (args: Args) => {
|
|
13
|
+
boundsWidth: number;
|
|
14
|
+
boundsHeight: number;
|
|
15
|
+
legendHeight: number;
|
|
16
|
+
};
|
|
17
|
+
export {};
|