SidebarSidebar
워크스페이스, 메뉴, 펼쳐지는 프로젝트, 계정을 담는 왼쪽 패널입니다.The left panel with the workspace, menu, expandable projects, and account.
내비게이션Navigation
import { formatShortcut, HoronIcon, HoronWordmark, Menu, MenuContent, MenuItem, MenuLabel, MenuSeparator, MenuTrigger, Sidebar, SidebarAccount, SidebarAddButton, SidebarGroup, SidebarHeader, SidebarItem, SidebarProject, SidebarProjectMore, SidebarProjectTile, SidebarSpacer, SidebarWorkspaceSwitch, usePlatform, WorkspaceMenu, } from "@horon-ai/react"; import { Archive, Bell, CalendarDots, ChartLineUp, ChatCircle, ClipboardText, Code, Copy, Files, Drop, FileText, Flask, GearSix, MagnifyingGlass, Megaphone, PenNib, PencilSimple, Question, Repeat, SignOut, Trash, UserPlus, TreeStructure, UsersThree, } from "@phosphor-icons/react"; import { useState, type ReactNode } from "react"; const workspaces = [ { id: "hq", name: "Horon HQ", meta: "12 members" }, { id: "labs", name: "Horon Labs", meta: "4 members" }, { id: "personal", name: "Personal", meta: "Just you" }, ]; const projects: { name: string; icon: ReactNode; tile?: string; children?: { icon: ReactNode; name: string }[] }[] = [ { name: "Horon app", icon: <Code />, tile: "bg-sky-500", children: [ { icon: <ChatCircle />, name: "API rate limits" }, { icon: <FileText />, name: "Release notes v0.4" }, { icon: <ChatCircle />, name: "Bug triage" }, ], }, { name: "Q4 roadmap", icon: <ClipboardText /> }, { name: "Launch campaign", icon: <Megaphone />, tile: "bg-sky-700" }, { name: "Brand refresh", icon: <PenNib />, tile: "bg-sky-500" }, { name: "Investor update", icon: <ChartLineUp /> }, { name: "User research", icon: <Flask />, tile: "bg-sky-700" }, { name: "Q4 hiring", icon: <UsersThree />, tile: "bg-sky-500" }, ]; /** Figma `Project menu` (314:9199): the row's width, 5px under the row (13px under the 16px More button). */ function ProjectMenu() { return ( <Menu> <MenuTrigger render={<SidebarProjectMore />} /> <MenuContent surface="solid" align="end" alignOffset={-10} sideOffset={13} className="w-64"> <MenuItem icon={<PencilSimple />}>Rename</MenuItem> <MenuItem icon={<GearSix />}>Project settings</MenuItem> <MenuItem icon={<Copy />}>Duplicate</MenuItem> <MenuItem icon={<Archive />}>Archive</MenuItem> <MenuSeparator /> <MenuItem icon={<Trash />} danger> Delete project </MenuItem> </MenuContent> </Menu> ); } /** The sidebar shown on this page; the Flue page reuses it with `background` laid under its glass. */ export function DemoSidebar({ background }: { background?: ReactNode }) { const [workspace, setWorkspace] = useState("hq"); const current = workspaces.find((item) => item.id === workspace) ?? workspaces[0]; const { os } = usePlatform(); return ( <Sidebar resizable className={background ? "isolate h-190" : "h-190"}> {background} <SidebarHeader initial={current.name.charAt(0)} name={current.name} switchSlot={ <WorkspaceMenu workspaces={workspaces} value={workspace} onValueChange={setWorkspace} trigger={<SidebarWorkspaceSwitch initial={current.name.charAt(0)} name={current.name} />} /> } /> <SidebarItem icon={<HoronIcon />} current aria-label="Horon"> <HoronWordmark height={9.6} /> </SidebarItem> <SidebarItem icon={<Bell />} count={12}> Inbox </SidebarItem> <SidebarItem icon={<Files />}>Artifacts</SidebarItem> <SidebarItem icon={<CalendarDots />}>Schedule</SidebarItem> <SidebarItem icon={<Repeat />}>Routines</SidebarItem> <SidebarItem icon={<TreeStructure />}>Workflows</SidebarItem> <SidebarItem icon={<MagnifyingGlass />}>Search</SidebarItem> <SidebarGroup label="Projects"> {projects.map((project) => ( <SidebarProject key={project.name} defaultOpen={project.children !== undefined} icon={<SidebarProjectTile className={project.tile}>{project.icon}</SidebarProjectTile>} label={project.name} actions={<ProjectMenu />} > {(project.children ?? [{ icon: <ChatCircle />, name: "Kickoff notes" }]).map((child) => ( <SidebarItem key={child.name} icon={child.icon}> {child.name} </SidebarItem> ))} </SidebarProject> ))} </SidebarGroup> <SidebarAddButton>Add project</SidebarAddButton> <SidebarSpacer /> <Menu> <MenuTrigger render={<SidebarAccount initials="AL" name="Alpox" email="[email protected]" />} /> <MenuContent surface="solid" side="top" className="w-64"> <MenuLabel className="pb-1.5 text-body-sm">[email protected]</MenuLabel> <MenuItem icon={<GearSix />} trailing={formatShortcut("Mod+,", os)}> Settings </MenuItem> <MenuItem icon={<Drop />}>Usage</MenuItem> <MenuItem icon={<UserPlus />}>Invite friends</MenuItem> <MenuSeparator /> <MenuItem icon={<Question />}>Help</MenuItem> <MenuItem icon={<SignOut />}>Log out</MenuItem> </MenuContent> </Menu> </Sidebar> ); } export default function SidebarDemo() { return ( <> {/* Left-aligned so the right edge follows the pointer while resizing. */} <div className="flex w-full"> <DemoSidebar /> </div> </> ); }