@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
|
@@ -13,9 +13,36 @@ import { SparklineInteractive } from '@coinbase/cds-web-visualization'
|
|
|
13
13
|
### Default usage
|
|
14
14
|
|
|
15
15
|
```jsx live
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
() => {
|
|
17
|
+
const periods = [
|
|
18
|
+
{ label: '1H', value: 'hour' },
|
|
19
|
+
{ label: '1D', value: 'day' },
|
|
20
|
+
{ label: '1W', value: 'week' },
|
|
21
|
+
{ label: '1M', value: 'month' },
|
|
22
|
+
{ label: '1Y', value: 'year' },
|
|
23
|
+
{ label: 'All', value: 'all' },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const formatDate = useCallback((value, period) => {
|
|
27
|
+
if (period === 'hour' || period === 'day')
|
|
28
|
+
return value.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' });
|
|
29
|
+
if (period === 'week' || period === 'month')
|
|
30
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', day: 'numeric' });
|
|
31
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', year: 'numeric' });
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Box padding={3} width="100%">
|
|
36
|
+
<SparklineInteractive
|
|
37
|
+
data={sparklineInteractiveData}
|
|
38
|
+
defaultPeriod="day"
|
|
39
|
+
formatDate={formatDate}
|
|
40
|
+
periods={periods}
|
|
41
|
+
strokeColor="#F7931A"
|
|
42
|
+
/>
|
|
43
|
+
</Box>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
19
46
|
```
|
|
20
47
|
|
|
21
48
|
### Fill Type
|
|
@@ -23,34 +50,110 @@ import { SparklineInteractive } from '@coinbase/cds-web-visualization'
|
|
|
23
50
|
The fill will be added by default with a gradient style. You can set `fillType="dotted"` to get a dotted gradient fill.
|
|
24
51
|
|
|
25
52
|
```jsx live
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
53
|
+
() => {
|
|
54
|
+
const periods = [
|
|
55
|
+
{ label: '1H', value: 'hour' },
|
|
56
|
+
{ label: '1D', value: 'day' },
|
|
57
|
+
{ label: '1W', value: 'week' },
|
|
58
|
+
{ label: '1M', value: 'month' },
|
|
59
|
+
{ label: '1Y', value: 'year' },
|
|
60
|
+
{ label: 'All', value: 'all' },
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const formatDate = useCallback((value, period) => {
|
|
64
|
+
if (period === 'hour' || period === 'day')
|
|
65
|
+
return value.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' });
|
|
66
|
+
if (period === 'week' || period === 'month')
|
|
67
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', day: 'numeric' });
|
|
68
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', year: 'numeric' });
|
|
69
|
+
}, []);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<Box padding={3} width="100%">
|
|
73
|
+
<SparklineInteractive
|
|
74
|
+
fill
|
|
75
|
+
data={sparklineInteractiveData}
|
|
76
|
+
defaultPeriod="day"
|
|
77
|
+
fillType="dotted"
|
|
78
|
+
formatDate={formatDate}
|
|
79
|
+
periods={periods}
|
|
80
|
+
strokeColor="#F7931A"
|
|
81
|
+
/>
|
|
82
|
+
</Box>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
34
85
|
```
|
|
35
86
|
|
|
36
87
|
### Compact
|
|
37
88
|
|
|
38
89
|
```jsx live
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
90
|
+
() => {
|
|
91
|
+
const periods = [
|
|
92
|
+
{ label: '1H', value: 'hour' },
|
|
93
|
+
{ label: '1D', value: 'day' },
|
|
94
|
+
{ label: '1W', value: 'week' },
|
|
95
|
+
{ label: '1M', value: 'month' },
|
|
96
|
+
{ label: '1Y', value: 'year' },
|
|
97
|
+
{ label: 'All', value: 'all' },
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
const formatDate = useCallback((value, period) => {
|
|
101
|
+
if (period === 'hour' || period === 'day')
|
|
102
|
+
return value.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' });
|
|
103
|
+
if (period === 'week' || period === 'month')
|
|
104
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', day: 'numeric' });
|
|
105
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', year: 'numeric' });
|
|
106
|
+
}, []);
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<Box padding={3} width="100%">
|
|
110
|
+
<SparklineInteractive
|
|
111
|
+
compact
|
|
112
|
+
data={sparklineInteractiveData}
|
|
113
|
+
defaultPeriod="day"
|
|
114
|
+
formatDate={formatDate}
|
|
115
|
+
periods={periods}
|
|
116
|
+
strokeColor="#F7931A"
|
|
117
|
+
/>
|
|
118
|
+
</Box>
|
|
119
|
+
);
|
|
120
|
+
};
|
|
42
121
|
```
|
|
43
122
|
|
|
44
123
|
### Hide period selector
|
|
45
124
|
|
|
46
125
|
```jsx live
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
126
|
+
() => {
|
|
127
|
+
const periods = [
|
|
128
|
+
{ label: '1H', value: 'hour' },
|
|
129
|
+
{ label: '1D', value: 'day' },
|
|
130
|
+
{ label: '1W', value: 'week' },
|
|
131
|
+
{ label: '1M', value: 'month' },
|
|
132
|
+
{ label: '1Y', value: 'year' },
|
|
133
|
+
{ label: 'All', value: 'all' },
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
const formatDate = useCallback((value, period) => {
|
|
137
|
+
if (period === 'hour' || period === 'day')
|
|
138
|
+
return value.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' });
|
|
139
|
+
if (period === 'week' || period === 'month')
|
|
140
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', day: 'numeric' });
|
|
141
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', year: 'numeric' });
|
|
142
|
+
}, []);
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<Box padding={3} width="100%">
|
|
146
|
+
<SparklineInteractive
|
|
147
|
+
data={sparklineInteractiveData}
|
|
148
|
+
defaultPeriod="day"
|
|
149
|
+
formatDate={formatDate}
|
|
150
|
+
hidePeriodSelector
|
|
151
|
+
periods={periods}
|
|
152
|
+
strokeColor="#F7931A"
|
|
153
|
+
/>
|
|
154
|
+
</Box>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
54
157
|
```
|
|
55
158
|
|
|
56
159
|
### Scaling Factor
|
|
@@ -58,34 +161,169 @@ The fill will be added by default with a gradient style. You can set `fillType="
|
|
|
58
161
|
The scaling factor is usually used when you want to show less variance in the chart. An example of this is a stable coin that doesn't change price by more than a few cents.
|
|
59
162
|
|
|
60
163
|
```jsx live
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
164
|
+
() => {
|
|
165
|
+
const periods = [
|
|
166
|
+
{ label: '1H', value: 'hour' },
|
|
167
|
+
{ label: '1D', value: 'day' },
|
|
168
|
+
{ label: '1W', value: 'week' },
|
|
169
|
+
{ label: '1M', value: 'month' },
|
|
170
|
+
{ label: '1Y', value: 'year' },
|
|
171
|
+
{ label: 'All', value: 'all' },
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const formatDate = useCallback((value, period) => {
|
|
175
|
+
if (period === 'hour' || period === 'day')
|
|
176
|
+
return value.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' });
|
|
177
|
+
if (period === 'week' || period === 'month')
|
|
178
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', day: 'numeric' });
|
|
179
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', year: 'numeric' });
|
|
180
|
+
}, []);
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<Box padding={3} width="100%">
|
|
184
|
+
<SparklineInteractive
|
|
185
|
+
data={sparklineInteractiveData}
|
|
186
|
+
defaultPeriod="day"
|
|
187
|
+
formatDate={formatDate}
|
|
188
|
+
periods={periods}
|
|
189
|
+
strokeColor="#F7931A"
|
|
190
|
+
yAxisScalingFactor={0.1}
|
|
191
|
+
/>
|
|
192
|
+
</Box>
|
|
193
|
+
);
|
|
194
|
+
};
|
|
68
195
|
```
|
|
69
196
|
|
|
70
197
|
### With header
|
|
71
198
|
|
|
72
199
|
```jsx live
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
200
|
+
() => {
|
|
201
|
+
const periods = [
|
|
202
|
+
{ label: '1H', value: 'hour' },
|
|
203
|
+
{ label: '1D', value: 'day' },
|
|
204
|
+
{ label: '1W', value: 'week' },
|
|
205
|
+
{ label: '1M', value: 'month' },
|
|
206
|
+
{ label: '1Y', value: 'year' },
|
|
207
|
+
{ label: 'All', value: 'all' },
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
const formatDate = useCallback((value, period) => {
|
|
211
|
+
if (period === 'hour' || period === 'day')
|
|
212
|
+
return value.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' });
|
|
213
|
+
if (period === 'week' || period === 'month')
|
|
214
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', day: 'numeric' });
|
|
215
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', year: 'numeric' });
|
|
216
|
+
}, []);
|
|
217
|
+
|
|
218
|
+
const formatPrice = (num) => num.toLocaleString('en-US', { maximumFractionDigits: 2 });
|
|
219
|
+
|
|
220
|
+
const generateSubHead = useCallback((point, period) => {
|
|
221
|
+
const firstPoint = sparklineInteractiveData[period][0];
|
|
222
|
+
const increase = point.value > firstPoint.value;
|
|
223
|
+
return {
|
|
224
|
+
percent: `${formatPrice(Math.abs((point.value - firstPoint.value) / firstPoint.value) * 100)}%`,
|
|
225
|
+
sign: increase ? 'upwardTrend' : 'downwardTrend',
|
|
226
|
+
variant: increase ? 'positive' : 'negative',
|
|
227
|
+
priceChange: `$${formatPrice(Math.abs(point.value - firstPoint.value))}`,
|
|
228
|
+
};
|
|
229
|
+
}, []);
|
|
230
|
+
|
|
231
|
+
const headerRef = useRef(null);
|
|
232
|
+
const [currentPeriod, setCurrentPeriod] = useState('day');
|
|
233
|
+
const data = sparklineInteractiveData[currentPeriod];
|
|
234
|
+
const lastPoint = data[data.length - 1];
|
|
235
|
+
|
|
236
|
+
const handleScrub = useCallback(
|
|
237
|
+
({ point, period }) => {
|
|
238
|
+
headerRef.current?.update({
|
|
239
|
+
title: `$${point.value.toLocaleString('en-US')}`,
|
|
240
|
+
subHead: generateSubHead(point, period),
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
[generateSubHead],
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const handleScrubEnd = useCallback(() => {
|
|
247
|
+
headerRef.current?.update({
|
|
248
|
+
title: `$${formatPrice(lastPoint.value)}`,
|
|
249
|
+
subHead: generateSubHead(lastPoint, currentPeriod),
|
|
250
|
+
});
|
|
251
|
+
}, [lastPoint, currentPeriod, generateSubHead]);
|
|
252
|
+
|
|
253
|
+
const handlePeriodChanged = useCallback(
|
|
254
|
+
(period) => {
|
|
255
|
+
setCurrentPeriod(period);
|
|
256
|
+
const newData = sparklineInteractiveData[period];
|
|
257
|
+
const newLastPoint = newData[newData.length - 1];
|
|
258
|
+
headerRef.current?.update({
|
|
259
|
+
title: `$${formatPrice(newLastPoint.value)}`,
|
|
260
|
+
subHead: generateSubHead(newLastPoint, period),
|
|
261
|
+
});
|
|
262
|
+
},
|
|
263
|
+
[generateSubHead],
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<Box padding={3} width="100%">
|
|
268
|
+
<SparklineInteractive
|
|
269
|
+
fill
|
|
270
|
+
data={sparklineInteractiveData}
|
|
271
|
+
defaultPeriod="day"
|
|
272
|
+
formatDate={formatDate}
|
|
273
|
+
headerNode={
|
|
274
|
+
<SparklineInteractiveHeader
|
|
275
|
+
ref={headerRef}
|
|
276
|
+
defaultLabel="Bitcoin Price"
|
|
277
|
+
defaultSubHead={generateSubHead(lastPoint, currentPeriod)}
|
|
278
|
+
defaultTitle={`$${formatPrice(lastPoint.value)}`}
|
|
279
|
+
/>
|
|
280
|
+
}
|
|
281
|
+
onPeriodChanged={handlePeriodChanged}
|
|
282
|
+
onScrub={handleScrub}
|
|
283
|
+
onScrubEnd={handleScrubEnd}
|
|
284
|
+
periods={periods}
|
|
285
|
+
strokeColor="#F7931A"
|
|
286
|
+
/>
|
|
287
|
+
</Box>
|
|
288
|
+
);
|
|
289
|
+
};
|
|
76
290
|
```
|
|
77
291
|
|
|
78
292
|
### Custom hover data
|
|
79
293
|
|
|
80
294
|
```jsx live
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
295
|
+
() => {
|
|
296
|
+
const periods = [
|
|
297
|
+
{ label: '1H', value: 'hour' },
|
|
298
|
+
{ label: '1D', value: 'day' },
|
|
299
|
+
{ label: '1W', value: 'week' },
|
|
300
|
+
{ label: '1M', value: 'month' },
|
|
301
|
+
{ label: '1Y', value: 'year' },
|
|
302
|
+
{ label: 'All', value: 'all' },
|
|
303
|
+
];
|
|
304
|
+
|
|
305
|
+
const formatDate = useCallback((value, period) => {
|
|
306
|
+
if (period === 'hour' || period === 'day')
|
|
307
|
+
return value.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' });
|
|
308
|
+
if (period === 'week' || period === 'month')
|
|
309
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', day: 'numeric' });
|
|
310
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', year: 'numeric' });
|
|
311
|
+
}, []);
|
|
312
|
+
|
|
313
|
+
return (
|
|
314
|
+
<Box padding={3} width="100%">
|
|
315
|
+
<SparklineInteractive
|
|
316
|
+
fill
|
|
317
|
+
data={sparklineInteractiveData}
|
|
318
|
+
defaultPeriod="day"
|
|
319
|
+
formatDate={formatDate}
|
|
320
|
+
hoverData={sparklineInteractiveHoverData}
|
|
321
|
+
periods={periods}
|
|
322
|
+
strokeColor="#F7931A"
|
|
323
|
+
/>
|
|
324
|
+
</Box>
|
|
325
|
+
);
|
|
326
|
+
};
|
|
89
327
|
```
|
|
90
328
|
|
|
91
329
|
### Period selector placement
|
|
@@ -93,13 +331,37 @@ The scaling factor is usually used when you want to show less variance in the ch
|
|
|
93
331
|
`periodSelectorPlacement` can be used to place the period selector in different positions (`above` or `below`).
|
|
94
332
|
|
|
95
333
|
```jsx live
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
334
|
+
() => {
|
|
335
|
+
const periods = [
|
|
336
|
+
{ label: '1H', value: 'hour' },
|
|
337
|
+
{ label: '1D', value: 'day' },
|
|
338
|
+
{ label: '1W', value: 'week' },
|
|
339
|
+
{ label: '1M', value: 'month' },
|
|
340
|
+
{ label: '1Y', value: 'year' },
|
|
341
|
+
{ label: 'All', value: 'all' },
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
const formatDate = useCallback((value, period) => {
|
|
345
|
+
if (period === 'hour' || period === 'day')
|
|
346
|
+
return value.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' });
|
|
347
|
+
if (period === 'week' || period === 'month')
|
|
348
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', day: 'numeric' });
|
|
349
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', year: 'numeric' });
|
|
350
|
+
}, []);
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<Box padding={3} width="100%">
|
|
354
|
+
<SparklineInteractive
|
|
355
|
+
data={sparklineInteractiveData}
|
|
356
|
+
defaultPeriod="day"
|
|
357
|
+
formatDate={formatDate}
|
|
358
|
+
periodSelectorPlacement="below"
|
|
359
|
+
periods={periods}
|
|
360
|
+
strokeColor="#F7931A"
|
|
361
|
+
/>
|
|
362
|
+
</Box>
|
|
363
|
+
);
|
|
364
|
+
};
|
|
103
365
|
```
|
|
104
366
|
|
|
105
367
|
### Custom styles
|
|
@@ -107,14 +369,97 @@ The scaling factor is usually used when you want to show less variance in the ch
|
|
|
107
369
|
You can also provide custom styles, such as to remove any horizontal padding from the header.
|
|
108
370
|
|
|
109
371
|
```jsx live
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
372
|
+
() => {
|
|
373
|
+
const periods = [
|
|
374
|
+
{ label: '1H', value: 'hour' },
|
|
375
|
+
{ label: '1D', value: 'day' },
|
|
376
|
+
{ label: '1W', value: 'week' },
|
|
377
|
+
{ label: '1M', value: 'month' },
|
|
378
|
+
{ label: '1Y', value: 'year' },
|
|
379
|
+
{ label: 'All', value: 'all' },
|
|
380
|
+
];
|
|
381
|
+
|
|
382
|
+
const formatDate = useCallback((value, period) => {
|
|
383
|
+
if (period === 'hour' || period === 'day')
|
|
384
|
+
return value.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' });
|
|
385
|
+
if (period === 'week' || period === 'month')
|
|
386
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', day: 'numeric' });
|
|
387
|
+
return value.toLocaleDateString('en-US', { month: 'numeric', year: 'numeric' });
|
|
388
|
+
}, []);
|
|
389
|
+
|
|
390
|
+
const formatPrice = (num) => num.toLocaleString('en-US', { maximumFractionDigits: 2 });
|
|
391
|
+
|
|
392
|
+
const generateSubHead = useCallback((point, period) => {
|
|
393
|
+
const firstPoint = sparklineInteractiveData[period][0];
|
|
394
|
+
const increase = point.value > firstPoint.value;
|
|
395
|
+
return {
|
|
396
|
+
percent: `${formatPrice(Math.abs((point.value - firstPoint.value) / firstPoint.value) * 100)}%`,
|
|
397
|
+
sign: increase ? 'upwardTrend' : 'downwardTrend',
|
|
398
|
+
variant: increase ? 'positive' : 'negative',
|
|
399
|
+
priceChange: `$${formatPrice(Math.abs(point.value - firstPoint.value))}`,
|
|
400
|
+
};
|
|
401
|
+
}, []);
|
|
402
|
+
|
|
403
|
+
const headerRef = useRef(null);
|
|
404
|
+
const [currentPeriod, setCurrentPeriod] = useState('day');
|
|
405
|
+
const data = sparklineInteractiveData[currentPeriod];
|
|
406
|
+
const lastPoint = data[data.length - 1];
|
|
407
|
+
|
|
408
|
+
const handleScrub = useCallback(
|
|
409
|
+
({ point, period }) => {
|
|
410
|
+
headerRef.current?.update({
|
|
411
|
+
title: `$${point.value.toLocaleString('en-US')}`,
|
|
412
|
+
subHead: generateSubHead(point, period),
|
|
413
|
+
});
|
|
414
|
+
},
|
|
415
|
+
[generateSubHead],
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
const handleScrubEnd = useCallback(() => {
|
|
419
|
+
headerRef.current?.update({
|
|
420
|
+
title: `$${formatPrice(lastPoint.value)}`,
|
|
421
|
+
subHead: generateSubHead(lastPoint, currentPeriod),
|
|
422
|
+
});
|
|
423
|
+
}, [lastPoint, currentPeriod, generateSubHead]);
|
|
424
|
+
|
|
425
|
+
const handlePeriodChanged = useCallback(
|
|
426
|
+
(period) => {
|
|
427
|
+
setCurrentPeriod(period);
|
|
428
|
+
const newData = sparklineInteractiveData[period];
|
|
429
|
+
const newLastPoint = newData[newData.length - 1];
|
|
430
|
+
headerRef.current?.update({
|
|
431
|
+
title: `$${formatPrice(newLastPoint.value)}`,
|
|
432
|
+
subHead: generateSubHead(newLastPoint, period),
|
|
433
|
+
});
|
|
434
|
+
},
|
|
435
|
+
[generateSubHead],
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
return (
|
|
439
|
+
<Box padding={3} width="100%">
|
|
440
|
+
<SparklineInteractive
|
|
441
|
+
fill
|
|
442
|
+
data={sparklineInteractiveData}
|
|
443
|
+
defaultPeriod="day"
|
|
444
|
+
formatDate={formatDate}
|
|
445
|
+
headerNode={
|
|
446
|
+
<SparklineInteractiveHeader
|
|
447
|
+
ref={headerRef}
|
|
448
|
+
defaultLabel="Bitcoin Price"
|
|
449
|
+
defaultSubHead={generateSubHead(lastPoint, currentPeriod)}
|
|
450
|
+
defaultTitle={`$${formatPrice(lastPoint.value)}`}
|
|
451
|
+
/>
|
|
452
|
+
}
|
|
453
|
+
onPeriodChanged={handlePeriodChanged}
|
|
454
|
+
onScrub={handleScrub}
|
|
455
|
+
onScrubEnd={handleScrubEnd}
|
|
456
|
+
periods={periods}
|
|
457
|
+
strokeColor="#F7931A"
|
|
458
|
+
styles={{ header: { paddingLeft: 0, paddingRight: 0 } }}
|
|
459
|
+
/>
|
|
460
|
+
</Box>
|
|
461
|
+
);
|
|
462
|
+
};
|
|
118
463
|
```
|
|
119
464
|
|
|
120
465
|
## Props
|