Files
Termix/src/ui/Desktop/Navigation/Tabs/TabDropdown.tsx
T
ZacharyZcR d1635c35f3 Add tab navigation dropdown menu for better multi-tab management
Implement Issue #130 suggestion: Add dropdown menu in top tab bar to help
users quickly navigate between open hosts/tabs when many are open.

Features:
- Smart visibility: Only shows when multiple tabs are open
- Visual identification: Each tab type has corresponding icon (Home, Terminal, Server, etc.)
- Active tab indicator: Current tab highlighted with blue dot
- Quick switching: Click any tab to switch instantly
- Seamless integration: Positioned in top navbar without disrupting existing UI

New Components:
- src/components/ui/dropdown-menu.tsx: Radix UI based dropdown component
- src/ui/Desktop/Navigation/Tabs/TabDropdown.tsx: Tab navigation dropdown logic
- Updated TopNavbar.tsx with integrated dropdown

UI/UX Improvements:
- Consistent dark theme styling (#18181b background, #303032 borders)
- 30x30px button size matching existing toolbar buttons
- Right-aligned dropdown to avoid screen boundary issues
- Hover effects and smooth transitions
- Responsive design for different screen sizes

i18n Support:
- Added "tabNavigation" key in both English and Chinese translations
- Accessible tooltips and labels

Technical Implementation:
- Zero breaking changes: Pure incremental feature
- Reuses existing tab context and switching logic
- TypeScript safe with proper type definitions
- Performance optimized: Only renders when needed
- Follows existing code patterns and conventions

This enhancement significantly improves the user experience when managing
multiple host connections, providing quick access to any tab without scrolling.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-07 16:35:09 +08:00

111 lines
3.8 KiB
TypeScript

import React from "react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu.tsx";
import { Button } from "@/components/ui/button.tsx";
import {
ChevronDown,
Home,
Terminal as TerminalIcon,
Server as ServerIcon,
Folder as FolderIcon,
Shield as AdminIcon,
Network as SshManagerIcon
} from "lucide-react";
import { useTabs, type Tab } from "@/ui/Desktop/Navigation/Tabs/TabContext.tsx";
import { useTranslation } from "react-i18next";
export function TabDropdown(): React.ReactElement {
const { tabs, currentTab, setCurrentTab } = useTabs();
const { t } = useTranslation();
const getTabIcon = (tabType: Tab['type']) => {
switch (tabType) {
case 'home':
return <Home className="h-4 w-4" />;
case 'terminal':
return <TerminalIcon className="h-4 w-4" />;
case 'server':
return <ServerIcon className="h-4 w-4" />;
case 'file_manager':
return <FolderIcon className="h-4 w-4" />;
case 'ssh_manager':
return <SshManagerIcon className="h-4 w-4" />;
case 'admin':
return <AdminIcon className="h-4 w-4" />;
default:
return <TerminalIcon className="h-4 w-4" />;
}
};
const getTabDisplayTitle = (tab: Tab) => {
switch (tab.type) {
case 'home':
return t('nav.home');
case 'server':
return tab.title || t('nav.serverStats');
case 'file_manager':
return tab.title || t('nav.fileManager');
case 'ssh_manager':
return tab.title || t('nav.sshManager');
case 'admin':
return tab.title || t('nav.admin');
case 'terminal':
default:
return tab.title || t('nav.terminal');
}
};
const handleTabSwitch = (tabId: number) => {
setCurrentTab(tabId);
};
// If only one tab (home), don't show dropdown
if (tabs.length <= 1) {
return null;
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className="w-[30px] h-[30px] border-[#303032]"
title={t('nav.tabNavigation', { defaultValue: 'Tab Navigation' })}
>
<ChevronDown className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="w-56 bg-[#18181b] border-[#303032] text-white"
>
{tabs.map((tab) => {
const isActive = tab.id === currentTab;
return (
<DropdownMenuItem
key={tab.id}
onClick={() => handleTabSwitch(tab.id)}
className={`flex items-center gap-2 cursor-pointer px-3 py-2 ${
isActive
? 'bg-[#1d1d1f] text-white'
: 'hover:bg-[#2d2d30] text-gray-300'
}`}
>
{getTabIcon(tab.type)}
<span className="flex-1 truncate">
{getTabDisplayTitle(tab)}
</span>
{isActive && (
<div className="w-2 h-2 rounded-full bg-blue-500 flex-shrink-0" />
)}
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
}