199 lines
7.0 KiB
TypeScript
199 lines
7.0 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* BuilderNav.tsx
|
|
* -----------------------------------------------------------------------------
|
|
* Top navigation for the Builder experience.
|
|
*
|
|
* MAJOR LAYOUT IDEA:
|
|
* - Keep all primary nav items on the LEFT
|
|
* - Put *everything else* (search, viewing state, theme toggle later, user menu later)
|
|
* into a RIGHT "actions cluster" using a single `ml-auto` wrapper.
|
|
*
|
|
* WHY:
|
|
* - Prevents multiple `ml-auto` elements fighting each other
|
|
* - Makes the right side easy to extend without breaking alignment
|
|
*/
|
|
|
|
import Link from "next/link";
|
|
import { useSearchParams } from "next/navigation";
|
|
import { CATEGORIES } from "@/data/gunbuilderParts";
|
|
import type { Category } from "@/types/builderSlots";
|
|
|
|
type BuilderNavProps = {
|
|
// Optional override if a parent wants to force the active category
|
|
activeCategoryId?: string | null;
|
|
};
|
|
|
|
/**
|
|
* Helper: group categories for dropdowns.
|
|
* This assumes your Category has a `group` field like "lower" | "upper" | "accessories".
|
|
*/
|
|
function groupCategories(group: "lower" | "upper" | "accessories"): Category[] {
|
|
return CATEGORIES.filter((c) => c.group === group);
|
|
}
|
|
|
|
export function BuilderNav({ activeCategoryId }: BuilderNavProps) {
|
|
const searchParams = useSearchParams();
|
|
|
|
// ---- Data / derived state --------------------------------------------------
|
|
const lower = groupCategories("lower");
|
|
const upper = groupCategories("upper");
|
|
const accessories = groupCategories("accessories");
|
|
|
|
// Determine which category is "active" so we can highlight dropdown items
|
|
// and show the "Viewing" indicator on the right.
|
|
const currentCategory =
|
|
activeCategoryId ?? searchParams.get("category") ?? undefined;
|
|
|
|
// Base route for parts browsing (used for dropdown links + "Clear")
|
|
const baseHref = "/parts";
|
|
|
|
/**
|
|
* Dropdown renderer (hover-based).
|
|
* - Uses Tailwind `group` + `group-hover` to show/hide the menu.
|
|
* - Highlights the active item.
|
|
*/
|
|
const renderDropdown = (label: string, items: Category[]) => {
|
|
if (!items.length) return null;
|
|
|
|
const platform = searchParams.get("platform");
|
|
|
|
return (
|
|
<div className="relative group">
|
|
{/* Dropdown trigger */}
|
|
<button
|
|
type="button"
|
|
className={`inline-flex items-center gap-1 font-medium tracking-wide uppercase text-[11px] transition-colors ${
|
|
items.some((cat) => cat.id === currentCategory)
|
|
? "text-white"
|
|
: "text-neutral-300 hover:text-white"
|
|
}`}
|
|
>
|
|
{label}
|
|
<span className="text-[9px]">▾</span>
|
|
</button>
|
|
|
|
{/* Dropdown menu */}
|
|
<div className="invisible opacity-0 group-hover:visible group-hover:opacity-100 transition-all absolute left-0 mt-2 rounded-md border border-neutral-800 bg-black/95 shadow-xl z-100 min-w-[220px]">
|
|
<ul className="py-2 text-sm">
|
|
{items.map((cat) => {
|
|
const isActive = currentCategory === cat.id;
|
|
return (
|
|
<li key={cat.id}>
|
|
<Link
|
|
href={{
|
|
pathname: `${baseHref}/${cat.id}`,
|
|
query: platform ? { platform } : {},
|
|
}}
|
|
className={`block w-full px-3 py-1.5 transition-colors ${
|
|
isActive
|
|
? "bg-neutral-800 text-white"
|
|
: "text-neutral-300 hover:bg-neutral-800 hover:text-white"
|
|
}`}
|
|
>
|
|
{cat.name}
|
|
</Link>
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
/**
|
|
* Outer container matches TopNav width:
|
|
* - max-w-6xl mx-auto w-full ensures consistent alignment across app
|
|
*/
|
|
<div className="max-w-6xl mx-auto w-full">
|
|
{/**
|
|
* FLEX STRATEGY:
|
|
* - Everything before the RIGHT cluster is "left side"
|
|
* - Then a single `ml-auto` cluster pushes everything else to the right
|
|
*/}
|
|
<nav className="relative z-50 flex items-center gap-6 border-b border-neutral-900 px-6 py-3 text-sm backdrop-blur-sm">
|
|
{/* ------------------------------------------------------------------ */}
|
|
{/* LEFT SIDE: primary nav */}
|
|
{/* ------------------------------------------------------------------ */}
|
|
|
|
<Link
|
|
href="/builder"
|
|
className="font-semibold text-neutral-100 hover:text-white tracking-[0.25em] uppercase text-[11px]"
|
|
>
|
|
Builder
|
|
</Link>
|
|
|
|
{renderDropdown("Lower Parts", lower)}
|
|
{renderDropdown("Upper Parts", upper)}
|
|
{renderDropdown("Accessories", accessories)}
|
|
|
|
<Link
|
|
href="/builds"
|
|
className="font-medium text-neutral-300 hover:text-white tracking-[0.25em] uppercase text-[11px]"
|
|
>
|
|
Builds
|
|
</Link>
|
|
|
|
{/* ------------------------------------------------------------------ */}
|
|
{/* RIGHT SIDE: actions + state */}
|
|
{/* IMPORTANT: this is the ONLY `ml-auto` in the nav to avoid conflicts */}
|
|
{/* ------------------------------------------------------------------ */}
|
|
<div className="ml-auto flex items-center gap-3">
|
|
{/* Search icon (placeholder action) */}
|
|
<button
|
|
type="button"
|
|
className="inline-flex h-8 w-8 items-center justify-center rounded-full border border-zinc-700 bg-zinc-900/60 text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 hover:border-zinc-500 transition-colors"
|
|
aria-label="Search (coming soon)"
|
|
title="Search (coming soon)"
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth={2}
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className="h-4 w-4"
|
|
>
|
|
<circle cx="11" cy="11" r="6" />
|
|
<line x1="16.5" y1="16.5" x2="21" y2="21" />
|
|
</svg>
|
|
</button>
|
|
|
|
{/* "Viewing" indicator (only shows when a category is active) */}
|
|
{currentCategory && (
|
|
<div className="flex items-center gap-2 text-neutral-400 text-xs">
|
|
<span className="uppercase tracking-wide text-[10px]">
|
|
Viewing
|
|
</span>
|
|
|
|
<span className="font-medium text-neutral-100">
|
|
{CATEGORIES.find((c) => c.id === currentCategory)?.name ??
|
|
currentCategory}
|
|
</span>
|
|
|
|
{/* Clears category filter -> go back to /parts */}
|
|
<Link
|
|
href={baseHref}
|
|
className="rounded border border-neutral-700 px-2 py-0.5 text-[10px] uppercase tracking-wide hover:bg-neutral-800 hover:text-white"
|
|
>
|
|
Clear
|
|
</Link>
|
|
</div>
|
|
)}
|
|
|
|
{/* FUTURE: drop in ThemeToggle, UserMenu, Notifications here */}
|
|
{/* <ThemeToggle /> */}
|
|
{/* <UserMenu /> */}
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default BuilderNav;
|