@gravity-ui/charts 1.35.2 → 1.36.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/AxisX/AxisX.js +15 -8
- package/dist/cjs/components/AxisX/prepare-axis-data.js +12 -0
- package/dist/cjs/components/AxisX/styles.css +7 -2
- package/dist/cjs/components/AxisX/types.d.ts +5 -0
- package/dist/cjs/components/AxisY/AxisY.js +13 -7
- package/dist/cjs/components/AxisY/prepare-axis-data.js +14 -0
- package/dist/cjs/components/AxisY/styles.css +7 -2
- package/dist/cjs/components/AxisY/types.d.ts +5 -0
- package/dist/cjs/components/ChartInner/index.js +2 -0
- package/dist/cjs/components/ChartInner/styles.css +1 -0
- package/dist/cjs/components/ChartInner/useChartInnerHandlers.d.ts +3 -0
- package/dist/cjs/components/ChartInner/useChartInnerHandlers.js +26 -3
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +1 -0
- package/dist/cjs/components/Tooltip/ChartTooltipContent.d.ts +3 -1
- package/dist/cjs/components/Tooltip/ChartTooltipContent.js +2 -2
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/Row.js +1 -1
- package/dist/cjs/components/Tooltip/index.js +5 -2
- package/dist/cjs/components/Tooltip/styles.css +7 -0
- package/dist/cjs/constants/defaults/axis.d.ts +4 -0
- package/dist/cjs/constants/defaults/axis.js +4 -0
- package/dist/cjs/hooks/useAxis/types.d.ts +8 -1
- package/dist/cjs/hooks/useAxis/x-axis.js +9 -3
- package/dist/cjs/hooks/useAxis/y-axis.js +7 -1
- package/dist/cjs/hooks/useNormalizedOriginalData/index.d.ts +1 -0
- package/dist/cjs/hooks/useTooltip/index.d.ts +3 -1
- package/dist/cjs/hooks/useTooltip/index.js +7 -3
- package/dist/cjs/types/chart/axis.d.ts +16 -1
- package/dist/cjs/types/chart/tooltip.d.ts +5 -1
- package/dist/cjs/utils/chart/format.js +17 -2
- package/dist/cjs/utils/chart/get-hovered-plots.d.ts +14 -0
- package/dist/cjs/utils/chart/get-hovered-plots.js +67 -0
- package/dist/cjs/utils/chart/time.d.ts +1 -0
- package/dist/cjs/utils/chart/time.js +9 -0
- package/dist/esm/components/AxisX/AxisX.js +15 -8
- package/dist/esm/components/AxisX/prepare-axis-data.js +12 -0
- package/dist/esm/components/AxisX/styles.css +7 -2
- package/dist/esm/components/AxisX/types.d.ts +5 -0
- package/dist/esm/components/AxisY/AxisY.js +13 -7
- package/dist/esm/components/AxisY/prepare-axis-data.js +14 -0
- package/dist/esm/components/AxisY/styles.css +7 -2
- package/dist/esm/components/AxisY/types.d.ts +5 -0
- package/dist/esm/components/ChartInner/index.js +2 -0
- package/dist/esm/components/ChartInner/styles.css +1 -0
- package/dist/esm/components/ChartInner/useChartInnerHandlers.d.ts +3 -0
- package/dist/esm/components/ChartInner/useChartInnerHandlers.js +26 -3
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +1 -0
- package/dist/esm/components/Tooltip/ChartTooltipContent.d.ts +3 -1
- package/dist/esm/components/Tooltip/ChartTooltipContent.js +2 -2
- package/dist/esm/components/Tooltip/DefaultTooltipContent/Row.js +1 -1
- package/dist/esm/components/Tooltip/index.js +5 -2
- package/dist/esm/components/Tooltip/styles.css +7 -0
- package/dist/esm/constants/defaults/axis.d.ts +4 -0
- package/dist/esm/constants/defaults/axis.js +4 -0
- package/dist/esm/hooks/useAxis/types.d.ts +8 -1
- package/dist/esm/hooks/useAxis/x-axis.js +9 -3
- package/dist/esm/hooks/useAxis/y-axis.js +7 -1
- package/dist/esm/hooks/useNormalizedOriginalData/index.d.ts +1 -0
- package/dist/esm/hooks/useTooltip/index.d.ts +3 -1
- package/dist/esm/hooks/useTooltip/index.js +7 -3
- package/dist/esm/types/chart/axis.d.ts +16 -1
- package/dist/esm/types/chart/tooltip.d.ts +5 -1
- package/dist/esm/utils/chart/format.js +17 -2
- package/dist/esm/utils/chart/get-hovered-plots.d.ts +14 -0
- package/dist/esm/utils/chart/get-hovered-plots.js +67 -0
- package/dist/esm/utils/chart/time.d.ts +1 -0
- package/dist/esm/utils/chart/time.js +9 -0
- package/package.json +1 -1
|
@@ -7,7 +7,10 @@ import './styles.css';
|
|
|
7
7
|
const b = block('tooltip');
|
|
8
8
|
export const Tooltip = (props) => {
|
|
9
9
|
const { tooltip, xAxis, yAxis, svgContainer, dispatcher, tooltipPinned, onOutsideClick } = props;
|
|
10
|
-
const { hovered, pointerPosition } = useTooltip({
|
|
10
|
+
const { hovered, hoveredPlotLines, hoveredPlotBands, pointerPosition } = useTooltip({
|
|
11
|
+
dispatcher,
|
|
12
|
+
tooltip,
|
|
13
|
+
});
|
|
11
14
|
const containerRect = (svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) || { left: 0, top: 0 };
|
|
12
15
|
const left = ((pointerPosition === null || pointerPosition === void 0 ? void 0 : pointerPosition[0]) || 0) + containerRect.left;
|
|
13
16
|
const top = ((pointerPosition === null || pointerPosition === void 0 ? void 0 : pointerPosition[1]) || 0) + containerRect.top;
|
|
@@ -23,5 +26,5 @@ export const Tooltip = (props) => {
|
|
|
23
26
|
}, [left, top]);
|
|
24
27
|
return (hovered === null || hovered === void 0 ? void 0 : hovered.length) ? (React.createElement(Popup, { anchorElement: anchor, className: b({ pinned: tooltipPinned }), disableTransition: true, floatingStyles: tooltipPinned ? undefined : { pointerEvents: 'none' }, offset: { mainAxis: 20 }, onOpenChange: tooltipPinned ? handleOnOpenChange : undefined, open: true, placement: ['right', 'left', 'top', 'bottom'] },
|
|
25
28
|
React.createElement("div", { className: b('popup-content') },
|
|
26
|
-
React.createElement(ChartTooltipContent, { hovered: hovered, pinned: tooltipPinned, renderer: tooltip.renderer, rowRenderer: tooltip.rowRenderer, totals: tooltip.totals, valueFormat: tooltip.valueFormat, headerFormat: tooltip.headerFormat, xAxis: xAxis, yAxis: yAxis, qa: tooltip.qa })))) : null;
|
|
29
|
+
React.createElement(ChartTooltipContent, { hovered: hovered, hoveredPlotLines: hoveredPlotLines, hoveredPlotBands: hoveredPlotBands, pinned: tooltipPinned, renderer: tooltip.renderer, rowRenderer: tooltip.rowRenderer, totals: tooltip.totals, valueFormat: tooltip.valueFormat, headerFormat: tooltip.headerFormat, xAxis: xAxis, yAxis: yAxis, qa: tooltip.qa })))) : null;
|
|
27
30
|
};
|
|
@@ -45,14 +45,21 @@
|
|
|
45
45
|
font-weight: 600;
|
|
46
46
|
background-color: var(--g-color-base-info-medium);
|
|
47
47
|
}
|
|
48
|
+
.gcharts-tooltip__content-row-label {
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
white-space: nowrap;
|
|
51
|
+
text-overflow: ellipsis;
|
|
52
|
+
}
|
|
48
53
|
.gcharts-tooltip__content-row-color {
|
|
49
54
|
display: inline-block;
|
|
55
|
+
flex-shrink: 0;
|
|
50
56
|
width: 16px;
|
|
51
57
|
height: 8px;
|
|
52
58
|
border-radius: 2px;
|
|
53
59
|
background-color: #dddddd;
|
|
54
60
|
}
|
|
55
61
|
.gcharts-tooltip__content-row-value {
|
|
62
|
+
flex-shrink: 0;
|
|
56
63
|
margin-inline-start: auto;
|
|
57
64
|
}
|
|
58
65
|
.gcharts-tooltip__content-row-totals {
|
|
@@ -12,5 +12,9 @@ type AxisCrosshairDefaults = Required<AxisCrosshair>;
|
|
|
12
12
|
export declare const axisCrosshairDefaults: AxisCrosshairDefaults;
|
|
13
13
|
export declare const xAxisTitleDefaults: AxisTitleDefaults;
|
|
14
14
|
export declare const yAxisTitleDefaults: AxisTitleDefaults;
|
|
15
|
+
export declare const axisTickMarksDefaults: {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
length: number;
|
|
18
|
+
};
|
|
15
19
|
export declare const DEFAULT_AXIS_TYPE: ChartAxisType;
|
|
16
20
|
export {};
|
|
@@ -25,4 +25,8 @@ export const axisCrosshairDefaults = {
|
|
|
25
25
|
};
|
|
26
26
|
export const xAxisTitleDefaults = Object.assign(Object.assign({}, axisTitleDefaults), { margin: 4 });
|
|
27
27
|
export const yAxisTitleDefaults = Object.assign(Object.assign({}, axisTitleDefaults), { margin: 8 });
|
|
28
|
+
export const axisTickMarksDefaults = {
|
|
29
|
+
enabled: false,
|
|
30
|
+
length: 6,
|
|
31
|
+
};
|
|
28
32
|
export const DEFAULT_AXIS_TYPE = 'linear';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import type { DashStyle } from '../../constants';
|
|
3
|
-
import type { AxisCrosshair, AxisPlotBand, BaseTextStyle, ChartAxis, ChartAxisLabels, ChartAxisRangeSlider, ChartAxisTitleAlignment, ChartAxisType, DeepRequired, PlotLayerPlacement } from '../../types';
|
|
3
|
+
import type { AxisCrosshair, AxisPlotBand, BaseTextStyle, ChartAxis, ChartAxisLabels, ChartAxisRangeSlider, ChartAxisTitleAlignment, ChartAxisType, DeepRequired, MeaningfulAny, PlotLayerPlacement } from '../../types';
|
|
4
4
|
type PreparedAxisLabels = Omit<ChartAxisLabels, 'enabled' | 'padding' | 'style' | 'autoRotation'> & Required<Pick<ChartAxisLabels, 'enabled' | 'padding' | 'margin' | 'rotation' | 'html'>> & {
|
|
5
5
|
style: BaseTextStyle;
|
|
6
6
|
rotation: number;
|
|
@@ -10,6 +10,7 @@ type PreparedAxisLabels = Omit<ChartAxisLabels, 'enabled' | 'padding' | 'style'
|
|
|
10
10
|
maxWidth: number;
|
|
11
11
|
};
|
|
12
12
|
export type PreparedAxisPlotBand = Required<AxisPlotBand> & {
|
|
13
|
+
custom?: MeaningfulAny;
|
|
13
14
|
label: {
|
|
14
15
|
text: string;
|
|
15
16
|
style: BaseTextStyle;
|
|
@@ -25,6 +26,7 @@ export type PreparedAxisPlotLine = {
|
|
|
25
26
|
dashStyle: DashStyle;
|
|
26
27
|
opacity: number;
|
|
27
28
|
layerPlacement: PlotLayerPlacement;
|
|
29
|
+
custom?: MeaningfulAny;
|
|
28
30
|
label: {
|
|
29
31
|
text: string;
|
|
30
32
|
style: BaseTextStyle;
|
|
@@ -35,6 +37,10 @@ export type PreparedAxisPlotLine = {
|
|
|
35
37
|
export type PreparedRangeSlider = DeepRequired<Omit<ChartAxisRangeSlider, 'defaultRange'>> & {
|
|
36
38
|
defaultRange?: ChartAxisRangeSlider['defaultRange'];
|
|
37
39
|
};
|
|
40
|
+
export type PreparedAxisTickMarks = {
|
|
41
|
+
enabled: boolean;
|
|
42
|
+
length: number;
|
|
43
|
+
};
|
|
38
44
|
type PreparedBaseAxis = Omit<ChartAxis, 'type' | 'labels' | 'plotLines' | 'plotBands'> & {
|
|
39
45
|
type: ChartAxisType;
|
|
40
46
|
labels: PreparedAxisLabels;
|
|
@@ -58,6 +64,7 @@ type PreparedBaseAxis = Omit<ChartAxis, 'type' | 'labels' | 'plotLines' | 'plotB
|
|
|
58
64
|
ticks: {
|
|
59
65
|
pixelInterval?: number;
|
|
60
66
|
};
|
|
67
|
+
tickMarks: PreparedAxisTickMarks;
|
|
61
68
|
position: 'left' | 'right' | 'top' | 'bottom';
|
|
62
69
|
plotIndex: number;
|
|
63
70
|
plotLines: PreparedAxisPlotLine[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
|
-
import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, SERIES_TYPE, axisCrosshairDefaults, axisLabelsDefaults, xAxisTitleDefaults, } from '../../constants';
|
|
3
|
-
import { calculateCos, calculateNumericProperty, formatAxisTickLabel, getDefaultDateFormat, getHorizontalHtmlTextHeight, getHorizontalSvgTextHeight, getLabelsSize, getMinSpaceBetween, getTextSizeFn, isAxisRelatedSeries, wrapText, } from '../../utils';
|
|
2
|
+
import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, SERIES_TYPE, axisCrosshairDefaults, axisLabelsDefaults, axisTickMarksDefaults, xAxisTitleDefaults, } from '../../constants';
|
|
3
|
+
import { TIME_UNITS, calculateCos, calculateNumericProperty, formatAxisTickLabel, getDefaultDateFormat, getHorizontalHtmlTextHeight, getHorizontalSvgTextHeight, getLabelsSize, getMinSpaceBetween, getTextSizeFn, isAxisRelatedSeries, wrapText, } from '../../utils';
|
|
4
4
|
import { getXAxisTickValues } from '../../utils/chart/axis/x-axis';
|
|
5
5
|
import { createXScale } from '../useAxisScales';
|
|
6
6
|
import { getPreparedRangeSlider } from './range-slider';
|
|
@@ -17,7 +17,7 @@ async function setLabelSettings({ axis, seriesData, width, axisLabels, }) {
|
|
|
17
17
|
const labelLineHeight = (await getTextSize('Tmp')).height;
|
|
18
18
|
const tickValues = getXAxisTickValues({ axis, scale, labelLineHeight, series: seriesData });
|
|
19
19
|
const tickStep = getMinSpaceBetween(tickValues, (d) => Number(d.value));
|
|
20
|
-
if (axis.type === 'datetime' && !(axisLabels === null || axisLabels === void 0 ? void 0 : axisLabels.dateFormat)) {
|
|
20
|
+
if (axis.type === 'datetime' && !(axisLabels === null || axisLabels === void 0 ? void 0 : axisLabels.dateFormat) && tickStep >= TIME_UNITS.day) {
|
|
21
21
|
axis.labels.dateFormat = getDefaultDateFormat(tickStep);
|
|
22
22
|
}
|
|
23
23
|
const labels = tickValues.map((tick) => formatAxisTickLabel({
|
|
@@ -141,6 +141,10 @@ export const getPreparedXAxis = async ({ xAxis, seriesData, width, boundsWidth,
|
|
|
141
141
|
})
|
|
142
142
|
: (_j = xAxis === null || xAxis === void 0 ? void 0 : xAxis.ticks) === null || _j === void 0 ? void 0 : _j.pixelInterval,
|
|
143
143
|
},
|
|
144
|
+
tickMarks: {
|
|
145
|
+
enabled: get(xAxis, 'tickMarks.enabled', axisTickMarksDefaults.enabled),
|
|
146
|
+
length: get(xAxis, 'tickMarks.length', axisTickMarksDefaults.length),
|
|
147
|
+
},
|
|
144
148
|
position: 'bottom',
|
|
145
149
|
plotIndex: 0,
|
|
146
150
|
plotLines: get(xAxis, 'plotLines', []).map((d) => ({
|
|
@@ -150,6 +154,7 @@ export const getPreparedXAxis = async ({ xAxis, seriesData, width, boundsWidth,
|
|
|
150
154
|
dashStyle: get(d, 'dashStyle', DASH_STYLE.Solid),
|
|
151
155
|
opacity: get(d, 'opacity', 1),
|
|
152
156
|
layerPlacement: get(d, 'layerPlacement', 'before'),
|
|
157
|
+
custom: d.custom,
|
|
153
158
|
label: prepareAxisPlotLabel(d),
|
|
154
159
|
})),
|
|
155
160
|
plotBands: get(xAxis, 'plotBands', []).map((d) => ({
|
|
@@ -158,6 +163,7 @@ export const getPreparedXAxis = async ({ xAxis, seriesData, width, boundsWidth,
|
|
|
158
163
|
from: get(d, 'from', 0),
|
|
159
164
|
to: get(d, 'to', 0),
|
|
160
165
|
layerPlacement: get(d, 'layerPlacement', 'before'),
|
|
166
|
+
custom: d.custom,
|
|
161
167
|
label: prepareAxisPlotLabel(d),
|
|
162
168
|
})),
|
|
163
169
|
crosshair: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import { getTickValues } from '../../components/AxisY/utils';
|
|
3
|
-
import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, DEFAULT_AXIS_TYPE, SERIES_TYPE, axisCrosshairDefaults, axisLabelsDefaults, yAxisTitleDefaults, } from '../../constants';
|
|
3
|
+
import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, DEFAULT_AXIS_TYPE, SERIES_TYPE, axisCrosshairDefaults, axisLabelsDefaults, axisTickMarksDefaults, yAxisTitleDefaults, } from '../../constants';
|
|
4
4
|
import { calculateNumericProperty, formatAxisTickLabel, getDefaultDateFormat, getDefaultMinYAxisValue, getHorizontalHtmlTextHeight, getHorizontalSvgTextHeight, getLabelsSize, getMinSpaceBetween, getTextSizeFn, isAxisRelatedSeries, shouldSyncAxisWithPrimary, wrapText, } from '../../utils';
|
|
5
5
|
import { createYScale } from '../useAxisScales';
|
|
6
6
|
import { prepareAxisPlotLabel } from './utils';
|
|
@@ -160,6 +160,10 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
160
160
|
})
|
|
161
161
|
: (_q = axisItem.ticks) === null || _q === void 0 ? void 0 : _q.pixelInterval,
|
|
162
162
|
},
|
|
163
|
+
tickMarks: {
|
|
164
|
+
enabled: get(axisItem, 'tickMarks.enabled', axisTickMarksDefaults.enabled),
|
|
165
|
+
length: get(axisItem, 'tickMarks.length', axisTickMarksDefaults.length),
|
|
166
|
+
},
|
|
163
167
|
position: axisPosition,
|
|
164
168
|
plotIndex: get(axisItem, 'plotIndex', 0),
|
|
165
169
|
plotLines: get(axisItem, 'plotLines', []).map((d) => ({
|
|
@@ -169,6 +173,7 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
169
173
|
dashStyle: get(d, 'dashStyle', DASH_STYLE.Solid),
|
|
170
174
|
opacity: get(d, 'opacity', 1),
|
|
171
175
|
layerPlacement: get(d, 'layerPlacement', 'before'),
|
|
176
|
+
custom: d.custom,
|
|
172
177
|
label: prepareAxisPlotLabel(d),
|
|
173
178
|
})),
|
|
174
179
|
plotBands: get(axisItem, 'plotBands', []).map((d) => ({
|
|
@@ -177,6 +182,7 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxi
|
|
|
177
182
|
from: get(d, 'from', 0),
|
|
178
183
|
to: get(d, 'to', 0),
|
|
179
184
|
layerPlacement: get(d, 'layerPlacement', 'before'),
|
|
185
|
+
custom: d.custom,
|
|
180
186
|
label: prepareAxisPlotLabel(d),
|
|
181
187
|
})),
|
|
182
188
|
crosshair: {
|
|
@@ -27,6 +27,7 @@ export declare function useNormalizedOriginalData(props: UseOriginalDataProps):
|
|
|
27
27
|
maxPadding?: number;
|
|
28
28
|
plotLines?: import("../..").AxisPlotLine[];
|
|
29
29
|
plotBands?: import("../..").AxisPlotBand[];
|
|
30
|
+
tickMarks?: import("../..").ChartAxisTickMarks;
|
|
30
31
|
visible?: boolean;
|
|
31
32
|
order?: "sortAsc" | "sortDesc" | "reverse";
|
|
32
33
|
startOnTick?: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Dispatch } from 'd3';
|
|
2
|
-
import type { PointPosition, TooltipDataChunk } from '../../types';
|
|
2
|
+
import type { AxisPlotBand, AxisPlotLine, PointPosition, TooltipDataChunk } from '../../types';
|
|
3
3
|
import type { PreparedTooltip } from '../useChartOptions/types';
|
|
4
4
|
type Args = {
|
|
5
5
|
dispatcher: Dispatch<object>;
|
|
@@ -7,6 +7,8 @@ type Args = {
|
|
|
7
7
|
};
|
|
8
8
|
export declare const useTooltip: ({ dispatcher, tooltip }: Args) => {
|
|
9
9
|
hovered: TooltipDataChunk[] | undefined;
|
|
10
|
+
hoveredPlotLines: AxisPlotLine[] | undefined;
|
|
11
|
+
hoveredPlotBands: AxisPlotBand[] | undefined;
|
|
10
12
|
pointerPosition: PointPosition | undefined;
|
|
11
13
|
};
|
|
12
14
|
export {};
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import isEqual from 'lodash/isEqual';
|
|
3
3
|
export const useTooltip = ({ dispatcher, tooltip }) => {
|
|
4
|
-
const [{ hovered, pointerPosition }, setTooltipState] = React.useState({});
|
|
4
|
+
const [{ hovered, hoveredPlotLines, hoveredPlotBands, pointerPosition }, setTooltipState] = React.useState({});
|
|
5
5
|
const prevHovered = React.useRef(hovered);
|
|
6
6
|
React.useEffect(() => {
|
|
7
7
|
if (tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabled) {
|
|
8
|
-
dispatcher.on('hover-shape.tooltip', (nextHovered, nextPointerPosition) => {
|
|
8
|
+
dispatcher.on('hover-shape.tooltip', (nextHovered, nextPointerPosition, nextHoveredPlots) => {
|
|
9
9
|
const filteredNextHovered = nextHovered === null || nextHovered === void 0 ? void 0 : nextHovered.filter((item) => 'y' in item.data ? item.data.y !== null : true);
|
|
10
10
|
const isHoveredChanged = !isEqual(prevHovered.current, filteredNextHovered);
|
|
11
11
|
const newTooltipState = {
|
|
12
|
-
pointerPosition: nextPointerPosition,
|
|
13
12
|
hovered: isHoveredChanged ? filteredNextHovered : prevHovered.current,
|
|
13
|
+
hoveredPlotLines: nextHoveredPlots === null || nextHoveredPlots === void 0 ? void 0 : nextHoveredPlots.lines,
|
|
14
|
+
hoveredPlotBands: nextHoveredPlots === null || nextHoveredPlots === void 0 ? void 0 : nextHoveredPlots.bands,
|
|
15
|
+
pointerPosition: nextPointerPosition,
|
|
14
16
|
};
|
|
15
17
|
if (isHoveredChanged) {
|
|
16
18
|
prevHovered.current = filteredNextHovered;
|
|
@@ -26,6 +28,8 @@ export const useTooltip = ({ dispatcher, tooltip }) => {
|
|
|
26
28
|
}, [dispatcher, tooltip]);
|
|
27
29
|
return {
|
|
28
30
|
hovered,
|
|
31
|
+
hoveredPlotLines,
|
|
32
|
+
hoveredPlotBands,
|
|
29
33
|
pointerPosition,
|
|
30
34
|
};
|
|
31
35
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DurationInput } from '@gravity-ui/date-utils';
|
|
2
2
|
import type { AXIS_TYPE, DashStyle } from '../../constants';
|
|
3
3
|
import type { FormatNumberOptions } from '../formatter';
|
|
4
|
+
import type { MeaningfulAny } from '../misc';
|
|
4
5
|
import type { BaseTextStyle } from './base';
|
|
5
6
|
import type { ChartBrush } from './brush';
|
|
6
7
|
export type ChartAxisType = (typeof AXIS_TYPE)[keyof typeof AXIS_TYPE];
|
|
@@ -94,6 +95,16 @@ export interface ChartAxisTitle {
|
|
|
94
95
|
*/
|
|
95
96
|
maxRowCount?: number;
|
|
96
97
|
}
|
|
98
|
+
export interface ChartAxisTickMarks {
|
|
99
|
+
/** Enable or disable the tick marks on the axis.
|
|
100
|
+
* @default false
|
|
101
|
+
*/
|
|
102
|
+
enabled?: boolean;
|
|
103
|
+
/** The length of the tick marks in pixels (perpendicular to the axis).
|
|
104
|
+
* @default 6
|
|
105
|
+
*/
|
|
106
|
+
length?: number;
|
|
107
|
+
}
|
|
97
108
|
export interface ChartAxis {
|
|
98
109
|
categories?: string[];
|
|
99
110
|
/** Configure a crosshair that follows either the mouse pointer or the hovered point. */
|
|
@@ -161,6 +172,8 @@ export interface ChartAxis {
|
|
|
161
172
|
plotLines?: AxisPlotLine[];
|
|
162
173
|
/** An array of colored bands stretching across the plot area marking an interval on the axis. */
|
|
163
174
|
plotBands?: AxisPlotBand[];
|
|
175
|
+
/** Small perpendicular marks on the axis line at each tick position. */
|
|
176
|
+
tickMarks?: ChartAxisTickMarks;
|
|
164
177
|
/** Whether axis, including axis title, line, ticks and labels, should be visible. */
|
|
165
178
|
visible?: boolean;
|
|
166
179
|
/** Setting the order of the axis values. It is not applied by default.
|
|
@@ -226,6 +239,8 @@ export interface AxisPlot {
|
|
|
226
239
|
* It is assigned as a data-qa attribute to an element. */
|
|
227
240
|
qa?: string;
|
|
228
241
|
};
|
|
242
|
+
/** Custom data associated with the plot line/band, accessible in tooltip renderer args. */
|
|
243
|
+
custom?: MeaningfulAny;
|
|
229
244
|
}
|
|
230
245
|
export interface AxisPlotLine extends AxisPlot {
|
|
231
246
|
/** The position of the line in axis units. */
|
|
@@ -259,7 +274,7 @@ export interface AxisPlotBand extends AxisPlot {
|
|
|
259
274
|
*/
|
|
260
275
|
to: number | string | null;
|
|
261
276
|
}
|
|
262
|
-
export interface AxisCrosshair extends Omit<AxisPlotLine, 'value' | 'label'> {
|
|
277
|
+
export interface AxisCrosshair extends Omit<AxisPlotLine, 'value' | 'label' | 'custom'> {
|
|
263
278
|
/** Whether the crosshair should snap to the point or follow the pointer independent of points.
|
|
264
279
|
* @default true
|
|
265
280
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { TOOLTIP_TOTALS_BUILT_IN_AGGREGATION } from '../../constants';
|
|
2
2
|
import type { MeaningfulAny } from '../misc';
|
|
3
3
|
import type { AreaSeries, AreaSeriesData } from './area';
|
|
4
|
-
import type { ChartXAxis, ChartYAxis } from './axis';
|
|
4
|
+
import type { AxisPlotBand, AxisPlotLine, ChartXAxis, ChartYAxis } from './axis';
|
|
5
5
|
import type { BarXSeries, BarXSeriesData } from './bar-x';
|
|
6
6
|
import type { BarYSeries, BarYSeriesData } from './bar-y';
|
|
7
7
|
import type { CustomFormat, ValueFormat } from './base';
|
|
@@ -91,6 +91,10 @@ export type TooltipDataChunk<T = MeaningfulAny> = (TooltipDataChunkBarX<T> | Too
|
|
|
91
91
|
};
|
|
92
92
|
export interface ChartTooltipRendererArgs<T = MeaningfulAny> {
|
|
93
93
|
hovered: TooltipDataChunk<T>[];
|
|
94
|
+
/** Plot lines that intersect with the current pointer position. */
|
|
95
|
+
hoveredPlotLines?: AxisPlotLine[];
|
|
96
|
+
/** Plot bands that contain the current pointer position. */
|
|
97
|
+
hoveredPlotBands?: AxisPlotBand[];
|
|
94
98
|
xAxis?: ChartXAxis | null;
|
|
95
99
|
yAxis?: ChartYAxis;
|
|
96
100
|
}
|
|
@@ -2,7 +2,7 @@ import { dateTimeUtc } from '@gravity-ui/date-utils';
|
|
|
2
2
|
import capitalize from 'lodash/capitalize';
|
|
3
3
|
import { DEFAULT_DATE_FORMAT } from '../../constants';
|
|
4
4
|
import { formatNumber, getDefaultUnit } from '../../libs';
|
|
5
|
-
import { getDefaultDateFormat } from './time';
|
|
5
|
+
import { DATETIME_LABEL_FORMATS, TIME_UNITS, getDefaultDateFormat, getDefaultTimeOnlyFormat, } from './time';
|
|
6
6
|
const LETTER_MOUNTH_AT_START_FORMAT_REGEXP = /^M{3,}/;
|
|
7
7
|
function getFormattedDate(args) {
|
|
8
8
|
const { value, format = DEFAULT_DATE_FORMAT } = args;
|
|
@@ -43,7 +43,22 @@ export function formatAxisTickLabel(args) {
|
|
|
43
43
|
}
|
|
44
44
|
case 'datetime': {
|
|
45
45
|
const date = value;
|
|
46
|
-
|
|
46
|
+
let format;
|
|
47
|
+
if (axis.labels.dateFormat) {
|
|
48
|
+
format = axis.labels.dateFormat;
|
|
49
|
+
}
|
|
50
|
+
else if (step !== undefined && step < TIME_UNITS.day) {
|
|
51
|
+
const d = dateTimeUtc({ input: date });
|
|
52
|
+
const isMidnight = d.isValid() &&
|
|
53
|
+
d.hour() === 0 &&
|
|
54
|
+
d.minute() === 0 &&
|
|
55
|
+
d.second() === 0 &&
|
|
56
|
+
d.millisecond() === 0;
|
|
57
|
+
format = isMidnight ? DATETIME_LABEL_FORMATS.day : getDefaultTimeOnlyFormat(step);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
format = getDefaultDateFormat(step);
|
|
61
|
+
}
|
|
47
62
|
return getFormattedDate({ value: date, format });
|
|
48
63
|
}
|
|
49
64
|
case 'linear':
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PreparedXAxis, PreparedYAxis } from '../../hooks';
|
|
2
|
+
import type { ChartScale } from '../../hooks/useAxisScales/types';
|
|
3
|
+
import type { AxisPlotBand, AxisPlotLine } from '../../types';
|
|
4
|
+
export declare function getHoveredPlots(args: {
|
|
5
|
+
pointerX: number;
|
|
6
|
+
pointerY: number;
|
|
7
|
+
xAxis?: PreparedXAxis | null;
|
|
8
|
+
yAxis: PreparedYAxis[];
|
|
9
|
+
xScale?: ChartScale;
|
|
10
|
+
yScale?: (ChartScale | undefined)[];
|
|
11
|
+
}): {
|
|
12
|
+
plotLines: AxisPlotLine[];
|
|
13
|
+
plotBands: AxisPlotBand[];
|
|
14
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { getBandsPosition, isBandScale } from './axis/common';
|
|
2
|
+
const PLOT_LINE_HIT_THRESHOLD_PX = 4;
|
|
3
|
+
function getHoveredAxisPlotBands(args) {
|
|
4
|
+
const { pointerPx, plotBands, scale, axis } = args;
|
|
5
|
+
const axisScale = scale;
|
|
6
|
+
const result = [];
|
|
7
|
+
for (const band of plotBands) {
|
|
8
|
+
const { from, to } = getBandsPosition({ band, axisScale, axis });
|
|
9
|
+
const startPx = Math.min(from, to);
|
|
10
|
+
const endPx = Math.max(from, to);
|
|
11
|
+
if (pointerPx >= startPx && pointerPx <= endPx) {
|
|
12
|
+
result.push(band);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
function getHoveredAxisPlotLines(args) {
|
|
18
|
+
const { pointerPx, plotLines, scale } = args;
|
|
19
|
+
const result = [];
|
|
20
|
+
if (isBandScale(scale)) {
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
for (const line of plotLines) {
|
|
24
|
+
const linePx = Number(scale(line.value));
|
|
25
|
+
if (Math.abs(pointerPx - linePx) <= PLOT_LINE_HIT_THRESHOLD_PX + line.width / 2) {
|
|
26
|
+
result.push(line);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
export function getHoveredPlots(args) {
|
|
32
|
+
const { pointerX, pointerY, xAxis, yAxis, xScale, yScale } = args;
|
|
33
|
+
const plotLines = [];
|
|
34
|
+
const plotBands = [];
|
|
35
|
+
if (xAxis && xScale) {
|
|
36
|
+
plotBands.push(...getHoveredAxisPlotBands({
|
|
37
|
+
pointerPx: pointerX,
|
|
38
|
+
plotBands: xAxis.plotBands,
|
|
39
|
+
scale: xScale,
|
|
40
|
+
axis: 'x',
|
|
41
|
+
}));
|
|
42
|
+
plotLines.push(...getHoveredAxisPlotLines({
|
|
43
|
+
pointerPx: pointerX,
|
|
44
|
+
plotLines: xAxis.plotLines,
|
|
45
|
+
scale: xScale,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
for (let i = 0; i < yAxis.length; i++) {
|
|
49
|
+
const yAxisItem = yAxis[i];
|
|
50
|
+
const yScaleItem = yScale === null || yScale === void 0 ? void 0 : yScale[i];
|
|
51
|
+
if (!yAxisItem || !yScaleItem) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
plotBands.push(...getHoveredAxisPlotBands({
|
|
55
|
+
pointerPx: pointerY,
|
|
56
|
+
plotBands: yAxisItem.plotBands,
|
|
57
|
+
scale: yScaleItem,
|
|
58
|
+
axis: 'y',
|
|
59
|
+
}));
|
|
60
|
+
plotLines.push(...getHoveredAxisPlotLines({
|
|
61
|
+
pointerPx: pointerY,
|
|
62
|
+
plotLines: yAxisItem.plotLines,
|
|
63
|
+
scale: yScaleItem,
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
return { plotLines, plotBands };
|
|
67
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export declare const TIME_UNITS: Record<string, number>;
|
|
2
2
|
export declare const DATETIME_LABEL_FORMATS: Record<keyof typeof TIME_UNITS, string>;
|
|
3
3
|
export declare function getDefaultDateFormat(range?: number): string;
|
|
4
|
+
export declare function getDefaultTimeOnlyFormat(step: number): string;
|
|
@@ -32,3 +32,12 @@ export function getDefaultDateFormat(range) {
|
|
|
32
32
|
}
|
|
33
33
|
return DATETIME_LABEL_FORMATS.day;
|
|
34
34
|
}
|
|
35
|
+
export function getDefaultTimeOnlyFormat(step) {
|
|
36
|
+
if (step < TIME_UNITS.second) {
|
|
37
|
+
return 'HH:mm:ss.SSS';
|
|
38
|
+
}
|
|
39
|
+
if (step < TIME_UNITS.minute) {
|
|
40
|
+
return 'HH:mm:ss';
|
|
41
|
+
}
|
|
42
|
+
return 'HH:mm';
|
|
43
|
+
}
|