@gravity-ui/chartkit 5.6.0 → 5.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/plugins/d3/examples/area/NegativeValues.d.ts +2 -0
- package/build/plugins/d3/examples/area/NegativeValues.js +24 -0
- package/build/plugins/d3/examples/bar-x/NegativeValues.d.ts +2 -0
- package/build/plugins/d3/examples/bar-x/NegativeValues.js +41 -0
- package/build/plugins/d3/examples/bar-y/NegativeValues.d.ts +2 -0
- package/build/plugins/d3/examples/bar-y/NegativeValues.js +40 -0
- package/build/plugins/d3/renderer/hooks/useAxisScales/index.js +11 -5
- package/build/plugins/d3/renderer/hooks/useChartOptions/y-axis.js +5 -4
- package/build/plugins/d3/renderer/hooks/useShapes/area/prepare-data.js +15 -5
- package/build/plugins/d3/renderer/hooks/useShapes/bar-x/prepare-data.js +5 -3
- package/build/plugins/d3/renderer/hooks/useShapes/bar-y/prepare-data.js +5 -3
- package/build/plugins/d3/renderer/utils/index.d.ts +2 -0
- package/build/plugins/d3/renderer/utils/index.js +11 -0
- package/build/plugins/highcharts/renderer/helpers/config/config.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ChartKit } from '../../../../components/ChartKit';
|
|
3
|
+
import { ExampleWrapper } from '../ExampleWrapper';
|
|
4
|
+
export const NegativeValues = () => {
|
|
5
|
+
const data = [
|
|
6
|
+
{ x: 0, y: 10 },
|
|
7
|
+
{ x: 1, y: 20 },
|
|
8
|
+
{ x: 2, y: -30 },
|
|
9
|
+
{ x: 3, y: 100 },
|
|
10
|
+
];
|
|
11
|
+
const widgetData = {
|
|
12
|
+
series: {
|
|
13
|
+
data: [
|
|
14
|
+
{
|
|
15
|
+
type: 'area',
|
|
16
|
+
data: data,
|
|
17
|
+
name: 'Min temperature',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
return (React.createElement(ExampleWrapper, null,
|
|
23
|
+
React.createElement(ChartKit, { type: "d3", data: widgetData })));
|
|
24
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { dateTime } from '@gravity-ui/date-utils';
|
|
3
|
+
import { ChartKit } from '../../../../components/ChartKit';
|
|
4
|
+
import { ExampleWrapper } from '../ExampleWrapper';
|
|
5
|
+
import marsWeatherData from '../mars-weather';
|
|
6
|
+
export const NegativeValues = () => {
|
|
7
|
+
const data = marsWeatherData.map((d) => ({
|
|
8
|
+
x: dateTime({ input: d.terrestrial_date, format: 'YYYY-MM-DD' }).valueOf(),
|
|
9
|
+
y: d.min_temp,
|
|
10
|
+
}));
|
|
11
|
+
const widgetData = {
|
|
12
|
+
series: {
|
|
13
|
+
data: [
|
|
14
|
+
{
|
|
15
|
+
type: 'bar-x',
|
|
16
|
+
data: data,
|
|
17
|
+
name: 'Min temperature',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
yAxis: [
|
|
22
|
+
{
|
|
23
|
+
title: {
|
|
24
|
+
text: 'Min temperature',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
xAxis: {
|
|
29
|
+
type: 'datetime',
|
|
30
|
+
title: {
|
|
31
|
+
text: 'Terrestrial date',
|
|
32
|
+
},
|
|
33
|
+
ticks: { pixelInterval: 200 },
|
|
34
|
+
},
|
|
35
|
+
title: {
|
|
36
|
+
text: 'Mars weather',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
return (React.createElement(ExampleWrapper, null,
|
|
40
|
+
React.createElement(ChartKit, { type: "d3", data: widgetData })));
|
|
41
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { dateTime } from '@gravity-ui/date-utils';
|
|
3
|
+
import { ChartKit } from '../../../../components/ChartKit';
|
|
4
|
+
import { ExampleWrapper } from '../ExampleWrapper';
|
|
5
|
+
import marsWeatherData from '../mars-weather';
|
|
6
|
+
export const NegativeValues = () => {
|
|
7
|
+
const data = marsWeatherData.map((d) => ({
|
|
8
|
+
y: dateTime({ input: d.terrestrial_date, format: 'YYYY-MM-DD' }).valueOf(),
|
|
9
|
+
x: d.min_temp,
|
|
10
|
+
}));
|
|
11
|
+
const widgetData = {
|
|
12
|
+
series: {
|
|
13
|
+
data: [
|
|
14
|
+
{
|
|
15
|
+
type: 'bar-y',
|
|
16
|
+
data: data,
|
|
17
|
+
name: 'Min temperature',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
xAxis: {
|
|
22
|
+
title: {
|
|
23
|
+
text: 'Min temperature',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
yAxis: [
|
|
27
|
+
{
|
|
28
|
+
type: 'datetime',
|
|
29
|
+
title: {
|
|
30
|
+
text: 'Terrestrial date',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
title: {
|
|
35
|
+
text: 'Mars weather',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
return (React.createElement(ExampleWrapper, null,
|
|
39
|
+
React.createElement(ChartKit, { type: "d3", data: widgetData })));
|
|
40
|
+
};
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { extent, scaleBand, scaleLinear, scaleUtc } from 'd3';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { DEFAULT_AXIS_TYPE } from '../../constants';
|
|
5
|
-
import { getDataCategoryValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
|
|
5
|
+
import { CHART_SERIES_WITH_VOLUME, getDataCategoryValue, getDefaultMaxXAxisValue, 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
|
};
|
|
@@ -28,9 +28,13 @@ export function createYScale(axis, series, boundsHeight) {
|
|
|
28
28
|
const domain = getDomainDataYBySeries(series);
|
|
29
29
|
const range = [boundsHeight, boundsHeight * axis.maxPadding];
|
|
30
30
|
if (isNumericalArrayData(domain)) {
|
|
31
|
-
const [domainYMin,
|
|
31
|
+
const [domainYMin, domainMax] = extent(domain);
|
|
32
32
|
const yMinValue = typeof yMin === 'number' ? yMin : domainYMin;
|
|
33
|
-
|
|
33
|
+
let yMaxValue = domainMax;
|
|
34
|
+
if (series.some((s) => CHART_SERIES_WITH_VOLUME.includes(s.type))) {
|
|
35
|
+
yMaxValue = Math.max(yMaxValue, 0);
|
|
36
|
+
}
|
|
37
|
+
return scaleLinear().domain([yMinValue, yMaxValue]).range(range).nice();
|
|
34
38
|
}
|
|
35
39
|
break;
|
|
36
40
|
}
|
|
@@ -79,6 +83,7 @@ function calculateXAxisPadding(series) {
|
|
|
79
83
|
}
|
|
80
84
|
export function createXScale(axis, series, boundsWidth) {
|
|
81
85
|
const xMin = get(axis, 'min');
|
|
86
|
+
const xMax = getDefaultMaxXAxisValue(series);
|
|
82
87
|
const xType = get(axis, 'type', DEFAULT_AXIS_TYPE);
|
|
83
88
|
const xCategories = get(axis, 'categories');
|
|
84
89
|
const xTimestamps = get(axis, 'timestamps');
|
|
@@ -89,9 +94,10 @@ export function createXScale(axis, series, boundsWidth) {
|
|
|
89
94
|
case 'linear': {
|
|
90
95
|
const domain = getDomainDataXBySeries(series);
|
|
91
96
|
if (isNumericalArrayData(domain)) {
|
|
92
|
-
const [domainXMin,
|
|
97
|
+
const [domainXMin, domainXMax] = extent(domain);
|
|
93
98
|
const xMinValue = typeof xMin === 'number' ? xMin : domainXMin;
|
|
94
|
-
|
|
99
|
+
const xMaxValue = typeof xMax === 'number' ? Math.max(xMax, domainXMax) : domainXMax;
|
|
100
|
+
return scaleLinear().domain([xMinValue, xMaxValue]).range(xRange).nice();
|
|
95
101
|
}
|
|
96
102
|
break;
|
|
97
103
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import { DEFAULT_AXIS_LABEL_FONT_SIZE, axisLabelsDefaults, yAxisTitleDefaults, } from '../../constants';
|
|
3
|
-
import { formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getScaleTicks, getWaterfallPointSubtotal, } from '../../utils';
|
|
3
|
+
import { CHART_SERIES_WITH_VOLUME, formatAxisTickLabel, getClosestPointsRange, getHorisontalSvgTextHeight, getLabelsSize, getScaleTicks, getWaterfallPointSubtotal, } from '../../utils';
|
|
4
4
|
import { createYScale } from '../useAxisScales';
|
|
5
5
|
const getAxisLabelMaxWidth = (args) => {
|
|
6
6
|
const { axis, series } = args;
|
|
@@ -24,8 +24,8 @@ const getAxisLabelMaxWidth = (args) => {
|
|
|
24
24
|
};
|
|
25
25
|
function getAxisMin(axis, series) {
|
|
26
26
|
const min = axis === null || axis === void 0 ? void 0 : axis.min;
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
if (typeof min === 'undefined' &&
|
|
28
|
+
(series === null || series === void 0 ? void 0 : series.some((s) => CHART_SERIES_WITH_VOLUME.includes(s.type)))) {
|
|
29
29
|
return series.reduce((minValue, s) => {
|
|
30
30
|
switch (s.type) {
|
|
31
31
|
case 'waterfall': {
|
|
@@ -33,7 +33,8 @@ function getAxisMin(axis, series) {
|
|
|
33
33
|
return Math.min(minValue, minSubTotal);
|
|
34
34
|
}
|
|
35
35
|
default: {
|
|
36
|
-
|
|
36
|
+
const minYValue = s.data.reduce((res, d) => Math.min(res, get(d, 'y', 0)), 0);
|
|
37
|
+
return Math.min(minValue, minYValue);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
}, 0);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { group, sort } from 'd3';
|
|
2
|
-
import { getLabelsSize, getLeftPosition } from '../../../utils';
|
|
2
|
+
import { getDataCategoryValue, getLabelsSize, getLeftPosition } from '../../../utils';
|
|
3
3
|
import { getXValue, getYValue } from '../utils';
|
|
4
4
|
function getLabelData(point, series, xMax) {
|
|
5
5
|
const text = String(point.data.label || point.data.y);
|
|
@@ -28,9 +28,12 @@ function getLabelData(point, series, xMax) {
|
|
|
28
28
|
return labelData;
|
|
29
29
|
}
|
|
30
30
|
function getXValues(series, xAxis, xScale) {
|
|
31
|
+
const categories = xAxis.categories || [];
|
|
31
32
|
const xValues = series.reduce((acc, s) => {
|
|
32
33
|
s.data.forEach((d) => {
|
|
33
|
-
const key = String(
|
|
34
|
+
const key = String(xAxis.type === 'category'
|
|
35
|
+
? getDataCategoryValue({ axisDirection: 'x', categories, data: d })
|
|
36
|
+
: d.x);
|
|
34
37
|
if (!acc.has(key)) {
|
|
35
38
|
acc.set(key, getXValue({ point: d, xAxis, xScale }));
|
|
36
39
|
}
|
|
@@ -38,7 +41,7 @@ function getXValues(series, xAxis, xScale) {
|
|
|
38
41
|
return acc;
|
|
39
42
|
}, new Map());
|
|
40
43
|
if (xAxis.type === 'category') {
|
|
41
|
-
return
|
|
44
|
+
return categories.reduce((acc, category) => {
|
|
42
45
|
const xValue = xValues.get(category);
|
|
43
46
|
if (typeof xValue === 'number') {
|
|
44
47
|
acc.push([category, xValue]);
|
|
@@ -62,9 +65,16 @@ export const prepareAreaData = (args) => {
|
|
|
62
65
|
const yAxisIndex = s.yAxis;
|
|
63
66
|
const seriesYAxis = yAxis[yAxisIndex];
|
|
64
67
|
const seriesYScale = yScale[yAxisIndex];
|
|
65
|
-
const
|
|
68
|
+
const yMin = getYValue({ point: { y: 0 }, yAxis: seriesYAxis, yScale: seriesYScale });
|
|
66
69
|
const seriesData = s.data.reduce((m, d) => {
|
|
67
|
-
|
|
70
|
+
const key = String(xAxis.type === 'category'
|
|
71
|
+
? getDataCategoryValue({
|
|
72
|
+
axisDirection: 'x',
|
|
73
|
+
categories: xAxis.categories || [],
|
|
74
|
+
data: d,
|
|
75
|
+
})
|
|
76
|
+
: d.x);
|
|
77
|
+
return m.set(key, d);
|
|
68
78
|
}, new Map());
|
|
69
79
|
const points = xValues.reduce((pointsAcc, [x, xValue]) => {
|
|
70
80
|
const accumulatedYValue = accumulatedYValues.get(x) || 0;
|
|
@@ -110,11 +110,13 @@ export const prepareBarXData = (args) => {
|
|
|
110
110
|
xCenter = xLinearScale(Number(xValue));
|
|
111
111
|
}
|
|
112
112
|
const x = xCenter - currentGroupWidth / 2 + (rectWidth + rectGap) * groupItemIndex;
|
|
113
|
-
const
|
|
114
|
-
const
|
|
113
|
+
const yDataValue = yValue.data.y;
|
|
114
|
+
const y = seriesYScale(yDataValue);
|
|
115
|
+
const base = seriesYScale(0);
|
|
116
|
+
const height = yDataValue > 0 ? base - y : y - base;
|
|
115
117
|
const barData = {
|
|
116
118
|
x,
|
|
117
|
-
y: y - stackHeight,
|
|
119
|
+
y: yDataValue > 0 ? y - stackHeight : seriesYScale(0),
|
|
118
120
|
width: rectWidth,
|
|
119
121
|
height,
|
|
120
122
|
opacity: get(yValue.data, 'opacity', null),
|
|
@@ -81,7 +81,8 @@ export const prepareBarYData = (args) => {
|
|
|
81
81
|
const stacks = Object.values(val);
|
|
82
82
|
const currentBarHeight = barHeight * stacks.length + rectGap * (stacks.length - 1);
|
|
83
83
|
stacks.forEach((measureValues, groupItemIndex) => {
|
|
84
|
-
|
|
84
|
+
const base = xLinearScale(0);
|
|
85
|
+
let stackSum = base;
|
|
85
86
|
const stackItems = [];
|
|
86
87
|
const sortedData = sortKey
|
|
87
88
|
? sort(measureValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
|
|
@@ -97,9 +98,10 @@ export const prepareBarYData = (args) => {
|
|
|
97
98
|
center = scale(Number(yValue));
|
|
98
99
|
}
|
|
99
100
|
const y = center - currentBarHeight / 2 + (barHeight + rectGap) * groupItemIndex;
|
|
100
|
-
const
|
|
101
|
+
const xValue = Number(data.x);
|
|
102
|
+
const width = xValue > 0 ? xLinearScale(xValue) - base : base - xLinearScale(xValue);
|
|
101
103
|
stackItems.push({
|
|
102
|
-
x: stackSum,
|
|
104
|
+
x: xValue > 0 ? stackSum : stackSum - width,
|
|
103
105
|
y,
|
|
104
106
|
width,
|
|
105
107
|
height: barHeight,
|
|
@@ -8,6 +8,7 @@ export * from './axis';
|
|
|
8
8
|
export * from './labels';
|
|
9
9
|
export * from './symbol';
|
|
10
10
|
export * from './series';
|
|
11
|
+
export declare const CHART_SERIES_WITH_VOLUME: ChartKitWidgetSeries['type'][];
|
|
11
12
|
export type AxisDirection = 'x' | 'y';
|
|
12
13
|
type UnknownSeries = {
|
|
13
14
|
type: ChartKitWidgetSeries['type'];
|
|
@@ -39,6 +40,7 @@ export declare function isSeriesWithCategoryValues(series: UnknownSeries): serie
|
|
|
39
40
|
}[];
|
|
40
41
|
};
|
|
41
42
|
export declare const getDomainDataXBySeries: (series: UnknownSeries[]) => number[];
|
|
43
|
+
export declare function getDefaultMaxXAxisValue(series: UnknownSeries[]): 0 | undefined;
|
|
42
44
|
export declare const getDomainDataYBySeries: (series: UnknownSeries[]) => unknown[];
|
|
43
45
|
export declare const getSeriesNames: (series: ChartKitWidgetSeries[]) => string[];
|
|
44
46
|
export declare const getOnlyVisibleSeries: <T extends {
|
|
@@ -15,6 +15,11 @@ export * from './labels';
|
|
|
15
15
|
export * from './symbol';
|
|
16
16
|
export * from './series';
|
|
17
17
|
const CHARTS_WITHOUT_AXIS = ['pie', 'treemap'];
|
|
18
|
+
export const CHART_SERIES_WITH_VOLUME = [
|
|
19
|
+
'bar-x',
|
|
20
|
+
'area',
|
|
21
|
+
'waterfall',
|
|
22
|
+
];
|
|
18
23
|
/**
|
|
19
24
|
* Checks whether the series should be drawn with axes.
|
|
20
25
|
*
|
|
@@ -41,6 +46,12 @@ export const getDomainDataXBySeries = (series) => {
|
|
|
41
46
|
return acc;
|
|
42
47
|
}, []);
|
|
43
48
|
};
|
|
49
|
+
export function getDefaultMaxXAxisValue(series) {
|
|
50
|
+
if (series.some((s) => s.type === 'bar-y')) {
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
44
55
|
export const getDomainDataYBySeries = (series) => {
|
|
45
56
|
const groupedSeries = group(series, (item) => item.type);
|
|
46
57
|
return Array.from(groupedSeries).reduce((acc, [type, seriesList]) => {
|
|
@@ -1328,8 +1328,8 @@ export function prepareConfig(data, options, isMobile, holidays) {
|
|
|
1328
1328
|
// Callback setExtremes used because of it obligatory invocation on every zoom event
|
|
1329
1329
|
// setTimeout used because of absence resetZoomButton node in dom on first zoom event
|
|
1330
1330
|
setTimeout(() => {
|
|
1331
|
-
var _a;
|
|
1332
|
-
const text = (_a = this.chart
|
|
1331
|
+
var _a, _b;
|
|
1332
|
+
const text = (_b = (_a = this.chart) === null || _a === void 0 ? void 0 : _a.resetZoomButton) === null || _b === void 0 ? void 0 : _b.text;
|
|
1333
1333
|
if (text) {
|
|
1334
1334
|
text.translate(0, -6);
|
|
1335
1335
|
}
|
package/package.json
CHANGED