SPB Git

spb/ai-atlas

Public
4.1 KB · 86 lines tsx
1'use client';2import { Activity, Compass, Home, MoreHorizontal, Search } from 'lucide-react';3import Link from 'next/link';4import { usePathname } from 'next/navigation';5import { useEffect, useState } from 'react';6import { cn } from '@/lib/cn';7import { exploreNav, moreNav, primaryNav, routes } from '@/lib/site';8import { useOpenSearch } from './search-context';910/**11 * Bottom tab bar (< md): Home · Explore · Changes · Search · More. Safe-area aware; <body> reserves12 * `pb-[calc(var(--tabbar-h)+env(safe-area-inset-bottom))]` so it never covers content.13 */14export function MobileTabBar() {15  const pathname = usePathname();16  const openSearch = useOpenSearch();17  const [more, setMore] = useState(false);18  useEffect(() => setMore(false), [pathname]);19  const exploreActive = pathname.startsWith('/explore') || exploreNav.some((n) => pathname.startsWith(n.href));20  const cls = (active: boolean) => cn('flex h-full w-full flex-col items-center justify-center gap-0.5 text-[10.5px]', active ? 'text-accent' : 'text-ink-2');21  return (22    <>23      {more && (24        <div className="fixed inset-0 z-[45] bg-black/40 md:hidden" onClick={() => setMore(false)}>25          <div className="panel absolute inset-x-2 bottom-[calc(var(--tabbar-h)+env(safe-area-inset-bottom,0px)+8px)] max-h-[72vh] overflow-y-auto p-3" onClick={(e) => e.stopPropagation()}>26            <p className="eyebrow px-2 pb-1">Explore</p>27            <ul className="grid grid-cols-2 gap-0.5">28              {exploreNav.map((n) => (29                <li key={n.href}>30                  <Link href={n.href} className="block px-3 py-2.5 text-[15px] text-ink-2 hover:text-ink">31                    {n.label}32                  </Link>33                </li>34              ))}35            </ul>36            <p className="eyebrow px-2 pt-3 pb-1">Track & about</p>37            <ul className="grid grid-cols-2 gap-0.5">38              {[...primaryNav.filter((n) => n.href !== '/changes'), ...moreNav.filter((n) => n.href !== '/developers')].map((n) => (39                <li key={n.href}>40                  <Link href={n.href} className="block px-3 py-2.5 text-[15px] text-ink-2 hover:text-ink">41                    {n.label}42                  </Link>43                </li>44              ))}45            </ul>46          </div>47        </div>48      )}49      <nav aria-label="Primary (mobile)" className="safe-bottom fixed inset-x-0 bottom-0 z-[46] border-t border-rule bg-canvas/95 backdrop-blur-md md:hidden">50        <ul className="grid h-[var(--tabbar-h)] grid-cols-5">51          <li className="min-w-0">52            <Link href={routes.home()} aria-current={pathname === '/' ? 'page' : undefined} className={cls(pathname === '/')}>53              <Home size={21} aria-hidden strokeWidth={pathname === '/' ? 2.25 : 1.75} />54              <span className="truncate">Home</span>55            </Link>56          </li>57          <li className="min-w-0">58            <Link href={routes.explore()} aria-current={exploreActive ? 'page' : undefined} className={cls(exploreActive)}>59              <Compass size={21} aria-hidden strokeWidth={exploreActive ? 2.25 : 1.75} />60              <span className="truncate">Explore</span>61            </Link>62          </li>63          <li className="min-w-0">64            <Link href={routes.changes()} aria-current={pathname.startsWith('/changes') ? 'page' : undefined} className={cls(pathname.startsWith('/changes'))}>65              <Activity size={21} aria-hidden strokeWidth={pathname.startsWith('/changes') ? 2.25 : 1.75} />66              <span className="truncate">Changes</span>67            </Link>68          </li>69          <li className="min-w-0">70            <button type="button" onClick={openSearch} className={cls(false)}>71              <Search size={21} aria-hidden strokeWidth={1.75} />72              <span className="truncate">Search</span>73            </button>74          </li>75          <li className="min-w-0">76            <button type="button" onClick={() => setMore((m) => !m)} aria-expanded={more} className={cls(more)}>77              <MoreHorizontal size={21} aria-hidden strokeWidth={1.75} />78              <span className="truncate">More</span>79            </button>80          </li>81        </ul>82      </nav>83    </>84  );85}86
Files · 9f1db171c786cf4a3b99436329f29b145c02aeec
Menu

Theme