{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "bitcoin-search", "title": "Bitcoin search", "description": "Validated search for Bitcoin heights, hashes, and addresses.", "dependencies": [ "lucide-react" ], "registryDependencies": [ "dennisonbertram/bitcoin-ui/bitcoin-core", "dennisonbertram/bitcoin-ui/bitcoin-theme" ], "files": [ { "path": "components/bitcoin/bitcoin-search.tsx", "content": "\"use client\";\n\nimport { Search } from \"lucide-react\";\nimport {\n type ComponentProps,\n type FormEvent,\n useId,\n useState,\n} from \"react\";\n\nimport {\n classifyBitcoinQuery,\n type BitcoinSearchKind,\n} from \"@/lib/bitcoin\";\nimport { componentClasses } from \"@/lib/utils\";\n\nimport {\n interactiveStyles,\n type BitcoinVisualProps,\n} from \"./shared\";\n\nexport type BitcoinSearchSubmit = {\n query: string;\n kind: BitcoinSearchKind;\n};\n\nexport type BitcoinSearchProps = Omit<\n ComponentProps<\"form\">,\n \"children\" | \"onSubmit\"\n> &\n BitcoinVisualProps & {\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n onSearch: (search: BitcoinSearchSubmit) => void | Promise;\n label?: string;\n placeholder?: string;\n loading?: boolean;\n error?: string;\n };\n\nexport function BitcoinSearch({\n value,\n defaultValue = \"\",\n onValueChange,\n onSearch,\n label = \"Search the Bitcoin chain\",\n placeholder = \"Block, transaction, or address\",\n loading,\n error,\n unstyled,\n className,\n ...props\n}: BitcoinSearchProps) {\n const inputId = useId();\n const helpId = useId();\n const [internalValue, setInternalValue] = useState(defaultValue);\n const [internalError, setInternalError] = useState();\n const query = value ?? internalValue;\n const visibleError = error ?? internalError;\n\n async function handleSubmit(event: FormEvent) {\n event.preventDefault();\n const normalized = query.trim();\n const kind = classifyBitcoinQuery(normalized);\n\n if (!normalized) {\n setInternalError(\"Enter a block height, transaction ID, block hash, or address.\");\n return;\n }\n\n if (kind === \"unknown\") {\n setInternalError(\"That does not look like a Bitcoin identifier.\");\n return;\n }\n\n setInternalError(undefined);\n await onSearch({ query: normalized, kind });\n }\n\n function updateValue(nextValue: string) {\n if (value === undefined) setInternalValue(nextValue);\n setInternalError(undefined);\n onValueChange?.(nextValue);\n }\n\n return (\n \n \n {label}\n \n \n \n updateValue(event.target.value)}\n placeholder={placeholder}\n disabled={loading}\n aria-invalid={Boolean(visibleError)}\n aria-describedby={helpId}\n autoComplete=\"off\"\n spellCheck={false}\n className={componentClasses(\n unstyled,\n \"min-h-12 min-w-0 border-0 bg-transparent px-3 font-mono text-sm outline-none placeholder:font-sans placeholder:text-[var(--color-muted)] disabled:cursor-not-allowed disabled:opacity-45\",\n )}\n />\n \n {loading ? \"Searching…\" : \"Search\"}\n \n \n \n {visibleError ??\n \"Accepts block heights, 64-character hashes, and Bitcoin addresses.\"}\n

\n \n );\n}\n", "type": "registry:component" } ], "type": "registry:component" }