@@ -5,44 +5,146 @@ import { type ShortcutDefinition, useShortcutKeys } from "~/hooks/useShortcutKey
55import { cn } from "~/utils/cn" ;
66import { ShortcutKey } from "./ShortcutKey" ;
77
8+ const variants = {
9+ underline : {
10+ base : "bg-charcoal-700" ,
11+ active : "text-text-bright hover:bg-charcoal-750/50" ,
12+ } ,
13+ "pipe-divider" : {
14+ base : "bg-charcoal-700/50" ,
15+ active : "text-text-bright bg-charcoal-700 rounded-[2px] border border-charcoal-600/50" ,
16+ } ,
17+ segmented : {
18+ base : "bg-charcoal-700/50" ,
19+ active : "text-text-bright bg-charcoal-700 rounded-[2px] border border-charcoal-600/50" ,
20+ } ,
21+ } ;
22+
23+ export type Variants = keyof typeof variants ;
24+
825export type TabsProps = {
926 tabs : {
1027 label : string ;
1128 to : string ;
1229 } [ ] ;
1330 className ?: string ;
1431 layoutId : string ;
32+ variant ?: Variants ;
1533} ;
1634
17- export function Tabs ( { tabs, className, layoutId } : TabsProps ) {
35+ export function Tabs ( { tabs, className, layoutId, variant = "underline" } : TabsProps ) {
1836 return (
19- < TabContainer className = { className } >
37+ < TabContainer className = { className } variant = { variant } >
2038 { tabs . map ( ( tab , index ) => (
21- < TabLink key = { index } to = { tab . to } layoutId = { layoutId } >
39+ < TabLink key = { index } to = { tab . to } layoutId = { layoutId } variant = { variant } >
2240 { tab . label }
2341 </ TabLink >
2442 ) ) }
2543 </ TabContainer >
2644 ) ;
2745}
2846
29- export function TabContainer ( { children, className } : { children : ReactNode ; className ?: string } ) {
30- return (
31- < div className = { cn ( `flex flex-row gap-x-6 border-b border-grid-bright` , className ) } >
32- { children }
33- </ div >
34- ) ;
47+ export function TabContainer ( {
48+ children,
49+ className,
50+ variant = "underline" ,
51+ } : {
52+ children : ReactNode ;
53+ className ?: string ;
54+ variant ?: Variants ;
55+ } ) {
56+ if ( variant === "segmented" ) {
57+ return (
58+ < div
59+ className = { cn ( "relative flex h-10 items-center rounded bg-charcoal-700/50 p-1" , className ) }
60+ >
61+ { children }
62+ </ div >
63+ ) ;
64+ }
65+
66+ if ( variant === "underline" ) {
67+ return (
68+ < div className = { cn ( `flex gap-x-6 border-b border-grid-bright` , className ) } > { children } </ div >
69+ ) ;
70+ }
71+
72+ return < div className = { cn ( `flex` , className ) } > { children } </ div > ;
3573}
3674
3775export function TabLink ( {
3876 to,
3977 children,
4078 layoutId,
79+ variant = "underline" ,
4180} : {
4281 to : string ;
4382 children : ReactNode ;
4483 layoutId : string ;
84+ variant ?: Variants ;
4585} ) {
86+ if ( variant === "segmented" ) {
87+ return (
88+ < NavLink
89+ to = { to }
90+ className = "group relative flex h-full grow items-center justify-center focus-custom"
91+ end
92+ >
93+ { ( { isActive, isPending } ) => {
94+ const active = isActive || isPending ;
95+ return (
96+ < >
97+ < div className = "relative z-10 flex h-full w-full items-center justify-center px-3 py-[0.13rem]" >
98+ < span
99+ className = { cn (
100+ "text-sm transition duration-200" ,
101+ active
102+ ? "text-text-bright"
103+ : "text-text-dimmed transition hover:text-text-bright"
104+ ) }
105+ >
106+ { children }
107+ </ span >
108+ </ div >
109+ { active && (
110+ < motion . div
111+ layoutId = { layoutId }
112+ transition = { { duration : 0.4 , type : "spring" } }
113+ className = "absolute inset-0 rounded-[2px] border border-charcoal-500/50 bg-charcoal-600"
114+ />
115+ ) }
116+ </ >
117+ ) ;
118+ } }
119+ </ NavLink >
120+ ) ;
121+ }
122+
123+ if ( variant === "pipe-divider" ) {
124+ return (
125+ < NavLink
126+ to = { to }
127+ className = "group flex flex-col items-center border-r border-charcoal-700 px-2 pt-1 focus-custom first:pl-0 last:border-none"
128+ end
129+ >
130+ { ( { isActive, isPending } ) => {
131+ const active = isActive || isPending ;
132+ return (
133+ < span
134+ className = { cn (
135+ "text-sm transition duration-200" ,
136+ active ? "text-text-link" : "text-text-dimmed transition hover:text-text-bright"
137+ ) }
138+ >
139+ { children }
140+ </ span >
141+ ) ;
142+ } }
143+ </ NavLink >
144+ ) ;
145+ }
146+
147+ // underline variant (default)
46148 return (
47149 < NavLink to = { to } className = "group flex flex-col items-center pt-1 focus-custom" end >
48150 { ( { isActive, isPending } ) => {
@@ -51,13 +153,19 @@ export function TabLink({
51153 < span
52154 className = { cn (
53155 "text-sm transition duration-200" ,
54- isActive || isPending ? "text-text-bright" : "text-text-bright"
156+ isActive || isPending
157+ ? "text-text-bright"
158+ : "text-text-dimmed hover:text-text-bright"
55159 ) }
56160 >
57161 { children }
58162 </ span >
59163 { isActive || isPending ? (
60- < motion . div layoutId = { layoutId } className = "mt-1 h-0.5 w-full bg-indigo-500" />
164+ < motion . div
165+ layoutId = { layoutId }
166+ transition = { { type : "spring" , stiffness : 500 , damping : 30 } }
167+ className = "mt-1 h-0.5 w-full bg-indigo-500"
168+ />
61169 ) : (
62170 < div className = "mt-1 h-0.5 w-full bg-charcoal-500 opacity-0 transition duration-200 group-hover:opacity-100" />
63171 ) }
0 commit comments