Page Layouts 02
Page Layouts 02 é um componente para React e Tailwind CSS, da biblioteca Bund UI, com licença MIT. Copia e cola no teu projeto.
Bund UI
MIT
other
O que é
Page Layouts 02 é um componente para React e Tailwind CSS, da biblioteca Bund UI, com licença MIT. Copia e cola no teu projeto.
Como usar
Abre o componente no catálogo, carrega em copiar, e cola no teu projeto. O prompt copiado leva o código e as regras para o teu agente o reproduzir sem alterar nada.
Cinco cópias grátis por mês. Sem cartão.
Precisa de
npm i lucide-react
Licença
Este componente vem de Bund UI e é redistribuído sob a licença MIT, que permite uso comercial. O aviso de copyright viaja com o código.
Page Layouts 02
import * as React from "react";
import { SidebarTrigger } from "@/components/ui/sidebar";
import { Separator } from "@/components/ui/separator";
import { NavMainHorizontal } from "./nav-main-horizontal";
import { navigationLinks } from "./app-sidebar";
import { NavUser } from "./nav-user";
import { Logo } from "./logo";
import { BellIcon, MailIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
export function AppHeader() {
return (
<header
data-slot="header"
className="flex h-16 shrink-0 items-center justify-between gap-2 px-4 [&_[data-logo=description]]:hidden">
<div className="flex grow items-center">
<Logo />
<Separator orientation="vertical" className="mx-4 data-[orientation=vertical]:h-4" />
<SidebarTrigger className="-ml-1 md:hidden" />
<NavMainHorizontal items={navigationLinks} />
</div>
<div className="flex items-center gap-6">
<div className="flex items-center gap-2">
<Button size="icon-sm" variant="ghost">
<MailIcon />
</Button>
<Button size="icon-sm" variant="ghost" className="relative">
<BellIcon />
<Badge className="absolute -top-2 left-full min-w-5 -translate-x-1/2 px-1 text-[10px]">
5
</Badge>
</Button>
</div>
<NavUser />
</div>
</header>
);
}
"use client";
import * as React from "react";
import { BookOpenIcon, InfoIcon, LifeBuoyIcon } from "lucide-react";
import {
Sidebar,
SidebarContent,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem
} from "@/components/ui/sidebar";
import { NavMainVertical } from "./nav-main-vertical";
import { useIsMobile } from "@/hooks/use-mobile";
import { Logo } from "./logo";
export const navigationLinks = [
{
title: "Get Started",
type: "description",
items: [
{
url: "#",
title: "Dashboard",
description: "Browse all components in the library."
},
{
url: "#",
title: "Ecommerce",
description: "Learn how to use the library."
},
{
url: "#",
title: "Reports",
description: "Pre-built layouts for common use cases."
}
]
},
{
title: "Apps",
type: "simple",
items: [
{ url: "#", title: "Chats" },
{ url: "#", title: "Kanban Board" },
{ url: "#", title: "Notes" },
{ url: "#", title: "Event Calendar" }
]
},
{
title: "Pages",
type: "icon",
items: [
{ url: "#", title: "Profile Page", icon: BookOpenIcon },
{ url: "#", title: "User List", icon: LifeBuoyIcon },
{ url: "#", title: "Pricing Page", icon: InfoIcon }
]
}
];
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
const isMobile = useIsMobile();
if (!isMobile) return null;
return (
<Sidebar variant="inset" {...props}>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" asChild>
<Logo />
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<NavMainVertical items={navigationLinks} />
</SidebarContent>
</Sidebar>
);
}
import * as React from "react";
import Link from "next/link";
export function Logo() {
return (
<Link href="#" className="flex gap-3">
<img src="https://shadcnuikit.com/logo.png" className="size-8 rounded-md" alt="" />
<div
data-logo="description"
className="grid flex-1 text-left text-sm leading-tight md:hidden">
<span className="truncate font-medium">Acme Inc</span>
<span className="truncate text-xs">Enterprise</span>
</div>
</Link>
);
}
"use client";
import { type LucideIcon } from "lucide-react";
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger
} from "@/components/ui/navigation-menu";
import { cn } from "@/lib/utils";
export function NavMainHorizontal({
items
}: {
items: {
title: string;
type: string;
url?: string;
icon?: LucideIcon;
items?: {
title: string;
url: string;
description?: string;
icon?: LucideIcon;
}[];
}[];
}) {
return (
<NavigationMenu viewport={false} className="max-md:hidden">
<NavigationMenuList className="gap-2">
{items.map((link, index) => (
<NavigationMenuItem key={index}>
{link.items?.length ? (
<>
<NavigationMenuTrigger className="text-muted-foreground hover:text-primary bg-transparent px-2 py-1.5 font-medium *:[svg]:-me-0.5 *:[svg]:size-3.5">
{link.title}
</NavigationMenuTrigger>
<NavigationMenuContent className="data-[motion=from-end]:slide-in-from-right-16! data-[motion=from-start]:slide-in-from-left-16! data-[motion=to-end]:slide-out-to-right-16! data-[motion=to-start]:slide-out-to-left-16! z-50 p-1">
<ul className={cn(link.type === "description" ? "min-w-64" : "min-w-48")}>
{link.items.map((item, itemIndex) => (
<li key={itemIndex}>
<NavigationMenuLink href={item.url} className="py-1.5">
{/* Display icon if present */}
{link.type === "icon" && "icon" in item && (
<div className="flex items-center gap-2">
{item.icon && (
<item.icon
size={16}
className="text-foreground opacity-60"
aria-hidden="true"
/>
)}
<span>{item.title}</span>
</div>
)}
{/* Display label with description if present */}
{link.type === "description" && "description" in item ? (
<div className="space-y-1">
<div className="font-medium">{item.title}</div>
<p className="text-muted-foreground line-clamp-2 text-xs">
{item.description}
</p>
</div>
) : (
// Display simple label if not icon or description type
!link.type ||
(link.type !== "icon" && link.type !== "description" && (
<span>{item.title}</span>
))
)}
</NavigationMenuLink>
</li>
))}
</ul>
</NavigationMenuContent>
</>
) : (
<NavigationMenuLink
href={link.url}
className="text-muted-foreground hover:text-primary py-1.5 font-medium">
{link.title}
</NavigationMenuLink>
)}
</NavigationMenuItem>
))}
</NavigationMenuList>
</NavigationMenu>
);
}
"use client";
import { ChevronRight, type LucideIcon } from "lucide-react";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import {
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuAction,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem
} from "@/components/ui/sidebar";
export function NavMainVertical({
items
}: {
items: {
title: string;
type: string;
url?: string;
icon?: LucideIcon;
items?: {
title: string;
url: string;
description?: string;
icon?: LucideIcon;
}[];
}[];
}) {
return (
<>
{items.map((item, i) => (
<SidebarGroup>
<SidebarGroupLabel>{item.title}</SidebarGroupLabel>
<SidebarMenu>
{item.items?.length ? (
<>
{item.items?.map((subItem) => (
<SidebarMenuItem key={subItem.title}>
<SidebarMenuButton asChild>
<a href={subItem.url}>
<span>{subItem.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</>
) : null}
</SidebarMenu>
</SidebarGroup>
))}
</>
);
}
"use client";
import { type LucideIcon } from "lucide-react";
import {
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem
} from "@/components/ui/sidebar";
import Link from "next/link";
export function NavProjects({
projects
}: {
projects: {
name: string;
url: string;
icon: LucideIcon;
}[];
}) {
return (
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarMenu>
{projects.map((item) => (
<SidebarMenuItem key={item.name}>
<SidebarMenuButton asChild>
<Link href={item.url}>
<item.icon />
<span>{item.name}</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroup>
);
}
"use client";
import { BadgeCheck, Bell, CreditCard, LogOut, Sparkles } from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
const user = {
name: "shadcn",
email: "m@example.com",
avatar: "https://ui.shadcn.com/avatars/shadcn.jpg"
};
export function NavUser() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="p-0">
<Avatar>
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="min-w-56 rounded-lg" align="end" sideOffset={4}>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar>
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Sparkles />
Upgrade to Pro
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheck />
Account
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCard />
Billing
</DropdownMenuItem>
<DropdownMenuItem>
<Bell />
Notifications
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { AppSidebar } from "./components/app-sidebar";
import { AppHeader } from "./components/app-header";
export default function Page() {
return (
<SidebarProvider>
<AppSidebar />
<SidebarInset className="bg-muted/50">
<AppHeader />
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
<div className="bg-background grid min-h-[100vh] flex-1 place-items-center rounded-xl md:min-h-min">
<span className="text-muted-foreground text-sm">Content goes here</span>
</div>
</div>
</SidebarInset>
</SidebarProvider>
);
}