This commit introduces a new CopyButton component in the ui components. It also includes a feature to copy multiple choices. The button changes its icon after copying to clipboard, indicating that the copying has been done.
113 lines
2.6 KiB
TypeScript
113 lines
2.6 KiB
TypeScript
"use client";
|
|
import type { DropdownMenuTriggerProps } from "@radix-ui/react-dropdown-menu";
|
|
import { CheckIcon, ClipboardIcon } from "lucide-react";
|
|
|
|
import { Button, type ButtonProps } from "@/components/ui/button";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu";
|
|
import { cn } from "@/lib/utils";
|
|
import { useCallback, useEffect, useState } from "react";
|
|
|
|
interface CopyButtonProps extends ButtonProps {
|
|
value: string;
|
|
src?: string;
|
|
event?: Event["NONE"];
|
|
}
|
|
|
|
interface Value {
|
|
data: string;
|
|
title: string;
|
|
}
|
|
|
|
export async function copyToClipboardWithMeta(value: string) {
|
|
await window?.navigator.clipboard.writeText(value);
|
|
}
|
|
|
|
export function CopyButton({
|
|
value,
|
|
className,
|
|
src,
|
|
variant = "ghost",
|
|
event,
|
|
...props
|
|
}: CopyButtonProps) {
|
|
const [hasCopied, setHasCopied] = useState(false);
|
|
|
|
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
|
|
useEffect(() => {
|
|
setTimeout(() => {
|
|
setHasCopied(false);
|
|
}, 2000);
|
|
}, [hasCopied]);
|
|
|
|
return (
|
|
<Button
|
|
size="icon"
|
|
variant={variant}
|
|
className={cn(
|
|
"relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50 [&_svg]:size-3",
|
|
className,
|
|
)}
|
|
onClick={() => {
|
|
copyToClipboardWithMeta(value).then(() => setHasCopied(true));
|
|
}}
|
|
{...props}
|
|
>
|
|
<span className="sr-only">Copy</span>
|
|
{hasCopied ? <CheckIcon /> : <ClipboardIcon />}
|
|
</Button>
|
|
);
|
|
}
|
|
|
|
interface CopyMultipleChoiceButtonProps extends DropdownMenuTriggerProps {
|
|
values: Value[];
|
|
}
|
|
|
|
export function CopyMultipleChoiceButton({
|
|
values,
|
|
className,
|
|
...props
|
|
}: CopyMultipleChoiceButtonProps) {
|
|
const [hasCopied, setHasCopied] = useState(false);
|
|
|
|
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
|
|
useEffect(() => {
|
|
setTimeout(() => {
|
|
setHasCopied(false);
|
|
}, 2000);
|
|
}, [hasCopied]);
|
|
|
|
const copyCommand = useCallback((value: string) => {
|
|
copyToClipboardWithMeta(value).then(() => setHasCopied(true));
|
|
}, []);
|
|
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
size="icon"
|
|
variant="ghost"
|
|
className={cn(
|
|
"relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50",
|
|
className,
|
|
)}
|
|
>
|
|
{hasCopied ? (
|
|
<CheckIcon className="h-3 w-3" />
|
|
) : (
|
|
<ClipboardIcon className="h-3 w-3" />
|
|
)}
|
|
<span className="sr-only">Copy</span>
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuItem onClick={() => copyCommand("npm")}>npm</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
);
|
|
}
|