@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
|
@@ -10,9 +10,13 @@ import { SegmentedTabs } from '@coinbase/cds-web/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 live
|
|
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 live
|
|
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 live
|
|
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 live
|
|
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 live
|
|
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,43 @@ 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 live
|
|
139
140
|
function Example() {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
const tabs = [
|
|
142
|
+
{ id: 'buy', label: 'Buy' },
|
|
143
|
+
{ id: 'sell', label: 'Sell' },
|
|
144
|
+
{ id: 'convert', label: 'Convert' },
|
|
145
|
+
];
|
|
146
|
+
const [activeTab, updateActiveTab] = useState(tabs[0]);
|
|
147
|
+
const handleChange = useCallback((activeTab) => updateActiveTab(activeTab), []);
|
|
148
|
+
return (
|
|
149
|
+
<SegmentedTabs
|
|
150
|
+
accessibilityLabel="Switch token action views"
|
|
151
|
+
activeTab={activeTab}
|
|
152
|
+
borderRadius={300}
|
|
153
|
+
onChange={handleChange}
|
|
154
|
+
padding={0.5}
|
|
155
|
+
styles={{
|
|
156
|
+
activeIndicator: { borderRadius: 'var(--borderRadius-200)' },
|
|
157
|
+
}}
|
|
158
|
+
tabs={tabs}
|
|
159
|
+
/>
|
|
143
160
|
);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
144
163
|
|
|
145
|
-
|
|
146
|
-
(props) => <SegmentedTab {...props} font="label2" />,
|
|
147
|
-
[],
|
|
148
|
-
);
|
|
164
|
+
#### Labels
|
|
149
165
|
|
|
166
|
+
Labels can be customized with any `ReactNode`, including instances of `Icon`.
|
|
167
|
+
|
|
168
|
+
```jsx live
|
|
169
|
+
function Example() {
|
|
150
170
|
const tabs = [
|
|
151
|
-
{ id: 'buy', label:
|
|
152
|
-
{ id: 'sell', label:
|
|
153
|
-
{ id: 'convert', label:
|
|
171
|
+
{ id: 'buy', label: <Icon name="chartLine" size="s" color="currentColor" active /> },
|
|
172
|
+
{ id: 'sell', label: <Icon name="chartCandles" size="s" color="currentColor" active /> },
|
|
173
|
+
{ id: 'convert', label: <Icon name="chartBar" size="s" color="currentColor" active /> },
|
|
154
174
|
];
|
|
155
175
|
const [activeTab, updateActiveTab] = useState(tabs[0]);
|
|
156
176
|
const handleChange = useCallback((activeTab) => updateActiveTab(activeTab), []);
|
|
@@ -158,16 +178,23 @@ function Example() {
|
|
|
158
178
|
<SegmentedTabs
|
|
159
179
|
accessibilityLabel="Switch token action views"
|
|
160
180
|
activeTab={activeTab}
|
|
181
|
+
borderRadius={300}
|
|
182
|
+
gap={0.5}
|
|
161
183
|
onChange={handleChange}
|
|
184
|
+
padding={0.5}
|
|
185
|
+
styles={{
|
|
186
|
+
activeIndicator: { borderRadius: 'var(--borderRadius-200)' },
|
|
187
|
+
}}
|
|
162
188
|
tabs={tabs}
|
|
189
|
+
width="fit-content"
|
|
163
190
|
/>
|
|
164
191
|
);
|
|
165
192
|
}
|
|
166
193
|
```
|
|
167
194
|
|
|
168
|
-
### Custom
|
|
195
|
+
### Custom Components
|
|
169
196
|
|
|
170
|
-
|
|
197
|
+
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
198
|
|
|
172
199
|
```jsx live
|
|
173
200
|
function Example() {
|
|
@@ -178,7 +205,7 @@ function Example() {
|
|
|
178
205
|
[],
|
|
179
206
|
);
|
|
180
207
|
|
|
181
|
-
const
|
|
208
|
+
const CustomTab = useCallback(({ id, label, disabled }) => {
|
|
182
209
|
const { activeTab } = useTabsContext();
|
|
183
210
|
const isActive = activeTab?.id === id;
|
|
184
211
|
const renderedLabel = (
|
|
@@ -206,7 +233,7 @@ function Example() {
|
|
|
206
233
|
borderRadius={0}
|
|
207
234
|
onChange={handleChange}
|
|
208
235
|
tabs={tabs}
|
|
209
|
-
TabComponent={
|
|
236
|
+
TabComponent={CustomTab}
|
|
210
237
|
TabsActiveIndicatorComponent={CustomActiveIndicator}
|
|
211
238
|
/>
|
|
212
239
|
);
|
|
@@ -248,6 +275,7 @@ function Example() {
|
|
|
248
275
|
| `borderedTop` | `boolean` | No | `-` | Add a border to the top side of the box. |
|
|
249
276
|
| `borderedVertical` | `boolean` | No | `-` | Add a border to the top and bottom sides of the box. |
|
|
250
277
|
| `bottom` | `ResponsiveProp<Bottom<string \| number>>` | No | `-` | - |
|
|
278
|
+
| `classNames` | `{ root?: string; tab?: string \| undefined; activeIndicator?: string \| undefined; } \| undefined` | No | `-` | Custom class names for individual elements of the SegmentedTabs component |
|
|
251
279
|
| `color` | `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 | `-` | - |
|
|
252
280
|
| `columnGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
|
|
253
281
|
| `dangerouslySetBackground` | `string` | No | `-` | - |
|
|
@@ -311,6 +339,7 @@ function Example() {
|
|
|
311
339
|
| `right` | `ResponsiveProp<Right<string \| number>>` | No | `-` | - |
|
|
312
340
|
| `rowGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
|
|
313
341
|
| `style` | `CSSProperties` | No | `-` | - |
|
|
342
|
+
| `styles` | `{ root?: CSSProperties; tab?: CSSProperties \| undefined; activeIndicator?: CSSProperties \| undefined; } \| undefined` | No | `-` | Custom styles for individual elements of the SegmentedTabs component |
|
|
314
343
|
| `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 |
|
|
315
344
|
| `textAlign` | `ResponsiveProp<center \| start \| end \| justify>` | No | `-` | - |
|
|
316
345
|
| `textDecoration` | `ResponsiveProp<none \| underline \| overline \| line-through \| underline overline \| underline double>` | No | `-` | - |
|
|
@@ -323,3 +352,12 @@ function Example() {
|
|
|
323
352
|
| `zIndex` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
|
|
324
353
|
|
|
325
354
|
|
|
355
|
+
## Styles
|
|
356
|
+
|
|
357
|
+
| Selector | Static class name | Description |
|
|
358
|
+
| --- | --- | --- |
|
|
359
|
+
| `root` | `-` | Root element |
|
|
360
|
+
| `tab` | `-` | Tab element |
|
|
361
|
+
| `activeIndicator` | `-` | Active indicator element |
|
|
362
|
+
|
|
363
|
+
|
|
@@ -10,22 +10,28 @@ import { Tabs } from '@coinbase/cds-web/tabs/Tabs'
|
|
|
10
10
|
|
|
11
11
|
## Examples
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Tabs is a low-level primitive for building custom tab interfaces. It requires a `TabComponent` and `TabsActiveIndicatorComponent` to render. For a ready-to-use tab experience, see [SegmentedTabs](/components/navigation/SegmentedTabs).
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
### Basics
|
|
16
|
+
|
|
17
|
+
#### Initial Value
|
|
18
|
+
|
|
19
|
+
Use `useTabsContext` inside your `TabComponent` to access the active tab state. Pair with [TabLabel](/components/navigation/TabLabel) for consistent label styling and [TabsActiveIndicator](/components/navigation/TabIndicator) for the animated indicator.
|
|
20
|
+
|
|
21
|
+
```jsx live
|
|
22
|
+
function Example() {
|
|
17
23
|
const tabs = [
|
|
18
24
|
{ id: 'tab1', label: 'Tab 1' },
|
|
19
25
|
{ id: 'tab2', label: 'Tab 2' },
|
|
20
26
|
{ id: 'tab3', label: 'Tab 3' },
|
|
21
27
|
];
|
|
22
|
-
|
|
23
|
-
const TabComponent = ({ id, label, disabled, ...props }) => {
|
|
24
|
-
const
|
|
25
|
-
const isActive =
|
|
28
|
+
|
|
29
|
+
const TabComponent = useCallback(({ id, label, disabled, ...props }) => {
|
|
30
|
+
const { activeTab, updateActiveTab } = useTabsContext();
|
|
31
|
+
const isActive = activeTab?.id === id;
|
|
26
32
|
return (
|
|
27
33
|
<Pressable
|
|
28
|
-
onClick={() =>
|
|
34
|
+
onClick={() => updateActiveTab(id)}
|
|
29
35
|
disabled={disabled}
|
|
30
36
|
aria-pressed={isActive}
|
|
31
37
|
{...props}
|
|
@@ -35,12 +41,13 @@ import { Tabs } from '@coinbase/cds-web/tabs/Tabs'
|
|
|
35
41
|
</TabLabel>
|
|
36
42
|
</Pressable>
|
|
37
43
|
);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const CustomTabsActiveIndicator = useCallback((props) => {
|
|
41
|
-
return <TabsActiveIndicator {...props} background="bgPrimary" bottom={0} height={2} />;
|
|
42
44
|
}, []);
|
|
43
45
|
|
|
46
|
+
const ActiveIndicator = useCallback(
|
|
47
|
+
(props) => <TabsActiveIndicator {...props} background="bgPrimary" bottom={0} height={2} />,
|
|
48
|
+
[],
|
|
49
|
+
);
|
|
50
|
+
|
|
44
51
|
const [activeTab, setActiveTab] = useState(tabs[0]);
|
|
45
52
|
return (
|
|
46
53
|
<Tabs
|
|
@@ -49,28 +56,71 @@ import { Tabs } from '@coinbase/cds-web/tabs/Tabs'
|
|
|
49
56
|
activeTab={activeTab}
|
|
50
57
|
onChange={setActiveTab}
|
|
51
58
|
TabComponent={TabComponent}
|
|
52
|
-
TabsActiveIndicatorComponent={
|
|
53
|
-
activeBackground="bgPrimary"
|
|
59
|
+
TabsActiveIndicatorComponent={ActiveIndicator}
|
|
54
60
|
/>
|
|
55
61
|
);
|
|
56
|
-
}
|
|
62
|
+
}
|
|
57
63
|
```
|
|
58
64
|
|
|
59
|
-
|
|
65
|
+
Tabs can also start with no active selection by passing `null`.
|
|
60
66
|
|
|
61
|
-
```
|
|
62
|
-
()
|
|
67
|
+
```jsx live
|
|
68
|
+
function Example() {
|
|
63
69
|
const tabs = [
|
|
64
|
-
{ id: '
|
|
65
|
-
{ id: '
|
|
66
|
-
{ id: '
|
|
70
|
+
{ id: 'tab1', label: 'Tab 1' },
|
|
71
|
+
{ id: 'tab2', label: 'Tab 2' },
|
|
72
|
+
{ id: 'tab3', label: 'Tab 3' },
|
|
67
73
|
];
|
|
68
74
|
|
|
69
|
-
const
|
|
70
|
-
|
|
75
|
+
const TabComponent = useCallback(({ id, label, disabled, ...props }) => {
|
|
76
|
+
const { activeTab, updateActiveTab } = useTabsContext();
|
|
77
|
+
const isActive = activeTab?.id === id;
|
|
78
|
+
return (
|
|
79
|
+
<Pressable
|
|
80
|
+
onClick={() => updateActiveTab(id)}
|
|
81
|
+
disabled={disabled}
|
|
82
|
+
aria-pressed={isActive}
|
|
83
|
+
{...props}
|
|
84
|
+
>
|
|
85
|
+
<TabLabel id={id} active={isActive}>
|
|
86
|
+
{label}
|
|
87
|
+
</TabLabel>
|
|
88
|
+
</Pressable>
|
|
89
|
+
);
|
|
71
90
|
}, []);
|
|
72
|
-
|
|
73
|
-
const
|
|
91
|
+
|
|
92
|
+
const ActiveIndicator = useCallback(
|
|
93
|
+
(props) => <TabsActiveIndicator {...props} background="bgPrimary" bottom={0} height={2} />,
|
|
94
|
+
[],
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const [activeTab, setActiveTab] = useState(null);
|
|
98
|
+
return (
|
|
99
|
+
<Tabs
|
|
100
|
+
gap={4}
|
|
101
|
+
tabs={tabs}
|
|
102
|
+
activeTab={activeTab}
|
|
103
|
+
onChange={setActiveTab}
|
|
104
|
+
TabComponent={TabComponent}
|
|
105
|
+
TabsActiveIndicatorComponent={ActiveIndicator}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Disabled
|
|
112
|
+
|
|
113
|
+
The entire component can be disabled with the `disabled` prop.
|
|
114
|
+
|
|
115
|
+
```jsx live
|
|
116
|
+
function Example() {
|
|
117
|
+
const tabs = [
|
|
118
|
+
{ id: 'tab1', label: 'Tab 1' },
|
|
119
|
+
{ id: 'tab2', label: 'Tab 2' },
|
|
120
|
+
{ id: 'tab3', label: 'Tab 3' },
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
const TabComponent = useCallback(({ id, label, disabled, ...props }) => {
|
|
74
124
|
const { activeTab, updateActiveTab } = useTabsContext();
|
|
75
125
|
const isActive = activeTab?.id === id;
|
|
76
126
|
return (
|
|
@@ -82,11 +132,116 @@ import { Tabs } from '@coinbase/cds-web/tabs/Tabs'
|
|
|
82
132
|
>
|
|
83
133
|
<TabLabel id={id} active={isActive}>
|
|
84
134
|
{label}
|
|
85
|
-
{icon}
|
|
86
135
|
</TabLabel>
|
|
87
136
|
</Pressable>
|
|
88
137
|
);
|
|
89
|
-
};
|
|
138
|
+
}, []);
|
|
139
|
+
|
|
140
|
+
const ActiveIndicator = useCallback(
|
|
141
|
+
(props) => <TabsActiveIndicator {...props} background="bgPrimary" bottom={0} height={2} />,
|
|
142
|
+
[],
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const [activeTab, setActiveTab] = useState(tabs[0]);
|
|
146
|
+
return (
|
|
147
|
+
<Tabs
|
|
148
|
+
disabled
|
|
149
|
+
gap={4}
|
|
150
|
+
tabs={tabs}
|
|
151
|
+
activeTab={activeTab}
|
|
152
|
+
onChange={setActiveTab}
|
|
153
|
+
TabComponent={TabComponent}
|
|
154
|
+
TabsActiveIndicatorComponent={ActiveIndicator}
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Individual tabs can also be disabled while keeping others interactive.
|
|
161
|
+
|
|
162
|
+
```jsx live
|
|
163
|
+
function Example() {
|
|
164
|
+
const tabs = [
|
|
165
|
+
{ id: 'tab1', label: 'Tab 1' },
|
|
166
|
+
{ id: 'tab2', label: 'Tab 2', disabled: true },
|
|
167
|
+
{ id: 'tab3', label: 'Tab 3' },
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
const TabComponent = useCallback(({ id, label, disabled, ...props }) => {
|
|
171
|
+
const { activeTab, updateActiveTab } = useTabsContext();
|
|
172
|
+
const isActive = activeTab?.id === id;
|
|
173
|
+
return (
|
|
174
|
+
<Pressable
|
|
175
|
+
onClick={() => updateActiveTab(id)}
|
|
176
|
+
disabled={disabled}
|
|
177
|
+
aria-pressed={isActive}
|
|
178
|
+
{...props}
|
|
179
|
+
>
|
|
180
|
+
<TabLabel id={id} active={isActive}>
|
|
181
|
+
{label}
|
|
182
|
+
</TabLabel>
|
|
183
|
+
</Pressable>
|
|
184
|
+
);
|
|
185
|
+
}, []);
|
|
186
|
+
|
|
187
|
+
const ActiveIndicator = useCallback(
|
|
188
|
+
(props) => <TabsActiveIndicator {...props} background="bgPrimary" bottom={0} height={2} />,
|
|
189
|
+
[],
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const [activeTab, setActiveTab] = useState(tabs[0]);
|
|
193
|
+
return (
|
|
194
|
+
<Tabs
|
|
195
|
+
gap={4}
|
|
196
|
+
tabs={tabs}
|
|
197
|
+
activeTab={activeTab}
|
|
198
|
+
onChange={setActiveTab}
|
|
199
|
+
TabComponent={TabComponent}
|
|
200
|
+
TabsActiveIndicatorComponent={ActiveIndicator}
|
|
201
|
+
/>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Custom Components
|
|
207
|
+
|
|
208
|
+
#### Tab
|
|
209
|
+
|
|
210
|
+
Pass additional data through the tab definitions and access it in your `TabComponent` to render custom content like icons.
|
|
211
|
+
|
|
212
|
+
```jsx live
|
|
213
|
+
function Example() {
|
|
214
|
+
const tabs = [
|
|
215
|
+
{ id: 'home', label: 'Home', icon: 'home' },
|
|
216
|
+
{ id: 'profile', label: 'Profile', icon: 'user' },
|
|
217
|
+
{ id: 'settings', label: 'Settings', icon: 'settings' },
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
const CustomTab = useCallback(({ id, label, icon, disabled, ...props }) => {
|
|
221
|
+
const { activeTab, updateActiveTab } = useTabsContext();
|
|
222
|
+
const isActive = activeTab?.id === id;
|
|
223
|
+
return (
|
|
224
|
+
<Pressable
|
|
225
|
+
onClick={() => updateActiveTab(id)}
|
|
226
|
+
disabled={disabled}
|
|
227
|
+
aria-pressed={isActive}
|
|
228
|
+
{...props}
|
|
229
|
+
>
|
|
230
|
+
<HStack gap={1} alignItems="center">
|
|
231
|
+
<Icon name={icon} size="s" color={isActive ? 'fgPrimary' : 'fgMuted'} />
|
|
232
|
+
<TabLabel id={id} active={isActive}>
|
|
233
|
+
{label}
|
|
234
|
+
</TabLabel>
|
|
235
|
+
</HStack>
|
|
236
|
+
</Pressable>
|
|
237
|
+
);
|
|
238
|
+
}, []);
|
|
239
|
+
|
|
240
|
+
const ActiveIndicator = useCallback(
|
|
241
|
+
(props) => <TabsActiveIndicator {...props} background="bgPrimary" bottom={0} height={2} />,
|
|
242
|
+
[],
|
|
243
|
+
);
|
|
244
|
+
|
|
90
245
|
const [activeTab, setActiveTab] = useState(tabs[0]);
|
|
91
246
|
return (
|
|
92
247
|
<Tabs
|
|
@@ -95,10 +250,10 @@ import { Tabs } from '@coinbase/cds-web/tabs/Tabs'
|
|
|
95
250
|
activeTab={activeTab}
|
|
96
251
|
onChange={setActiveTab}
|
|
97
252
|
TabComponent={CustomTab}
|
|
98
|
-
TabsActiveIndicatorComponent={
|
|
253
|
+
TabsActiveIndicatorComponent={ActiveIndicator}
|
|
99
254
|
/>
|
|
100
255
|
);
|
|
101
|
-
}
|
|
256
|
+
}
|
|
102
257
|
```
|
|
103
258
|
|
|
104
259
|
## Props
|
|
@@ -136,6 +291,7 @@ import { Tabs } from '@coinbase/cds-web/tabs/Tabs'
|
|
|
136
291
|
| `borderedTop` | `boolean` | No | `-` | Add a border to the top side of the box. |
|
|
137
292
|
| `borderedVertical` | `boolean` | No | `-` | Add a border to the top and bottom sides of the box. |
|
|
138
293
|
| `bottom` | `ResponsiveProp<Bottom<string \| number>>` | No | `-` | - |
|
|
294
|
+
| `classNames` | `{ root?: string; tab?: string \| undefined; activeIndicator?: string \| undefined; } \| undefined` | No | `-` | Custom class names for individual elements of the Tabs component |
|
|
139
295
|
| `color` | `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 | `-` | - |
|
|
140
296
|
| `columnGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
|
|
141
297
|
| `dangerouslySetBackground` | `string` | No | `-` | - |
|
|
@@ -199,6 +355,7 @@ import { Tabs } from '@coinbase/cds-web/tabs/Tabs'
|
|
|
199
355
|
| `right` | `ResponsiveProp<Right<string \| number>>` | No | `-` | - |
|
|
200
356
|
| `rowGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
|
|
201
357
|
| `style` | `CSSProperties` | No | `-` | - |
|
|
358
|
+
| `styles` | `{ root?: CSSProperties; tab?: CSSProperties \| undefined; activeIndicator?: CSSProperties \| undefined; } \| undefined` | No | `-` | Custom styles for individual elements of the Tabs component |
|
|
202
359
|
| `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 |
|
|
203
360
|
| `textAlign` | `ResponsiveProp<center \| start \| end \| justify>` | No | `-` | - |
|
|
204
361
|
| `textDecoration` | `ResponsiveProp<none \| underline \| overline \| line-through \| underline overline \| underline double>` | No | `-` | - |
|
|
@@ -211,3 +368,12 @@ import { Tabs } from '@coinbase/cds-web/tabs/Tabs'
|
|
|
211
368
|
| `zIndex` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
|
|
212
369
|
|
|
213
370
|
|
|
371
|
+
## Styles
|
|
372
|
+
|
|
373
|
+
| Selector | Static class name | Description |
|
|
374
|
+
| --- | --- | --- |
|
|
375
|
+
| `root` | `-` | Root element |
|
|
376
|
+
| `tab` | `-` | Tab element |
|
|
377
|
+
| `activeIndicator` | `-` | Active indicator element |
|
|
378
|
+
|
|
379
|
+
|