@gravity-ui/charts 1.35.0 → 1.35.1
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/hooks/useShapes/area/index.js +38 -0
- package/dist/cjs/hooks/useShapes/area/prepare-data.js +12 -7
- package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +18 -7
- package/dist/cjs/hooks/useShapes/line/index.js +38 -0
- package/dist/cjs/hooks/useShapes/line/prepare-data.js +12 -7
- package/dist/esm/hooks/useShapes/area/index.js +38 -0
- package/dist/esm/hooks/useShapes/area/prepare-data.js +12 -7
- package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +18 -7
- package/dist/esm/hooks/useShapes/line/index.js +38 -0
- package/dist/esm/hooks/useShapes/line/prepare-data.js +12 -7
- package/package.json +1 -1
- package/dist/cjs/hooks/utils/bar-x.d.ts +0 -17
- package/dist/cjs/hooks/utils/bar-x.js +0 -43
- package/dist/esm/hooks/utils/bar-x.d.ts +0 -17
- package/dist/esm/hooks/utils/bar-x.js +0 -43
|
@@ -11,6 +11,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
11
11
|
const hoveredDataRef = React.useRef(null);
|
|
12
12
|
const plotRef = React.useRef(null);
|
|
13
13
|
const markersRef = React.useRef(null);
|
|
14
|
+
const hoverMarkersRef = React.useRef(null);
|
|
14
15
|
const allowOverlapDataLabels = React.useMemo(() => {
|
|
15
16
|
return preparedData.some((d) => d === null || d === void 0 ? void 0 : d.series.dataLabels.allowOverlap);
|
|
16
17
|
}, [preparedData]);
|
|
@@ -20,6 +21,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
20
21
|
}
|
|
21
22
|
const plotSvgElement = select(plotRef.current);
|
|
22
23
|
const markersSvgElement = select(markersRef.current);
|
|
24
|
+
const hoverMarkersSvgElement = select(hoverMarkersRef.current);
|
|
23
25
|
const hoverOptions = get(seriesOptions, 'area.states.hover');
|
|
24
26
|
const inactiveOptions = get(seriesOptions, 'area.states.inactive');
|
|
25
27
|
const line = lineGenerator()
|
|
@@ -141,6 +143,41 @@ export const AreaSeriesShapes = (args) => {
|
|
|
141
143
|
}
|
|
142
144
|
return d;
|
|
143
145
|
});
|
|
146
|
+
hoverMarkersSvgElement.selectAll('*').remove();
|
|
147
|
+
if (hoverEnabled && selected.length > 0) {
|
|
148
|
+
const hoverOnlyMarkers = [];
|
|
149
|
+
for (const chunk of selected) {
|
|
150
|
+
const seriesData = preparedData.find((pd) => pd.id === chunk.series.id);
|
|
151
|
+
if (!seriesData) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const { series } = seriesData;
|
|
155
|
+
if (series.marker.states.normal.enabled ||
|
|
156
|
+
!series.marker.states.hover.enabled) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const point = seriesData.points.find((p) => p.data === chunk.data);
|
|
160
|
+
if (!point || point.y === null) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
hoverOnlyMarkers.push({
|
|
164
|
+
point: point,
|
|
165
|
+
active: true,
|
|
166
|
+
hovered: true,
|
|
167
|
+
clipped: false,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (hoverOnlyMarkers.length > 0) {
|
|
171
|
+
hoverMarkersSvgElement
|
|
172
|
+
.selectAll('g')
|
|
173
|
+
.data(hoverOnlyMarkers)
|
|
174
|
+
.join('g')
|
|
175
|
+
.call(renderMarker)
|
|
176
|
+
.each((_d, i, nodes) => {
|
|
177
|
+
selectMarkerSymbol(select(nodes[i])).call(setMarker, 'hover');
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
144
181
|
}
|
|
145
182
|
if (hoveredDataRef.current !== null) {
|
|
146
183
|
handleShapeHover(hoveredDataRef.current);
|
|
@@ -160,5 +197,6 @@ export const AreaSeriesShapes = (args) => {
|
|
|
160
197
|
return (React.createElement(React.Fragment, null,
|
|
161
198
|
React.createElement("g", { ref: plotRef, className: b(), clipPath: `url(#${clipPathId})` }),
|
|
162
199
|
React.createElement("g", { ref: markersRef }),
|
|
200
|
+
React.createElement("g", { ref: hoverMarkersRef }),
|
|
163
201
|
React.createElement(HtmlLayer, { preparedData: htmlLayerData, htmlLayout: htmlLayout })));
|
|
164
202
|
};
|
|
@@ -220,17 +220,22 @@ export const prepareAreaData = async (args) => {
|
|
|
220
220
|
return pointsAcc;
|
|
221
221
|
}, []);
|
|
222
222
|
let markers = [];
|
|
223
|
-
|
|
223
|
+
const hasPerPointNormalMarkers = s.data.some((d) => { var _a, _b, _c; return (_c = (_b = (_a = d.marker) === null || _a === void 0 ? void 0 : _a.states) === null || _b === void 0 ? void 0 : _b.normal) === null || _c === void 0 ? void 0 : _c.enabled; });
|
|
224
|
+
if (s.marker.states.normal.enabled || hasPerPointNormalMarkers) {
|
|
224
225
|
markers = points.reduce((markersAcc, p) => {
|
|
226
|
+
var _a, _b, _c, _d;
|
|
225
227
|
if (p.y === null) {
|
|
226
228
|
return markersAcc;
|
|
227
229
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
230
|
+
const pointNormalEnabled = (_d = (_c = (_b = (_a = p.data.marker) === null || _a === void 0 ? void 0 : _a.states) === null || _b === void 0 ? void 0 : _b.normal) === null || _c === void 0 ? void 0 : _c.enabled) !== null && _d !== void 0 ? _d : false;
|
|
231
|
+
if (s.marker.states.normal.enabled || pointNormalEnabled) {
|
|
232
|
+
markersAcc.push({
|
|
233
|
+
point: p,
|
|
234
|
+
active: true,
|
|
235
|
+
hovered: false,
|
|
236
|
+
clipped: isOutsideBounds(p.x, p.y),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
234
239
|
return markersAcc;
|
|
235
240
|
}, []);
|
|
236
241
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { ascending, descending, reverse, sort } from 'd3';
|
|
1
|
+
import { ascending, descending, max, reverse, sort } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
3
|
import { getDataCategoryValue, getLabelsSize } from '../../../utils';
|
|
4
4
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
5
|
+
import { MIN_BAR_GAP, MIN_BAR_GROUP_GAP, MIN_BAR_WIDTH } from '../../constants';
|
|
5
6
|
import { getSeriesStackId } from '../../useSeries/utils';
|
|
6
|
-
import {
|
|
7
|
+
import { getBandSize } from '../../utils/get-band-size';
|
|
7
8
|
const isSeriesDataValid = (d) => d.y !== null;
|
|
8
9
|
async function getLabelData(d, xMax) {
|
|
9
10
|
var _a;
|
|
@@ -54,6 +55,8 @@ export const prepareBarXData = async (args) => {
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
})();
|
|
58
|
+
const domain = new Set();
|
|
59
|
+
let maxGroupSize = 1;
|
|
57
60
|
// series grouped by plotIndex > xValue > data[];
|
|
58
61
|
const dataByPlots = new Map();
|
|
59
62
|
series.forEach((s) => {
|
|
@@ -81,19 +84,27 @@ export const prepareBarXData = async (args) => {
|
|
|
81
84
|
data[key][stackId] = [];
|
|
82
85
|
}
|
|
83
86
|
data[key][stackId].push({ data: d, series: s });
|
|
87
|
+
domain.add(key);
|
|
84
88
|
}
|
|
85
89
|
});
|
|
90
|
+
maxGroupSize = Math.max(maxGroupSize, max(Object.values(data), (d) => Object.values(d).length) || 1);
|
|
86
91
|
});
|
|
87
92
|
const result = [];
|
|
93
|
+
const barMaxWidth = get(seriesOptions, 'bar-x.barMaxWidth');
|
|
94
|
+
const barPadding = get(seriesOptions, 'bar-x.barPadding');
|
|
95
|
+
const groupPadding = get(seriesOptions, 'bar-x.groupPadding');
|
|
96
|
+
const bandSize = getBandSize({
|
|
97
|
+
domain: Array.from(domain),
|
|
98
|
+
scale: xScale,
|
|
99
|
+
});
|
|
100
|
+
const groupGap = Math.max(bandSize * groupPadding, MIN_BAR_GROUP_GAP);
|
|
101
|
+
const groupSize = bandSize - groupGap;
|
|
102
|
+
const rectGap = Math.max(bandSize * barPadding, MIN_BAR_GAP);
|
|
103
|
+
const rectWidth = Math.max(MIN_BAR_WIDTH, Math.min(groupSize / maxGroupSize - rectGap, barMaxWidth));
|
|
88
104
|
const plotIndexes = Array.from(dataByPlots.keys());
|
|
89
105
|
for (let plotDataIndex = 0; plotDataIndex < plotIndexes.length; plotDataIndex++) {
|
|
90
106
|
const data = (_b = dataByPlots.get(plotIndexes[plotDataIndex])) !== null && _b !== void 0 ? _b : {};
|
|
91
107
|
const groupedData = Object.entries(data);
|
|
92
|
-
const { bandSize, barGap: rectGap, barSize: rectWidth, } = getBarXLayout({
|
|
93
|
-
groupedData: data,
|
|
94
|
-
seriesOptions,
|
|
95
|
-
scale: xScale,
|
|
96
|
-
});
|
|
97
108
|
for (let groupedDataIndex = 0; groupedDataIndex < groupedData.length; groupedDataIndex++) {
|
|
98
109
|
const [xValue, val] = groupedData[groupedDataIndex];
|
|
99
110
|
const stacks = Object.values(val);
|
|
@@ -11,6 +11,7 @@ export const LineSeriesShapes = (args) => {
|
|
|
11
11
|
const hoveredDataRef = React.useRef(null);
|
|
12
12
|
const plotRef = React.useRef(null);
|
|
13
13
|
const markersRef = React.useRef(null);
|
|
14
|
+
const hoverMarkersRef = React.useRef(null);
|
|
14
15
|
const allowOverlapDataLabels = React.useMemo(() => {
|
|
15
16
|
return preparedData.some((d) => d === null || d === void 0 ? void 0 : d.series.dataLabels.allowOverlap);
|
|
16
17
|
}, [preparedData]);
|
|
@@ -20,6 +21,7 @@ export const LineSeriesShapes = (args) => {
|
|
|
20
21
|
}
|
|
21
22
|
const plotSvgElement = select(plotRef.current);
|
|
22
23
|
const markersSvgElement = select(markersRef.current);
|
|
24
|
+
const hoverMarkersSvgElement = select(hoverMarkersRef.current);
|
|
23
25
|
const hoverOptions = get(seriesOptions, 'line.states.hover');
|
|
24
26
|
const inactiveOptions = get(seriesOptions, 'line.states.inactive');
|
|
25
27
|
const line = lineGenerator()
|
|
@@ -127,6 +129,41 @@ export const LineSeriesShapes = (args) => {
|
|
|
127
129
|
}
|
|
128
130
|
return d;
|
|
129
131
|
});
|
|
132
|
+
hoverMarkersSvgElement.selectAll('*').remove();
|
|
133
|
+
if (hoverEnabled && selected.length > 0) {
|
|
134
|
+
const hoverOnlyMarkers = [];
|
|
135
|
+
for (const chunk of selected) {
|
|
136
|
+
const seriesData = preparedData.find((pd) => pd.id === chunk.series.id);
|
|
137
|
+
if (!seriesData) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const { series } = seriesData;
|
|
141
|
+
if (series.marker.states.normal.enabled ||
|
|
142
|
+
!series.marker.states.hover.enabled) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const point = seriesData.points.find((p) => p.data === chunk.data);
|
|
146
|
+
if (!point || point.x === null || point.y === null) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
hoverOnlyMarkers.push({
|
|
150
|
+
point: point,
|
|
151
|
+
active: true,
|
|
152
|
+
hovered: true,
|
|
153
|
+
clipped: false,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
if (hoverOnlyMarkers.length > 0) {
|
|
157
|
+
hoverMarkersSvgElement
|
|
158
|
+
.selectAll('g')
|
|
159
|
+
.data(hoverOnlyMarkers)
|
|
160
|
+
.join('g')
|
|
161
|
+
.call(renderMarker)
|
|
162
|
+
.each((_d, i, nodes) => {
|
|
163
|
+
selectMarkerSymbol(select(nodes[i])).call(setMarker, 'hover');
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
130
167
|
}
|
|
131
168
|
if (hoveredDataRef.current !== null) {
|
|
132
169
|
handleShapeHover(hoveredDataRef.current);
|
|
@@ -146,5 +183,6 @@ export const LineSeriesShapes = (args) => {
|
|
|
146
183
|
return (React.createElement(React.Fragment, null,
|
|
147
184
|
React.createElement("g", { ref: plotRef, className: b(), clipPath: `url(#${clipPathId})` }),
|
|
148
185
|
React.createElement("g", { ref: markersRef }),
|
|
186
|
+
React.createElement("g", { ref: hoverMarkersRef }),
|
|
149
187
|
React.createElement(HtmlLayer, { preparedData: htmlLayerData, htmlLayout: htmlLayout })));
|
|
150
188
|
};
|
|
@@ -86,17 +86,22 @@ export const prepareLineData = async (args) => {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
let markers = [];
|
|
89
|
-
|
|
89
|
+
const hasPerPointNormalMarkers = s.data.some((d) => { var _a, _b, _c; return (_c = (_b = (_a = d.marker) === null || _a === void 0 ? void 0 : _a.states) === null || _b === void 0 ? void 0 : _b.normal) === null || _c === void 0 ? void 0 : _c.enabled; });
|
|
90
|
+
if (s.marker.states.normal.enabled || hasPerPointNormalMarkers) {
|
|
90
91
|
markers = points.reduce((result, p) => {
|
|
92
|
+
var _a, _b, _c, _d;
|
|
91
93
|
if (p.y === null || p.x === null) {
|
|
92
94
|
return result;
|
|
93
95
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
const pointNormalEnabled = (_d = (_c = (_b = (_a = p.data.marker) === null || _a === void 0 ? void 0 : _a.states) === null || _b === void 0 ? void 0 : _b.normal) === null || _c === void 0 ? void 0 : _c.enabled) !== null && _d !== void 0 ? _d : false;
|
|
97
|
+
if (s.marker.states.normal.enabled || pointNormalEnabled) {
|
|
98
|
+
result.push({
|
|
99
|
+
point: p,
|
|
100
|
+
active: true,
|
|
101
|
+
hovered: false,
|
|
102
|
+
clipped: isOutsideBounds(p.x, p.y),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
100
105
|
return result;
|
|
101
106
|
}, []);
|
|
102
107
|
}
|
|
@@ -11,6 +11,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
11
11
|
const hoveredDataRef = React.useRef(null);
|
|
12
12
|
const plotRef = React.useRef(null);
|
|
13
13
|
const markersRef = React.useRef(null);
|
|
14
|
+
const hoverMarkersRef = React.useRef(null);
|
|
14
15
|
const allowOverlapDataLabels = React.useMemo(() => {
|
|
15
16
|
return preparedData.some((d) => d === null || d === void 0 ? void 0 : d.series.dataLabels.allowOverlap);
|
|
16
17
|
}, [preparedData]);
|
|
@@ -20,6 +21,7 @@ export const AreaSeriesShapes = (args) => {
|
|
|
20
21
|
}
|
|
21
22
|
const plotSvgElement = select(plotRef.current);
|
|
22
23
|
const markersSvgElement = select(markersRef.current);
|
|
24
|
+
const hoverMarkersSvgElement = select(hoverMarkersRef.current);
|
|
23
25
|
const hoverOptions = get(seriesOptions, 'area.states.hover');
|
|
24
26
|
const inactiveOptions = get(seriesOptions, 'area.states.inactive');
|
|
25
27
|
const line = lineGenerator()
|
|
@@ -141,6 +143,41 @@ export const AreaSeriesShapes = (args) => {
|
|
|
141
143
|
}
|
|
142
144
|
return d;
|
|
143
145
|
});
|
|
146
|
+
hoverMarkersSvgElement.selectAll('*').remove();
|
|
147
|
+
if (hoverEnabled && selected.length > 0) {
|
|
148
|
+
const hoverOnlyMarkers = [];
|
|
149
|
+
for (const chunk of selected) {
|
|
150
|
+
const seriesData = preparedData.find((pd) => pd.id === chunk.series.id);
|
|
151
|
+
if (!seriesData) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const { series } = seriesData;
|
|
155
|
+
if (series.marker.states.normal.enabled ||
|
|
156
|
+
!series.marker.states.hover.enabled) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const point = seriesData.points.find((p) => p.data === chunk.data);
|
|
160
|
+
if (!point || point.y === null) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
hoverOnlyMarkers.push({
|
|
164
|
+
point: point,
|
|
165
|
+
active: true,
|
|
166
|
+
hovered: true,
|
|
167
|
+
clipped: false,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (hoverOnlyMarkers.length > 0) {
|
|
171
|
+
hoverMarkersSvgElement
|
|
172
|
+
.selectAll('g')
|
|
173
|
+
.data(hoverOnlyMarkers)
|
|
174
|
+
.join('g')
|
|
175
|
+
.call(renderMarker)
|
|
176
|
+
.each((_d, i, nodes) => {
|
|
177
|
+
selectMarkerSymbol(select(nodes[i])).call(setMarker, 'hover');
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
144
181
|
}
|
|
145
182
|
if (hoveredDataRef.current !== null) {
|
|
146
183
|
handleShapeHover(hoveredDataRef.current);
|
|
@@ -160,5 +197,6 @@ export const AreaSeriesShapes = (args) => {
|
|
|
160
197
|
return (React.createElement(React.Fragment, null,
|
|
161
198
|
React.createElement("g", { ref: plotRef, className: b(), clipPath: `url(#${clipPathId})` }),
|
|
162
199
|
React.createElement("g", { ref: markersRef }),
|
|
200
|
+
React.createElement("g", { ref: hoverMarkersRef }),
|
|
163
201
|
React.createElement(HtmlLayer, { preparedData: htmlLayerData, htmlLayout: htmlLayout })));
|
|
164
202
|
};
|
|
@@ -220,17 +220,22 @@ export const prepareAreaData = async (args) => {
|
|
|
220
220
|
return pointsAcc;
|
|
221
221
|
}, []);
|
|
222
222
|
let markers = [];
|
|
223
|
-
|
|
223
|
+
const hasPerPointNormalMarkers = s.data.some((d) => { var _a, _b, _c; return (_c = (_b = (_a = d.marker) === null || _a === void 0 ? void 0 : _a.states) === null || _b === void 0 ? void 0 : _b.normal) === null || _c === void 0 ? void 0 : _c.enabled; });
|
|
224
|
+
if (s.marker.states.normal.enabled || hasPerPointNormalMarkers) {
|
|
224
225
|
markers = points.reduce((markersAcc, p) => {
|
|
226
|
+
var _a, _b, _c, _d;
|
|
225
227
|
if (p.y === null) {
|
|
226
228
|
return markersAcc;
|
|
227
229
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
230
|
+
const pointNormalEnabled = (_d = (_c = (_b = (_a = p.data.marker) === null || _a === void 0 ? void 0 : _a.states) === null || _b === void 0 ? void 0 : _b.normal) === null || _c === void 0 ? void 0 : _c.enabled) !== null && _d !== void 0 ? _d : false;
|
|
231
|
+
if (s.marker.states.normal.enabled || pointNormalEnabled) {
|
|
232
|
+
markersAcc.push({
|
|
233
|
+
point: p,
|
|
234
|
+
active: true,
|
|
235
|
+
hovered: false,
|
|
236
|
+
clipped: isOutsideBounds(p.x, p.y),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
234
239
|
return markersAcc;
|
|
235
240
|
}, []);
|
|
236
241
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { ascending, descending, reverse, sort } from 'd3';
|
|
1
|
+
import { ascending, descending, max, reverse, sort } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
3
|
import { getDataCategoryValue, getLabelsSize } from '../../../utils';
|
|
4
4
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
5
|
+
import { MIN_BAR_GAP, MIN_BAR_GROUP_GAP, MIN_BAR_WIDTH } from '../../constants';
|
|
5
6
|
import { getSeriesStackId } from '../../useSeries/utils';
|
|
6
|
-
import {
|
|
7
|
+
import { getBandSize } from '../../utils/get-band-size';
|
|
7
8
|
const isSeriesDataValid = (d) => d.y !== null;
|
|
8
9
|
async function getLabelData(d, xMax) {
|
|
9
10
|
var _a;
|
|
@@ -54,6 +55,8 @@ export const prepareBarXData = async (args) => {
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
})();
|
|
58
|
+
const domain = new Set();
|
|
59
|
+
let maxGroupSize = 1;
|
|
57
60
|
// series grouped by plotIndex > xValue > data[];
|
|
58
61
|
const dataByPlots = new Map();
|
|
59
62
|
series.forEach((s) => {
|
|
@@ -81,19 +84,27 @@ export const prepareBarXData = async (args) => {
|
|
|
81
84
|
data[key][stackId] = [];
|
|
82
85
|
}
|
|
83
86
|
data[key][stackId].push({ data: d, series: s });
|
|
87
|
+
domain.add(key);
|
|
84
88
|
}
|
|
85
89
|
});
|
|
90
|
+
maxGroupSize = Math.max(maxGroupSize, max(Object.values(data), (d) => Object.values(d).length) || 1);
|
|
86
91
|
});
|
|
87
92
|
const result = [];
|
|
93
|
+
const barMaxWidth = get(seriesOptions, 'bar-x.barMaxWidth');
|
|
94
|
+
const barPadding = get(seriesOptions, 'bar-x.barPadding');
|
|
95
|
+
const groupPadding = get(seriesOptions, 'bar-x.groupPadding');
|
|
96
|
+
const bandSize = getBandSize({
|
|
97
|
+
domain: Array.from(domain),
|
|
98
|
+
scale: xScale,
|
|
99
|
+
});
|
|
100
|
+
const groupGap = Math.max(bandSize * groupPadding, MIN_BAR_GROUP_GAP);
|
|
101
|
+
const groupSize = bandSize - groupGap;
|
|
102
|
+
const rectGap = Math.max(bandSize * barPadding, MIN_BAR_GAP);
|
|
103
|
+
const rectWidth = Math.max(MIN_BAR_WIDTH, Math.min(groupSize / maxGroupSize - rectGap, barMaxWidth));
|
|
88
104
|
const plotIndexes = Array.from(dataByPlots.keys());
|
|
89
105
|
for (let plotDataIndex = 0; plotDataIndex < plotIndexes.length; plotDataIndex++) {
|
|
90
106
|
const data = (_b = dataByPlots.get(plotIndexes[plotDataIndex])) !== null && _b !== void 0 ? _b : {};
|
|
91
107
|
const groupedData = Object.entries(data);
|
|
92
|
-
const { bandSize, barGap: rectGap, barSize: rectWidth, } = getBarXLayout({
|
|
93
|
-
groupedData: data,
|
|
94
|
-
seriesOptions,
|
|
95
|
-
scale: xScale,
|
|
96
|
-
});
|
|
97
108
|
for (let groupedDataIndex = 0; groupedDataIndex < groupedData.length; groupedDataIndex++) {
|
|
98
109
|
const [xValue, val] = groupedData[groupedDataIndex];
|
|
99
110
|
const stacks = Object.values(val);
|
|
@@ -11,6 +11,7 @@ export const LineSeriesShapes = (args) => {
|
|
|
11
11
|
const hoveredDataRef = React.useRef(null);
|
|
12
12
|
const plotRef = React.useRef(null);
|
|
13
13
|
const markersRef = React.useRef(null);
|
|
14
|
+
const hoverMarkersRef = React.useRef(null);
|
|
14
15
|
const allowOverlapDataLabels = React.useMemo(() => {
|
|
15
16
|
return preparedData.some((d) => d === null || d === void 0 ? void 0 : d.series.dataLabels.allowOverlap);
|
|
16
17
|
}, [preparedData]);
|
|
@@ -20,6 +21,7 @@ export const LineSeriesShapes = (args) => {
|
|
|
20
21
|
}
|
|
21
22
|
const plotSvgElement = select(plotRef.current);
|
|
22
23
|
const markersSvgElement = select(markersRef.current);
|
|
24
|
+
const hoverMarkersSvgElement = select(hoverMarkersRef.current);
|
|
23
25
|
const hoverOptions = get(seriesOptions, 'line.states.hover');
|
|
24
26
|
const inactiveOptions = get(seriesOptions, 'line.states.inactive');
|
|
25
27
|
const line = lineGenerator()
|
|
@@ -127,6 +129,41 @@ export const LineSeriesShapes = (args) => {
|
|
|
127
129
|
}
|
|
128
130
|
return d;
|
|
129
131
|
});
|
|
132
|
+
hoverMarkersSvgElement.selectAll('*').remove();
|
|
133
|
+
if (hoverEnabled && selected.length > 0) {
|
|
134
|
+
const hoverOnlyMarkers = [];
|
|
135
|
+
for (const chunk of selected) {
|
|
136
|
+
const seriesData = preparedData.find((pd) => pd.id === chunk.series.id);
|
|
137
|
+
if (!seriesData) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const { series } = seriesData;
|
|
141
|
+
if (series.marker.states.normal.enabled ||
|
|
142
|
+
!series.marker.states.hover.enabled) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const point = seriesData.points.find((p) => p.data === chunk.data);
|
|
146
|
+
if (!point || point.x === null || point.y === null) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
hoverOnlyMarkers.push({
|
|
150
|
+
point: point,
|
|
151
|
+
active: true,
|
|
152
|
+
hovered: true,
|
|
153
|
+
clipped: false,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
if (hoverOnlyMarkers.length > 0) {
|
|
157
|
+
hoverMarkersSvgElement
|
|
158
|
+
.selectAll('g')
|
|
159
|
+
.data(hoverOnlyMarkers)
|
|
160
|
+
.join('g')
|
|
161
|
+
.call(renderMarker)
|
|
162
|
+
.each((_d, i, nodes) => {
|
|
163
|
+
selectMarkerSymbol(select(nodes[i])).call(setMarker, 'hover');
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
130
167
|
}
|
|
131
168
|
if (hoveredDataRef.current !== null) {
|
|
132
169
|
handleShapeHover(hoveredDataRef.current);
|
|
@@ -146,5 +183,6 @@ export const LineSeriesShapes = (args) => {
|
|
|
146
183
|
return (React.createElement(React.Fragment, null,
|
|
147
184
|
React.createElement("g", { ref: plotRef, className: b(), clipPath: `url(#${clipPathId})` }),
|
|
148
185
|
React.createElement("g", { ref: markersRef }),
|
|
186
|
+
React.createElement("g", { ref: hoverMarkersRef }),
|
|
149
187
|
React.createElement(HtmlLayer, { preparedData: htmlLayerData, htmlLayout: htmlLayout })));
|
|
150
188
|
};
|
|
@@ -86,17 +86,22 @@ export const prepareLineData = async (args) => {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
let markers = [];
|
|
89
|
-
|
|
89
|
+
const hasPerPointNormalMarkers = s.data.some((d) => { var _a, _b, _c; return (_c = (_b = (_a = d.marker) === null || _a === void 0 ? void 0 : _a.states) === null || _b === void 0 ? void 0 : _b.normal) === null || _c === void 0 ? void 0 : _c.enabled; });
|
|
90
|
+
if (s.marker.states.normal.enabled || hasPerPointNormalMarkers) {
|
|
90
91
|
markers = points.reduce((result, p) => {
|
|
92
|
+
var _a, _b, _c, _d;
|
|
91
93
|
if (p.y === null || p.x === null) {
|
|
92
94
|
return result;
|
|
93
95
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
const pointNormalEnabled = (_d = (_c = (_b = (_a = p.data.marker) === null || _a === void 0 ? void 0 : _a.states) === null || _b === void 0 ? void 0 : _b.normal) === null || _c === void 0 ? void 0 : _c.enabled) !== null && _d !== void 0 ? _d : false;
|
|
97
|
+
if (s.marker.states.normal.enabled || pointNormalEnabled) {
|
|
98
|
+
result.push({
|
|
99
|
+
point: p,
|
|
100
|
+
active: true,
|
|
101
|
+
hovered: false,
|
|
102
|
+
clipped: isOutsideBounds(p.x, p.y),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
100
105
|
return result;
|
|
101
106
|
}, []);
|
|
102
107
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { BarXSeries, BarXSeriesData } from '../../types';
|
|
2
|
-
import type { PreparedXAxis } from '../useAxis/types';
|
|
3
|
-
import type { ChartScale } from '../useAxisScales/types';
|
|
4
|
-
import type { PreparedBarXSeries, PreparedSeriesOptions } from '../useSeries/types';
|
|
5
|
-
export declare function groupBarXDataByXValue<T extends BarXSeries | PreparedBarXSeries>(series: T[], xAxis: PreparedXAxis): Record<string | number, Record<string, {
|
|
6
|
-
data: BarXSeriesData;
|
|
7
|
-
series: T;
|
|
8
|
-
}[]>>;
|
|
9
|
-
export declare function getBarXLayout(args: {
|
|
10
|
-
seriesOptions: PreparedSeriesOptions;
|
|
11
|
-
groupedData: ReturnType<typeof groupBarXDataByXValue>;
|
|
12
|
-
scale: ChartScale | undefined;
|
|
13
|
-
}): {
|
|
14
|
-
bandSize: number;
|
|
15
|
-
barGap: number;
|
|
16
|
-
barSize: number;
|
|
17
|
-
};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { max } from 'd3';
|
|
2
|
-
import get from 'lodash/get';
|
|
3
|
-
import { getDataCategoryValue } from '../../utils';
|
|
4
|
-
import { MIN_BAR_GAP, MIN_BAR_GROUP_GAP, MIN_BAR_WIDTH } from '../constants';
|
|
5
|
-
import { getSeriesStackId } from '../useSeries/utils';
|
|
6
|
-
import { getBandSize } from './get-band-size';
|
|
7
|
-
export function groupBarXDataByXValue(series, xAxis) {
|
|
8
|
-
const data = {};
|
|
9
|
-
series.forEach((s) => {
|
|
10
|
-
s.data.forEach((d) => {
|
|
11
|
-
var _a;
|
|
12
|
-
const categories = (_a = xAxis.categories) !== null && _a !== void 0 ? _a : [];
|
|
13
|
-
const key = xAxis.type === 'category'
|
|
14
|
-
? getDataCategoryValue({ axisDirection: 'x', categories, data: d })
|
|
15
|
-
: d.x;
|
|
16
|
-
if (key !== undefined) {
|
|
17
|
-
if (!data[key]) {
|
|
18
|
-
data[key] = {};
|
|
19
|
-
}
|
|
20
|
-
const stackId = getSeriesStackId(s);
|
|
21
|
-
if (!data[key][stackId]) {
|
|
22
|
-
data[key][stackId] = [];
|
|
23
|
-
}
|
|
24
|
-
data[key][stackId].push({ data: d, series: s });
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
return data;
|
|
29
|
-
}
|
|
30
|
-
export function getBarXLayout(args) {
|
|
31
|
-
const { groupedData, seriesOptions, scale } = args;
|
|
32
|
-
const barMaxWidth = get(seriesOptions, 'bar-x.barMaxWidth');
|
|
33
|
-
const barPadding = get(seriesOptions, 'bar-x.barPadding');
|
|
34
|
-
const groupPadding = get(seriesOptions, 'bar-x.groupPadding');
|
|
35
|
-
const domain = Object.keys(groupedData);
|
|
36
|
-
const bandSize = getBandSize({ domain, scale: scale });
|
|
37
|
-
const groupGap = Math.max(bandSize * groupPadding, MIN_BAR_GROUP_GAP);
|
|
38
|
-
const maxGroupSize = max(Object.values(groupedData), (d) => Object.values(d).length) || 1;
|
|
39
|
-
const groupSize = bandSize - groupGap;
|
|
40
|
-
const barGap = Math.max(bandSize * barPadding, MIN_BAR_GAP);
|
|
41
|
-
const barSize = Math.max(MIN_BAR_WIDTH, Math.min(groupSize / maxGroupSize - barGap, barMaxWidth));
|
|
42
|
-
return { bandSize, barGap, barSize };
|
|
43
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { BarXSeries, BarXSeriesData } from '../../types';
|
|
2
|
-
import type { PreparedXAxis } from '../useAxis/types';
|
|
3
|
-
import type { ChartScale } from '../useAxisScales/types';
|
|
4
|
-
import type { PreparedBarXSeries, PreparedSeriesOptions } from '../useSeries/types';
|
|
5
|
-
export declare function groupBarXDataByXValue<T extends BarXSeries | PreparedBarXSeries>(series: T[], xAxis: PreparedXAxis): Record<string | number, Record<string, {
|
|
6
|
-
data: BarXSeriesData;
|
|
7
|
-
series: T;
|
|
8
|
-
}[]>>;
|
|
9
|
-
export declare function getBarXLayout(args: {
|
|
10
|
-
seriesOptions: PreparedSeriesOptions;
|
|
11
|
-
groupedData: ReturnType<typeof groupBarXDataByXValue>;
|
|
12
|
-
scale: ChartScale | undefined;
|
|
13
|
-
}): {
|
|
14
|
-
bandSize: number;
|
|
15
|
-
barGap: number;
|
|
16
|
-
barSize: number;
|
|
17
|
-
};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { max } from 'd3';
|
|
2
|
-
import get from 'lodash/get';
|
|
3
|
-
import { getDataCategoryValue } from '../../utils';
|
|
4
|
-
import { MIN_BAR_GAP, MIN_BAR_GROUP_GAP, MIN_BAR_WIDTH } from '../constants';
|
|
5
|
-
import { getSeriesStackId } from '../useSeries/utils';
|
|
6
|
-
import { getBandSize } from './get-band-size';
|
|
7
|
-
export function groupBarXDataByXValue(series, xAxis) {
|
|
8
|
-
const data = {};
|
|
9
|
-
series.forEach((s) => {
|
|
10
|
-
s.data.forEach((d) => {
|
|
11
|
-
var _a;
|
|
12
|
-
const categories = (_a = xAxis.categories) !== null && _a !== void 0 ? _a : [];
|
|
13
|
-
const key = xAxis.type === 'category'
|
|
14
|
-
? getDataCategoryValue({ axisDirection: 'x', categories, data: d })
|
|
15
|
-
: d.x;
|
|
16
|
-
if (key !== undefined) {
|
|
17
|
-
if (!data[key]) {
|
|
18
|
-
data[key] = {};
|
|
19
|
-
}
|
|
20
|
-
const stackId = getSeriesStackId(s);
|
|
21
|
-
if (!data[key][stackId]) {
|
|
22
|
-
data[key][stackId] = [];
|
|
23
|
-
}
|
|
24
|
-
data[key][stackId].push({ data: d, series: s });
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
return data;
|
|
29
|
-
}
|
|
30
|
-
export function getBarXLayout(args) {
|
|
31
|
-
const { groupedData, seriesOptions, scale } = args;
|
|
32
|
-
const barMaxWidth = get(seriesOptions, 'bar-x.barMaxWidth');
|
|
33
|
-
const barPadding = get(seriesOptions, 'bar-x.barPadding');
|
|
34
|
-
const groupPadding = get(seriesOptions, 'bar-x.groupPadding');
|
|
35
|
-
const domain = Object.keys(groupedData);
|
|
36
|
-
const bandSize = getBandSize({ domain, scale: scale });
|
|
37
|
-
const groupGap = Math.max(bandSize * groupPadding, MIN_BAR_GROUP_GAP);
|
|
38
|
-
const maxGroupSize = max(Object.values(groupedData), (d) => Object.values(d).length) || 1;
|
|
39
|
-
const groupSize = bandSize - groupGap;
|
|
40
|
-
const barGap = Math.max(bandSize * barPadding, MIN_BAR_GAP);
|
|
41
|
-
const barSize = Math.max(MIN_BAR_WIDTH, Math.min(groupSize / maxGroupSize - barGap, barMaxWidth));
|
|
42
|
-
return { bandSize, barGap, barSize };
|
|
43
|
-
}
|