setting up dark/white mode. fixed some other small things
This commit is contained in:
+102
-26
@@ -1,15 +1,34 @@
|
||||
"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/gunbuilder";
|
||||
|
||||
type BuilderNavProps = {
|
||||
// Optional override if a parent wants to force the active category
|
||||
activeCategoryId?: string | null;
|
||||
};
|
||||
|
||||
// Helper to group categories for the dropdowns
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -17,32 +36,43 @@ function groupCategories(group: "lower" | "upper" | "accessories"): Category[] {
|
||||
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;
|
||||
|
||||
return (
|
||||
<div className="relative group">
|
||||
{/* Dropdown trigger */}
|
||||
<button
|
||||
type="button"
|
||||
className={`inline-flex items-center gap-1 font-medium tracking-wide transition-colors uppercase text-[11px] ${
|
||||
items.some(cat => cat.id === currentCategory)
|
||||
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-200 hover: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-40 min-w-[220px]">
|
||||
<ul className="py-2 text-sm">
|
||||
{items.map((cat) => {
|
||||
@@ -51,12 +81,11 @@ export function BuilderNav({ activeCategoryId }: BuilderNavProps) {
|
||||
<li key={cat.id}>
|
||||
<Link
|
||||
href={`${baseHref}/${cat.id}`}
|
||||
className={[
|
||||
"block w-full px-3 py-1.5 text-left text-sm transition-colors",
|
||||
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",
|
||||
].join(" ")}
|
||||
: "text-neutral-300 hover:bg-neutral-800 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
{cat.name}
|
||||
</Link>
|
||||
@@ -70,11 +99,23 @@ export function BuilderNav({ activeCategoryId }: BuilderNavProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-6xl mx-auto w-full"> {/* ← aligns with topnav */}
|
||||
/**
|
||||
* 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="flex items-center gap-6 border-b border-neutral-900 px-6 py-3 text-sm backdrop-blur-sm">
|
||||
{/* Primary builder link */}
|
||||
{/* ------------------------------------------------------------------ */}
|
||||
{/* LEFT SIDE: primary nav */}
|
||||
{/* ------------------------------------------------------------------ */}
|
||||
|
||||
<Link
|
||||
href={'/builder'}
|
||||
href="/builder"
|
||||
className="font-semibold text-neutral-100 hover:text-white tracking-[0.25em] uppercase text-[11px]"
|
||||
>
|
||||
Builder
|
||||
@@ -91,22 +132,57 @@ export function BuilderNav({ activeCategoryId }: BuilderNavProps) {
|
||||
Builds
|
||||
</Link>
|
||||
|
||||
{/* Right → Current filter */}
|
||||
{currentCategory && (
|
||||
<div className="ml-auto 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>
|
||||
<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"
|
||||
{/* ------------------------------------------------------------------ */}
|
||||
{/* 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"
|
||||
>
|
||||
Clear
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
<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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user