mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-11 09:51:40 +00:00
191 lines
5.0 KiB
TypeScript
191 lines
5.0 KiB
TypeScript
"use client"
|
|
|
|
import * as React from "react"
|
|
import { DropdownMenuTriggerProps } from "@radix-ui/react-dropdown-menu"
|
|
import { NpmCommands } from "types/unist"
|
|
|
|
import { cn } from "@/lib/utils"
|
|
import { Icons } from "@/components/icons"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
|
|
interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
|
|
value: string
|
|
src?: string
|
|
}
|
|
|
|
async function copyToClipboardWithMeta(
|
|
value: string,
|
|
meta?: Record<string, unknown>
|
|
) {
|
|
navigator.clipboard.writeText(value)
|
|
|
|
if (meta) {
|
|
await fetch("/api/log", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
event: "copy_primitive",
|
|
data: {
|
|
primitive: meta?.component,
|
|
},
|
|
}),
|
|
})
|
|
}
|
|
}
|
|
|
|
export function CopyButton({
|
|
value,
|
|
className,
|
|
src,
|
|
...props
|
|
}: CopyButtonProps) {
|
|
const [hasCopied, setHasCopied] = React.useState(false)
|
|
|
|
React.useEffect(() => {
|
|
setTimeout(() => {
|
|
setHasCopied(false)
|
|
}, 2000)
|
|
}, [hasCopied])
|
|
|
|
return (
|
|
<button
|
|
className={cn(
|
|
"relative z-20 inline-flex h-8 items-center justify-center rounded-md border-slate-200 p-2 text-sm font-medium text-slate-900 transition-all hover:bg-slate-100 focus:outline-none dark:text-slate-100 dark:hover:bg-slate-800",
|
|
className
|
|
)}
|
|
onClick={() => {
|
|
copyToClipboardWithMeta(value, {
|
|
component: src,
|
|
})
|
|
setHasCopied(true)
|
|
}}
|
|
{...props}
|
|
>
|
|
<span className="sr-only">Copy</span>
|
|
{hasCopied ? (
|
|
<Icons.check className="h-4 w-4" />
|
|
) : (
|
|
<Icons.copy className="h-4 w-4" />
|
|
)}
|
|
</button>
|
|
)
|
|
}
|
|
|
|
interface CopyWithClassNamesProps extends DropdownMenuTriggerProps {
|
|
value: string
|
|
classNames: string
|
|
className: string
|
|
}
|
|
|
|
export function CopyWithClassNames({
|
|
value,
|
|
classNames,
|
|
className,
|
|
...props
|
|
}: CopyWithClassNamesProps) {
|
|
const [hasCopied, setHasCopied] = React.useState(false)
|
|
|
|
React.useEffect(() => {
|
|
setTimeout(() => {
|
|
setHasCopied(false)
|
|
}, 2000)
|
|
}, [hasCopied])
|
|
|
|
const copyToClipboard = React.useCallback((value: string) => {
|
|
copyToClipboardWithMeta(value)
|
|
setHasCopied(true)
|
|
}, [])
|
|
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger
|
|
className={cn(
|
|
"relative z-20 inline-flex h-8 items-center justify-center rounded-md p-2 text-sm font-medium text-slate-900 transition-all hover:bg-slate-100 focus:outline-none dark:text-slate-100 dark:hover:bg-slate-800",
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
{hasCopied ? (
|
|
<Icons.check className="h-4 w-4" />
|
|
) : (
|
|
<Icons.copy className="h-4 w-4" />
|
|
)}
|
|
<span className="sr-only">Copy</span>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent>
|
|
<DropdownMenuItem onClick={() => copyToClipboard(value)}>
|
|
<Icons.react className="mr-2 h-4 w-4" />
|
|
<span>Component</span>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem onClick={() => copyToClipboard(classNames)}>
|
|
<Icons.tailwind className="mr-2 h-4 w-4" />
|
|
<span>Classname</span>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)
|
|
}
|
|
|
|
interface CopyNpmCommandButtonProps extends DropdownMenuTriggerProps {
|
|
commands: Required<NpmCommands>
|
|
}
|
|
|
|
export function CopyNpmCommandButton({
|
|
commands,
|
|
className,
|
|
...props
|
|
}: CopyNpmCommandButtonProps) {
|
|
const [hasCopied, setHasCopied] = React.useState(false)
|
|
|
|
React.useEffect(() => {
|
|
setTimeout(() => {
|
|
setHasCopied(false)
|
|
}, 2000)
|
|
}, [hasCopied])
|
|
|
|
const copyCommand = React.useCallback((value: string) => {
|
|
copyToClipboardWithMeta(value)
|
|
setHasCopied(true)
|
|
}, [])
|
|
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger
|
|
className={cn(
|
|
"relative z-20 inline-flex h-8 items-center justify-center rounded-md p-2 text-sm font-medium text-slate-900 transition-all hover:bg-slate-100 focus:outline-none",
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
{hasCopied ? (
|
|
<Icons.check className="h-4 w-4" />
|
|
) : (
|
|
<Icons.copy className="h-4 w-4" />
|
|
)}
|
|
<span className="sr-only">Copy</span>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent>
|
|
<DropdownMenuItem onClick={() => copyCommand(commands.__npmCommand__)}>
|
|
<Icons.npm className="mr-2 h-4 w-4 fill-[#CB3837]" />
|
|
<span>npm</span>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem onClick={() => copyCommand(commands.__yarnCommand__)}>
|
|
<Icons.yarn className="mr-2 h-4 w-4 fill-[#2C8EBB]" />
|
|
<span>yarn</span>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem onClick={() => copyCommand(commands.__pnpmCommand__)}>
|
|
<Icons.pnpm className="mr-2 h-4 w-4 fill-[#F69220]" />
|
|
<span>pnpm</span>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)
|
|
}
|