All files / src/components/shared/tab-navigation index.tsx

100% Statements 85/85
100% Branches 7/7
100% Functions 2/2
100% Lines 85/85

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 861x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 26x 26x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 1x 1x 11x 11x 11x 11x 11x 11x 11x  
'use client';
 
import Link from 'next/link';
import { useSearchParams } from 'next/navigation';
 
import { Suspense } from 'react';
 
import { cn } from '@/lib/utils';
 
export interface Tab {
  label: string;
  value: string;
}
 
interface TabNavigationProps {
  tabs: Tab[];
  paramName?: string;
  basePath?: string;
  defaultValue?: string;
}
 
interface TabItemProps {
  label: string;
  href: string;
  isActive: boolean;
}
 
const TAB_TEXT_STYLES = {
  base: 'flex h-full items-center justify-center text-text-sm-semibold transition-colors',
  active: 'text-mint-600',
  inactive: 'text-gray-600',
} as const;
 
const INDICATOR_STYLES = 'absolute bottom-0 left-0 z-10 h-[2px] w-full bg-mint-500';
const BASE_LINE_STYLES = 'absolute bottom-0 left-0 right-0 h-[2px] bg-gray-200';
 
const TabItem = ({ label, href, isActive }: TabItemProps) => (
  <li className='relative flex-1'>
    <Link
      href={href}
      className={cn(
        TAB_TEXT_STYLES.base,
        isActive ? TAB_TEXT_STYLES.active : TAB_TEXT_STYLES.inactive,
      )}
    >
      {label}
    </Link>
    {isActive && <span className={INDICATOR_STYLES} />}
  </li>
);
 
const TabNavigationInner = ({
  tabs,
  paramName = 'tab',
  basePath = '',
  defaultValue,
}: TabNavigationProps) => {
  const searchParams = useSearchParams();
  const currentTab = searchParams.get(paramName) ?? defaultValue ?? tabs[0].value;
 
  return (
    <nav className='sticky top-14 z-50 h-11 bg-white'>
      <ul className='flex h-full w-full px-4'>
        {tabs.map((tab) => (
          <TabItem
            key={tab.value}
            href={`${basePath}?${paramName}=${tab.value}`}
            isActive={currentTab === tab.value}
            label={tab.label}
          />
        ))}
      </ul>
      <div className={BASE_LINE_STYLES} />
    </nav>
  );
};
 
export const TabNavigation = (props: TabNavigationProps) => {
  return (
    // 나중에 레이아웃으로 뺄거임 임시
    <Suspense fallback={null}>
      <TabNavigationInner {...props} />
    </Suspense>
  );
};