feat: 사이드바 토글 기능 적용
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
DropdownMenuItem,
|
||||
} from "../dropdown-menu";
|
||||
import { Button } from "../button";
|
||||
import { SidebarTrigger } from "../sidebar";
|
||||
|
||||
const { SITE_ID } = getConfig();
|
||||
|
||||
@@ -91,7 +92,8 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) {
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-30 box-border flex h-[5rem] w-full items-center justify-between border-b border-solid border-border bg-background">
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center px-6">
|
||||
<SidebarTrigger className="size-12 !bg-transparent p-2 hover:!bg-accent [&>svg]:!size-full [&>svg]:text-foreground" />
|
||||
<div className="MobMenuBtn">
|
||||
<img src={images.menuBtn} />
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,9 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarMenuSub,
|
||||
SidebarMenuSubButton,
|
||||
SidebarMenuSubItem,
|
||||
useSidebar,
|
||||
} from "../sidebar";
|
||||
import {
|
||||
Collapsible,
|
||||
@@ -79,7 +82,14 @@ export function LNB({
|
||||
}
|
||||
}, [menuList, currentPath]);
|
||||
|
||||
function MenuItem({ item }: { item: MenuItem }) {
|
||||
function MenuItem({
|
||||
item,
|
||||
isSubMenu,
|
||||
}: {
|
||||
item: MenuItem;
|
||||
isSubMenu?: boolean;
|
||||
}) {
|
||||
const { open: isSidebarOpen } = useSidebar();
|
||||
const isMatched = isPathMatched(item.urlPath);
|
||||
|
||||
const handleClick = () => {
|
||||
@@ -91,24 +101,37 @@ export function LNB({
|
||||
return <MenuGroup item={item} />;
|
||||
}
|
||||
|
||||
const MenuItem = isSubMenu ? SidebarMenuSubItem : SidebarMenuItem;
|
||||
const MenuButton = isSubMenu ? SidebarMenuSubButton : SidebarMenuButton;
|
||||
|
||||
return (
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
<MenuItem>
|
||||
<MenuButton
|
||||
className={cn(
|
||||
"h-auto py-4 text-[1.4rem] text-label transition-colors hover:bg-accent",
|
||||
"h-auto cursor-pointer py-4 text-[1.4rem] text-label transition-colors hover:bg-accent",
|
||||
isMatched && "bg-accent !text-primary",
|
||||
!isSidebarOpen && "justify-center",
|
||||
)}
|
||||
data-matched={isMatched}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<i className={cn("diveicon ml-2", `di-${item.iconNm}`)} />
|
||||
{item.menuKornNm}
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
<i
|
||||
className={cn(
|
||||
"diveicon",
|
||||
`di-${item.iconNm}`,
|
||||
isSidebarOpen && "ml-2",
|
||||
)}
|
||||
/>
|
||||
<span className={cn("whitespace-nowrap", !isSidebarOpen && "hidden")}>
|
||||
{item.menuKornNm}
|
||||
</span>
|
||||
</MenuButton>
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
function MenuGroup({ item }: { item: MenuItem }) {
|
||||
const { open: isSidebarOpen } = useSidebar();
|
||||
const isOpen = openMenuIds.includes(item.menuCd);
|
||||
|
||||
const handleOpenChange = (open: boolean) => {
|
||||
@@ -127,30 +150,48 @@ export function LNB({
|
||||
};
|
||||
|
||||
return (
|
||||
<Collapsible open={isOpen} onOpenChange={handleOpenChange}>
|
||||
<Collapsible
|
||||
open={isOpen}
|
||||
onOpenChange={handleOpenChange}
|
||||
className="group/collapsible"
|
||||
>
|
||||
<SidebarMenuItem>
|
||||
<CollapsibleTrigger asChild>
|
||||
<div className="group">
|
||||
<SidebarMenuButton className="group/menu-button flex h-auto items-center justify-between py-4 pr-2 text-[1.4rem] group-data-[state=open]:bg-transparent">
|
||||
<div className="flex items-center gap-2">
|
||||
<i
|
||||
className={cn(
|
||||
"diveicon ml-2 text-label",
|
||||
`di-${item.iconNm}`,
|
||||
)}
|
||||
/>
|
||||
<span className="text-label group-hover/menu-button:text-foreground">
|
||||
{item.menuKornNm}
|
||||
</span>
|
||||
</div>
|
||||
<ChevronRight className="!size-6 text-label group-data-[state=open]:rotate-90" />
|
||||
</SidebarMenuButton>
|
||||
</div>
|
||||
<SidebarMenuButton
|
||||
className={cn(
|
||||
"flex h-auto items-center py-4 pr-2 text-[1.4rem] group-data-[state=open]/collapsible:bg-transparent",
|
||||
!isSidebarOpen && "justify-center",
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<i
|
||||
className={cn(
|
||||
"diveicon text-label",
|
||||
`di-${item.iconNm}`,
|
||||
isSidebarOpen && "ml-2",
|
||||
)}
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
"whitespace-nowrap text-label group-hover/menu-button:text-foreground",
|
||||
!isSidebarOpen && "hidden",
|
||||
)}
|
||||
>
|
||||
{item.menuKornNm}
|
||||
</span>
|
||||
</div>
|
||||
<ChevronRight
|
||||
className={cn(
|
||||
"ml-auto !size-6 text-label group-data-[state=open]/collapsible:rotate-90",
|
||||
!isSidebarOpen && "hidden",
|
||||
)}
|
||||
/>
|
||||
</SidebarMenuButton>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<SidebarMenuSub className="mr-0">
|
||||
<SidebarMenuSub>
|
||||
{item.children?.map((child) => (
|
||||
<MenuItem key={child.menuCd} item={child} />
|
||||
<MenuItem key={child.menuCd} item={child} isSubMenu />
|
||||
))}
|
||||
</SidebarMenuSub>
|
||||
</CollapsibleContent>
|
||||
@@ -160,7 +201,10 @@ export function LNB({
|
||||
}
|
||||
|
||||
return (
|
||||
<Sidebar className="border-border bg-background px-4 py-[6rem]">
|
||||
<Sidebar
|
||||
collapsible="icon"
|
||||
className="border-border bg-background px-4 py-[6rem]"
|
||||
>
|
||||
<SidebarContent className="bg-background">
|
||||
<SidebarMenu className="gap-0">
|
||||
{menuList?.map((item) => <MenuItem key={item.menuCd} item={item} />)}
|
||||
|
||||
@@ -28,7 +28,7 @@ const SIDEBAR_COOKIE_NAME = "sidebar_state";
|
||||
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
||||
const SIDEBAR_WIDTH = "22rem";
|
||||
const SIDEBAR_WIDTH_MOBILE = "18rem";
|
||||
const SIDEBAR_WIDTH_ICON = "3rem";
|
||||
const SIDEBAR_WIDTH_ICON = "6rem";
|
||||
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
||||
|
||||
type SidebarContextProps = {
|
||||
@@ -530,7 +530,7 @@ const SidebarMenuItem = React.forwardRef<
|
||||
SidebarMenuItem.displayName = "SidebarMenuItem";
|
||||
|
||||
const sidebarMenuButtonVariants = cva(
|
||||
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-16 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -23,12 +23,19 @@
|
||||
background: none !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
[data-sidebar="menu-button"]:hover {
|
||||
[data-sidebar="trigger"]:hover,
|
||||
[data-sidebar="menu-button"]:hover,
|
||||
[data-sidebar="menu-sub-button"]:hover {
|
||||
background: rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
[data-sidebar="menu-button"][data-matched="true"] {
|
||||
[data-sidebar="trigger"][data-matched="true"],
|
||||
[data-sidebar="menu-button"][data-matched="true"],
|
||||
[data-sidebar="menu-sub-button"][data-matched="true"] {
|
||||
background: rgba(0, 0, 0, 0.2) !important;
|
||||
}
|
||||
[data-sidebar="trigger"] > svg {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme="red"] {
|
||||
@@ -58,11 +65,18 @@
|
||||
background: none !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
[data-sidebar="menu-button"]:hover {
|
||||
[data-sidebar="trigger"]:hover,
|
||||
[data-sidebar="menu-button"]:hover,
|
||||
[data-sidebar="menu-sub-button"]:hover {
|
||||
background: rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
[data-sidebar="menu-button"][data-matched="true"] {
|
||||
[data-sidebar="trigger"][data-matched="true"],
|
||||
[data-sidebar="menu-button"][data-matched="true"],
|
||||
[data-sidebar="menu-sub-button"][data-matched="true"] {
|
||||
background: rgba(0, 0, 0, 0.2) !important;
|
||||
}
|
||||
[data-sidebar="trigger"] > svg {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user