@gravity-ui/chartkit 3.1.2 → 3.2.0-beta.1

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.
Files changed (104) hide show
  1. package/build/plugins/d3/__stories__/bar/category.stories.d.ts +4 -0
  2. package/build/plugins/d3/__stories__/bar/category.stories.js +75 -0
  3. package/build/plugins/d3/__stories__/bar/datetime.stories.d.ts +4 -0
  4. package/build/plugins/d3/__stories__/bar/datetime.stories.js +71 -0
  5. package/build/plugins/d3/__stories__/bar/linear.stories.d.ts +4 -0
  6. package/build/plugins/d3/__stories__/bar/linear.stories.js +74 -0
  7. package/build/plugins/d3/__stories__/penguins.json +3098 -0
  8. package/build/plugins/d3/__stories__/scatter/LinearCategories.stories.d.ts +4 -0
  9. package/build/plugins/d3/__stories__/scatter/LinearCategories.stories.js +103 -0
  10. package/build/plugins/d3/__stories__/scatter/Timestamp.stories.d.ts +4 -0
  11. package/build/plugins/d3/__stories__/scatter/Timestamp.stories.js +91 -0
  12. package/build/plugins/d3/index.d.ts +7 -0
  13. package/build/plugins/d3/index.js +10 -0
  14. package/build/plugins/d3/renderer/D3Widget.d.ts +15 -0
  15. package/build/plugins/d3/renderer/D3Widget.js +40 -0
  16. package/build/plugins/d3/renderer/components/AxisX.d.ts +10 -0
  17. package/build/plugins/d3/renderer/components/AxisX.js +68 -0
  18. package/build/plugins/d3/renderer/components/AxisY.d.ts +10 -0
  19. package/build/plugins/d3/renderer/components/AxisY.js +73 -0
  20. package/build/plugins/d3/renderer/components/Chart.d.ts +10 -0
  21. package/build/plugins/d3/renderer/components/Chart.js +64 -0
  22. package/build/plugins/d3/renderer/components/Legend.d.ts +12 -0
  23. package/build/plugins/d3/renderer/components/Legend.js +66 -0
  24. package/build/plugins/d3/renderer/components/Title.d.ts +7 -0
  25. package/build/plugins/d3/renderer/components/Title.js +8 -0
  26. package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.d.ts +10 -0
  27. package/build/plugins/d3/renderer/components/Tooltip/DefaultContent.js +21 -0
  28. package/build/plugins/d3/renderer/components/Tooltip/index.d.ts +12 -0
  29. package/build/plugins/d3/renderer/components/Tooltip/index.js +53 -0
  30. package/build/plugins/d3/renderer/components/index.d.ts +1 -0
  31. package/build/plugins/d3/renderer/components/index.js +1 -0
  32. package/build/plugins/d3/renderer/components/styles.css +61 -0
  33. package/build/plugins/d3/renderer/constants.d.ts +1 -0
  34. package/build/plugins/d3/renderer/constants.js +22 -0
  35. package/build/plugins/d3/renderer/hooks/index.d.ts +10 -0
  36. package/build/plugins/d3/renderer/hooks/index.js +10 -0
  37. package/build/plugins/d3/renderer/hooks/useChartDimensions/index.d.ts +17 -0
  38. package/build/plugins/d3/renderer/hooks/useChartDimensions/index.js +13 -0
  39. package/build/plugins/d3/renderer/hooks/useChartEvents/index.d.ts +5 -0
  40. package/build/plugins/d3/renderer/hooks/useChartEvents/index.js +15 -0
  41. package/build/plugins/d3/renderer/hooks/useChartOptions/chart.d.ts +8 -0
  42. package/build/plugins/d3/renderer/hooks/useChartOptions/chart.js +60 -0
  43. package/build/plugins/d3/renderer/hooks/useChartOptions/constants.d.ts +3 -0
  44. package/build/plugins/d3/renderer/hooks/useChartOptions/constants.js +3 -0
  45. package/build/plugins/d3/renderer/hooks/useChartOptions/index.d.ts +3 -0
  46. package/build/plugins/d3/renderer/hooks/useChartOptions/index.js +32 -0
  47. package/build/plugins/d3/renderer/hooks/useChartOptions/legend.d.ts +6 -0
  48. package/build/plugins/d3/renderer/hooks/useChartOptions/legend.js +7 -0
  49. package/build/plugins/d3/renderer/hooks/useChartOptions/title.d.ts +5 -0
  50. package/build/plugins/d3/renderer/hooks/useChartOptions/title.js +17 -0
  51. package/build/plugins/d3/renderer/hooks/useChartOptions/tooltip.d.ts +5 -0
  52. package/build/plugins/d3/renderer/hooks/useChartOptions/tooltip.js +5 -0
  53. package/build/plugins/d3/renderer/hooks/useChartOptions/types.d.ts +33 -0
  54. package/build/plugins/d3/renderer/hooks/useChartOptions/types.js +1 -0
  55. package/build/plugins/d3/renderer/hooks/useChartOptions/utils.d.ts +5 -0
  56. package/build/plugins/d3/renderer/hooks/useChartOptions/utils.js +18 -0
  57. package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.d.ts +5 -0
  58. package/build/plugins/d3/renderer/hooks/useChartOptions/x-axis.js +29 -0
  59. package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.d.ts +5 -0
  60. package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +35 -0
  61. package/build/plugins/d3/renderer/hooks/useLegend/index.d.ts +13 -0
  62. package/build/plugins/d3/renderer/hooks/useLegend/index.js +28 -0
  63. package/build/plugins/d3/renderer/hooks/useScales/index.d.ts +17 -0
  64. package/build/plugins/d3/renderer/hooks/useScales/index.js +106 -0
  65. package/build/plugins/d3/renderer/hooks/useSeries/index.d.ts +14 -0
  66. package/build/plugins/d3/renderer/hooks/useSeries/index.js +23 -0
  67. package/build/plugins/d3/renderer/hooks/useShapes/bar.d.ts +16 -0
  68. package/build/plugins/d3/renderer/hooks/useShapes/bar.js +75 -0
  69. package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +19 -0
  70. package/build/plugins/d3/renderer/hooks/useShapes/index.js +44 -0
  71. package/build/plugins/d3/renderer/hooks/useShapes/scatter.d.ts +18 -0
  72. package/build/plugins/d3/renderer/hooks/useShapes/scatter.js +62 -0
  73. package/build/plugins/d3/renderer/hooks/useTooltip/index.d.ts +13 -0
  74. package/build/plugins/d3/renderer/hooks/useTooltip/index.js +19 -0
  75. package/build/plugins/d3/renderer/hooks/useTooltip/types.d.ts +7 -0
  76. package/build/plugins/d3/renderer/hooks/useTooltip/types.js +1 -0
  77. package/build/plugins/d3/renderer/utils/index.d.ts +18 -0
  78. package/build/plugins/d3/renderer/utils/index.js +71 -0
  79. package/build/plugins/d3/types.d.ts +4 -0
  80. package/build/plugins/d3/types.js +1 -0
  81. package/build/types/widget-data/axis.d.ts +24 -0
  82. package/build/types/widget-data/axis.js +1 -0
  83. package/build/types/widget-data/bar.d.ts +31 -0
  84. package/build/types/widget-data/bar.js +1 -0
  85. package/build/types/widget-data/base.d.ts +15 -0
  86. package/build/types/widget-data/base.js +1 -0
  87. package/build/types/widget-data/chart.d.ts +9 -0
  88. package/build/types/widget-data/chart.js +1 -0
  89. package/build/types/widget-data/index.d.ts +28 -0
  90. package/build/types/widget-data/index.js +10 -0
  91. package/build/types/widget-data/legend.d.ts +3 -0
  92. package/build/types/widget-data/legend.js +1 -0
  93. package/build/types/widget-data/pie.d.ts +10 -0
  94. package/build/types/widget-data/pie.js +1 -0
  95. package/build/types/widget-data/scatter.d.ts +20 -0
  96. package/build/types/widget-data/scatter.js +1 -0
  97. package/build/types/widget-data/series.d.ts +18 -0
  98. package/build/types/widget-data/series.js +1 -0
  99. package/build/types/widget-data/title.d.ts +5 -0
  100. package/build/types/widget-data/title.js +1 -0
  101. package/build/types/widget-data/tooltip.d.ts +12 -0
  102. package/build/types/widget-data/tooltip.js +1 -0
  103. package/build/types/widget.d.ts +5 -0
  104. package/package.json +7 -2
