Vectrum
Sign In
145 lines · 4.8 KB · TypeScript
import type { LedKind } from "@unlocalhosted/metalui";
import type { Status } from "@/lib/issue-meta";

// Linear-style status and priority glyphs, drawn on MetalUI's 24 grid at its 1.7 stroke and coloured
// like its LEDs: green open, amber in progress or urgent, unlit (ink3) idle and closed.

const AMBER = "text-[#c98a18]";

const STATUS_COLORS: Record<Status, string> = {
  backlog: "text-ink3",
  todo: "text-green-deep",
  in_progress: AMBER,
  in_review: AMBER,
  done: "text-ink3",
  canceled: "text-ink3",
};

/** The LED that names a status beside its words (StatusBadge, Led). */
export const STATUS_LED: Record<Status, LedKind> = {
  backlog: "off",
  todo: "live",
  in_progress: "waiting",
  in_review: "waiting",
  done: "off",
  canceled: "off",
};

const RING = 9.15;

/** A pie slice filling `fraction` of the inner disc, starting at 12 o'clock. */
function Pie({ fraction }: { fraction: number }) {
  const r = 3.4;
  const c = 2 * Math.PI * r;
  return (
    <circle
      cx="12"
      cy="12"
      r={r}
      fill="none"
      stroke="currentColor"
      strokeWidth={r * 2}
      strokeDasharray={`${c * fraction} ${c}`}
      transform="rotate(-90 12 12)"
    />
  );
}

export function StatusIcon({ status, size = 14, className = "" }: { status: Status; size?: number; className?: string }) {
  return (
    <svg viewBox="0 0 24 24" width={size} height={size} className={`shrink-0 ${STATUS_COLORS[status]} ${className}`} aria-hidden>
      {status === "backlog" && (
        <circle cx="12" cy="12" r={RING} fill="none" stroke="currentColor" strokeWidth="1.7" strokeDasharray="2.6 2.19" />
      )}
      {(status === "todo" || status === "in_progress" || status === "in_review") && (
        <circle cx="12" cy="12" r={RING} fill="none" stroke="currentColor" strokeWidth="1.7" />
      )}
      {status === "in_progress" && <Pie fraction={0.5} />}
      {status === "in_review" && <Pie fraction={0.75} />}
      {status === "done" && (
        <>
          <circle cx="12" cy="12" r="10" fill="currentColor" />
          <path
            d="m7.6 12.3 3.1 3.1 5.7-6.2"
            fill="none"
            stroke="var(--canvas)"
            strokeWidth="1.9"
            strokeLinecap="round"
            strokeLinejoin="round"
          />
        </>
      )}
      {status === "canceled" && (
        <>
          <circle cx="12" cy="12" r="10" fill="currentColor" />
          <path d="m8.7 8.7 6.6 6.6m0-6.6-6.6 6.6" fill="none" stroke="var(--canvas)" strokeWidth="1.9" strokeLinecap="round" />
        </>
      )}
    </svg>
  );
}

export function PriorityIcon({ priority, size = 14, className = "" }: { priority: number; size?: number; className?: string }) {
  if (priority === 1) {
    return (
      <svg viewBox="0 0 24 24" width={size} height={size} className={`shrink-0 ${AMBER} ${className}`} aria-hidden>
        <rect x="2" y="2" width="20" height="20" rx="5.5" fill="currentColor" />
        <path d="M12 6.6v6.6" stroke="var(--canvas)" strokeWidth="2.4" strokeLinecap="round" />
        <circle cx="12" cy="17.2" r="1.5" fill="var(--canvas)" />
      </svg>
    );
  }
  if (priority === 0) {
    return (
      <svg viewBox="0 0 24 24" width={size} height={size} className={`shrink-0 text-ink3 ${className}`} aria-hidden>
        {[3.4, 10.2, 17].map((x) => (
          <rect key={x} x={x} y="10.8" width="3.6" height="2.4" rx="1.2" fill="currentColor" />
        ))}
      </svg>
    );
  }
  // High fills three bars, medium two, low one.
  const filled = 5 - priority;
  return (
    <svg viewBox="0 0 24 24" width={size} height={size} className={`shrink-0 text-ink ${className}`} aria-hidden>
      {[0, 1, 2].map((i) => (
        <rect
          key={i}
          x={2.5 + i * 7}
          y={13 - i * 5}
          width="5"
          height={8 + i * 5}
          rx="1.6"
          fill="currentColor"
          opacity={i < filled ? 1 : 0.25}
        />
      ))}
    </svg>
  );
}

/** A label's colour as a small lit swatch: the colour under the LED's hairline ring and top light. */
export function LabelDot({ color, size = 8 }: { color: string; size?: number }) {
  return (
    <span
      className="inline-block shrink-0 rounded-full"
      style={{
        width: size,
        height: size,
        background: `radial-gradient(circle at 40% 35%, color-mix(in srgb, ${color} 55%, white) 0 18%, ${color} 60%)`,
        boxShadow: "var(--mu-led-ring)",
      }}
      aria-hidden
    />
  );
}

/** A label on MetalUI's tag chip: a hairline pill with the swatch and the name. */
export function LabelChip({ name, color }: { name: string; color: string }) {
  return (
    <span className="inline-flex h-5 max-w-40 items-center gap-1.5 rounded-pill pr-2 pl-1.5 type-meta font-medium text-ink2 recipe-chip-tag">
      <LabelDot color={color} size={7} />
      <span className="truncate">{name}</span>
    </span>
  );
}