{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "hash-display", "title": "Hash display", "description": "Truncated, linkable, and copyable Bitcoin identifiers.", "dependencies": [ "lucide-react" ], "registryDependencies": [ "dennisonbertram/bitcoin-ui/bitcoin-core", "dennisonbertram/bitcoin-ui/bitcoin-theme" ], "files": [ { "path": "components/bitcoin/hash-display.tsx", "content": "\"use client\";\n\nimport { Check, Copy, ExternalLink, X } from \"lucide-react\";\nimport {\n type ComponentProps,\n type MouseEvent,\n useEffect,\n useRef,\n useState,\n} from \"react\";\n\nimport { truncateMiddle } from \"@/lib/bitcoin\";\nimport { componentClasses } from \"@/lib/utils\";\n\nimport {\n interactiveStyles,\n monoStyles,\n type BitcoinVisualProps,\n} from \"./shared\";\n\ntype CopyState = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nexport type HashDisplayProps = Omit, \"children\"> &\n BitcoinVisualProps & {\n value: string;\n label?: string;\n href?: string;\n copyable?: boolean;\n startCharacters?: number;\n endCharacters?: number;\n /** Shows the entire value instead of truncating it. @default false */\n full?: boolean;\n };\n\nexport function HashDisplay({\n value,\n label = \"Hash\",\n href,\n copyable = true,\n startCharacters = 8,\n endCharacters = 8,\n full = false,\n unstyled,\n className,\n ...props\n}: HashDisplayProps) {\n const [copyState, setCopyState] = useState(\"idle\");\n const resetTimer = useRef | null>(null);\n\n useEffect(\n () => () => {\n if (resetTimer.current) clearTimeout(resetTimer.current);\n },\n [],\n );\n\n async function handleCopy(event: MouseEvent) {\n event.preventDefault();\n event.stopPropagation();\n setCopyState(\"loading\");\n\n try {\n await navigator.clipboard.writeText(value);\n setCopyState(\"success\");\n } catch {\n setCopyState(\"error\");\n }\n\n if (resetTimer.current) clearTimeout(resetTimer.current);\n resetTimer.current = setTimeout(() => setCopyState(\"idle\"), 1_800);\n }\n\n const visibleValue = full\n ? value\n : truncateMiddle(value, startCharacters, endCharacters);\n const statusLabel = {\n idle: `Copy ${label.toLowerCase()}`,\n loading: `Copying ${label.toLowerCase()}`,\n success: `${label} copied`,\n error: `Could not copy ${label.toLowerCase()}`,\n }[copyState];\n const StatusIcon =\n copyState === \"success\" ? Check : copyState === \"error\" ? X : Copy;\n\n return (\n \n {href ? (\n \n {visibleValue}\n , open {label.toLowerCase()}\n \n \n ) : (\n \n {visibleValue}\n \n )}\n {copyable ? (\n \n \n \n ) : null}\n \n {copyState === \"success\" || copyState === \"error\" ? statusLabel : \"\"}\n \n \n );\n}\n", "type": "registry:component" } ], "type": "registry:component" }