WIP. A little custom music server
1import { Link } from "@tanstack/react-router";
2
3import { useState } from "react";
4import { ChevronDown, ChevronRight, Home, Menu, Network, SquareFunction, StickyNote, Webhook, X } from "lucide-react";
5
6export default function Header() {
7 const [isOpen, setIsOpen] = useState(false);
8 const [groupedExpanded, setGroupedExpanded] = useState<Record<string, boolean>>({});
9
10 return (
11 <>
12 <header className="p-4 flex items-center bg-gray-800 text-white shadow-lg">
13 <button
14 onClick={() => setIsOpen(true)}
15 className="p-2 hover:bg-gray-700 rounded-lg transition-colors"
16 aria-label="Open menu"
17 >
18 <Menu size={24} />
19 </button>
20 <h1 className="ml-4 text-xl font-semibold">
21 <Link to="/">
22 <img src="/tanstack-word-logo-white.svg" alt="TanStack Logo" className="h-10" />
23 </Link>
24 </h1>
25 </header>
26
27 <aside
28 className={`fixed top-0 left-0 h-full w-80 bg-gray-900 text-white shadow-2xl z-50 transform transition-transform duration-300 ease-in-out flex flex-col ${
29 isOpen ? "translate-x-0" : "-translate-x-full"
30 }`}
31 >
32 <div className="flex items-center justify-between p-4 border-b border-gray-700">
33 <h2 className="text-xl font-bold">Navigation</h2>
34 <button
35 onClick={() => setIsOpen(false)}
36 className="p-2 hover:bg-gray-800 rounded-lg transition-colors"
37 aria-label="Close menu"
38 >
39 <X size={24} />
40 </button>
41 </div>
42
43 <nav className="flex-1 p-4 overflow-y-auto">
44 <Link
45 to="/"
46 onClick={() => setIsOpen(false)}
47 className="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
48 activeProps={{
49 className:
50 "flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2",
51 }}
52 >
53 <Home size={20} />
54 <span className="font-medium">Home</span>
55 </Link>
56
57 {/* Demo Links Start */}
58
59 <Link
60 to="/demo/start/server-funcs"
61 onClick={() => setIsOpen(false)}
62 className="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
63 activeProps={{
64 className:
65 "flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2",
66 }}
67 >
68 <SquareFunction size={20} />
69 <span className="font-medium">Start - Server Functions</span>
70 </Link>
71
72 <Link
73 to="/demo/start/api-request"
74 onClick={() => setIsOpen(false)}
75 className="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
76 activeProps={{
77 className:
78 "flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2",
79 }}
80 >
81 <Network size={20} />
82 <span className="font-medium">Start - API Request</span>
83 </Link>
84
85 <div className="flex flex-row justify-between">
86 <Link
87 to="/demo/start/ssr"
88 onClick={() => setIsOpen(false)}
89 className="flex-1 flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
90 activeProps={{
91 className:
92 "flex-1 flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2",
93 }}
94 >
95 <StickyNote size={20} />
96 <span className="font-medium">Start - SSR Demos</span>
97 </Link>
98 <button
99 className="p-2 hover:bg-gray-800 rounded-lg transition-colors"
100 onClick={() =>
101 setGroupedExpanded((prev) => ({
102 ...prev,
103 StartSSRDemo: !prev.StartSSRDemo,
104 }))
105 }
106 >
107 {groupedExpanded.StartSSRDemo ? <ChevronDown size={20} /> : <ChevronRight size={20} />}
108 </button>
109 </div>
110 {groupedExpanded.StartSSRDemo && (
111 <div className="flex flex-col ml-4">
112 <Link
113 to="/demo/start/ssr/spa-mode"
114 onClick={() => setIsOpen(false)}
115 className="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
116 activeProps={{
117 className:
118 "flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2",
119 }}
120 >
121 <StickyNote size={20} />
122 <span className="font-medium">SPA Mode</span>
123 </Link>
124
125 <Link
126 to="/demo/start/ssr/full-ssr"
127 onClick={() => setIsOpen(false)}
128 className="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
129 activeProps={{
130 className:
131 "flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2",
132 }}
133 >
134 <StickyNote size={20} />
135 <span className="font-medium">Full SSR</span>
136 </Link>
137
138 <Link
139 to="/demo/start/ssr/data-only"
140 onClick={() => setIsOpen(false)}
141 className="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
142 activeProps={{
143 className:
144 "flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2",
145 }}
146 >
147 <StickyNote size={20} />
148 <span className="font-medium">Data Only</span>
149 </Link>
150 </div>
151 )}
152
153 <Link
154 to="/demo/mcp-todos"
155 onClick={() => setIsOpen(false)}
156 className="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
157 activeProps={{
158 className:
159 "flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2",
160 }}
161 >
162 <Webhook size={20} />
163 <span className="font-medium">MCP</span>
164 </Link>
165
166 <Link
167 to="/demo/tanstack-query"
168 onClick={() => setIsOpen(false)}
169 className="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
170 activeProps={{
171 className:
172 "flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2",
173 }}
174 >
175 <Network size={20} />
176 <span className="font-medium">TanStack Query</span>
177 </Link>
178
179 {/* Demo Links End */}
180 </nav>
181 </aside>
182 </>
183 );
184}