@gravity-ui/charts 1.10.2 → 1.11.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/ChartInner/index.js +8 -3
- package/dist/cjs/components/ChartInner/useChartInnerHandlers.d.ts +2 -1
- package/dist/cjs/components/ChartInner/useChartInnerHandlers.js +1 -5
- package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +2 -0
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +9 -3
- package/dist/cjs/components/ChartInner/utils.d.ts +1 -1
- package/dist/cjs/components/ChartInner/utils.js +3 -3
- package/dist/cjs/hooks/hooks-utils/zoom.d.ts +1 -1
- package/dist/cjs/hooks/hooks-utils/zoom.js +2 -2
- package/dist/cjs/hooks/useAxisScales/index.js +49 -18
- package/dist/cjs/hooks/useChartOptions/x-axis.js +3 -14
- package/dist/cjs/hooks/useChartOptions/y-axis.js +5 -24
- package/dist/cjs/hooks/useShapes/area/index.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/area/index.js +13 -9
- package/dist/cjs/hooks/useShapes/area/prepare-data.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/area/prepare-data.js +2 -1
- package/dist/cjs/hooks/useShapes/area/types.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/bar-x/index.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/bar-x/index.js +2 -2
- package/dist/cjs/hooks/useShapes/bar-y/index.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/bar-y/index.js +2 -2
- package/dist/cjs/hooks/useShapes/index.d.ts +2 -0
- package/dist/cjs/hooks/useShapes/index.js +11 -6
- package/dist/cjs/hooks/useShapes/line/index.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/line/index.js +16 -12
- package/dist/cjs/hooks/useShapes/line/prepare-data.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/line/prepare-data.js +2 -1
- package/dist/cjs/hooks/useShapes/line/types.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/marker.js +6 -0
- package/dist/cjs/hooks/useShapes/scatter/prepare-data.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/scatter/prepare-data.js +6 -3
- package/dist/cjs/hooks/useShapes/scatter/types.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/waterfall/index.d.ts +1 -0
- package/dist/cjs/hooks/useShapes/waterfall/index.js +2 -2
- package/dist/cjs/hooks/useZoom/index.js +1 -1
- package/dist/cjs/hooks/useZoom/utils.d.ts +1 -1
- package/dist/cjs/hooks/useZoom/utils.js +3 -3
- package/dist/cjs/types/chart/axis.d.ts +4 -5
- package/dist/cjs/utils/chart/axis-generators/bottom.js +3 -1
- package/dist/cjs/utils/chart/index.d.ts +2 -0
- package/dist/cjs/utils/chart/index.js +31 -0
- package/dist/esm/components/ChartInner/index.js +8 -3
- package/dist/esm/components/ChartInner/useChartInnerHandlers.d.ts +2 -1
- package/dist/esm/components/ChartInner/useChartInnerHandlers.js +1 -5
- package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +2 -0
- package/dist/esm/components/ChartInner/useChartInnerProps.js +9 -3
- package/dist/esm/components/ChartInner/utils.d.ts +1 -1
- package/dist/esm/components/ChartInner/utils.js +3 -3
- package/dist/esm/hooks/hooks-utils/zoom.d.ts +1 -1
- package/dist/esm/hooks/hooks-utils/zoom.js +2 -2
- package/dist/esm/hooks/useAxisScales/index.js +49 -18
- package/dist/esm/hooks/useChartOptions/x-axis.js +3 -14
- package/dist/esm/hooks/useChartOptions/y-axis.js +5 -24
- package/dist/esm/hooks/useShapes/area/index.d.ts +1 -0
- package/dist/esm/hooks/useShapes/area/index.js +13 -9
- package/dist/esm/hooks/useShapes/area/prepare-data.d.ts +1 -0
- package/dist/esm/hooks/useShapes/area/prepare-data.js +2 -1
- package/dist/esm/hooks/useShapes/area/types.d.ts +1 -0
- package/dist/esm/hooks/useShapes/bar-x/index.d.ts +1 -0
- package/dist/esm/hooks/useShapes/bar-x/index.js +2 -2
- package/dist/esm/hooks/useShapes/bar-y/index.d.ts +1 -0
- package/dist/esm/hooks/useShapes/bar-y/index.js +2 -2
- package/dist/esm/hooks/useShapes/index.d.ts +2 -0
- package/dist/esm/hooks/useShapes/index.js +11 -6
- package/dist/esm/hooks/useShapes/line/index.d.ts +1 -0
- package/dist/esm/hooks/useShapes/line/index.js +16 -12
- package/dist/esm/hooks/useShapes/line/prepare-data.d.ts +1 -0
- package/dist/esm/hooks/useShapes/line/prepare-data.js +2 -1
- package/dist/esm/hooks/useShapes/line/types.d.ts +1 -0
- package/dist/esm/hooks/useShapes/marker.js +6 -0
- package/dist/esm/hooks/useShapes/scatter/prepare-data.d.ts +1 -0
- package/dist/esm/hooks/useShapes/scatter/prepare-data.js +6 -3
- package/dist/esm/hooks/useShapes/scatter/types.d.ts +1 -0
- package/dist/esm/hooks/useShapes/waterfall/index.d.ts +1 -0
- package/dist/esm/hooks/useShapes/waterfall/index.js +2 -2
- package/dist/esm/hooks/useZoom/index.js +1 -1
- package/dist/esm/hooks/useZoom/utils.d.ts +1 -1
- package/dist/esm/hooks/useZoom/utils.js +3 -3
- package/dist/esm/types/chart/axis.d.ts +4 -5
- package/dist/esm/utils/chart/axis-generators/bottom.js +3 -1
- package/dist/esm/utils/chart/index.d.ts +2 -0
- package/dist/esm/utils/chart/index.js +31 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ArrowRotateLeft } from '@gravity-ui/icons';
|
|
3
|
-
import { Button, ButtonIcon } from '@gravity-ui/uikit';
|
|
3
|
+
import { Button, ButtonIcon, useUniqId } from '@gravity-ui/uikit';
|
|
4
4
|
import { useCrosshair } from '../../hooks';
|
|
5
5
|
import { EventType, block, getDispatcher } from '../../utils';
|
|
6
6
|
import { AxisX, AxisY } from '../Axis';
|
|
@@ -22,8 +22,9 @@ export const ChartInner = (props) => {
|
|
|
22
22
|
const plotBeforeRef = React.useRef(null);
|
|
23
23
|
const plotAfterRef = React.useRef(null);
|
|
24
24
|
const dispatcher = React.useMemo(() => getDispatcher(), []);
|
|
25
|
-
const
|
|
26
|
-
|
|
25
|
+
const clipPathId = useUniqId();
|
|
26
|
+
const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, handleZoomReset, isOutsideBounds, legendConfig, legendItems, preparedSeries, preparedSplit, preparedLegend, prevHeight, prevWidth, shapes, shapesData, title, tooltip, xAxis, xScale, yAxis, yScale, svgXPos, svgBottomPos, } = useChartInnerProps(Object.assign(Object.assign({}, props), { dispatcher,
|
|
27
|
+
htmlLayout, svgContainer: svgRef.current, plotNode: plotRef.current, clipPathId }));
|
|
27
28
|
const { tooltipPinned, togglePinTooltip, unpinTooltip } = useChartInnerState({
|
|
28
29
|
dispatcher,
|
|
29
30
|
tooltip,
|
|
@@ -42,6 +43,7 @@ export const ChartInner = (props) => {
|
|
|
42
43
|
xAxis,
|
|
43
44
|
yAxis,
|
|
44
45
|
tooltipThrottle: tooltip.throttle,
|
|
46
|
+
isOutsideBounds,
|
|
45
47
|
});
|
|
46
48
|
const clickHandler = (_b = (_a = data.chart) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.click;
|
|
47
49
|
const pointerMoveHandler = (_d = (_c = data.chart) === null || _c === void 0 ? void 0 : _c.events) === null || _d === void 0 ? void 0 : _d.pointermove;
|
|
@@ -83,6 +85,9 @@ export const ChartInner = (props) => {
|
|
|
83
85
|
// We use onPointerMove here because onMouseMove works incorrectly when the zoom setting is enabled:
|
|
84
86
|
// when starting to select an area, the tooltip remains in the position where the selection began
|
|
85
87
|
onPointerMove: throttledHandleMouseMove, onMouseLeave: handleMouseLeave, onTouchStart: throttledHandleTouchMove, onTouchMove: throttledHandleTouchMove, onClick: handleChartClick },
|
|
88
|
+
React.createElement("defs", null,
|
|
89
|
+
React.createElement("clipPath", { id: clipPathId },
|
|
90
|
+
React.createElement("rect", { x: 0, y: 0, width: boundsWidth, height: boundsHeight }))),
|
|
86
91
|
title && React.createElement(Title, Object.assign({}, title, { chartWidth: width })),
|
|
87
92
|
React.createElement("g", { transform: `translate(0, ${boundsOffsetTop})` }, preparedSplit.plots.map((plot, index) => {
|
|
88
93
|
return React.createElement(PlotTitle, { key: `plot-${index}`, title: plot.title });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import type React from 'react';
|
|
2
2
|
import type { Dispatch } from 'd3';
|
|
3
3
|
import type { PreparedAxis, ShapeData } from '../../hooks';
|
|
4
4
|
import type { useChartInnerState } from './useChartInnerState';
|
|
@@ -17,6 +17,7 @@ type Props = {
|
|
|
17
17
|
xAxis: PreparedAxis | null;
|
|
18
18
|
yAxis: PreparedAxis[];
|
|
19
19
|
tooltipThrottle: number;
|
|
20
|
+
isOutsideBounds: (x: number, y: number) => boolean;
|
|
20
21
|
};
|
|
21
22
|
export declare function useChartInnerHandlers(props: Props): {
|
|
22
23
|
handleChartClick: (event: React.MouseEvent<SVGSVGElement>) => void;
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { pointer } from 'd3';
|
|
3
2
|
import throttle from 'lodash/throttle';
|
|
4
3
|
import { IS_TOUCH_ENABLED } from '../../constants';
|
|
5
4
|
import { EventType } from '../../utils';
|
|
6
5
|
import { getClosestPoints } from '../../utils/chart/get-closest-data';
|
|
7
6
|
export function useChartInnerHandlers(props) {
|
|
8
|
-
const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, dispatcher, shapesData, svgContainer, togglePinTooltip, tooltipPinned, unpinTooltip, xAxis, yAxis, tooltipThrottle, } = props;
|
|
9
|
-
const isOutsideBounds = React.useCallback((x, y) => {
|
|
10
|
-
return x < 0 || x > boundsWidth || y < 0 || y > boundsHeight;
|
|
11
|
-
}, [boundsHeight, boundsWidth]);
|
|
7
|
+
const { boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, dispatcher, shapesData, svgContainer, togglePinTooltip, tooltipPinned, unpinTooltip, xAxis, yAxis, tooltipThrottle, isOutsideBounds, } = props;
|
|
12
8
|
const handleMove = ([pointerX, pointerY], event) => {
|
|
13
9
|
if (tooltipPinned) {
|
|
14
10
|
return;
|
|
@@ -7,6 +7,7 @@ type Props = ChartInnerProps & {
|
|
|
7
7
|
htmlLayout: HTMLElement | null;
|
|
8
8
|
svgContainer: SVGGElement | null;
|
|
9
9
|
plotNode: SVGGElement | null;
|
|
10
|
+
clipPathId: string;
|
|
10
11
|
};
|
|
11
12
|
export declare function useChartInnerProps(props: Props): {
|
|
12
13
|
svgBottomPos: number | undefined;
|
|
@@ -17,6 +18,7 @@ export declare function useChartInnerProps(props: Props): {
|
|
|
17
18
|
boundsWidth: number;
|
|
18
19
|
handleLegendItemClick: import("../../hooks").OnLegendItemClick;
|
|
19
20
|
handleZoomReset: (() => void) | undefined;
|
|
21
|
+
isOutsideBounds: (x: number, y: number) => boolean;
|
|
20
22
|
legendConfig: {
|
|
21
23
|
offset: {
|
|
22
24
|
left: number;
|
|
@@ -8,7 +8,7 @@ import { useZoom } from '../../hooks/useZoom';
|
|
|
8
8
|
import { hasAtLeastOneSeriesDataPerPlot } from './utils';
|
|
9
9
|
export function useChartInnerProps(props) {
|
|
10
10
|
var _a;
|
|
11
|
-
const { width, height, data, dispatcher, htmlLayout, svgContainer, plotNode } = props;
|
|
11
|
+
const { width, height, data, dispatcher, htmlLayout, svgContainer, plotNode, clipPathId } = props;
|
|
12
12
|
const prevWidth = usePrevious(width);
|
|
13
13
|
const prevHeight = usePrevious(height);
|
|
14
14
|
const [zoomState, setZoomState] = React.useState({});
|
|
@@ -17,7 +17,7 @@ export function useChartInnerProps(props) {
|
|
|
17
17
|
return getZoomedSeriesData({
|
|
18
18
|
seriesData: data.series.data,
|
|
19
19
|
xAxis: data.xAxis,
|
|
20
|
-
|
|
20
|
+
yAxes: data.yAxis,
|
|
21
21
|
zoomState,
|
|
22
22
|
});
|
|
23
23
|
}, [data.series.data, data.xAxis, data.yAxis, zoomState]);
|
|
@@ -60,6 +60,9 @@ export function useChartInnerProps(props) {
|
|
|
60
60
|
yAxis,
|
|
61
61
|
split: preparedSplit,
|
|
62
62
|
});
|
|
63
|
+
const isOutsideBounds = React.useCallback((x, y) => {
|
|
64
|
+
return x < 0 || x > boundsWidth || y < 0 || y > boundsHeight;
|
|
65
|
+
}, [boundsHeight, boundsWidth]);
|
|
63
66
|
const { shapes, shapesData } = useShapes({
|
|
64
67
|
boundsWidth,
|
|
65
68
|
boundsHeight,
|
|
@@ -72,12 +75,14 @@ export function useChartInnerProps(props) {
|
|
|
72
75
|
yScale,
|
|
73
76
|
split: preparedSplit,
|
|
74
77
|
htmlLayout,
|
|
78
|
+
clipPathId,
|
|
79
|
+
isOutsideBounds,
|
|
75
80
|
});
|
|
76
81
|
const handleAttemptToSetZoomState = React.useCallback((nextZoomState) => {
|
|
77
82
|
const nextZoomedSeriesData = getZoomedSeriesData({
|
|
78
83
|
seriesData: zoomedSeriesData,
|
|
79
84
|
xAxis: data.xAxis,
|
|
80
|
-
|
|
85
|
+
yAxes: data.yAxis,
|
|
81
86
|
zoomState: nextZoomState,
|
|
82
87
|
});
|
|
83
88
|
const hasData = hasAtLeastOneSeriesDataPerPlot(nextZoomedSeriesData, yAxis);
|
|
@@ -123,6 +128,7 @@ export function useChartInnerProps(props) {
|
|
|
123
128
|
boundsWidth,
|
|
124
129
|
handleLegendItemClick,
|
|
125
130
|
handleZoomReset: Object.keys(zoomState).length > 0 ? handleZoomReset : undefined,
|
|
131
|
+
isOutsideBounds,
|
|
126
132
|
legendConfig,
|
|
127
133
|
legendItems,
|
|
128
134
|
preparedLegend,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { PreparedAxis } from '../../hooks/useChartOptions/types';
|
|
2
2
|
import type { ChartSeries } from '../../types';
|
|
3
|
-
export declare function hasAtLeastOneSeriesDataPerPlot(seriesData: ChartSeries[],
|
|
3
|
+
export declare function hasAtLeastOneSeriesDataPerPlot(seriesData: ChartSeries[], yAxes?: PreparedAxis[]): boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export function hasAtLeastOneSeriesDataPerPlot(seriesData,
|
|
1
|
+
export function hasAtLeastOneSeriesDataPerPlot(seriesData, yAxes = []) {
|
|
2
2
|
const hasDataMap = new Map();
|
|
3
|
-
|
|
3
|
+
yAxes.forEach((yAxis) => {
|
|
4
4
|
var _a;
|
|
5
5
|
const plotIndex = (_a = yAxis.plotIndex) !== null && _a !== void 0 ? _a : 0;
|
|
6
6
|
if (!hasDataMap.has(plotIndex)) {
|
|
@@ -16,7 +16,7 @@ export function hasAtLeastOneSeriesDataPerPlot(seriesData, yAxises = []) {
|
|
|
16
16
|
if ('yAxis' in seriesDataChunk && typeof seriesDataChunk.yAxis === 'number') {
|
|
17
17
|
yAxisIndex = seriesDataChunk.yAxis;
|
|
18
18
|
}
|
|
19
|
-
const yAxis =
|
|
19
|
+
const yAxis = yAxes[yAxisIndex];
|
|
20
20
|
const plotIndex = (_a = yAxis === null || yAxis === void 0 ? void 0 : yAxis.plotIndex) !== null && _a !== void 0 ? _a : 0;
|
|
21
21
|
if (!hasDataMap.get(plotIndex)) {
|
|
22
22
|
if (seriesDataChunk.data.length > 0) {
|
|
@@ -40,7 +40,7 @@ function isValueInRange(args) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
export function getZoomedSeriesData(args) {
|
|
43
|
-
const { seriesData, xAxis,
|
|
43
|
+
const { seriesData, xAxis, yAxes, zoomState } = args;
|
|
44
44
|
if (Object.keys(zoomState).length < 0) {
|
|
45
45
|
return seriesData;
|
|
46
46
|
}
|
|
@@ -65,7 +65,7 @@ export function getZoomedSeriesData(args) {
|
|
|
65
65
|
const [yMin, yMax] = zoomState.y[yAxisIndex];
|
|
66
66
|
const y = 'y' in point ? point.y : undefined;
|
|
67
67
|
inYRange = isValueInRange({
|
|
68
|
-
axis:
|
|
68
|
+
axis: yAxes === null || yAxes === void 0 ? void 0 : yAxes[yAxisIndex],
|
|
69
69
|
value: y,
|
|
70
70
|
min: yMin,
|
|
71
71
|
max: yMax,
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { extent, scaleBand, scaleLinear, scaleLog, scaleUtc } from 'd3';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { DEFAULT_AXIS_TYPE } from '../../constants';
|
|
5
|
-
import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getAxisHeight, getDataCategoryValue, getDefaultMaxXAxisValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
|
|
5
|
+
import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getAxisHeight, getDataCategoryValue, getDefaultMaxXAxisValue, getDefaultMinXAxisValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
|
|
6
6
|
const isNumericalArrayData = (data) => {
|
|
7
7
|
return data.every((d) => typeof d === 'number' || d === null);
|
|
8
8
|
};
|
|
@@ -20,7 +20,8 @@ const filterCategoriesByVisibleSeries = (args) => {
|
|
|
20
20
|
};
|
|
21
21
|
export function createYScale(axis, series, boundsHeight) {
|
|
22
22
|
const yType = get(axis, 'type', DEFAULT_AXIS_TYPE);
|
|
23
|
-
const
|
|
23
|
+
const yMinProps = get(axis, 'min');
|
|
24
|
+
const yMaxProps = get(axis, 'max');
|
|
24
25
|
const yCategories = get(axis, 'categories');
|
|
25
26
|
const yTimestamps = get(axis, 'timestamps');
|
|
26
27
|
switch (yType) {
|
|
@@ -29,14 +30,18 @@ export function createYScale(axis, series, boundsHeight) {
|
|
|
29
30
|
const domain = getDomainDataYBySeries(series);
|
|
30
31
|
const range = [boundsHeight, boundsHeight * axis.maxPadding];
|
|
31
32
|
if (isNumericalArrayData(domain)) {
|
|
32
|
-
const [
|
|
33
|
-
const
|
|
34
|
-
let
|
|
35
|
-
if (
|
|
36
|
-
|
|
33
|
+
const [yMinDomain, yMaxDomain] = extent(domain);
|
|
34
|
+
const yMin = typeof yMinProps === 'number' ? yMinProps : yMinDomain;
|
|
35
|
+
let yMax;
|
|
36
|
+
if (typeof yMaxProps === 'number') {
|
|
37
|
+
yMax = yMaxProps;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
const hasSeriesWithVolumeOnYAxis = series.some((s) => CHART_SERIES_WITH_VOLUME_ON_Y_AXIS.includes(s.type));
|
|
41
|
+
yMax = hasSeriesWithVolumeOnYAxis ? Math.max(yMaxDomain, 0) : yMaxDomain;
|
|
37
42
|
}
|
|
38
43
|
const scaleFn = yType === 'logarithmic' ? scaleLog : scaleLinear;
|
|
39
|
-
return scaleFn().domain([
|
|
44
|
+
return scaleFn().domain([yMin, yMax]).range(range).nice();
|
|
40
45
|
}
|
|
41
46
|
break;
|
|
42
47
|
}
|
|
@@ -54,13 +59,17 @@ export function createYScale(axis, series, boundsHeight) {
|
|
|
54
59
|
case 'datetime': {
|
|
55
60
|
const range = [boundsHeight, boundsHeight * axis.maxPadding];
|
|
56
61
|
if (yTimestamps) {
|
|
57
|
-
const [
|
|
62
|
+
const [yMinTimestamp, yMaxTimestamp] = extent(yTimestamps);
|
|
63
|
+
const yMin = typeof yMinProps === 'number' ? yMinProps : yMinTimestamp;
|
|
64
|
+
const yMax = typeof yMaxProps === 'number' ? yMaxProps : yMaxTimestamp;
|
|
58
65
|
return scaleUtc().domain([yMin, yMax]).range(range).nice();
|
|
59
66
|
}
|
|
60
67
|
else {
|
|
61
68
|
const domain = getDomainDataYBySeries(series);
|
|
62
69
|
if (isNumericalArrayData(domain)) {
|
|
63
|
-
const [
|
|
70
|
+
const [yMinTimestamp, yMaxTimestamp] = extent(domain);
|
|
71
|
+
const yMin = typeof yMinProps === 'number' ? yMinProps : yMinTimestamp;
|
|
72
|
+
const yMax = typeof yMaxProps === 'number' ? yMaxProps : yMaxTimestamp;
|
|
64
73
|
return scaleUtc().domain([yMin, yMax]).range(range).nice();
|
|
65
74
|
}
|
|
66
75
|
}
|
|
@@ -86,9 +95,10 @@ function calculateXAxisPadding(series) {
|
|
|
86
95
|
});
|
|
87
96
|
return result;
|
|
88
97
|
}
|
|
98
|
+
// eslint-disable-next-line complexity
|
|
89
99
|
export function createXScale(axis, series, boundsWidth, hasZoomX) {
|
|
90
|
-
const
|
|
91
|
-
const
|
|
100
|
+
const xMinProps = get(axis, 'min');
|
|
101
|
+
const xMaxProps = get(axis, 'max');
|
|
92
102
|
const xType = get(axis, 'type', DEFAULT_AXIS_TYPE);
|
|
93
103
|
const xCategories = get(axis, 'categories');
|
|
94
104
|
const xTimestamps = get(axis, 'timestamps');
|
|
@@ -100,11 +110,28 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
|
|
|
100
110
|
case 'logarithmic': {
|
|
101
111
|
const domain = getDomainDataXBySeries(series);
|
|
102
112
|
if (isNumericalArrayData(domain)) {
|
|
103
|
-
const [
|
|
104
|
-
|
|
105
|
-
|
|
113
|
+
const [xMinDomain, xMaxDomain] = extent(domain);
|
|
114
|
+
let xMin;
|
|
115
|
+
let xMax;
|
|
116
|
+
if (typeof xMinProps === 'number') {
|
|
117
|
+
xMin = xMinProps;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
const xMinDefault = getDefaultMinXAxisValue(series);
|
|
121
|
+
xMin = xMinDefault !== null && xMinDefault !== void 0 ? xMinDefault : xMinDomain;
|
|
122
|
+
}
|
|
123
|
+
if (typeof xMaxProps === 'number') {
|
|
124
|
+
xMax = xMaxProps;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
const xMaxDefault = getDefaultMaxXAxisValue(series);
|
|
128
|
+
xMax =
|
|
129
|
+
typeof xMaxDefault === 'number'
|
|
130
|
+
? Math.max(xMaxDefault, xMaxDomain)
|
|
131
|
+
: xMaxDomain;
|
|
132
|
+
}
|
|
106
133
|
const scaleFn = xType === 'logarithmic' ? scaleLog : scaleLinear;
|
|
107
|
-
const scale = scaleFn().domain([
|
|
134
|
+
const scale = scaleFn().domain([xMin, xMax]).range(xRange);
|
|
108
135
|
if (!hasZoomX) {
|
|
109
136
|
scale.nice();
|
|
110
137
|
}
|
|
@@ -129,7 +156,9 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
|
|
|
129
156
|
}
|
|
130
157
|
case 'datetime': {
|
|
131
158
|
if (xTimestamps) {
|
|
132
|
-
const [
|
|
159
|
+
const [xMinTimestamp, xMaxTimestamp] = extent(xTimestamps);
|
|
160
|
+
const xMin = typeof xMinProps === 'number' ? xMinProps : xMinTimestamp;
|
|
161
|
+
const xMax = typeof xMaxProps === 'number' ? xMaxProps : xMaxTimestamp;
|
|
133
162
|
const scale = scaleUtc().domain([xMin, xMax]).range(xRange);
|
|
134
163
|
if (!hasZoomX) {
|
|
135
164
|
scale.nice();
|
|
@@ -139,7 +168,9 @@ export function createXScale(axis, series, boundsWidth, hasZoomX) {
|
|
|
139
168
|
else {
|
|
140
169
|
const domain = getDomainDataXBySeries(series);
|
|
141
170
|
if (isNumericalArrayData(domain)) {
|
|
142
|
-
const [
|
|
171
|
+
const [xMinTimestamp, xMaxTimestamp] = extent(domain);
|
|
172
|
+
const xMin = typeof xMinProps === 'number' ? xMinProps : xMinTimestamp;
|
|
173
|
+
const xMax = typeof xMaxProps === 'number' ? xMaxProps : xMaxTimestamp;
|
|
143
174
|
const scale = scaleUtc().domain([xMin, xMax]).range(xRange);
|
|
144
175
|
if (!hasZoomX) {
|
|
145
176
|
scale.nice();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, axisCrosshairDefaults, axisLabelsDefaults, xAxisTitleDefaults, } from '../../constants';
|
|
3
|
-
import {
|
|
3
|
+
import { calculateCos, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getMaxTickCount, getTicksCount, getXAxisItems, hasOverlappingLabels, wrapText, } from '../../utils';
|
|
4
4
|
import { createXScale } from '../useAxisScales';
|
|
5
5
|
async function getLabelSettings({ axis, seriesData, width, autoRotation = true, }) {
|
|
6
6
|
const scale = createXScale(axis, seriesData, width);
|
|
@@ -36,18 +36,6 @@ async function getLabelSettings({ axis, seriesData, width, autoRotation = true,
|
|
|
36
36
|
const maxHeight = rotation ? calculateCos(rotation) * axis.labels.maxWidth : labelsHeight;
|
|
37
37
|
return { height: Math.min(maxHeight, labelsHeight), rotation };
|
|
38
38
|
}
|
|
39
|
-
function getAxisMin(axis, series) {
|
|
40
|
-
const min = axis === null || axis === void 0 ? void 0 : axis.min;
|
|
41
|
-
if (typeof min === 'undefined' &&
|
|
42
|
-
(series === null || series === void 0 ? void 0 : series.some((s) => CHART_SERIES_WITH_VOLUME_ON_X_AXIS.includes(s.type)))) {
|
|
43
|
-
return series.reduce((minValue, s) => {
|
|
44
|
-
// @ts-expect-error
|
|
45
|
-
const minYValue = s.data.reduce((res, d) => Math.min(res, get(d, 'x', 0)), 0);
|
|
46
|
-
return Math.min(minValue, minYValue);
|
|
47
|
-
}, 0);
|
|
48
|
-
}
|
|
49
|
-
return min;
|
|
50
|
-
}
|
|
51
39
|
export const getPreparedXAxis = async ({ xAxis, seriesData, width, }) => {
|
|
52
40
|
var _a;
|
|
53
41
|
const titleText = get(xAxis, 'title.text', '');
|
|
@@ -92,7 +80,8 @@ export const getPreparedXAxis = async ({ xAxis, seriesData, width, }) => {
|
|
|
92
80
|
align: get(xAxis, 'title.align', xAxisTitleDefaults.align),
|
|
93
81
|
maxRowCount: get(xAxis, 'title.maxRowCount', xAxisTitleDefaults.maxRowCount),
|
|
94
82
|
},
|
|
95
|
-
min:
|
|
83
|
+
min: get(xAxis, 'min'),
|
|
84
|
+
max: get(xAxis, 'max'),
|
|
96
85
|
maxPadding: get(xAxis, 'maxPadding', 0.01),
|
|
97
86
|
grid: {
|
|
98
87
|
enabled: get(xAxis, 'grid.enabled', true),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import { DASH_STYLE, DEFAULT_AXIS_LABEL_FONT_SIZE, DEFAULT_AXIS_TYPE, axisCrosshairDefaults, axisLabelsDefaults, yAxisTitleDefaults, } from '../../constants';
|
|
3
|
-
import {
|
|
3
|
+
import { formatAxisTickLabel, getClosestPointsRange, getDefaultMinYAxisValue, getHorisontalSvgTextHeight, getLabelsSize, getScaleTicks, isAxisRelatedSeries, wrapText, } from '../../utils';
|
|
4
4
|
import { createYScale } from '../useAxisScales';
|
|
5
5
|
const getAxisLabelMaxWidth = async (args) => {
|
|
6
6
|
const { axis, seriesData } = args;
|
|
@@ -23,26 +23,6 @@ const getAxisLabelMaxWidth = async (args) => {
|
|
|
23
23
|
});
|
|
24
24
|
return size.maxWidth;
|
|
25
25
|
};
|
|
26
|
-
function getAxisMin(axis, seriesData) {
|
|
27
|
-
const min = axis === null || axis === void 0 ? void 0 : axis.min;
|
|
28
|
-
if (typeof min === 'undefined' &&
|
|
29
|
-
(seriesData === null || seriesData === void 0 ? void 0 : seriesData.some((s) => CHART_SERIES_WITH_VOLUME_ON_Y_AXIS.includes(s.type)))) {
|
|
30
|
-
return seriesData.reduce((minValue, s) => {
|
|
31
|
-
switch (s.type) {
|
|
32
|
-
case 'waterfall': {
|
|
33
|
-
const minSubTotal = s.data.reduce((res, d) => Math.min(res, getWaterfallPointSubtotal(d, s) || 0), 0);
|
|
34
|
-
return Math.min(minValue, minSubTotal);
|
|
35
|
-
}
|
|
36
|
-
default: {
|
|
37
|
-
// @ts-expect-error
|
|
38
|
-
const minYValue = s.data.reduce((res, d) => Math.min(res, get(d, 'y', 0)), 0);
|
|
39
|
-
return Math.min(minValue, minYValue);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}, 0);
|
|
43
|
-
}
|
|
44
|
-
return min;
|
|
45
|
-
}
|
|
46
26
|
export const getPreparedYAxis = ({ seriesData, yAxis, height, }) => {
|
|
47
27
|
const axisByPlot = [];
|
|
48
28
|
const axisItems = yAxis || [{}];
|
|
@@ -51,7 +31,7 @@ export const getPreparedYAxis = ({ seriesData, yAxis, height, }) => {
|
|
|
51
31
|
return Promise.resolve([]);
|
|
52
32
|
}
|
|
53
33
|
return Promise.all(axisItems.map(async (axisItem) => {
|
|
54
|
-
var _a;
|
|
34
|
+
var _a, _b;
|
|
55
35
|
const plotIndex = get(axisItem, 'plotIndex', 0);
|
|
56
36
|
const firstPlotAxis = !axisByPlot[plotIndex];
|
|
57
37
|
if (firstPlotAxis) {
|
|
@@ -104,11 +84,12 @@ export const getPreparedYAxis = ({ seriesData, yAxis, height, }) => {
|
|
|
104
84
|
align: get(axisItem, 'title.align', yAxisTitleDefaults.align),
|
|
105
85
|
maxRowCount: titleMaxRowsCount,
|
|
106
86
|
},
|
|
107
|
-
min:
|
|
87
|
+
min: (_a = get(axisItem, 'min')) !== null && _a !== void 0 ? _a : getDefaultMinYAxisValue(seriesData),
|
|
88
|
+
max: get(axisItem, 'max'),
|
|
108
89
|
maxPadding: get(axisItem, 'maxPadding', 0.05),
|
|
109
90
|
grid: {
|
|
110
91
|
enabled: get(axisItem, 'grid.enabled', firstPlotAxis ||
|
|
111
|
-
(!firstPlotAxis && !((
|
|
92
|
+
(!firstPlotAxis && !((_b = axisByPlot[plotIndex][0].visible) !== null && _b !== void 0 ? _b : true))),
|
|
112
93
|
},
|
|
113
94
|
ticks: {
|
|
114
95
|
pixelInterval: get(axisItem, 'ticks.pixelInterval'),
|
|
@@ -7,22 +7,25 @@ import { getMarkerHaloVisibility, getMarkerVisibility, renderMarker, selectMarke
|
|
|
7
7
|
import { setActiveState } from '../utils';
|
|
8
8
|
const b = block('area');
|
|
9
9
|
export const AreaSeriesShapes = (args) => {
|
|
10
|
-
const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
|
|
10
|
+
const { dispatcher, preparedData, seriesOptions, htmlLayout, clipPathId } = args;
|
|
11
11
|
const hoveredDataRef = React.useRef(null);
|
|
12
|
-
const
|
|
12
|
+
const plotRef = React.useRef(null);
|
|
13
|
+
const markersRef = React.useRef(null);
|
|
13
14
|
React.useEffect(() => {
|
|
14
15
|
var _a;
|
|
15
|
-
if (!
|
|
16
|
+
if (!plotRef.current || !markersRef.current) {
|
|
16
17
|
return () => { };
|
|
17
18
|
}
|
|
18
|
-
const
|
|
19
|
+
const plotSvgElement = select(plotRef.current);
|
|
20
|
+
const markersSvgElement = select(markersRef.current);
|
|
19
21
|
const hoverOptions = get(seriesOptions, 'area.states.hover');
|
|
20
22
|
const inactiveOptions = get(seriesOptions, 'area.states.inactive');
|
|
21
23
|
const line = lineGenerator()
|
|
22
24
|
.x((d) => d.x)
|
|
23
25
|
.y((d) => d.y);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
plotSvgElement.selectAll('*').remove();
|
|
27
|
+
markersSvgElement.selectAll('*').remove();
|
|
28
|
+
const shapeSelection = plotSvgElement
|
|
26
29
|
.selectAll('shape')
|
|
27
30
|
.data(preparedData)
|
|
28
31
|
.join('g')
|
|
@@ -53,7 +56,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
53
56
|
if (!((_a = preparedData[0]) === null || _a === void 0 ? void 0 : _a.series.dataLabels.allowOverlap)) {
|
|
54
57
|
dataLabels = filterOverlappingLabels(dataLabels);
|
|
55
58
|
}
|
|
56
|
-
const labelsSelection =
|
|
59
|
+
const labelsSelection = plotSvgElement
|
|
57
60
|
.selectAll('text')
|
|
58
61
|
.data(dataLabels)
|
|
59
62
|
.join('text')
|
|
@@ -66,7 +69,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
66
69
|
.style('font-weight', (d) => d.style.fontWeight || null)
|
|
67
70
|
.style('fill', (d) => d.style.fontColor || null);
|
|
68
71
|
const markers = preparedData.reduce((acc, d) => acc.concat(d.markers), []);
|
|
69
|
-
const markerSelection =
|
|
72
|
+
const markerSelection = markersSvgElement
|
|
70
73
|
.selectAll('marker')
|
|
71
74
|
.data(markers)
|
|
72
75
|
.join('g')
|
|
@@ -144,6 +147,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
144
147
|
};
|
|
145
148
|
}, [dispatcher, preparedData, seriesOptions]);
|
|
146
149
|
return (React.createElement(React.Fragment, null,
|
|
147
|
-
React.createElement("g", { ref:
|
|
150
|
+
React.createElement("g", { ref: plotRef, className: b(), clipPath: `url(#${clipPathId})` }),
|
|
151
|
+
React.createElement("g", { ref: markersRef }),
|
|
148
152
|
React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
|
|
149
153
|
};
|
|
@@ -53,7 +53,7 @@ function getXValues(series, xAxis, xScale) {
|
|
|
53
53
|
return sort(Array.from(xValues), ([_x, xValue]) => xValue);
|
|
54
54
|
}
|
|
55
55
|
export const prepareAreaData = async (args) => {
|
|
56
|
-
const { series, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight } = args;
|
|
56
|
+
const { series, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight, isOutsideBounds } = args;
|
|
57
57
|
const [_xMin, xRangeMax] = xScale.range();
|
|
58
58
|
const xMax = xRangeMax / (1 - xAxis.maxPadding);
|
|
59
59
|
const result = [];
|
|
@@ -142,6 +142,7 @@ export const prepareAreaData = async (args) => {
|
|
|
142
142
|
point: p,
|
|
143
143
|
active: true,
|
|
144
144
|
hovered: false,
|
|
145
|
+
clipped: isOutsideBounds(p.x, p.y),
|
|
145
146
|
}));
|
|
146
147
|
}
|
|
147
148
|
seriesStackData.push({
|
|
@@ -8,7 +8,7 @@ export { prepareBarXData } from './prepare-data';
|
|
|
8
8
|
export * from './types';
|
|
9
9
|
const b = block('bar-x');
|
|
10
10
|
export const BarXSeriesShapes = (args) => {
|
|
11
|
-
const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
|
|
11
|
+
const { dispatcher, preparedData, seriesOptions, htmlLayout, clipPathId } = args;
|
|
12
12
|
const hoveredDataRef = React.useRef(null);
|
|
13
13
|
const ref = React.useRef(null);
|
|
14
14
|
React.useEffect(() => {
|
|
@@ -110,6 +110,6 @@ export const BarXSeriesShapes = (args) => {
|
|
|
110
110
|
};
|
|
111
111
|
}, [dispatcher, preparedData, seriesOptions]);
|
|
112
112
|
return (React.createElement(React.Fragment, null,
|
|
113
|
-
React.createElement("g", { ref: ref, className: b() }),
|
|
113
|
+
React.createElement("g", { ref: ref, className: b(), clipPath: `url(#${clipPathId})` }),
|
|
114
114
|
React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
|
|
115
115
|
};
|
|
@@ -7,7 +7,7 @@ import { getRectPath } from '../utils';
|
|
|
7
7
|
export { prepareBarYData } from './prepare-data';
|
|
8
8
|
const b = block('bar-y');
|
|
9
9
|
export const BarYSeriesShapes = (args) => {
|
|
10
|
-
const { dispatcher, preparedData, seriesOptions, htmlLayout } = args;
|
|
10
|
+
const { dispatcher, preparedData, seriesOptions, htmlLayout, clipPathId } = args;
|
|
11
11
|
const hoveredDataRef = React.useRef(null);
|
|
12
12
|
const ref = React.useRef(null);
|
|
13
13
|
React.useEffect(() => {
|
|
@@ -99,6 +99,6 @@ export const BarYSeriesShapes = (args) => {
|
|
|
99
99
|
};
|
|
100
100
|
}, [dispatcher, preparedData, seriesOptions]);
|
|
101
101
|
return (React.createElement(React.Fragment, null,
|
|
102
|
-
React.createElement("g", { ref: ref, className: b() }),
|
|
102
|
+
React.createElement("g", { ref: ref, className: b(), clipPath: `url(#${clipPathId})` }),
|
|
103
103
|
React.createElement(HtmlLayer, { preparedData: preparedData, htmlLayout: htmlLayout })));
|
|
104
104
|
};
|
|
@@ -28,6 +28,8 @@ type Args = {
|
|
|
28
28
|
yScale?: ChartScale[];
|
|
29
29
|
split: PreparedSplit;
|
|
30
30
|
htmlLayout: HTMLElement | null;
|
|
31
|
+
clipPathId: string;
|
|
32
|
+
isOutsideBounds: (x: number, y: number) => boolean;
|
|
31
33
|
};
|
|
32
34
|
export declare const useShapes: (args: Args) => {
|
|
33
35
|
shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
|