demo
This commit is contained in:
100
src/components/skill-tag.tsx
Normal file
100
src/components/skill-tag.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user