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