@coinbase/cds-mobile-visualization 3.4.0-beta.18 → 3.4.0-beta.19
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/Path.d.ts +35 -13
- package/dts/chart/Path.d.ts.map +1 -1
- package/dts/chart/area/Area.d.ts +7 -11
- package/dts/chart/area/Area.d.ts.map +1 -1
- package/dts/chart/area/AreaChart.d.ts +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/bar/Bar.d.ts +32 -2
- package/dts/chart/bar/Bar.d.ts.map +1 -1
- package/dts/chart/bar/BarChart.d.ts +2 -0
- package/dts/chart/bar/BarChart.d.ts.map +1 -1
- package/dts/chart/bar/BarPlot.d.ts +2 -1
- package/dts/chart/bar/BarPlot.d.ts.map +1 -1
- package/dts/chart/bar/BarStack.d.ts +5 -10
- package/dts/chart/bar/BarStack.d.ts.map +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts +1 -0
- 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/line/DottedLine.d.ts.map +1 -1
- package/dts/chart/line/Line.d.ts +4 -9
- package/dts/chart/line/Line.d.ts.map +1 -1
- package/dts/chart/line/LineChart.d.ts +1 -1
- package/dts/chart/line/SolidLine.d.ts.map +1 -1
- package/dts/chart/point/Point.d.ts +18 -2
- package/dts/chart/point/Point.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +9 -1
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -1
- package/dts/chart/scrubber/Scrubber.d.ts +45 -24
- package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +9 -1
- package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -1
- package/dts/chart/utils/bar.d.ts +34 -0
- package/dts/chart/utils/bar.d.ts.map +1 -1
- package/dts/chart/utils/path.d.ts +6 -0
- package/dts/chart/utils/path.d.ts.map +1 -1
- package/dts/chart/utils/transition.d.ts +59 -21
- package/dts/chart/utils/transition.d.ts.map +1 -1
- package/esm/chart/Path.js +16 -14
- package/esm/chart/__stories__/CartesianChart.stories.js +3 -77
- package/esm/chart/__stories__/ChartTransitions.stories.js +629 -0
- package/esm/chart/area/Area.js +2 -0
- package/esm/chart/area/DottedArea.js +7 -3
- package/esm/chart/area/GradientArea.js +4 -2
- package/esm/chart/area/SolidArea.js +4 -2
- package/esm/chart/bar/Bar.js +2 -0
- package/esm/chart/bar/BarChart.js +4 -2
- package/esm/chart/bar/BarPlot.js +2 -0
- package/esm/chart/bar/BarStack.js +3 -0
- package/esm/chart/bar/DefaultBar.js +15 -15
- package/esm/chart/bar/DefaultBarStack.js +14 -3
- package/esm/chart/bar/__stories__/BarChart.stories.js +448 -52
- package/esm/chart/line/DottedLine.js +4 -2
- package/esm/chart/line/Line.js +6 -18
- package/esm/chart/line/SolidLine.js +4 -2
- package/esm/chart/line/__stories__/LineChart.stories.js +17 -226
- package/esm/chart/line/__stories__/ReferenceLine.stories.js +95 -1
- package/esm/chart/point/Point.js +33 -35
- package/esm/chart/scrubber/DefaultScrubberBeacon.js +2 -5
- package/esm/chart/scrubber/Scrubber.js +10 -8
- package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +29 -7
- package/esm/chart/utils/bar.js +43 -0
- package/esm/chart/utils/path.js +8 -0
- package/esm/chart/utils/transition.js +96 -61
- package/package.json +5 -5
- package/esm/chart/__stories__/Chart.stories.js +0 -77
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
const _excluded = ["dataX", "dataY"],
|
|
2
2
|
_excluded2 = ["label"],
|
|
3
|
-
_excluded3 = ["style"]
|
|
4
|
-
_excluded4 = ["x", "y", "width", "height", "originY", "dataX"];
|
|
3
|
+
_excluded3 = ["style"];
|
|
5
4
|
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); }
|
|
6
5
|
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; }
|
|
7
|
-
import { forwardRef, memo, useCallback, useEffect,
|
|
6
|
+
import { forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
8
7
|
import { useAnimatedReaction, useDerivedValue, useSharedValue, withDelay, withTiming } from 'react-native-reanimated';
|
|
9
8
|
import { assets, ethBackground } from '@coinbase/cds-common/internal/data/assets';
|
|
10
|
-
import { candles as btcCandles } from '@coinbase/cds-common/internal/data/candles';
|
|
11
9
|
import { prices } from '@coinbase/cds-common/internal/data/prices';
|
|
12
10
|
import { sparklineInteractiveData } from '@coinbase/cds-common/internal/visualizations/SparklineInteractiveData';
|
|
13
11
|
import { useTabsContext } from '@coinbase/cds-common/tabs/TabsContext';
|
|
@@ -23,16 +21,15 @@ import { SectionHeader } from '@coinbase/cds-mobile/section-header/SectionHeader
|
|
|
23
21
|
import { Pressable } from '@coinbase/cds-mobile/system';
|
|
24
22
|
import { SegmentedTab } from '@coinbase/cds-mobile/tabs/SegmentedTab';
|
|
25
23
|
import { Text } from '@coinbase/cds-mobile/typography';
|
|
26
|
-
import { Circle, FontWeight, Group,
|
|
24
|
+
import { Circle, FontWeight, Group, Skia, TextAlign } from '@shopify/react-native-skia';
|
|
27
25
|
import { Area, DottedArea } from '../../area';
|
|
28
26
|
import { DefaultAxisTickLabel, XAxis, YAxis } from '../../axis';
|
|
29
|
-
import { BarPlot } from '../../bar';
|
|
30
27
|
import { CartesianChart } from '../../CartesianChart';
|
|
31
28
|
import { useCartesianChartContext } from '../../ChartProvider';
|
|
32
29
|
import { PeriodSelector, PeriodSelectorActiveIndicator } from '../../PeriodSelector';
|
|
33
30
|
import { Point } from '../../point';
|
|
34
31
|
import { DefaultScrubberBeacon, Scrubber } from '../../scrubber';
|
|
35
|
-
import { buildTransition, defaultTransition,
|
|
32
|
+
import { buildTransition, defaultTransition, projectPointWithSerializableScale, unwrapAnimatedValue, useScrubberContext } from '../../utils';
|
|
36
33
|
import { DottedLine, Line, LineChart, ReferenceLine, SolidLine } from '..';
|
|
37
34
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
38
35
|
function MultipleLine() {
|
|
@@ -1180,210 +1177,6 @@ function Performance() {
|
|
|
1180
1177
|
})]
|
|
1181
1178
|
});
|
|
1182
1179
|
}
|
|
1183
|
-
const candlestickStockData = btcCandles.slice(0, 90).reverse();
|
|
1184
|
-
const CandlesticksHeader = /*#__PURE__*/memo(_ref11 => {
|
|
1185
|
-
let {
|
|
1186
|
-
currentIndex
|
|
1187
|
-
} = _ref11;
|
|
1188
|
-
const formatPrice = useCallback(price => {
|
|
1189
|
-
return new Intl.NumberFormat('en-US', {
|
|
1190
|
-
style: 'currency',
|
|
1191
|
-
currency: 'USD'
|
|
1192
|
-
}).format(parseFloat(price));
|
|
1193
|
-
}, []);
|
|
1194
|
-
const formatThousandsPriceNumber = useCallback(price => {
|
|
1195
|
-
const formattedPrice = new Intl.NumberFormat('en-US', {
|
|
1196
|
-
style: 'currency',
|
|
1197
|
-
currency: 'USD',
|
|
1198
|
-
minimumFractionDigits: 0,
|
|
1199
|
-
maximumFractionDigits: 0
|
|
1200
|
-
}).format(price / 1000);
|
|
1201
|
-
return formattedPrice + "k";
|
|
1202
|
-
}, []);
|
|
1203
|
-
const currentText = useMemo(() => {
|
|
1204
|
-
if (currentIndex !== undefined) {
|
|
1205
|
-
return "Open: " + formatThousandsPriceNumber(parseFloat(candlestickStockData[currentIndex].open)) + ", Close: " + formatThousandsPriceNumber(parseFloat(candlestickStockData[currentIndex].close)) + ", Volume: " + (parseFloat(candlestickStockData[currentIndex].volume) / 1000).toFixed(2) + "k";
|
|
1206
|
-
}
|
|
1207
|
-
return formatPrice(candlestickStockData[candlestickStockData.length - 1].close);
|
|
1208
|
-
}, [currentIndex, formatThousandsPriceNumber, formatPrice]);
|
|
1209
|
-
return /*#__PURE__*/_jsx(Text, {
|
|
1210
|
-
"aria-live": "polite",
|
|
1211
|
-
font: "headline",
|
|
1212
|
-
children: currentText
|
|
1213
|
-
});
|
|
1214
|
-
});
|
|
1215
|
-
const CandlesticksChart = /*#__PURE__*/memo(_ref12 => {
|
|
1216
|
-
let {
|
|
1217
|
-
infoTextId,
|
|
1218
|
-
onScrubberPositionChange
|
|
1219
|
-
} = _ref12;
|
|
1220
|
-
const theme = useTheme();
|
|
1221
|
-
const min = useMemo(() => Math.min(...candlestickStockData.map(data => parseFloat(data.low))), []);
|
|
1222
|
-
const ThinSolidLine = /*#__PURE__*/memo(props => /*#__PURE__*/_jsx(SolidLine, _extends({}, props, {
|
|
1223
|
-
strokeWidth: 1
|
|
1224
|
-
})));
|
|
1225
|
-
|
|
1226
|
-
// Custom line component that renders a rect to highlight the entire bandwidth
|
|
1227
|
-
const BandwidthHighlight = /*#__PURE__*/memo(_ref13 => {
|
|
1228
|
-
let {
|
|
1229
|
-
stroke
|
|
1230
|
-
} = _ref13;
|
|
1231
|
-
const {
|
|
1232
|
-
getXSerializableScale,
|
|
1233
|
-
drawingArea
|
|
1234
|
-
} = useCartesianChartContext();
|
|
1235
|
-
const {
|
|
1236
|
-
scrubberPosition
|
|
1237
|
-
} = useScrubberContext();
|
|
1238
|
-
const xScale = useMemo(() => getXSerializableScale(), [getXSerializableScale]);
|
|
1239
|
-
const rectWidth = useMemo(() => {
|
|
1240
|
-
if (xScale !== undefined && xScale.type === 'band') {
|
|
1241
|
-
return xScale.bandwidth;
|
|
1242
|
-
}
|
|
1243
|
-
return 0;
|
|
1244
|
-
}, [xScale]);
|
|
1245
|
-
const xPos = useDerivedValue(() => {
|
|
1246
|
-
const position = unwrapAnimatedValue(scrubberPosition);
|
|
1247
|
-
const xPos = position !== undefined && xScale ? getPointOnSerializableScale(position, xScale) : undefined;
|
|
1248
|
-
return xPos !== undefined ? xPos - rectWidth / 2 : 0;
|
|
1249
|
-
}, [scrubberPosition, xScale]);
|
|
1250
|
-
const opacity = useDerivedValue(() => xPos.value !== undefined ? 1 : 0, [xPos]);
|
|
1251
|
-
return /*#__PURE__*/_jsx(Rect, {
|
|
1252
|
-
color: stroke,
|
|
1253
|
-
height: drawingArea.height,
|
|
1254
|
-
opacity: opacity,
|
|
1255
|
-
width: rectWidth,
|
|
1256
|
-
x: xPos,
|
|
1257
|
-
y: drawingArea.y
|
|
1258
|
-
});
|
|
1259
|
-
});
|
|
1260
|
-
const candlesData = useMemo(() => candlestickStockData.map(data => [parseFloat(data.low), parseFloat(data.high)]), []);
|
|
1261
|
-
const CandlestickBarComponent = /*#__PURE__*/memo(_ref14 => {
|
|
1262
|
-
var _yScale, _yScale2;
|
|
1263
|
-
let {
|
|
1264
|
-
x,
|
|
1265
|
-
y,
|
|
1266
|
-
width,
|
|
1267
|
-
height,
|
|
1268
|
-
dataX
|
|
1269
|
-
} = _ref14,
|
|
1270
|
-
props = _objectWithoutPropertiesLoose(_ref14, _excluded4);
|
|
1271
|
-
const {
|
|
1272
|
-
getYScale
|
|
1273
|
-
} = useCartesianChartContext();
|
|
1274
|
-
const yScale = getYScale();
|
|
1275
|
-
const wickX = x + width / 2;
|
|
1276
|
-
const timePeriodValue = candlestickStockData[dataX];
|
|
1277
|
-
const open = parseFloat(timePeriodValue.open);
|
|
1278
|
-
const close = parseFloat(timePeriodValue.close);
|
|
1279
|
-
const bullish = open < close;
|
|
1280
|
-
const theme = useTheme();
|
|
1281
|
-
const color = bullish ? theme.color.fgPositive : theme.color.fgNegative;
|
|
1282
|
-
const openY = (_yScale = yScale == null ? void 0 : yScale(open)) != null ? _yScale : 0;
|
|
1283
|
-
const closeY = (_yScale2 = yScale == null ? void 0 : yScale(close)) != null ? _yScale2 : 0;
|
|
1284
|
-
const bodyHeight = Math.abs(openY - closeY);
|
|
1285
|
-
const bodyY = openY < closeY ? openY : closeY;
|
|
1286
|
-
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
1287
|
-
children: [/*#__PURE__*/_jsx(SkiaLine, {
|
|
1288
|
-
color: color,
|
|
1289
|
-
p1: {
|
|
1290
|
-
x: wickX,
|
|
1291
|
-
y
|
|
1292
|
-
},
|
|
1293
|
-
p2: {
|
|
1294
|
-
x: wickX,
|
|
1295
|
-
y: y + height
|
|
1296
|
-
},
|
|
1297
|
-
strokeWidth: 1
|
|
1298
|
-
}), /*#__PURE__*/_jsx(Rect, {
|
|
1299
|
-
color: color,
|
|
1300
|
-
height: bodyHeight,
|
|
1301
|
-
width: width,
|
|
1302
|
-
x: x,
|
|
1303
|
-
y: bodyY
|
|
1304
|
-
})]
|
|
1305
|
-
});
|
|
1306
|
-
});
|
|
1307
|
-
const formatThousandsPriceNumber = useCallback(price => {
|
|
1308
|
-
const formattedPrice = new Intl.NumberFormat('en-US', {
|
|
1309
|
-
style: 'currency',
|
|
1310
|
-
currency: 'USD',
|
|
1311
|
-
minimumFractionDigits: 0,
|
|
1312
|
-
maximumFractionDigits: 0
|
|
1313
|
-
}).format(price / 1000);
|
|
1314
|
-
return formattedPrice + "k";
|
|
1315
|
-
}, []);
|
|
1316
|
-
const formatTime = useCallback(index => {
|
|
1317
|
-
if (index === null || index === undefined || index >= candlestickStockData.length) return '';
|
|
1318
|
-
const ts = parseInt(candlestickStockData[index].start);
|
|
1319
|
-
return new Date(ts * 1000).toLocaleDateString('en-US', {
|
|
1320
|
-
month: 'short',
|
|
1321
|
-
day: 'numeric'
|
|
1322
|
-
});
|
|
1323
|
-
}, []);
|
|
1324
|
-
return /*#__PURE__*/_jsxs(CartesianChart, {
|
|
1325
|
-
enableScrubbing: true,
|
|
1326
|
-
animate: false,
|
|
1327
|
-
"aria-labelledby": infoTextId,
|
|
1328
|
-
borderRadius: 0,
|
|
1329
|
-
height: 150,
|
|
1330
|
-
inset: {
|
|
1331
|
-
top: 8,
|
|
1332
|
-
bottom: 8,
|
|
1333
|
-
left: 0,
|
|
1334
|
-
right: 0
|
|
1335
|
-
},
|
|
1336
|
-
onScrubberPositionChange: onScrubberPositionChange,
|
|
1337
|
-
series: [{
|
|
1338
|
-
id: 'stock-prices',
|
|
1339
|
-
data: candlesData
|
|
1340
|
-
}],
|
|
1341
|
-
xAxis: {
|
|
1342
|
-
scaleType: 'band'
|
|
1343
|
-
},
|
|
1344
|
-
yAxis: {
|
|
1345
|
-
domain: {
|
|
1346
|
-
min
|
|
1347
|
-
}
|
|
1348
|
-
},
|
|
1349
|
-
children: [/*#__PURE__*/_jsx(XAxis, {
|
|
1350
|
-
tickLabelFormatter: formatTime
|
|
1351
|
-
}), /*#__PURE__*/_jsx(YAxis, {
|
|
1352
|
-
showGrid: true,
|
|
1353
|
-
GridLineComponent: ThinSolidLine,
|
|
1354
|
-
tickLabelFormatter: formatThousandsPriceNumber,
|
|
1355
|
-
width: 40
|
|
1356
|
-
}), /*#__PURE__*/_jsx(Scrubber, {
|
|
1357
|
-
hideOverlay: true,
|
|
1358
|
-
LineComponent: BandwidthHighlight,
|
|
1359
|
-
lineStroke: theme.color.fgMuted,
|
|
1360
|
-
seriesIds: []
|
|
1361
|
-
}), /*#__PURE__*/_jsx(BarPlot, {
|
|
1362
|
-
BarComponent: CandlestickBarComponent,
|
|
1363
|
-
BarStackComponent: _ref15 => {
|
|
1364
|
-
let {
|
|
1365
|
-
children
|
|
1366
|
-
} = _ref15;
|
|
1367
|
-
return /*#__PURE__*/_jsx("g", {
|
|
1368
|
-
children: children
|
|
1369
|
-
});
|
|
1370
|
-
}
|
|
1371
|
-
})]
|
|
1372
|
-
});
|
|
1373
|
-
});
|
|
1374
|
-
function Candlesticks() {
|
|
1375
|
-
const infoTextId = useId();
|
|
1376
|
-
const [currentIndex, setCurrentIndex] = useState();
|
|
1377
|
-
return /*#__PURE__*/_jsxs(VStack, {
|
|
1378
|
-
gap: 2,
|
|
1379
|
-
children: [/*#__PURE__*/_jsx(CandlesticksHeader, {
|
|
1380
|
-
currentIndex: currentIndex
|
|
1381
|
-
}), /*#__PURE__*/_jsx(CandlesticksChart, {
|
|
1382
|
-
infoTextId: infoTextId,
|
|
1383
|
-
onScrubberPositionChange: setCurrentIndex
|
|
1384
|
-
})]
|
|
1385
|
-
});
|
|
1386
|
-
}
|
|
1387
1180
|
function MonotoneAssetPrice() {
|
|
1388
1181
|
const theme = useTheme();
|
|
1389
1182
|
const prices = sparklineInteractiveData.hour;
|
|
@@ -1457,14 +1250,14 @@ function MonotoneAssetPrice() {
|
|
|
1457
1250
|
dy: -12,
|
|
1458
1251
|
horizontalAlignment: "left"
|
|
1459
1252
|
})), []);
|
|
1460
|
-
const CustomScrubberBeacon = /*#__PURE__*/memo(
|
|
1253
|
+
const CustomScrubberBeacon = /*#__PURE__*/memo(_ref11 => {
|
|
1461
1254
|
let {
|
|
1462
1255
|
dataX,
|
|
1463
1256
|
dataY,
|
|
1464
1257
|
seriesId,
|
|
1465
1258
|
isIdle,
|
|
1466
1259
|
animate = true
|
|
1467
|
-
} =
|
|
1260
|
+
} = _ref11;
|
|
1468
1261
|
const {
|
|
1469
1262
|
getSeries,
|
|
1470
1263
|
getXSerializableScale,
|
|
@@ -1540,10 +1333,10 @@ function MonotoneAssetPrice() {
|
|
|
1540
1333
|
color: theme.color.fg,
|
|
1541
1334
|
gradient: {
|
|
1542
1335
|
axis: 'x',
|
|
1543
|
-
stops:
|
|
1336
|
+
stops: _ref12 => {
|
|
1544
1337
|
let {
|
|
1545
1338
|
min
|
|
1546
|
-
} =
|
|
1339
|
+
} = _ref12;
|
|
1547
1340
|
return [{
|
|
1548
1341
|
offset: min,
|
|
1549
1342
|
color: theme.color.fg,
|
|
@@ -1557,10 +1350,10 @@ function MonotoneAssetPrice() {
|
|
|
1557
1350
|
}
|
|
1558
1351
|
}],
|
|
1559
1352
|
xAxis: {
|
|
1560
|
-
range:
|
|
1353
|
+
range: _ref13 => {
|
|
1561
1354
|
let {
|
|
1562
1355
|
max
|
|
1563
|
-
} =
|
|
1356
|
+
} = _ref13;
|
|
1564
1357
|
return {
|
|
1565
1358
|
min: 96,
|
|
1566
1359
|
max
|
|
@@ -1611,11 +1404,11 @@ function ServiceAvailability() {
|
|
|
1611
1404
|
id: 'availability',
|
|
1612
1405
|
data: availabilityEvents.map(event => event.availability),
|
|
1613
1406
|
gradient: {
|
|
1614
|
-
stops:
|
|
1407
|
+
stops: _ref14 => {
|
|
1615
1408
|
let {
|
|
1616
1409
|
min,
|
|
1617
1410
|
max
|
|
1618
|
-
} =
|
|
1411
|
+
} = _ref14;
|
|
1619
1412
|
return [{
|
|
1620
1413
|
offset: min,
|
|
1621
1414
|
color: theme.color.fgNegative
|
|
@@ -1642,11 +1435,11 @@ function ServiceAvailability() {
|
|
|
1642
1435
|
data: availabilityEvents.map(event => event.date.getTime())
|
|
1643
1436
|
},
|
|
1644
1437
|
yAxis: {
|
|
1645
|
-
domain:
|
|
1438
|
+
domain: _ref15 => {
|
|
1646
1439
|
let {
|
|
1647
1440
|
min,
|
|
1648
1441
|
max
|
|
1649
|
-
} =
|
|
1442
|
+
} = _ref15;
|
|
1650
1443
|
return {
|
|
1651
1444
|
min: Math.max(min - 2, 0),
|
|
1652
1445
|
max: Math.min(max + 2, 100)
|
|
@@ -2059,11 +1852,11 @@ function ExampleNavigator() {
|
|
|
2059
1852
|
}],
|
|
2060
1853
|
xAxis: {
|
|
2061
1854
|
// Give space before the end of the chart for the scrubber
|
|
2062
|
-
range:
|
|
1855
|
+
range: _ref16 => {
|
|
2063
1856
|
let {
|
|
2064
1857
|
min,
|
|
2065
1858
|
max
|
|
2066
|
-
} =
|
|
1859
|
+
} = _ref16;
|
|
2067
1860
|
return {
|
|
2068
1861
|
min,
|
|
2069
1862
|
max: max - 24
|
|
@@ -2094,9 +1887,6 @@ function ExampleNavigator() {
|
|
|
2094
1887
|
}, {
|
|
2095
1888
|
title: 'Performance',
|
|
2096
1889
|
component: /*#__PURE__*/_jsx(Performance, {})
|
|
2097
|
-
}, {
|
|
2098
|
-
title: 'Candlesticks',
|
|
2099
|
-
component: /*#__PURE__*/_jsx(Candlesticks, {})
|
|
2100
1890
|
}, {
|
|
2101
1891
|
title: 'Monotone Asset Price',
|
|
2102
1892
|
component: /*#__PURE__*/_jsx(MonotoneAssetPrice, {})
|
|
@@ -2118,6 +1908,7 @@ function ExampleNavigator() {
|
|
|
2118
1908
|
setCurrentIndex(prev => (prev + 1 + examples.length) % examples.length);
|
|
2119
1909
|
}, [examples.length]);
|
|
2120
1910
|
return /*#__PURE__*/_jsx(ExampleScreen, {
|
|
1911
|
+
paddingX: 0,
|
|
2121
1912
|
children: /*#__PURE__*/_jsxs(VStack, {
|
|
2122
1913
|
gap: 4,
|
|
2123
1914
|
children: [/*#__PURE__*/_jsxs(HStack, {
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
const _excluded = ["accentColor", "yellowColor"];
|
|
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, useCallback } from 'react';
|
|
4
|
+
import { memo, useCallback, useMemo } from 'react';
|
|
5
|
+
import { useDerivedValue, withTiming } from 'react-native-reanimated';
|
|
6
|
+
import { sparklineInteractiveData } from '@coinbase/cds-common/internal/visualizations/SparklineInteractiveData';
|
|
5
7
|
import { useTheme } from '@coinbase/cds-mobile';
|
|
6
8
|
import { Example, ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
|
|
9
|
+
import { useCartesianChartContext } from '../../ChartProvider';
|
|
10
|
+
import { Scrubber } from '../../scrubber';
|
|
11
|
+
import { getPointOnSerializableScale, useScrubberContext } from '../../utils';
|
|
7
12
|
import { DefaultReferenceLineLabel } from '../DefaultReferenceLineLabel';
|
|
8
13
|
import { DottedLine } from '../DottedLine';
|
|
9
14
|
import { LineChart } from '../LineChart';
|
|
@@ -126,7 +131,96 @@ const ReferenceLineStories = () => {
|
|
|
126
131
|
stroke: theme.color.bgWarning
|
|
127
132
|
})
|
|
128
133
|
})
|
|
134
|
+
}), /*#__PURE__*/_jsx(Example, {
|
|
135
|
+
title: "Start Price Reference Line",
|
|
136
|
+
children: /*#__PURE__*/_jsx(StartPriceReferenceLine, {})
|
|
129
137
|
})]
|
|
130
138
|
});
|
|
131
139
|
};
|
|
140
|
+
const FADE_ZONE = 128;
|
|
141
|
+
const StartPriceLabel = /*#__PURE__*/memo(props => {
|
|
142
|
+
const theme = useTheme();
|
|
143
|
+
const {
|
|
144
|
+
scrubberPosition
|
|
145
|
+
} = useScrubberContext();
|
|
146
|
+
const {
|
|
147
|
+
getXSerializableScale,
|
|
148
|
+
drawingArea
|
|
149
|
+
} = useCartesianChartContext();
|
|
150
|
+
const xScale = useMemo(() => getXSerializableScale(), [getXSerializableScale]);
|
|
151
|
+
const opacity = useDerivedValue(() => {
|
|
152
|
+
if (scrubberPosition.value === undefined) return withTiming(0, {
|
|
153
|
+
duration: 250
|
|
154
|
+
});
|
|
155
|
+
if (!xScale) return withTiming(1, {
|
|
156
|
+
duration: 250
|
|
157
|
+
});
|
|
158
|
+
const scrubX = getPointOnSerializableScale(scrubberPosition.value, xScale);
|
|
159
|
+
const rightEdge = drawingArea.x + drawingArea.width;
|
|
160
|
+
const target = rightEdge - scrubX >= FADE_ZONE ? 1 : 0;
|
|
161
|
+
return withTiming(target, {
|
|
162
|
+
duration: 250
|
|
163
|
+
});
|
|
164
|
+
}, [scrubberPosition, xScale, drawingArea]);
|
|
165
|
+
return /*#__PURE__*/_jsx(DefaultReferenceLineLabel, _extends({}, props, {
|
|
166
|
+
background: theme.color.bgSecondary,
|
|
167
|
+
borderRadius: 12.5,
|
|
168
|
+
color: theme.color.fg,
|
|
169
|
+
font: "label1",
|
|
170
|
+
inset: {
|
|
171
|
+
top: 4,
|
|
172
|
+
bottom: 4,
|
|
173
|
+
left: 8,
|
|
174
|
+
right: 8
|
|
175
|
+
},
|
|
176
|
+
opacity: opacity
|
|
177
|
+
}));
|
|
178
|
+
});
|
|
179
|
+
function StartPriceReferenceLine() {
|
|
180
|
+
const theme = useTheme();
|
|
181
|
+
const hourData = useMemo(() => sparklineInteractiveData.hour, []);
|
|
182
|
+
const startPrice = hourData[0].value;
|
|
183
|
+
const endPrice = hourData[hourData.length - 1].value;
|
|
184
|
+
const isPositive = endPrice >= startPrice;
|
|
185
|
+
const seriesColor = isPositive ? theme.color.fgPositive : theme.color.fgNegative;
|
|
186
|
+
return /*#__PURE__*/_jsxs(LineChart, {
|
|
187
|
+
enableScrubbing: true,
|
|
188
|
+
showArea: true,
|
|
189
|
+
areaType: "dotted",
|
|
190
|
+
height: 300,
|
|
191
|
+
inset: 0,
|
|
192
|
+
series: [{
|
|
193
|
+
id: 'hourly-prices',
|
|
194
|
+
data: hourData.map(d => d.value),
|
|
195
|
+
color: seriesColor
|
|
196
|
+
}],
|
|
197
|
+
xAxis: {
|
|
198
|
+
range: _ref2 => {
|
|
199
|
+
let {
|
|
200
|
+
min,
|
|
201
|
+
max
|
|
202
|
+
} = _ref2;
|
|
203
|
+
return {
|
|
204
|
+
min,
|
|
205
|
+
max: max - 24
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
children: [/*#__PURE__*/_jsx(Scrubber, {}), /*#__PURE__*/_jsx(ReferenceLine, {
|
|
210
|
+
LabelComponent: StartPriceLabel,
|
|
211
|
+
LineComponent: props => /*#__PURE__*/_jsx(DottedLine, _extends({}, props, {
|
|
212
|
+
dashIntervals: [0, 16],
|
|
213
|
+
strokeWidth: 3
|
|
214
|
+
})),
|
|
215
|
+
dataY: startPrice,
|
|
216
|
+
label: startPrice.toLocaleString('en-US', {
|
|
217
|
+
minimumFractionDigits: 2,
|
|
218
|
+
maximumFractionDigits: 2
|
|
219
|
+
}),
|
|
220
|
+
labelDx: -12,
|
|
221
|
+
labelHorizontalAlignment: "right",
|
|
222
|
+
stroke: theme.color.fgMuted
|
|
223
|
+
})]
|
|
224
|
+
});
|
|
225
|
+
}
|
|
132
226
|
export default ReferenceLineStories;
|
package/esm/chart/point/Point.js
CHANGED
|
@@ -5,7 +5,7 @@ import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
|
|
|
5
5
|
import { Circle, Group, interpolateColors } from '@shopify/react-native-skia';
|
|
6
6
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
7
7
|
import { projectPoint } from '../utils';
|
|
8
|
-
import { buildTransition, defaultTransition } from '../utils/transition';
|
|
8
|
+
import { buildTransition, defaultAccessoryEnterTransition, defaultTransition, getTransition } from '../utils/transition';
|
|
9
9
|
import { DefaultPointLabel } from './DefaultPointLabel';
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -27,7 +27,8 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
27
27
|
labelPosition = 'center',
|
|
28
28
|
labelOffset,
|
|
29
29
|
labelFont,
|
|
30
|
-
|
|
30
|
+
transitions,
|
|
31
|
+
transition,
|
|
31
32
|
animate: animateProp
|
|
32
33
|
} = _ref;
|
|
33
34
|
const theme = useTheme();
|
|
@@ -43,6 +44,8 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
43
44
|
const xScale = getXScale();
|
|
44
45
|
const yScale = getYScale(yAxisId);
|
|
45
46
|
const shouldAnimate = animate != null ? animate : false;
|
|
47
|
+
const updateTransition = useMemo(() => getTransition((transitions == null ? void 0 : transitions.update) !== undefined ? transitions.update : transition, animate, defaultTransition), [animate, transitions == null ? void 0 : transitions.update, transition]);
|
|
48
|
+
const enterTransition = useMemo(() => getTransition(transitions == null ? void 0 : transitions.enter, animate, defaultAccessoryEnterTransition), [animate, transitions == null ? void 0 : transitions.enter]);
|
|
46
49
|
|
|
47
50
|
// Calculate pixel coordinates from data coordinates
|
|
48
51
|
const pixelCoordinate = useMemo(() => {
|
|
@@ -62,9 +65,13 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
62
65
|
// Animated values for position
|
|
63
66
|
const animatedX = useSharedValue(0);
|
|
64
67
|
const animatedY = useSharedValue(0);
|
|
65
|
-
|
|
66
|
-
// Animated value for color interpolation (0 = old color, 1 = new color)
|
|
68
|
+
const enterOpacity = useSharedValue(shouldAnimate ? 0 : 1);
|
|
67
69
|
const colorProgress = useSharedValue(1);
|
|
70
|
+
const isReady = !!xScale && !!yScale;
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!shouldAnimate || !isReady) return;
|
|
73
|
+
enterOpacity.value = buildTransition(1, enterTransition);
|
|
74
|
+
}, [shouldAnimate, isReady, enterTransition, enterOpacity]);
|
|
68
75
|
|
|
69
76
|
// Update position when coordinates change
|
|
70
77
|
useEffect(() => {
|
|
@@ -72,26 +79,26 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
72
79
|
return;
|
|
73
80
|
}
|
|
74
81
|
if (shouldAnimate && previousPixelCoordinate) {
|
|
75
|
-
animatedX.value = buildTransition(pixelCoordinate.x,
|
|
76
|
-
animatedY.value = buildTransition(pixelCoordinate.y,
|
|
82
|
+
animatedX.value = buildTransition(pixelCoordinate.x, updateTransition);
|
|
83
|
+
animatedY.value = buildTransition(pixelCoordinate.y, updateTransition);
|
|
77
84
|
} else {
|
|
78
85
|
cancelAnimation(animatedX);
|
|
79
86
|
cancelAnimation(animatedY);
|
|
80
87
|
animatedX.value = pixelCoordinate.x;
|
|
81
88
|
animatedY.value = pixelCoordinate.y;
|
|
82
89
|
}
|
|
83
|
-
}, [pixelCoordinate, shouldAnimate, previousPixelCoordinate, animatedX, animatedY,
|
|
90
|
+
}, [pixelCoordinate, shouldAnimate, previousPixelCoordinate, animatedX, animatedY, updateTransition]);
|
|
84
91
|
|
|
85
92
|
// Update color when fill changes
|
|
86
93
|
useEffect(() => {
|
|
87
94
|
if (shouldAnimate && previousFill && previousFill !== fill) {
|
|
88
95
|
colorProgress.value = 0;
|
|
89
|
-
colorProgress.value = buildTransition(1,
|
|
96
|
+
colorProgress.value = buildTransition(1, updateTransition);
|
|
90
97
|
} else {
|
|
91
98
|
cancelAnimation(colorProgress);
|
|
92
99
|
colorProgress.value = 1;
|
|
93
100
|
}
|
|
94
|
-
}, [fill, shouldAnimate, previousFill, colorProgress,
|
|
101
|
+
}, [fill, shouldAnimate, previousFill, colorProgress, updateTransition]);
|
|
95
102
|
|
|
96
103
|
// Create animated point for circles
|
|
97
104
|
const animatedPoint = useDerivedValue(() => {
|
|
@@ -108,24 +115,19 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
108
115
|
}
|
|
109
116
|
return interpolateColors(colorProgress.value, [0, 1], [previousFill, fill]);
|
|
110
117
|
}, [colorProgress, previousFill, fill]);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}, [animatedX, animatedY, drawingArea]);
|
|
116
|
-
|
|
117
|
-
// Compute effective opacity based on drawing area bounds
|
|
118
|
+
const isWithinDrawingArea = useMemo(() => {
|
|
119
|
+
if (!pixelCoordinate) return false;
|
|
120
|
+
return pixelCoordinate.x >= drawingArea.x && pixelCoordinate.x <= drawingArea.x + drawingArea.width && pixelCoordinate.y >= drawingArea.y && pixelCoordinate.y <= drawingArea.y + drawingArea.height;
|
|
121
|
+
}, [pixelCoordinate, drawingArea]);
|
|
118
122
|
const effectiveOpacity = useDerivedValue(() => {
|
|
119
123
|
const baseOpacity = opacity != null ? opacity : 1;
|
|
120
|
-
return isWithinDrawingArea
|
|
121
|
-
}, [isWithinDrawingArea, opacity]);
|
|
124
|
+
return isWithinDrawingArea ? baseOpacity * enterOpacity.value : 0;
|
|
125
|
+
}, [isWithinDrawingArea, opacity, enterOpacity]);
|
|
122
126
|
const offset = useMemo(() => labelOffset != null ? labelOffset : radius * 2, [labelOffset, radius]);
|
|
123
127
|
if (!pixelCoordinate) {
|
|
124
128
|
return null;
|
|
125
129
|
}
|
|
126
|
-
|
|
127
|
-
// If animation is disabled or on first render, use static rendering
|
|
128
|
-
if (!shouldAnimate || !previousPixelCoordinate) {
|
|
130
|
+
if (!shouldAnimate) {
|
|
129
131
|
const isWithinBounds = pixelCoordinate.x >= drawingArea.x && pixelCoordinate.x <= drawingArea.x + drawingArea.width && pixelCoordinate.y >= drawingArea.y && pixelCoordinate.y <= drawingArea.y + drawingArea.height;
|
|
130
132
|
const staticOpacity = isWithinBounds ? opacity != null ? opacity : 1 : 0;
|
|
131
133
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
@@ -159,20 +161,16 @@ export const Point = /*#__PURE__*/memo(_ref => {
|
|
|
159
161
|
})]
|
|
160
162
|
});
|
|
161
163
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
c: animatedPoint,
|
|
173
|
-
color: animatedFillColor,
|
|
174
|
-
r: radius - strokeWidth / 2
|
|
175
|
-
})]
|
|
164
|
+
return /*#__PURE__*/_jsxs(Group, {
|
|
165
|
+
opacity: effectiveOpacity,
|
|
166
|
+
children: [strokeWidth > 0 && /*#__PURE__*/_jsx(Circle, {
|
|
167
|
+
c: animatedPoint,
|
|
168
|
+
color: stroke,
|
|
169
|
+
r: radius + strokeWidth / 2
|
|
170
|
+
}), /*#__PURE__*/_jsx(Circle, {
|
|
171
|
+
c: animatedPoint,
|
|
172
|
+
color: animatedFillColor,
|
|
173
|
+
r: radius - strokeWidth / 2
|
|
176
174
|
}), label && /*#__PURE__*/_jsx(LabelComponent, {
|
|
177
175
|
dataX: dataX,
|
|
178
176
|
dataY: dataY,
|
|
@@ -5,7 +5,7 @@ import { Circle, Group } from '@shopify/react-native-skia';
|
|
|
5
5
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
6
6
|
import { unwrapAnimatedValue } from '../utils';
|
|
7
7
|
import { projectPointWithSerializableScale } from '../utils/point';
|
|
8
|
-
import { buildTransition, defaultTransition } from '../utils/transition';
|
|
8
|
+
import { buildTransition, defaultTransition, getTransition } from '../utils/transition';
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
10
|
const defaultRadius = 5;
|
|
11
11
|
const defaultStrokeWidth = 2;
|
|
@@ -48,10 +48,7 @@ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((
|
|
|
48
48
|
var _ref2;
|
|
49
49
|
return (_ref2 = colorProp != null ? colorProp : targetSeries == null ? void 0 : targetSeries.color) != null ? _ref2 : theme.color.fgPrimary;
|
|
50
50
|
}, [colorProp, targetSeries == null ? void 0 : targetSeries.color, theme.color.fgPrimary]);
|
|
51
|
-
const updateTransition = useMemo(() =>
|
|
52
|
-
var _transitions$update;
|
|
53
|
-
return (_transitions$update = transitions == null ? void 0 : transitions.update) != null ? _transitions$update : defaultTransition;
|
|
54
|
-
}, [transitions == null ? void 0 : transitions.update]);
|
|
51
|
+
const updateTransition = useMemo(() => getTransition(transitions == null ? void 0 : transitions.update, animate, defaultTransition), [transitions == null ? void 0 : transitions.update, animate]);
|
|
55
52
|
const pulseTransition = useMemo(() => {
|
|
56
53
|
var _transitions$pulse;
|
|
57
54
|
return (_transitions$pulse = transitions == null ? void 0 : transitions.pulse) != null ? _transitions$pulse : defaultPulseTransition;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React, { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo } from 'react';
|
|
2
|
-
import { runOnJS, useAnimatedReaction, useDerivedValue, useSharedValue
|
|
2
|
+
import { runOnJS, useAnimatedReaction, useDerivedValue, useSharedValue } from 'react-native-reanimated';
|
|
3
3
|
import { useTheme } from '@coinbase/cds-mobile';
|
|
4
4
|
import { Group, Rect } from '@shopify/react-native-skia';
|
|
5
5
|
import { useCartesianChartContext } from '../ChartProvider';
|
|
6
6
|
import { ReferenceLine } from '../line';
|
|
7
|
-
import {
|
|
7
|
+
import { defaultAccessoryEnterTransition, getPointOnSerializableScale, getTransition, useScrubberContext } from '../utils';
|
|
8
|
+
import { buildTransition } from '../utils/transition';
|
|
8
9
|
import { DefaultScrubberBeacon } from './DefaultScrubberBeacon';
|
|
9
10
|
import { DefaultScrubberLabel } from './DefaultScrubberLabel';
|
|
10
11
|
import { ScrubberBeaconGroup } from './ScrubberBeaconGroup';
|
|
@@ -35,6 +36,7 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
35
36
|
beaconLabelFont,
|
|
36
37
|
idlePulse,
|
|
37
38
|
beaconTransitions,
|
|
39
|
+
transitions = beaconTransitions,
|
|
38
40
|
beaconStroke
|
|
39
41
|
} = _ref;
|
|
40
42
|
const theme = useTheme();
|
|
@@ -124,13 +126,12 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
124
126
|
}))) != null ? _series$filter$filter : [];
|
|
125
127
|
}, [series, filteredSeriesIds]);
|
|
126
128
|
const isReady = !!xScale;
|
|
129
|
+
const groupEnterTransition = useMemo(() => getTransition(transitions == null ? void 0 : transitions.enter, animate, defaultAccessoryEnterTransition), [transitions == null ? void 0 : transitions.enter, animate]);
|
|
127
130
|
useEffect(() => {
|
|
128
131
|
if (animate && isReady) {
|
|
129
|
-
scrubberOpacity.value =
|
|
130
|
-
duration: accessoryFadeTransitionDuration
|
|
131
|
-
}));
|
|
132
|
+
scrubberOpacity.value = buildTransition(1, groupEnterTransition);
|
|
132
133
|
}
|
|
133
|
-
}, [animate, isReady, scrubberOpacity]);
|
|
134
|
+
}, [animate, isReady, scrubberOpacity, groupEnterTransition]);
|
|
134
135
|
if (!isReady) return;
|
|
135
136
|
return /*#__PURE__*/_jsxs(Group, {
|
|
136
137
|
opacity: scrubberOpacity,
|
|
@@ -157,14 +158,15 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
157
158
|
idlePulse: idlePulse,
|
|
158
159
|
seriesIds: filteredSeriesIds,
|
|
159
160
|
stroke: beaconStroke,
|
|
160
|
-
transitions:
|
|
161
|
+
transitions: transitions
|
|
161
162
|
}), !hideBeaconLabels && beaconLabels.length > 0 && /*#__PURE__*/_jsx(ScrubberBeaconLabelGroup, {
|
|
162
163
|
BeaconLabelComponent: BeaconLabelComponent,
|
|
163
164
|
labelFont: beaconLabelFont,
|
|
164
165
|
labelHorizontalOffset: beaconLabelHorizontalOffset,
|
|
165
166
|
labelMinGap: beaconLabelMinGap,
|
|
166
167
|
labelPreferredSide: beaconLabelPreferredSide,
|
|
167
|
-
labels: beaconLabels
|
|
168
|
+
labels: beaconLabels,
|
|
169
|
+
transitions: transitions
|
|
168
170
|
})]
|
|
169
171
|
});
|
|
170
172
|
}));
|