Skip to content

Commit

Permalink
feat: create navbar
Browse files Browse the repository at this point in the history
  • Loading branch information
nxhawk committed Jul 20, 2024
1 parent 0f195f2 commit f6931ed
Show file tree
Hide file tree
Showing 15 changed files with 1,787 additions and 145 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
https://github.com/gregrickaby/nextjs-github-pages/tree/main
[Setup github pages with nextjs, github action](https://github.com/gregrickaby/nextjs-github-pages/tree/main)

[Setup Webhooks Notifications with Discord Chat](https://www.youtube.com/watch?v=-KDQqWNK3Tw)

This is a [Next.js](https://nextjs.org/) project bootstrapped with
[`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
Expand Down
15 changes: 15 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,18 @@
--ring: 0 0% 83.1%;
}
}

@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
.mt-layout-screen {
@apply mt-12 md:mt-16 lg:mt-20;
}
.button-pop {
@apply active:focus:animate-button-pop active:focus:scale-[0.97] motion-reduce:animate-[button-pop_0.25s_ease-out];
}
}
17 changes: 17 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
200 changes: 199 additions & 1 deletion components/layout/header.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,213 @@
"use client";
import React from "react";
import Logo from "../logo";
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
} from "../ui/navigation-menu";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import Link from "next/link";
import { Sheet, SheetContent, SheetHeader, SheetTrigger } from "@/components/ui/sheet";
import { Button, buttonVariants } from "../ui/button";
import { usePathname } from "next/navigation";
import { useTheme } from "next-themes";
import { cn } from "@/lib/cn";
import { Menu, Moon, Sun, TabletSmartphone } from "lucide-react";
import NavbarActions from "../navbar-actions";

const navItems = [
{
title: "Trang chủ",
href: "/",
},
{
title: "Sản phẩm",
href: "/products",
items: [
{
title: "Loa dã ngoại - Xám",
href: "/products/loa-da-ngoai-xam-t-288",
description: "📟LOA XÁCH TAY Màu xám - GIÁ TẠI XƯỞNG - ĐÂU DÙNG CŨNG HAY📟",
},
{
title: "Loa dã ngoại - Đỏ",
href: "/products/loa-da-ngoai-do-t-288",
description: "📟LOA XÁCH TAY Màu đỏ - GIÁ TẠI XƯỞNG - ĐÂU DÙNG CŨNG HAY📟",
},
{
title: "Loa dã ngoại - Xanh lá",
href: "/products/loa-da-ngoai-xanh-la-t-288",
description: "📟LOA XÁCH TAY Màu xanh lá - GIÁ TẠI XƯỞNG - ĐÂU DÙNG CŨNG HAY📟",
},
],
},
{
title: "Blogs",
href: "/blogs",
},
{
title: "Liên hệ",
href: "/contact",
},
];

const Header = () => {
const currentPathname = usePathname();
const { setTheme, theme } = useTheme();

return (
<header className="sticky top-0 z-50 mx-auto w-full max-w-7xl p-1 md:p-4">
<div className="flex items-center justify-between rounded-lg bg-neutral-50/50 p-2 md:p-4 dark:bg-neutral-950/30 backdrop-blur-md">
<Logo />
Hello
<div className="hidden w-fit gap-2 lg:flex mx-auto z-10">
<NavigationMenu>
<NavigationMenuList>
{navItems.map(({ title, href, items }, index) => (
<NavigationMenuItem key={`navItems_${index}`}>
{items ? (
<>
<Link href={href} legacyBehavior passHref>
<NavigationMenuTrigger
className={buttonVariants({
variant: currentPathname === href ? "outline" : "link",
className:
currentPathname === href
? "text-lg !bg-background hover:!bg-accent hover:!text-accent-foreground"
: "text-lg !bg-transparent",
})}
>
{title}
</NavigationMenuTrigger>
</Link>
<NavigationMenuContent>
<ul className="grid w-[300px] gap-3 p-4 md:w-[500px] md:grid-cols-2">
{items.map((subItem, index) => (
<ListItem key={`subItems_${index}`} title={subItem.title} href={subItem.href}>
{subItem.description}
</ListItem>
))}
</ul>
</NavigationMenuContent>
</>
) : (
<Link href={href} legacyBehavior passHref>
<NavigationMenuLink
className={buttonVariants({
variant: currentPathname === href ? "outline" : "link",
className: "!text-lg dark:text-neutral-50 text-black",
})}
>
{title}
</NavigationMenuLink>
</Link>
)}
</NavigationMenuItem>
))}
</NavigationMenuList>
</NavigationMenu>
</div>
<div className="flex ml-auto gap-2 z-10">
<NavbarActions />
<Sheet>
<SheetTrigger
className={buttonVariants({
variant: "ghost",
className: "lg:hidden",
})}
>
<span className="sr-only">Menu</span>
<Menu className="h-6 w-6 cursor-pointer dark:text-neutral-50 lg:hidden" />
</SheetTrigger>
<SheetContent className="h-dvh bg-white dark:bg-black">
<SheetHeader>
<Logo hasText />
</SheetHeader>
<div className="!mt-4 h-[calc(100dvh-200px)] space-y-2">
<span className="font-semibold">Chủ đề:</span>
<ScrollArea className="-mx-4 !-mt-4">
<div className="flex gap-1 p-4 w-max space-x-2">
<Button variant={theme === "light" ? "secondary" : "ghost"} onClick={() => setTheme("light")}>
<Sun size={20} className="mr-1" /> Sáng
</Button>
<Button variant={theme === "dark" ? "secondary" : "ghost"} onClick={() => setTheme("dark")}>
<Moon size={20} className="mr-1" /> Tối
</Button>
<Button variant={theme === "system" ? "secondary" : "ghost"} onClick={() => setTheme("system")}>
<TabletSmartphone size={20} className="mr-1" /> Hệ thống
</Button>
</div>
<ScrollBar orientation="horizontal" />
</ScrollArea>
<ScrollArea className="h-full">
<div className="-mr-2 h-full pr-3">
{navItems.map(({ title, href, items }, index) => (
<div key={`navItems__${index}`} className="flex flex-col items-start gap-2">
<Link
className={buttonVariants({
variant: currentPathname === href ? "outline" : "link",
className: "w-fit !text-lg dark:text-neutral-50",
})}
href={href}
>
{title}
</Link>
{items && (
<div className="flex flex-col gap-3 pl-4">
{items.map((subItem, index) => (
<div key={`navItems__${index}`} className="border-l-2 pl-1 text-left">
<Link
href={subItem.href}
passHref
className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
>
<span className="block text-lg font-medium leading-none">{subItem.title}</span>
<span className="line-clamp-2 text-base leading-snug text-muted-foreground">
{subItem.description}
</span>
</Link>
</div>
))}
</div>
)}
</div>
))}
</div>
</ScrollArea>
</div>
</SheetContent>
</Sheet>
</div>
</div>
</header>
);
};

export default Header;

const ListItem = React.forwardRef<React.ElementRef<"a">, React.ComponentPropsWithoutRef<"a">>(
// eslint-disable-next-line react/prop-types
({ className, title, children, ...props }, ref) => {
return (
<li>
<NavigationMenuLink asChild>
<a
ref={ref}
className={cn(
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
className,
)}
{...props}
>
<div className="text-lg font-medium leading-none">{title}</div>
<p className="line-clamp-2 text-base leading-snug text-muted-foreground">{children}</p>
</a>
</NavigationMenuLink>
</li>
);
},
);
ListItem.displayName = "ListItem";
36 changes: 36 additions & 0 deletions components/layout/mode-toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client";

import * as React from "react";
import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";

import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

const ModeToggle = () => {
const { setTheme } = useTheme();

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>Sáng</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>Tối</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>Hệ thống</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
};

export default ModeToggle;
14 changes: 14 additions & 0 deletions components/navbar-actions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react";
import ModeToggle from "./layout/mode-toggle";

const NavbarActions = () => {
return (
<div className="items-center gap-2 flex">
<div className="hidden lg:block">
<ModeToggle />
</div>
</div>
);
};

export default NavbarActions;
47 changes: 47 additions & 0 deletions components/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from "@/lib/utils";

const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
},
);
Button.displayName = "Button";

export { Button, buttonVariants };
Loading

0 comments on commit f6931ed

Please sign in to comment.