/ src / components / Header.tsx
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  }