SPB Git forge

spb/countryatlas

Public
20commits 1branches 0releases
268.3 MBsize
maindefault branch
12 days agolast push
TypeScript 57% Python 38.6% JavaScript 3.6% CSS 0.6%
2.3 KB · 51 lines tsx
Raw Blame History
1'use client';2import { BarChart3, Globe2, Map, Scale, Search } from 'lucide-react';3import Link from 'next/link';4import { usePathname } from 'next/navigation';5import { t } from '@/i18n';6import { cn } from '@/lib/cn';7import { routes } from '@/lib/site';8import { isFullBleed } from './main-frame';9import { useOpenSearch } from './search-context';1011/**12 * Fixed bottom tab bar (< md): Explore · Countries · Compare · Rank · Search. Safe-area padding; the body13 * reserves space (`pb-[calc(56px+env(safe-area-inset-bottom))]`) so it never covers the footer. Hidden on the14 * full-viewport explorers, which carry their own bottom controls.15 */16export function MobileTabBar() {17  const pathname = usePathname();18  const openSearch = useOpenSearch();19  if (isFullBleed(pathname)) return null;20  const tabs = [21    { href: routes.explore(), label: t('nav.explore'), Icon: Map },22    { href: routes.countries(), label: t('nav.countries'), Icon: Globe2 },23    { href: routes.compare(), label: t('nav.compare'), Icon: Scale },24    { href: routes.rankings(), label: t('nav.rank'), Icon: BarChart3 },25  ];26  return (27    <nav aria-label={t('nav.primary')} className="fixed inset-x-0 bottom-0 z-30 border-t border-rule bg-paper/95 backdrop-blur supports-[backdrop-filter]:bg-paper/90 md:hidden safe-bottom">28      <ul className="grid h-14 grid-cols-5">29        {tabs.map(({ href, label, Icon }) => {30          const base = href.split('?')[0]!;31          const active = pathname === base || pathname.startsWith(`${base}/`);32          return (33            <li key={href} className="min-w-0">34              <Link href={href} aria-current={active ? 'page' : undefined} className={cn('flex h-full flex-col items-center justify-center gap-0.5 text-2xs', active ? 'text-accent' : 'text-ink-2')}>35                <Icon size={20} aria-hidden strokeWidth={active ? 2.25 : 1.75} />36                <span className="truncate">{label}</span>37              </Link>38            </li>39          );40        })}41        <li className="min-w-0">42          <button type="button" onClick={openSearch} className="flex h-full w-full flex-col items-center justify-center gap-0.5 text-2xs text-ink-2">43            <Search size={20} aria-hidden strokeWidth={1.75} />44            <span className="truncate">{t('nav.search')}</span>45          </button>46        </li>47      </ul>48    </nav>49  );50}51