Skip to content

Commit d9209e8

Browse files
thedaviddiasclaude
andauthored
feat: improve navigation template (#238)
Co-authored-by: Claude <[email protected]>
1 parent 13ec3ec commit d9209e8

File tree

22 files changed

+1708
-429
lines changed

22 files changed

+1708
-429
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { NextResponse } from "next/server";
2+
import { getUniquePatterns, loadEntries } from "@/lib/loadEntries";
3+
4+
export async function GET() {
5+
try {
6+
const entries = await loadEntries();
7+
const patterns = getUniquePatterns(entries);
8+
9+
return NextResponse.json({
10+
entries,
11+
patterns,
12+
});
13+
} catch (error) {
14+
console.error("Failed to load search data:", error);
15+
return NextResponse.json(
16+
{ error: "Failed to load search data" },
17+
{ status: 500 },
18+
);
19+
}
20+
}

apps/gallery/app/layout.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import "@/app/global.css";
22

3+
import { HomeLayout } from "@ux-patterns/ui/components/custom/header";
34
import { RootProvider } from "fumadocs-ui/provider";
45
import type { Metadata } from "next";
56
import { Inter } from "next/font/google";
67
import Link from "next/link";
78
import PlausibleProvider from "next-plausible";
89
import { NuqsAdapter } from "nuqs/adapters/next/app";
9-
import { Header } from "@/components/common/header";
1010
import { KeyboardShortcuts } from "@/components/common/keyboard-shortcuts";
1111
import { FooterCopyright } from "@/components/footer/footer-copyright";
12+
import { HeaderSearchDialog } from "@/components/header/header-search-wrapper";
13+
import { baseOptions } from "@/lib/layout.shared";
1214
import { SearchProvider } from "@/lib/search-context";
1315
import { metadataSEO } from "./metadata";
1416

@@ -45,12 +47,10 @@ export default function RootLayout({ children }: LayoutProps) {
4547
<NuqsAdapter>
4648
<SearchProvider>
4749
<KeyboardShortcuts />
50+
<HeaderSearchDialog />
4851
<div className="min-h-screen bg-fd-background">
4952
{/* Header */}
50-
<Header />
51-
52-
{/* Main content */}
53-
<main className="flex-1">{children}</main>
53+
<HomeLayout {...baseOptions()}>{children}</HomeLayout>
5454

5555
{/* Footer */}
5656
<footer className="bg-fd-card border-t border-fd-border mt-16">

apps/gallery/components/common/header.tsx

Lines changed: 0 additions & 86 deletions
This file was deleted.
Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,89 @@
1-
import { getUniquePatterns, loadEntries } from "@/lib/loadEntries";
2-
import { HeaderSearch } from "./header-search";
1+
"use client";
32

4-
export async function HeaderSearchWrapper() {
5-
const entries = await loadEntries();
6-
const patterns = getUniquePatterns(entries);
3+
import { LargeSearchToggleBtn } from "@ux-patterns/ui/components/custom/search-button";
4+
import { SearchModal } from "@ux-patterns/ui/components/custom/search-modal";
5+
import { usePlausible } from "next-plausible";
6+
import { useEffect, useState } from "react";
7+
import { useSearch } from "@/lib/search-context";
8+
import { trackGallerySearch } from "@/lib/tracking";
9+
import type { Entry } from "@/lib/types";
710

8-
return <HeaderSearch entries={entries} patterns={patterns} />;
11+
interface HeaderSearchWrapperProps {
12+
entries: Entry[];
13+
patterns: string[];
14+
}
15+
16+
export function HeaderSearchWrapper({
17+
entries,
18+
patterns,
19+
}: HeaderSearchWrapperProps) {
20+
const { isSearchOpen, openSearch, closeSearch } = useSearch();
21+
const plausible = usePlausible();
22+
23+
const handleSearchClick = () => {
24+
trackGallerySearch(plausible, "open");
25+
openSearch();
26+
};
27+
28+
return (
29+
<>
30+
<LargeSearchToggleBtn
31+
onClick={handleSearchClick}
32+
placeholder="Search..."
33+
/>
34+
<SearchModal
35+
isOpen={isSearchOpen}
36+
onClose={closeSearch}
37+
entries={entries}
38+
patterns={patterns}
39+
/>
40+
</>
41+
);
42+
}
43+
44+
// Client component that uses SearchProvider context
45+
export function HeaderSearchDialog() {
46+
const [entries, setEntries] = useState<Entry[]>([]);
47+
const [patterns, setPatterns] = useState<string[]>([]);
48+
const [isLoading, setIsLoading] = useState(true);
49+
const { isSearchOpen, openSearch, closeSearch } = useSearch();
50+
const plausible = usePlausible();
51+
52+
useEffect(() => {
53+
// Load data on the client side
54+
const loadData = async () => {
55+
try {
56+
const response = await fetch("/api/search-data");
57+
if (response.ok) {
58+
const data = await response.json();
59+
setEntries(data.entries);
60+
setPatterns(data.patterns);
61+
}
62+
} catch (error) {
63+
console.error("Failed to load search data:", error);
64+
} finally {
65+
setIsLoading(false);
66+
}
67+
};
68+
69+
loadData();
70+
}, []);
71+
72+
const _handleSearchClick = () => {
73+
trackGallerySearch(plausible, "open");
74+
openSearch();
75+
};
76+
77+
if (isLoading) {
78+
return null; // Don't render anything while loading
79+
}
80+
81+
return (
82+
<SearchModal
83+
isOpen={isSearchOpen}
84+
onClose={closeSearch}
85+
entries={entries}
86+
patterns={patterns}
87+
/>
88+
);
989
}

apps/gallery/components/header/header-search.tsx

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"use client";
2+
3+
import { LargeSearchToggleBtn } from "@ux-patterns/ui/components/custom/search-button";
4+
import { usePlausible } from "next-plausible";
5+
import { useSearch } from "@/lib/search-context";
6+
import { trackGallerySearch } from "@/lib/tracking";
7+
8+
export function SearchButton() {
9+
const { openSearch } = useSearch();
10+
const plausible = usePlausible();
11+
12+
const handleSearchClick = () => {
13+
trackGallerySearch(plausible, "open");
14+
openSearch();
15+
};
16+
17+
return (
18+
<LargeSearchToggleBtn onClick={handleSearchClick} placeholder="Search..." />
19+
);
20+
}

apps/gallery/lib/layout.shared.tsx

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,76 @@
1-
import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared";
1+
import { SOCIAL_LINKS } from "@ux-patterns/constants/social";
2+
import { GitHubStarsWrapper } from "@ux-patterns/ui/components/custom/github-stars-wrapper";
3+
import type { HomeLayoutProps } from "@ux-patterns/ui/components/custom/header";
4+
import { ArrowUpRight } from "lucide-react";
5+
import Link from "next/link";
6+
import { SearchButton } from "@/components/header/search-button";
7+
import { GALLERY_TRACKING_EVENTS } from "./tracking";
28

39
/**
410
* Shared layout configurations
511
*
6-
* you can customise layouts individually from:
7-
* Home Layout: app/(home)/layout.tsx
8-
* Docs Layout: app/docs/layout.tsx
912
*/
10-
export function baseOptions(): BaseLayoutProps {
13+
export function baseOptions(): HomeLayoutProps {
1114
return {
15+
wide: true,
1216
nav: {
1317
title: (
1418
<>
15-
<svg
16-
width="24"
17-
height="24"
18-
xmlns="http://www.w3.org/2000/svg"
19-
aria-label="Logo"
20-
role="img"
21-
>
22-
<title>UX Patterns Gallery Logo</title>
23-
<circle cx={12} cy={12} r={12} fill="currentColor" />
24-
</svg>
25-
My App
19+
<div className="inline-flex items-center">
20+
<Link
21+
href="https://uxpatterns.dev"
22+
className="hidden md:flex items-center gap-1 text-xl font-bold text-fd-muted-foreground hover:text-fd-primary transition-colors group"
23+
title="Go to UX Patterns main site"
24+
>
25+
UX Patterns
26+
<ArrowUpRight className="w-3 h-3" />
27+
</Link>
28+
<span className="hidden md:block mx-2 text-fd-muted-foreground">
29+
/
30+
</span>
31+
<Link
32+
href="/"
33+
className="text-xl font-bold text-fd-primary hover:text-fd-primary/90 transition-colors"
34+
>
35+
Gallery
36+
</Link>
37+
</div>
2638
</>
2739
),
2840
},
29-
// see https://fumadocs.dev/docs/ui/navigation/links
30-
links: [],
41+
searchToggle: {
42+
enabled: true,
43+
components: {
44+
lg: <SearchButton />,
45+
sm: <SearchButton />,
46+
},
47+
},
48+
githubStars: (
49+
<GitHubStarsWrapper
50+
variant="small"
51+
asLink={true}
52+
trackingEvent={GALLERY_TRACKING_EVENTS.GITHUB_STAR_CLICK}
53+
/>
54+
),
55+
links: [
56+
{
57+
text: "Websites",
58+
url: "/website",
59+
active: "nested-url",
60+
},
61+
{
62+
text: "Kit",
63+
url: "https://kit.uxpatterns.dev",
64+
external: false,
65+
},
66+
...SOCIAL_LINKS.map((social) => ({
67+
type: "icon" as const,
68+
label: social.label,
69+
icon: social.icon,
70+
text: social.label,
71+
url: social.link,
72+
external: true,
73+
})),
74+
],
3175
};
3276
}

apps/kit/app/(home)/layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { HomeLayout } from "fumadocs-ui/layouts/home";
1+
// import { HomeLayout } from "fumadocs-ui/layouts/home";
2+
import { HomeLayout } from "@ux-patterns/ui/components/custom/header";
23
import { baseOptions } from "@/lib/layout.shared";
34

45
export default function Layout({ children }: LayoutProps<"/">) {

0 commit comments

Comments
 (0)