@coinbase/cds-mcp-server 8.41.0 → 8.43.0
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/Banner.txt +1 -1
- package/mcp-docs/mobile/components/Button.txt +2 -2
- package/mcp-docs/mobile/components/Carousel.txt +381 -72
- package/mcp-docs/mobile/components/CellMedia.txt +1 -1
- package/mcp-docs/mobile/components/ContainedAssetCard.txt +1 -1
- package/mcp-docs/mobile/components/ContentCard.txt +220 -174
- package/mcp-docs/mobile/components/ContentCardBody.txt +127 -19
- package/mcp-docs/mobile/components/ContentCardFooter.txt +63 -1
- package/mcp-docs/mobile/components/ContentCardHeader.txt +71 -16
- package/mcp-docs/mobile/components/DataCard.txt +723 -0
- package/mcp-docs/mobile/components/DotSymbol.txt +1 -1
- package/mcp-docs/mobile/components/FloatingAssetCard.txt +1 -1
- package/mcp-docs/mobile/components/Icon.txt +1 -1
- package/mcp-docs/mobile/components/IconButton.txt +1 -1
- package/mcp-docs/mobile/components/MediaCard.txt +526 -0
- package/mcp-docs/mobile/components/MessagingCard.txt +1025 -0
- package/mcp-docs/mobile/components/Scrubber.txt +140 -0
- package/mcp-docs/mobile/routes.txt +6 -3
- package/mcp-docs/web/components/Banner.txt +1 -1
- package/mcp-docs/web/components/Button.txt +2 -2
- package/mcp-docs/web/components/Carousel.txt +346 -62
- package/mcp-docs/web/components/CellMedia.txt +1 -1
- package/mcp-docs/web/components/Checkbox.txt +1 -1
- package/mcp-docs/web/components/CheckboxCell.txt +1 -1
- package/mcp-docs/web/components/Chip.txt +1 -1
- package/mcp-docs/web/components/ContentCard.txt +419 -327
- package/mcp-docs/web/components/ContentCardBody.txt +91 -16
- package/mcp-docs/web/components/ContentCardFooter.txt +231 -165
- package/mcp-docs/web/components/ContentCardHeader.txt +237 -177
- package/mcp-docs/web/components/DataCard.txt +800 -0
- package/mcp-docs/web/components/DateInput.txt +1 -1
- package/mcp-docs/web/components/DatePicker.txt +1 -1
- package/mcp-docs/web/components/DotSymbol.txt +1 -1
- package/mcp-docs/web/components/Icon.txt +1 -1
- package/mcp-docs/web/components/IconButton.txt +1 -1
- package/mcp-docs/web/components/InputChip.txt +1 -1
- package/mcp-docs/web/components/MediaCard.txt +559 -0
- package/mcp-docs/web/components/MediaChip.txt +1 -1
- package/mcp-docs/web/components/MessagingCard.txt +1054 -0
- package/mcp-docs/web/components/Radio.txt +1 -1
- package/mcp-docs/web/components/RadioCell.txt +1 -1
- package/mcp-docs/web/components/ReferenceLine.txt +1 -0
- package/mcp-docs/web/components/Scrubber.txt +106 -0
- package/mcp-docs/web/components/SearchInput.txt +1 -1
- package/mcp-docs/web/components/SelectChip.txt +1 -1
- package/mcp-docs/web/components/SidebarItem.txt +2 -2
- package/mcp-docs/web/components/Switch.txt +1 -1
- package/mcp-docs/web/components/TextInput.txt +1 -1
- package/mcp-docs/web/routes.txt +3 -0
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ import { Carousel } from '@coinbase/cds-mobile/carousel/Carousel'
|
|
|
10
10
|
|
|
11
11
|
## Examples
|
|
12
12
|
|
|
13
|
-
###
|
|
13
|
+
### Basics
|
|
14
14
|
|
|
15
15
|
Carousels are a great way to showcase a list of items in a compact and engaging way.
|
|
16
16
|
By default, Carousels have navigation and pagination enabled.
|
|
@@ -23,18 +23,40 @@ You can also set the `styles` prop to control the styles of the carousel, such a
|
|
|
23
23
|
```jsx
|
|
24
24
|
function MyCarousel() {
|
|
25
25
|
const theme = useTheme();
|
|
26
|
+
|
|
27
|
+
function SquareAssetCard({ imageUrl, name, onPress }) {
|
|
28
|
+
return (
|
|
29
|
+
<ContainedAssetCard
|
|
30
|
+
description={
|
|
31
|
+
<Text font="label2" color="fgPositive" numberOfLines={2}>
|
|
32
|
+
↗6.37%
|
|
33
|
+
</Text>
|
|
34
|
+
}
|
|
35
|
+
header={<RemoteImage height={32} source={imageUrl} width={32} />}
|
|
36
|
+
onPress={onPress}
|
|
37
|
+
subtitle={name}
|
|
38
|
+
title="$0.87"
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
26
43
|
return (
|
|
27
44
|
<Carousel
|
|
28
|
-
|
|
45
|
+
loop
|
|
46
|
+
paginationVariant="dot"
|
|
29
47
|
title="Explore Assets"
|
|
30
48
|
styles={{
|
|
31
|
-
root: {
|
|
49
|
+
root: { paddingHorizontal: theme.space[2] },
|
|
32
50
|
carousel: { gap: theme.space[1] },
|
|
33
51
|
}}
|
|
34
52
|
>
|
|
35
|
-
{Object.values(assets).map((asset
|
|
36
|
-
<CarouselItem key={asset.symbol} id={asset.symbol}>
|
|
37
|
-
<SquareAssetCard
|
|
53
|
+
{Object.values(assets).map((asset) => (
|
|
54
|
+
<CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
|
|
55
|
+
<SquareAssetCard
|
|
56
|
+
imageUrl={asset.imageUrl}
|
|
57
|
+
name={asset.symbol}
|
|
58
|
+
onPress={() => console.log(`${asset.symbol} clicked`)}
|
|
59
|
+
/>
|
|
38
60
|
</CarouselItem>
|
|
39
61
|
))}
|
|
40
62
|
</Carousel>
|
|
@@ -80,10 +102,10 @@ function DynamicSizingCarousel() {
|
|
|
80
102
|
|
|
81
103
|
return (
|
|
82
104
|
<Carousel
|
|
83
|
-
|
|
105
|
+
paginationVariant="dot"
|
|
84
106
|
title="Learn more"
|
|
85
107
|
styles={{
|
|
86
|
-
root: {
|
|
108
|
+
root: { paddingHorizontal: horizontalPadding },
|
|
87
109
|
carousel: { gap: horizontalGap },
|
|
88
110
|
}}
|
|
89
111
|
>
|
|
@@ -96,8 +118,8 @@ function DynamicSizingCarousel() {
|
|
|
96
118
|
<Pictogram dimension="64x64" name="recurringPurchases" />
|
|
97
119
|
</Box>
|
|
98
120
|
}
|
|
99
|
-
minWidth=
|
|
100
|
-
onActionPress={
|
|
121
|
+
minWidth={0}
|
|
122
|
+
onActionPress={() => console.log('Get started pressed')}
|
|
101
123
|
title="Recurring Buy"
|
|
102
124
|
width="100%"
|
|
103
125
|
/>
|
|
@@ -107,7 +129,7 @@ function DynamicSizingCarousel() {
|
|
|
107
129
|
action="Start earning"
|
|
108
130
|
dangerouslySetBackground="rgb(var(--purple70))"
|
|
109
131
|
description={
|
|
110
|
-
<Text
|
|
132
|
+
<Text font="label2" numberOfLines={3} color="fgInverse">
|
|
111
133
|
Earn staking rewards on ETH by holding it on Coinbase
|
|
112
134
|
</Text>
|
|
113
135
|
}
|
|
@@ -116,10 +138,10 @@ function DynamicSizingCarousel() {
|
|
|
116
138
|
<RemoteImage height={174} source="/img/feature.png" />
|
|
117
139
|
</Box>
|
|
118
140
|
}
|
|
119
|
-
minWidth=
|
|
120
|
-
onActionPress={
|
|
141
|
+
minWidth={0}
|
|
142
|
+
onActionPress={() => console.log('Start earning pressed')}
|
|
121
143
|
title={
|
|
122
|
-
<Text color="fgInverse"
|
|
144
|
+
<Text color="fgInverse" font="headline">
|
|
123
145
|
Up to 3.29% APR on ETHs
|
|
124
146
|
</Text>
|
|
125
147
|
}
|
|
@@ -161,10 +183,10 @@ function ResponsiveSizingCarousel() {
|
|
|
161
183
|
|
|
162
184
|
return (
|
|
163
185
|
<Carousel
|
|
164
|
-
|
|
186
|
+
paginationVariant="dot"
|
|
165
187
|
title="Learn more"
|
|
166
188
|
styles={{
|
|
167
|
-
root: {
|
|
189
|
+
root: { paddingHorizontal: horizontalPadding },
|
|
168
190
|
carousel: { gap: horizontalGap },
|
|
169
191
|
}}
|
|
170
192
|
drag="free"
|
|
@@ -236,10 +258,10 @@ function VariedSizingCarousel() {
|
|
|
236
258
|
|
|
237
259
|
return (
|
|
238
260
|
<Carousel
|
|
239
|
-
|
|
261
|
+
paginationVariant="dot"
|
|
240
262
|
title="Varied Sizing"
|
|
241
263
|
styles={{
|
|
242
|
-
root: {
|
|
264
|
+
root: { paddingHorizontal: horizontalPadding },
|
|
243
265
|
carousel: { gap: horizontalGap },
|
|
244
266
|
}}
|
|
245
267
|
>
|
|
@@ -254,8 +276,12 @@ function VariedSizingCarousel() {
|
|
|
254
276
|
minWidth={0}
|
|
255
277
|
/>
|
|
256
278
|
</CarouselItem>
|
|
257
|
-
<CarouselItem id="btc">
|
|
258
|
-
<SquareAssetCard
|
|
279
|
+
<CarouselItem id="btc" accessibilityLabel="Bitcoin">
|
|
280
|
+
<SquareAssetCard
|
|
281
|
+
imageUrl={assets.btc.imageUrl}
|
|
282
|
+
name="BTC"
|
|
283
|
+
onPress={() => console.log('BTC clicked')}
|
|
284
|
+
/>
|
|
259
285
|
</CarouselItem>
|
|
260
286
|
<CarouselItem id="secure-your-account" width={itemWidth}>
|
|
261
287
|
<NudgeCard
|
|
@@ -268,8 +294,12 @@ function VariedSizingCarousel() {
|
|
|
268
294
|
minWidth={0}
|
|
269
295
|
/>
|
|
270
296
|
</CarouselItem>
|
|
271
|
-
<CarouselItem id="eth">
|
|
272
|
-
<SquareAssetCard
|
|
297
|
+
<CarouselItem id="eth" accessibilityLabel="Ethereum">
|
|
298
|
+
<SquareAssetCard
|
|
299
|
+
imageUrl={assets.eth.imageUrl}
|
|
300
|
+
name="ETH"
|
|
301
|
+
onPress={() => console.log('ETH clicked')}
|
|
302
|
+
/>
|
|
273
303
|
</CarouselItem>
|
|
274
304
|
<CarouselItem id="complete-your-profile" width={itemWidth}>
|
|
275
305
|
<NudgeCard
|
|
@@ -282,8 +312,12 @@ function VariedSizingCarousel() {
|
|
|
282
312
|
minWidth={0}
|
|
283
313
|
/>
|
|
284
314
|
</CarouselItem>
|
|
285
|
-
<CarouselItem id="ltc">
|
|
286
|
-
<SquareAssetCard
|
|
315
|
+
<CarouselItem id="ltc" accessibilityLabel="Litecoin">
|
|
316
|
+
<SquareAssetCard
|
|
317
|
+
imageUrl={assets.ltc.imageUrl}
|
|
318
|
+
name="LTC"
|
|
319
|
+
onPress={() => console.log('LTC clicked')}
|
|
320
|
+
/>
|
|
287
321
|
</CarouselItem>
|
|
288
322
|
</Carousel>
|
|
289
323
|
);
|
|
@@ -297,20 +331,28 @@ When set to `snap`, upon release the carousel will snap to either the nearest it
|
|
|
297
331
|
|
|
298
332
|
```jsx
|
|
299
333
|
function DragCarousel() {
|
|
334
|
+
const theme = useTheme();
|
|
335
|
+
const horizontalPadding = theme.space[2];
|
|
336
|
+
const horizontalGap = theme.space[1];
|
|
337
|
+
|
|
300
338
|
return (
|
|
301
339
|
<Carousel
|
|
302
340
|
title="Explore Assets"
|
|
303
|
-
|
|
341
|
+
paginationVariant="dot"
|
|
304
342
|
drag="free"
|
|
305
343
|
styles={{
|
|
306
|
-
root: {
|
|
344
|
+
root: { paddingHorizontal: horizontalPadding },
|
|
307
345
|
carousel: { gap: horizontalGap },
|
|
308
346
|
}}
|
|
309
347
|
snapMode="item"
|
|
310
348
|
>
|
|
311
|
-
{Object.values(assets).map((asset
|
|
312
|
-
<CarouselItem key={asset.symbol} id={asset.symbol}>
|
|
313
|
-
<SquareAssetCard
|
|
349
|
+
{Object.values(assets).map((asset) => (
|
|
350
|
+
<CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
|
|
351
|
+
<SquareAssetCard
|
|
352
|
+
imageUrl={asset.imageUrl}
|
|
353
|
+
name={asset.symbol}
|
|
354
|
+
onPress={() => console.log(`${asset.symbol} clicked`)}
|
|
355
|
+
/>
|
|
314
356
|
</CarouselItem>
|
|
315
357
|
))}
|
|
316
358
|
</Carousel>
|
|
@@ -326,18 +368,27 @@ When set to `item`, the carousel will snap to the nearest item.
|
|
|
326
368
|
|
|
327
369
|
```jsx
|
|
328
370
|
function SquareItemsCarousel() {
|
|
371
|
+
const theme = useTheme();
|
|
372
|
+
const horizontalPadding = theme.space[2];
|
|
373
|
+
const horizontalGap = theme.space[1];
|
|
374
|
+
|
|
329
375
|
return (
|
|
330
376
|
<Carousel
|
|
331
377
|
title="Explore Assets"
|
|
378
|
+
paginationVariant="dot"
|
|
332
379
|
styles={{
|
|
333
|
-
root: {
|
|
380
|
+
root: { paddingHorizontal: horizontalPadding },
|
|
334
381
|
carousel: { gap: horizontalGap },
|
|
335
382
|
}}
|
|
336
383
|
snapMode="item"
|
|
337
384
|
>
|
|
338
|
-
{Object.values(assets).map((asset
|
|
339
|
-
<CarouselItem key={asset.symbol} id={asset.symbol}>
|
|
340
|
-
<SquareAssetCard
|
|
385
|
+
{Object.values(assets).map((asset) => (
|
|
386
|
+
<CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
|
|
387
|
+
<SquareAssetCard
|
|
388
|
+
imageUrl={asset.imageUrl}
|
|
389
|
+
name={asset.symbol}
|
|
390
|
+
onPress={() => console.log(`${asset.symbol} clicked`)}
|
|
391
|
+
/>
|
|
341
392
|
</CarouselItem>
|
|
342
393
|
))}
|
|
343
394
|
</Carousel>
|
|
@@ -362,18 +413,27 @@ If you want to have the next item be shown at the edge of the screen, make sure
|
|
|
362
413
|
|
|
363
414
|
```jsx
|
|
364
415
|
function OverflowCarousel() {
|
|
416
|
+
const theme = useTheme();
|
|
417
|
+
const horizontalPadding = theme.space[2];
|
|
418
|
+
const horizontalGap = theme.space[1];
|
|
419
|
+
|
|
365
420
|
return (
|
|
366
421
|
<Carousel
|
|
367
422
|
title="Explore Assets"
|
|
423
|
+
paginationVariant="dot"
|
|
368
424
|
snapMode="item"
|
|
369
425
|
styles={{
|
|
370
|
-
root: {
|
|
426
|
+
root: { paddingHorizontal: horizontalPadding },
|
|
371
427
|
carousel: { gap: horizontalGap },
|
|
372
428
|
}}
|
|
373
429
|
>
|
|
374
|
-
{Object.values(assets).map((asset
|
|
375
|
-
<CarouselItem key={asset.symbol} id={asset.symbol}>
|
|
376
|
-
<SquareAssetCard
|
|
430
|
+
{Object.values(assets).map((asset) => (
|
|
431
|
+
<CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
|
|
432
|
+
<SquareAssetCard
|
|
433
|
+
imageUrl={asset.imageUrl}
|
|
434
|
+
name={asset.symbol}
|
|
435
|
+
onPress={() => console.log(`${asset.symbol} clicked`)}
|
|
436
|
+
/>
|
|
377
437
|
</CarouselItem>
|
|
378
438
|
))}
|
|
379
439
|
</Carousel>
|
|
@@ -381,29 +441,56 @@ function OverflowCarousel() {
|
|
|
381
441
|
}
|
|
382
442
|
```
|
|
383
443
|
|
|
384
|
-
###
|
|
444
|
+
### Autoplay
|
|
385
445
|
|
|
386
|
-
|
|
446
|
+
Use `autoplay` to allow for automatic page advancement. The default interval is 3 seconds but can be changed with `autoplayInterval`.
|
|
387
447
|
|
|
388
|
-
|
|
448
|
+
It is recommended to use pagination with autoplay so users know how many pages there are, and you should also set `paginationVariant="dot"` to best indicate the active page and progress.
|
|
389
449
|
|
|
390
|
-
|
|
450
|
+
```jsx
|
|
451
|
+
function AutoplayCarousel() {
|
|
452
|
+
const theme = useTheme();
|
|
453
|
+
const horizontalPadding = theme.space[2];
|
|
454
|
+
const horizontalGap = theme.space[1];
|
|
391
455
|
|
|
392
|
-
|
|
456
|
+
return (
|
|
457
|
+
<Carousel
|
|
458
|
+
autoplay
|
|
459
|
+
loop
|
|
460
|
+
paginationVariant="dot"
|
|
461
|
+
title="Trending Assets"
|
|
462
|
+
styles={{
|
|
463
|
+
root: { paddingHorizontal: horizontalPadding },
|
|
464
|
+
carousel: { gap: horizontalGap },
|
|
465
|
+
}}
|
|
466
|
+
>
|
|
467
|
+
{Object.values(assets).map((asset) => (
|
|
468
|
+
<CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
|
|
469
|
+
<SquareAssetCard
|
|
470
|
+
imageUrl={asset.imageUrl}
|
|
471
|
+
name={asset.symbol}
|
|
472
|
+
onPress={() => console.log(`${asset.symbol} clicked`)}
|
|
473
|
+
/>
|
|
474
|
+
</CarouselItem>
|
|
475
|
+
))}
|
|
476
|
+
</Carousel>
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
```
|
|
393
480
|
|
|
394
481
|
```jsx
|
|
395
|
-
function
|
|
482
|
+
function CustomIntervalCarousel() {
|
|
396
483
|
const theme = useTheme();
|
|
397
|
-
const windowWidth = Dimensions.get('window').width;
|
|
398
|
-
|
|
399
484
|
const horizontalPadding = theme.space[2];
|
|
400
|
-
const carouselWidth = windowWidth - horizontalPadding * 2;
|
|
401
485
|
const horizontalGap = theme.space[1];
|
|
402
486
|
|
|
403
487
|
return (
|
|
404
488
|
<Carousel
|
|
489
|
+
autoplay
|
|
490
|
+
autoplayInterval={5000}
|
|
405
491
|
loop
|
|
406
|
-
|
|
492
|
+
paginationVariant="dot"
|
|
493
|
+
title="5 Second Interval"
|
|
407
494
|
styles={{
|
|
408
495
|
root: { paddingHorizontal: horizontalPadding },
|
|
409
496
|
carousel: { gap: horizontalGap },
|
|
@@ -411,7 +498,11 @@ function LoopingSnapCarousel() {
|
|
|
411
498
|
>
|
|
412
499
|
{Object.values(assets).map((asset) => (
|
|
413
500
|
<CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
|
|
414
|
-
<SquareAssetCard
|
|
501
|
+
<SquareAssetCard
|
|
502
|
+
imageUrl={asset.imageUrl}
|
|
503
|
+
name={asset.symbol}
|
|
504
|
+
onPress={() => console.log(`${asset.symbol} clicked`)}
|
|
505
|
+
/>
|
|
415
506
|
</CarouselItem>
|
|
416
507
|
))}
|
|
417
508
|
</Carousel>
|
|
@@ -419,25 +510,23 @@ function LoopingSnapCarousel() {
|
|
|
419
510
|
}
|
|
420
511
|
```
|
|
421
512
|
|
|
422
|
-
|
|
513
|
+
### Looping
|
|
423
514
|
|
|
424
|
-
|
|
515
|
+
Use `loop` to allow for infinite scrolling.
|
|
425
516
|
|
|
426
517
|
```jsx
|
|
427
|
-
function
|
|
518
|
+
function LoopingCarousel() {
|
|
428
519
|
const theme = useTheme();
|
|
429
|
-
const windowWidth = Dimensions.get('window').width;
|
|
430
|
-
|
|
431
520
|
const horizontalPadding = theme.space[2];
|
|
432
|
-
const carouselWidth = windowWidth - horizontalPadding * 2;
|
|
433
521
|
const horizontalGap = theme.space[1];
|
|
434
522
|
|
|
435
523
|
return (
|
|
436
524
|
<Carousel
|
|
525
|
+
autoplay
|
|
437
526
|
loop
|
|
438
|
-
|
|
527
|
+
paginationVariant="dot"
|
|
439
528
|
snapMode="item"
|
|
440
|
-
title="
|
|
529
|
+
title="Infinite Scroll"
|
|
441
530
|
styles={{
|
|
442
531
|
root: { paddingHorizontal: horizontalPadding },
|
|
443
532
|
carousel: { gap: horizontalGap },
|
|
@@ -445,7 +534,11 @@ function LoopingFreeDragCarousel() {
|
|
|
445
534
|
>
|
|
446
535
|
{Object.values(assets).map((asset) => (
|
|
447
536
|
<CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
|
|
448
|
-
<SquareAssetCard
|
|
537
|
+
<SquareAssetCard
|
|
538
|
+
imageUrl={asset.imageUrl}
|
|
539
|
+
name={asset.symbol}
|
|
540
|
+
onPress={() => console.log(`${asset.symbol} clicked`)}
|
|
541
|
+
/>
|
|
449
542
|
</CarouselItem>
|
|
450
543
|
))}
|
|
451
544
|
</Carousel>
|
|
@@ -462,7 +555,7 @@ You need to use `accessibilityLabel` or `accessibilityLabelledBy` props to provi
|
|
|
462
555
|
Similar to web, you are provided the `isVisible` render prop, however it is not necessary to use since mobile users do not have a keyboard.
|
|
463
556
|
|
|
464
557
|
```jsx
|
|
465
|
-
<Carousel>
|
|
558
|
+
<Carousel paginationVariant="dot">
|
|
466
559
|
<CarouselItem id="btc" accessibilityLabel="Bitcoin">
|
|
467
560
|
<SquareAssetCard imageUrl={assets.btc.imageUrl} name={assets.btc.symbol} />
|
|
468
561
|
</CarouselItem>
|
|
@@ -698,6 +791,7 @@ You can use the `styles` props to customize different parts of the carousel.
|
|
|
698
791
|
function CustomStylesCarousel() {
|
|
699
792
|
return (
|
|
700
793
|
<Carousel
|
|
794
|
+
paginationVariant="dot"
|
|
701
795
|
styles={{
|
|
702
796
|
root: { paddingInline: horizontalPadding },
|
|
703
797
|
carousel: { gap: horizontalGap },
|
|
@@ -778,12 +872,200 @@ function CustomStylesCarousel() {
|
|
|
778
872
|
}
|
|
779
873
|
```
|
|
780
874
|
|
|
875
|
+
#### Composed Example
|
|
876
|
+
|
|
877
|
+
You can use `useCarouselAutoplayContext` inside a custom `PaginationComponent` to build your own controls. This example shows a composed layout with pagination and play/pause on the left, and navigation arrows on the right.
|
|
878
|
+
|
|
879
|
+
```jsx
|
|
880
|
+
function ComposedAutoplayCarousel() {
|
|
881
|
+
const carouselRef = useRef(null);
|
|
882
|
+
const theme = useTheme();
|
|
883
|
+
|
|
884
|
+
function CustomPaginationDots({ totalPages, activePageIndex, onPressPage }) {
|
|
885
|
+
const autoplay = useCarouselAutoplayContext();
|
|
886
|
+
|
|
887
|
+
return (
|
|
888
|
+
<HStack
|
|
889
|
+
alignItems="center"
|
|
890
|
+
background="bgSecondary"
|
|
891
|
+
borderRadius={1000}
|
|
892
|
+
gap={0.5}
|
|
893
|
+
paddingX={1.5}
|
|
894
|
+
style={{ height: theme.space[5] }}
|
|
895
|
+
>
|
|
896
|
+
{Array.from({ length: totalPages }, (_, index) => {
|
|
897
|
+
const isActive = index === activePageIndex;
|
|
898
|
+
const showProgress = isActive && autoplay.isEnabled;
|
|
899
|
+
|
|
900
|
+
const springProps = useSpring({
|
|
901
|
+
width: isActive ? theme.space[3] : theme.space[1],
|
|
902
|
+
backgroundColor:
|
|
903
|
+
isActive && !showProgress ? theme.color.fgPrimary : theme.color.fgMuted,
|
|
904
|
+
config: { tension: 300, friction: 25 },
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
// Calculate progress from timing info
|
|
908
|
+
const remainingTime = autoplay.getRemainingTime();
|
|
909
|
+
const progress = 1 - remainingTime / autoplay.totalTime;
|
|
910
|
+
const progressSpring = useSpring({
|
|
911
|
+
width: autoplay.isPlaying ? theme.space[3] : progress * theme.space[3],
|
|
912
|
+
config: autoplay.isPlaying ? { duration: remainingTime } : { duration: 0 },
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
return (
|
|
916
|
+
<Pressable
|
|
917
|
+
key={index}
|
|
918
|
+
accessibilityLabel={`Go to page ${index + 1}`}
|
|
919
|
+
borderRadius={1000}
|
|
920
|
+
borderWidth={0}
|
|
921
|
+
onPress={() => onPressPage?.(index)}
|
|
922
|
+
overflow="hidden"
|
|
923
|
+
>
|
|
924
|
+
<animated.View
|
|
925
|
+
style={{
|
|
926
|
+
width: springProps.width,
|
|
927
|
+
height: theme.space[1],
|
|
928
|
+
backgroundColor: springProps.backgroundColor,
|
|
929
|
+
borderRadius: theme.borderRadius[1000],
|
|
930
|
+
overflow: 'hidden',
|
|
931
|
+
}}
|
|
932
|
+
>
|
|
933
|
+
{showProgress && (
|
|
934
|
+
<animated.View
|
|
935
|
+
style={{
|
|
936
|
+
width: progressSpring.width,
|
|
937
|
+
height: '100%',
|
|
938
|
+
backgroundColor: theme.color.fgPrimary,
|
|
939
|
+
borderRadius: theme.borderRadius[1000],
|
|
940
|
+
}}
|
|
941
|
+
/>
|
|
942
|
+
)}
|
|
943
|
+
</animated.View>
|
|
944
|
+
</Pressable>
|
|
945
|
+
);
|
|
946
|
+
})}
|
|
947
|
+
</HStack>
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
function CustomControls({ totalPages, activePageIndex, onPressPage }) {
|
|
952
|
+
const autoplay = useCarouselAutoplayContext();
|
|
953
|
+
|
|
954
|
+
return (
|
|
955
|
+
<HStack justifyContent="space-between" paddingY={1}>
|
|
956
|
+
<HStack gap={1} alignItems="center">
|
|
957
|
+
<CustomPaginationDots
|
|
958
|
+
totalPages={totalPages}
|
|
959
|
+
activePageIndex={activePageIndex}
|
|
960
|
+
onPressPage={onPressPage}
|
|
961
|
+
/>
|
|
962
|
+
<IconButton
|
|
963
|
+
accessibilityLabel={autoplay.isStopped ? 'Play' : 'Pause'}
|
|
964
|
+
name={autoplay.isStopped ? 'play' : 'pause'}
|
|
965
|
+
onPress={autoplay.toggle}
|
|
966
|
+
variant="secondary"
|
|
967
|
+
/>
|
|
968
|
+
</HStack>
|
|
969
|
+
<HStack gap={1}>
|
|
970
|
+
<IconButton
|
|
971
|
+
accessibilityLabel="Previous"
|
|
972
|
+
disabled={activePageIndex <= 0}
|
|
973
|
+
name="caretLeft"
|
|
974
|
+
onPress={() => carouselRef.current?.goToPage(activePageIndex - 1)}
|
|
975
|
+
variant="secondary"
|
|
976
|
+
/>
|
|
977
|
+
<IconButton
|
|
978
|
+
accessibilityLabel="Next"
|
|
979
|
+
disabled={activePageIndex >= totalPages - 1}
|
|
980
|
+
name="caretRight"
|
|
981
|
+
onPress={() => carouselRef.current?.goToPage(activePageIndex + 1)}
|
|
982
|
+
variant="secondary"
|
|
983
|
+
/>
|
|
984
|
+
</HStack>
|
|
985
|
+
</HStack>
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
return (
|
|
990
|
+
<Carousel
|
|
991
|
+
ref={carouselRef}
|
|
992
|
+
autoplay
|
|
993
|
+
loop
|
|
994
|
+
hideNavigation
|
|
995
|
+
PaginationComponent={CustomControls}
|
|
996
|
+
styles={{
|
|
997
|
+
root: { paddingHorizontal: theme.space[3] },
|
|
998
|
+
carousel: { gap: theme.space[2] },
|
|
999
|
+
}}
|
|
1000
|
+
>
|
|
1001
|
+
<CarouselItem id="innovation-1" width={`${(100 - 2 * 2) / 3}%`}>
|
|
1002
|
+
<NudgeCard
|
|
1003
|
+
title="Innovation"
|
|
1004
|
+
description="Cards are a great way to showcase content."
|
|
1005
|
+
pictogram="shield"
|
|
1006
|
+
width="100%"
|
|
1007
|
+
minWidth={0}
|
|
1008
|
+
/>
|
|
1009
|
+
</CarouselItem>
|
|
1010
|
+
<CarouselItem id="innovation-2" width={`${(100 - 2 * 2) / 3}%`}>
|
|
1011
|
+
<NudgeCard
|
|
1012
|
+
title="Innovation"
|
|
1013
|
+
description="Cards are a great way to showcase content."
|
|
1014
|
+
pictogram="security"
|
|
1015
|
+
width="100%"
|
|
1016
|
+
minWidth={0}
|
|
1017
|
+
/>
|
|
1018
|
+
</CarouselItem>
|
|
1019
|
+
<CarouselItem id="innovation-3" width={`${(100 - 2 * 2) / 3}%`}>
|
|
1020
|
+
<NudgeCard
|
|
1021
|
+
title="Innovation"
|
|
1022
|
+
description="Cards are a great way to showcase content."
|
|
1023
|
+
pictogram="institutions"
|
|
1024
|
+
width="100%"
|
|
1025
|
+
minWidth={0}
|
|
1026
|
+
/>
|
|
1027
|
+
</CarouselItem>
|
|
1028
|
+
<CarouselItem id="innovation-4" width={`${(100 - 2 * 2) / 3}%`}>
|
|
1029
|
+
<NudgeCard
|
|
1030
|
+
title="Innovation"
|
|
1031
|
+
description="Cards are a great way to showcase content."
|
|
1032
|
+
pictogram="key"
|
|
1033
|
+
width="100%"
|
|
1034
|
+
minWidth={0}
|
|
1035
|
+
/>
|
|
1036
|
+
</CarouselItem>
|
|
1037
|
+
<CarouselItem id="innovation-5" width={`${(100 - 2 * 2) / 3}%`}>
|
|
1038
|
+
<NudgeCard
|
|
1039
|
+
title="Innovation"
|
|
1040
|
+
description="Cards are a great way to showcase content."
|
|
1041
|
+
pictogram="receipt"
|
|
1042
|
+
width="100%"
|
|
1043
|
+
minWidth={0}
|
|
1044
|
+
/>
|
|
1045
|
+
</CarouselItem>
|
|
1046
|
+
<CarouselItem id="innovation-6" width={`${(100 - 2 * 2) / 3}%`}>
|
|
1047
|
+
<NudgeCard
|
|
1048
|
+
title="Innovation"
|
|
1049
|
+
description="Cards are a great way to showcase content."
|
|
1050
|
+
pictogram="worldwide"
|
|
1051
|
+
width="100%"
|
|
1052
|
+
minWidth={0}
|
|
1053
|
+
/>
|
|
1054
|
+
</CarouselItem>
|
|
1055
|
+
</Carousel>
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
```
|
|
1059
|
+
|
|
781
1060
|
#### Dynamic Content
|
|
782
1061
|
|
|
783
1062
|
You can dynamically add and remove items from the carousel.
|
|
784
1063
|
|
|
785
1064
|
```jsx
|
|
786
1065
|
function DynamicContentCarousel() {
|
|
1066
|
+
const theme = useTheme();
|
|
1067
|
+
const horizontalPadding = theme.space[2];
|
|
1068
|
+
const horizontalGap = theme.space[1];
|
|
787
1069
|
const [items, setItems] = useState(Object.values(assets).slice(0, 3));
|
|
788
1070
|
|
|
789
1071
|
function addAsset() {
|
|
@@ -804,14 +1086,19 @@ function DynamicContentCarousel() {
|
|
|
804
1086
|
</HStack>
|
|
805
1087
|
<Carousel
|
|
806
1088
|
title="Explore Assets"
|
|
1089
|
+
paginationVariant="dot"
|
|
807
1090
|
styles={{
|
|
808
|
-
root: {
|
|
1091
|
+
root: { paddingHorizontal: horizontalPadding },
|
|
809
1092
|
carousel: { gap: horizontalGap, height: 156 },
|
|
810
1093
|
}}
|
|
811
1094
|
>
|
|
812
|
-
{items.map((asset
|
|
813
|
-
<CarouselItem key={asset.symbol} id={asset.symbol}>
|
|
814
|
-
<SquareAssetCard
|
|
1095
|
+
{items.map((asset) => (
|
|
1096
|
+
<CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
|
|
1097
|
+
<SquareAssetCard
|
|
1098
|
+
imageUrl={asset.imageUrl}
|
|
1099
|
+
name={asset.symbol}
|
|
1100
|
+
onPress={() => console.log(`${asset.symbol} clicked`)}
|
|
1101
|
+
/>
|
|
815
1102
|
</CarouselItem>
|
|
816
1103
|
))}
|
|
817
1104
|
</Carousel>
|
|
@@ -822,10 +1109,16 @@ function DynamicContentCarousel() {
|
|
|
822
1109
|
|
|
823
1110
|
#### Hide Navigation and Pagination
|
|
824
1111
|
|
|
825
|
-
You can hide the navigation and pagination components of the carousel if desired.
|
|
1112
|
+
You can hide the navigation and pagination components of the carousel if desired (using `hideNavigation` and `hidePagination` props).
|
|
1113
|
+
|
|
1114
|
+
Note that this can prevent proper accessibility for the carousel, if carousel items are not focusable. If hiding pagination, it's recommended to ensure that the carousel is navigable by other means.
|
|
826
1115
|
|
|
827
1116
|
```jsx
|
|
828
1117
|
function HideNavigationAndPaginationCarousel() {
|
|
1118
|
+
const theme = useTheme();
|
|
1119
|
+
const horizontalPadding = theme.space[2];
|
|
1120
|
+
const horizontalGap = theme.space[1];
|
|
1121
|
+
|
|
829
1122
|
return (
|
|
830
1123
|
<Carousel
|
|
831
1124
|
title="Explore Assets"
|
|
@@ -834,13 +1127,17 @@ function HideNavigationAndPaginationCarousel() {
|
|
|
834
1127
|
drag="free"
|
|
835
1128
|
snapMode="item"
|
|
836
1129
|
styles={{
|
|
837
|
-
root: {
|
|
1130
|
+
root: { paddingHorizontal: horizontalPadding },
|
|
838
1131
|
carousel: { gap: horizontalGap },
|
|
839
1132
|
}}
|
|
840
1133
|
>
|
|
841
|
-
{Object.values(assets).map((asset
|
|
842
|
-
<CarouselItem key={asset.symbol} id={asset.symbol}>
|
|
843
|
-
<SquareAssetCard
|
|
1134
|
+
{Object.values(assets).map((asset) => (
|
|
1135
|
+
<CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
|
|
1136
|
+
<SquareAssetCard
|
|
1137
|
+
imageUrl={asset.imageUrl}
|
|
1138
|
+
name={asset.symbol}
|
|
1139
|
+
onPress={() => console.log(`${asset.symbol} clicked`)}
|
|
1140
|
+
/>
|
|
844
1141
|
</CarouselItem>
|
|
845
1142
|
))}
|
|
846
1143
|
</Carousel>
|
|
@@ -950,7 +1247,11 @@ function ImperativeApiCarousel() {
|
|
|
950
1247
|
>
|
|
951
1248
|
{Object.values(assets).map((asset) => (
|
|
952
1249
|
<CarouselItem key={asset.symbol} accessibilityLabel={asset.name} id={asset.symbol}>
|
|
953
|
-
<SquareAssetCard
|
|
1250
|
+
<SquareAssetCard
|
|
1251
|
+
imageUrl={asset.imageUrl}
|
|
1252
|
+
name={asset.symbol}
|
|
1253
|
+
onPress={() => console.log(`${asset.symbol} clicked`)}
|
|
1254
|
+
/>
|
|
954
1255
|
</CarouselItem>
|
|
955
1256
|
))}
|
|
956
1257
|
</Carousel>
|
|
@@ -1026,9 +1327,13 @@ function AnimatedPaginationCarousel() {
|
|
|
1026
1327
|
}}
|
|
1027
1328
|
title="Explore Assets"
|
|
1028
1329
|
>
|
|
1029
|
-
{Object.values(assets).map((asset
|
|
1330
|
+
{Object.values(assets).map((asset) => (
|
|
1030
1331
|
<CarouselItem key={asset.symbol} id={asset.symbol} accessibilityLabel={asset.name}>
|
|
1031
|
-
<SquareAssetCard
|
|
1332
|
+
<SquareAssetCard
|
|
1333
|
+
imageUrl={asset.imageUrl}
|
|
1334
|
+
name={asset.symbol}
|
|
1335
|
+
onPress={() => console.log(`${asset.symbol} clicked`)}
|
|
1336
|
+
/>
|
|
1032
1337
|
</CarouselItem>
|
|
1033
1338
|
))}
|
|
1034
1339
|
</Carousel>
|
|
@@ -1061,6 +1366,8 @@ You can use the `onChangePage`, `onDragStart`, and `onDragEnd` callbacks to list
|
|
|
1061
1366
|
| `alignSelf` | `auto \| FlexAlignType` | No | `-` | - |
|
|
1062
1367
|
| `animated` | `boolean` | No | `-` | - |
|
|
1063
1368
|
| `aspectRatio` | `string \| number` | No | `-` | - |
|
|
1369
|
+
| `autoplay` | `boolean` | No | `-` | Whether autoplay is enabled for the carousel. |
|
|
1370
|
+
| `autoplayInterval` | `number` | No | `3000 (3 seconds)` | The interval in milliseconds for autoplay. |
|
|
1064
1371
|
| `background` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - |
|
|
1065
1372
|
| `borderBottomLeftRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
|
|
1066
1373
|
| `borderBottomRightRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
|
|
@@ -1098,7 +1405,6 @@ You can use the `onChangePage`, `onDragStart`, and `onDragEnd` callbacks to list
|
|
|
1098
1405
|
| `fontSize` | `inherit \| FontSize` | No | `-` | - |
|
|
1099
1406
|
| `fontWeight` | `inherit \| FontWeight` | No | `-` | - |
|
|
1100
1407
|
| `gap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
|
|
1101
|
-
| `goToPageAccessibilityLabel` | `string` | No | `-` | Accessibility label for the go to page button. |
|
|
1102
1408
|
| `height` | `string \| number` | No | `-` | - |
|
|
1103
1409
|
| `hideNavigation` | `boolean` | No | `-` | Hides the navigation arrows (previous/next buttons). |
|
|
1104
1410
|
| `hidePagination` | `boolean` | No | `-` | Hides the pagination indicators (dots/bars showing current page). |
|
|
@@ -1131,7 +1437,8 @@ You can use the `onChangePage`, `onDragStart`, and `onDragEnd` callbacks to list
|
|
|
1131
1437
|
| `paddingTop` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
|
|
1132
1438
|
| `paddingX` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
|
|
1133
1439
|
| `paddingY` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
|
|
1134
|
-
| `paginationAccessibilityLabel` | `string \| ((pageIndex: number) => string)` | No |
|
|
1440
|
+
| `paginationAccessibilityLabel` | `string \| ((pageIndex: number) => string)` | No | `(pageIndex) => `Go to page ${pageIndex + 1}`` | Accessibility label for the go to page button. When a string is provided, it is used as-is for all indicators. When a function is provided, it receives the page index and returns a label. |
|
|
1441
|
+
| `paginationVariant` | `dot \| pill` | No | `'pill'` | Visual variant for the pagination indicators. - pill: All indicators are pill-shaped (default) - dot: Inactive indicators are small dots, active indicator expands to a pill |
|
|
1135
1442
|
| `pin` | `top \| bottom \| left \| right \| all` | No | `-` | Direction in which to absolutely pin the box. |
|
|
1136
1443
|
| `position` | `static \| relative \| fixed \| absolute \| sticky` | No | `-` | - |
|
|
1137
1444
|
| `previousPageAccessibilityLabel` | `string` | No | `-` | Accessibility label for the previous page button. |
|
|
@@ -1139,6 +1446,8 @@ You can use the `onChangePage`, `onDragStart`, and `onDragEnd` callbacks to list
|
|
|
1139
1446
|
| `right` | `string \| number` | No | `-` | - |
|
|
1140
1447
|
| `rowGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
|
|
1141
1448
|
| `snapMode` | `item \| page` | No | `'page'` | Specifies the pagination and navigation strategy for the carousel. item treats each item as a separate page for navigation, pagination, and snapping. page groups items into pages based on visible area for navigation, pagination, and snapping. This affects page calculation, navigation button behavior, and snap targets when dragging. |
|
|
1449
|
+
| `startAutoplayAccessibilityLabel` | `string` | No | `'Play Carousel'` | Accessibility label for starting autoplay. |
|
|
1450
|
+
| `stopAutoplayAccessibilityLabel` | `string` | No | `'Pause Carousel'` | Accessibility label for stopping autoplay. |
|
|
1142
1451
|
| `style` | `((false \| RegisteredStyle<ViewStyle> \| Value \| AnimatedInterpolation<string \| number> \| WithAnimatedObject<ViewStyle> \| WithAnimatedArray<Falsy \| ViewStyle \| RegisteredStyle<ViewStyle> \| RecursiveArray<Falsy \| ViewStyle \| RegisteredStyle<ViewStyle>> \| readonly (Falsy \| ViewStyle \| RegisteredStyle<ViewStyle>)[]>) & (false \| ViewStyle \| RegisteredStyle<ViewStyle> \| RecursiveArray<Falsy \| ViewStyle \| RegisteredStyle<ViewStyle>>)) \| null` | No | `-` | Custom styles for the root element. |
|
|
1143
1452
|
| `styles` | `{ root?: StyleProp<ViewStyle>; title?: StyleProp<TextStyle>; navigation?: StyleProp<ViewStyle>; pagination?: StyleProp<ViewStyle>; carousel?: StyleProp<ViewStyle>; carouselContainer?: StyleProp<ViewStyle>; }` | No | `-` | Custom styles for the component. |
|
|
1144
1453
|
| `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID Used to locate this element in unit and end-to-end tests. |
|