tabs.tsx raw
1 "use client";
2
3 import * as React from "react";
4 import { cva, type VariantProps } from "class-variance-authority";
5 import { Tabs as TabsPrimitive } from "radix-ui";
6
7 import { cn } from "src/lib/utils";
8
9 function Tabs({
10 className,
11 orientation = "horizontal",
12 ...props
13 }: React.ComponentProps<typeof TabsPrimitive.Root>) {
14 return (
15 <TabsPrimitive.Root
16 data-slot="tabs"
17 data-orientation={orientation}
18 orientation={orientation}
19 className={cn(
20 "group/tabs flex gap-2 data-[orientation=horizontal]:flex-col",
21 className
22 )}
23 {...props}
24 />
25 );
26 }
27
28 const tabsListVariants = cva(
29 "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none",
30 {
31 variants: {
32 variant: {
33 default: "bg-muted",
34 line: "gap-1 bg-transparent",
35 },
36 },
37 defaultVariants: {
38 variant: "default",
39 },
40 }
41 );
42
43 function TabsList({
44 className,
45 variant = "default",
46 ...props
47 }: React.ComponentProps<typeof TabsPrimitive.List> &
48 VariantProps<typeof tabsListVariants>) {
49 return (
50 <TabsPrimitive.List
51 data-slot="tabs-list"
52 data-variant={variant}
53 className={cn(tabsListVariants({ variant }), className)}
54 {...props}
55 />
56 );
57 }
58
59 function TabsTrigger({
60 className,
61 ...props
62 }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
63 return (
64 <TabsPrimitive.Trigger
65 data-slot="tabs-trigger"
66 className={cn(
67 "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
68 "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
69 "data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground",
70 "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
71 className
72 )}
73 {...props}
74 />
75 );
76 }
77
78 function TabsContent({
79 className,
80 ...props
81 }: React.ComponentProps<typeof TabsPrimitive.Content>) {
82 return (
83 <TabsPrimitive.Content
84 data-slot="tabs-content"
85 className={cn("flex-1 outline-none", className)}
86 {...props}
87 />
88 );
89 }
90
91 export { Tabs, TabsList, TabsTrigger, TabsContent };
92