@coinbase/cds-mcp-server 8.47.2 → 8.47.4
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 +8 -0
- package/mcp-docs/mobile/components/BarChart.txt +8 -8
- package/mcp-docs/mobile/components/CartesianChart.txt +85 -30
- package/mcp-docs/mobile/components/LineChart.txt +16 -16
- package/mcp-docs/mobile/components/Numpad.txt +2 -2
- package/mcp-docs/mobile/components/Point.txt +2 -2
- package/mcp-docs/mobile/components/ReferenceLine.txt +151 -65
- package/mcp-docs/mobile/components/Scrubber.txt +12 -19
- package/mcp-docs/mobile/components/Select.txt +1 -1
- package/mcp-docs/mobile/components/SelectAlpha.txt +1 -1
- package/mcp-docs/mobile/components/SelectOption.txt +1 -1
- package/mcp-docs/mobile/components/SlideButton.txt +1 -1
- package/mcp-docs/mobile/components/SparklineInteractive.txt +239 -46
- package/mcp-docs/mobile/components/SparklineInteractiveHeader.txt +55 -13
- package/mcp-docs/mobile/components/XAxis.txt +4 -5
- package/mcp-docs/mobile/components/YAxis.txt +2 -2
- package/mcp-docs/mobile/getting-started/theming.txt +1 -1
- package/mcp-docs/web/components/BarChart.txt +40 -48
- package/mcp-docs/web/components/Carousel.txt +2 -2
- package/mcp-docs/web/components/CartesianChart.txt +82 -45
- package/mcp-docs/web/components/Combobox.txt +61 -61
- package/mcp-docs/web/components/LineChart.txt +87 -110
- package/mcp-docs/web/components/MediaQueryProvider.txt +10 -2
- package/mcp-docs/web/components/PeriodSelector.txt +57 -39
- package/mcp-docs/web/components/Point.txt +3 -3
- package/mcp-docs/web/components/ReferenceLine.txt +341 -279
- package/mcp-docs/web/components/Scrubber.txt +48 -52
- package/mcp-docs/web/components/SelectChipAlpha.txt +1 -1
- package/mcp-docs/web/components/SparklineInteractive.txt +399 -54
- package/mcp-docs/web/components/SparklineInteractiveHeader.txt +368 -28
- package/mcp-docs/web/components/TabbedChipsAlpha.txt +1 -1
- package/mcp-docs/web/components/XAxis.txt +5 -6
- package/mcp-docs/web/components/YAxis.txt +2 -2
- package/mcp-docs/web/getting-started/theming.txt +1 -1
- package/mcp-docs/web/hooks/useBreakpoints.txt +5 -4
- package/mcp-docs/web/hooks/useMediaQuery.txt +10 -2
- package/package.json +1 -1
|
@@ -44,14 +44,14 @@ function MultipleLine() {
|
|
|
44
44
|
const chartAccessibilityLabel = `Website visitors across ${pageViews.length} pages.`;
|
|
45
45
|
|
|
46
46
|
const scrubberAccessibilityLabel = useCallback(
|
|
47
|
-
(index
|
|
47
|
+
(index) => {
|
|
48
48
|
return `${pages[index]} has ${pageViews[index]} views and ${uniqueVisitors[index]} unique visitors.`;
|
|
49
49
|
},
|
|
50
50
|
[pages, pageViews, uniqueVisitors],
|
|
51
51
|
);
|
|
52
52
|
|
|
53
53
|
const numberFormatter = useCallback(
|
|
54
|
-
(value
|
|
54
|
+
(value) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value),
|
|
55
55
|
[],
|
|
56
56
|
);
|
|
57
57
|
|
|
@@ -107,7 +107,7 @@ function DataFormat() {
|
|
|
107
107
|
const chartAccessibilityLabel = `Chart with custom X and Y data. ${yData.length} data points`;
|
|
108
108
|
|
|
109
109
|
const scrubberAccessibilityLabel = useCallback(
|
|
110
|
-
(index
|
|
110
|
+
(index) => {
|
|
111
111
|
return `Point ${index + 1}: X value ${xData[index]}, Y value ${yData[index]}`;
|
|
112
112
|
},
|
|
113
113
|
[xData, yData],
|
|
@@ -238,7 +238,7 @@ function MissingData() {
|
|
|
238
238
|
const uniqueVisitors = [4000, 3000, null, 2780, 1890, 2390, 3490];
|
|
239
239
|
|
|
240
240
|
const numberFormatter = useCallback(
|
|
241
|
-
(value
|
|
241
|
+
(value) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value),
|
|
242
242
|
[],
|
|
243
243
|
);
|
|
244
244
|
|
|
@@ -329,7 +329,7 @@ Charts have built in functionality enabled through scrubbing, which can be used
|
|
|
329
329
|
|
|
330
330
|
```jsx live
|
|
331
331
|
function Interaction() {
|
|
332
|
-
const [scrubberPosition, setScrubberPosition] = useState
|
|
332
|
+
const [scrubberPosition, setScrubberPosition] = useState();
|
|
333
333
|
|
|
334
334
|
return (
|
|
335
335
|
<VStack gap={2}>
|
|
@@ -417,7 +417,7 @@ function Transitions() {
|
|
|
417
417
|
const negativeColor = 'rgb(var(--gray15))';
|
|
418
418
|
const positiveColor = 'var(--color-fgPositive)';
|
|
419
419
|
|
|
420
|
-
function generateNextValue(previousValue
|
|
420
|
+
function generateNextValue(previousValue) {
|
|
421
421
|
const range = maxStepOffset - minStepOffset;
|
|
422
422
|
const offset = Math.random() * range + minStepOffset;
|
|
423
423
|
|
|
@@ -450,9 +450,9 @@ function Transitions() {
|
|
|
450
450
|
return data;
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
-
const MyGradient = memo((props
|
|
453
|
+
const MyGradient = memo((props) => {
|
|
454
454
|
const areaGradient = {
|
|
455
|
-
stops: ({ min, max }
|
|
455
|
+
stops: ({ min, max }) => [
|
|
456
456
|
{ offset: min, color: negativeColor, opacity: 1 },
|
|
457
457
|
{ offset: 0, color: negativeColor, opacity: 0 },
|
|
458
458
|
{ offset: 0, color: positiveColor, opacity: 0 },
|
|
@@ -480,7 +480,7 @@ function Transitions() {
|
|
|
480
480
|
}, []);
|
|
481
481
|
|
|
482
482
|
const tickLabelFormatter = useCallback(
|
|
483
|
-
(value
|
|
483
|
+
(value) =>
|
|
484
484
|
new Intl.NumberFormat('en-US', {
|
|
485
485
|
style: 'currency',
|
|
486
486
|
currency: 'USD',
|
|
@@ -490,7 +490,7 @@ function Transitions() {
|
|
|
490
490
|
);
|
|
491
491
|
|
|
492
492
|
const valueAtIndexFormatter = useCallback(
|
|
493
|
-
(dataIndex
|
|
493
|
+
(dataIndex) =>
|
|
494
494
|
new Intl.NumberFormat('en-US', {
|
|
495
495
|
style: 'currency',
|
|
496
496
|
currency: 'USD',
|
|
@@ -557,7 +557,7 @@ function BasicAccessible() {
|
|
|
557
557
|
|
|
558
558
|
// Scrubber-level accessibility label provides specific position info
|
|
559
559
|
const scrubberAccessibilityLabel = useCallback(
|
|
560
|
-
(index
|
|
560
|
+
(index) => {
|
|
561
561
|
return `Price at position ${index + 1} of ${data.length}: ${data[index]}`;
|
|
562
562
|
},
|
|
563
563
|
[data],
|
|
@@ -601,7 +601,7 @@ function AccessibleWithHeader() {
|
|
|
601
601
|
|
|
602
602
|
// Scrubber-specific accessibility label
|
|
603
603
|
const scrubberAccessibilityLabel = useCallback(
|
|
604
|
-
(index
|
|
604
|
+
(index) => {
|
|
605
605
|
return `Viewing position ${index + 1} of ${data.length}, value: ${data[index]}`;
|
|
606
606
|
},
|
|
607
607
|
[data],
|
|
@@ -657,7 +657,7 @@ Using `showXAxis` and `showYAxis` allows you to display the axes. For more infor
|
|
|
657
657
|
showGrid: true,
|
|
658
658
|
showLine: true,
|
|
659
659
|
showTickMarks: true,
|
|
660
|
-
tickLabelFormatter: (dataX
|
|
660
|
+
tickLabelFormatter: (dataX) => `Day ${dataX}`,
|
|
661
661
|
}}
|
|
662
662
|
yAxis={{
|
|
663
663
|
showGrid: true,
|
|
@@ -782,7 +782,7 @@ function GainLossChart() {
|
|
|
782
782
|
const positiveColor = 'var(--color-fgPositive)';
|
|
783
783
|
|
|
784
784
|
const tickLabelFormatter = useCallback(
|
|
785
|
-
(value
|
|
785
|
+
(value) =>
|
|
786
786
|
new Intl.NumberFormat('en-US', {
|
|
787
787
|
style: 'currency',
|
|
788
788
|
currency: 'USD',
|
|
@@ -802,7 +802,7 @@ function GainLossChart() {
|
|
|
802
802
|
const chartAccessibilityLabel = `Gain/Loss chart showing price changes. Current value: ${tickLabelFormatter(data[data.length - 1])}`;
|
|
803
803
|
|
|
804
804
|
const scrubberAccessibilityLabel = useCallback(
|
|
805
|
-
(index
|
|
805
|
+
(index) => {
|
|
806
806
|
const value = data[index];
|
|
807
807
|
const status = value >= 0 ? 'gain' : 'loss';
|
|
808
808
|
return `Position ${index + 1} of ${data.length}: ${tickLabelFormatter(value)} ${status}`;
|
|
@@ -810,7 +810,7 @@ function GainLossChart() {
|
|
|
810
810
|
[data, tickLabelFormatter],
|
|
811
811
|
);
|
|
812
812
|
|
|
813
|
-
const GradientDottedArea = memo((props
|
|
813
|
+
const GradientDottedArea = memo((props) => (
|
|
814
814
|
<DottedArea
|
|
815
815
|
{...props}
|
|
816
816
|
gradient={{
|
|
@@ -884,7 +884,8 @@ You can use `legendPosition` to place the legend at different positions around t
|
|
|
884
884
|
}}
|
|
885
885
|
yAxis={{
|
|
886
886
|
showGrid: true,
|
|
887
|
-
tickLabelFormatter: (value
|
|
887
|
+
tickLabelFormatter: (value) =>
|
|
888
|
+
new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value),
|
|
888
889
|
}}
|
|
889
890
|
/>
|
|
890
891
|
```
|
|
@@ -974,7 +975,7 @@ function HighLowPrice() {
|
|
|
974
975
|
const minPriceIndex = data.indexOf(minPrice);
|
|
975
976
|
const maxPriceIndex = data.indexOf(maxPrice);
|
|
976
977
|
|
|
977
|
-
const formatPrice = useCallback((price
|
|
978
|
+
const formatPrice = useCallback((price) => {
|
|
978
979
|
return `$${price.toLocaleString('en-US', {
|
|
979
980
|
minimumFractionDigits: 2,
|
|
980
981
|
maximumFractionDigits: 2,
|
|
@@ -1024,7 +1025,7 @@ function StylingScrubber() {
|
|
|
1024
1025
|
const uniqueVisitors = [4000, 3000, 2000, 2780, 1890, 2390, 3490];
|
|
1025
1026
|
|
|
1026
1027
|
const numberFormatter = useCallback(
|
|
1027
|
-
(value
|
|
1028
|
+
(value) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value),
|
|
1028
1029
|
[],
|
|
1029
1030
|
);
|
|
1030
1031
|
|
|
@@ -1082,7 +1083,7 @@ function DynamicChartSizing() {
|
|
|
1082
1083
|
const previousPrice = prices[prices.length - 2];
|
|
1083
1084
|
const change24h = ((latestPrice - previousPrice) / previousPrice) * 100;
|
|
1084
1085
|
|
|
1085
|
-
function DetailCell({ title, description }
|
|
1086
|
+
function DetailCell({ title, description }) {
|
|
1086
1087
|
return (
|
|
1087
1088
|
<VStack>
|
|
1088
1089
|
<Text color="fgMuted" font="label2">
|
|
@@ -1094,8 +1095,8 @@ function DynamicChartSizing() {
|
|
|
1094
1095
|
}
|
|
1095
1096
|
|
|
1096
1097
|
// Calculate 7-day moving average
|
|
1097
|
-
const calculateMA = (data
|
|
1098
|
-
const ma
|
|
1098
|
+
const calculateMA = (data, period) => {
|
|
1099
|
+
const ma = [];
|
|
1099
1100
|
for (let i = 0; i < data.length; i++) {
|
|
1100
1101
|
if (i >= period - 1) {
|
|
1101
1102
|
const sum = data.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0);
|
|
@@ -1106,19 +1107,19 @@ function DynamicChartSizing() {
|
|
|
1106
1107
|
};
|
|
1107
1108
|
|
|
1108
1109
|
const ma7 = calculateMA(prices, 7);
|
|
1109
|
-
const latestMA7
|
|
1110
|
+
const latestMA7 = ma7[ma7.length - 1];
|
|
1110
1111
|
|
|
1111
1112
|
const periodHigh = Math.max(...highs);
|
|
1112
1113
|
const periodLow = Math.min(...lows);
|
|
1113
1114
|
|
|
1114
|
-
const formatPrice = useCallback((price
|
|
1115
|
+
const formatPrice = useCallback((price) => {
|
|
1115
1116
|
return `$${price.toLocaleString('en-US', {
|
|
1116
1117
|
minimumFractionDigits: 2,
|
|
1117
1118
|
maximumFractionDigits: 2,
|
|
1118
1119
|
})}`;
|
|
1119
1120
|
}, []);
|
|
1120
1121
|
|
|
1121
|
-
const formatPercentage = useCallback((value
|
|
1122
|
+
const formatPercentage = useCallback((value) => {
|
|
1122
1123
|
const sign = value >= 0 ? '+' : '';
|
|
1123
1124
|
return `${sign}${value.toFixed(2)}%`;
|
|
1124
1125
|
}, []);
|
|
@@ -1180,21 +1181,14 @@ function Compact() {
|
|
|
1180
1181
|
const negativeData = sparklineData.map((price) => -1 * price).reverse();
|
|
1181
1182
|
const negativeCeiling = Math.max(...negativeData) + 10;
|
|
1182
1183
|
|
|
1183
|
-
const formatPrice = useCallback((price
|
|
1184
|
+
const formatPrice = useCallback((price) => {
|
|
1184
1185
|
return `$${price.toLocaleString('en-US', {
|
|
1185
1186
|
minimumFractionDigits: 2,
|
|
1186
1187
|
maximumFractionDigits: 2,
|
|
1187
1188
|
})}`;
|
|
1188
1189
|
}, []);
|
|
1189
1190
|
|
|
1190
|
-
|
|
1191
|
-
data: number[];
|
|
1192
|
-
showArea?: boolean;
|
|
1193
|
-
color?: string;
|
|
1194
|
-
referenceY: number;
|
|
1195
|
-
};
|
|
1196
|
-
|
|
1197
|
-
const CompactChart = memo(({ data, showArea, color, referenceY }: CompactChartProps) => (
|
|
1191
|
+
const CompactChart = memo(({ data, showArea, color, referenceY }) => (
|
|
1198
1192
|
<Box style={{ padding: 1 }}>
|
|
1199
1193
|
<LineChart
|
|
1200
1194
|
{...dimensions}
|
|
@@ -1214,33 +1208,25 @@ function Compact() {
|
|
|
1214
1208
|
</Box>
|
|
1215
1209
|
));
|
|
1216
1210
|
|
|
1217
|
-
const ChartCell = memo(
|
|
1218
|
-
(
|
|
1219
|
-
data,
|
|
1220
|
-
showArea,
|
|
1221
|
-
color,
|
|
1222
|
-
referenceY,
|
|
1223
|
-
subdetail,
|
|
1224
|
-
}: CompactChartProps & { subdetail: string }) => {
|
|
1225
|
-
const { isPhone } = useBreakpoints();
|
|
1211
|
+
const ChartCell = memo(({ data, showArea, color, referenceY, subdetail }) => {
|
|
1212
|
+
const { isPhone } = useBreakpoints();
|
|
1226
1213
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
);
|
|
1214
|
+
return (
|
|
1215
|
+
<ListCell
|
|
1216
|
+
description={isPhone ? undefined : assets.btc.symbol}
|
|
1217
|
+
detail={formatPrice(parseFloat(prices[0]))}
|
|
1218
|
+
intermediary={
|
|
1219
|
+
<CompactChart color={color} data={data} referenceY={referenceY} showArea={showArea} />
|
|
1220
|
+
}
|
|
1221
|
+
media={<Avatar src={assets.btc.imageUrl} />}
|
|
1222
|
+
onClick={() => console.log('clicked')}
|
|
1223
|
+
spacingVariant="condensed"
|
|
1224
|
+
style={{ padding: 0 }}
|
|
1225
|
+
subdetail={subdetail}
|
|
1226
|
+
title={isPhone ? undefined : assets.btc.name}
|
|
1227
|
+
/>
|
|
1228
|
+
);
|
|
1229
|
+
});
|
|
1244
1230
|
|
|
1245
1231
|
return (
|
|
1246
1232
|
<VStack>
|
|
@@ -1284,34 +1270,32 @@ You can use [PeriodSelector](/components/graphs/PeriodSelector) to have a chart
|
|
|
1284
1270
|
|
|
1285
1271
|
```jsx live
|
|
1286
1272
|
function AssetPriceWithDottedArea() {
|
|
1287
|
-
const BTCTab
|
|
1288
|
-
forwardRef(
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
const isActive = activeTab?.id === props.id;
|
|
1273
|
+
const BTCTab = memo(
|
|
1274
|
+
forwardRef(({ label, ...props }, ref) => {
|
|
1275
|
+
const { activeTab } = useTabsContext();
|
|
1276
|
+
const isActive = activeTab?.id === props.id;
|
|
1292
1277
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
),
|
|
1278
|
+
return (
|
|
1279
|
+
<SegmentedTab
|
|
1280
|
+
ref={ref}
|
|
1281
|
+
label={
|
|
1282
|
+
<Text
|
|
1283
|
+
font="label1"
|
|
1284
|
+
style={{
|
|
1285
|
+
transition: 'color 0.2s ease',
|
|
1286
|
+
color: isActive ? assets.btc.color : undefined,
|
|
1287
|
+
}}
|
|
1288
|
+
>
|
|
1289
|
+
{label}
|
|
1290
|
+
</Text>
|
|
1291
|
+
}
|
|
1292
|
+
{...props}
|
|
1293
|
+
/>
|
|
1294
|
+
);
|
|
1295
|
+
}),
|
|
1312
1296
|
);
|
|
1313
1297
|
|
|
1314
|
-
const BTCActiveIndicator = memo(({ style, ...props }
|
|
1298
|
+
const BTCActiveIndicator = memo(({ style, ...props }) => (
|
|
1315
1299
|
<PeriodSelectorActiveIndicator
|
|
1316
1300
|
{...props}
|
|
1317
1301
|
style={{ ...style, backgroundColor: `${assets.btc.color}1A` }}
|
|
@@ -1332,10 +1316,10 @@ function AssetPriceWithDottedArea() {
|
|
|
1332
1316
|
],
|
|
1333
1317
|
[],
|
|
1334
1318
|
);
|
|
1335
|
-
const [timePeriod, setTimePeriod] = useState
|
|
1319
|
+
const [timePeriod, setTimePeriod] = useState(tabs[0]);
|
|
1336
1320
|
|
|
1337
1321
|
const sparklineTimePeriodData = useMemo(() => {
|
|
1338
|
-
return sparklineInteractiveData[timePeriod.id
|
|
1322
|
+
return sparklineInteractiveData[timePeriod.id];
|
|
1339
1323
|
}, [timePeriod]);
|
|
1340
1324
|
|
|
1341
1325
|
const sparklineTimePeriodDataValues = useMemo(() => {
|
|
@@ -1347,7 +1331,7 @@ function AssetPriceWithDottedArea() {
|
|
|
1347
1331
|
}, [sparklineTimePeriodData]);
|
|
1348
1332
|
|
|
1349
1333
|
const onPeriodChange = useCallback(
|
|
1350
|
-
(period
|
|
1334
|
+
(period) => {
|
|
1351
1335
|
setTimePeriod(period || tabs[0]);
|
|
1352
1336
|
},
|
|
1353
1337
|
[tabs, setTimePeriod],
|
|
@@ -1372,13 +1356,13 @@ function AssetPriceWithDottedArea() {
|
|
|
1372
1356
|
);
|
|
1373
1357
|
|
|
1374
1358
|
const formatPrice = useCallback(
|
|
1375
|
-
(price
|
|
1359
|
+
(price) => {
|
|
1376
1360
|
return priceFormatter.format(price);
|
|
1377
1361
|
},
|
|
1378
1362
|
[priceFormatter],
|
|
1379
1363
|
);
|
|
1380
1364
|
|
|
1381
|
-
const formatDate = useCallback((date
|
|
1365
|
+
const formatDate = useCallback((date) => {
|
|
1382
1366
|
const dayOfWeek = date.toLocaleDateString('en-US', { weekday: 'short' });
|
|
1383
1367
|
|
|
1384
1368
|
const monthDay = date.toLocaleDateString('en-US', {
|
|
@@ -1396,7 +1380,7 @@ function AssetPriceWithDottedArea() {
|
|
|
1396
1380
|
}, []);
|
|
1397
1381
|
|
|
1398
1382
|
const scrubberLabel = useCallback(
|
|
1399
|
-
(index
|
|
1383
|
+
(index) => {
|
|
1400
1384
|
const price = scrubberPriceFormatter.format(sparklineTimePeriodDataValues[index]);
|
|
1401
1385
|
const date = formatDate(sparklineTimePeriodDataTimestamps[index]);
|
|
1402
1386
|
return (
|
|
@@ -1416,7 +1400,7 @@ function AssetPriceWithDottedArea() {
|
|
|
1416
1400
|
const chartAccessibilityLabel = `Bitcoin price chart for ${timePeriod.label} period. Current price: ${formatPrice(currentPrice)}`;
|
|
1417
1401
|
|
|
1418
1402
|
const scrubberAccessibilityLabel = useCallback(
|
|
1419
|
-
(index
|
|
1403
|
+
(index) => {
|
|
1420
1404
|
const price = scrubberPriceFormatter.format(sparklineTimePeriodDataValues[index]);
|
|
1421
1405
|
const date = formatDate(sparklineTimePeriodDataTimestamps[index]);
|
|
1422
1406
|
return `${price} USD ${date}`;
|
|
@@ -1506,25 +1490,18 @@ function MonotoneAssetPrice() {
|
|
|
1506
1490
|
);
|
|
1507
1491
|
|
|
1508
1492
|
const formatPrice = useCallback(
|
|
1509
|
-
(price
|
|
1493
|
+
(price) => {
|
|
1510
1494
|
return priceFormatter.format(price);
|
|
1511
1495
|
},
|
|
1512
1496
|
[priceFormatter],
|
|
1513
1497
|
);
|
|
1514
1498
|
|
|
1515
1499
|
const CustomYAxisTickLabel = useCallback(
|
|
1516
|
-
(props) =>
|
|
1517
|
-
<DefaultAxisTickLabel
|
|
1518
|
-
{...props}
|
|
1519
|
-
dx={4}
|
|
1520
|
-
dy={-12}
|
|
1521
|
-
horizontalAlignment="left"
|
|
1522
|
-
/>
|
|
1523
|
-
),
|
|
1500
|
+
(props) => <DefaultAxisTickLabel {...props} dx={4} dy={-12} horizontalAlignment="left" />,
|
|
1524
1501
|
[],
|
|
1525
1502
|
);
|
|
1526
1503
|
|
|
1527
|
-
const formatDate = useCallback((date
|
|
1504
|
+
const formatDate = useCallback((date) => {
|
|
1528
1505
|
const dayOfWeek = date.toLocaleDateString('en-US', { weekday: 'short' });
|
|
1529
1506
|
|
|
1530
1507
|
const monthDay = date.toLocaleDateString('en-US', {
|
|
@@ -1542,7 +1519,7 @@ function MonotoneAssetPrice() {
|
|
|
1542
1519
|
}, []);
|
|
1543
1520
|
|
|
1544
1521
|
const scrubberLabel = useCallback(
|
|
1545
|
-
(index
|
|
1522
|
+
(index) => {
|
|
1546
1523
|
const price = scrubberPriceFormatter.format(prices[index].value);
|
|
1547
1524
|
const date = formatDate(prices[index].date);
|
|
1548
1525
|
return (
|
|
@@ -1619,14 +1596,14 @@ function AssetPriceWidget() {
|
|
|
1619
1596
|
const prices = [...btcCandles].reverse().map((candle) => parseFloat(candle.close));
|
|
1620
1597
|
const latestPrice = prices[prices.length - 1];
|
|
1621
1598
|
|
|
1622
|
-
const formatPrice = (price
|
|
1599
|
+
const formatPrice = (price) => {
|
|
1623
1600
|
return new Intl.NumberFormat('en-US', {
|
|
1624
1601
|
style: 'currency',
|
|
1625
1602
|
currency: 'USD',
|
|
1626
1603
|
}).format(price);
|
|
1627
1604
|
};
|
|
1628
1605
|
|
|
1629
|
-
const formatPercentChange = (price
|
|
1606
|
+
const formatPercentChange = (price) => {
|
|
1630
1607
|
return new Intl.NumberFormat('en-US', {
|
|
1631
1608
|
style: 'percent',
|
|
1632
1609
|
minimumFractionDigits: 2,
|
|
@@ -1639,7 +1616,7 @@ function AssetPriceWidget() {
|
|
|
1639
1616
|
const chartAccessibilityLabel = `Bitcoin price chart. Current price: ${formatPrice(latestPrice)}. Change: ${formatPercentChange(percentChange)}`;
|
|
1640
1617
|
|
|
1641
1618
|
const scrubberAccessibilityLabel = useCallback(
|
|
1642
|
-
(index
|
|
1619
|
+
(index) => {
|
|
1643
1620
|
return `Bitcoin price at position ${index + 1}: ${formatPrice(prices[index])}`;
|
|
1644
1621
|
},
|
|
1645
1622
|
[prices],
|
|
@@ -1730,7 +1707,7 @@ function ServiceAvailability() {
|
|
|
1730
1707
|
const chartAccessibilityLabel = `Availability chart showing ${availabilityEvents.length} data points over time`;
|
|
1731
1708
|
|
|
1732
1709
|
const scrubberAccessibilityLabel = useCallback(
|
|
1733
|
-
(index
|
|
1710
|
+
(index) => {
|
|
1734
1711
|
const event = availabilityEvents[index];
|
|
1735
1712
|
const formattedDate = event.date.toLocaleDateString('en-US', {
|
|
1736
1713
|
weekday: 'short',
|
|
@@ -1816,13 +1793,13 @@ function ForecastAssetPrice() {
|
|
|
1816
1793
|
const clipOffset = strokeWidth;
|
|
1817
1794
|
|
|
1818
1795
|
const axisFormatter = useCallback(
|
|
1819
|
-
(dataIndex
|
|
1796
|
+
(dataIndex) => {
|
|
1820
1797
|
return startYear + dataIndex;
|
|
1821
1798
|
},
|
|
1822
1799
|
[startYear],
|
|
1823
1800
|
);
|
|
1824
1801
|
|
|
1825
|
-
const HistoricalLineComponent = memo((props
|
|
1802
|
+
const HistoricalLineComponent = memo((props) => {
|
|
1826
1803
|
const { drawingArea, getXScale } = useCartesianChartContext();
|
|
1827
1804
|
const xScale = getXScale();
|
|
1828
1805
|
|
|
@@ -1855,7 +1832,7 @@ function ForecastAssetPrice() {
|
|
|
1855
1832
|
// we need two separate line components. Otherwise we could
|
|
1856
1833
|
// have one line component with SolidLine and DottedLine inside
|
|
1857
1834
|
// of it and two clipPaths.
|
|
1858
|
-
const ForecastLineComponent = memo((props
|
|
1835
|
+
const ForecastLineComponent = memo((props) => {
|
|
1859
1836
|
const { drawingArea, getXScale } = useCartesianChartContext();
|
|
1860
1837
|
const xScale = getXScale();
|
|
1861
1838
|
|
|
@@ -39,7 +39,11 @@ Do not use `useMediaQuery` for responsive styles. Use CSS media queries or [the
|
|
|
39
39
|
const Page = () => {
|
|
40
40
|
const isMobile = useMediaQuery('(max-width: 767px)');
|
|
41
41
|
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
|
42
|
-
return
|
|
42
|
+
return (
|
|
43
|
+
<pre style={{ fontFamily: 'monospace', whiteSpace: 'pre-wrap' }}>
|
|
44
|
+
{JSON.stringify({ isMobile, prefersDarkMode }, null, 2)}
|
|
45
|
+
</pre>
|
|
46
|
+
);
|
|
43
47
|
};
|
|
44
48
|
|
|
45
49
|
const App = () => (
|
|
@@ -95,7 +99,11 @@ Complex queries cannot be solved during SSR. They are solved on the client by ca
|
|
|
95
99
|
(width < 560px) or ((width > 768px) and (width < 900px))`,
|
|
96
100
|
);
|
|
97
101
|
|
|
98
|
-
return
|
|
102
|
+
return (
|
|
103
|
+
<pre style={{ fontFamily: 'monospace', whiteSpace: 'pre-wrap' }}>
|
|
104
|
+
{JSON.stringify({ isPortrait, isHighDPI, isTouch, isMediumHeight, complexQuery }, null, 2)}
|
|
105
|
+
</pre>
|
|
106
|
+
);
|
|
99
107
|
};
|
|
100
108
|
```
|
|
101
109
|
|
|
@@ -363,35 +363,41 @@ function CustomizableAssetPriceExample() {
|
|
|
363
363
|
const [scrubIndex, setScrubIndex] = useState();
|
|
364
364
|
const breakpoints = useBreakpoints();
|
|
365
365
|
|
|
366
|
-
const formatPrice = useCallback((price
|
|
366
|
+
const formatPrice = useCallback((price) => {
|
|
367
367
|
return new Intl.NumberFormat('en-US', {
|
|
368
368
|
style: 'currency',
|
|
369
369
|
currency: 'USD',
|
|
370
370
|
}).format(price);
|
|
371
371
|
}, []);
|
|
372
372
|
|
|
373
|
-
const formatYAxisPrice = useCallback(
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
373
|
+
const formatYAxisPrice = useCallback(
|
|
374
|
+
(price) => {
|
|
375
|
+
if (breakpoints.isPhone) {
|
|
376
|
+
// Compact format for mobile: $45k, $1.2M, etc.
|
|
377
|
+
if (price >= 1000000) {
|
|
378
|
+
return `$${(price / 1000000).toFixed(1)}M`;
|
|
379
|
+
} else if (price >= 1000) {
|
|
380
|
+
return `$${(price / 1000).toFixed(0)}k`;
|
|
381
|
+
}
|
|
382
|
+
return `$${price.toFixed(0)}`;
|
|
380
383
|
}
|
|
381
|
-
return
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
384
|
+
return new Intl.NumberFormat('en-US', {
|
|
385
|
+
style: 'currency',
|
|
386
|
+
currency: 'USD',
|
|
387
|
+
minimumFractionDigits: 0,
|
|
388
|
+
maximumFractionDigits: 0,
|
|
389
|
+
}).format(price);
|
|
390
|
+
},
|
|
391
|
+
[breakpoints.isPhone],
|
|
392
|
+
);
|
|
390
393
|
const toggleShowYAxis = useCallback(() => setShowYAxis((show) => !show), []);
|
|
391
394
|
const toggleShowXAxis = useCallback(() => setShowXAxis((show) => !show), []);
|
|
392
395
|
|
|
393
396
|
const data = useMemo(() => sparklineInteractiveData[activeTab.id], [activeTab.id]);
|
|
394
|
-
const currentPrice = useMemo(
|
|
397
|
+
const currentPrice = useMemo(
|
|
398
|
+
() => sparklineInteractiveData.hour[sparklineInteractiveData.hour.length - 1].value,
|
|
399
|
+
[],
|
|
400
|
+
);
|
|
395
401
|
const currentTimePrice = useMemo(() => {
|
|
396
402
|
if (scrubIndex !== undefined) {
|
|
397
403
|
return data[scrubIndex].value;
|
|
@@ -457,17 +463,20 @@ function CustomizableAssetPriceExample() {
|
|
|
457
463
|
}
|
|
458
464
|
}, []);
|
|
459
465
|
|
|
460
|
-
const formatXAxisDate = useCallback(
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
466
|
+
const formatXAxisDate = useCallback(
|
|
467
|
+
(index) => {
|
|
468
|
+
if (!data[index]) return '';
|
|
469
|
+
const date = data[index].date;
|
|
470
|
+
const formatConfig = getFormattingConfigForPeriod(activeTab.id);
|
|
464
471
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
472
|
+
if (activeTab.id === 'hour' || activeTab.id === 'day') {
|
|
473
|
+
return date.toLocaleTimeString('en-US', formatConfig);
|
|
474
|
+
} else {
|
|
475
|
+
return date.toLocaleDateString('en-US', formatConfig);
|
|
476
|
+
}
|
|
477
|
+
},
|
|
478
|
+
[data, activeTab.id, getFormattingConfigForPeriod],
|
|
479
|
+
);
|
|
471
480
|
|
|
472
481
|
const isMobile = breakpoints.isPhone || breakpoints.isTabletPortrait;
|
|
473
482
|
|
|
@@ -476,16 +485,25 @@ function CustomizableAssetPriceExample() {
|
|
|
476
485
|
<SectionHeader
|
|
477
486
|
padding={0}
|
|
478
487
|
title={<Text font="label1">Asset Price</Text>}
|
|
479
|
-
balance={
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
488
|
+
balance={
|
|
489
|
+
<RollingNumber
|
|
490
|
+
format={{ style: 'currency', currency: 'USD' }}
|
|
491
|
+
font="display3"
|
|
492
|
+
color="fgMuted"
|
|
493
|
+
value={currentTimePrice}
|
|
494
|
+
/>
|
|
495
|
+
}
|
|
496
|
+
end={
|
|
497
|
+
isMobile ? undefined : (
|
|
498
|
+
<HStack alignItems="center">
|
|
499
|
+
<PeriodSelectorWrapper
|
|
500
|
+
activeTab={activeTab}
|
|
501
|
+
setActiveTab={setActiveTab}
|
|
502
|
+
tabs={tabs}
|
|
503
|
+
onClickSettings={onClickSettings}
|
|
504
|
+
/>
|
|
505
|
+
</HStack>
|
|
506
|
+
)
|
|
489
507
|
}
|
|
490
508
|
/>
|
|
491
509
|
<LineChart
|
|
@@ -497,7 +515,7 @@ function CustomizableAssetPriceExample() {
|
|
|
497
515
|
domainLimit: 'strict',
|
|
498
516
|
showGrid: true,
|
|
499
517
|
tickLabelFormatter: formatYAxisPrice,
|
|
500
|
-
width: breakpoints.isPhone ? 50 : 80
|
|
518
|
+
width: breakpoints.isPhone ? 50 : 80,
|
|
501
519
|
}}
|
|
502
520
|
xAxis={{
|
|
503
521
|
tickLabelFormatter: formatXAxisDate,
|
|
@@ -81,7 +81,7 @@ function AssetPriceWithMinMax() {
|
|
|
81
81
|
const minPrice = Math.min(...data);
|
|
82
82
|
const maxPrice = Math.max(...data);
|
|
83
83
|
|
|
84
|
-
const formatPrice = useCallback((price
|
|
84
|
+
const formatPrice = useCallback((price) => {
|
|
85
85
|
return new Intl.NumberFormat('en-US', {
|
|
86
86
|
style: 'currency',
|
|
87
87
|
currency: 'USD',
|
|
@@ -93,7 +93,7 @@ function AssetPriceWithMinMax() {
|
|
|
93
93
|
showArea
|
|
94
94
|
areaType="dotted"
|
|
95
95
|
height={{ base: 150, tablet: 200, desktop: 250 }}
|
|
96
|
-
points={({ dataX, dataY }
|
|
96
|
+
points={({ dataX, dataY }) => {
|
|
97
97
|
const isMin = dataY === minPrice;
|
|
98
98
|
const isMax = dataY === maxPrice;
|
|
99
99
|
|
|
@@ -115,7 +115,7 @@ function AssetPriceWithMinMax() {
|
|
|
115
115
|
style={{ outlineColor: assets.btc.color }}
|
|
116
116
|
/>
|
|
117
117
|
);
|
|
118
|
-
}
|
|
118
|
+
}
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
### Interaction
|