import React, { useCallback, useState, useEffect, useRef } from 'react' import { useTabStore } from '../../stores/tabStore' import { getFileName } from '../../lib/fileUtils' import { Close, Plus } from '../Icons' interface ContextMenuState { visible: boolean x: number y: number tabId: string } export function TabBar() { const tabs = useTabStore(s => s.tabs) const activeTabId = useTabStore(s => s.activeTabId) const switchToTab = useTabStore(s => s.switchToTab) const closeTab = useTabStore(s => s.closeTab) const createTab = useTabStore(s => s.createTab) const closeOtherTabs = useTabStore(s => s.closeOtherTabs) const closeAllTabs = useTabStore(s => s.closeAllTabs) const closeTabsToRight = useTabStore(s => s.closeTabsToRight) const tabListRef = useRef(null) const [menu, setMenu] = useState({ visible: false, x: 0, y: 0, tabId: '' }) // 支持鼠标滚轮水平滚动标签栏 useEffect(() => { const tabList = tabListRef.current if (!tabList) return const handleWheel = (e: WheelEvent) => { // 只有当标签列表有水平滚动空间时才处理 const hasHorizontalScroll = tabList.scrollWidth > tabList.clientWidth if (!hasHorizontalScroll) return // 阻止默认的垂直滚动 e.preventDefault() e.stopPropagation() // 将垂直滚动转换为水平滚动 tabList.scrollLeft += e.deltaY } // 使用 capture 阶段,确保事件能被捕获 tabList.addEventListener('wheel', handleWheel, { passive: false, capture: true }) return () => tabList.removeEventListener('wheel', handleWheel, { capture: true }) }, []) // 自动滚动到活动标签 useEffect(() => { if (!tabListRef.current || !activeTabId) return // 使用 requestAnimationFrame 确保 DOM 已更新 requestAnimationFrame(() => { const activeTab = tabListRef.current?.querySelector('.tab-item.active') if (activeTab) { activeTab.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' }) } }) }, [activeTabId]) const handleClose = useCallback((e: React.MouseEvent, tabId: string) => { e.stopPropagation() const tab = tabs.find(t => t.id === tabId) if (tab?.isModified) { const name = tab.filePath ? getFileName(tab.filePath) : '未命名' if (!confirm(`"${name}" 尚未保存,确定要关闭吗?`)) return } closeTab(tabId) }, [tabs, closeTab]) // C-06: 右键菜单(带边界修正) const handleContextMenu = useCallback((e: React.MouseEvent, tabId: string) => { e.preventDefault() e.stopPropagation() const MENU_WIDTH = 170 const MENU_HEIGHT = 140 const x = Math.min(e.clientX, window.innerWidth - MENU_WIDTH) const y = Math.min(e.clientY, window.innerHeight - MENU_HEIGHT) setMenu({ visible: true, x: Math.max(0, x), y: Math.max(0, y), tabId }) }, []) useEffect(() => { if (!menu.visible) return const handleClick = () => setMenu(prev => ({ ...prev, visible: false })) document.addEventListener('click', handleClick) return () => document.removeEventListener('click', handleClick) }, [menu.visible]) const handleMenuClose = useCallback(() => { const tab = tabs.find(t => t.id === menu.tabId) if (tab?.isModified) { const name = tab.filePath ? getFileName(tab.filePath) : '未命名' if (!confirm(`"${name}" 尚未保存,确定要关闭吗?`)) return } closeTab(menu.tabId) setMenu(prev => ({ ...prev, visible: false })) }, [tabs, menu.tabId, closeTab]) const handleMenuCloseOthers = useCallback(() => { const otherModified = tabs.filter(t => t.id !== menu.tabId && t.isModified) if (otherModified.length > 0) { const names = otherModified.map(t => t.filePath ? getFileName(t.filePath) : '未命名').join('、') if (!confirm(`以下文件尚未保存:${names},确定要关闭吗?`)) return } closeOtherTabs(menu.tabId) setMenu(prev => ({ ...prev, visible: false })) }, [tabs, menu.tabId, closeOtherTabs]) const handleMenuCloseAll = useCallback(() => { const modified = tabs.filter(t => t.isModified) if (modified.length > 0) { const names = modified.map(t => t.filePath ? getFileName(t.filePath) : '未命名').join('、') if (!confirm(`以下文件尚未保存:${names},确定要关闭吗?`)) return } closeAllTabs() setMenu(prev => ({ ...prev, visible: false })) }, [tabs, closeAllTabs]) const handleMenuCloseRight = useCallback(() => { const index = tabs.findIndex(t => t.id === menu.tabId) const rightTabs = tabs.slice(index + 1) const modified = rightTabs.filter(t => t.isModified) if (modified.length > 0) { const names = modified.map(t => t.filePath ? getFileName(t.filePath) : '未命名').join('、') if (!confirm(`以下文件尚未保存:${names},确定要关闭吗?`)) return } closeTabsToRight(menu.tabId) setMenu(prev => ({ ...prev, visible: false })) }, [tabs, menu.tabId, closeTabsToRight]) const hasRightTabs = menu.visible && (() => { const index = tabs.findIndex(t => t.id === menu.tabId) return index < tabs.length - 1 })() if (tabs.length === 0) return null return (
{tabs.map(tab => (
switchToTab(tab.id)} onContextMenu={(e) => handleContextMenu(e, tab.id)} > {tab.filePath ? getFileName(tab.filePath) : '未命名'}
))}
{/* 右键菜单 */} {menu.visible && (
e.stopPropagation()} >
关闭
{tabs.length > 1 && (
关闭其他标签
)} {hasRightTabs && (
关闭右侧标签
)}
关闭全部标签
)}
) }