feat: tab bar

This commit is contained in:
OpenSquared
2026-06-29 08:56:10 +02:00
parent 97077af369
commit 45e5504256
5 changed files with 196 additions and 45 deletions

View File

@@ -0,0 +1,52 @@
import clsx from 'clsx'
import { X } from 'lucide-react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useTabs } from '../../context/TabsContext'
import { KEEP_ALIVE_META } from '../../config/keepAliveMeta'
export default function TabBar() {
const { kept, forgetRoute } = useTabs()
const location = useLocation()
const navigate = useNavigate()
const visible = KEEP_ALIVE_META.filter(m => kept.has(m.path))
if (visible.length === 0) return null
return (
<div className="flex items-end bg-dark-900 border-b border-slate-700/40 px-2 gap-0.5 pt-1 shrink-0">
{visible.map(({ path, label, icon: Icon }) => {
const isActive = location.pathname === path || location.pathname.startsWith(path + '/')
return (
<button
key={path}
onClick={() => navigate(path)}
className={clsx(
'group flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-t border border-b-0 transition-all -mb-px select-none whitespace-nowrap',
isActive
? 'bg-dark-800 border-slate-600/50 text-slate-200 font-medium z-10'
: 'bg-transparent border-transparent text-slate-500 hover:text-slate-300 hover:bg-dark-800/40 hover:border-slate-700/40'
)}
>
<Icon size={11} className="shrink-0" />
<span>{label}</span>
<span
role="button"
tabIndex={-1}
onClick={e => {
e.stopPropagation()
const remaining = visible.filter(m => m.path !== path)
forgetRoute(path)
if (isActive && remaining.length > 0) {
navigate(remaining[remaining.length - 1].path)
}
}}
className="ml-1.5 opacity-0 group-hover:opacity-100 text-slate-600 hover:text-white rounded hover:bg-slate-600/40 p-0.5 transition-opacity leading-none"
>
<X size={9} />
</span>
</button>
)
})}
</div>
)
}