WIP. A little custom music server
at m4a 184 lines 6.1 kB view raw
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}