Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | /** * AgentTabBar — presentational tab bar for the agent detail page (#1419). * Renders Chat / Sessions / Schedules / Settings tabs, reusing AppNav styling. * * @module */ import { useRef, type JSX, type KeyboardEvent } from "react"; import { Activity, CalendarClock, MessageSquare, Settings } from "lucide-react"; import { useAppNavigate, agentUrl, type AgentTab } from "../../utils/navigation.js"; import navStyles from "../layout/AppNav.module.scss"; const ICON_SIZE: number = 18; interface AgentTabDef { id: AgentTab; label: string; icon: JSX.Element; } const AGENT_TABS: AgentTabDef[] = [ { id: "chat", label: "Chat", icon: <MessageSquare size={ICON_SIZE} /> }, { id: "sessions", label: "Sessions", icon: <Activity size={ICON_SIZE} /> }, { id: "schedules", label: "Schedules", icon: <CalendarClock size={ICON_SIZE} /> }, { id: "settings", label: "Settings", icon: <Settings size={ICON_SIZE} /> }, ]; /** Props for {@link AgentTabBar}. */ export interface AgentTabBarProps { /** The agent whose tabs are being rendered. */ agentId: string; /** Which tab is currently active. */ activeTab: AgentTab; } /** Horizontal tab bar for navigating between agent detail views. */ export function AgentTabBar({ agentId, activeTab }: AgentTabBarProps): JSX.Element { const navigate = useAppNavigate(); const navRef = useRef<HTMLElement>(null); const handleKeyDown = (e: KeyboardEvent<HTMLElement>): void => { const buttons = navRef.current?.querySelectorAll<HTMLButtonElement>('[role="tab"]'); if (!buttons || buttons.length === 0) return; const currentIndex = AGENT_TABS.findIndex((t) => t.id === activeTab); let nextIndex = currentIndex; if (e.key === "ArrowRight" || e.key === "ArrowDown") { e.preventDefault(); nextIndex = (currentIndex + 1) % AGENT_TABS.length; } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") { e.preventDefault(); nextIndex = (currentIndex - 1 + AGENT_TABS.length) % AGENT_TABS.length; } else if (e.key === "Home") { e.preventDefault(); nextIndex = 0; } else if (e.key === "End") { e.preventDefault(); nextIndex = AGENT_TABS.length - 1; } else { return; } const nextTab = AGENT_TABS[nextIndex]; navigate(agentUrl(agentId, nextTab.id)); buttons[nextIndex].focus(); }; return ( <nav ref={navRef} className={navStyles.nav} role="tablist" aria-label="Agent navigation" aria-orientation="horizontal" onKeyDown={handleKeyDown} data-testid="agent-tab-bar" > {AGENT_TABS.map((tab) => { const isActive = tab.id === activeTab; return ( <button key={tab.id} role="tab" aria-selected={isActive} tabIndex={isActive ? 0 : -1} className={`${navStyles.tab} ${isActive ? navStyles.tabActive : ""}`} onClick={() => navigate(agentUrl(agentId, tab.id))} data-testid={`agent-tab-${tab.id}`} > <span className={navStyles.tabIcon}>{tab.icon}</span> <span className={navStyles.tabLabel}>{tab.label}</span> </button> ); })} </nav> ); } |