refactor: simplify hover menu logic and update related tests for workspace switcher

This commit is contained in:
mlogclub
2026-06-21 10:29:54 +08:00
parent fe5041191f
commit 991243625d
2 changed files with 5 additions and 45 deletions
+4 -1
View File
@@ -14,8 +14,11 @@ it("wraps the dropdown label in a Base UI menu group", async () => {
assert.match(source, /<DropdownMenuGroup>[\s\S]*<DropdownMenuLabel>\{t\("workspace\.switchWorkspace"\)\}<\/DropdownMenuLabel>/);
});
it("does not auto-open the rail menu from focus events", async () => {
it("does not auto-open the rail menu from focus or hover events", async () => {
assert.doesNotMatch(source, /onFocus=\{openHoverMenu\}/);
assert.doesNotMatch(source, /onBlur=\{closeHoverMenu\}/);
assert.doesNotMatch(source, /onPointerEnter=\{openHoverMenu\}/);
assert.doesNotMatch(source, /onPointerLeave=\{closeHoverMenu\}/);
assert.doesNotMatch(source, /hoverOpen/);
});
});
+1 -44
View File
@@ -2,7 +2,6 @@
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react"
import Link from "next/link"
import { useEffect, useRef, useState } from "react"
import type { ReactElement } from "react"
import { useI18n } from "@/i18n/provider"
@@ -52,44 +51,9 @@ export function WorkspaceSwitcher({
trigger,
}: WorkspaceSwitcherProps) {
const t = useI18n()
const [hoverOpen, setHoverOpen] = useState(false)
const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const isRail = variant === "rail"
const currentOption =
workspaceOptions.find((item) => item.key === currentWorkspace) ?? workspaceOptions[0]
const clearCloseTimer = () => {
if (closeTimerRef.current) {
clearTimeout(closeTimerRef.current)
closeTimerRef.current = null
}
}
const openHoverMenu = () => {
if (!isRail) {
return
}
clearCloseTimer()
setHoverOpen(true)
}
const closeHoverMenu = () => {
if (!isRail) {
return
}
clearCloseTimer()
closeTimerRef.current = setTimeout(() => {
setHoverOpen(false)
closeTimerRef.current = null
}, 120)
}
useEffect(() => {
return () => {
if (closeTimerRef.current) {
clearTimeout(closeTimerRef.current)
}
}
}, [])
const triggerClassName = cn(
"gap-2 text-left",
variant === "header" &&
@@ -134,13 +98,8 @@ export function WorkspaceSwitcher({
)
return (
<DropdownMenu
open={isRail ? hoverOpen : undefined}
onOpenChange={isRail ? setHoverOpen : undefined}
>
<DropdownMenu>
<DropdownMenuTrigger
onPointerEnter={openHoverMenu}
onPointerLeave={closeHoverMenu}
render={
trigger ?? <Button variant="ghost" className={triggerClassName} />
}
@@ -151,8 +110,6 @@ export function WorkspaceSwitcher({
align="start"
side={variant === "sidebar" || variant === "rail" ? "right" : "bottom"}
sideOffset={8}
onPointerEnter={openHoverMenu}
onPointerLeave={closeHoverMenu}
className="w-60 min-w-60"
>
<DropdownMenuGroup>