@xsolla/xui-tabs 0.141.0 → 0.141.1

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/README.md DELETED
@@ -1,447 +0,0 @@
1
- # Tabs
2
-
3
- A cross-platform React tabs component for organizing content into multiple panels that users can switch between. Implements WAI-ARIA tablist pattern for accessibility.
4
-
5
- **Variants:**
6
- - **Line** (default): Traditional underlined tabs with bottom border indicator
7
- - **Segmented**: Button-group style segmented control with sliding active indicator
8
-
9
- ## Installation
10
-
11
- ```bash
12
- npm install @xsolla/xui-tabs
13
- # or
14
- yarn add @xsolla/xui-tabs
15
- ```
16
-
17
- ## Demo
18
-
19
- ### Basic Tabs
20
-
21
- ```tsx
22
- import * as React from 'react';
23
- import { Tabs, TabPanel } from '@xsolla/xui-tabs';
24
-
25
- export default function BasicTabs() {
26
- const [activeTab, setActiveTab] = React.useState('tab1');
27
-
28
- const tabs = [
29
- { id: 'tab1', label: 'Overview' },
30
- { id: 'tab2', label: 'Features' },
31
- { id: 'tab3', label: 'Pricing' },
32
- ];
33
-
34
- return (
35
- <div>
36
- <Tabs
37
- id="basic-tabs"
38
- tabs={tabs}
39
- activeTabId={activeTab}
40
- onChange={setActiveTab}
41
- />
42
- <TabPanel id="tab1" tabsId="basic-tabs" hidden={activeTab !== 'tab1'}>
43
- <p>Overview content goes here.</p>
44
- </TabPanel>
45
- <TabPanel id="tab2" tabsId="basic-tabs" hidden={activeTab !== 'tab2'}>
46
- <p>Features content goes here.</p>
47
- </TabPanel>
48
- <TabPanel id="tab3" tabsId="basic-tabs" hidden={activeTab !== 'tab3'}>
49
- <p>Pricing content goes here.</p>
50
- </TabPanel>
51
- </div>
52
- );
53
- }
54
- ```
55
-
56
- ### Tabs with Icons
57
-
58
- ```tsx
59
- import * as React from 'react';
60
- import { Tabs } from '@xsolla/xui-tabs';
61
- import { Home, User, Settings } from '@xsolla/xui-icons';
62
-
63
- export default function TabsWithIcons() {
64
- const [activeTab, setActiveTab] = React.useState('home');
65
-
66
- const tabs = [
67
- { id: 'home', label: 'Home', icon: <Home size={16} /> },
68
- { id: 'profile', label: 'Profile', icon: <User size={16} /> },
69
- { id: 'settings', label: 'Settings', icon: <Settings size={16} /> },
70
- ];
71
-
72
- return (
73
- <Tabs
74
- tabs={tabs}
75
- activeTabId={activeTab}
76
- onChange={setActiveTab}
77
- />
78
- );
79
- }
80
- ```
81
-
82
- ### Tabs with Counter
83
-
84
- ```tsx
85
- import * as React from 'react';
86
- import { Tabs } from '@xsolla/xui-tabs';
87
-
88
- export default function TabsWithCounter() {
89
- const [activeTab, setActiveTab] = React.useState('inbox');
90
-
91
- const tabs = [
92
- { id: 'inbox', label: 'Inbox', counter: 12 },
93
- { id: 'sent', label: 'Sent', counter: 5 },
94
- { id: 'drafts', label: 'Drafts', counter: 3 },
95
- { id: 'spam', label: 'Spam', badge: true },
96
- ];
97
-
98
- return (
99
- <Tabs
100
- tabs={tabs}
101
- activeTabId={activeTab}
102
- onChange={setActiveTab}
103
- />
104
- );
105
- }
106
- ```
107
-
108
- ### Tabs Sizes
109
-
110
- ```tsx
111
- import * as React from 'react';
112
- import { Tabs } from '@xsolla/xui-tabs';
113
-
114
- export default function TabsSizes() {
115
- const tabs = [
116
- { id: 'a', label: 'Tab A' },
117
- { id: 'b', label: 'Tab B' },
118
- { id: 'c', label: 'Tab C' },
119
- ];
120
-
121
- return (
122
- <div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
123
- <Tabs tabs={tabs} activeTabId="a" size="sm" />
124
- <Tabs tabs={tabs} activeTabId="a" size="md" />
125
- <Tabs tabs={tabs} activeTabId="a" size="lg" />
126
- <Tabs tabs={tabs} activeTabId="a" size="xl" />
127
- </div>
128
- );
129
- }
130
- ```
131
-
132
- ### Segmented Variant
133
-
134
- A button-group style segmented control with a sliding active indicator animation.
135
-
136
- ```tsx
137
- import * as React from 'react';
138
- import { Tabs } from '@xsolla/xui-tabs';
139
-
140
- export default function SegmentedTabs() {
141
- const [activeTab, setActiveTab] = React.useState('daily');
142
-
143
- const tabs = [
144
- { id: 'daily', label: 'Daily' },
145
- { id: 'weekly', label: 'Weekly' },
146
- { id: 'monthly', label: 'Monthly' },
147
- ];
148
-
149
- return (
150
- <Tabs
151
- tabs={tabs}
152
- activeTabId={activeTab}
153
- onChange={setActiveTab}
154
- variant="segmented"
155
- />
156
- );
157
- }
158
- ```
159
-
160
- ### Stretched Tabs
161
-
162
- Use the `stretched` prop to make tabs fill the entire container width.
163
-
164
- ```tsx
165
- import * as React from 'react';
166
- import { Tabs } from '@xsolla/xui-tabs';
167
-
168
- export default function StretchedTabs() {
169
- const [activeTab, setActiveTab] = React.useState('tab1');
170
-
171
- const tabs = [
172
- { id: 'tab1', label: 'First' },
173
- { id: 'tab2', label: 'Second' },
174
- { id: 'tab3', label: 'Third' },
175
- ];
176
-
177
- return (
178
- <div style={{ width: '100%' }}>
179
- {/* Line variant stretched */}
180
- <Tabs
181
- tabs={tabs}
182
- activeTabId={activeTab}
183
- onChange={setActiveTab}
184
- stretched
185
- />
186
-
187
- {/* Segmented variant stretched */}
188
- <Tabs
189
- tabs={tabs}
190
- activeTabId={activeTab}
191
- onChange={setActiveTab}
192
- variant="segmented"
193
- stretched
194
- />
195
- </div>
196
- );
197
- }
198
- ```
199
-
200
- ## Anatomy
201
-
202
- Import the components and compose them:
203
-
204
- ```jsx
205
- import { Tabs, TabPanel } from '@xsolla/xui-tabs';
206
-
207
- // Tab list
208
- <Tabs
209
- id="my-tabs" // Unique ID for ARIA
210
- tabs={tabItems} // Array of tab definitions
211
- activeTabId={activeId} // Currently active tab
212
- onChange={handleChange} // Tab selection handler
213
- size="md" // Size variant
214
- variant="line" // Visual variant: "line" | "segmented"
215
- stretched={false} // Fill container width
216
- alignLeft={true} // Alignment (line variant only)
217
- activateOnFocus={true} // Auto-activate on focus
218
- />
219
-
220
- // Tab panels
221
- <TabPanel
222
- id="tab-id" // Matches tab.id
223
- tabsId="my-tabs" // Parent Tabs id
224
- hidden={!isActive} // Visibility control
225
- >
226
- Panel content
227
- </TabPanel>
228
- ```
229
-
230
- ## Examples
231
-
232
- ### Disabled Tab
233
-
234
- ```tsx
235
- import * as React from 'react';
236
- import { Tabs } from '@xsolla/xui-tabs';
237
-
238
- export default function DisabledTab() {
239
- const [activeTab, setActiveTab] = React.useState('active');
240
-
241
- const tabs = [
242
- { id: 'active', label: 'Active Tab' },
243
- { id: 'disabled', label: 'Disabled Tab', disabled: true },
244
- { id: 'another', label: 'Another Tab' },
245
- ];
246
-
247
- return (
248
- <Tabs
249
- tabs={tabs}
250
- activeTabId={activeTab}
251
- onChange={setActiveTab}
252
- />
253
- );
254
- }
255
- ```
256
-
257
- ### Manual Activation
258
-
259
- ```tsx
260
- import * as React from 'react';
261
- import { Tabs } from '@xsolla/xui-tabs';
262
-
263
- export default function ManualActivation() {
264
- const [activeTab, setActiveTab] = React.useState('tab1');
265
-
266
- const tabs = [
267
- { id: 'tab1', label: 'First' },
268
- { id: 'tab2', label: 'Second' },
269
- { id: 'tab3', label: 'Third' },
270
- ];
271
-
272
- return (
273
- <div>
274
- <p>Use Arrow keys to navigate, Enter/Space to activate</p>
275
- <Tabs
276
- tabs={tabs}
277
- activeTabId={activeTab}
278
- onChange={setActiveTab}
279
- activateOnFocus={false}
280
- />
281
- </div>
282
- );
283
- }
284
- ```
285
-
286
- ### Full Example with Content
287
-
288
- ```tsx
289
- import * as React from 'react';
290
- import { Tabs, TabPanel } from '@xsolla/xui-tabs';
291
- import { Settings } from '@xsolla/xui-icons';
292
- import { LayoutDashboard, BarChart3, FileText } from '@xsolla/xui-icons-base';
293
-
294
- export default function FullTabsExample() {
295
- const [activeTab, setActiveTab] = React.useState('dashboard');
296
-
297
- const tabs = [
298
- { id: 'dashboard', label: 'Dashboard', icon: <LayoutDashboard size={16} /> },
299
- { id: 'analytics', label: 'Analytics', icon: <BarChart3 size={16} />, counter: 5 },
300
- { id: 'reports', label: 'Reports', icon: <FileText size={16} /> },
301
- { id: 'settings', label: 'Settings', icon: <Settings size={16} /> },
302
- ];
303
-
304
- return (
305
- <div style={{ width: '100%' }}>
306
- <Tabs
307
- id="main-tabs"
308
- tabs={tabs}
309
- activeTabId={activeTab}
310
- onChange={setActiveTab}
311
- size="md"
312
- />
313
-
314
- <div style={{ padding: 24, border: '1px solid #eee', borderTop: 'none' }}>
315
- <TabPanel id="dashboard" tabsId="main-tabs" hidden={activeTab !== 'dashboard'}>
316
- <h3>Dashboard</h3>
317
- <p>Welcome to your dashboard overview.</p>
318
- </TabPanel>
319
-
320
- <TabPanel id="analytics" tabsId="main-tabs" hidden={activeTab !== 'analytics'}>
321
- <h3>Analytics</h3>
322
- <p>View your analytics and metrics here.</p>
323
- </TabPanel>
324
-
325
- <TabPanel id="reports" tabsId="main-tabs" hidden={activeTab !== 'reports'}>
326
- <h3>Reports</h3>
327
- <p>Generate and view reports.</p>
328
- </TabPanel>
329
-
330
- <TabPanel id="settings" tabsId="main-tabs" hidden={activeTab !== 'settings'}>
331
- <h3>Settings</h3>
332
- <p>Configure your preferences.</p>
333
- </TabPanel>
334
- </div>
335
- </div>
336
- );
337
- }
338
- ```
339
-
340
- ## API Reference
341
-
342
- ### Tabs
343
-
344
- The tab list component.
345
-
346
- **Tabs Props:**
347
-
348
- | Prop | Type | Default | Description |
349
- | :--- | :--- | :------ | :---------- |
350
- | tabs | `TabItemType[]` | - | **Required.** Array of tab definitions. |
351
- | activeTabId | `string` | - | Currently active tab ID. |
352
- | onChange | `(id: string) => void` | - | Callback when tab selection changes. |
353
- | size | `"xl" \| "lg" \| "md" \| "sm"` | `"md"` | Size of the tabs. |
354
- | variant | `"line" \| "segmented"` | `"line"` | Visual variant. Line shows underlined tabs; segmented shows button-group style. |
355
- | stretched | `boolean` | `false` | Whether to stretch tabs to fill container width. |
356
- | alignLeft | `boolean` | `true` | Left vs center alignment (line variant only). |
357
- | activateOnFocus | `boolean` | `true` | Auto-activate tab on focus. |
358
- | id | `string` | - | Unique ID for ARIA association. |
359
- | aria-label | `string` | - | Accessible label for tab list. |
360
- | aria-labelledby | `string` | - | ID of labeling element. |
361
- | testID | `string` | - | Test identifier. |
362
-
363
- **TabItemType:**
364
-
365
- ```typescript
366
- interface TabItemType {
367
- id: string; // Unique tab identifier
368
- label: string; // Display label
369
- icon?: ReactNode; // Optional icon
370
- counter?: string | number; // Optional counter badge
371
- badge?: boolean | string | number; // Optional badge
372
- disabled?: boolean; // Whether tab is disabled
373
- "aria-label"?: string; // Custom accessible label
374
- }
375
- ```
376
-
377
- ---
378
-
379
- ### TabPanel
380
-
381
- Container for tab content.
382
-
383
- **TabPanel Props:**
384
-
385
- | Prop | Type | Default | Description |
386
- | :--- | :--- | :------ | :---------- |
387
- | id | `string` | - | **Required.** Matches corresponding tab.id. |
388
- | tabsId | `string` | - | **Required.** Parent Tabs component id. |
389
- | hidden | `boolean` | - | Whether panel is hidden. |
390
- | children | `ReactNode` | - | Panel content. |
391
- | aria-label | `string` | - | Accessible label. |
392
- | testID | `string` | - | Test identifier. |
393
-
394
- ## Keyboard Navigation
395
-
396
- | Key | Action |
397
- | :-- | :----- |
398
- | Arrow Right/Down | Move to next enabled tab |
399
- | Arrow Left/Up | Move to previous enabled tab |
400
- | Home | Jump to first enabled tab |
401
- | End | Jump to last enabled tab |
402
- | Enter/Space | Activate tab (when activateOnFocus is false) |
403
-
404
- ## Theming
405
-
406
- Tabs uses the design system theme for colors:
407
-
408
- ### Line Variant
409
-
410
- ```typescript
411
- // Colors accessed via theme
412
- theme.colors.content.primary // Active tab text
413
- theme.colors.content.tertiary // Inactive tab text
414
- theme.colors.border.primary // Active tab indicator (bottom border)
415
- theme.colors.border.secondary // Container bottom border
416
- theme.colors.overlay.mono // Hover background
417
- ```
418
-
419
- ### Segmented Variant
420
-
421
- ```typescript
422
- // Colors accessed via theme
423
- theme.colors.control.segmented.bg // Container background
424
- theme.colors.control.segmented.bgHover // Tab hover background
425
- theme.colors.control.segmented.bgActive // Active tab indicator background
426
- theme.colors.control.segmented.textActive // Active tab text color
427
- theme.colors.control.text.primary // Inactive tab text
428
- theme.colors.control.text.disable // Disabled tab text
429
- ```
430
-
431
- ## Accessibility
432
-
433
- - Implements WAI-ARIA tablist pattern
434
- - Uses `role="tablist"`, `role="tab"`, `role="tabpanel"`
435
- - `aria-selected` indicates active tab
436
- - `aria-controls` links tabs to panels
437
- - `aria-labelledby` links panels to tabs
438
- - Full keyboard navigation support
439
- - Focus management follows ARIA best practices
440
- - Segmented variant sliding indicator is marked with `aria-hidden`
441
-
442
- ## Platform Support
443
-
444
- Both variants support web and React Native platforms:
445
-
446
- - **Web**: Segmented variant includes smooth sliding animation for the active indicator
447
- - **React Native**: Falls back to static background without animation