优化folder窗口顶部的模式选择样式

This commit is contained in:
xintaofei
2026-03-24 23:53:37 +08:00
parent 593bbe70e3
commit 421857f249

View File

@@ -38,6 +38,7 @@ import { FolderNameDropdown } from "./folder-name-dropdown"
import { BranchDropdown } from "./branch-dropdown" import { BranchDropdown } from "./branch-dropdown"
import { CommandDropdown } from "./command-dropdown" import { CommandDropdown } from "./command-dropdown"
import { SearchCommandDialog } from "@/components/conversations/search-command-dialog" import { SearchCommandDialog } from "@/components/conversations/search-command-dialog"
import { cn } from "@/lib/utils"
const MODE_TABS = [ const MODE_TABS = [
{ {
@@ -185,8 +186,43 @@ export function FolderTitleBar() {
setBranch(null) setBranch(null)
} }
}, [folderPath]) }, [folderPath])
const modeIndex = MODE_TABS.findIndex((item) => item.mode === mode) const modeContainerRef = useRef<HTMLDivElement>(null)
const indicatorLeft = `${2 + modeIndex * 32}px` const modeItemRefs = useRef<Map<string, HTMLDivElement>>(new Map())
const [modeIndicator, setModeIndicator] = useState<{
left: number
width: number
} | null>(null)
useEffect(() => {
const container = modeContainerRef.current
if (!container) return
const measure = () => {
const btn = modeItemRefs.current.get(mode)
if (!btn || !container) {
setModeIndicator(null)
return
}
const containerRect = container.getBoundingClientRect()
const btnRect = btn.getBoundingClientRect()
setModeIndicator({
left: btnRect.left - containerRect.left,
width: btnRect.width,
})
}
const ro = new ResizeObserver(() => measure())
for (const btn of modeItemRefs.current.values()) {
ro.observe(btn)
}
ro.observe(container)
measure()
return () => {
ro.disconnect()
}
}, [mode])
const handleModeKeyDown = useCallback( const handleModeKeyDown = useCallback(
(event: ReactKeyboardEvent<HTMLDivElement>, nextMode: typeof mode) => { (event: ReactKeyboardEvent<HTMLDivElement>, nextMode: typeof mode) => {
if (event.key === "Enter" || event.key === " ") { if (event.key === "Enter" || event.key === " ") {
@@ -214,14 +250,20 @@ export function FolderTitleBar() {
} }
center={ center={
<div <div
ref={modeContainerRef}
role="tablist" role="tablist"
aria-label={tModes("workspaceModesAria")} aria-label={tModes("workspaceModesAria")}
className="relative flex h-[27px] items-center rounded-full border border-border/80 bg-background/80 p-0.5" className="relative inline-flex h-[27px] items-center rounded-full border border-border/50 bg-muted/50 p-0.5"
> >
<div {modeIndicator && (
className="pointer-events-none absolute bottom-[2px] top-[2px] w-8 rounded-full bg-accent transition-[left] duration-300 ease-out" <div
style={{ left: indicatorLeft }} className="pointer-events-none absolute top-0.5 bottom-0.5 rounded-full bg-background shadow-sm ring-1 ring-border/50 transition-all duration-300 ease-[cubic-bezier(0.4,0,0.2,1)]"
/> style={{
left: modeIndicator.left,
width: modeIndicator.width,
}}
/>
)}
{MODE_TABS.map((item) => { {MODE_TABS.map((item) => {
const Icon = item.icon const Icon = item.icon
const isActive = mode === item.mode const isActive = mode === item.mode
@@ -229,17 +271,26 @@ export function FolderTitleBar() {
return ( return (
<div <div
key={item.mode} key={item.mode}
ref={(el) => {
if (el) {
modeItemRefs.current.set(item.mode, el)
} else {
modeItemRefs.current.delete(item.mode)
}
}}
role="tab" role="tab"
tabIndex={0} tabIndex={0}
className={`relative z-10 m-0 flex h-[23px] w-8 cursor-pointer select-none items-center justify-center rounded-full border-0 bg-transparent p-0 align-middle leading-none transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60 ${ className={cn(
"relative z-10 m-0 flex h-[23px] cursor-pointer select-none items-center justify-center gap-1 rounded-full border-0 bg-transparent p-0 align-middle text-xs font-medium leading-none transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60",
isActive ? "px-2.5" : "px-2",
isActive isActive
? "text-foreground" ? "text-foreground"
: "text-muted-foreground hover:text-foreground/80" : "text-muted-foreground hover:text-foreground/70"
}`} )}
onClick={() => setMode(item.mode)} onClick={() => setMode(item.mode)}
onKeyDown={(event) => handleModeKeyDown(event, item.mode)} onKeyDown={(event) => handleModeKeyDown(event, item.mode)}
onMouseDown={(event) => event.preventDefault()} onMouseDown={(event) => event.preventDefault()}
title={title} title={!isActive ? title : undefined}
aria-label={title} aria-label={title}
aria-selected={isActive} aria-selected={isActive}
> >
@@ -247,6 +298,16 @@ export function FolderTitleBar() {
className="block h-3 w-3 shrink-0" className="block h-3 w-3 shrink-0"
shapeRendering="geometricPrecision" shapeRendering="geometricPrecision"
/> />
<span
className={cn(
"overflow-hidden whitespace-nowrap transition-all duration-300",
isActive
? "max-w-[60px] opacity-100"
: "max-w-0 opacity-0"
)}
>
{title}
</span>
</div> </div>
) )
})} })}