Skip to content

Commit f64d653

Browse files
committed
feat(tailwind): add Tabs component
1 parent d59d0d9 commit f64d653

16 files changed

Lines changed: 743 additions & 0 deletions

File tree

apps/showcase/__store__/index.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2113,6 +2113,10 @@ export const Store: Record<string, Record<string, Record<string, { component: Re
21132113
'component': React.lazy(() => import('demo/styled/tabs/dynamic-demo')),
21142114
'filePath': 'demo/styled/tabs/dynamic-demo.tsx',
21152115
},
2116+
'focus-selection-demo': {
2117+
'component': React.lazy(() => import('demo/styled/tabs/focus-selection-demo')),
2118+
'filePath': 'demo/styled/tabs/focus-selection-demo.tsx',
2119+
},
21162120
'scrollable-demo': {
21172121
'component': React.lazy(() => import('demo/styled/tabs/scrollable-demo')),
21182122
'filePath': 'demo/styled/tabs/scrollable-demo.tsx',
@@ -3052,6 +3056,40 @@ export const Store: Record<string, Record<string, Record<string, { component: Re
30523056
'filePath': 'demo/tailwind/switch/uncontrolled-demo.tsx',
30533057
},
30543058
},
3059+
'tabs': {
3060+
'basic-demo': {
3061+
'component': React.lazy(() => import('demo/tailwind/tabs/basic-demo')),
3062+
'filePath': 'demo/tailwind/tabs/basic-demo.tsx',
3063+
},
3064+
'controlled-demo': {
3065+
'component': React.lazy(() => import('demo/tailwind/tabs/controlled-demo')),
3066+
'filePath': 'demo/tailwind/tabs/controlled-demo.tsx',
3067+
},
3068+
'disabled-demo': {
3069+
'component': React.lazy(() => import('demo/tailwind/tabs/disabled-demo')),
3070+
'filePath': 'demo/tailwind/tabs/disabled-demo.tsx',
3071+
},
3072+
'dynamic-demo': {
3073+
'component': React.lazy(() => import('demo/tailwind/tabs/dynamic-demo')),
3074+
'filePath': 'demo/tailwind/tabs/dynamic-demo.tsx',
3075+
},
3076+
'focus-selection-demo': {
3077+
'component': React.lazy(() => import('demo/tailwind/tabs/focus-selection-demo')),
3078+
'filePath': 'demo/tailwind/tabs/focus-selection-demo.tsx',
3079+
},
3080+
'scrollable-demo': {
3081+
'component': React.lazy(() => import('demo/tailwind/tabs/scrollable-demo')),
3082+
'filePath': 'demo/tailwind/tabs/scrollable-demo.tsx',
3083+
},
3084+
'tabs-pt': {
3085+
'component': React.lazy(() => import('demo/tailwind/tabs/tabs-pt')),
3086+
'filePath': 'demo/tailwind/tabs/tabs-pt.tsx',
3087+
},
3088+
'template-demo': {
3089+
'component': React.lazy(() => import('demo/tailwind/tabs/template-demo')),
3090+
'filePath': 'demo/tailwind/tabs/template-demo.tsx',
3091+
},
3092+
},
30553093
'textarea': {
30563094
'auto-resize-demo': {
30573095
'component': React.lazy(() => import('demo/tailwind/textarea/auto-resize-demo')),

apps/showcase/assets/menu/submenu/menu-tailwind.data.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ export const tailwindMenu = [
106106
{
107107
name: 'Panel',
108108
href: '/docs/tailwind/components/panel'
109+
},
110+
{
111+
name: 'Tabs',
112+
href: '/docs/tailwind/components/tabs'
109113
}
110114
]
111115
},
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Tabs, TabsList, TabsPanel, TabsPanels, TabsTab } from '@/components/ui/tabs';
2+
3+
export default function BasicDemo() {
4+
return (
5+
<Tabs defaultValue="tab1">
6+
<TabsList>
7+
<TabsTab value="tab1">Account Info</TabsTab>
8+
<TabsTab value="tab2">Payment</TabsTab>
9+
<TabsTab value="tab3">Preferences</TabsTab>
10+
</TabsList>
11+
<TabsPanels>
12+
<TabsPanel value="tab1">
13+
<h2 className="text-lg font-bold">Account Info</h2>
14+
<p className="text-surface-500 mt-1">Update your personal information such as name, email address, and profile picture.</p>
15+
</TabsPanel>
16+
<TabsPanel value="tab2">
17+
<h2 className="text-lg font-bold">Payment</h2>
18+
<p className="text-surface-500 mt-1">Manage your subscription plan, view invoices, and update your payment method.</p>
19+
</TabsPanel>
20+
<TabsPanel value="tab3">
21+
<h2 className="text-lg font-bold">Preferences</h2>
22+
<p className="text-surface-500 mt-1">Customize how the application looks and behaves to match your personal preferences.</p>
23+
</TabsPanel>
24+
</TabsPanels>
25+
</Tabs>
26+
);
27+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use client';
2+
import type { TabsRootChangeEvent, TabsRootProps } from '@primereact/types/shared/tabs';
3+
import { Button } from '@/components/ui/button';
4+
import { Tabs, TabsList, TabsPanel, TabsPanels, TabsTab } from '@/components/ui/tabs';
5+
import * as React from 'react';
6+
7+
const tabs = [
8+
{ id: 'tab1', title: 'Account Info', content: 'Update your personal information such as name, email address, and profile picture.' },
9+
{ id: 'tab2', title: 'Payment', content: 'Manage your subscription plan, view invoices, and update your payment method.' },
10+
{ id: 'tab3', title: 'Preferences', content: 'Customize how the application looks and behaves to match your personal preferences.' }
11+
];
12+
13+
export default function ControlledDemo() {
14+
const [activeTab, setActiveTab] = React.useState<TabsRootProps['value']>('tab1');
15+
16+
return (
17+
<div className="space-y-4">
18+
<Button onClick={() => setActiveTab('tab2')}>Go to Payment</Button>
19+
<Tabs value={activeTab} onValueChange={(e: TabsRootChangeEvent) => setActiveTab(e.value)}>
20+
<TabsList>
21+
{tabs.map((tab) => (
22+
<TabsTab key={tab.id} value={tab.id}>
23+
{tab.title}
24+
</TabsTab>
25+
))}
26+
</TabsList>
27+
<TabsPanels>
28+
{tabs.map((tab) => (
29+
<TabsPanel key={tab.id} value={tab.id}>
30+
<h2 className="text-lg font-bold">{tab.title}</h2>
31+
<p className="text-surface-500 mt-1">{tab.content}</p>
32+
</TabsPanel>
33+
))}
34+
</TabsPanels>
35+
</Tabs>
36+
</div>
37+
);
38+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Tabs, TabsList, TabsPanel, TabsPanels, TabsTab } from '@/components/ui/tabs';
2+
3+
export default function DisabledDemo() {
4+
return (
5+
<Tabs defaultValue="tab1">
6+
<TabsList>
7+
<TabsTab value="tab1">Account Info</TabsTab>
8+
<TabsTab value="tab2" disabled>
9+
Payment
10+
</TabsTab>
11+
<TabsTab value="tab3">Preferences</TabsTab>
12+
</TabsList>
13+
<TabsPanels>
14+
<TabsPanel value="tab1">
15+
<h2 className="text-lg font-bold">Account Info</h2>
16+
<p className="text-surface-500 mt-1">Update your personal information such as name, email address, and profile picture.</p>
17+
</TabsPanel>
18+
<TabsPanel value="tab2">
19+
<h2 className="text-lg font-bold">Payment</h2>
20+
<p className="text-surface-500 mt-1">Manage your subscription plan, view invoices, and update your payment method.</p>
21+
</TabsPanel>
22+
<TabsPanel value="tab3">
23+
<h2 className="text-lg font-bold">Preferences</h2>
24+
<p className="text-surface-500 mt-1">Customize how the application looks and behaves to match your personal preferences.</p>
25+
</TabsPanel>
26+
</TabsPanels>
27+
</Tabs>
28+
);
29+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Tabs, TabsList, TabsPanel, TabsPanels, TabsTab } from '@/components/ui/tabs';
2+
3+
const tabs = [
4+
{ id: 'tab1', title: 'Account Info', content: 'Update your personal information such as name, email address, and profile picture.' },
5+
{ id: 'tab2', title: 'Payment', content: 'Manage your subscription plan, view invoices, and update your payment method.' },
6+
{ id: 'tab3', title: 'Preferences', content: 'Customize how the application looks and behaves to match your personal preferences.' }
7+
];
8+
9+
export default function DynamicDemo() {
10+
return (
11+
<Tabs defaultValue="tab1">
12+
<TabsList>
13+
{tabs.map((tab) => (
14+
<TabsTab key={tab.id} value={tab.id}>
15+
{tab.title}
16+
</TabsTab>
17+
))}
18+
</TabsList>
19+
<TabsPanels>
20+
{tabs.map((tab) => (
21+
<TabsPanel key={tab.id} value={tab.id}>
22+
<h2 className="text-lg font-bold">{tab.title}</h2>
23+
<p className="text-surface-500 mt-1">{tab.content}</p>
24+
</TabsPanel>
25+
))}
26+
</TabsPanels>
27+
</Tabs>
28+
);
29+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Tabs, TabsList, TabsPanel, TabsPanels, TabsTab } from '@/components/ui/tabs';
2+
3+
const tabs = [
4+
{ id: 'tab1', title: 'Account Info', content: 'Update your personal information such as name, email address, and profile picture.' },
5+
{ id: 'tab2', title: 'Payment', content: 'Manage your subscription plan, view invoices, and update your payment method.' },
6+
{ id: 'tab3', title: 'Preferences', content: 'Customize how the application looks and behaves to match your personal preferences.' }
7+
];
8+
9+
export default function FocusSelectionDemo() {
10+
return (
11+
<Tabs defaultValue="tab1" selectOnFocus>
12+
<TabsList>
13+
{tabs.map((tab) => (
14+
<TabsTab key={tab.id} value={tab.id}>
15+
{tab.title}
16+
</TabsTab>
17+
))}
18+
</TabsList>
19+
<TabsPanels>
20+
{tabs.map((tab) => (
21+
<TabsPanel key={tab.id} value={tab.id}>
22+
<h2 className="text-lg font-bold">{tab.title}</h2>
23+
<p className="text-surface-500 mt-1">{tab.content}</p>
24+
</TabsPanel>
25+
))}
26+
</TabsPanels>
27+
</Tabs>
28+
);
29+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Tabs, TabsList, TabsPanel, TabsPanels, TabsTab } from '@/components/ui/tabs';
2+
3+
export default function ScrollableDemo() {
4+
return (
5+
<Tabs defaultValue={'0'}>
6+
<TabsList>
7+
{scrollableTabs.map((tab) => (
8+
<TabsTab key={tab.value} value={tab.value}>
9+
{tab.title}
10+
</TabsTab>
11+
))}
12+
</TabsList>
13+
<TabsPanels>
14+
{scrollableTabs.map((tab) => (
15+
<TabsPanel key={tab.value} value={tab.value}>
16+
<h2 className="text-lg font-bold">{tab.title}</h2>
17+
<p className="text-surface-500 mt-1">{tab.content}</p>
18+
</TabsPanel>
19+
))}
20+
</TabsPanels>
21+
</Tabs>
22+
);
23+
}
24+
25+
const sentences = [
26+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
27+
'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
28+
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.',
29+
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore.',
30+
'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia.',
31+
'Curabitur pretium tincidunt lacus, nec viverra velit semper at.',
32+
'Fusce condimentum nunc ac nisi vulputate fringilla.',
33+
'Donec fermentum porttitor nunc, vitae pellentesque tortor.',
34+
'Pellentesque habitant morbi tristique senectus et netus et malesuada fames.',
35+
'Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia.'
36+
];
37+
38+
const scrollableTabs = Array.from({ length: 50 }, (_, i) => {
39+
const start = i % sentences.length;
40+
const content = Array.from({ length: 4 }, (_, j) => sentences[(start + j) % sentences.length]).join(' ');
41+
42+
return { title: `Tab ${i + 1}`, value: `${i}`, content };
43+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Tabs, TabsList, TabsPanel, TabsPanels, TabsTab } from '@/components/ui/tabs';
2+
3+
export default function TabsPT() {
4+
return (
5+
<Tabs defaultValue="tab1">
6+
<TabsList>
7+
<TabsTab value="tab1">Overview</TabsTab>
8+
<TabsTab value="tab2">Details</TabsTab>
9+
<TabsTab value="tab3">Activity</TabsTab>
10+
</TabsList>
11+
<TabsPanels>
12+
<TabsPanel value="tab1">
13+
<p className="m-0">Overview content</p>
14+
</TabsPanel>
15+
<TabsPanel value="tab2">
16+
<p className="m-0">Details content</p>
17+
</TabsPanel>
18+
<TabsPanel value="tab3">
19+
<p className="m-0">Activity content</p>
20+
</TabsPanel>
21+
</TabsPanels>
22+
</Tabs>
23+
);
24+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { Badge } from '@/components/ui/badge';
2+
import { Button } from '@/components/ui/button';
3+
import { InputText } from '@/components/ui/inputtext';
4+
import { Label } from '@/components/ui/label';
5+
import { Switch } from '@/components/ui/switch';
6+
import { Tabs, TabsList, TabsPanel, TabsPanels, TabsTab } from '@/components/ui/tabs';
7+
import { Cog } from '@primeicons/react/cog';
8+
import { CreditCard } from '@primeicons/react/credit-card';
9+
import { User } from '@primeicons/react/user';
10+
11+
const tabs = [
12+
{
13+
id: 'tab1',
14+
title: 'Account Info',
15+
icon: User,
16+
content: 'Update your personal information such as name, email address, and profile picture.'
17+
},
18+
{
19+
id: 'tab2',
20+
title: 'Payment',
21+
icon: CreditCard,
22+
badge: 'New',
23+
content: 'Manage your subscription plan, view invoices, and update your payment method.'
24+
},
25+
{
26+
id: 'tab3',
27+
title: 'Preferences',
28+
icon: Cog,
29+
content: 'Customize how the application looks and behaves to match your personal preferences.'
30+
}
31+
];
32+
33+
export default function TemplateDemo() {
34+
return (
35+
<Tabs defaultValue="tab1" className="max-w-md mx-auto">
36+
<TabsList>
37+
{tabs.map((tab) => (
38+
<TabsTab key={tab.id} value={tab.id} className="flex items-center gap-2">
39+
<tab.icon />
40+
{tab.title}
41+
{tab.badge && <Badge size="small">{tab.badge}</Badge>}
42+
</TabsTab>
43+
))}
44+
</TabsList>
45+
<TabsPanels>
46+
<TabsPanel value="tab1">
47+
<p className="mt-2 mb-8 text-surface-500">Update your personal information such as name, email address, and profile picture.</p>
48+
<form>
49+
<div className="space-y-4">
50+
<div className="flex flex-col gap-1">
51+
<Label htmlFor="username">Username</Label>
52+
<InputText id="username" placeholder="john.doe" />
53+
</div>
54+
<div className="flex flex-col gap-1">
55+
<Label htmlFor="email">Email</Label>
56+
<InputText id="email" placeholder="john.doe@example.com" />
57+
</div>
58+
</div>
59+
<Button className="mt-8 w-fit">Save Changes</Button>
60+
</form>
61+
</TabsPanel>
62+
<TabsPanel value="tab2">
63+
<p className="mt-2 mb-8 text-surface-500">Manage your subscription plan, view invoices, and update your payment method.</p>
64+
<form>
65+
<div className="space-y-4">
66+
<div className="flex flex-col gap-1">
67+
<Label htmlFor="cardName">Cardholder Name</Label>
68+
<InputText id="cardName" placeholder="John Doe" />
69+
</div>
70+
<div className="flex flex-col gap-1">
71+
<Label htmlFor="cardNumber">Card Number</Label>
72+
<InputText id="cardNumber" placeholder="0000 0000 0000 0000" />
73+
</div>
74+
<div className="flex flex-col gap-1">
75+
<Label htmlFor="expiryDate">Expiry Date</Label>
76+
<InputText id="expiryDate" placeholder="MM/YY" />
77+
</div>
78+
</div>
79+
<Button className="mt-8 w-fit">Update Payment</Button>
80+
</form>
81+
</TabsPanel>
82+
<TabsPanel value="tab3">
83+
<p className="mt-2 mb-8 text-surface-500">Customize how the application looks and behaves to match your personal preferences.</p>
84+
<form>
85+
<div className="space-y-4">
86+
<div className="flex items-center justify-between">
87+
<Label htmlFor="darkMode">Dark Mode</Label>
88+
<Switch inputId="darkMode" />
89+
</div>
90+
<div className="flex items-center justify-between">
91+
<Label htmlFor="emailNotifications">Email Notifications</Label>
92+
<Switch inputId="emailNotifications" defaultChecked />
93+
</div>
94+
<div className="flex items-center justify-between">
95+
<Label htmlFor="desktopNotifications">Desktop Notifications</Label>
96+
<Switch inputId="desktopNotifications" />
97+
</div>
98+
</div>
99+
<Button className="w-fit mt-8 ml-auto mr-0">Save Preferences</Button>
100+
</form>
101+
</TabsPanel>
102+
</TabsPanels>
103+
</Tabs>
104+
);
105+
}

0 commit comments

Comments
 (0)