@coinbase/cds-mcp-server 8.47.2 → 8.47.3
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 +4 -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
|
@@ -40,7 +40,7 @@ You can also provide multiple series of data to the chart. Series will have thei
|
|
|
40
40
|
|
|
41
41
|
```jsx live
|
|
42
42
|
function MonthlyGainsByAsset() {
|
|
43
|
-
const ThinSolidLine = memo((props
|
|
43
|
+
const ThinSolidLine = memo((props) => <SolidLine {...props} strokeWidth={1} />);
|
|
44
44
|
|
|
45
45
|
const tickFormatter = useCallback(
|
|
46
46
|
(amount) =>
|
|
@@ -91,7 +91,7 @@ You can also configure stacking for your chart using the `stacked` prop.
|
|
|
91
91
|
|
|
92
92
|
```jsx live
|
|
93
93
|
function MonthlyGainsByAsset() {
|
|
94
|
-
const ThinSolidLine = memo((props
|
|
94
|
+
const ThinSolidLine = memo((props) => <SolidLine {...props} strokeWidth={1} />);
|
|
95
95
|
|
|
96
96
|
const tickFormatter = useCallback(
|
|
97
97
|
(amount) =>
|
|
@@ -157,7 +157,7 @@ You can also configure multiple stacks by setting the `stackId` prop on each ser
|
|
|
157
157
|
|
|
158
158
|
```jsx live
|
|
159
159
|
function MonthlyGainsMultipleStacks() {
|
|
160
|
-
const ThinSolidLine = memo((props
|
|
160
|
+
const ThinSolidLine = memo((props) => <SolidLine {...props} strokeWidth={1} />);
|
|
161
161
|
|
|
162
162
|
const tickFormatter = useCallback(
|
|
163
163
|
(amount) =>
|
|
@@ -227,7 +227,7 @@ function MonthlyGainsMultipleStacks() {
|
|
|
227
227
|
|
|
228
228
|
```jsx live
|
|
229
229
|
function MonthlyGainsByAsset() {
|
|
230
|
-
const ThinSolidLine = memo((props
|
|
230
|
+
const ThinSolidLine = memo((props) => <SolidLine {...props} strokeWidth={1} />);
|
|
231
231
|
|
|
232
232
|
const tickFormatter = useCallback(
|
|
233
233
|
(amount) =>
|
|
@@ -284,7 +284,7 @@ function MonthlyGainsByAsset() {
|
|
|
284
284
|
GridLineComponent: ThinSolidLine,
|
|
285
285
|
tickLabelFormatter: tickFormatter,
|
|
286
286
|
width: 70,
|
|
287
|
-
domainLimit: 'strict'
|
|
287
|
+
domainLimit: 'strict',
|
|
288
288
|
}}
|
|
289
289
|
/>
|
|
290
290
|
);
|
|
@@ -368,7 +368,7 @@ You can also round the baseline of the bars by setting the `roundBaseline` prop
|
|
|
368
368
|
|
|
369
369
|
```jsx live
|
|
370
370
|
function PositiveAndNegativeCashFlow() {
|
|
371
|
-
const ThinSolidLine = memo((props
|
|
371
|
+
const ThinSolidLine = memo((props) => <SolidLine {...props} strokeWidth={1} />);
|
|
372
372
|
|
|
373
373
|
const categories = Array.from({ length: 31 }, (_, i) => `3/${i + 1}`);
|
|
374
374
|
const gains = [
|
|
@@ -455,7 +455,7 @@ function MonthlyRewards() {
|
|
|
455
455
|
{ id: 'green', data: green, color: '#33c481' },
|
|
456
456
|
];
|
|
457
457
|
|
|
458
|
-
const CustomBarStackComponent = ({ children, ...props }
|
|
458
|
+
const CustomBarStackComponent = ({ children, ...props }) => {
|
|
459
459
|
if (props.height === 0) {
|
|
460
460
|
const diameter = props.width;
|
|
461
461
|
return (
|
|
@@ -499,7 +499,7 @@ function MonthlyRewards() {
|
|
|
499
499
|
}}
|
|
500
500
|
/>
|
|
501
501
|
);
|
|
502
|
-
}
|
|
502
|
+
}
|
|
503
503
|
```
|
|
504
504
|
|
|
505
505
|
### Customization
|
|
@@ -677,13 +677,12 @@ You can set the `BarComponent` prop to render a custom component for bars.
|
|
|
677
677
|
```jsx live
|
|
678
678
|
function Candlesticks() {
|
|
679
679
|
const infoTextId = useId();
|
|
680
|
-
const infoTextRef = React.useRef
|
|
681
|
-
const selectedIndexRef = React.useRef
|
|
682
|
-
const stockData = btcCandles.slice(0, 90)
|
|
683
|
-
.reverse();
|
|
680
|
+
const infoTextRef = React.useRef(null);
|
|
681
|
+
const selectedIndexRef = React.useRef(null);
|
|
682
|
+
const stockData = btcCandles.slice(0, 90).reverse();
|
|
684
683
|
const min = Math.min(...stockData.map((data) => parseFloat(data.low)));
|
|
685
684
|
|
|
686
|
-
const ThinSolidLine = memo((props
|
|
685
|
+
const ThinSolidLine = memo((props) => <SolidLine {...props} strokeWidth={1} />);
|
|
687
686
|
|
|
688
687
|
// Custom line component that renders a rect to highlight the entire bandwidth
|
|
689
688
|
const BandwidthHighlight = memo(({ d, stroke }) => {
|
|
@@ -692,11 +691,11 @@ function Candlesticks() {
|
|
|
692
691
|
const xScale = getXScale();
|
|
693
692
|
const xAxis = getXAxis();
|
|
694
693
|
|
|
695
|
-
if (!xScale || scrubberPosition === undefined) return
|
|
694
|
+
if (!xScale || scrubberPosition === undefined) return;
|
|
696
695
|
|
|
697
696
|
const xPos = xScale(scrubberPosition);
|
|
698
697
|
|
|
699
|
-
if (xPos === undefined) return
|
|
698
|
+
if (xPos === undefined) return;
|
|
700
699
|
|
|
701
700
|
return (
|
|
702
701
|
<rect
|
|
@@ -709,49 +708,43 @@ function Candlesticks() {
|
|
|
709
708
|
);
|
|
710
709
|
});
|
|
711
710
|
|
|
712
|
-
const candlesData = stockData.map((data) => [parseFloat(data.low), parseFloat(data.high)])
|
|
713
|
-
number,
|
|
714
|
-
number,
|
|
715
|
-
][];
|
|
711
|
+
const candlesData = stockData.map((data) => [parseFloat(data.low), parseFloat(data.high)]);
|
|
716
712
|
|
|
717
|
-
const CandlestickBarComponent = memo
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
const yScale = getYScale();
|
|
713
|
+
const CandlestickBarComponent = memo(({ x, y, width, height, originY, dataX, ...props }) => {
|
|
714
|
+
const { getYScale } = useCartesianChartContext();
|
|
715
|
+
const yScale = getYScale();
|
|
721
716
|
|
|
722
|
-
|
|
717
|
+
const wickX = x + width / 2;
|
|
723
718
|
|
|
724
|
-
|
|
719
|
+
const timePeriodValue = stockData[dataX];
|
|
725
720
|
|
|
726
|
-
|
|
727
|
-
|
|
721
|
+
const open = parseFloat(timePeriodValue.open);
|
|
722
|
+
const close = parseFloat(timePeriodValue.close);
|
|
728
723
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
724
|
+
const bullish = open < close;
|
|
725
|
+
const color = bullish ? 'var(--color-fgPositive)' : 'var(--color-fgNegative)';
|
|
726
|
+
const openY = yScale?.(open) ?? 0;
|
|
727
|
+
const closeY = yScale?.(close) ?? 0;
|
|
733
728
|
|
|
734
|
-
|
|
735
|
-
|
|
729
|
+
const bodyHeight = Math.abs(openY - closeY);
|
|
730
|
+
const bodyY = openY < closeY ? openY : closeY;
|
|
736
731
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
);
|
|
732
|
+
return (
|
|
733
|
+
<g>
|
|
734
|
+
<line stroke={color} strokeWidth={1} x1={wickX} x2={wickX} y1={y} y2={y + height} />
|
|
735
|
+
<rect fill={color} height={bodyHeight} width={width} x={x} y={bodyY} />
|
|
736
|
+
</g>
|
|
737
|
+
);
|
|
738
|
+
});
|
|
745
739
|
|
|
746
|
-
const formatPrice = React.useCallback((price
|
|
740
|
+
const formatPrice = React.useCallback((price) => {
|
|
747
741
|
return new Intl.NumberFormat('en-US', {
|
|
748
742
|
style: 'currency',
|
|
749
743
|
currency: 'USD',
|
|
750
744
|
}).format(parseFloat(price));
|
|
751
745
|
}, []);
|
|
752
746
|
|
|
753
|
-
|
|
754
|
-
const formatThousandsPrice = React.useCallback((price: string) => {
|
|
747
|
+
const formatThousandsPrice = React.useCallback((price) => {
|
|
755
748
|
const formattedPrice = new Intl.NumberFormat('en-US', {
|
|
756
749
|
style: 'currency',
|
|
757
750
|
currency: 'USD',
|
|
@@ -762,8 +755,7 @@ function Candlesticks() {
|
|
|
762
755
|
return `${formattedPrice}k`;
|
|
763
756
|
}, []);
|
|
764
757
|
|
|
765
|
-
|
|
766
|
-
const formatVolume = React.useCallback((volume: string) => {
|
|
758
|
+
const formatVolume = React.useCallback((volume) => {
|
|
767
759
|
const volumeInThousands = parseFloat(volume) / 1000;
|
|
768
760
|
return (
|
|
769
761
|
new Intl.NumberFormat('en-US', {
|
|
@@ -847,7 +839,7 @@ function Candlesticks() {
|
|
|
847
839
|
</BarChart>
|
|
848
840
|
</VStack>
|
|
849
841
|
);
|
|
850
|
-
}
|
|
842
|
+
}
|
|
851
843
|
```
|
|
852
844
|
|
|
853
845
|
##### Outlined Stacks
|
|
@@ -856,7 +848,7 @@ You can set the `BarStackComponent` prop to render a custom component for stacks
|
|
|
856
848
|
|
|
857
849
|
```jsx live
|
|
858
850
|
function MonthlyRewards() {
|
|
859
|
-
const CustomBarStackComponent = ({ children, ...props }
|
|
851
|
+
const CustomBarStackComponent = ({ children, ...props }) => {
|
|
860
852
|
return (
|
|
861
853
|
<>
|
|
862
854
|
<Bar
|
|
@@ -1007,7 +1007,7 @@ function CustomComponentsCarousel() {
|
|
|
1007
1007
|
dangerouslySetBackground="rgb(var(--blue80))"
|
|
1008
1008
|
description={
|
|
1009
1009
|
<Text as="p" font="label2" numberOfLines={3} color="fgInverse">
|
|
1010
|
-
Use code NOV60 when you
|
|
1010
|
+
Use code NOV60 when you sign up for Coinbase One
|
|
1011
1011
|
</Text>
|
|
1012
1012
|
}
|
|
1013
1013
|
media={
|
|
@@ -1691,7 +1691,7 @@ function ImperativeApiCarousel() {
|
|
|
1691
1691
|
const carouselRef = useRef(null);
|
|
1692
1692
|
const [currentPageInfo, setCurrentPageInfo] = useState('Page 1');
|
|
1693
1693
|
|
|
1694
|
-
function handleGoToPage(pageIndex
|
|
1694
|
+
function handleGoToPage(pageIndex) {
|
|
1695
1695
|
if (carouselRef.current) {
|
|
1696
1696
|
const clampedPageIndex = Math.max(0, Math.min(carouselRef.current.totalPages - 1, pageIndex));
|
|
1697
1697
|
carouselRef.current.goToPage(clampedPageIndex);
|
|
@@ -268,13 +268,13 @@ You can also remove the default inset, such as to have a compact line chart.
|
|
|
268
268
|
function Insets() {
|
|
269
269
|
const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
|
|
270
270
|
|
|
271
|
-
const formatPrice = useCallback((dataIndex
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}, []);
|
|
271
|
+
const formatPrice = useCallback((dataIndex) => {
|
|
272
|
+
const price = data[dataIndex];
|
|
273
|
+
return `$${price.toLocaleString('en-US', {
|
|
274
|
+
minimumFractionDigits: 2,
|
|
275
|
+
maximumFractionDigits: 2,
|
|
276
|
+
})}`;
|
|
277
|
+
}, []);
|
|
278
278
|
|
|
279
279
|
return (
|
|
280
280
|
<HStack gap={2}>
|
|
@@ -290,7 +290,6 @@ const formatPrice = useCallback((dataIndex: number) => {
|
|
|
290
290
|
},
|
|
291
291
|
]}
|
|
292
292
|
yAxis={{ domainLimit: 'strict' }}
|
|
293
|
-
|
|
294
293
|
showArea
|
|
295
294
|
style={{ border: '2px solid var(--color-fgPrimary)' }}
|
|
296
295
|
/>
|
|
@@ -308,7 +307,6 @@ const formatPrice = useCallback((dataIndex: number) => {
|
|
|
308
307
|
},
|
|
309
308
|
]}
|
|
310
309
|
yAxis={{ domainLimit: 'strict' }}
|
|
311
|
-
|
|
312
310
|
showArea
|
|
313
311
|
style={{ border: '2px solid var(--color-fgPrimary)' }}
|
|
314
312
|
>
|
|
@@ -327,7 +325,6 @@ const formatPrice = useCallback((dataIndex: number) => {
|
|
|
327
325
|
},
|
|
328
326
|
]}
|
|
329
327
|
yAxis={{ domainLimit: 'strict' }}
|
|
330
|
-
|
|
331
328
|
showArea
|
|
332
329
|
style={{ border: '2px solid var(--color-fgPrimary)' }}
|
|
333
330
|
>
|
|
@@ -347,13 +344,13 @@ CartesianChart has built-in scrubbing functionality that can be enabled with the
|
|
|
347
344
|
function Scrubbing() {
|
|
348
345
|
const [scrubIndex, setScrubIndex] = useState(undefined);
|
|
349
346
|
|
|
350
|
-
const onScrubberPositionChange = useCallback((index
|
|
347
|
+
const onScrubberPositionChange = useCallback((index) => {
|
|
351
348
|
setScrubIndex(index);
|
|
352
349
|
}, []);
|
|
353
350
|
|
|
354
351
|
return (
|
|
355
352
|
<VStack gap={2}>
|
|
356
|
-
|
|
353
|
+
<Text font="label1">Scrubber index: {scrubIndex ?? 'none'}</Text>
|
|
357
354
|
<LineChart
|
|
358
355
|
enableScrubbing
|
|
359
356
|
onScrubberPositionChange={onScrubberPositionChange}
|
|
@@ -364,12 +361,11 @@ function Scrubbing() {
|
|
|
364
361
|
data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
|
|
365
362
|
},
|
|
366
363
|
]}
|
|
367
|
-
|
|
368
364
|
showYAxis
|
|
369
365
|
showArea
|
|
370
366
|
yAxis={{
|
|
371
367
|
showGrid: true,
|
|
372
|
-
width: 32
|
|
368
|
+
width: 32,
|
|
373
369
|
}}
|
|
374
370
|
inset={{ right: 0 }}
|
|
375
371
|
>
|
|
@@ -389,33 +385,31 @@ You can showcase the price and volume of an asset over time within one chart.
|
|
|
389
385
|
```jsx live
|
|
390
386
|
function PriceWithVolume() {
|
|
391
387
|
const [scrubIndex, setScrubIndex] = useState(null);
|
|
392
|
-
const btcData = btcCandles
|
|
393
|
-
.slice(0, 180)
|
|
394
|
-
.reverse()
|
|
388
|
+
const btcData = btcCandles.slice(0, 180).reverse();
|
|
395
389
|
|
|
396
390
|
const btcPrices = btcData.map((candle) => parseFloat(candle.close));
|
|
397
391
|
const btcVolumes = btcData.map((candle) => parseFloat(candle.volume));
|
|
398
392
|
const btcDates = btcData.map((candle) => new Date(parseInt(candle.start) * 1000));
|
|
399
393
|
|
|
400
|
-
const formatPrice = useCallback((price
|
|
394
|
+
const formatPrice = useCallback((price) => {
|
|
401
395
|
return `$${price.toLocaleString('en-US', {
|
|
402
396
|
minimumFractionDigits: 2,
|
|
403
397
|
maximumFractionDigits: 2,
|
|
404
398
|
})}`;
|
|
405
399
|
}, []);
|
|
406
400
|
|
|
407
|
-
const formatPriceInThousands = useCallback((price
|
|
401
|
+
const formatPriceInThousands = useCallback((price) => {
|
|
408
402
|
return `$${(price / 1000).toLocaleString('en-US', {
|
|
409
403
|
minimumFractionDigits: 0,
|
|
410
404
|
maximumFractionDigits: 2,
|
|
411
405
|
})}k`;
|
|
412
406
|
}, []);
|
|
413
407
|
|
|
414
|
-
const formatVolume = useCallback((volume
|
|
408
|
+
const formatVolume = useCallback((volume) => {
|
|
415
409
|
return `${(volume / 1000).toFixed(2)}K`;
|
|
416
410
|
}, []);
|
|
417
411
|
|
|
418
|
-
const formatDate = useCallback((date
|
|
412
|
+
const formatDate = useCallback((date) => {
|
|
419
413
|
return date.toLocaleDateString('en-US', {
|
|
420
414
|
month: 'short',
|
|
421
415
|
day: 'numeric',
|
|
@@ -426,22 +420,24 @@ function PriceWithVolume() {
|
|
|
426
420
|
const currentPrice = btcPrices[displayIndex];
|
|
427
421
|
const currentVolume = btcVolumes[displayIndex];
|
|
428
422
|
const currentDate = btcDates[displayIndex];
|
|
429
|
-
const priceChange =
|
|
430
|
-
|
|
431
|
-
|
|
423
|
+
const priceChange =
|
|
424
|
+
displayIndex > 0
|
|
425
|
+
? (currentPrice - btcPrices[displayIndex - 1]) / btcPrices[displayIndex - 1]
|
|
426
|
+
: 0;
|
|
432
427
|
|
|
433
428
|
const accessibilityLabel = useMemo(() => {
|
|
434
|
-
if (scrubIndex === null)
|
|
429
|
+
if (scrubIndex === null)
|
|
430
|
+
return `Current Bitcoin price: ${formatPrice(currentPrice)}, Volume: ${formatVolume(currentVolume)}`;
|
|
435
431
|
return `Bitcoin price at ${formatDate(currentDate)}: ${formatPrice(currentPrice)}, Volume: ${formatVolume(currentVolume)}`;
|
|
436
432
|
}, [scrubIndex, currentPrice, currentVolume, currentDate, formatPrice, formatVolume, formatDate]);
|
|
437
433
|
|
|
438
|
-
const ThinSolidLine = memo((props
|
|
434
|
+
const ThinSolidLine = memo((props) => <SolidLine {...props} strokeWidth={1} />);
|
|
439
435
|
|
|
440
436
|
const headerId = useId();
|
|
441
437
|
|
|
442
438
|
return (
|
|
443
439
|
<VStack gap={2}>
|
|
444
|
-
|
|
440
|
+
<SectionHeader
|
|
445
441
|
id={headerId}
|
|
446
442
|
style={{ padding: 0 }}
|
|
447
443
|
title={<Text font="title1">Bitcoin</Text>}
|
|
@@ -492,9 +488,15 @@ function PriceWithVolume() {
|
|
|
492
488
|
aria-labelledby={headerId}
|
|
493
489
|
inset={{ top: 8, left: 8, right: 0, bottom: 0 }}
|
|
494
490
|
>
|
|
495
|
-
<YAxis
|
|
491
|
+
<YAxis
|
|
492
|
+
axisId="price"
|
|
493
|
+
showGrid
|
|
494
|
+
tickLabelFormatter={formatPriceInThousands}
|
|
495
|
+
width={48}
|
|
496
|
+
GridLineComponent={ThinSolidLine}
|
|
497
|
+
/>
|
|
496
498
|
<BarPlot seriesIds={['volume']} />
|
|
497
|
-
<Line seriesId="prices"
|
|
499
|
+
<Line seriesId="prices" showArea />
|
|
498
500
|
<Scrubber seriesIds={['prices']} />
|
|
499
501
|
</CartesianChart>
|
|
500
502
|
</VStack>
|
|
@@ -508,8 +510,9 @@ You can also create your own type of cartesian chart by using `getSeriesData`, `
|
|
|
508
510
|
|
|
509
511
|
```jsx live
|
|
510
512
|
function EarningsHistory() {
|
|
511
|
-
const CirclePlot = memo(({ seriesId, opacity = 1 }
|
|
512
|
-
const { drawingArea, getSeries, getSeriesData, getXScale, getYScale } =
|
|
513
|
+
const CirclePlot = memo(({ seriesId, opacity = 1 }) => {
|
|
514
|
+
const { drawingArea, getSeries, getSeriesData, getXScale, getYScale } =
|
|
515
|
+
useCartesianChartContext();
|
|
513
516
|
const series = getSeries(seriesId);
|
|
514
517
|
const data = getSeriesData(seriesId);
|
|
515
518
|
const xScale = getXScale();
|
|
@@ -557,7 +560,7 @@ function EarningsHistory() {
|
|
|
557
560
|
const estimatedEPS = useMemo(() => [1.71, 1.82, 1.93, 2.34], []);
|
|
558
561
|
const actualEPS = useMemo(() => [1.68, 1.83, 2.01, 2.24], []);
|
|
559
562
|
|
|
560
|
-
const formatEarningAmount = useCallback((value
|
|
563
|
+
const formatEarningAmount = useCallback((value) => {
|
|
561
564
|
return `$${value.toLocaleString('en-US', {
|
|
562
565
|
minimumFractionDigits: 2,
|
|
563
566
|
maximumFractionDigits: 2,
|
|
@@ -565,7 +568,7 @@ function EarningsHistory() {
|
|
|
565
568
|
}, []);
|
|
566
569
|
|
|
567
570
|
const surprisePercentage = useCallback(
|
|
568
|
-
(index
|
|
571
|
+
(index) => {
|
|
569
572
|
const percentage = (actualEPS[index] - estimatedEPS[index]) / estimatedEPS[index];
|
|
570
573
|
const percentageString = percentage.toLocaleString('en-US', {
|
|
571
574
|
style: 'percent',
|
|
@@ -588,7 +591,7 @@ function EarningsHistory() {
|
|
|
588
591
|
[actualEPS, estimatedEPS],
|
|
589
592
|
);
|
|
590
593
|
|
|
591
|
-
const LegendEntry = memo(({ opacity = 1, label }
|
|
594
|
+
const LegendEntry = memo(({ opacity = 1, label }) => {
|
|
592
595
|
return (
|
|
593
596
|
<Box alignItems="center" gap={0.5}>
|
|
594
597
|
<LegendDot opacity={opacity} />
|
|
@@ -597,7 +600,7 @@ function EarningsHistory() {
|
|
|
597
600
|
);
|
|
598
601
|
});
|
|
599
602
|
|
|
600
|
-
const LegendDot = memo((props
|
|
603
|
+
const LegendDot = memo((props) => {
|
|
601
604
|
return <Box borderRadius={1000} width={10} height={10} background="bgPositive" {...props} />;
|
|
602
605
|
});
|
|
603
606
|
|
|
@@ -647,19 +650,35 @@ function TradingTrends() {
|
|
|
647
650
|
const gains = profitData.map((value) => (value > 0 ? value : 0));
|
|
648
651
|
const losses = profitData.map((value) => (value < 0 ? value : 0));
|
|
649
652
|
|
|
650
|
-
const renderProfit = useCallback((value
|
|
653
|
+
const renderProfit = useCallback((value) => {
|
|
651
654
|
return `$${value}M`;
|
|
652
655
|
}, []);
|
|
653
656
|
|
|
654
|
-
const ThinSolidLine = memo((props
|
|
655
|
-
|
|
657
|
+
const ThinSolidLine = memo((props) => (
|
|
658
|
+
<SolidLine {...props} strokeWidth={1} strokeLinecap="butt" />
|
|
659
|
+
));
|
|
660
|
+
const ThickSolidLine = memo((props) => (
|
|
661
|
+
<SolidLine {...props} strokeWidth={2} strokeLinecap="butt" />
|
|
662
|
+
));
|
|
656
663
|
|
|
657
664
|
return (
|
|
658
665
|
<CartesianChart
|
|
659
666
|
height={250}
|
|
660
667
|
series={[
|
|
661
|
-
{
|
|
662
|
-
|
|
668
|
+
{
|
|
669
|
+
id: 'gains',
|
|
670
|
+
data: gains,
|
|
671
|
+
yAxisId: 'profit',
|
|
672
|
+
color: 'var(--color-bgPositive)',
|
|
673
|
+
stackId: 'bars',
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
id: 'losses',
|
|
677
|
+
data: losses,
|
|
678
|
+
yAxisId: 'profit',
|
|
679
|
+
color: 'var(--color-bgNegative)',
|
|
680
|
+
stackId: 'bars',
|
|
681
|
+
},
|
|
663
682
|
{
|
|
664
683
|
id: 'revenue',
|
|
665
684
|
data: [128, 118, 122, 116, 120, 114, 118, 122, 126, 130, 134, 138],
|
|
@@ -667,17 +686,35 @@ function TradingTrends() {
|
|
|
667
686
|
color: 'var(--color-fgMuted)',
|
|
668
687
|
},
|
|
669
688
|
]}
|
|
670
|
-
xAxis={{
|
|
689
|
+
xAxis={{
|
|
690
|
+
scaleType: 'band',
|
|
691
|
+
data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
|
692
|
+
}}
|
|
671
693
|
yAxis={[
|
|
672
|
-
{
|
|
694
|
+
{
|
|
695
|
+
id: 'profit',
|
|
696
|
+
range: ({ min, max }) => ({ min: min, max: max - 64 }),
|
|
697
|
+
domain: { min: -40, max: 40 },
|
|
698
|
+
},
|
|
673
699
|
{ id: 'revenue', range: ({ min, max }) => ({ min: max - 64, max }), domain: { min: 100 } },
|
|
674
700
|
]}
|
|
675
701
|
>
|
|
676
|
-
<YAxis
|
|
702
|
+
<YAxis
|
|
703
|
+
axisId="profit"
|
|
704
|
+
position="left"
|
|
705
|
+
showGrid
|
|
706
|
+
tickLabelFormatter={renderProfit}
|
|
707
|
+
GridLineComponent={ThinSolidLine}
|
|
708
|
+
/>
|
|
677
709
|
<XAxis />
|
|
678
|
-
<ReferenceLine
|
|
710
|
+
<ReferenceLine
|
|
711
|
+
LineComponent={ThickSolidLine}
|
|
712
|
+
dataY={0}
|
|
713
|
+
yAxisId="profit"
|
|
714
|
+
stroke="rgb(var(--gray15))"
|
|
715
|
+
/>
|
|
679
716
|
<BarPlot seriesIds={['gains', 'losses']} />
|
|
680
|
-
<Line seriesId="revenue"
|
|
717
|
+
<Line seriesId="revenue" showArea />
|
|
681
718
|
</CartesianChart>
|
|
682
719
|
);
|
|
683
720
|
}
|
|
@@ -18,37 +18,37 @@ We use [fuse.js](https://www.fusejs.io/) to power the fuzzy search logic for Com
|
|
|
18
18
|
|
|
19
19
|
Basic multi-selection combobox with search.
|
|
20
20
|
|
|
21
|
-
```
|
|
21
|
+
```tsx live
|
|
22
22
|
function MultiSelect() {
|
|
23
|
-
const fruitOptions: SelectOption[] = [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
];
|
|
23
|
+
const fruitOptions: SelectOption[] = [
|
|
24
|
+
{ value: 'apple', label: 'Apple' },
|
|
25
|
+
{ value: 'banana', label: 'Banana' },
|
|
26
|
+
{ value: 'cherry', label: 'Cherry' },
|
|
27
|
+
{ value: 'date', label: 'Date' },
|
|
28
|
+
{ value: 'elderberry', label: 'Elderberry' },
|
|
29
|
+
{ value: 'fig', label: 'Fig' },
|
|
30
|
+
{ value: 'grape', label: 'Grape' },
|
|
31
|
+
{ value: 'honeydew', label: 'Honeydew' },
|
|
32
|
+
{ value: 'kiwi', label: 'Kiwi' },
|
|
33
|
+
{ value: 'lemon', label: 'Lemon' },
|
|
34
|
+
{ value: 'mango', label: 'Mango' },
|
|
35
|
+
{ value: 'orange', label: 'Orange' },
|
|
36
|
+
{ value: 'papaya', label: 'Papaya' },
|
|
37
|
+
{ value: 'raspberry', label: 'Raspberry' },
|
|
38
|
+
{ value: 'strawberry', label: 'Strawberry' },
|
|
39
|
+
];
|
|
40
40
|
|
|
41
41
|
const { value, onChange } = useMultiSelect({ initialValue: ['apple', 'banana'] });
|
|
42
42
|
|
|
43
43
|
return (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
<Combobox
|
|
45
|
+
label="Select fruits"
|
|
46
|
+
onChange={onChange}
|
|
47
|
+
options={fruitOptions}
|
|
48
|
+
placeholder="Search and select fruits..."
|
|
49
|
+
type="multi"
|
|
50
|
+
value={value}
|
|
51
|
+
/>
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
54
|
```
|
|
@@ -85,38 +85,38 @@ function SingleSelect() {
|
|
|
85
85
|
|
|
86
86
|
Communicate limits or guidance by pairing helper text with multi-select usage.
|
|
87
87
|
|
|
88
|
-
```
|
|
88
|
+
```tsx live
|
|
89
89
|
function HelperText() {
|
|
90
|
-
const fruitOptions: SelectOption[] = [
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
];
|
|
90
|
+
const fruitOptions: SelectOption[] = [
|
|
91
|
+
{ value: 'apple', label: 'Apple' },
|
|
92
|
+
{ value: 'banana', label: 'Banana' },
|
|
93
|
+
{ value: 'cherry', label: 'Cherry' },
|
|
94
|
+
{ value: 'date', label: 'Date' },
|
|
95
|
+
{ value: 'elderberry', label: 'Elderberry' },
|
|
96
|
+
{ value: 'fig', label: 'Fig' },
|
|
97
|
+
{ value: 'grape', label: 'Grape' },
|
|
98
|
+
{ value: 'honeydew', label: 'Honeydew' },
|
|
99
|
+
{ value: 'kiwi', label: 'Kiwi' },
|
|
100
|
+
{ value: 'lemon', label: 'Lemon' },
|
|
101
|
+
{ value: 'mango', label: 'Mango' },
|
|
102
|
+
{ value: 'orange', label: 'Orange' },
|
|
103
|
+
{ value: 'papaya', label: 'Papaya' },
|
|
104
|
+
{ value: 'raspberry', label: 'Raspberry' },
|
|
105
|
+
{ value: 'strawberry', label: 'Strawberry' },
|
|
106
|
+
];
|
|
107
107
|
|
|
108
108
|
const { value, onChange } = useMultiSelect({ initialValue: ['apple', 'banana'] });
|
|
109
109
|
|
|
110
110
|
return (
|
|
111
|
-
|
|
111
|
+
<Combobox
|
|
112
112
|
helperText="Choose more than one fruit"
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
113
|
+
label="Select fruits"
|
|
114
|
+
onChange={onChange}
|
|
115
|
+
options={fruitOptions}
|
|
116
|
+
placeholder="Search and select fruits..."
|
|
117
|
+
type="multi"
|
|
118
|
+
value={value}
|
|
119
|
+
/>
|
|
120
120
|
);
|
|
121
121
|
}
|
|
122
122
|
```
|
|
@@ -129,16 +129,16 @@ The alignment of the selected value(s) can be adjusted using the `align` prop.
|
|
|
129
129
|
Left / right alignment is preferred for styling.
|
|
130
130
|
::::
|
|
131
131
|
|
|
132
|
-
```
|
|
132
|
+
```tsx live
|
|
133
133
|
function AlignmentExample() {
|
|
134
134
|
const fruitOptions: SelectOption[] = [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
];
|
|
135
|
+
{ value: 'apple', label: 'Apple' },
|
|
136
|
+
{ value: 'banana', label: 'Banana' },
|
|
137
|
+
{ value: 'cherry', label: 'Cherry' },
|
|
138
|
+
{ value: 'date', label: 'Date' },
|
|
139
|
+
{ value: 'elderberry', label: 'Elderberry' },
|
|
140
|
+
{ value: 'fig', label: 'Fig' },
|
|
141
|
+
];
|
|
142
142
|
const { value: multiValue, onChange: multiOnChange } = useMultiSelect({
|
|
143
143
|
initialValue: ['apple', 'banana'],
|
|
144
144
|
});
|
|
@@ -163,7 +163,7 @@ function AlignmentExample() {
|
|
|
163
163
|
value={multiValue}
|
|
164
164
|
/>
|
|
165
165
|
</VStack>
|
|
166
|
-
)
|
|
166
|
+
);
|
|
167
167
|
}
|
|
168
168
|
```
|
|
169
169
|
|