From 09f964451d7d92e9891430ec4595c1276d486aab Mon Sep 17 00:00:00 2001 From: zwlucas Date: Fri, 5 Jun 2026 17:52:54 -0300 Subject: feat: upload Signed-off-by: zwlucas --- src/components/TabsBar/TabsBar.tsx | 136 +++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/components/TabsBar/TabsBar.tsx (limited to 'src/components/TabsBar/TabsBar.tsx') diff --git a/src/components/TabsBar/TabsBar.tsx b/src/components/TabsBar/TabsBar.tsx new file mode 100644 index 0000000..86c77ea --- /dev/null +++ b/src/components/TabsBar/TabsBar.tsx @@ -0,0 +1,136 @@ +import { useRef, useEffect, useCallback, useState } from 'react' +import { useTabsStore } from '@/stores/useTabsStore' +import { useContextMenuStore } from '@/stores/useContextMenuStore' + +function CloseIcon() { + return ( + + + + + ) +} + +export default function TabsBar() { + const tabs = useTabsStore((s) => s.tabs) + const activeTabId = useTabsStore((s) => s.activeTabId) + const setActiveTab = useTabsStore((s) => s.setActiveTab) + const closeTab = useTabsStore((s) => s.closeTab) + const openContextMenu = useContextMenuStore((s) => s.open) + + const scrollRef = useRef(null) + const activeTabRef = useRef(null) + const [showLeftFade, setShowLeftFade] = useState(false) + const [showRightFade, setShowRightFade] = useState(true) + + const handleTabContextMenu = useCallback((e: React.MouseEvent, tabId: string) => { + e.preventDefault() + const numTabs = tabs.length + openContextMenu(e.clientX, e.clientY, [ + { id: 'close', label: 'Close', shortcut: 'Ctrl+W', icon: 'close', action: () => closeTab(tabId) }, + { id: 'closeOthers', label: 'Close Others', action: () => { tabs.forEach((t) => { if (t.id !== tabId) closeTab(t.id) }) }, disabled: numTabs < 2 }, + { id: 'closeAll', label: 'Close All', action: () => { tabs.forEach((t) => closeTab(t.id)) }, disabled: numTabs < 1 }, + { id: 'sep1', label: '', separator: true, action: () => {} }, + { id: 'copyPath', label: 'Copy Path', icon: 'link', action: () => { const tab = tabs.find((t) => t.id === tabId); if (tab) navigator.clipboard.writeText(tab.path) } }, + ]) + }, [tabs, closeTab, openContextMenu]) + + const updateFades = useCallback(() => { + const el = scrollRef.current + if (!el) return + setShowLeftFade(el.scrollLeft > 4) + setShowRightFade(el.scrollLeft < el.scrollWidth - el.clientWidth - 4) + }, []) + + useEffect(() => { + const el = scrollRef.current + if (!el) return + el.addEventListener('scroll', updateFades, { passive: true }) + updateFades() + return () => el.removeEventListener('scroll', updateFades) + }, [updateFades, tabs.length]) + + useEffect(() => { + const el = scrollRef.current + const tabEl = activeTabRef.current + if (!el || !tabEl) return + + const containerRect = el.getBoundingClientRect() + const tabRect = tabEl.getBoundingClientRect() + const isVisible = + tabRect.left >= containerRect.left && tabRect.right <= containerRect.right + + if (!isVisible) { + const offset = tabRect.left - containerRect.left + const centerOffset = offset - containerRect.width / 2 + tabRect.width / 2 + el.scrollBy({ left: centerOffset, behavior: 'smooth' }) + } + }, [activeTabId]) + + return ( +
+ {showLeftFade && ( +
+ )} + {showRightFade && ( +
+ )} + +
+ {tabs.length === 0 && ( +
+ No files open +
+ )} + + {tabs.map((tab) => { + const isActive = tab.id === activeTabId + + return ( +
setActiveTab(tab.id)} + onContextMenu={(e) => handleTabContextMenu(e, tab.id)} + > + {isActive && ( + + )} + + {tab.name} + + {tab.modified && ( + + )} + + +
+ ) + })} +
+
+ ) +} -- cgit v1.2.3