215 lines
6.3 KiB
TypeScript
215 lines
6.3 KiB
TypeScript
import { Home, Inbox, Settings, HelpCircle, Store } from "lucide-react";
|
|
import { Link, useRouterState } from "@tanstack/react-router";
|
|
import { useSidebar } from "@/components/ui/sidebar"; // import useSidebar hook
|
|
import { useEffect, useState, useRef } from "react";
|
|
import { useAtom } from "jotai";
|
|
import { dropdownOpenAtom } from "@/atoms/uiAtoms";
|
|
|
|
import {
|
|
Sidebar,
|
|
SidebarContent,
|
|
SidebarFooter,
|
|
SidebarGroup,
|
|
SidebarGroupContent,
|
|
SidebarMenu,
|
|
SidebarMenuButton,
|
|
SidebarMenuItem,
|
|
SidebarRail,
|
|
SidebarTrigger,
|
|
} from "@/components/ui/sidebar";
|
|
import { ChatList } from "./ChatList";
|
|
import { AppList } from "./AppList";
|
|
import { HelpDialog } from "./HelpDialog"; // Import the new dialog
|
|
import { SettingsList } from "./SettingsList";
|
|
|
|
// Menu items.
|
|
const items = [
|
|
{
|
|
title: "Apps",
|
|
to: "/",
|
|
icon: Home,
|
|
},
|
|
{
|
|
title: "Chat",
|
|
to: "/chat",
|
|
icon: Inbox,
|
|
},
|
|
{
|
|
title: "Settings",
|
|
to: "/settings",
|
|
icon: Settings,
|
|
},
|
|
{
|
|
title: "Hub",
|
|
to: "/hub",
|
|
icon: Store,
|
|
},
|
|
];
|
|
|
|
// Hover state types
|
|
type HoverState =
|
|
| "start-hover:app"
|
|
| "start-hover:chat"
|
|
| "start-hover:settings"
|
|
| "clear-hover"
|
|
| "no-hover";
|
|
|
|
export function AppSidebar() {
|
|
const { state, toggleSidebar } = useSidebar(); // retrieve current sidebar state
|
|
const [hoverState, setHoverState] = useState<HoverState>("no-hover");
|
|
const expandedByHover = useRef(false);
|
|
const [isHelpDialogOpen, setIsHelpDialogOpen] = useState(false); // State for dialog
|
|
const [isDropdownOpen] = useAtom(dropdownOpenAtom);
|
|
|
|
useEffect(() => {
|
|
if (hoverState.startsWith("start-hover") && state === "collapsed") {
|
|
expandedByHover.current = true;
|
|
toggleSidebar();
|
|
}
|
|
if (
|
|
hoverState === "clear-hover" &&
|
|
state === "expanded" &&
|
|
expandedByHover.current &&
|
|
!isDropdownOpen
|
|
) {
|
|
toggleSidebar();
|
|
expandedByHover.current = false;
|
|
setHoverState("no-hover");
|
|
}
|
|
}, [hoverState, toggleSidebar, state, setHoverState, isDropdownOpen]);
|
|
|
|
const routerState = useRouterState();
|
|
const isAppRoute =
|
|
routerState.location.pathname === "/" ||
|
|
routerState.location.pathname.startsWith("/app-details");
|
|
const isChatRoute = routerState.location.pathname === "/chat";
|
|
const isSettingsRoute = routerState.location.pathname.startsWith("/settings");
|
|
|
|
let selectedItem: string | null = null;
|
|
if (hoverState === "start-hover:app") {
|
|
selectedItem = "Apps";
|
|
} else if (hoverState === "start-hover:chat") {
|
|
selectedItem = "Chat";
|
|
} else if (hoverState === "start-hover:settings") {
|
|
selectedItem = "Settings";
|
|
} else if (state === "expanded") {
|
|
if (isAppRoute) {
|
|
selectedItem = "Apps";
|
|
} else if (isChatRoute) {
|
|
selectedItem = "Chat";
|
|
} else if (isSettingsRoute) {
|
|
selectedItem = "Settings";
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Sidebar
|
|
collapsible="icon"
|
|
onMouseLeave={() => {
|
|
if (!isDropdownOpen) {
|
|
setHoverState("clear-hover");
|
|
}
|
|
}}
|
|
>
|
|
<SidebarContent className="overflow-hidden">
|
|
<div className="flex mt-8">
|
|
{/* Left Column: Menu items */}
|
|
<div className="">
|
|
<SidebarTrigger
|
|
onMouseEnter={() => {
|
|
setHoverState("clear-hover");
|
|
}}
|
|
/>
|
|
<AppIcons onHoverChange={setHoverState} />
|
|
</div>
|
|
{/* Right Column: Chat List Section */}
|
|
<div className="w-[240px]">
|
|
<AppList show={selectedItem === "Apps"} />
|
|
<ChatList show={selectedItem === "Chat"} />
|
|
<SettingsList show={selectedItem === "Settings"} />
|
|
</div>
|
|
</div>
|
|
</SidebarContent>
|
|
|
|
<SidebarFooter>
|
|
<SidebarMenu>
|
|
<SidebarMenuItem>
|
|
{/* Change button to open dialog instead of linking */}
|
|
<SidebarMenuButton
|
|
size="sm"
|
|
className="font-medium w-14 flex flex-col items-center gap-1 h-14 mb-2 rounded-2xl"
|
|
onClick={() => setIsHelpDialogOpen(true)} // Open dialog on click
|
|
>
|
|
<HelpCircle className="h-5 w-5" />
|
|
<span className={"text-xs"}>Help</span>
|
|
</SidebarMenuButton>
|
|
<HelpDialog
|
|
isOpen={isHelpDialogOpen}
|
|
onClose={() => setIsHelpDialogOpen(false)}
|
|
/>
|
|
</SidebarMenuItem>
|
|
</SidebarMenu>
|
|
</SidebarFooter>
|
|
|
|
<SidebarRail />
|
|
</Sidebar>
|
|
);
|
|
}
|
|
|
|
function AppIcons({
|
|
onHoverChange,
|
|
}: {
|
|
onHoverChange: (state: HoverState) => void;
|
|
}) {
|
|
const routerState = useRouterState();
|
|
const pathname = routerState.location.pathname;
|
|
|
|
return (
|
|
// When collapsed: only show the main menu
|
|
<SidebarGroup className="pr-0">
|
|
{/* <SidebarGroupLabel>Dyad</SidebarGroupLabel> */}
|
|
|
|
<SidebarGroupContent>
|
|
<SidebarMenu>
|
|
{items.map((item) => {
|
|
const isActive =
|
|
(item.to === "/" && pathname === "/") ||
|
|
(item.to !== "/" && pathname.startsWith(item.to));
|
|
|
|
return (
|
|
<SidebarMenuItem key={item.title}>
|
|
<SidebarMenuButton
|
|
asChild
|
|
size="sm"
|
|
className="font-medium w-14"
|
|
>
|
|
<Link
|
|
to={item.to}
|
|
className={`flex flex-col items-center gap-1 h-14 mb-2 rounded-2xl ${
|
|
isActive ? "bg-sidebar-accent" : ""
|
|
}`}
|
|
onMouseEnter={() => {
|
|
if (item.title === "Apps") {
|
|
onHoverChange("start-hover:app");
|
|
} else if (item.title === "Chat") {
|
|
onHoverChange("start-hover:chat");
|
|
} else if (item.title === "Settings") {
|
|
onHoverChange("start-hover:settings");
|
|
}
|
|
}}
|
|
>
|
|
<div className="flex flex-col items-center gap-1">
|
|
<item.icon className="h-5 w-5" />
|
|
<span className={"text-xs"}>{item.title}</span>
|
|
</div>
|
|
</Link>
|
|
</SidebarMenuButton>
|
|
</SidebarMenuItem>
|
|
);
|
|
})}
|
|
</SidebarMenu>
|
|
</SidebarGroupContent>
|
|
</SidebarGroup>
|
|
);
|
|
}
|