Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/app/benchmarks/category/[cat]/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -102,7 +102,11 @@ export default async function BenchmarkCategoryPage({
{entry.description}
</p>
</header>
<BenchmarkGrid benchmarks={benchmarks} lockedCategory={entry.label} />
<BenchmarkGrid
benchmarks={benchmarks}
lockedCategory={entry.label}
allCategories={Array.from(new Set(all.map((b) => b.category)))}
/>
</article>
);
}
94 changes: 52 additions & 42 deletions src/components/benchmark-grid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,12 +28,17 @@ import { categorySlugFromLabel } from "@/lib/categories";
export function BenchmarkGrid({
benchmarks,
lockedCategory = null,
allCategories,
}: {
benchmarks: Benchmark[];
/** When set, force the grid to this category and hide the filter pills.
* Used by the per-category hub routes so the rendered DOM matches the
* URL and the in-page filter UI doesn't conflict with the route. */
/** When set, force the grid to this category. The pill row still
* renders so users can jump to other category hubs via the same UI
* they had on the /benchmarks root. */
lockedCategory?: string | null;
/** Full category list to show in the pills, used by the category hub
* routes where the `benchmarks` prop is pre-filtered to a single
* category. If unset, the grid derives pills from `benchmarks`. */
allCategories?: string[];
}) {
const [query, setQuery] = useState("");
const [activeCategory, setActiveCategory] = useState<string | null>(
Expand All@@ -43,6 +48,7 @@ export function BenchmarkGrid({
const q = query.trim().toLowerCase();

const categories = useMemo(() => {
if (allCategories && allCategories.length > 0) return allCategories;
const seen = new Set<string>();
const list: string[] = [];
for (const b of benchmarks) {
Expand All@@ -52,7 +58,7 @@ export function BenchmarkGrid({
}
}
return list;
}, [benchmarks]);
}, [benchmarks, allCategories]);

const filtered = useMemo(() => {
return benchmarks.filter((b) => {
Expand All@@ -71,48 +77,52 @@ export function BenchmarkGrid({
});
}, [benchmarks, q, activeCategory]);

const showFilterPills = !lockedCategory;

// Pills always render. When the grid is locked to a category route,
// pill clicks navigate (no preventDefault) so the user moves between
// /benchmarks/category/<slug> URLs. On the unlocked /benchmarks page
// they filter in place for a snappier UX without a navigation
// roundtrip.
return (
<div>
{/* Filter row */}
<div className="mb-8 flex flex-col sm:flex-row sm:flex-wrap sm:items-center gap-3">
{showFilterPills && (
<ul className="-mx-4 px-4 sm:mx-0 sm:px-0 flex flex-nowrap sm:flex-wrap overflow-x-auto sm:overflow-visible items-center gap-2 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
<li>
<Link
href="/benchmarks"
className="pill"
data-active={activeCategory === null}
onClick={(e) => {
e.preventDefault();
setActiveCategory(null);
}}
>
All
</Link>
</li>
{categories.map((c) => {
const slug = categorySlugFromLabel(c);
const href = slug ? `/benchmarks/category/${slug}` : "/benchmarks";
return (
<li key={c}>
<Link
href={href}
className="pill"
data-active={activeCategory === c}
onClick={(e) => {
e.preventDefault();
setActiveCategory(activeCategory === c ? null : c);
}}
>
{c}
</Link>
</li>
);
})}
</ul>
)}
<ul className="-mx-4 px-4 sm:mx-0 sm:px-0 flex flex-nowrap sm:flex-wrap overflow-x-auto sm:overflow-visible items-center gap-2 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
<li>
<Link
href="/benchmarks"
className="pill"
data-active={!lockedCategory && activeCategory === null}
onClick={(e) => {
if (lockedCategory) return;
e.preventDefault();
setActiveCategory(null);
}}
>
All
</Link>
</li>
{categories.map((c) => {
const slug = categorySlugFromLabel(c);
const href = slug ? `/benchmarks/category/${slug}` : "/benchmarks";
const isActive = lockedCategory === c || (!lockedCategory && activeCategory === c);
return (
<li key={c}>
<Link
href={href}
className="pill"
data-active={isActive}
onClick={(e) => {
if (lockedCategory) return;
e.preventDefault();
setActiveCategory(activeCategory === c ? null : c);
}}
>
{c}
</Link>
</li>
);
})}
</ul>

<div className="sm:ml-auto flex items-center gap-3">
{/* View toggle */}
Expand Down
3 changes: 0 additions & 3 deletions src/components/site-header.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,6 @@ import { usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import { SearchTrigger } from "@/components/search/search-trigger";
import { SiteLogoSwitcher } from "@/components/site-logo-switcher";
import { SiteSubNav } from "@/components/site-sub-nav";
import { ThemeToggle } from "@/components/theme-toggle";

function GithubIcon({ size = 15 }: { size?: number }) {
Expand DownExpand Up@@ -190,8 +189,6 @@ export function SiteHeader() {
</nav>
)}
</header>

<SiteSubNav pathname={pathname} />
</div>
);
}
82 changes: 0 additions & 82 deletions src/components/site-sub-nav.tsx

This file was deleted.

Loading