101 lines
2.9 KiB
TypeScript
101 lines
2.9 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
CheckCircle2,
|
|
CircleMinus,
|
|
GripVertical,
|
|
LoaderCircle,
|
|
Sparkles,
|
|
X,
|
|
} from "lucide-react";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
export function SkillTag({
|
|
name,
|
|
index,
|
|
onRemove,
|
|
active = false,
|
|
muted = false,
|
|
draggable = false,
|
|
status = "selected",
|
|
showStatusLabel = true,
|
|
className,
|
|
}: {
|
|
name: string;
|
|
index?: number;
|
|
onRemove?: () => void;
|
|
active?: boolean;
|
|
muted?: boolean;
|
|
draggable?: boolean;
|
|
status?: "selected" | "checking" | "used" | "unused";
|
|
showStatusLabel?: boolean;
|
|
className?: string;
|
|
}) {
|
|
return (
|
|
<span
|
|
className={cn(
|
|
"inline-flex h-7 max-w-full items-center gap-1.5 rounded-full border px-2.5 text-[11px] font-medium transition-colors",
|
|
active
|
|
? "border-foreground/15 bg-foreground text-background"
|
|
: "border-border bg-background text-foreground/75",
|
|
status === "checking" &&
|
|
"border-foreground/15 bg-background text-foreground",
|
|
status === "used" &&
|
|
"border-emerald-200 bg-emerald-50 text-emerald-700",
|
|
status === "unused" &&
|
|
"border-border bg-background text-muted-foreground opacity-65",
|
|
muted && "opacity-55 grayscale-[0.2]",
|
|
className,
|
|
)}
|
|
>
|
|
{draggable ? (
|
|
<GripVertical className="size-3.5 shrink-0 text-muted-foreground" />
|
|
) : active ? (
|
|
<Sparkles className="size-3.5 shrink-0" />
|
|
) : null}
|
|
{typeof index === "number" && (
|
|
<span
|
|
className={cn(
|
|
"font-mono text-[10px]",
|
|
active ? "text-background/75" : "text-muted-foreground",
|
|
)}
|
|
>
|
|
{String(index + 1).padStart(2, "0")}
|
|
</span>
|
|
)}
|
|
<span className="truncate">{name}</span>
|
|
{showStatusLabel && status === "checking" && (
|
|
<span className="ml-0.5 inline-flex items-center gap-1 text-[9px] font-medium">
|
|
<LoaderCircle className="size-3 animate-spin" />
|
|
判断中
|
|
</span>
|
|
)}
|
|
{showStatusLabel && status === "used" && (
|
|
<span className="ml-0.5 inline-flex items-center gap-1 text-[9px] font-medium">
|
|
<CheckCircle2 className="size-3" />
|
|
已触发
|
|
</span>
|
|
)}
|
|
{showStatusLabel && status === "unused" && (
|
|
<span className="ml-0.5 inline-flex items-center gap-1 text-[9px] font-medium">
|
|
<CircleMinus className="size-3" />
|
|
未触发
|
|
</span>
|
|
)}
|
|
{onRemove && (
|
|
<button
|
|
type="button"
|
|
aria-label={`移除 ${name}`}
|
|
className="ml-0.5 grid size-5 shrink-0 place-items-center rounded-full text-current/55 hover:bg-black/5 hover:text-current focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40"
|
|
onClick={(event) => {
|
|
event.stopPropagation();
|
|
onRemove();
|
|
}}
|
|
>
|
|
<X className="size-3.5" />
|
|
</button>
|
|
)}
|
|
</span>
|
|
);
|
|
}
|