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>
);
}