Release 3X-UI for Docker / build (push) Canceled after 0s
Release 3X-UI / build (386) (push) Canceled after 0s
Release 3X-UI / build (amd64) (push) Canceled after 0s
Release 3X-UI / build (arm64) (push) Canceled after 0s
Release 3X-UI / build (armv5) (push) Canceled after 0s
Release 3X-UI / build (armv6) (push) Canceled after 0s
Release 3X-UI / build (armv7) (push) Canceled after 0s
Release 3X-UI / build (s390x) (push) Canceled after 0s
Release 3X-UI / Build for Windows (amd64) (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
47 lines
1.1 KiB
TypeScript
47 lines
1.1 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { Check, Copy } from 'lucide-react';
|
|
import { cn } from '@/lib/cn';
|
|
|
|
export function CopyButton({
|
|
value,
|
|
label = 'Copy',
|
|
className,
|
|
}: {
|
|
value: string;
|
|
label?: string;
|
|
className?: string;
|
|
}) {
|
|
const [copied, setCopied] = useState(false);
|
|
|
|
async function copy() {
|
|
try {
|
|
await navigator.clipboard.writeText(value);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 2000);
|
|
} catch {
|
|
// Clipboard unavailable (insecure context) — ignore.
|
|
}
|
|
}
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
onClick={copy}
|
|
aria-label={copied ? 'Copied' : label}
|
|
className={cn(
|
|
'inline-flex items-center gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs font-medium transition-colors hover:bg-fd-accent hover:text-fd-accent-foreground focus-visible:outline-2 focus-visible:outline-fd-ring',
|
|
className,
|
|
)}
|
|
>
|
|
{copied ? (
|
|
<Check className="size-3.5 text-brand" aria-hidden />
|
|
) : (
|
|
<Copy className="size-3.5" aria-hidden />
|
|
)}
|
|
<span>{copied ? 'Copied' : label}</span>
|
|
</button>
|
|
);
|
|
}
|