@@ -0,0 +1,4 @@
1
+ import { Meta } from '@storybook/react';
2
+ export declare const LinearAndCategories: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, import("@storybook/types").Args>;
3
+ declare const meta: Meta;
4
+ export default meta;
@@ -0,0 +1,103 @@
1
+ import React from 'react';
2
+ import random from 'lodash/random';
3
+ import { withKnobs, select, radios, text } from '@storybook/addon-knobs';
4
+ import { Button } from '@gravity-ui/uikit';
5
+ import { settings } from '../../../../libs';
6
+ import { ChartKit } from '../../../../components/ChartKit';
7
+ import { D3Plugin } from '../..';
8
+ import penguins from '../penguins.json';
9
+ const shapeScatterSeriesData = (args) => {
10
+ const { data, groupBy, map } = args;
11
+ return data.reduce((acc, d) => {
12
+ const seriesName = d[groupBy];
13
+ if (!seriesName) {
14
+ return acc;
15
+ }
16
+ if (!acc[seriesName]) {
17
+ acc[seriesName] = [];
18
+ }
19
+ acc[seriesName].push(Object.assign({ x: d[map.x], y: d[map.y], radius: random(3, 6) }, (map.category && { category: d[map.category] })));
20
+ return acc;
21
+ }, {});
22
+ };
23
+ const shapeScatterSeries = (data) => {
24
+ return Object.keys(data).reduce((acc, name) => {
25
+ acc.push({
26
+ type: 'scatter',
27
+ data: data[name],
28
+ name,
29
+ });
30
+ return acc;
31
+ }, []);
32
+ };
33
+ const shapeScatterChartData = (series, categoriesType, categories) => {
34
+ let xAxis = {
35
+ labels: {
36
+ enabled: false,
37
+ },
38
+ title: {
39
+ text: text('X axis title', ''),
40
+ },
41
+ };
42
+ let yAxis = {
43
+ title: {
44
+ text: text('Y axis title', ''),
45
+ },
46
+ };
47
+ if (categories && categoriesType === 'x') {
48
+ xAxis = Object.assign(Object.assign({}, xAxis), { type: 'category', categories });
49
+ }
50
+ if (categories && categoriesType === 'y') {
51
+ yAxis = Object.assign(Object.assign({}, yAxis), { type: 'category', categories });
52
+ }
53
+ return {
54
+ series: {
55
+ data: series,
56
+ },
57
+ xAxis,
58
+ yAxis: [yAxis],
59
+ title: {
60
+ text: text('title', 'Chart title'),
61
+ },
62
+ };
63
+ };
64
+ const Template = () => {
65
+ const [shown, setShown] = React.useState(false);
66
+ const chartkitRef = React.useRef();
67
+ const x = select('x', ['culmen_length_mm', 'culmen_depth_mm', 'flipper_length_mm', 'body_mass_g'], 'culmen_length_mm');
68
+ const y = select('y', ['culmen_length_mm', 'culmen_depth_mm', 'flipper_length_mm', 'body_mass_g'], 'culmen_depth_mm');
69
+ const groupBy = select('groupBy', ['species', 'island', 'sex'], 'species');
70
+ const categoriesType = radios('categoriesType', { none: 'none', x: 'x', y: 'y' }, 'none');
71
+ const category = categoriesType === 'none'
72
+ ? undefined
73
+ : select('category', ['species', 'island', 'sex'], 'island');
74
+ let categories;
75
+ if (categoriesType !== 'none' && category) {
76
+ categories = penguins.reduce((acc, p) => {
77
+ const cerrentCategory = p[category];
78
+ if (typeof cerrentCategory === 'string' && !acc.includes(cerrentCategory)) {
79
+ acc.push(cerrentCategory);
80
+ }
81
+ return acc;
82
+ }, []);
83
+ }
84
+ const shapedScatterSeriesData = shapeScatterSeriesData({
85
+ data: penguins,
86
+ groupBy,
87
+ map: { x, y, category },
88
+ });
89
+ const shapedScatterSeries = shapeScatterSeries(shapedScatterSeriesData);
90
+ const data = shapeScatterChartData(shapedScatterSeries, categoriesType, categories);
91
+ if (!shown) {
92
+ settings.set({ plugins: [D3Plugin] });
93
+ return React.createElement(Button, { onClick: () => setShown(true) }, "Show chart");
94
+ }
95
+ return (React.createElement("div", { style: { height: '80vh', width: '100%' } },
96
+ React.createElement(ChartKit, { ref: chartkitRef, type: "d3", data: data })));
97
+ };
98
+ export const LinearAndCategories = Template.bind({});
99
+ const meta = {
100
+ title: 'Plugins/D3/Scatter',
101
+ decorators: [withKnobs],
102
+ };
103
+ export default meta;
@@ -0,0 +1,4 @@
1
+ import { Meta } from '@storybook/react';
2
+ export declare const Timestamp: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, import("@storybook/types").Args>;
3
+ declare const meta: Meta;
4
+ export default meta;
@@ -0,0 +1,91 @@
1
+ import React from 'react';
2
+ import random from 'lodash/random';
3
+ import { dateTime } from '@gravity-ui/date-utils';
4
+ import { Button } from '@gravity-ui/uikit';
5
+ import { settings } from '../../../../libs';
6
+ import { ChartKit } from '../../../../components/ChartKit';
7
+ import { D3Plugin } from '../..';
8
+ const rowData = [
9
+ {
10
+ x: 1690686000000,
11
+ y: 86.71905594602345,
12
+ custom: 'green',
13
+ },
14
+ {
15
+ x: 1690426800000,
16
+ y: 86.73089353359981,
17
+ custom: 'yellow',
18
+ },
19
+ {
20
+ x: 1690254000000,
21
+ y: 86.53675705168267,
22
+ custom: 'red',
23
+ },
24
+ {
25
+ x: 1690772400000,
26
+ y: 86.47880981408552,
27
+ custom: 'blue',
28
+ },
29
+ {
30
+ x: 1690340400000,
31
+ y: 86.4108836764148,
32
+ custom: 'gray',
33
+ },
34
+ {
35
+ x: 1690599600000,
36
+ y: 86.73440096266042,
37
+ custom: 'pink',
38
+ },
39
+ {
40
+ x: 1690513200000,
41
+ y: 86.64935929597681,
42
+ custom: 'purple',
43
+ },
44
+ ];
45
+ const shapeData = (data) => {
46
+ const scatterData = data.map((d) => ({
47
+ x: d.x,
48
+ y: d.y,
49
+ radius: random(3, 6),
50
+ custom: d.custom,
51
+ }));
52
+ const scatterSeries = {
53
+ type: 'scatter',
54
+ data: scatterData,
55
+ name: 'some-name',
56
+ };
57
+ return {
58
+ series: {
59
+ data: [scatterSeries],
60
+ },
61
+ xAxis: {
62
+ type: 'datetime',
63
+ timestamps: data.map((d) => d.x),
64
+ },
65
+ tooltip: {
66
+ renderer: ({ hovered }) => {
67
+ const d = hovered.data;
68
+ return React.createElement("div", { style: { color: d.custom } }, dateTime({ input: d.x }).format('LL'));
69
+ },
70
+ },
71
+ title: {
72
+ text: 'Chart title',
73
+ },
74
+ };
75
+ };
76
+ const Template = () => {
77
+ const [shown, setShown] = React.useState(false);
78
+ const chartkitRef = React.useRef();
79
+ const data = shapeData(rowData);
80
+ if (!shown) {
81
+ settings.set({ plugins: [D3Plugin] });
82
+ return React.createElement(Button, { onClick: () => setShown(true) }, "Show chart");
83
+ }
84
+ return (React.createElement("div", { style: { height: '300px', width: '100%' } },
85
+ React.createElement(ChartKit, { ref: chartkitRef, type: "d3", data: data })));
86
+ };
87
+ export const Timestamp = Template.bind({});
88
+ const meta = {
89
+ title: 'Plugins/D3/Scatter',
90
+ };
91
+ export default meta;
@@ -0,0 +1,7 @@
1
+ import { ChartKitPlugin } from '../../types';
2
+ /**
3
+ * It is an experemental plugin
4
+ *
5
+ * DO NOT USE IT IN YOUR PRODUCTION
6
+ * */
7
+ export declare const D3Plugin: ChartKitPlugin;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ /**
3
+ * It is an experemental plugin
4
+ *
5
+ * DO NOT USE IT IN YOUR PRODUCTION
6
+ * */
7
+ export const D3Plugin = {
8
+ type: 'd3',
9
+ renderer: React.lazy(() => import('./renderer/D3Widget')),
10
+ };
@@ -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.data.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: series.data });
29
+ const { chartSeries } = useSeries({ activeLegendItems, series: series.data });
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,7 @@
1
+ import React from 'react';
2
+ import type { PreparedTitle } from '../hooks';
3
+ type Props = PreparedTitle & {
4
+ chartWidth: number;
5
+ };
6
+ export declare const Title: (props: Props) => React.JSX.Element;
7
+ export {};
@@ -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
+ };