feat: implement dropdown menu in NavMain for collapsed state with hover functionality

This commit is contained in:
mlogclub
2026-05-30 20:58:25 +08:00
parent 57995e352b
commit b1b632e28b
2 changed files with 62 additions and 0 deletions
+9
View File
@@ -9,3 +9,12 @@ test("dashboard nav items expose full labels when visual text is truncated", ()
assert.match(source, /tooltip=\{item\.title\}/)
assert.match(source, /<span\s+title=\{item\.title\}>\{item\.title\}<\/span>/)
})
test("collapsed dashboard nav sections expose submenu flyouts on hover", () => {
assert.match(source, /useSidebar/)
assert.match(source, /state === "collapsed" && !isMobile/)
assert.match(source, /<DropdownMenuTrigger\s+openOnHover/)
assert.match(source, /render=\{<SidebarMenuButton \/>\}/)
assert.match(source, /<DropdownMenuContent[\s\S]*side="right"/)
assert.match(source, /<DropdownMenuGroup>[\s\S]*<DropdownMenuLabel/)
})
+53
View File
@@ -15,6 +15,14 @@ import {
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
SidebarGroup,
SidebarMenu,
@@ -23,6 +31,7 @@ import {
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
useSidebar,
} from "@/components/ui/sidebar"
export function NavMain({
@@ -41,6 +50,7 @@ export function NavMain({
}>
}) {
const pathname = usePathname()
const { isMobile, state } = useSidebar()
const storageKey = getDashboardNavSectionStorageKey(sectionKey)
const [open, setOpen] = useState(() => {
if (typeof window === "undefined") {
@@ -54,6 +64,49 @@ export function NavMain({
window.localStorage.setItem(storageKey, String(nextOpen))
}
if (state === "collapsed" && !isMobile) {
return (
<SidebarGroup className="px-2 py-0 first:pt-2 last:pb-2">
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger
openOnHover
render={<SidebarMenuButton />}
>
{icon}
<span title={title}>{title}</span>
</DropdownMenuTrigger>
<DropdownMenuContent
side="right"
align="start"
sideOffset={8}
className="w-56"
>
<DropdownMenuGroup>
<DropdownMenuLabel className="truncate" title={title}>
{title}
</DropdownMenuLabel>
{items.map((item) => (
<DropdownMenuItem
key={item.title}
render={<Link href={item.url} />}
className="cursor-pointer"
>
<span className="truncate" title={item.title}>
{item.title}
</span>
</DropdownMenuItem>
))}
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroup>
)
}
return (
<SidebarGroup className="px-2 py-0 first:pt-2 last:pb-2">
<SidebarMenu>