{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "copy-button", "title": "Copy Button", "description": "Outline icon button that copies text to the clipboard and confirms with a check for 1.5s. Optional label swaps to \"Copied\".", "dependencies": [ "lucide-react" ], "registryDependencies": [ "https://ds.formance.com/r/button.json" ], "files": [ { "path": "registry/default/ui-fragments/copy-button.tsx", "content": "'use client';\n\n/**\n * CopyButton — outline icon button that copies text to the clipboard and\n * confirms with a check for 1.5s.\n *\n * icon only\n * icon + label, swaps to \"Copied\"\n *\n * Used by ApiSnippet and LedgerSchema so copy / play actions read consistently\n * across fragments. Clicks are stopped from propagating, so the button is safe\n * inside a clickable row or card header.\n */\n\nimport { Check, Copy } from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\n\nimport { cn } from '@/lib/utils';\nimport { Button } from '@/registry/default/ui/button';\n\nexport type TCopyButtonProps = {\n text: string;\n label?: string;\n className?: string;\n};\n\nexport function CopyButton({ text, label, className }: TCopyButtonProps) {\n const [copied, setCopied] = useState(false);\n const resetTimer = useRef>(undefined);\n\n // The timer outlives the click that started it: a second click within 1.5s\n // would otherwise let the first one reset the state while the button still\n // reads \"Copied\", and an unmount would leave it pending.\n useEffect(() => () => clearTimeout(resetTimer.current), []);\n\n return (\n svg]:size-3.5',\n className\n )}\n onClick={async (e) => {\n e.stopPropagation();\n // Confirm after the write resolves, never before: a denied clipboard\n // permission must not show a check for text that was not copied.\n await navigator.clipboard.writeText(text.trim());\n setCopied(true);\n clearTimeout(resetTimer.current);\n resetTimer.current = setTimeout(() => setCopied(false), 1500);\n }}\n >\n {copied ? : }\n {label && {copied ? 'Copied' : label}}\n \n );\n}\n", "type": "registry:component", "target": "components/ui-fragments/copy-button.tsx" } ], "type": "registry:component" }