Skip to content

Commit

Permalink
Collapsible button now always active when submenus inside it active
Browse files Browse the repository at this point in the history
  • Loading branch information
Rot4tion committed Oct 11, 2024
1 parent 6a733dc commit a51baea
Show file tree
Hide file tree
Showing 3 changed files with 6 additions and 6 deletions.
4 changes: 2 additions & 2 deletions public/registry/shadcn-sidebar.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
},
{
"type": "registry:hook",
"content": "import { useState, useEffect } from \"react\";\r\n/**\r\n * This hook fix hydration when use persist to save hook data to localStorage\r\n */\r\nexport const useStore = <T, F>(\r\n store: (callback: (state: T) => unknown) => unknown,\r\n callback: (state: T) => F\r\n) => {\r\n const result = store(callback) as F;\r\n const [data, setData] = useState<F>();\r\n\r\n useEffect(() => {\r\n setData(result);\r\n }, [result]);\r\n\r\n return data;\r\n};\r\n",
"content": "import { useState, useEffect } from \"react\";\n/**\n * This hook fix hydration when use persist to save hook data to localStorage\n */\nexport const useStore = <T, F>(\n store: (callback: (state: T) => unknown) => unknown,\n callback: (state: T) => F\n) => {\n const result = store(callback) as F;\n const [data, setData] = useState<F>();\n\n useEffect(() => {\n setData(result);\n }, [result]);\n\n return data;\n};\n",
"path": "hooks/use-store.ts",
"target": "hooks/use-store.ts"
},
Expand All @@ -85,7 +85,7 @@
},
{
"type": "registry:component",
"content": "\"use client\";\n\nimport Link from \"next/link\";\nimport { useState } from \"react\";\nimport { ChevronDown, Dot, LucideIcon } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { DropdownMenuArrow } from \"@radix-ui/react-dropdown-menu\";\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger\n} from \"@/components/ui/collapsible\";\nimport {\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n TooltipProvider\n} from \"@/components/ui/tooltip\";\nimport {\n DropdownMenu,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuSeparator\n} from \"@/components/ui/dropdown-menu\";\nimport { usePathname } from \"next/navigation\";\n\ntype Submenu = {\n href: string;\n label: string;\n active?: boolean;\n};\n\ninterface CollapseMenuButtonProps {\n icon: LucideIcon;\n label: string;\n active: boolean;\n submenus: Submenu[];\n isOpen: boolean | undefined;\n}\n\nexport function CollapseMenuButton({\n icon: Icon,\n label,\n active,\n submenus,\n isOpen\n}: CollapseMenuButtonProps) {\n const pathname = usePathname();\n const isSubmenuActive = submenus.some((submenu) =>\n submenu.active == undefined ? submenu.href === pathname : submenu.active\n );\n const [isCollapsed, setIsCollapsed] = useState<boolean>(isSubmenuActive);\n\n return isOpen ? (\n <Collapsible\n open={isCollapsed}\n onOpenChange={setIsCollapsed}\n className=\"w-full\"\n >\n <CollapsibleTrigger\n className=\"[&[data-state=open]>div>div>svg]:rotate-180 mb-1\"\n asChild\n >\n <Button\n variant={active ? \"secondary\" : \"ghost\"}\n className=\"w-full justify-start h-10\"\n >\n <div className=\"w-full items-center flex justify-between\">\n <div className=\"flex items-center\">\n <span className=\"mr-4\">\n <Icon size={18} />\n </span>\n <p\n className={cn(\n \"max-w-[150px] truncate\",\n isOpen\n ? \"translate-x-0 opacity-100\"\n : \"-translate-x-96 opacity-0\"\n )}\n >\n {label}\n </p>\n </div>\n <div\n className={cn(\n \"whitespace-nowrap\",\n isOpen\n ? \"translate-x-0 opacity-100\"\n : \"-translate-x-96 opacity-0\"\n )}\n >\n <ChevronDown\n size={18}\n className=\"transition-transform duration-200\"\n />\n </div>\n </div>\n </Button>\n </CollapsibleTrigger>\n <CollapsibleContent className=\"overflow-hidden data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down\">\n {submenus.map(({ href, label, active }, index) => (\n <Button\n key={index}\n variant={\n (active === undefined && pathname === href) || active\n ? \"secondary\"\n : \"ghost\"\n }\n className=\"w-full justify-start h-10 mb-1\"\n asChild\n >\n <Link href={href}>\n <span className=\"mr-4 ml-2\">\n <Dot size={18} />\n </span>\n <p\n className={cn(\n \"max-w-[170px] truncate\",\n isOpen\n ? \"translate-x-0 opacity-100\"\n : \"-translate-x-96 opacity-0\"\n )}\n >\n {label}\n </p>\n </Link>\n </Button>\n ))}\n </CollapsibleContent>\n </Collapsible>\n ) : (\n <DropdownMenu>\n <TooltipProvider disableHoverableContent>\n <Tooltip delayDuration={100}>\n <TooltipTrigger asChild>\n <DropdownMenuTrigger asChild>\n <Button\n variant={active ? \"secondary\" : \"ghost\"}\n className=\"w-full justify-start h-10 mb-1\"\n >\n <div className=\"w-full items-center flex justify-between\">\n <div className=\"flex items-center\">\n <span className={cn(isOpen === false ? \"\" : \"mr-4\")}>\n <Icon size={18} />\n </span>\n <p\n className={cn(\n \"max-w-[200px] truncate\",\n isOpen === false ? \"opacity-0\" : \"opacity-100\"\n )}\n >\n {label}\n </p>\n </div>\n </div>\n </Button>\n </DropdownMenuTrigger>\n </TooltipTrigger>\n <TooltipContent side=\"right\" align=\"start\" alignOffset={2}>\n {label}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <DropdownMenuContent side=\"right\" sideOffset={25} align=\"start\">\n <DropdownMenuLabel className=\"max-w-[190px] truncate\">\n {label}\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n {submenus.map(({ href, label, active }, index) => (\n <DropdownMenuItem key={index} asChild>\n <Link\n className={`cursor-pointer ${\n ((active === undefined && pathname === href) || active) &&\n \"bg-secondary\"\n }`}\n href={href}\n >\n <p className=\"max-w-[180px] truncate\">{label}</p>\n </Link>\n </DropdownMenuItem>\n ))}\n <DropdownMenuArrow className=\"fill-border\" />\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n",
"content": "\"use client\";\n\nimport Link from \"next/link\";\nimport { useState } from \"react\";\nimport { ChevronDown, Dot, LucideIcon } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { DropdownMenuArrow } from \"@radix-ui/react-dropdown-menu\";\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger\n} from \"@/components/ui/collapsible\";\nimport {\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n TooltipProvider\n} from \"@/components/ui/tooltip\";\nimport {\n DropdownMenu,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuSeparator\n} from \"@/components/ui/dropdown-menu\";\nimport { usePathname } from \"next/navigation\";\n\ntype Submenu = {\n href: string;\n label: string;\n active?: boolean;\n};\n\ninterface CollapseMenuButtonProps {\n icon: LucideIcon;\n label: string;\n active: boolean;\n submenus: Submenu[];\n isOpen: boolean | undefined;\n}\n\nexport function CollapseMenuButton({\n icon: Icon,\n label,\n active,\n submenus,\n isOpen\n}: CollapseMenuButtonProps) {\n const pathname = usePathname();\n const isSubmenuActive = submenus.some((submenu) =>\n submenu.active === undefined ? submenu.href === pathname : submenu.active\n );\n const [isCollapsed, setIsCollapsed] = useState<boolean>(isSubmenuActive);\n\n return isOpen ? (\n <Collapsible\n open={isCollapsed}\n onOpenChange={setIsCollapsed}\n className=\"w-full\"\n >\n <CollapsibleTrigger\n className=\"[&[data-state=open]>div>div>svg]:rotate-180 mb-1\"\n asChild\n >\n <Button\n variant={isSubmenuActive ? \"secondary\" : \"ghost\"}\n className=\"w-full justify-start h-10\"\n >\n <div className=\"w-full items-center flex justify-between\">\n <div className=\"flex items-center\">\n <span className=\"mr-4\">\n <Icon size={18} />\n </span>\n <p\n className={cn(\n \"max-w-[150px] truncate\",\n isOpen\n ? \"translate-x-0 opacity-100\"\n : \"-translate-x-96 opacity-0\"\n )}\n >\n {label}\n </p>\n </div>\n <div\n className={cn(\n \"whitespace-nowrap\",\n isOpen\n ? \"translate-x-0 opacity-100\"\n : \"-translate-x-96 opacity-0\"\n )}\n >\n <ChevronDown\n size={18}\n className=\"transition-transform duration-200\"\n />\n </div>\n </div>\n </Button>\n </CollapsibleTrigger>\n <CollapsibleContent className=\"overflow-hidden data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down\">\n {submenus.map(({ href, label, active }, index) => (\n <Button\n key={index}\n variant={\n (active === undefined && pathname === href) || active\n ? \"secondary\"\n : \"ghost\"\n }\n className=\"w-full justify-start h-10 mb-1\"\n asChild\n >\n <Link href={href}>\n <span className=\"mr-4 ml-2\">\n <Dot size={18} />\n </span>\n <p\n className={cn(\n \"max-w-[170px] truncate\",\n isOpen\n ? \"translate-x-0 opacity-100\"\n : \"-translate-x-96 opacity-0\"\n )}\n >\n {label}\n </p>\n </Link>\n </Button>\n ))}\n </CollapsibleContent>\n </Collapsible>\n ) : (\n <DropdownMenu>\n <TooltipProvider disableHoverableContent>\n <Tooltip delayDuration={100}>\n <TooltipTrigger asChild>\n <DropdownMenuTrigger asChild>\n <Button\n variant={active ? \"secondary\" : \"ghost\"}\n className=\"w-full justify-start h-10 mb-1\"\n >\n <div className=\"w-full items-center flex justify-between\">\n <div className=\"flex items-center\">\n <span className={cn(isOpen === false ? \"\" : \"mr-4\")}>\n <Icon size={18} />\n </span>\n <p\n className={cn(\n \"max-w-[200px] truncate\",\n isOpen === false ? \"opacity-0\" : \"opacity-100\"\n )}\n >\n {label}\n </p>\n </div>\n </div>\n </Button>\n </DropdownMenuTrigger>\n </TooltipTrigger>\n <TooltipContent side=\"right\" align=\"start\" alignOffset={2}>\n {label}\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <DropdownMenuContent side=\"right\" sideOffset={25} align=\"start\">\n <DropdownMenuLabel className=\"max-w-[190px] truncate\">\n {label}\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n {submenus.map(({ href, label, active }, index) => (\n <DropdownMenuItem key={index} asChild>\n <Link\n className={`cursor-pointer ${\n ((active === undefined && pathname === href) || active) &&\n \"bg-secondary\"\n }`}\n href={href}\n >\n <p className=\"max-w-[180px] truncate\">{label}</p>\n </Link>\n </DropdownMenuItem>\n ))}\n <DropdownMenuArrow className=\"fill-border\" />\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n",
"path": "components/admin-panel/collapse-menu-button.tsx",
"target": "components/admin-panel/collapse-menu-button.tsx"
},
Expand Down
4 changes: 2 additions & 2 deletions src/app/(demo)/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ export default function DashboardPage() {
<TooltipTrigger asChild>
<div className="flex items-center space-x-2">
<Switch
id="is-hover-open"
id="disable-sidebar"
onCheckedChange={(x) => setSettings({ disabled: x })}
checked={settings.disabled}
/>
<Label htmlFor="is-hover-open">Disable Sidebar</Label>
<Label htmlFor="disable-sidebar">Disable Sidebar</Label>
</div>
</TooltipTrigger>
<TooltipContent>
Expand Down
4 changes: 2 additions & 2 deletions src/components/admin-panel/collapse-menu-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function CollapseMenuButton({
}: CollapseMenuButtonProps) {
const pathname = usePathname();
const isSubmenuActive = submenus.some((submenu) =>
submenu.active == undefined ? submenu.href === pathname : submenu.active
submenu.active === undefined ? submenu.href === pathname : submenu.active
);
const [isCollapsed, setIsCollapsed] = useState<boolean>(isSubmenuActive);

Expand All @@ -66,7 +66,7 @@ export function CollapseMenuButton({
asChild
>
<Button
variant={active ? "secondary" : "ghost"}
variant={isSubmenuActive ? "secondary" : "ghost"}
className="w-full justify-start h-10"
>
<div className="w-full items-center flex justify-between">
Expand Down

0 comments on commit a51baea

Please sign in to comment.