@gravity-ui/chartkit 5.14.1 → 5.16.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/Chart.js +5 -0
- package/build/plugins/d3/renderer/components/Legend.js +129 -66
- package/build/plugins/d3/renderer/components/styles.css +16 -0
- package/build/plugins/d3/renderer/constants/defaults/legend.d.ts +12 -4
- package/build/plugins/d3/renderer/constants/defaults/legend.js +4 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-area.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-x.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-bar-y.js +8 -6
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-legend.js +58 -11
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-line.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-pie.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-treemap.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/prepare-waterfall.js +1 -0
- package/build/plugins/d3/renderer/hooks/useSeries/types.d.ts +24 -1
- package/build/plugins/d3/renderer/hooks/useShapes/HtmlLayer.d.ts +8 -0
- package/build/plugins/d3/renderer/hooks/useShapes/HtmlLayer.js +22 -0
- package/build/plugins/d3/renderer/hooks/useShapes/area/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/area/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.js +18 -3
- package/build/plugins/d3/renderer/hooks/useShapes/area/types.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +21 -4
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/types.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/index.js +18 -23
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +44 -3
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/types.d.ts +3 -0
- package/build/plugins/d3/renderer/hooks/useShapes/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/index.js +9 -9
- package/build/plugins/d3/renderer/hooks/useShapes/line/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/line/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/line/prepare-data.js +17 -1
- package/build/plugins/d3/renderer/hooks/useShapes/line/types.d.ts +2 -1
- package/build/plugins/d3/renderer/hooks/useShapes/pie/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/pie/index.js +10 -14
- package/build/plugins/d3/renderer/hooks/useShapes/pie/prepare-data.js +30 -12
- package/build/plugins/d3/renderer/hooks/useShapes/pie/types.d.ts +7 -5
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/prepare-data.js +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/scatter/types.d.ts +2 -0
- package/build/plugins/d3/renderer/hooks/useShapes/treemap/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/treemap/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/treemap/prepare-data.js +1 -1
- package/build/plugins/d3/renderer/hooks/useShapes/treemap/types.d.ts +2 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/index.d.ts +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/index.js +5 -2
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/prepare-data.js +1 -0
- package/build/plugins/d3/renderer/hooks/useShapes/waterfall/types.d.ts +2 -1
- package/build/plugins/d3/renderer/types/index.d.ts +8 -0
- package/build/plugins/d3/renderer/utils/axis-generators/bottom.d.ts +5 -4
- package/build/plugins/d3/renderer/utils/axis-generators/bottom.js +11 -7
- package/build/plugins/d3/renderer/utils/axis.d.ts +1 -1
- package/build/plugins/d3/renderer/utils/axis.js +1 -1
- package/build/plugins/d3/renderer/utils/color.d.ts +10 -0
- package/build/plugins/d3/renderer/utils/color.js +43 -0
- package/build/plugins/d3/renderer/utils/index.d.ts +2 -0
- package/build/plugins/d3/renderer/utils/index.js +2 -0
- package/build/plugins/d3/renderer/utils/legend.d.ts +8 -0
- package/build/plugins/d3/renderer/utils/legend.js +23 -0
- package/build/plugins/d3/renderer/utils/text.d.ts +2 -1
- package/build/plugins/d3/renderer/utils/text.js +32 -10
- package/build/types/widget-data/bar-x.d.ts +1 -1
- package/build/types/widget-data/base.d.ts +7 -0
- package/build/types/widget-data/legend.d.ts +24 -0
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { arc, group } from 'd3';
|
|
1
|
+
import { arc, group, line as lineGenerator } from 'd3';
|
|
2
2
|
import { calculateNumericProperty, getLabelsSize, getLeftPosition, isLabelsOverlapping, } from '../../../utils';
|
|
3
|
-
import { pieGenerator } from './utils';
|
|
3
|
+
import { getCurveFactory, pieGenerator } from './utils';
|
|
4
4
|
const FULL_CIRCLE = Math.PI * 2;
|
|
5
5
|
const getCenter = (boundsWidth, boundsHeight, center) => {
|
|
6
6
|
var _a, _b;
|
|
@@ -30,6 +30,7 @@ export function preparePieData(args) {
|
|
|
30
30
|
radius,
|
|
31
31
|
segments: [],
|
|
32
32
|
labels: [],
|
|
33
|
+
connectors: [],
|
|
33
34
|
borderColor,
|
|
34
35
|
borderWidth,
|
|
35
36
|
borderRadius,
|
|
@@ -40,6 +41,7 @@ export function preparePieData(args) {
|
|
|
40
41
|
opacity: series.states.hover.halo.opacity,
|
|
41
42
|
size: series.states.hover.halo.size,
|
|
42
43
|
},
|
|
44
|
+
htmlElements: [],
|
|
43
45
|
};
|
|
44
46
|
const segments = items.map((item) => {
|
|
45
47
|
return {
|
|
@@ -53,6 +55,11 @@ export function preparePieData(args) {
|
|
|
53
55
|
};
|
|
54
56
|
});
|
|
55
57
|
data.segments = pieGenerator(segments);
|
|
58
|
+
let line = lineGenerator();
|
|
59
|
+
const curveFactory = getCurveFactory(data);
|
|
60
|
+
if (curveFactory) {
|
|
61
|
+
line = line.curve(curveFactory);
|
|
62
|
+
}
|
|
56
63
|
if (dataLabels.enabled) {
|
|
57
64
|
const { style, connectorPadding, distance } = dataLabels;
|
|
58
65
|
const { maxHeight: labelHeight } = getLabelsSize({ labels: ['Some Label'], style });
|
|
@@ -81,15 +88,17 @@ export function preparePieData(args) {
|
|
|
81
88
|
items.forEach((d, index) => {
|
|
82
89
|
const prevLabel = labels[labels.length - 1];
|
|
83
90
|
const text = String(d.data.label || d.data.value);
|
|
84
|
-
const
|
|
91
|
+
const shouldUseHtml = dataLabels.html;
|
|
92
|
+
const labelSize = getLabelsSize({ labels: [text], style, html: shouldUseHtml });
|
|
85
93
|
const labelWidth = labelSize.maxWidth;
|
|
86
94
|
const relatedSegment = data.segments[index];
|
|
87
95
|
const getLabelPosition = (angle) => {
|
|
88
96
|
let [x, y] = labelArcGenerator.centroid(Object.assign(Object.assign({}, relatedSegment), { startAngle: angle, endAngle: angle }));
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
|
|
97
|
+
y = y < 0 ? y - labelHeight : y;
|
|
98
|
+
if (shouldUseHtml) {
|
|
99
|
+
x = x < 0 ? x - labelWidth : x;
|
|
92
100
|
}
|
|
101
|
+
x = Math.max(-boundsWidth / 2, x);
|
|
93
102
|
return [x, y];
|
|
94
103
|
};
|
|
95
104
|
const getConnectorPoints = (angle) => {
|
|
@@ -114,10 +123,6 @@ export function preparePieData(args) {
|
|
|
114
123
|
textAnchor: midAngle < Math.PI ? 'start' : 'end',
|
|
115
124
|
series: { id: d.id },
|
|
116
125
|
active: true,
|
|
117
|
-
connector: {
|
|
118
|
-
points: getConnectorPoints(midAngle),
|
|
119
|
-
color: relatedSegment.data.color,
|
|
120
|
-
},
|
|
121
126
|
segment: relatedSegment.data,
|
|
122
127
|
angle: midAngle,
|
|
123
128
|
};
|
|
@@ -138,7 +143,6 @@ export function preparePieData(args) {
|
|
|
138
143
|
const [newX, newY] = getLabelPosition(newAngle);
|
|
139
144
|
label.x = newX;
|
|
140
145
|
label.y = newY;
|
|
141
|
-
label.connector.points = getConnectorPoints(newAngle);
|
|
142
146
|
if (!isLabelsOverlapping(prevLabel, label, dataLabels.padding)) {
|
|
143
147
|
shouldAdjustAngle = false;
|
|
144
148
|
overlap = false;
|
|
@@ -158,7 +162,21 @@ export function preparePieData(args) {
|
|
|
158
162
|
label.maxWidth = label.size.width - (right - boundsWidth / 2);
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
|
-
|
|
165
|
+
if (shouldUseHtml) {
|
|
166
|
+
data.htmlElements.push({
|
|
167
|
+
x: boundsWidth / 2 + label.x,
|
|
168
|
+
y: boundsHeight / 2 + label.y,
|
|
169
|
+
content: label.text,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
labels.push(label);
|
|
174
|
+
}
|
|
175
|
+
const connector = {
|
|
176
|
+
path: line(getConnectorPoints(midAngle)),
|
|
177
|
+
color: relatedSegment.data.color,
|
|
178
|
+
};
|
|
179
|
+
data.connectors.push(connector);
|
|
162
180
|
}
|
|
163
181
|
});
|
|
164
182
|
data.labels = labels;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PieArcDatum } from 'd3';
|
|
2
2
|
import { ConnectorCurve } from '../../../../../../types';
|
|
3
|
-
import { LabelData } from '../../../types';
|
|
3
|
+
import { HtmlItem, LabelData } from '../../../types';
|
|
4
4
|
import { PreparedPieSeries } from '../../useSeries/types';
|
|
5
5
|
export type SegmentData = {
|
|
6
6
|
value: number;
|
|
@@ -12,18 +12,19 @@ export type SegmentData = {
|
|
|
12
12
|
pie: PreparedPieData;
|
|
13
13
|
};
|
|
14
14
|
export type PieLabelData = LabelData & {
|
|
15
|
-
connector: {
|
|
16
|
-
points: [number, number][];
|
|
17
|
-
color: string;
|
|
18
|
-
};
|
|
19
15
|
segment: SegmentData;
|
|
20
16
|
angle: number;
|
|
21
17
|
maxWidth: number;
|
|
22
18
|
};
|
|
19
|
+
export type PieConnectorData = {
|
|
20
|
+
path: string | null;
|
|
21
|
+
color: string;
|
|
22
|
+
};
|
|
23
23
|
export type PreparedPieData = {
|
|
24
24
|
id: string;
|
|
25
25
|
segments: PieArcDatum<SegmentData>[];
|
|
26
26
|
labels: PieLabelData[];
|
|
27
|
+
connectors: PieConnectorData[];
|
|
27
28
|
center: [number, number];
|
|
28
29
|
radius: number;
|
|
29
30
|
innerRadius: number;
|
|
@@ -37,4 +38,5 @@ export type PreparedPieData = {
|
|
|
37
38
|
opacity: number;
|
|
38
39
|
size: number;
|
|
39
40
|
};
|
|
41
|
+
htmlElements: HtmlItem[];
|
|
40
42
|
};
|
|
@@ -7,5 +7,6 @@ type ScatterSeriesShapeProps = {
|
|
|
7
7
|
dispatcher: Dispatch<object>;
|
|
8
8
|
preparedData: PreparedScatterData[];
|
|
9
9
|
seriesOptions: PreparedSeriesOptions;
|
|
10
|
+
htmlLayout: HTMLElement | null;
|
|
10
11
|
};
|
|
11
12
|
export declare function ScatterSeriesShape(props: ScatterSeriesShapeProps): React.JSX.Element;
|
|
@@ -2,12 +2,13 @@ import React from 'react';
|
|
|
2
2
|
import { select } from 'd3';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { block } from '../../../../../../utils/cn';
|
|
5
|
+
import { HtmlLayer } from '../HtmlLayer';
|
|
5
6
|
import { getMarkerHaloVisibility, renderMarker, selectMarkerHalo, selectMarkerSymbol, setMarker, } from '../marker';
|
|
6
7
|
import { setActiveState, shapeKey } from '../utils';
|
|
7
8
|
export { prepareScatterData } from './prepare-data';
|
|
8
9
|
const b = block('d3-scatter');
|
|
9
10
|
export function ScatterSeriesShape(props) {
|
|
10
|
-
const { dispatcher, preparedData, seriesOptions } = props;
|
|
11
|
+
const { dispatcher, preparedData, seriesOptions, htmlLayout } = props;
|
|
11
12
|
const ref = React.useRef(null);
|
|
12
13
|
React.useEffect(() => {
|
|
13
14
|
if (!ref.current) {
|
|
@@ -62,5 +63,7 @@ export function ScatterSeriesShape(props) {
|
|
|
62
63
|
dispatcher.on('hover-shape.scatter', null);
|
|
63
64
|
};
|
|
64
65
|
}, [dispatcher, preparedData, seriesOptions]);
|
|
65
|
-
return React.createElement(
|
|
66
|
+
return (React.createElement(React.Fragment, null,
|
|
67
|
+
React.createElement("g", { ref: ref, className: b() }),
|
|
68
|
+
React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
|
|
66
69
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ScatterSeriesData } from '../../../../../../types';
|
|
2
|
+
import { HtmlItem } from '../../../types';
|
|
2
3
|
import { PreparedScatterSeries } from '../../useSeries/types';
|
|
3
4
|
type PointData = {
|
|
4
5
|
x: number;
|
|
@@ -11,6 +12,7 @@ export type MarkerData = {
|
|
|
11
12
|
point: PointData;
|
|
12
13
|
active: boolean;
|
|
13
14
|
hovered: boolean;
|
|
15
|
+
htmlElements: HtmlItem[];
|
|
14
16
|
};
|
|
15
17
|
export type PreparedScatterData = MarkerData;
|
|
16
18
|
export {};
|
|
@@ -6,6 +6,7 @@ type ShapeProps = {
|
|
|
6
6
|
dispatcher: Dispatch<object>;
|
|
7
7
|
preparedData: PreparedTreemapData;
|
|
8
8
|
seriesOptions: PreparedSeriesOptions;
|
|
9
|
+
htmlLayout: HTMLElement | null;
|
|
9
10
|
};
|
|
10
11
|
export declare const TreemapSeriesShape: (props: ShapeProps) => React.JSX.Element;
|
|
11
12
|
export {};
|
|
@@ -3,9 +3,10 @@ import { color, select } from 'd3';
|
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { block } from '../../../../../../utils/cn';
|
|
5
5
|
import { setEllipsisForOverflowTexts } from '../../../utils';
|
|
6
|
+
import { HtmlLayer } from '../HtmlLayer';
|
|
6
7
|
const b = block('d3-treemap');
|
|
7
8
|
export const TreemapSeriesShape = (props) => {
|
|
8
|
-
const { dispatcher, preparedData, seriesOptions } = props;
|
|
9
|
+
const { dispatcher, preparedData, seriesOptions, htmlLayout } = props;
|
|
9
10
|
const ref = React.useRef(null);
|
|
10
11
|
React.useEffect(() => {
|
|
11
12
|
if (!ref.current) {
|
|
@@ -92,5 +93,7 @@ export const TreemapSeriesShape = (props) => {
|
|
|
92
93
|
dispatcher.on(eventName, null);
|
|
93
94
|
};
|
|
94
95
|
}, [dispatcher, preparedData, seriesOptions]);
|
|
95
|
-
return React.createElement(
|
|
96
|
+
return (React.createElement(React.Fragment, null,
|
|
97
|
+
React.createElement("g", { ref: ref, className: b() }),
|
|
98
|
+
React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
|
|
96
99
|
};
|
|
@@ -51,7 +51,7 @@ export function prepareTreemapData(args) {
|
|
|
51
51
|
})(hierarchy);
|
|
52
52
|
const leaves = root.leaves();
|
|
53
53
|
const labelData = ((_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.enabled) ? getLabelData(leaves) : [];
|
|
54
|
-
return { labelData, leaves, series };
|
|
54
|
+
return { labelData, leaves, series, htmlElements: [] };
|
|
55
55
|
}
|
|
56
56
|
function getSeriesDataWithRootNode(series) {
|
|
57
57
|
return series.data.reduce((acc, d) => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { HierarchyRectangularNode } from 'd3';
|
|
2
2
|
import type { TreemapSeriesData } from '../../../../../../types';
|
|
3
|
+
import { HtmlItem } from '../../../types';
|
|
3
4
|
import type { PreparedTreemapSeries } from '../../useSeries/types';
|
|
4
5
|
export type TreemapLabelData = {
|
|
5
6
|
text: string;
|
|
@@ -12,4 +13,5 @@ export type PreparedTreemapData = {
|
|
|
12
13
|
labelData: TreemapLabelData[];
|
|
13
14
|
leaves: HierarchyRectangularNode<TreemapSeriesData<any>>[];
|
|
14
15
|
series: PreparedTreemapSeries;
|
|
16
|
+
htmlElements: HtmlItem[];
|
|
15
17
|
};
|
|
@@ -4,12 +4,13 @@ import get from 'lodash/get';
|
|
|
4
4
|
import { DashStyle } from '../../../../../../constants';
|
|
5
5
|
import { block } from '../../../../../../utils/cn';
|
|
6
6
|
import { filterOverlappingLabels, getWaterfallPointColor } from '../../../utils';
|
|
7
|
+
import { HtmlLayer } from '../HtmlLayer';
|
|
7
8
|
import { getLineDashArray } from '../utils';
|
|
8
9
|
export { prepareWaterfallData } from './prepare-data';
|
|
9
10
|
export * from './types';
|
|
10
11
|
const b = block('d3-waterfall');
|
|
11
12
|
export const WaterfallSeriesShapes = (args) => {
|
|
12
|
-
const { dispatcher, preparedData, seriesOptions } = args;
|
|
13
|
+
const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
|
|
13
14
|
const ref = React.useRef(null);
|
|
14
15
|
const connectorSelector = `.${b('connector')}`;
|
|
15
16
|
React.useEffect(() => {
|
|
@@ -121,5 +122,7 @@ export const WaterfallSeriesShapes = (args) => {
|
|
|
121
122
|
dispatcher.on('hover-shape.waterfall', null);
|
|
122
123
|
};
|
|
123
124
|
}, [dispatcher, preparedData, seriesOptions]);
|
|
124
|
-
return React.createElement(
|
|
125
|
+
return (React.createElement(React.Fragment, null,
|
|
126
|
+
React.createElement("g", { ref: ref, className: b() }),
|
|
127
|
+
React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
|
|
125
128
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WaterfallSeriesData } from '../../../../../../types';
|
|
2
|
-
import { LabelData } from '../../../types';
|
|
2
|
+
import { HtmlItem, LabelData } from '../../../types';
|
|
3
3
|
import { PreparedWaterfallSeries } from '../../useSeries/types';
|
|
4
4
|
export type PreparedWaterfallData = {
|
|
5
5
|
x: number;
|
|
@@ -11,4 +11,5 @@ export type PreparedWaterfallData = {
|
|
|
11
11
|
data: WaterfallSeriesData;
|
|
12
12
|
label?: LabelData;
|
|
13
13
|
subTotal: number;
|
|
14
|
+
htmlElements: HtmlItem[];
|
|
14
15
|
};
|
|
@@ -4,15 +4,16 @@ type AxisBottomArgs = {
|
|
|
4
4
|
scale: AxisScale<AxisDomain>;
|
|
5
5
|
ticks: {
|
|
6
6
|
count?: number;
|
|
7
|
-
maxTickCount
|
|
8
|
-
labelFormat
|
|
7
|
+
maxTickCount?: number;
|
|
8
|
+
labelFormat?: (value: any) => string;
|
|
9
9
|
labelsPaddings?: number;
|
|
10
10
|
labelsMargin?: number;
|
|
11
11
|
labelsStyle?: BaseTextStyle;
|
|
12
12
|
labelsMaxWidth?: number;
|
|
13
13
|
labelsLineHeight: number;
|
|
14
|
-
items
|
|
15
|
-
rotation
|
|
14
|
+
items?: [number, number][];
|
|
15
|
+
rotation?: number;
|
|
16
|
+
tickColor?: string;
|
|
16
17
|
};
|
|
17
18
|
domain: {
|
|
18
19
|
size: number;
|
|
@@ -16,7 +16,7 @@ function addDomain(selection, options) {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
export function axisBottom(args) {
|
|
19
|
-
const { scale, ticks: { labelFormat, labelsPaddings = 0, labelsMargin = 0, labelsMaxWidth = Infinity, labelsStyle, labelsLineHeight, items: tickItems, count: ticksCount, maxTickCount, rotation, }, domain
|
|
19
|
+
const { scale, ticks: { labelFormat = (value) => String(value), labelsPaddings = 0, labelsMargin = 0, labelsMaxWidth = Infinity, labelsStyle, labelsLineHeight, items: tickItems, count: ticksCount, maxTickCount, rotation = 0, tickColor, }, domain, } = args;
|
|
20
20
|
const offset = getXAxisOffset();
|
|
21
21
|
const position = getXTickPosition({ scale, offset });
|
|
22
22
|
const values = getXAxisItems({ scale, count: ticksCount, maxCount: maxTickCount });
|
|
@@ -25,10 +25,11 @@ export function axisBottom(args) {
|
|
|
25
25
|
style: labelsStyle,
|
|
26
26
|
}).maxHeight;
|
|
27
27
|
return function (selection) {
|
|
28
|
-
var _a, _b;
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
28
|
+
var _a, _b, _c;
|
|
29
|
+
const rect = (_a = selection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
30
|
+
const x = (rect === null || rect === void 0 ? void 0 : rect.x) || 0;
|
|
31
|
+
const right = x + domain.size;
|
|
32
|
+
const top = -((_c = (_b = tickItems === null || tickItems === void 0 ? void 0 : tickItems[0]) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : 0);
|
|
32
33
|
let transform = `translate(0, ${labelHeight + labelsMargin - top}px)`;
|
|
33
34
|
if (rotation) {
|
|
34
35
|
const labelsOffsetTop = labelHeight * calculateCos(rotation) + labelsMargin - top;
|
|
@@ -39,7 +40,7 @@ export function axisBottom(args) {
|
|
|
39
40
|
transform = `translate(${-labelsOffsetLeft}px, ${labelsOffsetTop}px) rotate(${rotation}deg)`;
|
|
40
41
|
}
|
|
41
42
|
const tickPath = path();
|
|
42
|
-
tickItems.forEach(([start, end]) => {
|
|
43
|
+
tickItems === null || tickItems === void 0 ? void 0 : tickItems.forEach(([start, end]) => {
|
|
43
44
|
tickPath.moveTo(0, start);
|
|
44
45
|
tickPath.lineTo(0, end);
|
|
45
46
|
});
|
|
@@ -49,7 +50,9 @@ export function axisBottom(args) {
|
|
|
49
50
|
.order()
|
|
50
51
|
.join((el) => {
|
|
51
52
|
const tick = el.append('g').attr('class', 'tick');
|
|
52
|
-
tick.append('path')
|
|
53
|
+
tick.append('path')
|
|
54
|
+
.attr('d', tickPath.toString())
|
|
55
|
+
.attr('stroke', tickColor !== null && tickColor !== void 0 ? tickColor : 'currentColor');
|
|
53
56
|
tick.append('text')
|
|
54
57
|
.text(labelFormat)
|
|
55
58
|
.attr('fill', 'currentColor')
|
|
@@ -115,6 +118,7 @@ export function axisBottom(args) {
|
|
|
115
118
|
}
|
|
116
119
|
});
|
|
117
120
|
}
|
|
121
|
+
const { size: domainSize, color: domainColor } = domain;
|
|
118
122
|
selection
|
|
119
123
|
.call(addDomain, { size: domainSize, color: domainColor })
|
|
120
124
|
.style('font-size', (labelsStyle === null || labelsStyle === void 0 ? void 0 : labelsStyle.fontSize) || '');
|
|
@@ -15,7 +15,7 @@ export declare function getXTickPosition({ scale, offset }: {
|
|
|
15
15
|
export declare function getXAxisItems({ scale, count, maxCount, }: {
|
|
16
16
|
scale: AxisScale<AxisDomain>;
|
|
17
17
|
count?: number;
|
|
18
|
-
maxCount
|
|
18
|
+
maxCount?: number;
|
|
19
19
|
}): any;
|
|
20
20
|
export declare function getMaxTickCount({ axis, width }: {
|
|
21
21
|
axis: PreparedAxis;
|
|
@@ -32,7 +32,7 @@ export function getXTickPosition({ scale, offset }) {
|
|
|
32
32
|
}
|
|
33
33
|
export function getXAxisItems({ scale, count, maxCount, }) {
|
|
34
34
|
let values = getScaleTicks(scale, count);
|
|
35
|
-
if (values.length > maxCount) {
|
|
35
|
+
if (maxCount && values.length > maxCount) {
|
|
36
36
|
const step = Math.ceil(values.length / maxCount);
|
|
37
37
|
values = values.filter((_, i) => i % step === 0);
|
|
38
38
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ChartKitWidgetData } from '../../../../types';
|
|
2
|
+
export declare function getDomainForContinuousColorScale(args: {
|
|
3
|
+
series: ChartKitWidgetData['series']['data'];
|
|
4
|
+
}): number[];
|
|
5
|
+
export declare function getDefaultColorStops(size: number): number[];
|
|
6
|
+
export declare function getContinuesColorFn(args: {
|
|
7
|
+
values: number[];
|
|
8
|
+
colors: string[];
|
|
9
|
+
stops?: number[];
|
|
10
|
+
}): (value: number) => string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { range, scaleLinear } from 'd3';
|
|
2
|
+
export function getDomainForContinuousColorScale(args) {
|
|
3
|
+
const { series } = args;
|
|
4
|
+
const values = series.reduce((acc, s) => {
|
|
5
|
+
switch (s.type) {
|
|
6
|
+
case 'pie': {
|
|
7
|
+
acc.push(...s.data.map((d) => d.value));
|
|
8
|
+
break;
|
|
9
|
+
}
|
|
10
|
+
case 'bar-y': {
|
|
11
|
+
acc.push(...s.data.map((d) => Number(d.x)));
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
case 'scatter':
|
|
15
|
+
case 'bar-x':
|
|
16
|
+
case 'waterfall':
|
|
17
|
+
case 'line':
|
|
18
|
+
case 'area': {
|
|
19
|
+
acc.push(...s.data.map((d) => Number(d.y)));
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
default: {
|
|
23
|
+
throw Error(`The method for calculation a domain for a continuous color scale for the "${s.type}" series is not defined`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return acc;
|
|
27
|
+
}, []);
|
|
28
|
+
return [Math.min(...values), Math.max(...values)];
|
|
29
|
+
}
|
|
30
|
+
export function getDefaultColorStops(size) {
|
|
31
|
+
return range(size).map((d) => d / size);
|
|
32
|
+
}
|
|
33
|
+
export function getContinuesColorFn(args) {
|
|
34
|
+
const { values, colors, stops: customStops } = args;
|
|
35
|
+
const min = Math.min(...values);
|
|
36
|
+
const max = Math.max(...values);
|
|
37
|
+
const stops = customStops !== null && customStops !== void 0 ? customStops : getDefaultColorStops(colors.length);
|
|
38
|
+
const color = scaleLinear(stops, colors);
|
|
39
|
+
return (value) => {
|
|
40
|
+
const colorValue = (value - min) / (max - min);
|
|
41
|
+
return color(colorValue);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -6,8 +6,10 @@ export * from './text';
|
|
|
6
6
|
export * from './time';
|
|
7
7
|
export * from './axis';
|
|
8
8
|
export * from './labels';
|
|
9
|
+
export * from './legend';
|
|
9
10
|
export * from './symbol';
|
|
10
11
|
export * from './series';
|
|
12
|
+
export * from './color';
|
|
11
13
|
export declare const CHART_SERIES_WITH_VOLUME_ON_Y_AXIS: ChartKitWidgetSeries['type'][];
|
|
12
14
|
export declare const CHART_SERIES_WITH_VOLUME_ON_X_AXIS: ChartKitWidgetSeries['type'][];
|
|
13
15
|
export type AxisDirection = 'x' | 'y';
|
|
@@ -12,8 +12,10 @@ export * from './text';
|
|
|
12
12
|
export * from './time';
|
|
13
13
|
export * from './axis';
|
|
14
14
|
export * from './labels';
|
|
15
|
+
export * from './legend';
|
|
15
16
|
export * from './symbol';
|
|
16
17
|
export * from './series';
|
|
18
|
+
export * from './color';
|
|
17
19
|
const CHARTS_WITHOUT_AXIS = ['pie', 'treemap'];
|
|
18
20
|
export const CHART_SERIES_WITH_VOLUME_ON_Y_AXIS = [
|
|
19
21
|
'bar-x',
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Selection } from 'd3';
|
|
2
|
+
export declare function createGradientRect(container: Selection<SVGGElement, unknown, null, undefined>, args: {
|
|
3
|
+
x?: number;
|
|
4
|
+
y?: number;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
interpolator: (value: number) => string;
|
|
8
|
+
}): Selection<SVGImageElement, unknown, null, undefined>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function createGradientRect(container, args) {
|
|
2
|
+
const { x = 0, y = 0, width, height, interpolator } = args;
|
|
3
|
+
const n = 256;
|
|
4
|
+
const canvas = document.createElement('canvas');
|
|
5
|
+
canvas.width = n;
|
|
6
|
+
canvas.height = 1;
|
|
7
|
+
const context = canvas.getContext('2d');
|
|
8
|
+
if (!context) {
|
|
9
|
+
throw Error("Couldn't get canvas context");
|
|
10
|
+
}
|
|
11
|
+
for (let i = 0, j = n - 1; i < n; ++i) {
|
|
12
|
+
context.fillStyle = interpolator(i / j);
|
|
13
|
+
context.fillRect(i, 0, 1, height);
|
|
14
|
+
}
|
|
15
|
+
return container
|
|
16
|
+
.append('image')
|
|
17
|
+
.attr('preserveAspectRatio', 'none')
|
|
18
|
+
.attr('height', height)
|
|
19
|
+
.attr('width', width)
|
|
20
|
+
.attr('x', x)
|
|
21
|
+
.attr('y', y)
|
|
22
|
+
.attr('xlink:href', canvas.toDataURL());
|
|
23
|
+
}
|
|
@@ -9,10 +9,11 @@ export declare function hasOverlappingLabels({ width, labels, padding, style, }:
|
|
|
9
9
|
style?: BaseTextStyle;
|
|
10
10
|
padding?: number;
|
|
11
11
|
}): boolean;
|
|
12
|
-
export declare function getLabelsSize({ labels, style, rotation, }: {
|
|
12
|
+
export declare function getLabelsSize({ labels, style, rotation, html, }: {
|
|
13
13
|
labels: string[];
|
|
14
14
|
style?: BaseTextStyle;
|
|
15
15
|
rotation?: number;
|
|
16
|
+
html?: boolean;
|
|
16
17
|
}): {
|
|
17
18
|
maxHeight: number;
|
|
18
19
|
maxWidth: number;
|
|
@@ -63,24 +63,46 @@ function renderLabels(selection, { labels, style = {}, attrs = {}, }) {
|
|
|
63
63
|
.text((d) => d);
|
|
64
64
|
return text;
|
|
65
65
|
}
|
|
66
|
-
export function getLabelsSize({ labels, style, rotation, }) {
|
|
67
|
-
var _a;
|
|
66
|
+
export function getLabelsSize({ labels, style, rotation, html, }) {
|
|
67
|
+
var _a, _b, _c;
|
|
68
68
|
if (!labels.filter(Boolean).length) {
|
|
69
69
|
return { maxHeight: 0, maxWidth: 0 };
|
|
70
70
|
}
|
|
71
71
|
const container = select(document.body)
|
|
72
72
|
.append('div')
|
|
73
73
|
.attr('class', 'chartkit chartkit-theme_common');
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
const result = { maxHeight: 0, maxWidth: 0 };
|
|
75
|
+
let labelWrapper;
|
|
76
|
+
if (html) {
|
|
77
|
+
labelWrapper = container.append('div').style('position', 'absolute').node();
|
|
78
|
+
const { height, width } = labels.reduce((acc, l) => {
|
|
79
|
+
var _a, _b;
|
|
80
|
+
if (labelWrapper) {
|
|
81
|
+
labelWrapper.innerHTML = l;
|
|
82
|
+
}
|
|
83
|
+
const rect = labelWrapper === null || labelWrapper === void 0 ? void 0 : labelWrapper.getBoundingClientRect();
|
|
84
|
+
return {
|
|
85
|
+
width: Math.max(acc.width, (_a = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _a !== void 0 ? _a : 0),
|
|
86
|
+
height: Math.max(acc.height, (_b = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _b !== void 0 ? _b : 0),
|
|
87
|
+
};
|
|
88
|
+
}, { height: 0, width: 0 });
|
|
89
|
+
result.maxWidth = width;
|
|
90
|
+
result.maxHeight = height;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
const svg = container.append('svg');
|
|
94
|
+
const textSelection = renderLabels(svg, { labels, style });
|
|
95
|
+
if (rotation) {
|
|
96
|
+
textSelection
|
|
97
|
+
.attr('text-anchor', rotation > 0 ? 'start' : 'end')
|
|
98
|
+
.style('transform', `rotate(${rotation}deg)`);
|
|
99
|
+
}
|
|
100
|
+
const rect = (_a = svg.select('g').node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
101
|
+
result.maxWidth = (_b = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _b !== void 0 ? _b : 0;
|
|
102
|
+
result.maxHeight = (_c = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _c !== void 0 ? _c : 0;
|
|
80
103
|
}
|
|
81
|
-
const { height = 0, width = 0 } = ((_a = svg.select('g').node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) || {};
|
|
82
104
|
container.remove();
|
|
83
|
-
return
|
|
105
|
+
return result;
|
|
84
106
|
}
|
|
85
107
|
export function wrapText(args) {
|
|
86
108
|
const { text, style, width } = args;
|
|
@@ -49,7 +49,7 @@ export type BarXSeries<T = any> = BaseSeries & {
|
|
|
49
49
|
* @default true
|
|
50
50
|
* */
|
|
51
51
|
grouping?: boolean;
|
|
52
|
-
dataLabels?: ChartKitWidgetSeriesOptions['dataLabels'] & {
|
|
52
|
+
dataLabels?: BaseSeries['dataLabels'] & ChartKitWidgetSeriesOptions['dataLabels'] & {
|
|
53
53
|
/**
|
|
54
54
|
* Whether to align the data label inside or outside the box
|
|
55
55
|
*
|
|
@@ -21,6 +21,13 @@ export type BaseSeries = {
|
|
|
21
21
|
* @default false
|
|
22
22
|
* */
|
|
23
23
|
allowOverlap?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Allows to use any html-tags to display the content.
|
|
26
|
+
* The element will be displayed outside the box of the SVG element.
|
|
27
|
+
*
|
|
28
|
+
* @default false
|
|
29
|
+
* */
|
|
30
|
+
html?: boolean;
|
|
24
31
|
};
|
|
25
32
|
/** You can set the cursor to "pointer" if you have click events attached to the series, to signal to the user that the points and lines can be clicked. */
|
|
26
33
|
cursor?: string;
|