72 lines · 3.2 KB
import { Link } from "@tanstack/react-router";
import { Icon } from "~/ui/Icon";
import { cn } from "~/utils/cn";
import type { usePull } from "~/hooks/use-pull";

type StackEntry = ReturnType<typeof usePull>["stack"][number];

function stateOf(entry: StackEntry): { label: string; dot: string } {
  if (entry.merged) return { label: "Merged", dot: "bg-merged-500" };
  if (entry.state === "closed") return { label: "Closed", dot: "bg-danger-500" };
  if (entry.draft) return { label: "Draft", dot: "bg-gray-500" };
  return { label: "Open", dot: "bg-success-500" };
}

/** The stack this pull request belongs to, newest on top and the trunk at the bottom, like `gt log`. */
export function PullStack({ owner, repo, stack }: { owner: string; repo: string; stack: StackEntry[] }) {
  if (stack.length === 0) return null;
  const trunk = stack[0]?.baseRef;
  const position = stack.findIndex((entry) => entry.current) + 1;
  return (
    <nav aria-label="Stack" className="rounded-lg border border-kumo-hairline bg-kumo-canvas/60">
      <header className="flex items-center gap-2 border-b border-kumo-hairline px-4 py-2 text-xs text-kumo-subtle">
        <Icon name="stack" className="text-accent-400" />
        <span className="font-medium text-kumo-default">Stack</span>
        <span>
          {position} of {stack.length}
        </span>
      </header>
      <ol className="relative px-4 py-2">
        <span aria-hidden className="absolute top-4 bottom-4 left-[1.3rem] w-px bg-kumo-fill" />
        {[...stack].reverse().map((entry) => {
          const state = stateOf(entry);
          return (
            <li key={entry.issueId} className="relative flex items-center gap-3 py-1">
              <span
                title={state.label}
                className={cn(
                  "relative z-10 size-2.5 shrink-0 rounded-full ring-4 ring-gray-950",
                  state.dot,
                  entry.current && "ring-accent-500/40",