@coinbase/cds-mobile-visualization 3.4.0-beta.10 → 3.4.0-beta.12
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 +12 -0
- package/dts/chart/axis/Axis.d.ts +19 -41
- package/dts/chart/axis/Axis.d.ts.map +1 -1
- package/dts/chart/axis/XAxis.d.ts.map +1 -1
- package/dts/chart/axis/YAxis.d.ts.map +1 -1
- package/dts/chart/line/Line.d.ts +13 -11
- package/dts/chart/line/Line.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -1
- package/dts/chart/utils/axis.d.ts +25 -1
- package/dts/chart/utils/axis.d.ts.map +1 -1
- package/dts/chart/utils/point.d.ts +17 -12
- package/dts/chart/utils/point.d.ts.map +1 -1
- package/dts/chart/utils/scale.d.ts +11 -0
- package/dts/chart/utils/scale.d.ts.map +1 -1
- package/esm/chart/axis/Axis.js +5 -41
- package/esm/chart/axis/XAxis.js +102 -27
- package/esm/chart/axis/YAxis.js +100 -23
- package/esm/chart/axis/__stories__/Axis.stories.js +259 -0
- package/esm/chart/bar/__stories__/BarChart.stories.js +39 -0
- package/esm/chart/scrubber/DefaultScrubberBeacon.js +21 -21
- package/esm/chart/utils/axis.js +45 -29
- package/esm/chart/utils/point.js +64 -21
- package/esm/chart/utils/scale.js +13 -2
- package/package.json +5 -5
package/esm/chart/axis/YAxis.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
const _excluded = ["axisId", "position", "showGrid", "requestedTickCount", "ticks", "tickLabelFormatter", "TickLabelComponent", "GridLineComponent", "LineComponent", "TickMarkLineComponent", "tickMarkLabelGap", "minTickLabelGap", "showTickMarks", "showLine", "tickMarkSize", "tickInterval", "label", "labelGap", "width"];
|
|
1
|
+
const _excluded = ["axisId", "position", "showGrid", "requestedTickCount", "ticks", "tickLabelFormatter", "TickLabelComponent", "GridLineComponent", "LineComponent", "TickMarkLineComponent", "tickMarkLabelGap", "minTickLabelGap", "showTickMarks", "showLine", "tickMarkSize", "tickInterval", "label", "labelGap", "width", "bandGridLinePlacement", "bandTickMarkPlacement"];
|
|
2
2
|
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; }
|
|
3
3
|
import { memo, useCallback, useEffect, useId, useMemo } from 'react';
|
|
4
4
|
import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
|
|
5
5
|
import { Group, vec } from '@shopify/react-native-skia';
|
|
6
6
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
7
7
|
import { DottedLine } from '../line/DottedLine';
|
|
8
|
-
import { ReferenceLine } from '../line/ReferenceLine';
|
|
9
8
|
import { SolidLine } from '../line/SolidLine';
|
|
10
9
|
import { ChartText } from '../text/ChartText';
|
|
11
10
|
import { ChartTextGroup } from '../text/ChartTextGroup';
|
|
12
|
-
import { getAxisTicksData, isCategoricalScale, lineToPath } from '../utils';
|
|
11
|
+
import { getAxisTicksData, getPointOnScale, isCategoricalScale, lineToPath, toPointAnchor } from '../utils';
|
|
13
12
|
import { DefaultAxisTickLabel } from './DefaultAxisTickLabel';
|
|
14
13
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
15
14
|
const AXIS_WIDTH = 44;
|
|
@@ -34,13 +33,16 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
|
|
|
34
33
|
tickInterval,
|
|
35
34
|
label,
|
|
36
35
|
labelGap = 4,
|
|
37
|
-
width = label ? AXIS_WIDTH + LABEL_SIZE : AXIS_WIDTH
|
|
36
|
+
width = label ? AXIS_WIDTH + LABEL_SIZE : AXIS_WIDTH,
|
|
37
|
+
bandGridLinePlacement = 'edges',
|
|
38
|
+
bandTickMarkPlacement = 'middle'
|
|
38
39
|
} = _ref,
|
|
39
40
|
props = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
40
41
|
const theme = useTheme();
|
|
41
42
|
const registrationId = useId();
|
|
42
43
|
const {
|
|
43
44
|
animate,
|
|
45
|
+
drawingArea,
|
|
44
46
|
getYScale,
|
|
45
47
|
getYAxis,
|
|
46
48
|
registerAxis,
|
|
@@ -50,10 +52,6 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
|
|
|
50
52
|
const yScale = getYScale(axisId);
|
|
51
53
|
const yAxis = getYAxis(axisId);
|
|
52
54
|
const axisBounds = getAxisBounds(registrationId);
|
|
53
|
-
|
|
54
|
-
// Note: gridOpacity not currently used in Skia version
|
|
55
|
-
// const gridOpacity = useSharedValue(1);
|
|
56
|
-
|
|
57
55
|
useEffect(() => {
|
|
58
56
|
registerAxis(registrationId, position, width);
|
|
59
57
|
return () => unregisterAxis(registrationId);
|
|
@@ -102,6 +100,72 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
|
|
|
102
100
|
tickInterval: tickInterval
|
|
103
101
|
});
|
|
104
102
|
}, [ticks, yScale, requestedTickCount, tickInterval, yAxis == null ? void 0 : yAxis.data]);
|
|
103
|
+
const isBandScale = useMemo(() => {
|
|
104
|
+
if (!yScale) return false;
|
|
105
|
+
return isCategoricalScale(yScale);
|
|
106
|
+
}, [yScale]);
|
|
107
|
+
|
|
108
|
+
// Compute grid line positions (including bounds closing line for band scales)
|
|
109
|
+
const gridLinePositions = useMemo(() => {
|
|
110
|
+
if (!yScale) return [];
|
|
111
|
+
return ticksData.flatMap((tick, index) => {
|
|
112
|
+
if (!isBandScale) {
|
|
113
|
+
return [{
|
|
114
|
+
y: tick.position,
|
|
115
|
+
key: "grid-" + tick.tick + "-" + index
|
|
116
|
+
}];
|
|
117
|
+
}
|
|
118
|
+
const bandScale = yScale;
|
|
119
|
+
const isLastTick = index === ticksData.length - 1;
|
|
120
|
+
const isEdges = bandGridLinePlacement === 'edges';
|
|
121
|
+
const startY = getPointOnScale(tick.tick, bandScale, toPointAnchor(bandGridLinePlacement));
|
|
122
|
+
const positions = [{
|
|
123
|
+
y: startY,
|
|
124
|
+
key: "grid-" + tick.tick + "-" + index
|
|
125
|
+
}];
|
|
126
|
+
|
|
127
|
+
// For edges on last tick, add the closing line at stepEnd
|
|
128
|
+
if (isLastTick && isEdges) {
|
|
129
|
+
const endY = getPointOnScale(tick.tick, bandScale, 'stepEnd');
|
|
130
|
+
positions.push({
|
|
131
|
+
y: endY,
|
|
132
|
+
key: "grid-" + tick.tick + "-" + index + "-end"
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return positions;
|
|
136
|
+
});
|
|
137
|
+
}, [ticksData, yScale, isBandScale, bandGridLinePlacement]);
|
|
138
|
+
|
|
139
|
+
// Compute tick mark positions (including bounds closing tick for band scales)
|
|
140
|
+
const tickMarkPositions = useMemo(() => {
|
|
141
|
+
if (!yScale) return [];
|
|
142
|
+
return ticksData.flatMap((tick, index) => {
|
|
143
|
+
if (!isBandScale) {
|
|
144
|
+
return [{
|
|
145
|
+
y: tick.position,
|
|
146
|
+
key: "tick-mark-" + tick.tick + "-" + index
|
|
147
|
+
}];
|
|
148
|
+
}
|
|
149
|
+
const bandScale = yScale;
|
|
150
|
+
const isLastTick = index === ticksData.length - 1;
|
|
151
|
+
const isEdges = bandTickMarkPlacement === 'edges';
|
|
152
|
+
const startY = getPointOnScale(tick.tick, bandScale, toPointAnchor(bandTickMarkPlacement));
|
|
153
|
+
const positions = [{
|
|
154
|
+
y: startY,
|
|
155
|
+
key: "tick-mark-" + tick.tick + "-" + index
|
|
156
|
+
}];
|
|
157
|
+
|
|
158
|
+
// For edges on last tick, add the closing tick mark at stepEnd
|
|
159
|
+
if (isLastTick && isEdges) {
|
|
160
|
+
const endY = getPointOnScale(tick.tick, bandScale, 'stepEnd');
|
|
161
|
+
positions.push({
|
|
162
|
+
y: endY,
|
|
163
|
+
key: "tick-mark-" + tick.tick + "-" + index + "-end"
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return positions;
|
|
167
|
+
});
|
|
168
|
+
}, [ticksData, yScale, isBandScale, bandTickMarkPlacement]);
|
|
105
169
|
const chartTextData = useMemo(() => {
|
|
106
170
|
if (!axisBounds) return null;
|
|
107
171
|
return ticksData.map(tick => {
|
|
@@ -122,17 +186,29 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
|
|
|
122
186
|
if (!yScale || !axisBounds) return;
|
|
123
187
|
const labelX = position === 'left' ? axisBounds.x + LABEL_SIZE / 2 : axisBounds.x + axisBounds.width - LABEL_SIZE / 2;
|
|
124
188
|
const labelY = axisBounds.y + axisBounds.height / 2;
|
|
189
|
+
|
|
190
|
+
// Pre-compute tick mark X coordinates
|
|
191
|
+
const tickXLeft = axisBounds.x;
|
|
192
|
+
const tickXRight = axisBounds.x + axisBounds.width;
|
|
193
|
+
const tickXStart = position === 'left' ? tickXRight : tickXLeft;
|
|
194
|
+
const tickXEnd = position === 'left' ? tickXRight - tickMarkSize : tickXLeft + tickMarkSize;
|
|
195
|
+
|
|
196
|
+
// Note: Unlike web, mobile renders grid lines and tick marks immediately without fade animation.
|
|
197
|
+
// This is because Skia can measure text dimensions synchronously, so there's no need to hide
|
|
198
|
+
// elements while waiting for measurements (web uses async ResizeObserver).
|
|
125
199
|
return /*#__PURE__*/_jsxs(Group, {
|
|
126
200
|
children: [showGrid && /*#__PURE__*/_jsx(Group, {
|
|
127
|
-
children:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
201
|
+
children: gridLinePositions.map(_ref2 => {
|
|
202
|
+
let {
|
|
203
|
+
y,
|
|
204
|
+
key
|
|
205
|
+
} = _ref2;
|
|
206
|
+
return /*#__PURE__*/_jsx(GridLineComponent, {
|
|
207
|
+
animate: false,
|
|
208
|
+
clipPath: null,
|
|
209
|
+
d: lineToPath(drawingArea.x, y, drawingArea.x + drawingArea.width, y),
|
|
210
|
+
stroke: theme.color.bgLine
|
|
211
|
+
}, key);
|
|
136
212
|
})
|
|
137
213
|
}), chartTextData && /*#__PURE__*/_jsx(ChartTextGroup, {
|
|
138
214
|
prioritizeEndLabels: true,
|
|
@@ -140,18 +216,19 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
|
|
|
140
216
|
labels: chartTextData,
|
|
141
217
|
minGap: minTickLabelGap
|
|
142
218
|
}), axisBounds && showTickMarks && /*#__PURE__*/_jsx(Group, {
|
|
143
|
-
children:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
219
|
+
children: tickMarkPositions.map(_ref3 => {
|
|
220
|
+
let {
|
|
221
|
+
y,
|
|
222
|
+
key
|
|
223
|
+
} = _ref3;
|
|
147
224
|
return /*#__PURE__*/_jsx(TickMarkLineComponent, {
|
|
148
225
|
animate: false,
|
|
149
226
|
clipPath: null,
|
|
150
|
-
d: lineToPath(
|
|
227
|
+
d: lineToPath(tickXStart, y, tickXEnd, y),
|
|
151
228
|
stroke: theme.color.fg,
|
|
152
229
|
strokeCap: "square",
|
|
153
230
|
strokeWidth: 1
|
|
154
|
-
},
|
|
231
|
+
}, key);
|
|
155
232
|
})
|
|
156
233
|
}), showLine && /*#__PURE__*/_jsx(LineComponent, {
|
|
157
234
|
animate: false,
|
|
@@ -2,6 +2,7 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
|
|
|
2
2
|
import { memo, useCallback, useMemo } from 'react';
|
|
3
3
|
import { Example, ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
|
|
4
4
|
import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
|
|
5
|
+
import { BarPlot } from '../../bar';
|
|
5
6
|
import { CartesianChart } from '../../CartesianChart';
|
|
6
7
|
import { LineChart, SolidLine } from '../../line';
|
|
7
8
|
import { Line } from '../../line/Line';
|
|
@@ -194,6 +195,105 @@ const MultipleYAxesExample = () => /*#__PURE__*/_jsxs(CartesianChart, {
|
|
|
194
195
|
seriesId: "log"
|
|
195
196
|
}), /*#__PURE__*/_jsx(Scrubber, {})]
|
|
196
197
|
});
|
|
198
|
+
const AxesOnAllSides = () => {
|
|
199
|
+
const theme = useTheme();
|
|
200
|
+
const data = [30, 45, 60, 80, 55, 40, 65];
|
|
201
|
+
const labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
|
202
|
+
return /*#__PURE__*/_jsxs(CartesianChart, {
|
|
203
|
+
height: defaultChartHeight,
|
|
204
|
+
series: [{
|
|
205
|
+
id: 'data',
|
|
206
|
+
data,
|
|
207
|
+
color: theme.color.accentBoldBlue
|
|
208
|
+
}],
|
|
209
|
+
xAxis: {
|
|
210
|
+
data: labels
|
|
211
|
+
},
|
|
212
|
+
yAxis: {
|
|
213
|
+
domain: {
|
|
214
|
+
min: 0,
|
|
215
|
+
max: 100
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
219
|
+
showLine: true,
|
|
220
|
+
showTickMarks: true,
|
|
221
|
+
label: "Bottom Axis",
|
|
222
|
+
position: "bottom",
|
|
223
|
+
ticks: labels.map((label, index) => index)
|
|
224
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
225
|
+
showLine: true,
|
|
226
|
+
showTickMarks: true,
|
|
227
|
+
label: "Top Axis",
|
|
228
|
+
position: "top",
|
|
229
|
+
ticks: labels.map((label, index) => index)
|
|
230
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
231
|
+
showLine: true,
|
|
232
|
+
showTickMarks: true,
|
|
233
|
+
label: "Left Axis",
|
|
234
|
+
position: "left"
|
|
235
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
236
|
+
showLine: true,
|
|
237
|
+
showTickMarks: true,
|
|
238
|
+
label: "Right Axis",
|
|
239
|
+
position: "right"
|
|
240
|
+
}), /*#__PURE__*/_jsx(Line, {
|
|
241
|
+
curve: "natural",
|
|
242
|
+
seriesId: "data"
|
|
243
|
+
})]
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
const CustomTickMarkSizes = () => {
|
|
247
|
+
const theme = useTheme();
|
|
248
|
+
const data = [25, 50, 75, 60, 45, 80, 35];
|
|
249
|
+
return /*#__PURE__*/_jsxs(CartesianChart, {
|
|
250
|
+
height: 300,
|
|
251
|
+
series: [{
|
|
252
|
+
id: 'data',
|
|
253
|
+
data,
|
|
254
|
+
color: theme.color.accentBoldGreen
|
|
255
|
+
}],
|
|
256
|
+
xAxis: {
|
|
257
|
+
data: ['A', 'B', 'C', 'D', 'E', 'F', 'G']
|
|
258
|
+
},
|
|
259
|
+
yAxis: {
|
|
260
|
+
domain: {
|
|
261
|
+
min: 0,
|
|
262
|
+
max: 100
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
266
|
+
showLine: true,
|
|
267
|
+
showTickMarks: true,
|
|
268
|
+
label: "tickMarkSize=4 (default)",
|
|
269
|
+
tickMarkSize: 4
|
|
270
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
271
|
+
showLine: true,
|
|
272
|
+
showTickMarks: true,
|
|
273
|
+
height: 60,
|
|
274
|
+
label: "tickMarkSize=8",
|
|
275
|
+
position: "top",
|
|
276
|
+
tickMarkSize: 8
|
|
277
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
278
|
+
showLine: true,
|
|
279
|
+
showTickMarks: true,
|
|
280
|
+
label: "tickMarkSize=16",
|
|
281
|
+
position: "left",
|
|
282
|
+
tickMarkSize: 16,
|
|
283
|
+
width: 76
|
|
284
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
285
|
+
showLine: true,
|
|
286
|
+
showTickMarks: true,
|
|
287
|
+
label: "tickMarkSize=24",
|
|
288
|
+
position: "right",
|
|
289
|
+
tickMarkSize: 24,
|
|
290
|
+
width: 84
|
|
291
|
+
}), /*#__PURE__*/_jsx(Line, {
|
|
292
|
+
curve: "monotone",
|
|
293
|
+
seriesId: "data"
|
|
294
|
+
})]
|
|
295
|
+
});
|
|
296
|
+
};
|
|
197
297
|
const DomainLimitType = _ref => {
|
|
198
298
|
let {
|
|
199
299
|
limit
|
|
@@ -249,6 +349,97 @@ const DomainLimitType = _ref => {
|
|
|
249
349
|
}), /*#__PURE__*/_jsx(Scrubber, {})]
|
|
250
350
|
});
|
|
251
351
|
};
|
|
352
|
+
|
|
353
|
+
// Band scale with tick filtering - show every other tick
|
|
354
|
+
const BandScaleTickFiltering = () => /*#__PURE__*/_jsxs(CartesianChart, {
|
|
355
|
+
height: defaultChartHeight,
|
|
356
|
+
inset: 8,
|
|
357
|
+
series: [{
|
|
358
|
+
id: 'data',
|
|
359
|
+
data: [10, 22, 29, 45, 98, 45, 22, 35, 42, 18, 55, 67]
|
|
360
|
+
}],
|
|
361
|
+
xAxis: {
|
|
362
|
+
scaleType: 'band',
|
|
363
|
+
data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
|
364
|
+
},
|
|
365
|
+
yAxis: {
|
|
366
|
+
domain: {
|
|
367
|
+
min: 0
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
371
|
+
showGrid: true,
|
|
372
|
+
showLine: true,
|
|
373
|
+
showTickMarks: true,
|
|
374
|
+
label: "ticks={(i) => i % 2 === 0}",
|
|
375
|
+
ticks: i => i % 2 === 0
|
|
376
|
+
}), /*#__PURE__*/_jsx(BarPlot, {})]
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Band scale with explicit ticks array
|
|
380
|
+
const BandScaleExplicitTicks = () => /*#__PURE__*/_jsxs(CartesianChart, {
|
|
381
|
+
height: defaultChartHeight,
|
|
382
|
+
inset: 8,
|
|
383
|
+
series: [{
|
|
384
|
+
id: 'data',
|
|
385
|
+
data: [10, 22, 29, 45, 98, 45, 22]
|
|
386
|
+
}],
|
|
387
|
+
xAxis: {
|
|
388
|
+
scaleType: 'band',
|
|
389
|
+
data: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
|
390
|
+
},
|
|
391
|
+
yAxis: {
|
|
392
|
+
domain: {
|
|
393
|
+
min: 0
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
397
|
+
showGrid: true,
|
|
398
|
+
showLine: true,
|
|
399
|
+
showTickMarks: true,
|
|
400
|
+
label: "ticks={[0, 3, 6]} (first, middle, last)",
|
|
401
|
+
ticks: [0, 3, 6]
|
|
402
|
+
}), /*#__PURE__*/_jsx(BarPlot, {})]
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// Line chart on band scale - comparing grid placements
|
|
406
|
+
const LineChartOnBandScale = _ref2 => {
|
|
407
|
+
let {
|
|
408
|
+
bandGridLinePlacement
|
|
409
|
+
} = _ref2;
|
|
410
|
+
const theme = useTheme();
|
|
411
|
+
return /*#__PURE__*/_jsxs(CartesianChart, {
|
|
412
|
+
height: 180,
|
|
413
|
+
inset: 8,
|
|
414
|
+
series: [{
|
|
415
|
+
id: 'line1',
|
|
416
|
+
data: [10, 22, 29, 45, 98, 45, 22],
|
|
417
|
+
color: theme.color.accentBoldBlue
|
|
418
|
+
}],
|
|
419
|
+
xAxis: {
|
|
420
|
+
scaleType: 'band',
|
|
421
|
+
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|
422
|
+
},
|
|
423
|
+
yAxis: {
|
|
424
|
+
domain: {
|
|
425
|
+
min: 0
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
429
|
+
showGrid: true,
|
|
430
|
+
showLine: true,
|
|
431
|
+
showTickMarks: true,
|
|
432
|
+
bandGridLinePlacement: bandGridLinePlacement,
|
|
433
|
+
bandTickMarkPlacement: bandGridLinePlacement,
|
|
434
|
+
label: "bandGridLinePlacement: " + bandGridLinePlacement
|
|
435
|
+
}), /*#__PURE__*/_jsx(YAxis, {
|
|
436
|
+
showGrid: true,
|
|
437
|
+
position: "left"
|
|
438
|
+
}), /*#__PURE__*/_jsx(Line, {
|
|
439
|
+
seriesId: "line1"
|
|
440
|
+
})]
|
|
441
|
+
});
|
|
442
|
+
};
|
|
252
443
|
const AxisStories = () => {
|
|
253
444
|
return /*#__PURE__*/_jsxs(ExampleScreen, {
|
|
254
445
|
children: [/*#__PURE__*/_jsx(Example, {
|
|
@@ -270,6 +461,74 @@ const AxisStories = () => {
|
|
|
270
461
|
children: /*#__PURE__*/_jsx(DomainLimitType, {
|
|
271
462
|
limit: "nice"
|
|
272
463
|
})
|
|
464
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
465
|
+
title: "Band Axis Grid Alignment",
|
|
466
|
+
children: /*#__PURE__*/_jsxs(CartesianChart, {
|
|
467
|
+
height: 350,
|
|
468
|
+
inset: 8,
|
|
469
|
+
series: [{
|
|
470
|
+
id: 'prices',
|
|
471
|
+
data: [10, 22, 29, 45, 98, 45, 22]
|
|
472
|
+
}],
|
|
473
|
+
xAxis: {
|
|
474
|
+
scaleType: 'band',
|
|
475
|
+
data: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
|
476
|
+
},
|
|
477
|
+
yAxis: {
|
|
478
|
+
domain: {
|
|
479
|
+
min: 0
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
483
|
+
showGrid: true,
|
|
484
|
+
showLine: true,
|
|
485
|
+
showTickMarks: true,
|
|
486
|
+
label: "Default"
|
|
487
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
488
|
+
showLine: true,
|
|
489
|
+
showTickMarks: true,
|
|
490
|
+
bandTickMarkPlacement: "start",
|
|
491
|
+
label: "Start"
|
|
492
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
493
|
+
showLine: true,
|
|
494
|
+
showTickMarks: true,
|
|
495
|
+
bandTickMarkPlacement: "end",
|
|
496
|
+
label: "End"
|
|
497
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
498
|
+
showLine: true,
|
|
499
|
+
showTickMarks: true,
|
|
500
|
+
bandTickMarkPlacement: "middle",
|
|
501
|
+
label: "Middle"
|
|
502
|
+
}), /*#__PURE__*/_jsx(XAxis, {
|
|
503
|
+
showLine: true,
|
|
504
|
+
showTickMarks: true,
|
|
505
|
+
bandTickMarkPlacement: "edges",
|
|
506
|
+
label: "Edges"
|
|
507
|
+
}), /*#__PURE__*/_jsx(BarPlot, {})]
|
|
508
|
+
})
|
|
509
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
510
|
+
title: "Band Scale - Tick Filtering",
|
|
511
|
+
children: /*#__PURE__*/_jsx(BandScaleTickFiltering, {})
|
|
512
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
513
|
+
title: "Band Scale - Explicit Ticks",
|
|
514
|
+
children: /*#__PURE__*/_jsx(BandScaleExplicitTicks, {})
|
|
515
|
+
}), /*#__PURE__*/_jsxs(Example, {
|
|
516
|
+
title: "Line Chart on Band Scale - Grid Positions",
|
|
517
|
+
children: [/*#__PURE__*/_jsx(LineChartOnBandScale, {
|
|
518
|
+
bandGridLinePlacement: "edges"
|
|
519
|
+
}), /*#__PURE__*/_jsx(LineChartOnBandScale, {
|
|
520
|
+
bandGridLinePlacement: "start"
|
|
521
|
+
}), /*#__PURE__*/_jsx(LineChartOnBandScale, {
|
|
522
|
+
bandGridLinePlacement: "middle"
|
|
523
|
+
}), /*#__PURE__*/_jsx(LineChartOnBandScale, {
|
|
524
|
+
bandGridLinePlacement: "end"
|
|
525
|
+
})]
|
|
526
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
527
|
+
title: "Axes on All Sides",
|
|
528
|
+
children: /*#__PURE__*/_jsx(AxesOnAllSides, {})
|
|
529
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
530
|
+
title: "Custom Tick Mark Sizes",
|
|
531
|
+
children: /*#__PURE__*/_jsx(CustomTickMarkSizes, {})
|
|
273
532
|
})]
|
|
274
533
|
});
|
|
275
534
|
};
|
|
@@ -621,6 +621,34 @@ const ColorMapWithOpacity = () => {
|
|
|
621
621
|
}
|
|
622
622
|
});
|
|
623
623
|
};
|
|
624
|
+
const BandGridPositionExample = _ref6 => {
|
|
625
|
+
let {
|
|
626
|
+
position
|
|
627
|
+
} = _ref6;
|
|
628
|
+
return /*#__PURE__*/_jsxs(CartesianChart, {
|
|
629
|
+
height: 180,
|
|
630
|
+
inset: 4,
|
|
631
|
+
series: [{
|
|
632
|
+
id: 'data',
|
|
633
|
+
data: [30, 50, 40, 60, 35]
|
|
634
|
+
}],
|
|
635
|
+
xAxis: {
|
|
636
|
+
scaleType: 'band',
|
|
637
|
+
data: ['A', 'B', 'C', 'D', 'E']
|
|
638
|
+
},
|
|
639
|
+
yAxis: {
|
|
640
|
+
domain: {
|
|
641
|
+
min: 0
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
645
|
+
showGrid: true,
|
|
646
|
+
showLine: true,
|
|
647
|
+
bandGridLinePlacement: position,
|
|
648
|
+
label: position
|
|
649
|
+
}), /*#__PURE__*/_jsx(BarPlot, {})]
|
|
650
|
+
});
|
|
651
|
+
};
|
|
624
652
|
const BarChartStories = () => {
|
|
625
653
|
return /*#__PURE__*/_jsxs(ExampleScreen, {
|
|
626
654
|
children: [/*#__PURE__*/_jsx(Example, {
|
|
@@ -662,6 +690,17 @@ const BarChartStories = () => {
|
|
|
662
690
|
}), /*#__PURE__*/_jsx(Example, {
|
|
663
691
|
title: "ColorMap with Opacity",
|
|
664
692
|
children: /*#__PURE__*/_jsx(ColorMapWithOpacity, {})
|
|
693
|
+
}), /*#__PURE__*/_jsxs(Example, {
|
|
694
|
+
title: "Band Grid Position",
|
|
695
|
+
children: [/*#__PURE__*/_jsx(BandGridPositionExample, {
|
|
696
|
+
position: "edges"
|
|
697
|
+
}), /*#__PURE__*/_jsx(BandGridPositionExample, {
|
|
698
|
+
position: "start"
|
|
699
|
+
}), /*#__PURE__*/_jsx(BandGridPositionExample, {
|
|
700
|
+
position: "middle"
|
|
701
|
+
}), /*#__PURE__*/_jsx(BandGridPositionExample, {
|
|
702
|
+
position: "end"
|
|
703
|
+
})]
|
|
665
704
|
})]
|
|
666
705
|
});
|
|
667
706
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { cancelAnimation, Easing, useAnimatedReaction, useDerivedValue, useSharedValue, withRepeat, withSequence } from 'react-native-reanimated';
|
|
1
|
+
import { forwardRef, memo, useEffect, useImperativeHandle, useMemo } from 'react';
|
|
2
|
+
import { cancelAnimation, Easing, useAnimatedReaction, useDerivedValue, useSharedValue, withDelay, withRepeat, withSequence, withTiming } from 'react-native-reanimated';
|
|
4
3
|
import { useTheme } from '@coinbase/cds-mobile';
|
|
5
4
|
import { Circle, Group } from '@shopify/react-native-skia';
|
|
6
5
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
@@ -60,6 +59,14 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
|
|
|
60
59
|
}, [transitions == null ? void 0 : transitions.pulseRepeatDelay]);
|
|
61
60
|
const pulseOpacity = useSharedValue(0);
|
|
62
61
|
const pulseRadius = useSharedValue(pulseRadiusStart);
|
|
62
|
+
|
|
63
|
+
// Convert idlePulse prop to SharedValue so useAnimatedReaction can detect changes.
|
|
64
|
+
// In the new React Native architecture, regular JS props are captured by value in worklets
|
|
65
|
+
// and won't update when the prop changes.
|
|
66
|
+
const idlePulseShared = useSharedValue(idlePulse != null ? idlePulse : false);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
idlePulseShared.value = idlePulse != null ? idlePulse : false;
|
|
69
|
+
}, [idlePulse, idlePulseShared]);
|
|
63
70
|
const animatedX = useSharedValue(0);
|
|
64
71
|
const animatedY = useSharedValue(0);
|
|
65
72
|
|
|
@@ -103,41 +110,34 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
|
|
|
103
110
|
useImperativeHandle(ref, () => ({
|
|
104
111
|
pulse: () => {
|
|
105
112
|
// Only trigger manual pulse when idlePulse is not enabled
|
|
106
|
-
if (!
|
|
113
|
+
if (!idlePulseShared.value) {
|
|
107
114
|
cancelAnimation(pulseOpacity);
|
|
108
115
|
cancelAnimation(pulseRadius);
|
|
109
116
|
|
|
110
117
|
// Manual pulse without delay
|
|
111
|
-
const immediatePulseTransition = _extends({}, pulseTransition, {
|
|
112
|
-
delay: 0
|
|
113
|
-
});
|
|
114
118
|
pulseOpacity.value = pulseOpacityStart;
|
|
115
119
|
pulseRadius.value = pulseRadiusStart;
|
|
116
|
-
pulseOpacity.value = buildTransition(pulseOpacityEnd,
|
|
117
|
-
pulseRadius.value = buildTransition(pulseRadiusEnd,
|
|
120
|
+
pulseOpacity.value = buildTransition(pulseOpacityEnd, pulseTransition);
|
|
121
|
+
pulseRadius.value = buildTransition(pulseRadiusEnd, pulseTransition);
|
|
118
122
|
}
|
|
119
123
|
}
|
|
120
|
-
}), [
|
|
124
|
+
}), [idlePulseShared, pulseOpacity, pulseRadius, pulseTransition]);
|
|
121
125
|
|
|
122
126
|
// Watch idlePulse changes and control continuous pulse
|
|
123
|
-
useAnimatedReaction(() =>
|
|
127
|
+
useAnimatedReaction(() => idlePulseShared.value, (current, previous) => {
|
|
124
128
|
if (!animate) return;
|
|
125
129
|
if (current) {
|
|
126
130
|
// Start continuous pulse when idlePulse is enabled
|
|
127
|
-
// Create instant transition to reset pulse after delay
|
|
128
|
-
const instantTransition = {
|
|
129
|
-
type: 'timing',
|
|
130
|
-
duration: 0
|
|
131
|
-
};
|
|
132
|
-
const resetWithDelay = _extends({}, instantTransition, {
|
|
133
|
-
delay: pulseRepeatDelay
|
|
134
|
-
});
|
|
135
131
|
pulseOpacity.value = pulseOpacityStart;
|
|
136
132
|
pulseRadius.value = pulseRadiusStart;
|
|
137
|
-
pulseOpacity.value = withRepeat(withSequence(buildTransition(pulseOpacityEnd, pulseTransition),
|
|
133
|
+
pulseOpacity.value = withRepeat(withSequence(buildTransition(pulseOpacityEnd, pulseTransition), withDelay(pulseRepeatDelay, withTiming(pulseOpacityStart, {
|
|
134
|
+
duration: 0
|
|
135
|
+
}))), -1,
|
|
138
136
|
// infinite loop
|
|
139
137
|
false);
|
|
140
|
-
pulseRadius.value = withRepeat(withSequence(buildTransition(pulseRadiusEnd, pulseTransition),
|
|
138
|
+
pulseRadius.value = withRepeat(withSequence(buildTransition(pulseRadiusEnd, pulseTransition), withDelay(pulseRepeatDelay, withTiming(pulseRadiusStart, {
|
|
139
|
+
duration: 0
|
|
140
|
+
}))), -1,
|
|
141
141
|
// infinite loop
|
|
142
142
|
false);
|
|
143
143
|
} else {
|