Todos os componentes

Page Layouts 03

Page Layouts 03 é 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 03 é 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.

Abrir no catálogo

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 03

examples/blocks/dashboard-ui/page-layouts/03/components/app-header.tsx
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 ThemeToggle from "./theme-toggle";

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">
          <ThemeToggle />
          <Button size="icon-sm" variant="ghost" className="relative">
            <MailIcon />
            <span className="bg-foreground absolute end-0 top-0 inline size-1.5 rounded-full" />
          </Button>
          <Button size="icon-sm" variant="ghost" className="relative">
            <BellIcon />
            <span className="bg-foreground absolute end-0 top-0 inline size-1.5 rounded-full" />
          </Button>
        </div>
        <NavUser />
      </div>
    </header>
  );
}
examples/blocks/dashboard-ui/page-layouts/03/components/app-sidebar.tsx
"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",
    isActive: true,
    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>
  );
}
examples/blocks/dashboard-ui/page-layouts/03/components/logo.tsx
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>
  );
}
examples/blocks/dashboard-ui/page-layouts/03/components/nav-main-horizontal.tsx
"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;
    isActive?: boolean;
    items?: {
      title: string;
      url: string;
      description?: string;
      icon?: LucideIcon;
      isActive?: boolean;
    }[];
  }[];
}) {
  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 border-y-2 border-y-transparent bg-transparent px-2 py-1.5 font-medium *:[svg]:-me-0.5 *:[svg]:size-3.5">
                  {link.title}
                  {link.isActive && (
                    <div className="bg-primary absolute start-2 end-2 -bottom-4 h-0.5"></div>
                  )}
                </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>
  );
}
examples/blocks/dashboard-ui/page-layouts/03/components/nav-main-vertical.tsx
"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>
      ))}
    </>
  );
}
examples/blocks/dashboard-ui/page-layouts/03/components/nav-projects.tsx
"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>
  );
}
examples/blocks/dashboard-ui/page-layouts/03/components/nav-user.tsx
"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>
  );
}
examples/blocks/dashboard-ui/page-layouts/03/components/theme-toggle.tsx
"use client";

import { useState } from "react";
import { MoonIcon, SunIcon } from "lucide-react";

import { Toggle } from "@/components/ui/toggle";

export default function ThemeToggle() {
  const [theme, setTheme] = useState<string>("light");

  return (
    <div>
      <Toggle
        variant="outline"
        className="group text-muted-foreground data-[state=on]:text-muted-foreground data-[state=on]:hover:bg-muted data-[state=on]:hover:text-foreground size-8 rounded-full border-none shadow-none data-[state=on]:bg-transparent"
        pressed={theme === "dark"}
        onPressedChange={() => setTheme((prev) => (prev === "dark" ? "light" : "dark"))}
        aria-label={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}>
        {/* Note: After dark mode implementation, rely on dark: prefix rather than group-data-[state=on]: */}
        <MoonIcon
          size={16}
          className="shrink-0 scale-0 opacity-0 transition-all group-data-[state=on]:scale-100 group-data-[state=on]:opacity-100"
          aria-hidden="true"
        />
        <SunIcon
          size={16}
          className="absolute shrink-0 scale-100 opacity-100 transition-all group-data-[state=on]:scale-0 group-data-[state=on]:opacity-0"
          aria-hidden="true"
        />
      </Toggle>
    </div>
  );
}
examples/blocks/dashboard-ui/page-layouts/03/page.tsx
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>
  );
}

Componentes parecidos