@gravity-ui/charts 1.7.1 → 1.9.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/dist/cjs/components/Axis/AxisX.js +10 -8
- package/dist/cjs/components/Axis/AxisY.js +16 -36
- package/dist/cjs/components/ChartInner/index.js +15 -1
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +4 -2
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +4 -1
- package/dist/cjs/components/Legend/index.d.ts +1 -0
- package/dist/cjs/components/Legend/index.js +113 -51
- package/dist/cjs/components/Legend/styles.css +13 -0
- package/dist/cjs/components/Tooltip/DefaultContent.js +6 -3
- package/dist/cjs/constants/chart-types.d.ts +12 -0
- package/dist/cjs/constants/chart-types.js +12 -0
- package/dist/cjs/constants/defaults/axis.d.ts +3 -1
- package/dist/cjs/constants/defaults/axis.js +10 -0
- package/dist/cjs/constants/index.d.ts +6 -47
- package/dist/cjs/constants/index.js +6 -72
- package/dist/cjs/constants/layout-algorithms.d.ts +7 -0
- package/dist/cjs/constants/layout-algorithms.js +8 -0
- package/dist/cjs/constants/line-styles.d.ts +20 -0
- package/dist/cjs/constants/line-styles.js +20 -0
- package/dist/cjs/constants/palette.d.ts +1 -0
- package/dist/cjs/constants/palette.js +22 -0
- package/dist/cjs/constants/symbol-types.d.ts +7 -0
- package/dist/cjs/constants/symbol-types.js +8 -0
- package/dist/cjs/constants/typography.d.ts +1 -0
- package/dist/cjs/constants/typography.js +1 -0
- package/dist/cjs/hooks/index.d.ts +1 -0
- package/dist/cjs/hooks/index.js +1 -0
- package/dist/cjs/hooks/useChartOptions/types.d.ts +4 -2
- package/dist/cjs/hooks/useChartOptions/x-axis.js +11 -2
- package/dist/cjs/hooks/useChartOptions/y-axis.js +14 -3
- package/dist/cjs/hooks/useCrosshair/index.d.ts +17 -0
- package/dist/cjs/hooks/useCrosshair/index.js +139 -0
- package/dist/cjs/hooks/useCrosshair/useCrosshairHover.d.ts +11 -0
- package/dist/cjs/hooks/useCrosshair/useCrosshairHover.js +18 -0
- package/dist/cjs/hooks/useSeries/index.d.ts +4 -2
- package/dist/cjs/hooks/useSeries/prepare-legend.d.ts +11 -9
- package/dist/cjs/hooks/useSeries/prepare-legend.js +78 -23
- package/dist/cjs/hooks/useSeries/prepare-line.d.ts +1 -2
- package/dist/cjs/hooks/useSeries/prepare-line.js +3 -3
- package/dist/cjs/hooks/useSeries/types.d.ts +7 -3
- package/dist/cjs/hooks/useShapes/waterfall/index.js +2 -2
- package/dist/cjs/types/chart/axis.d.ts +26 -21
- package/dist/cjs/types/chart/legend.d.ts +6 -0
- package/dist/cjs/types/chart/line.d.ts +1 -1
- package/dist/cjs/types/chart/series.d.ts +1 -1
- package/dist/cjs/utils/chart/axis.d.ts +1 -0
- package/dist/cjs/utils/chart/axis.js +8 -0
- package/dist/esm/components/Axis/AxisX.js +10 -8
- package/dist/esm/components/Axis/AxisY.js +16 -36
- package/dist/esm/components/ChartInner/index.js +15 -1
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +4 -2
- package/dist/esm/components/ChartInner/useChartInnerProps.js +4 -1
- package/dist/esm/components/Legend/index.d.ts +1 -0
- package/dist/esm/components/Legend/index.js +113 -51
- package/dist/esm/components/Legend/styles.css +13 -0
- package/dist/esm/components/Tooltip/DefaultContent.js +6 -3
- package/dist/esm/constants/chart-types.d.ts +12 -0
- package/dist/esm/constants/chart-types.js +12 -0
- package/dist/esm/constants/defaults/axis.d.ts +3 -1
- package/dist/esm/constants/defaults/axis.js +10 -0
- package/dist/esm/constants/index.d.ts +6 -47
- package/dist/esm/constants/index.js +6 -72
- package/dist/esm/constants/layout-algorithms.d.ts +7 -0
- package/dist/esm/constants/layout-algorithms.js +8 -0
- package/dist/esm/constants/line-styles.d.ts +20 -0
- package/dist/esm/constants/line-styles.js +20 -0
- package/dist/esm/constants/palette.d.ts +1 -0
- package/dist/esm/constants/palette.js +22 -0
- package/dist/esm/constants/symbol-types.d.ts +7 -0
- package/dist/esm/constants/symbol-types.js +8 -0
- package/dist/esm/constants/typography.d.ts +1 -0
- package/dist/esm/constants/typography.js +1 -0
- package/dist/esm/hooks/index.d.ts +1 -0
- package/dist/esm/hooks/index.js +1 -0
- package/dist/esm/hooks/useChartOptions/types.d.ts +4 -2
- package/dist/esm/hooks/useChartOptions/x-axis.js +11 -2
- package/dist/esm/hooks/useChartOptions/y-axis.js +14 -3
- package/dist/esm/hooks/useCrosshair/index.d.ts +17 -0
- package/dist/esm/hooks/useCrosshair/index.js +139 -0
- package/dist/esm/hooks/useCrosshair/useCrosshairHover.d.ts +11 -0
- package/dist/esm/hooks/useCrosshair/useCrosshairHover.js +18 -0
- package/dist/esm/hooks/useSeries/index.d.ts +4 -2
- package/dist/esm/hooks/useSeries/prepare-legend.d.ts +11 -9
- package/dist/esm/hooks/useSeries/prepare-legend.js +78 -23
- package/dist/esm/hooks/useSeries/prepare-line.d.ts +1 -2
- package/dist/esm/hooks/useSeries/prepare-line.js +3 -3
- package/dist/esm/hooks/useSeries/types.d.ts +7 -3
- package/dist/esm/hooks/useShapes/waterfall/index.js +2 -2
- package/dist/esm/types/chart/axis.d.ts +26 -21
- package/dist/esm/types/chart/legend.d.ts +6 -0
- package/dist/esm/types/chart/line.d.ts +1 -1
- package/dist/esm/types/chart/series.d.ts +1 -1
- package/dist/esm/utils/chart/axis.d.ts +1 -0
- package/dist/esm/utils/chart/axis.js +8 -0
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ import { CONTINUOUS_LEGEND_SIZE, legendDefaults } from '../../constants';
|
|
|
6
6
|
import { getDefaultColorStops, getDomainForContinuousColorScale, getHorisontalSvgTextHeight, getLabelsSize, } from '../../utils';
|
|
7
7
|
import { getBoundsWidth } from '../useChartDimensions';
|
|
8
8
|
import { getYAxisWidth } from '../useChartDimensions/utils';
|
|
9
|
-
export
|
|
9
|
+
export function getPreparedLegend(args) {
|
|
10
10
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
11
11
|
const { legend, series } = args;
|
|
12
12
|
const enabled = Boolean(typeof (legend === null || legend === void 0 ? void 0 : legend.enabled) === 'boolean' ? legend === null || legend === void 0 ? void 0 : legend.enabled : series.length > 1);
|
|
@@ -69,40 +69,65 @@ export const getPreparedLegend = (args) => {
|
|
|
69
69
|
width: legendWidth,
|
|
70
70
|
ticks,
|
|
71
71
|
colorScale,
|
|
72
|
+
html: get(legend, 'html', false),
|
|
72
73
|
};
|
|
73
|
-
}
|
|
74
|
-
|
|
74
|
+
}
|
|
75
|
+
function getFlattenLegendItems(series, preparedLegend) {
|
|
75
76
|
return series.reduce((acc, s) => {
|
|
76
77
|
const legendEnabled = get(s, 'legend.enabled', true);
|
|
77
78
|
if (legendEnabled) {
|
|
78
|
-
acc.push(Object.assign(Object.assign({}, s), { symbol: s.legend.symbol }));
|
|
79
|
+
acc.push(Object.assign(Object.assign({}, s), { height: preparedLegend.lineHeight, symbol: s.legend.symbol }));
|
|
79
80
|
}
|
|
80
81
|
return acc;
|
|
81
82
|
}, []);
|
|
82
|
-
}
|
|
83
|
-
|
|
83
|
+
}
|
|
84
|
+
function getGroupedLegendItems(args) {
|
|
84
85
|
const { maxLegendWidth, items, preparedLegend } = args;
|
|
85
86
|
const result = [[]];
|
|
87
|
+
const bodySelection = select(document.body);
|
|
86
88
|
let textWidthsInLine = [0];
|
|
87
89
|
let lineIndex = 0;
|
|
88
90
|
items.forEach((item) => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
const itemSelection = preparedLegend.html
|
|
92
|
+
? bodySelection
|
|
93
|
+
.append('div')
|
|
94
|
+
.html(item.name)
|
|
95
|
+
.style('position', 'absolute')
|
|
96
|
+
.style('display', 'inline-block')
|
|
97
|
+
.style('white-space', 'nowrap')
|
|
98
|
+
: bodySelection.append('text').text(item.name).style('white-space', 'nowrap');
|
|
99
|
+
itemSelection
|
|
92
100
|
.style('font-size', preparedLegend.itemStyle.fontSize)
|
|
93
101
|
.each(function () {
|
|
94
102
|
const resultItem = clone(item);
|
|
95
|
-
const textWidth = this.getBoundingClientRect()
|
|
96
|
-
resultItem.
|
|
103
|
+
const { height, width: textWidth } = this.getBoundingClientRect();
|
|
104
|
+
resultItem.height = height;
|
|
105
|
+
if (textWidth >
|
|
106
|
+
maxLegendWidth - resultItem.symbol.width - resultItem.symbol.padding) {
|
|
107
|
+
resultItem.overflowed = true;
|
|
108
|
+
resultItem.textWidth =
|
|
109
|
+
maxLegendWidth - resultItem.symbol.width - resultItem.symbol.padding;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
resultItem.textWidth = textWidth;
|
|
113
|
+
}
|
|
97
114
|
textWidthsInLine.push(textWidth);
|
|
98
115
|
const textsWidth = textWidthsInLine.reduce((acc, width) => acc + width, 0);
|
|
116
|
+
if (!result[lineIndex]) {
|
|
117
|
+
result[lineIndex] = [];
|
|
118
|
+
}
|
|
99
119
|
result[lineIndex].push(resultItem);
|
|
100
120
|
const symbolsWidth = result[lineIndex].reduce((acc, { symbol }) => {
|
|
101
121
|
return acc + symbol.width + symbol.padding;
|
|
102
122
|
}, 0);
|
|
103
123
|
const distancesWidth = (result[lineIndex].length - 1) * preparedLegend.itemDistance;
|
|
104
|
-
const
|
|
105
|
-
|
|
124
|
+
const isOverflowedAsOnlyItemInLine = resultItem.overflowed && result[lineIndex].length === 1;
|
|
125
|
+
const isCurrentLineOverMaxWidth = maxLegendWidth < textsWidth + symbolsWidth + distancesWidth;
|
|
126
|
+
if (isOverflowedAsOnlyItemInLine) {
|
|
127
|
+
lineIndex += 1;
|
|
128
|
+
textWidthsInLine = [];
|
|
129
|
+
}
|
|
130
|
+
else if (isCurrentLineOverMaxWidth) {
|
|
106
131
|
result[lineIndex].pop();
|
|
107
132
|
lineIndex += 1;
|
|
108
133
|
textWidthsInLine = [textWidth];
|
|
@@ -114,12 +139,36 @@ const getGroupedLegendItems = (args) => {
|
|
|
114
139
|
.remove();
|
|
115
140
|
});
|
|
116
141
|
return result;
|
|
117
|
-
}
|
|
118
|
-
|
|
142
|
+
}
|
|
143
|
+
function getPagination(args) {
|
|
144
|
+
const { items, maxLegendHeight, paginatorHeight } = args;
|
|
145
|
+
const pages = [];
|
|
146
|
+
let currentPageIndex = 0;
|
|
147
|
+
let currentHeight = 0;
|
|
148
|
+
items.forEach((item, i) => {
|
|
149
|
+
if (!pages[currentPageIndex]) {
|
|
150
|
+
pages[currentPageIndex] = { start: i, end: i };
|
|
151
|
+
}
|
|
152
|
+
const legendLineHeight = Math.max(...item.map((item) => item.height));
|
|
153
|
+
currentHeight += legendLineHeight;
|
|
154
|
+
if (currentHeight > maxLegendHeight - paginatorHeight) {
|
|
155
|
+
pages[currentPageIndex].end = i;
|
|
156
|
+
currentPageIndex += 1;
|
|
157
|
+
currentHeight = legendLineHeight;
|
|
158
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice#end
|
|
159
|
+
pages[currentPageIndex] = { start: i, end: i + (i === items.length - 1 ? 1 : 0) };
|
|
160
|
+
}
|
|
161
|
+
else if (i === items.length - 1) {
|
|
162
|
+
pages[currentPageIndex].end = i + 1;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
return { pages };
|
|
166
|
+
}
|
|
167
|
+
export function getLegendComponents(args) {
|
|
119
168
|
const { chartWidth, chartHeight, chartMargin, series, preparedLegend, preparedYAxis } = args;
|
|
120
169
|
const maxLegendWidth = getBoundsWidth({ chartWidth, chartMargin, preparedYAxis });
|
|
121
170
|
const maxLegendHeight = (chartHeight - chartMargin.top - chartMargin.bottom - preparedLegend.margin) / 2;
|
|
122
|
-
const flattenLegendItems = getFlattenLegendItems(series);
|
|
171
|
+
const flattenLegendItems = getFlattenLegendItems(series, preparedLegend);
|
|
123
172
|
const items = getGroupedLegendItems({
|
|
124
173
|
maxLegendWidth,
|
|
125
174
|
items: flattenLegendItems,
|
|
@@ -127,13 +176,19 @@ export const getLegendComponents = (args) => {
|
|
|
127
176
|
});
|
|
128
177
|
let pagination;
|
|
129
178
|
if (preparedLegend.type === 'discrete') {
|
|
130
|
-
|
|
179
|
+
const lineHeights = items.reduce((acc, item) => {
|
|
180
|
+
acc.push(Math.max(...item.map((item) => item.height)));
|
|
181
|
+
return acc;
|
|
182
|
+
}, []);
|
|
183
|
+
let legendHeight = lineHeights.reduce((acc, height) => acc + height, 0);
|
|
131
184
|
if (maxLegendHeight < legendHeight) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
185
|
+
const lines = Math.floor(maxLegendHeight / preparedLegend.lineHeight);
|
|
186
|
+
legendHeight = preparedLegend.lineHeight * lines;
|
|
187
|
+
pagination = getPagination({
|
|
188
|
+
items,
|
|
189
|
+
maxLegendHeight: legendHeight,
|
|
190
|
+
paginatorHeight: preparedLegend.lineHeight,
|
|
191
|
+
});
|
|
137
192
|
}
|
|
138
193
|
preparedLegend.height = legendHeight;
|
|
139
194
|
}
|
|
@@ -143,4 +198,4 @@ export const getLegendComponents = (args) => {
|
|
|
143
198
|
top,
|
|
144
199
|
};
|
|
145
200
|
return { legendConfig: { offset, pagination }, legendItems: items };
|
|
146
|
-
}
|
|
201
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { ScaleOrdinal } from 'd3';
|
|
2
|
-
import { DashStyle } from '../../constants';
|
|
3
2
|
import type { ChartSeriesOptions, LineSeries } from '../../types';
|
|
4
3
|
import type { PreparedLegend, PreparedLineSeries } from './types';
|
|
5
4
|
export declare const DEFAULT_LEGEND_SYMBOL_SIZE = 16;
|
|
6
5
|
export declare const DEFAULT_LINE_WIDTH = 1;
|
|
7
|
-
export declare const DEFAULT_DASH_STYLE
|
|
6
|
+
export declare const DEFAULT_DASH_STYLE: "Solid";
|
|
8
7
|
export declare const DEFAULT_MARKER: {
|
|
9
8
|
enabled: boolean;
|
|
10
9
|
symbol: `${import("../../constants").SymbolType}`;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import merge from 'lodash/merge';
|
|
3
|
-
import {
|
|
3
|
+
import { DASH_STYLE, DEFAULT_DATALABELS_STYLE, LineCap } from '../../constants';
|
|
4
4
|
import { getUniqId } from '../../utils';
|
|
5
5
|
import { DEFAULT_DATALABELS_PADDING, DEFAULT_HALO_OPTIONS, DEFAULT_LEGEND_SYMBOL_PADDING, DEFAULT_POINT_MARKER_OPTIONS, } from './constants';
|
|
6
6
|
export const DEFAULT_LEGEND_SYMBOL_SIZE = 16;
|
|
7
7
|
export const DEFAULT_LINE_WIDTH = 1;
|
|
8
|
-
export const DEFAULT_DASH_STYLE =
|
|
8
|
+
export const DEFAULT_DASH_STYLE = DASH_STYLE.Solid;
|
|
9
9
|
export const DEFAULT_MARKER = Object.assign(Object.assign({}, DEFAULT_POINT_MARKER_OPTIONS), { enabled: false });
|
|
10
10
|
function prepareLinecap(dashStyle, series, seriesOptions) {
|
|
11
|
-
const defaultLineCap = dashStyle ===
|
|
11
|
+
const defaultLineCap = dashStyle === DASH_STYLE.Solid ? LineCap.Round : LineCap.None;
|
|
12
12
|
const lineCapFromSeriesOptions = get(seriesOptions, 'line.linecap', defaultLineCap);
|
|
13
13
|
return get(series, 'linecap', lineCapFromSeriesOptions);
|
|
14
14
|
}
|
|
@@ -39,11 +39,13 @@ export type OnLegendItemClick = (data: {
|
|
|
39
39
|
}) => void;
|
|
40
40
|
export type LegendItem = {
|
|
41
41
|
color: string;
|
|
42
|
+
height: number;
|
|
42
43
|
name: string;
|
|
43
44
|
symbol: PreparedLegendSymbol;
|
|
44
45
|
textWidth: number;
|
|
45
|
-
visible?: boolean;
|
|
46
46
|
dashStyle?: DashStyle;
|
|
47
|
+
overflowed?: boolean;
|
|
48
|
+
visible?: boolean;
|
|
47
49
|
};
|
|
48
50
|
export type LegendConfig = {
|
|
49
51
|
offset: {
|
|
@@ -51,8 +53,10 @@ export type LegendConfig = {
|
|
|
51
53
|
top: number;
|
|
52
54
|
};
|
|
53
55
|
pagination?: {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
pages: {
|
|
57
|
+
start: number;
|
|
58
|
+
end: number;
|
|
59
|
+
}[];
|
|
56
60
|
};
|
|
57
61
|
};
|
|
58
62
|
export type PreparedHaloOptions = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { color, line as lineGenerator, select } from 'd3';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
|
-
import {
|
|
4
|
+
import { DASH_STYLE } from '../../../constants';
|
|
5
5
|
import { block, filterOverlappingLabels, getLineDashArray, getWaterfallPointColor, } from '../../../utils';
|
|
6
6
|
import { HtmlLayer } from '../HtmlLayer';
|
|
7
7
|
export { prepareWaterfallData } from './prepare-data';
|
|
@@ -77,7 +77,7 @@ export const WaterfallSeriesShapes = (args) => {
|
|
|
77
77
|
return line(points);
|
|
78
78
|
})
|
|
79
79
|
.attr('stroke-width', 1)
|
|
80
|
-
.attr('stroke-dasharray', () => getLineDashArray(
|
|
80
|
+
.attr('stroke-dasharray', () => getLineDashArray(DASH_STYLE.Dash, 1));
|
|
81
81
|
function handleShapeHover(data) {
|
|
82
82
|
hoveredDataRef.current = data;
|
|
83
83
|
const hoverEnabled = hoverOptions === null || hoverOptions === void 0 ? void 0 : hoverOptions.enabled;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DashStyle } from '
|
|
1
|
+
import type { DashStyle } from '../../constants';
|
|
2
2
|
import type { FormatNumberOptions } from '../formatter';
|
|
3
3
|
import type { BaseTextStyle } from './base';
|
|
4
4
|
export type ChartAxisType = 'category' | 'datetime' | 'linear' | 'logarithmic';
|
|
@@ -7,14 +7,12 @@ export interface ChartAxisLabels {
|
|
|
7
7
|
/** Enable or disable the axis labels. */
|
|
8
8
|
enabled?: boolean;
|
|
9
9
|
/** The label's pixel distance from the perimeter of the plot area.
|
|
10
|
-
*
|
|
11
|
-
* @default: 10
|
|
10
|
+
* @default 10
|
|
12
11
|
*/
|
|
13
12
|
margin?: number;
|
|
14
13
|
/** The pixel padding for axis labels, to ensure white space between them.
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
* */
|
|
14
|
+
* @default 5
|
|
15
|
+
*/
|
|
18
16
|
padding?: number;
|
|
19
17
|
dateFormat?: string;
|
|
20
18
|
numberFormat?: FormatNumberOptions;
|
|
@@ -24,13 +22,14 @@ export interface ChartAxisLabels {
|
|
|
24
22
|
* As the chart gets narrower, it will start rotating the labels -45 degrees. */
|
|
25
23
|
autoRotation?: boolean;
|
|
26
24
|
/** Rotation of the labels in degrees.
|
|
27
|
-
*
|
|
28
|
-
* @default: 0
|
|
25
|
+
* @default 0
|
|
29
26
|
*/
|
|
30
27
|
rotation?: number;
|
|
31
28
|
}
|
|
32
29
|
export interface ChartAxis {
|
|
33
30
|
categories?: string[];
|
|
31
|
+
/** Configure a crosshair that follows either the mouse pointer or the hovered point. */
|
|
32
|
+
crosshair?: AxisCrosshair;
|
|
34
33
|
timestamps?: number[];
|
|
35
34
|
type?: ChartAxisType;
|
|
36
35
|
/** The axis labels show the number or category for each tick. */
|
|
@@ -42,14 +41,14 @@ export interface ChartAxis {
|
|
|
42
41
|
/** CSS styles for the title */
|
|
43
42
|
style?: Partial<BaseTextStyle>;
|
|
44
43
|
/** The pixel distance between the axis labels or line and the title.
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
* */
|
|
44
|
+
* @default 4 for horizontal axes, 8 for vertical
|
|
45
|
+
*/
|
|
48
46
|
margin?: number;
|
|
49
47
|
/** Alignment of the title. */
|
|
50
48
|
align?: ChartAxisTitleAlignment;
|
|
51
49
|
/** Allows limiting of the contents of a title block to the specified number of lines.
|
|
52
|
-
*
|
|
50
|
+
* @default 1
|
|
51
|
+
*/
|
|
53
52
|
maxRowCount?: number;
|
|
54
53
|
};
|
|
55
54
|
/** The minimum value of the axis. If undefined the min value is automatically calculate. */
|
|
@@ -57,9 +56,8 @@ export interface ChartAxis {
|
|
|
57
56
|
/** The grid lines settings. */
|
|
58
57
|
grid?: {
|
|
59
58
|
/** Enable or disable the grid lines.
|
|
60
|
-
*
|
|
61
|
-
|
|
62
|
-
* */
|
|
59
|
+
* @default true
|
|
60
|
+
*/
|
|
63
61
|
enabled?: boolean;
|
|
64
62
|
};
|
|
65
63
|
ticks?: {
|
|
@@ -69,9 +67,8 @@ export interface ChartAxis {
|
|
|
69
67
|
};
|
|
70
68
|
/** Padding of the max value relative to the length of the axis.
|
|
71
69
|
* A padding of 0.05 will make a 100px axis 5px longer.
|
|
72
|
-
*
|
|
73
|
-
|
|
74
|
-
* */
|
|
70
|
+
* @default 0.05 for Y axis, 0.01 for X axis
|
|
71
|
+
*/
|
|
75
72
|
maxPadding?: number;
|
|
76
73
|
/** An array of lines stretching across the plot area, marking a specific value */
|
|
77
74
|
plotLines?: AxisPlotLine[];
|
|
@@ -90,7 +87,6 @@ export interface AxisPlot {
|
|
|
90
87
|
color?: string;
|
|
91
88
|
/**
|
|
92
89
|
* Individual opacity for the line.
|
|
93
|
-
*
|
|
94
90
|
* @default 1
|
|
95
91
|
* */
|
|
96
92
|
opacity?: number;
|
|
@@ -101,12 +97,11 @@ export interface AxisPlotLine extends AxisPlot {
|
|
|
101
97
|
/** The color of the plot line (hex, rgba). */
|
|
102
98
|
color?: string;
|
|
103
99
|
/** Pixel width of the plot line.
|
|
104
|
-
*
|
|
105
100
|
* @default 1
|
|
106
101
|
* */
|
|
107
102
|
width?: number;
|
|
108
103
|
/** Option for line stroke style. */
|
|
109
|
-
dashStyle?:
|
|
104
|
+
dashStyle?: DashStyle;
|
|
110
105
|
}
|
|
111
106
|
export interface AxisPlotBand extends AxisPlot {
|
|
112
107
|
/**
|
|
@@ -124,6 +119,16 @@ export interface AxisPlotBand extends AxisPlot {
|
|
|
124
119
|
*/
|
|
125
120
|
to: number | string;
|
|
126
121
|
}
|
|
122
|
+
export interface AxisCrosshair extends Omit<AxisPlotLine, 'value'> {
|
|
123
|
+
/** Whether the crosshair should snap to the point or follow the pointer independent of points.
|
|
124
|
+
* @default true
|
|
125
|
+
*/
|
|
126
|
+
snap?: boolean;
|
|
127
|
+
/** Enable or disable the axis crosshair.
|
|
128
|
+
* @default false
|
|
129
|
+
*/
|
|
130
|
+
enabled?: boolean;
|
|
131
|
+
}
|
|
127
132
|
export interface ChartYAxis extends ChartAxis {
|
|
128
133
|
/** Axis location.
|
|
129
134
|
* Possible values - 'left' and 'right'.
|
|
@@ -47,7 +47,7 @@ export interface LineSeries<T = MeaningfulAny> extends BaseSeries {
|
|
|
47
47
|
/** Options for the point markers of line series */
|
|
48
48
|
marker?: PointMarkerOptions;
|
|
49
49
|
/** Option for line stroke style */
|
|
50
|
-
dashStyle?:
|
|
50
|
+
dashStyle?: DashStyle;
|
|
51
51
|
/** Option for line cap style */
|
|
52
52
|
linecap?: `${LineCap}`;
|
|
53
53
|
/** Individual opacity for the line. */
|
|
@@ -183,7 +183,7 @@ export interface ChartSeriesOptions {
|
|
|
183
183
|
*
|
|
184
184
|
* @default 'Solid'
|
|
185
185
|
* */
|
|
186
|
-
dashStyle?:
|
|
186
|
+
dashStyle?: DashStyle;
|
|
187
187
|
/** Options for line cap style
|
|
188
188
|
*
|
|
189
189
|
* @default 'round' when dashStyle is not 'solid', 'none' when dashStyle is not 'solid'
|
|
@@ -35,6 +35,7 @@ interface GetBandsPositionArgs {
|
|
|
35
35
|
axisScale: AxisScale<AxisDomain>;
|
|
36
36
|
axis: AxisDirection;
|
|
37
37
|
}
|
|
38
|
+
export declare const getAxisPlotsPosition: (axis: PreparedAxis, split: PreparedSplit, width?: number) => string;
|
|
38
39
|
export declare function getBandsPosition(args: GetBandsPositionArgs): {
|
|
39
40
|
from: number;
|
|
40
41
|
to: number;
|
|
@@ -70,6 +70,14 @@ export function getAxisTitleRows(args) {
|
|
|
70
70
|
return acc;
|
|
71
71
|
}, []);
|
|
72
72
|
}
|
|
73
|
+
export const getAxisPlotsPosition = (axis, split, width = 0) => {
|
|
74
|
+
var _a;
|
|
75
|
+
const top = ((_a = split.plots[axis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
|
|
76
|
+
if (axis.position === 'left') {
|
|
77
|
+
return `translate(0, ${top}px)`;
|
|
78
|
+
}
|
|
79
|
+
return `translate(${width}px, ${top}px)`;
|
|
80
|
+
};
|
|
73
81
|
export function getBandsPosition(args) {
|
|
74
82
|
var _a, _b, _c;
|
|
75
83
|
const { band, axisScale } = args;
|
|
@@ -50,11 +50,11 @@ export const AxisX = React.memo(function AxisX(props) {
|
|
|
50
50
|
}
|
|
51
51
|
const svgElement = select(ref.current);
|
|
52
52
|
svgElement.selectAll('*').remove();
|
|
53
|
-
const
|
|
53
|
+
const plotDataAttr = 'data-plot-x';
|
|
54
54
|
let plotContainer = null;
|
|
55
55
|
if (plotRef === null || plotRef === void 0 ? void 0 : plotRef.current) {
|
|
56
56
|
plotContainer = select(plotRef.current);
|
|
57
|
-
plotContainer.selectAll(
|
|
57
|
+
plotContainer.selectAll(`[${plotDataAttr}]`).remove();
|
|
58
58
|
}
|
|
59
59
|
if (!axis.visible) {
|
|
60
60
|
return;
|
|
@@ -116,12 +116,13 @@ export const AxisX = React.memo(function AxisX(props) {
|
|
|
116
116
|
}
|
|
117
117
|
// add plot bands
|
|
118
118
|
if (plotContainer && axis.plotBands.length > 0) {
|
|
119
|
-
const
|
|
119
|
+
const plotBandDataAttr = 'plot-x-band';
|
|
120
120
|
const plotBandsSelection = plotContainer
|
|
121
|
-
.selectAll(
|
|
121
|
+
.selectAll(`[${plotBandDataAttr}]`)
|
|
122
122
|
.data(axis.plotBands)
|
|
123
123
|
.join('g')
|
|
124
|
-
.attr(
|
|
124
|
+
.attr(plotDataAttr, 1)
|
|
125
|
+
.attr(plotBandDataAttr, 1);
|
|
125
126
|
plotBandsSelection
|
|
126
127
|
.append('rect')
|
|
127
128
|
.attr('x', (band) => {
|
|
@@ -153,12 +154,13 @@ export const AxisX = React.memo(function AxisX(props) {
|
|
|
153
154
|
}
|
|
154
155
|
// add plot lines
|
|
155
156
|
if (plotContainer && axis.plotLines.length > 0) {
|
|
156
|
-
const
|
|
157
|
+
const plotLineDataAttr = 'plot-x-line';
|
|
157
158
|
const plotLinesSelection = plotContainer
|
|
158
|
-
.selectAll(
|
|
159
|
+
.selectAll(`[${plotLineDataAttr}]`)
|
|
159
160
|
.data(axis.plotLines)
|
|
160
161
|
.join('g')
|
|
161
|
-
.attr(
|
|
162
|
+
.attr(plotDataAttr, 1)
|
|
163
|
+
.attr(plotLineDataAttr, 1);
|
|
162
164
|
const lineGenerator = line();
|
|
163
165
|
plotLinesSelection
|
|
164
166
|
.append('path')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { axisLeft, axisRight, line, select } from 'd3';
|
|
3
|
-
import { block, calculateCos, calculateSin, formatAxisTickLabel, getAxisHeight, getAxisTitleRows, getBandsPosition, getClosestPointsRange, getLineDashArray, getScaleTicks, getTicksCount, handleOverflowingText, parseTransformStyle, setEllipsisForOverflowTexts, wrapText, } from '../../utils';
|
|
3
|
+
import { block, calculateCos, calculateSin, formatAxisTickLabel, getAxisHeight, getAxisPlotsPosition, getAxisTitleRows, getBandsPosition, getClosestPointsRange, getLineDashArray, getScaleTicks, getTicksCount, handleOverflowingText, parseTransformStyle, setEllipsisForOverflowTexts, wrapText, } from '../../utils';
|
|
4
4
|
import './styles.css';
|
|
5
5
|
const b = block('axis');
|
|
6
6
|
function transformLabel(args) {
|
|
@@ -97,39 +97,17 @@ export const AxisY = (props) => {
|
|
|
97
97
|
const svgElement = select(ref.current);
|
|
98
98
|
svgElement.selectAll('*').remove();
|
|
99
99
|
let plotContainer = null;
|
|
100
|
-
const
|
|
100
|
+
const plotDataAttr = 'data-plot-y';
|
|
101
101
|
if (plotRef === null || plotRef === void 0 ? void 0 : plotRef.current) {
|
|
102
102
|
plotContainer = select(plotRef.current);
|
|
103
|
-
plotContainer.selectAll(
|
|
103
|
+
plotContainer.selectAll(`[${plotDataAttr}]`).remove();
|
|
104
104
|
}
|
|
105
|
-
const getAxisPosition = (axis) => {
|
|
106
|
-
var _a;
|
|
107
|
-
const top = ((_a = split.plots[axis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
|
|
108
|
-
if (axis.position === 'left') {
|
|
109
|
-
return `translate(0, ${top}px)`;
|
|
110
|
-
}
|
|
111
|
-
return `translate(${width}px, 0)`;
|
|
112
|
-
};
|
|
113
|
-
const plotLines = axes.reduce((acc, axis) => {
|
|
114
|
-
if (axis.plotLines.length) {
|
|
115
|
-
acc.push(...axis.plotLines.map((plotLine) => {
|
|
116
|
-
return Object.assign(Object.assign({}, plotLine), { transform: getAxisPosition(axis) });
|
|
117
|
-
}));
|
|
118
|
-
}
|
|
119
|
-
return acc;
|
|
120
|
-
}, []);
|
|
121
|
-
const plotBands = axes.reduce((acc, axis) => {
|
|
122
|
-
if (axis.plotBands.length) {
|
|
123
|
-
acc.push(...axis.plotBands.map((plotBand) => (Object.assign(Object.assign({}, plotBand), { transform: getAxisPosition(axis) }))));
|
|
124
|
-
}
|
|
125
|
-
return acc;
|
|
126
|
-
}, []);
|
|
127
105
|
const axisSelection = svgElement
|
|
128
106
|
.selectAll('axis')
|
|
129
107
|
.data(axes)
|
|
130
108
|
.join('g')
|
|
131
109
|
.attr('class', b())
|
|
132
|
-
.style('transform', (d) =>
|
|
110
|
+
.style('transform', (d) => getAxisPlotsPosition(d, split, width));
|
|
133
111
|
axisSelection.each((d, index, node) => {
|
|
134
112
|
const seriesScale = scale[index];
|
|
135
113
|
const axisItem = select(node[index]);
|
|
@@ -197,13 +175,14 @@ export const AxisY = (props) => {
|
|
|
197
175
|
.remove();
|
|
198
176
|
}
|
|
199
177
|
if (plotContainer && d.plotBands.length > 0) {
|
|
200
|
-
const
|
|
178
|
+
const plotBandDataAttr = `data-plot-y-band-${index}`;
|
|
201
179
|
const plotBandsSelection = plotContainer
|
|
202
|
-
.selectAll(
|
|
203
|
-
.data(plotBands)
|
|
180
|
+
.selectAll(`[${plotBandDataAttr}]`)
|
|
181
|
+
.data(d.plotBands)
|
|
204
182
|
.join('g')
|
|
205
|
-
.attr(
|
|
206
|
-
.
|
|
183
|
+
.attr(plotDataAttr, 1)
|
|
184
|
+
.attr(plotBandDataAttr, 1)
|
|
185
|
+
.style('transform', getAxisPlotsPosition(d, split));
|
|
207
186
|
plotBandsSelection
|
|
208
187
|
.append('rect')
|
|
209
188
|
.attr('x', 0)
|
|
@@ -234,13 +213,14 @@ export const AxisY = (props) => {
|
|
|
234
213
|
});
|
|
235
214
|
}
|
|
236
215
|
if (plotContainer && d.plotLines.length > 0) {
|
|
237
|
-
const
|
|
216
|
+
const plotLineDataAttr = `data-plot-y-line-${index}`;
|
|
238
217
|
const plotLinesSelection = plotContainer
|
|
239
|
-
.selectAll(
|
|
240
|
-
.data(plotLines)
|
|
218
|
+
.selectAll(`[${plotLineDataAttr}]`)
|
|
219
|
+
.data(d.plotLines)
|
|
241
220
|
.join('g')
|
|
242
|
-
.attr(
|
|
243
|
-
.
|
|
221
|
+
.attr(plotDataAttr, 1)
|
|
222
|
+
.attr(plotLineDataAttr, 1)
|
|
223
|
+
.style('transform', getAxisPlotsPosition(d, split));
|
|
244
224
|
plotLinesSelection
|
|
245
225
|
.append('path')
|
|
246
226
|
.attr('d', (plotLine) => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { useCrosshair } from '../../hooks';
|
|
2
3
|
import { EventType, block, getDispatcher } from '../../utils';
|
|
3
4
|
import { AxisX, AxisY } from '../Axis';
|
|
4
5
|
import { Legend } from '../Legend';
|
|
@@ -39,6 +40,19 @@ export const ChartInner = (props) => {
|
|
|
39
40
|
});
|
|
40
41
|
const clickHandler = (_b = (_a = data.chart) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.click;
|
|
41
42
|
const pointerMoveHandler = (_d = (_c = data.chart) === null || _c === void 0 ? void 0 : _c.events) === null || _d === void 0 ? void 0 : _d.pointermove;
|
|
43
|
+
useCrosshair({
|
|
44
|
+
split: preparedSplit,
|
|
45
|
+
plotElement: plotRef.current,
|
|
46
|
+
boundsOffsetLeft,
|
|
47
|
+
boundsOffsetTop,
|
|
48
|
+
width: boundsWidth,
|
|
49
|
+
height: boundsHeight,
|
|
50
|
+
xAxis,
|
|
51
|
+
yAxes: yAxis,
|
|
52
|
+
yScale,
|
|
53
|
+
xScale,
|
|
54
|
+
dispatcher,
|
|
55
|
+
});
|
|
42
56
|
React.useEffect(() => {
|
|
43
57
|
if (clickHandler) {
|
|
44
58
|
dispatcher.on(EventType.CLICK_CHART, clickHandler);
|
|
@@ -71,7 +85,7 @@ export const ChartInner = (props) => {
|
|
|
71
85
|
React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
|
|
72
86
|
React.createElement(AxisX, { leftmostLimit: svgXPos, axis: xAxis, width: boundsWidth, height: boundsHeight, scale: xScale, split: preparedSplit, plotRef: plotRef })))),
|
|
73
87
|
shapes),
|
|
74
|
-
preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick, onUpdate: unpinTooltip }))),
|
|
88
|
+
preparedLegend.enabled && (React.createElement(Legend, { chartSeries: preparedSeries, boundsWidth: boundsWidth, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick, onUpdate: unpinTooltip, htmlLayout: htmlLayerRef.current }))),
|
|
75
89
|
React.createElement("div", { className: b('html-layer'), ref: htmlLayerRef, style: {
|
|
76
90
|
'--g-html-layout-transform': `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
|
|
77
91
|
} }),
|
|
@@ -20,8 +20,10 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
20
20
|
top: number;
|
|
21
21
|
};
|
|
22
22
|
pagination: {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
pages: {
|
|
24
|
+
start: number;
|
|
25
|
+
end: number;
|
|
26
|
+
}[];
|
|
25
27
|
} | undefined;
|
|
26
28
|
};
|
|
27
29
|
legendItems: import("../../hooks").LegendItem[][];
|
|
@@ -56,9 +56,12 @@ export function useChartInnerProps(props) {
|
|
|
56
56
|
htmlLayout,
|
|
57
57
|
});
|
|
58
58
|
const boundsOffsetTop = chart.margin.top;
|
|
59
|
-
// We need to calculate the width of each axis because the first axis can be hidden
|
|
59
|
+
// We need to calculate the width of each left axis because the first axis can be hidden
|
|
60
60
|
const boundsOffsetLeft = chart.margin.left +
|
|
61
61
|
yAxis.reduce((acc, axis) => {
|
|
62
|
+
if (axis.position !== 'left') {
|
|
63
|
+
return acc;
|
|
64
|
+
}
|
|
62
65
|
const axisWidth = getYAxisWidth(axis);
|
|
63
66
|
if (acc < axisWidth) {
|
|
64
67
|
acc = axisWidth;
|