Vectrum
Sign In
65 lines · 2.8 KB · TypeScript
"use client";

import Link from "next/link";
import { usePathname } from "next/navigation";
import { useState } from "react";
import { BranchIcon, CodeIcon, GearIcon, HistoryIcon, IssueOpenedIcon, PackageIcon } from "./icons";
import { SlidingIndicator, thumbClass } from "./sliding-indicator";

/**
 * A repository's sections on the switcher track: they are pages, so they are links (not MetalUI Tabs),
 * with the switcher's raised thumb gliding under the current one.
 */
export function RepoTabs({
  base,
  openIssues,
  images,
  showSettings,
}: {
  base: string;
  openIssues: number;
  images: number;
  showSettings: boolean;
}) {
  const pathname = usePathname();
  const section = pathname.slice(base.length).split("/")[1] ?? "";
  const tabs = [
    { href: base, label: "Code", icon: CodeIcon, active: !["issues", "branches", "commits", "commit", "packages", "settings"].includes(section) },
    { href: `${base}/issues`, label: "Issues", icon: IssueOpenedIcon, count: openIssues, active: section === "issues" },
    { href: `${base}/commits`, label: "Commits", icon: HistoryIcon, active: section === "commits" || section === "commit" },
    { href: `${base}/branches`, label: "Branches", icon: BranchIcon, active: section === "branches" },
    { href: `${base}/packages`, label: "Packages", icon: PackageIcon, count: images, active: section === "packages" },
    ...(showSettings ? [{ href: `${base}/settings`, label: "Settings", icon: GearIcon, active: section === "settings" }] : []),
  ];

  // Move the thumb as soon as a tab is clicked instead of waiting for the next page;
  // the optimistic choice is dropped once the URL changes.
  const [clicked, setClicked] = useState<{ from: string; index: number } | null>(null);
  const activeIndex = clicked?.from === pathname ? clicked.index : tabs.findIndex((t) => t.active);

  return (
    <div className="-mx-1 overflow-x-auto px-1 py-1">
      <nav aria-label="Repository" className="relative inline-flex items-center gap-0.5 rounded-pill recipe-well-track p-[3px]">
        <SlidingIndicator className={thumbClass} />
        {tabs.map((t, i) => {
          const active = i === activeIndex;
          return (
            <Link
              key={t.label}
              href={t.href}
              aria-current={active ? "page" : undefined}
              onClick={() => setClicked({ from: pathname, index: i })}
              className={`mu-icon-trigger relative z-1 flex h-7 shrink-0 items-center gap-1.5 rounded-pill px-3 type-ui outline-none transition-colors duration-150 focus-visible:focus-ring ${
                active ? "text-ink" : "text-ink2 hover:text-ink"
              }`}
            >
              <t.icon width={14} height={14} />
              {t.label}
              {t.count ? <span className="type-readout text-ink3">{t.count}</span> : null}
            </Link>
          );
        })}
      </nav>
    </div>
  );
}