@coinbase/cds-mobile-visualization 3.4.0-beta.22 → 3.4.0-beta.23
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/CHANGELOG.md +6 -0
- package/dts/chart/CartesianChart.d.ts +39 -7
- package/dts/chart/CartesianChart.d.ts.map +1 -1
- package/dts/chart/Path.d.ts.map +1 -1
- package/dts/chart/area/Area.d.ts +7 -0
- package/dts/chart/area/Area.d.ts.map +1 -1
- package/dts/chart/area/AreaChart.d.ts +5 -5
- package/dts/chart/area/AreaChart.d.ts.map +1 -1
- package/dts/chart/area/DottedArea.d.ts.map +1 -1
- package/dts/chart/area/GradientArea.d.ts.map +1 -1
- package/dts/chart/area/SolidArea.d.ts.map +1 -1
- package/dts/chart/axis/Axis.d.ts +3 -1
- package/dts/chart/axis/Axis.d.ts.map +1 -1
- package/dts/chart/axis/XAxis.d.ts +6 -0
- package/dts/chart/axis/XAxis.d.ts.map +1 -1
- package/dts/chart/axis/YAxis.d.ts +1 -0
- package/dts/chart/axis/YAxis.d.ts.map +1 -1
- package/dts/chart/bar/Bar.d.ts +4 -2
- package/dts/chart/bar/Bar.d.ts.map +1 -1
- package/dts/chart/bar/BarChart.d.ts +49 -9
- package/dts/chart/bar/BarChart.d.ts.map +1 -1
- package/dts/chart/bar/BarPlot.d.ts.map +1 -1
- package/dts/chart/bar/BarStack.d.ts +30 -9
- package/dts/chart/bar/BarStack.d.ts.map +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
- package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
- package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
- package/dts/chart/gradient/Gradient.d.ts +5 -0
- package/dts/chart/gradient/Gradient.d.ts.map +1 -1
- package/dts/chart/line/DottedLine.d.ts.map +1 -1
- package/dts/chart/line/Line.d.ts +7 -0
- package/dts/chart/line/Line.d.ts.map +1 -1
- package/dts/chart/line/LineChart.d.ts +5 -5
- package/dts/chart/line/LineChart.d.ts.map +1 -1
- package/dts/chart/line/ReferenceLine.d.ts +1 -0
- package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
- package/dts/chart/line/SolidLine.d.ts.map +1 -1
- package/dts/chart/point/Point.d.ts +7 -0
- package/dts/chart/point/Point.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +2 -1
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -1
- package/dts/chart/scrubber/Scrubber.d.ts +8 -0
- package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
- package/dts/chart/utils/axis.d.ts +20 -9
- package/dts/chart/utils/axis.d.ts.map +1 -1
- package/dts/chart/utils/bar.d.ts +4 -3
- package/dts/chart/utils/bar.d.ts.map +1 -1
- package/dts/chart/utils/chart.d.ts +13 -0
- package/dts/chart/utils/chart.d.ts.map +1 -1
- package/dts/chart/utils/context.d.ts +21 -6
- package/dts/chart/utils/context.d.ts.map +1 -1
- package/dts/chart/utils/gradient.d.ts +3 -1
- package/dts/chart/utils/gradient.d.ts.map +1 -1
- package/dts/chart/utils/path.d.ts +20 -0
- package/dts/chart/utils/path.d.ts.map +1 -1
- package/dts/chart/utils/point.d.ts +7 -0
- package/dts/chart/utils/point.d.ts.map +1 -1
- package/esm/chart/CartesianChart.js +107 -68
- package/esm/chart/Path.js +10 -7
- package/esm/chart/area/Area.js +19 -9
- package/esm/chart/area/AreaChart.js +11 -9
- package/esm/chart/area/DottedArea.js +11 -6
- package/esm/chart/area/GradientArea.js +11 -6
- package/esm/chart/area/SolidArea.js +3 -1
- package/esm/chart/area/__stories__/AreaChart.stories.js +30 -2
- package/esm/chart/axis/XAxis.js +14 -21
- package/esm/chart/axis/YAxis.js +4 -3
- package/esm/chart/bar/Bar.js +9 -5
- package/esm/chart/bar/BarChart.js +34 -31
- package/esm/chart/bar/BarPlot.js +7 -5
- package/esm/chart/bar/BarStack.js +176 -36
- package/esm/chart/bar/BarStackGroup.js +37 -27
- package/esm/chart/bar/DefaultBar.js +24 -8
- package/esm/chart/bar/DefaultBarStack.js +24 -10
- package/esm/chart/bar/__stories__/BarChart.stories.js +99 -3
- package/esm/chart/gradient/Gradient.js +2 -1
- package/esm/chart/line/DottedLine.js +3 -1
- package/esm/chart/line/Line.js +32 -19
- package/esm/chart/line/LineChart.js +9 -8
- package/esm/chart/line/SolidLine.js +3 -1
- package/esm/chart/line/__stories__/LineChart.stories.js +31 -0
- package/esm/chart/point/Point.js +2 -1
- package/esm/chart/scrubber/DefaultScrubberBeacon.js +1 -1
- package/esm/chart/scrubber/DefaultScrubberLabel.js +26 -10
- package/esm/chart/scrubber/Scrubber.js +47 -21
- package/esm/chart/scrubber/ScrubberBeaconGroup.js +24 -20
- package/esm/chart/scrubber/ScrubberProvider.js +29 -24
- package/esm/chart/scrubber/__stories__/Scrubber.stories.js +135 -1
- package/esm/chart/utils/axis.js +42 -14
- package/esm/chart/utils/bar.js +5 -4
- package/esm/chart/utils/chart.js +18 -5
- package/esm/chart/utils/context.js +7 -0
- package/esm/chart/utils/gradient.js +8 -4
- package/esm/chart/utils/path.js +90 -61
- package/esm/chart/utils/point.js +28 -18
- package/package.json +5 -5
|
@@ -42,7 +42,7 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
|
|
|
42
42
|
drawingArea
|
|
43
43
|
} = useCartesianChartContext();
|
|
44
44
|
const targetSeries = useMemo(() => getSeries(seriesId), [getSeries, seriesId]);
|
|
45
|
-
const xScale = useMemo(() => getXSerializableScale(), [getXSerializableScale]);
|
|
45
|
+
const xScale = useMemo(() => getXSerializableScale(targetSeries == null ? void 0 : targetSeries.xAxisId), [getXSerializableScale, targetSeries == null ? void 0 : targetSeries.xAxisId]);
|
|
46
46
|
const yScale = useMemo(() => getYSerializableScale(targetSeries == null ? void 0 : targetSeries.yAxisId), [getYSerializableScale, targetSeries == null ? void 0 : targetSeries.yAxisId]);
|
|
47
47
|
const color = useMemo(() => {
|
|
48
48
|
var _ref2;
|
|
@@ -1,28 +1,44 @@
|
|
|
1
|
-
const _excluded = ["
|
|
1
|
+
const _excluded = ["dx", "dy"];
|
|
2
2
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
3
3
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
4
|
-
import { memo } from 'react';
|
|
4
|
+
import { memo, useMemo } from 'react';
|
|
5
5
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
6
6
|
import { DefaultReferenceLineLabel } from '../line';
|
|
7
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
8
|
/**
|
|
9
9
|
* DefaultScrubberLabel is the default label component for the scrubber line.
|
|
10
10
|
* It will automatically add padding around the label when elevated to fit within chart bounds to prevent shadow from being cutoff.
|
|
11
|
-
*
|
|
11
|
+
* In vertical layout, it positions the label above the scrubber line.
|
|
12
|
+
* In horizontal layout, it centers the label in the chart's right inset.
|
|
12
13
|
*/
|
|
13
14
|
export const DefaultScrubberLabel = /*#__PURE__*/memo(_ref => {
|
|
14
15
|
let {
|
|
15
|
-
|
|
16
|
-
dy
|
|
17
|
-
boundsInset
|
|
16
|
+
dx: dxProp,
|
|
17
|
+
dy: dyProp
|
|
18
18
|
} = _ref,
|
|
19
19
|
props = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
20
20
|
const {
|
|
21
|
-
drawingArea
|
|
21
|
+
drawingArea,
|
|
22
|
+
layout,
|
|
23
|
+
width: chartWidth
|
|
22
24
|
} = useCartesianChartContext();
|
|
25
|
+
const isHorizontalLayout = layout === 'horizontal';
|
|
26
|
+
const dx = useMemo(() => {
|
|
27
|
+
if (dxProp !== undefined) return dxProp;
|
|
28
|
+
if (isHorizontalLayout) {
|
|
29
|
+
const drawingAreaEnd = drawingArea.x + drawingArea.width;
|
|
30
|
+
const rightOffset = chartWidth - drawingAreaEnd;
|
|
31
|
+
return rightOffset / 2;
|
|
32
|
+
}
|
|
33
|
+
return 0;
|
|
34
|
+
}, [drawingArea.width, drawingArea.x, dxProp, isHorizontalLayout, chartWidth]);
|
|
35
|
+
const dy = useMemo(() => {
|
|
36
|
+
if (dyProp !== undefined) return dyProp;
|
|
37
|
+
if (isHorizontalLayout) return 0;
|
|
38
|
+
return -0.5 * drawingArea.y;
|
|
39
|
+
}, [dyProp, isHorizontalLayout, drawingArea.y]);
|
|
23
40
|
return /*#__PURE__*/_jsx(DefaultReferenceLineLabel, _extends({
|
|
24
|
-
|
|
25
|
-
dy: dy
|
|
26
|
-
verticalAlignment: verticalAlignment
|
|
41
|
+
dx: dx,
|
|
42
|
+
dy: dy
|
|
27
43
|
}, props));
|
|
28
44
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
1
2
|
import React, { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo } from 'react';
|
|
2
3
|
import { runOnJS, useAnimatedReaction, useDerivedValue, useSharedValue } from 'react-native-reanimated';
|
|
3
4
|
import { useTheme } from '@coinbase/cds-mobile';
|
|
@@ -45,15 +46,19 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
45
46
|
scrubberPosition
|
|
46
47
|
} = useScrubberContext();
|
|
47
48
|
const {
|
|
49
|
+
layout,
|
|
48
50
|
getXSerializableScale,
|
|
51
|
+
getYSerializableScale,
|
|
49
52
|
getXAxis,
|
|
53
|
+
getYAxis,
|
|
50
54
|
series,
|
|
51
55
|
drawingArea,
|
|
52
56
|
animate,
|
|
53
57
|
dataLength
|
|
54
58
|
} = useCartesianChartContext();
|
|
55
|
-
const
|
|
56
|
-
const
|
|
59
|
+
const categoryAxisIsX = useMemo(() => layout !== 'horizontal', [layout]);
|
|
60
|
+
const indexAxis = useMemo(() => categoryAxisIsX ? getXAxis() : getYAxis(), [categoryAxisIsX, getXAxis, getYAxis]);
|
|
61
|
+
const indexScale = useMemo(() => categoryAxisIsX ? getXSerializableScale() : getYSerializableScale(), [categoryAxisIsX, getXSerializableScale, getYSerializableScale]);
|
|
57
62
|
|
|
58
63
|
// Animation state for delayed scrubber rendering (matches web timing)
|
|
59
64
|
const scrubberOpacity = useSharedValue(animate ? 0 : 1);
|
|
@@ -76,27 +81,43 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
76
81
|
var _scrubberPosition$val;
|
|
77
82
|
return (_scrubberPosition$val = scrubberPosition.value) != null ? _scrubberPosition$val : Math.max(0, dataLength - 1);
|
|
78
83
|
}, [scrubberPosition, dataLength]);
|
|
79
|
-
const
|
|
80
|
-
if (
|
|
81
|
-
const
|
|
82
|
-
return typeof
|
|
84
|
+
const dataValue = useDerivedValue(() => {
|
|
85
|
+
if (indexAxis != null && indexAxis.data && Array.isArray(indexAxis.data) && indexAxis.data[dataIndex.value] !== undefined) {
|
|
86
|
+
const axisValue = indexAxis.data[dataIndex.value];
|
|
87
|
+
return typeof axisValue === 'string' ? dataIndex.value : axisValue;
|
|
83
88
|
}
|
|
84
89
|
return dataIndex.value;
|
|
85
|
-
}, [
|
|
90
|
+
}, [indexAxis, dataIndex]);
|
|
86
91
|
const lineOpacity = useDerivedValue(() => {
|
|
87
92
|
return scrubberPosition.value !== undefined ? 1 : 0;
|
|
88
93
|
}, [scrubberPosition]);
|
|
89
94
|
const overlayOpacity = useDerivedValue(() => {
|
|
90
95
|
return scrubberPosition.value !== undefined ? 0.8 : 0;
|
|
91
96
|
}, [scrubberPosition]);
|
|
97
|
+
const pixelPosition = useDerivedValue(() => {
|
|
98
|
+
if (dataValue.value === undefined || !indexScale) return undefined;
|
|
99
|
+
return getPointOnSerializableScale(dataValue.value, indexScale);
|
|
100
|
+
}, [dataValue, indexScale]);
|
|
92
101
|
const overlayWidth = useDerivedValue(() => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
102
|
+
var _pixelPosition$value;
|
|
103
|
+
const pixel = (_pixelPosition$value = pixelPosition.value) != null ? _pixelPosition$value : 0;
|
|
104
|
+
return categoryAxisIsX ? drawingArea.x + drawingArea.width - pixel + overlayOffset : drawingArea.width + overlayOffset * 2;
|
|
105
|
+
}, [pixelPosition, categoryAxisIsX, drawingArea, overlayOffset]);
|
|
106
|
+
const overlayHeight = useDerivedValue(() => {
|
|
107
|
+
var _pixelPosition$value2;
|
|
108
|
+
const pixel = (_pixelPosition$value2 = pixelPosition.value) != null ? _pixelPosition$value2 : 0;
|
|
109
|
+
return categoryAxisIsX ? drawingArea.height + overlayOffset * 2 : drawingArea.y + drawingArea.height - pixel + overlayOffset;
|
|
110
|
+
}, [pixelPosition, categoryAxisIsX, drawingArea, overlayOffset]);
|
|
96
111
|
const overlayX = useDerivedValue(() => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
112
|
+
var _pixelPosition$value3;
|
|
113
|
+
const pixel = (_pixelPosition$value3 = pixelPosition.value) != null ? _pixelPosition$value3 : 0;
|
|
114
|
+
return categoryAxisIsX ? pixel : drawingArea.x - overlayOffset;
|
|
115
|
+
}, [pixelPosition, categoryAxisIsX, drawingArea, overlayOffset]);
|
|
116
|
+
const overlayY = useDerivedValue(() => {
|
|
117
|
+
var _pixelPosition$value4;
|
|
118
|
+
const pixel = (_pixelPosition$value4 = pixelPosition.value) != null ? _pixelPosition$value4 : 0;
|
|
119
|
+
return categoryAxisIsX ? drawingArea.y - overlayOffset : pixel;
|
|
120
|
+
}, [pixelPosition, categoryAxisIsX, drawingArea, overlayOffset]);
|
|
100
121
|
const resolvedLabelValue = useSharedValue('');
|
|
101
122
|
const updateResolvedLabel = useCallback(index => {
|
|
102
123
|
if (!label) {
|
|
@@ -125,7 +146,8 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
125
146
|
color: s.color
|
|
126
147
|
}))) != null ? _series$filter$filter : [];
|
|
127
148
|
}, [series, filteredSeriesIds]);
|
|
128
|
-
const
|
|
149
|
+
const showBeaconLabels = !hideBeaconLabels && categoryAxisIsX && beaconLabels.length > 0;
|
|
150
|
+
const isReady = !!indexScale;
|
|
129
151
|
const groupEnterTransition = useMemo(() => getTransition(transitions == null ? void 0 : transitions.enter, animate, defaultAccessoryEnterTransition), [transitions == null ? void 0 : transitions.enter, animate]);
|
|
130
152
|
useEffect(() => {
|
|
131
153
|
if (animate && isReady) {
|
|
@@ -137,29 +159,33 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
137
159
|
opacity: scrubberOpacity,
|
|
138
160
|
children: [!hideOverlay && /*#__PURE__*/_jsx(Rect, {
|
|
139
161
|
color: theme.color.bg,
|
|
140
|
-
height:
|
|
162
|
+
height: overlayHeight,
|
|
141
163
|
opacity: overlayOpacity,
|
|
142
164
|
width: overlayWidth,
|
|
143
165
|
x: overlayX,
|
|
144
|
-
y:
|
|
145
|
-
}), !hideLine && /*#__PURE__*/_jsx(ReferenceLine, {
|
|
166
|
+
y: overlayY
|
|
167
|
+
}), !hideLine && /*#__PURE__*/_jsx(ReferenceLine, _extends({
|
|
146
168
|
LabelComponent: LabelComponent,
|
|
147
|
-
LineComponent: LineComponent
|
|
148
|
-
|
|
169
|
+
LineComponent: LineComponent
|
|
170
|
+
}, categoryAxisIsX ? {
|
|
171
|
+
dataX: dataValue
|
|
172
|
+
} : {
|
|
173
|
+
dataY: dataValue
|
|
174
|
+
}, {
|
|
149
175
|
label: resolvedLabelValue,
|
|
150
176
|
labelBoundsInset: labelBoundsInset,
|
|
151
177
|
labelElevated: labelElevated,
|
|
152
178
|
labelFont: labelFont,
|
|
153
179
|
opacity: lineOpacity,
|
|
154
180
|
stroke: lineStroke
|
|
155
|
-
}), /*#__PURE__*/_jsx(ScrubberBeaconGroup, {
|
|
181
|
+
})), /*#__PURE__*/_jsx(ScrubberBeaconGroup, {
|
|
156
182
|
ref: beaconGroupRef,
|
|
157
183
|
BeaconComponent: BeaconComponent,
|
|
158
184
|
idlePulse: idlePulse,
|
|
159
185
|
seriesIds: filteredSeriesIds,
|
|
160
186
|
stroke: beaconStroke,
|
|
161
187
|
transitions: transitions
|
|
162
|
-
}),
|
|
188
|
+
}), showBeaconLabels && /*#__PURE__*/_jsx(ScrubberBeaconLabelGroup, {
|
|
163
189
|
BeaconLabelComponent: BeaconLabelComponent,
|
|
164
190
|
labelFont: beaconLabelFont,
|
|
165
191
|
labelHorizontalOffset: beaconLabelHorizontalOffset,
|
|
@@ -12,7 +12,7 @@ const BeaconWithData = /*#__PURE__*/memo(_ref => {
|
|
|
12
12
|
let {
|
|
13
13
|
seriesId,
|
|
14
14
|
dataIndex,
|
|
15
|
-
|
|
15
|
+
dataIndexValue,
|
|
16
16
|
isIdle,
|
|
17
17
|
BeaconComponent,
|
|
18
18
|
idlePulse,
|
|
@@ -22,6 +22,7 @@ const BeaconWithData = /*#__PURE__*/memo(_ref => {
|
|
|
22
22
|
stroke
|
|
23
23
|
} = _ref;
|
|
24
24
|
const {
|
|
25
|
+
layout,
|
|
25
26
|
getSeries,
|
|
26
27
|
getSeriesData,
|
|
27
28
|
getXScale,
|
|
@@ -49,10 +50,10 @@ const BeaconWithData = /*#__PURE__*/memo(_ref => {
|
|
|
49
50
|
// Get scales for gradient evaluation
|
|
50
51
|
const gradientScale = useMemo(() => {
|
|
51
52
|
if (!gradient) return undefined;
|
|
52
|
-
const scale = gradient.axis === 'x' ? getXScale() : getYScale(series == null ? void 0 : series.yAxisId);
|
|
53
|
+
const scale = gradient.axis === 'x' ? getXScale(series == null ? void 0 : series.xAxisId) : getYScale(series == null ? void 0 : series.yAxisId);
|
|
53
54
|
if (!scale) return undefined;
|
|
54
55
|
return convertToSerializableScale(scale);
|
|
55
|
-
}, [gradient, getXScale, getYScale, series == null ? void 0 : series.yAxisId]);
|
|
56
|
+
}, [gradient, getXScale, getYScale, series == null ? void 0 : series.xAxisId, series == null ? void 0 : series.yAxisId]);
|
|
56
57
|
const gradientStops = useMemo(() => {
|
|
57
58
|
if (!gradient || !gradientScale) return undefined;
|
|
58
59
|
const domain = {
|
|
@@ -70,25 +71,25 @@ const BeaconWithData = /*#__PURE__*/memo(_ref => {
|
|
|
70
71
|
var _series$color;
|
|
71
72
|
if (gradient && gradientScale && gradientStops) {
|
|
72
73
|
var _gradient$axis;
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
74
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
75
|
+
const gradientAxis = (_gradient$axis = gradient.axis) != null ? _gradient$axis : 'y';
|
|
76
|
+
const valueForAxis = gradientAxis === 'x' ? categoryAxisIsX ? dataIndexValue.value : dataY.value : categoryAxisIsX ? dataY.value : dataIndexValue.value;
|
|
77
|
+
const evaluatedColor = evaluateGradientAtValue(gradientStops, valueForAxis, gradientScale);
|
|
78
|
+
if (evaluatedColor) {
|
|
79
|
+
return evaluatedColor;
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// Fallback to series color
|
|
84
84
|
return (_series$color = series == null ? void 0 : series.color) != null ? _series$color : theme.color.fgPrimary;
|
|
85
|
-
}, [gradient, gradientScale, gradientStops,
|
|
85
|
+
}, [gradient, gradientScale, gradientStops, dataIndexValue, dataY, series == null ? void 0 : series.color, theme.color.fgPrimary, layout]);
|
|
86
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
86
87
|
return /*#__PURE__*/_jsx(BeaconComponent, {
|
|
87
88
|
ref: beaconRef,
|
|
88
89
|
animate: animate,
|
|
89
90
|
color: color,
|
|
90
|
-
dataX:
|
|
91
|
-
dataY: dataY,
|
|
91
|
+
dataX: categoryAxisIsX ? dataIndexValue : dataY,
|
|
92
|
+
dataY: categoryAxisIsX ? dataY : dataIndexValue,
|
|
92
93
|
idlePulse: idlePulse,
|
|
93
94
|
isIdle: isIdle,
|
|
94
95
|
seriesId: seriesId,
|
|
@@ -109,12 +110,15 @@ export const ScrubberBeaconGroup = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_r
|
|
|
109
110
|
scrubberPosition
|
|
110
111
|
} = useScrubberContext();
|
|
111
112
|
const {
|
|
113
|
+
layout,
|
|
112
114
|
getXAxis,
|
|
115
|
+
getYAxis,
|
|
113
116
|
series,
|
|
114
117
|
dataLength,
|
|
115
118
|
animate
|
|
116
119
|
} = useCartesianChartContext();
|
|
117
|
-
const
|
|
120
|
+
const categoryAxisIsX = useMemo(() => layout !== 'horizontal', [layout]);
|
|
121
|
+
const indexAxis = useMemo(() => categoryAxisIsX ? getXAxis() : getYAxis(), [categoryAxisIsX, getXAxis, getYAxis]);
|
|
118
122
|
|
|
119
123
|
// Expose imperative handle with pulse method
|
|
120
124
|
useImperativeHandle(ref, () => ({
|
|
@@ -132,14 +136,14 @@ export const ScrubberBeaconGroup = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_r
|
|
|
132
136
|
var _scrubberPosition$val;
|
|
133
137
|
return (_scrubberPosition$val = scrubberPosition.value) != null ? _scrubberPosition$val : Math.max(0, dataLength - 1);
|
|
134
138
|
}, [scrubberPosition, dataLength]);
|
|
135
|
-
const
|
|
136
|
-
// Convert index to actual
|
|
137
|
-
if (
|
|
138
|
-
const dataValue =
|
|
139
|
+
const dataIndexValue = useDerivedValue(() => {
|
|
140
|
+
// Convert index to actual category-axis value if axis has data.
|
|
141
|
+
if (indexAxis != null && indexAxis.data && Array.isArray(indexAxis.data) && indexAxis.data[dataIndex.value] !== undefined) {
|
|
142
|
+
const dataValue = indexAxis.data[dataIndex.value];
|
|
139
143
|
return typeof dataValue === 'string' ? dataIndex.value : dataValue;
|
|
140
144
|
}
|
|
141
145
|
return dataIndex.value;
|
|
142
|
-
}, [
|
|
146
|
+
}, [indexAxis, dataIndex]);
|
|
143
147
|
const isIdle = useDerivedValue(() => {
|
|
144
148
|
return scrubberPosition.value === undefined;
|
|
145
149
|
}, [scrubberPosition]);
|
|
@@ -155,7 +159,7 @@ export const ScrubberBeaconGroup = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_r
|
|
|
155
159
|
animate: animate,
|
|
156
160
|
beaconRef: createBeaconRef(s.id),
|
|
157
161
|
dataIndex: dataIndex,
|
|
158
|
-
|
|
162
|
+
dataIndexValue: dataIndexValue,
|
|
159
163
|
idlePulse: idlePulse,
|
|
160
164
|
isIdle: isIdle,
|
|
161
165
|
seriesId: s.id,
|
|
@@ -23,25 +23,29 @@ export const ScrubberProvider = _ref => {
|
|
|
23
23
|
throw new Error('ScrubberProvider must be used within a ChartContext');
|
|
24
24
|
}
|
|
25
25
|
const {
|
|
26
|
+
layout,
|
|
26
27
|
getXSerializableScale,
|
|
27
|
-
|
|
28
|
+
getYSerializableScale,
|
|
29
|
+
getXAxis,
|
|
30
|
+
getYAxis
|
|
28
31
|
} = chartContext;
|
|
29
32
|
const scrubberPosition = useSharedValue(undefined);
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
+
const categoryAxisIsX = useMemo(() => layout !== 'horizontal', [layout]);
|
|
34
|
+
const categoryAxis = useMemo(() => categoryAxisIsX ? getXAxis() : getYAxis(), [categoryAxisIsX, getXAxis, getYAxis]);
|
|
35
|
+
const categoryScale = useMemo(() => categoryAxisIsX ? getXSerializableScale() : getYSerializableScale(), [categoryAxisIsX, getXSerializableScale, getYSerializableScale]);
|
|
36
|
+
const getDataIndexFromPosition = useCallback(touchPosition => {
|
|
33
37
|
'worklet';
|
|
34
38
|
|
|
35
|
-
if (!
|
|
36
|
-
if (
|
|
37
|
-
const [domainMin, domainMax] =
|
|
39
|
+
if (!categoryScale || !categoryAxis) return 0;
|
|
40
|
+
if (categoryScale.type === 'band') {
|
|
41
|
+
const [domainMin, domainMax] = categoryScale.domain;
|
|
38
42
|
const categoryCount = domainMax - domainMin + 1;
|
|
39
43
|
let closestIndex = 0;
|
|
40
44
|
let closestDistance = Infinity;
|
|
41
45
|
for (let i = 0; i < categoryCount; i++) {
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
44
|
-
const distance = Math.abs(
|
|
46
|
+
const categoryPos = getPointOnSerializableScale(i, categoryScale);
|
|
47
|
+
if (categoryPos !== undefined) {
|
|
48
|
+
const distance = Math.abs(touchPosition - categoryPos);
|
|
45
49
|
if (distance < closestDistance) {
|
|
46
50
|
closestDistance = distance;
|
|
47
51
|
closestIndex = i;
|
|
@@ -50,18 +54,17 @@ export const ScrubberProvider = _ref => {
|
|
|
50
54
|
}
|
|
51
55
|
return closestIndex;
|
|
52
56
|
} else {
|
|
53
|
-
// For numeric scales with axis data, find the nearest data point
|
|
54
|
-
const axisData =
|
|
57
|
+
// For numeric scales with axis data, find the nearest data point.
|
|
58
|
+
const axisData = categoryAxis.data;
|
|
55
59
|
if (axisData && Array.isArray(axisData) && typeof axisData[0] === 'number') {
|
|
56
|
-
// We have numeric axis data - find the closest data point
|
|
57
60
|
const numericData = axisData;
|
|
58
61
|
let closestIndex = 0;
|
|
59
62
|
let closestDistance = Infinity;
|
|
60
63
|
for (let i = 0; i < numericData.length; i++) {
|
|
61
|
-
const
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
const distance = Math.abs(
|
|
64
|
+
const dataValue = numericData[i];
|
|
65
|
+
const categoryPos = getPointOnSerializableScale(dataValue, categoryScale);
|
|
66
|
+
if (categoryPos !== undefined) {
|
|
67
|
+
const distance = Math.abs(touchPosition - categoryPos);
|
|
65
68
|
if (distance < closestDistance) {
|
|
66
69
|
closestDistance = distance;
|
|
67
70
|
closestIndex = i;
|
|
@@ -71,13 +74,13 @@ export const ScrubberProvider = _ref => {
|
|
|
71
74
|
return closestIndex;
|
|
72
75
|
} else {
|
|
73
76
|
var _domain$min, _domain$max;
|
|
74
|
-
const
|
|
75
|
-
const dataIndex = Math.round(
|
|
76
|
-
const domain =
|
|
77
|
+
const dataValue = invertSerializableScale(touchPosition, categoryScale);
|
|
78
|
+
const dataIndex = Math.round(dataValue);
|
|
79
|
+
const domain = categoryAxis.domain;
|
|
77
80
|
return Math.max((_domain$min = domain.min) != null ? _domain$min : 0, Math.min(dataIndex, (_domain$max = domain.max) != null ? _domain$max : 0));
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
|
-
}, [
|
|
83
|
+
}, [categoryAxis, categoryScale]);
|
|
81
84
|
const handleStartEndHaptics = useCallback(() => {
|
|
82
85
|
void Haptics.lightImpact();
|
|
83
86
|
}, []);
|
|
@@ -95,13 +98,15 @@ export const ScrubberProvider = _ref => {
|
|
|
95
98
|
|
|
96
99
|
// Android does not trigger onUpdate when the gesture starts. This achieves consistent behavior across both iOS and Android
|
|
97
100
|
if (Platform.OS === 'android') {
|
|
98
|
-
const
|
|
101
|
+
const pointerPosition = categoryAxisIsX ? event.x : event.y;
|
|
102
|
+
const newScrubberPosition = getDataIndexFromPosition(pointerPosition);
|
|
99
103
|
if (newScrubberPosition !== scrubberPosition.value) {
|
|
100
104
|
scrubberPosition.value = newScrubberPosition;
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
107
|
}).onUpdate(function onUpdate(event) {
|
|
104
|
-
const
|
|
108
|
+
const pointerPosition = categoryAxisIsX ? event.x : event.y;
|
|
109
|
+
const newScrubberPosition = getDataIndexFromPosition(pointerPosition);
|
|
105
110
|
if (newScrubberPosition !== scrubberPosition.value) {
|
|
106
111
|
scrubberPosition.value = newScrubberPosition;
|
|
107
112
|
}
|
|
@@ -114,7 +119,7 @@ export const ScrubberProvider = _ref => {
|
|
|
114
119
|
if (enableScrubbing) {
|
|
115
120
|
scrubberPosition.value = undefined;
|
|
116
121
|
}
|
|
117
|
-
}), [allowOverflowGestures, handleStartEndHaptics,
|
|
122
|
+
}), [allowOverflowGestures, handleStartEndHaptics, getDataIndexFromPosition, categoryAxisIsX, scrubberPosition, enableScrubbing]);
|
|
118
123
|
const contextValue = useMemo(() => ({
|
|
119
124
|
enableScrubbing: !!enableScrubbing,
|
|
120
125
|
scrubberPosition
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const _excluded = ["seriesId", "color", "label"],
|
|
2
|
-
_excluded2 = ["seriesId", "color", "label"]
|
|
2
|
+
_excluded2 = ["seriesId", "color", "label"],
|
|
3
|
+
_excluded3 = ["seriesId", "color"];
|
|
3
4
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
4
5
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
5
6
|
import { memo, useCallback, useMemo, useRef, useState } from 'react';
|
|
@@ -654,6 +655,136 @@ const HideOverlay = () => {
|
|
|
654
655
|
})
|
|
655
656
|
});
|
|
656
657
|
};
|
|
658
|
+
const matchupBlueData = [47, 50, 51, 52, 53, 53, 53, 53, 52, 51, 51, 52, 53, 55, 57, 58, 59, 61, 63, 65, 64, 64, 64, 64, 64, 63, 63, 63, 64, 66, 68, 70, 71, 72, 74, 76, 76, 75, 74, 73, 74, 75, 75, 78];
|
|
659
|
+
const matchupRedData = matchupBlueData.map(value => 100 - value);
|
|
660
|
+
const matchupTeamLabels = {
|
|
661
|
+
blue: 'BLUE',
|
|
662
|
+
red: 'RED'
|
|
663
|
+
};
|
|
664
|
+
const MatchupBeaconLabels = () => {
|
|
665
|
+
const theme = useTheme();
|
|
666
|
+
const MatchupScrubberBeaconLabel = /*#__PURE__*/memo(_ref7 => {
|
|
667
|
+
var _matchupTeamLabels$se;
|
|
668
|
+
let {
|
|
669
|
+
seriesId,
|
|
670
|
+
color
|
|
671
|
+
} = _ref7,
|
|
672
|
+
props = _objectWithoutPropertiesLoose(_ref7, _excluded3);
|
|
673
|
+
const {
|
|
674
|
+
getSeriesData,
|
|
675
|
+
series,
|
|
676
|
+
fontProvider
|
|
677
|
+
} = useCartesianChartContext();
|
|
678
|
+
const {
|
|
679
|
+
scrubberPosition
|
|
680
|
+
} = useScrubberContext();
|
|
681
|
+
const seriesData = useMemo(() => getLineData(getSeriesData(seriesId)), [getSeriesData, seriesId]);
|
|
682
|
+
const dataLength = useMemo(() => {
|
|
683
|
+
var _series$reduce3;
|
|
684
|
+
return (_series$reduce3 = series == null ? void 0 : series.reduce((max, currentSeries) => {
|
|
685
|
+
var _data$length3;
|
|
686
|
+
const data = getSeriesData(currentSeries.id);
|
|
687
|
+
return Math.max(max, (_data$length3 = data == null ? void 0 : data.length) != null ? _data$length3 : 0);
|
|
688
|
+
}, 0)) != null ? _series$reduce3 : 0;
|
|
689
|
+
}, [series, getSeriesData]);
|
|
690
|
+
const dataIndex = useDerivedValue(() => {
|
|
691
|
+
var _scrubberPosition$val3;
|
|
692
|
+
return (_scrubberPosition$val3 = scrubberPosition.value) != null ? _scrubberPosition$val3 : Math.max(0, dataLength - 1);
|
|
693
|
+
}, [scrubberPosition, dataLength]);
|
|
694
|
+
const teamLabel = (_matchupTeamLabels$se = matchupTeamLabels[seriesId]) != null ? _matchupTeamLabels$se : String(seriesId).toUpperCase();
|
|
695
|
+
const labelColor = color != null ? color : theme.color.fgPrimary;
|
|
696
|
+
const legalFontSize = theme.fontSize.legal;
|
|
697
|
+
const title3FontSize = theme.fontSize.title3;
|
|
698
|
+
const teamStyle = useMemo(() => ({
|
|
699
|
+
fontFamilies: ['Inter'],
|
|
700
|
+
fontSize: legalFontSize,
|
|
701
|
+
fontStyle: {
|
|
702
|
+
weight: FontWeight.Normal
|
|
703
|
+
},
|
|
704
|
+
color: Skia.Color(labelColor)
|
|
705
|
+
}), [labelColor, legalFontSize]);
|
|
706
|
+
const percentageStyle = useMemo(() => ({
|
|
707
|
+
fontFamilies: ['Inter'],
|
|
708
|
+
fontSize: title3FontSize,
|
|
709
|
+
fontStyle: {
|
|
710
|
+
weight: FontWeight.Bold
|
|
711
|
+
},
|
|
712
|
+
color: Skia.Color(labelColor)
|
|
713
|
+
}), [title3FontSize, labelColor]);
|
|
714
|
+
const matchupLabel = useDerivedValue(() => {
|
|
715
|
+
if (seriesData === undefined) {
|
|
716
|
+
return teamLabel;
|
|
717
|
+
}
|
|
718
|
+
const value = seriesData[dataIndex.value];
|
|
719
|
+
const builder = Skia.ParagraphBuilder.Make({
|
|
720
|
+
textAlign: TextAlign.Left
|
|
721
|
+
}, fontProvider);
|
|
722
|
+
builder.pushStyle(teamStyle);
|
|
723
|
+
builder.addText(teamLabel);
|
|
724
|
+
builder.addText('\n');
|
|
725
|
+
builder.pushStyle(percentageStyle);
|
|
726
|
+
builder.addText(value + "%");
|
|
727
|
+
const paragraph = builder.build();
|
|
728
|
+
paragraph.layout(240);
|
|
729
|
+
return paragraph;
|
|
730
|
+
}, [dataIndex, fontProvider, percentageStyle, seriesData, teamLabel, teamStyle]);
|
|
731
|
+
return /*#__PURE__*/_jsx(DefaultScrubberBeaconLabel, _extends({}, props, {
|
|
732
|
+
background: "transparent",
|
|
733
|
+
color: labelColor,
|
|
734
|
+
elevated: false,
|
|
735
|
+
inset: 0,
|
|
736
|
+
label: matchupLabel,
|
|
737
|
+
seriesId: seriesId
|
|
738
|
+
}));
|
|
739
|
+
});
|
|
740
|
+
return /*#__PURE__*/_jsx(LineChart, {
|
|
741
|
+
enableScrubbing: true,
|
|
742
|
+
showArea: true,
|
|
743
|
+
areaType: "dotted",
|
|
744
|
+
height: 300,
|
|
745
|
+
inset: {
|
|
746
|
+
bottom: 8,
|
|
747
|
+
left: 8,
|
|
748
|
+
top: 8,
|
|
749
|
+
right: 0
|
|
750
|
+
},
|
|
751
|
+
series: [{
|
|
752
|
+
id: 'blue',
|
|
753
|
+
data: matchupBlueData,
|
|
754
|
+
color: "rgb(" + theme.spectrum.blue50 + ")",
|
|
755
|
+
label: 'BLUE'
|
|
756
|
+
}, {
|
|
757
|
+
id: 'red',
|
|
758
|
+
data: matchupRedData,
|
|
759
|
+
color: "rgb(" + theme.spectrum.red50 + ")",
|
|
760
|
+
label: 'RED'
|
|
761
|
+
}],
|
|
762
|
+
xAxis: {
|
|
763
|
+
range: _ref8 => {
|
|
764
|
+
let {
|
|
765
|
+
min,
|
|
766
|
+
max
|
|
767
|
+
} = _ref8;
|
|
768
|
+
return {
|
|
769
|
+
min,
|
|
770
|
+
max: max - 64
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
},
|
|
774
|
+
yAxis: {
|
|
775
|
+
domain: {
|
|
776
|
+
min: 0,
|
|
777
|
+
max: 100
|
|
778
|
+
}
|
|
779
|
+
},
|
|
780
|
+
children: /*#__PURE__*/_jsx(Scrubber, {
|
|
781
|
+
idlePulse: true,
|
|
782
|
+
BeaconLabelComponent: MatchupScrubberBeaconLabel,
|
|
783
|
+
beaconLabelHorizontalOffset: 16,
|
|
784
|
+
beaconLabelPreferredSide: "right"
|
|
785
|
+
})
|
|
786
|
+
});
|
|
787
|
+
};
|
|
657
788
|
const ExampleNavigator = () => {
|
|
658
789
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
659
790
|
const examples = useMemo(() => [{
|
|
@@ -707,6 +838,9 @@ const ExampleNavigator = () => {
|
|
|
707
838
|
}, {
|
|
708
839
|
title: 'Hide Overlay',
|
|
709
840
|
component: /*#__PURE__*/_jsx(HideOverlay, {})
|
|
841
|
+
}, {
|
|
842
|
+
title: 'Matchup Beacon Labels',
|
|
843
|
+
component: /*#__PURE__*/_jsx(MatchupBeaconLabels, {})
|
|
710
844
|
}], []);
|
|
711
845
|
const currentExample = examples[currentIndex];
|
|
712
846
|
const isFirstExample = currentIndex === 0;
|