Tabs.tsx raw
1 import { cn } from '@/lib/utils'
2 import { useTranslation } from 'react-i18next'
3 import { useRef, useEffect, useState } from 'react'
4
5 export type TTabValue = 'replies' | 'reactions' | 'quotes'
6 const TABS = [
7 { value: 'replies', label: 'Replies' },
8 { value: 'reactions', label: 'Reactions' },
9 { value: 'quotes', label: 'Quotes' }
10 ] as { value: TTabValue; label: string }[]
11
12 export function Tabs({
13 selectedTab,
14 onTabChange
15 }: {
16 selectedTab: TTabValue
17 onTabChange: (tab: TTabValue) => void
18 }) {
19 const { t } = useTranslation()
20 const tabRefs = useRef<(HTMLDivElement | null)[]>([])
21 const [indicatorStyle, setIndicatorStyle] = useState({ width: 0, left: 0 })
22
23 useEffect(() => {
24 setTimeout(() => {
25 const activeIndex = TABS.findIndex((tab) => tab.value === selectedTab)
26 if (activeIndex >= 0 && tabRefs.current[activeIndex]) {
27 const activeTab = tabRefs.current[activeIndex]
28 const { offsetWidth, offsetLeft } = activeTab
29 const padding = 32 // 16px padding on each side
30 setIndicatorStyle({
31 width: offsetWidth - padding,
32 left: offsetLeft + padding / 2
33 })
34 }
35 }, 20) // ensure tabs are rendered before calculating
36 }, [selectedTab])
37
38 return (
39 <div className="w-fit">
40 <div className="flex relative">
41 {TABS.map((tab, index) => (
42 <div
43 key={tab.value}
44 ref={(el) => (tabRefs.current[index] = el)}
45 className={cn(
46 `text-center px-4 py-2 font-semibold clickable cursor-pointer rounded-lg`,
47 selectedTab === tab.value ? '' : 'text-muted-foreground'
48 )}
49 onClick={() => onTabChange(tab.value)}
50 >
51 {t(tab.label)}
52 </div>
53 ))}
54 <div
55 className="absolute bottom-0 h-1 bg-primary rounded-full transition-all duration-500"
56 style={{
57 width: `${indicatorStyle.width}px`,
58 left: `${indicatorStyle.left}px`
59 }}
60 />
61 </div>
62 </div>
63 )
64 }
65