@coinbase/cds-mcp-server 8.48.3 → 8.49.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 +4 -0
- package/mcp-docs/mobile/components/PeriodSelector.txt +61 -149
- package/mcp-docs/mobile/components/SegmentedTabs.txt +103 -33
- package/mcp-docs/mobile/components/SlideButton.txt +20 -0
- package/mcp-docs/mobile/components/TabbedChipsAlpha.txt +3 -1
- package/mcp-docs/mobile/components/Tabs.txt +246 -63
- package/mcp-docs/web/components/PeriodSelector.txt +75 -75
- package/mcp-docs/web/components/SegmentedTabs.txt +72 -34
- package/mcp-docs/web/components/Tabs.txt +195 -29
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file.
|
|
|
8
8
|
|
|
9
9
|
<!-- template-start -->
|
|
10
10
|
|
|
11
|
+
## 8.49.0 ((2/26/2026, 04:03 PM PST))
|
|
12
|
+
|
|
13
|
+
This is an artificial version bump with no new change.
|
|
14
|
+
|
|
11
15
|
## 8.48.3 ((2/25/2026, 08:36 PM PST))
|
|
12
16
|
|
|
13
17
|
This is an artificial version bump with no new change.
|
|
@@ -10,10 +10,12 @@ import { PeriodSelector } from '@coinbase/cds-mobile-visualization'
|
|
|
10
10
|
|
|
11
11
|
## Examples
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
PeriodSelector is a specialized [SegmentedTabs](/components/navigation/SegmentedTabs) optimized for chart time-period selection. It provides a transparent background, primary wash active state, and full-width layout by default.
|
|
14
|
+
|
|
15
|
+
### Basics
|
|
14
16
|
|
|
15
17
|
```jsx
|
|
16
|
-
function
|
|
18
|
+
function Example() {
|
|
17
19
|
const tabs = [
|
|
18
20
|
{ id: '1H', label: '1H' },
|
|
19
21
|
{ id: '1D', label: '1D' },
|
|
@@ -29,12 +31,12 @@ function BasicExample() {
|
|
|
29
31
|
}
|
|
30
32
|
```
|
|
31
33
|
|
|
32
|
-
###
|
|
34
|
+
### Sizing
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
Set `width` to `fit-content` to make the selector only as wide as its content, and use `gap` to control spacing between tabs.
|
|
35
37
|
|
|
36
38
|
```jsx
|
|
37
|
-
function
|
|
39
|
+
function Example() {
|
|
38
40
|
const tabs = [
|
|
39
41
|
{ id: '1W', label: '1W' },
|
|
40
42
|
{ id: '1M', label: '1M' },
|
|
@@ -55,16 +57,49 @@ function MinimumWidthExample() {
|
|
|
55
57
|
}
|
|
56
58
|
```
|
|
57
59
|
|
|
58
|
-
###
|
|
60
|
+
### Live Indicator
|
|
61
|
+
|
|
62
|
+
Use the `LiveTabLabel` component (exported from PeriodSelector) to indicate a live data period. Pair it with a conditional `activeBackground` to visually differentiate the live state.
|
|
63
|
+
|
|
64
|
+
```jsx
|
|
65
|
+
function Example() {
|
|
66
|
+
const tabs = useMemo(
|
|
67
|
+
() => [
|
|
68
|
+
{ id: '1H', label: <LiveTabLabel /> },
|
|
69
|
+
{ id: '1D', label: '1D' },
|
|
70
|
+
{ id: '1W', label: '1W' },
|
|
71
|
+
{ id: '1M', label: '1M' },
|
|
72
|
+
{ id: '1Y', label: '1Y' },
|
|
73
|
+
{ id: 'All', label: 'All' },
|
|
74
|
+
],
|
|
75
|
+
[],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const [activeTab, setActiveTab] = useState(tabs[0]);
|
|
79
|
+
const isLive = useMemo(() => activeTab?.id === '1H', [activeTab]);
|
|
80
|
+
|
|
81
|
+
const activeBackground = useMemo(() => (isLive ? 'bgNegativeWash' : 'bgPrimaryWash'), [isLive]);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<PeriodSelector
|
|
85
|
+
activeBackground={activeBackground}
|
|
86
|
+
activeTab={activeTab}
|
|
87
|
+
onChange={setActiveTab}
|
|
88
|
+
tabs={tabs}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Overflow
|
|
95
|
+
|
|
96
|
+
When there are too many tabs to fit in a single row, wrap the selector in a horizontal `ScrollView` with an optional action button.
|
|
59
97
|
|
|
60
98
|
```jsx
|
|
61
|
-
function
|
|
99
|
+
function Example() {
|
|
62
100
|
const tabs = useMemo(
|
|
63
101
|
() => [
|
|
64
|
-
{
|
|
65
|
-
id: '1H',
|
|
66
|
-
label: <LiveTabLabel />,
|
|
67
|
-
},
|
|
102
|
+
{ id: '1H', label: <LiveTabLabel /> },
|
|
68
103
|
{ id: '1D', label: '1D' },
|
|
69
104
|
{ id: '1W', label: '1W' },
|
|
70
105
|
{ id: '1M', label: '1M' },
|
|
@@ -110,149 +145,17 @@ function ManyPeriodsExample() {
|
|
|
110
145
|
}
|
|
111
146
|
```
|
|
112
147
|
|
|
113
|
-
### Live Indicator
|
|
114
|
-
|
|
115
|
-
```jsx
|
|
116
|
-
function LiveExample() {
|
|
117
|
-
const tabs = useMemo(
|
|
118
|
-
() => [
|
|
119
|
-
// LiveTabLabel is exported from PeriodSelector
|
|
120
|
-
{ id: '1H', label: <LiveTabLabel /> },
|
|
121
|
-
{ id: '1D', label: '1D' },
|
|
122
|
-
{ id: '1W', label: '1W' },
|
|
123
|
-
{ id: '1M', label: '1M' },
|
|
124
|
-
{ id: '1Y', label: '1Y' },
|
|
125
|
-
{ id: 'All', label: 'All' },
|
|
126
|
-
],
|
|
127
|
-
[],
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
const [activeTab, setActiveTab] = useState(tabs[0]);
|
|
131
|
-
const isLive = useMemo(() => activeTab?.id === '1H', [activeTab]);
|
|
132
|
-
|
|
133
|
-
const activeBackground = useMemo(() => (isLive ? 'bgNegativeWash' : 'bgPrimaryWash'), [isLive]);
|
|
134
|
-
|
|
135
|
-
return (
|
|
136
|
-
<PeriodSelector
|
|
137
|
-
activeBackground={activeBackground}
|
|
138
|
-
activeTab={activeTab}
|
|
139
|
-
onChange={setActiveTab}
|
|
140
|
-
tabs={tabs}
|
|
141
|
-
/>
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
148
|
### Customization
|
|
147
149
|
|
|
148
150
|
#### Custom Colors
|
|
149
151
|
|
|
150
|
-
|
|
151
|
-
function ColoredBTCExample() {
|
|
152
|
-
const btcColor = assets.btc.color;
|
|
153
|
-
|
|
154
|
-
// Custom active indicator with BTC color
|
|
155
|
-
const BTCActiveIndicator = memo((props) => {
|
|
156
|
-
const theme = useTheme();
|
|
157
|
-
const { activeTab } = useTabsContext();
|
|
158
|
-
const isLive = useMemo(() => activeTab?.id === '1H', [activeTab]);
|
|
159
|
-
|
|
160
|
-
const backgroundColor = useMemo(
|
|
161
|
-
() => (isLive ? theme.color.bgNegativeWash : `${btcColor}1A`),
|
|
162
|
-
[isLive, theme.color.bgNegativeWash],
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
return <PeriodSelectorActiveIndicator {...props} background={backgroundColor} />;
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// Custom tab component with BTC color
|
|
169
|
-
const BTCTab = memo(
|
|
170
|
-
forwardRef(({ label, ...props }, ref) => {
|
|
171
|
-
const { activeTab } = useTabsContext();
|
|
172
|
-
const isActive = activeTab?.id === props.id;
|
|
173
|
-
const theme = useTheme();
|
|
174
|
-
|
|
175
|
-
const wrappedLabel =
|
|
176
|
-
typeof label === 'string' ? (
|
|
177
|
-
<Text font="label1" dangerouslySetColor={isActive ? btcColor : theme.color.fg}>
|
|
178
|
-
{label}
|
|
179
|
-
</Text>
|
|
180
|
-
) : (
|
|
181
|
-
label
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
return <SegmentedTab ref={ref} label={wrappedLabel} {...props} />;
|
|
185
|
-
}),
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
// Custom live label with BTC color
|
|
189
|
-
const BTCLiveLabel = memo(
|
|
190
|
-
forwardRef(({ label = 'LIVE', font = 'label1', hideDot, style, ...props }, ref) => {
|
|
191
|
-
const theme = useTheme();
|
|
192
|
-
|
|
193
|
-
const dotStyle = useMemo(
|
|
194
|
-
() => ({
|
|
195
|
-
width: theme.space[1],
|
|
196
|
-
height: theme.space[1],
|
|
197
|
-
borderRadius: 1000,
|
|
198
|
-
marginRight: theme.space[0.75],
|
|
199
|
-
backgroundColor: btcColor,
|
|
200
|
-
}),
|
|
201
|
-
[theme.space],
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
return (
|
|
205
|
-
<View
|
|
206
|
-
ref={ref}
|
|
207
|
-
style={[
|
|
208
|
-
{
|
|
209
|
-
flexDirection: 'row',
|
|
210
|
-
alignItems: 'center',
|
|
211
|
-
},
|
|
212
|
-
style,
|
|
213
|
-
]}
|
|
214
|
-
>
|
|
215
|
-
{!hideDot && <View style={dotStyle} />}
|
|
216
|
-
<Text font={font} style={{ color: btcColor }} {...props}>
|
|
217
|
-
{label}
|
|
218
|
-
</Text>
|
|
219
|
-
</View>
|
|
220
|
-
);
|
|
221
|
-
}),
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
const tabs = [
|
|
225
|
-
{ id: '1H', label: <BTCLiveLabel /> },
|
|
226
|
-
{ id: '1D', label: '1D' },
|
|
227
|
-
{ id: '1W', label: '1W' },
|
|
228
|
-
{ id: '1M', label: '1M' },
|
|
229
|
-
{ id: '1Y', label: '1Y' },
|
|
230
|
-
{ id: 'All', label: 'All' },
|
|
231
|
-
];
|
|
232
|
-
const [activeTab, setActiveTab] = useState(tabs[0]);
|
|
233
|
-
|
|
234
|
-
return (
|
|
235
|
-
<PeriodSelector
|
|
236
|
-
TabComponent={BTCTab}
|
|
237
|
-
TabsActiveIndicatorComponent={BTCActiveIndicator}
|
|
238
|
-
activeTab={activeTab}
|
|
239
|
-
onChange={setActiveTab}
|
|
240
|
-
tabs={tabs}
|
|
241
|
-
/>
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
#### Custom Colors Excluding Live
|
|
247
|
-
|
|
248
|
-
Customize colors for regular periods while keeping the default red styling for live periods.
|
|
152
|
+
Use `TabComponent` and `TabsActiveIndicatorComponent` to fully brand the selector. This example applies a custom asset color to both the active indicator background and the tab text, while keeping the default red styling for live periods.
|
|
249
153
|
|
|
250
154
|
```jsx
|
|
251
|
-
function
|
|
155
|
+
function Example() {
|
|
252
156
|
const btcColor = assets.btc.color;
|
|
253
157
|
|
|
254
|
-
|
|
255
|
-
const BTCActiveExcludingLiveIndicator = memo((props) => {
|
|
158
|
+
const BTCActiveIndicator = memo((props) => {
|
|
256
159
|
const theme = useTheme();
|
|
257
160
|
const { activeTab } = useTabsContext();
|
|
258
161
|
const isLive = useMemo(() => activeTab?.id === '1H', [activeTab]);
|
|
@@ -265,7 +168,6 @@ function ColoredExcludingLiveExample() {
|
|
|
265
168
|
return <PeriodSelectorActiveIndicator {...props} background={backgroundColor} />;
|
|
266
169
|
});
|
|
267
170
|
|
|
268
|
-
// Custom tab component with BTC color
|
|
269
171
|
const BTCTab = memo(
|
|
270
172
|
forwardRef(({ label, ...props }, ref) => {
|
|
271
173
|
const { activeTab } = useTabsContext();
|
|
@@ -293,12 +195,12 @@ function ColoredExcludingLiveExample() {
|
|
|
293
195
|
{ id: '1Y', label: '1Y' },
|
|
294
196
|
{ id: 'All', label: 'All' },
|
|
295
197
|
];
|
|
296
|
-
const [activeTab, setActiveTab] = useState(tabs[
|
|
198
|
+
const [activeTab, setActiveTab] = useState(tabs[1]);
|
|
297
199
|
|
|
298
200
|
return (
|
|
299
201
|
<PeriodSelector
|
|
300
202
|
TabComponent={BTCTab}
|
|
301
|
-
TabsActiveIndicatorComponent={
|
|
203
|
+
TabsActiveIndicatorComponent={BTCActiveIndicator}
|
|
302
204
|
activeTab={activeTab}
|
|
303
205
|
onChange={setActiveTab}
|
|
304
206
|
tabs={tabs}
|
|
@@ -402,6 +304,7 @@ function ColoredExcludingLiveExample() {
|
|
|
402
304
|
| `right` | `string \| number` | No | `-` | - |
|
|
403
305
|
| `rowGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
|
|
404
306
|
| `style` | `false \| RegisteredStyle<ViewStyle> \| WithAnimatedObject<ViewStyle> \| Value \| AnimatedInterpolation<string \| number> \| WithAnimatedArray<ViewStyle \| Falsy \| RegisteredStyle<ViewStyle> \| RecursiveArray<ViewStyle \| Falsy \| RegisteredStyle<ViewStyle>> \| readonly (ViewStyle \| Falsy \| RegisteredStyle<ViewStyle>)[]> \| null` | No | `-` | - |
|
|
307
|
+
| `styles` | `{ root?: StyleProp<ViewStyle>; tab?: StyleProp<ViewStyle>; activeIndicator?: StyleProp<ViewStyle>; }` | No | `-` | Custom styles for individual elements of the SegmentedTabs component |
|
|
405
308
|
| `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Used to locate this view in end-to-end tests. |
|
|
406
309
|
| `textAlign` | `left \| right \| auto \| center \| justify` | No | `-` | - |
|
|
407
310
|
| `textDecorationLine` | `none \| underline \| line-through \| underline line-through` | No | `-` | - |
|
|
@@ -414,3 +317,12 @@ function ColoredExcludingLiveExample() {
|
|
|
414
317
|
| `zIndex` | `number` | No | `-` | - |
|
|
415
318
|
|
|
416
319
|
|
|
320
|
+
## Styles
|
|
321
|
+
|
|
322
|
+
| Selector | Static class name | Description |
|
|
323
|
+
| --- | --- | --- |
|
|
324
|
+
| `root` | `-` | Root container element |
|
|
325
|
+
| `tab` | `-` | Tab element |
|
|
326
|
+
| `activeIndicator` | `-` | Active indicator element |
|
|
327
|
+
|
|
328
|
+
|
|
@@ -10,9 +10,13 @@ import { SegmentedTabs } from '@coinbase/cds-mobile/tabs/SegmentedTabs'
|
|
|
10
10
|
|
|
11
11
|
## Examples
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
SegmentedTabs is a controlled component that uses `activeTab` and `onChange` to manage selection state.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
### Basics
|
|
16
|
+
|
|
17
|
+
#### Initial Value
|
|
18
|
+
|
|
19
|
+
You can set any tab as the initial active tab by passing it to the `activeTab` state.
|
|
16
20
|
|
|
17
21
|
```jsx
|
|
18
22
|
function Example() {
|
|
@@ -34,9 +38,7 @@ function Example() {
|
|
|
34
38
|
}
|
|
35
39
|
```
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
You can set any tab as the initial active tab.
|
|
41
|
+
SegmentedTabs can also start with no active selection by passing `null`.
|
|
40
42
|
|
|
41
43
|
```jsx
|
|
42
44
|
function Example() {
|
|
@@ -45,7 +47,7 @@ function Example() {
|
|
|
45
47
|
{ id: 'sell', label: 'Sell' },
|
|
46
48
|
{ id: 'convert', label: 'Convert' },
|
|
47
49
|
];
|
|
48
|
-
const [activeTab, updateActiveTab] = useState(
|
|
50
|
+
const [activeTab, updateActiveTab] = useState(null);
|
|
49
51
|
const handleChange = useCallback((activeTab) => updateActiveTab(activeTab), []);
|
|
50
52
|
return (
|
|
51
53
|
<SegmentedTabs
|
|
@@ -58,9 +60,9 @@ function Example() {
|
|
|
58
60
|
}
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
#### Disabled
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
The entire component can be disabled with the `disabled` prop.
|
|
64
66
|
|
|
65
67
|
```jsx
|
|
66
68
|
function Example() {
|
|
@@ -69,12 +71,13 @@ function Example() {
|
|
|
69
71
|
{ id: 'sell', label: 'Sell' },
|
|
70
72
|
{ id: 'convert', label: 'Convert' },
|
|
71
73
|
];
|
|
72
|
-
const [activeTab, updateActiveTab] = useState(
|
|
74
|
+
const [activeTab, updateActiveTab] = useState(tabs[0]);
|
|
73
75
|
const handleChange = useCallback((activeTab) => updateActiveTab(activeTab), []);
|
|
74
76
|
return (
|
|
75
77
|
<SegmentedTabs
|
|
76
78
|
accessibilityLabel="Switch token action views"
|
|
77
79
|
activeTab={activeTab}
|
|
80
|
+
disabled
|
|
78
81
|
onChange={handleChange}
|
|
79
82
|
tabs={tabs}
|
|
80
83
|
/>
|
|
@@ -82,15 +85,13 @@ function Example() {
|
|
|
82
85
|
}
|
|
83
86
|
```
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
You can disable the entire SegmentedTabs component.
|
|
88
|
+
Individual tabs can also be disabled while keeping others interactive.
|
|
88
89
|
|
|
89
90
|
```jsx
|
|
90
91
|
function Example() {
|
|
91
92
|
const tabs = [
|
|
92
93
|
{ id: 'buy', label: 'Buy' },
|
|
93
|
-
{ id: 'sell', label: 'Sell' },
|
|
94
|
+
{ id: 'sell', label: 'Sell', disabled: true },
|
|
94
95
|
{ id: 'convert', label: 'Convert' },
|
|
95
96
|
];
|
|
96
97
|
const [activeTab, updateActiveTab] = useState(tabs[0]);
|
|
@@ -99,7 +100,6 @@ function Example() {
|
|
|
99
100
|
<SegmentedTabs
|
|
100
101
|
accessibilityLabel="Switch token action views"
|
|
101
102
|
activeTab={activeTab}
|
|
102
|
-
disabled
|
|
103
103
|
onChange={handleChange}
|
|
104
104
|
tabs={tabs}
|
|
105
105
|
/>
|
|
@@ -107,15 +107,17 @@ function Example() {
|
|
|
107
107
|
}
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
###
|
|
110
|
+
### Styling
|
|
111
|
+
|
|
112
|
+
#### Border Radius
|
|
111
113
|
|
|
112
|
-
|
|
114
|
+
Set `borderRadius` to change the shape of both the container and the active indicator.
|
|
113
115
|
|
|
114
116
|
```jsx
|
|
115
117
|
function Example() {
|
|
116
118
|
const tabs = [
|
|
117
119
|
{ id: 'buy', label: 'Buy' },
|
|
118
|
-
{ id: 'sell', label: 'Sell'
|
|
120
|
+
{ id: 'sell', label: 'Sell' },
|
|
119
121
|
{ id: 'convert', label: 'Convert' },
|
|
120
122
|
];
|
|
121
123
|
const [activeTab, updateActiveTab] = useState(tabs[0]);
|
|
@@ -124,6 +126,7 @@ function Example() {
|
|
|
124
126
|
<SegmentedTabs
|
|
125
127
|
accessibilityLabel="Switch token action views"
|
|
126
128
|
activeTab={activeTab}
|
|
129
|
+
borderRadius={300}
|
|
127
130
|
onChange={handleChange}
|
|
128
131
|
tabs={tabs}
|
|
129
132
|
/>
|
|
@@ -131,26 +134,76 @@ function Example() {
|
|
|
131
134
|
}
|
|
132
135
|
```
|
|
133
136
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
You can customize individual tabs by providing a custom `Component` for specific tabs.
|
|
137
|
+
Use the `styles` prop to set a different `borderRadius` on the active indicator than the outer container, with `padding` to create an inset effect.
|
|
137
138
|
|
|
138
139
|
```jsx
|
|
139
140
|
function Example() {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
const theme = useTheme();
|
|
142
|
+
const tabs = [
|
|
143
|
+
{ id: 'buy', label: 'Buy' },
|
|
144
|
+
{ id: 'sell', label: 'Sell' },
|
|
145
|
+
{ id: 'convert', label: 'Convert' },
|
|
146
|
+
];
|
|
147
|
+
const [activeTab, updateActiveTab] = useState(tabs[0]);
|
|
148
|
+
const handleChange = useCallback((activeTab) => updateActiveTab(activeTab), []);
|
|
149
|
+
return (
|
|
150
|
+
<SegmentedTabs
|
|
151
|
+
accessibilityLabel="Switch token action views"
|
|
152
|
+
activeTab={activeTab}
|
|
153
|
+
borderRadius={300}
|
|
154
|
+
onChange={handleChange}
|
|
155
|
+
padding={0.5}
|
|
156
|
+
styles={{
|
|
157
|
+
activeIndicator: { borderRadius: theme.borderRadius[200] },
|
|
158
|
+
}}
|
|
159
|
+
tabs={tabs}
|
|
160
|
+
/>
|
|
143
161
|
);
|
|
162
|
+
}
|
|
163
|
+
```
|
|
144
164
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
165
|
+
#### Labels
|
|
166
|
+
|
|
167
|
+
Labels can be customized with any `ReactNode`, including instances of `Icon`.
|
|
168
|
+
|
|
169
|
+
```jsx
|
|
170
|
+
import {
|
|
171
|
+
interpolateColor,
|
|
172
|
+
runOnJS,
|
|
173
|
+
useAnimatedReaction,
|
|
174
|
+
useSharedValue,
|
|
175
|
+
withSpring,
|
|
176
|
+
} from 'react-native-reanimated';
|
|
177
|
+
import { tabsSpringConfig } from '@coinbase/cds-mobile/tabs/Tabs';
|
|
178
|
+
|
|
179
|
+
function ColoredIcon({ tabId, name }) {
|
|
180
|
+
const { activeTab } = useTabsContext();
|
|
181
|
+
const isActive = activeTab?.id === tabId;
|
|
182
|
+
const theme = useTheme();
|
|
183
|
+
|
|
184
|
+
const progress = useSharedValue(isActive ? 1 : 0);
|
|
185
|
+
const [color, setColor] = useState(isActive ? theme.color.fgInverse : theme.color.fg);
|
|
186
|
+
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
progress.value = withSpring(isActive ? 1 : 0, tabsSpringConfig);
|
|
189
|
+
}, [isActive, progress]);
|
|
190
|
+
|
|
191
|
+
useAnimatedReaction(
|
|
192
|
+
() => interpolateColor(progress.value, [0, 1], [theme.color.fg, theme.color.fgInverse]),
|
|
193
|
+
(newColor) => {
|
|
194
|
+
runOnJS(setColor)(newColor);
|
|
195
|
+
},
|
|
148
196
|
);
|
|
149
197
|
|
|
198
|
+
return <Icon active name={name} size="s" styles={{ icon: { color } }} />;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function Example() {
|
|
202
|
+
const theme = useTheme();
|
|
150
203
|
const tabs = [
|
|
151
|
-
{ id: 'buy', label:
|
|
152
|
-
{ id: 'sell', label:
|
|
153
|
-
{ id: 'convert', label:
|
|
204
|
+
{ id: 'buy', label: <ColoredIcon name="chartLine" tabId="buy" /> },
|
|
205
|
+
{ id: 'sell', label: <ColoredIcon name="chartCandles" tabId="sell" /> },
|
|
206
|
+
{ id: 'convert', label: <ColoredIcon name="chartBar" tabId="convert" /> },
|
|
154
207
|
];
|
|
155
208
|
const [activeTab, updateActiveTab] = useState(tabs[0]);
|
|
156
209
|
const handleChange = useCallback((activeTab) => updateActiveTab(activeTab), []);
|
|
@@ -158,16 +211,23 @@ function Example() {
|
|
|
158
211
|
<SegmentedTabs
|
|
159
212
|
accessibilityLabel="Switch token action views"
|
|
160
213
|
activeTab={activeTab}
|
|
214
|
+
borderRadius={300}
|
|
215
|
+
gap={0.5}
|
|
161
216
|
onChange={handleChange}
|
|
217
|
+
padding={0.5}
|
|
218
|
+
styles={{
|
|
219
|
+
activeIndicator: { borderRadius: theme.borderRadius[200] },
|
|
220
|
+
}}
|
|
162
221
|
tabs={tabs}
|
|
222
|
+
width="fit-content"
|
|
163
223
|
/>
|
|
164
224
|
);
|
|
165
225
|
}
|
|
166
226
|
```
|
|
167
227
|
|
|
168
|
-
### Custom
|
|
228
|
+
### Custom Components
|
|
169
229
|
|
|
170
|
-
|
|
230
|
+
Use `TabComponent` and `TabsActiveIndicatorComponent` to fully customize the tabs and active indicator. Individual tabs can also be customized by providing a `Component` in the tab definition.
|
|
171
231
|
|
|
172
232
|
```jsx
|
|
173
233
|
function Example() {
|
|
@@ -175,7 +235,7 @@ function Example() {
|
|
|
175
235
|
<TabsActiveIndicator activeTabRect={activeTabRect} background="bgOverlay" />
|
|
176
236
|
);
|
|
177
237
|
|
|
178
|
-
const
|
|
238
|
+
const CustomTab = ({ id, label, disabled }) => {
|
|
179
239
|
const { activeTab } = useTabsContext();
|
|
180
240
|
const isActive = activeTab?.id === id;
|
|
181
241
|
const renderedLabel = (
|
|
@@ -201,7 +261,7 @@ function Example() {
|
|
|
201
261
|
borderRadius={0}
|
|
202
262
|
onChange={handleChange}
|
|
203
263
|
tabs={tabs}
|
|
204
|
-
TabComponent={
|
|
264
|
+
TabComponent={CustomTab}
|
|
205
265
|
TabsActiveIndicatorComponent={CustomActiveIndicator}
|
|
206
266
|
/>
|
|
207
267
|
);
|
|
@@ -302,6 +362,7 @@ function Example() {
|
|
|
302
362
|
| `right` | `string \| number` | No | `-` | - |
|
|
303
363
|
| `rowGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
|
|
304
364
|
| `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>)[]> \| null` | No | `-` | - |
|
|
365
|
+
| `styles` | `{ root?: StyleProp<ViewStyle>; tab?: StyleProp<ViewStyle>; activeIndicator?: StyleProp<ViewStyle>; }` | No | `-` | Custom styles for individual elements of the SegmentedTabs component |
|
|
305
366
|
| `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Used to locate this view in end-to-end tests. |
|
|
306
367
|
| `textAlign` | `left \| right \| auto \| center \| justify` | No | `-` | - |
|
|
307
368
|
| `textDecorationLine` | `none \| underline \| line-through \| underline line-through` | No | `-` | - |
|
|
@@ -314,3 +375,12 @@ function Example() {
|
|
|
314
375
|
| `zIndex` | `number` | No | `-` | - |
|
|
315
376
|
|
|
316
377
|
|
|
378
|
+
## Styles
|
|
379
|
+
|
|
380
|
+
| Selector | Static class name | Description |
|
|
381
|
+
| --- | --- | --- |
|
|
382
|
+
| `root` | `-` | Root container element |
|
|
383
|
+
| `tab` | `-` | Tab element |
|
|
384
|
+
| `activeIndicator` | `-` | Active indicator element |
|
|
385
|
+
|
|
386
|
+
|
|
@@ -226,6 +226,26 @@ function Example() {
|
|
|
226
226
|
}
|
|
227
227
|
```
|
|
228
228
|
|
|
229
|
+
### Custom Border Radius
|
|
230
|
+
|
|
231
|
+
Use `borderRadius` to customize the shape of both the handle and the background. Token values are resolved through the theme, so the handle and background always match.
|
|
232
|
+
|
|
233
|
+
```jsx
|
|
234
|
+
function Example() {
|
|
235
|
+
const [checked, setChecked] = useState(false);
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<SlideButton
|
|
239
|
+
checked={checked}
|
|
240
|
+
onChange={setChecked}
|
|
241
|
+
uncheckedLabel="Swipe to confirm"
|
|
242
|
+
checkedLabel="Confirming..."
|
|
243
|
+
borderRadius={200}
|
|
244
|
+
/>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
229
249
|
### Custom Background and Handle Components
|
|
230
250
|
|
|
231
251
|
You can fully customize the background and handle by providing your own components via `SlideButtonBackgroundComponent` and `SlideButtonHandleComponent`. Your components receive typed props (`SlideButtonBackgroundProps` and `SlideButtonHandleProps`) including a `progress` spring value and the current `checked` state.
|
|
@@ -197,7 +197,7 @@ render(<Example />);
|
|
|
197
197
|
| `disabled` | `boolean` | No | `-` | Disable interactions on all the tabs. |
|
|
198
198
|
| `gap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `1` | The spacing between Tabs |
|
|
199
199
|
| `ref` | `null \| (instance: View \| null) => void \| MutableRefObject<View \| null>` | No | `-` | - |
|
|
200
|
-
| `styles` | `{ root?: StyleProp<ViewStyle>; tabs?: StyleProp<ViewStyle>; }` | No | `-` |
|
|
200
|
+
| `styles` | `({ root?: StyleProp<ViewStyle>; tab?: StyleProp<ViewStyle>; activeIndicator?: StyleProp<ViewStyle>; } & { root?: StyleProp<ViewStyle>; tabs?: StyleProp<ViewStyle>; })` | No | `-` | Custom styles for individual elements of the Tabs component |
|
|
201
201
|
| `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 |
|
|
202
202
|
| `width` | `string \| number` | No | `-` | The width of the scroll container, defaults to 100% of the parent container If the tabs are wider than the width of the container, paddles will be shown to scroll the tabs. |
|
|
203
203
|
|
|
@@ -207,6 +207,8 @@ render(<Example />);
|
|
|
207
207
|
| Selector | Static class name | Description |
|
|
208
208
|
| --- | --- | --- |
|
|
209
209
|
| `root` | `-` | Root container element |
|
|
210
|
+
| `tab` | `-` | Tab element |
|
|
211
|
+
| `activeIndicator` | `-` | Active indicator element |
|
|
210
212
|
| `tabs` | `-` | Tabs root element |
|
|
211
213
|
|
|
212
214
|
|