'use client' import React from 'react' import { SvgCheck, SvgCopy } from '@chainlink/blocks-icons' import { useClipboardCopy } from '../../hooks/useClipboardCopy' import { cn } from '../../utils/cn' import { Popover, PopoverArrow, PopoverContent, PopoverTrigger, } from '../Popover' // ============================================================================ // Copy Context // ============================================================================ type CopyContextValue = { /** * The text to be copied to the clipboard. */ text: string /** * Whether the text was recently copied. */ isCopied: boolean /** * Function to copy the text to the clipboard. */ copy: () => void } const CopyContext = React.createContext(null) /** * Hook to access the Copy context. Must be used within a CopyRoot. * * @example * ```tsx * const { isCopied, copy, text } = useCopyContext() * ``` */ export const useCopyContext = () => { const context = React.useContext(CopyContext) if (!context) { throw new Error('useCopyContext must be used within a CopyRoot') } return context } /** * Hook to get copy functionality. Can be used standalone with text prop, * or within a CopyRoot to use context. * * @param text - Optional text to copy. If provided, uses standalone mode. * If omitted, reads from CopyRoot context. * * @example * ```tsx * // Standalone usage * const { isCopied, copy } = useCopy('Hello') * * // Context usage (within CopyRoot) * const { isCopied, copy, text } = useCopy() * ``` */ export const useCopy = (text?: string) => { const context = React.useContext(CopyContext) const localClipboard = useClipboardCopy() const isStandalone = text !== undefined const resolvedText = isStandalone ? text : context?.text const isCopied = isStandalone ? localClipboard.isCopied : (context?.isCopied ?? false) const copy = React.useCallback(() => { if (isStandalone && resolvedText) { void localClipboard.copy(resolvedText) } else { context?.copy() } }, [isStandalone, resolvedText, localClipboard, context]) return { text: resolvedText, isCopied, copy } } // ============================================================================ // CopyRoot // ============================================================================ export type CopyRootProps = { /** * The text to be copied to the clipboard. */ text: string /** * The content of the copy component. */ children: React.ReactNode /** * Additional class names for the root wrapper. */ className?: string } & Omit, 'children'> /** * Root component that provides copy context and layout styling. * Use this primitive to build custom copy components. * * @example * ```tsx * * 0x1234...abcd * * * ``` */ export const CopyRoot = React.forwardRef( ({ text, children, className, ...props }, ref) => { const { isCopied, copy: copyToClipboard } = useClipboardCopy() const copy = React.useCallback(() => { void copyToClipboard(text) }, [copyToClipboard, text]) return (
{children}
) }, ) CopyRoot.displayName = 'CopyRoot' // ============================================================================ // CopyTrigger // ============================================================================ export type CopyTriggerProps = { /** * The text to copy. If provided, CopyTrigger can be used standalone without CopyRoot. * If omitted, text will be read from CopyRoot context. */ text?: string /** * Custom icon to show when not copied. */ defaultIcon?: React.ReactNode /** * Custom icon to show when copied. */ copiedIcon?: React.ReactNode /** * Additional class names for the trigger button. */ className?: string /** * Whether to stop event propagation on click. * @default true */ stopPropagation?: boolean /** * Side of the tooltip popover. * @default "top" */ tooltipSide?: 'top' | 'bottom' | 'left' | 'right' } & Omit, 'onClick' | 'children'> /** * Clickable trigger that copies text and displays status icon with tooltip. * Can be used within a CopyRoot (context-based) or standalone with the `text` prop. * * @example * ```tsx * // With CopyRoot context * * Hello * * * * // Standalone usage * * ``` */ export const CopyTrigger = React.forwardRef< HTMLButtonElement, CopyTriggerProps >( ( { text: textProp, defaultIcon, copiedIcon, className, stopPropagation = true, tooltipSide = 'top', ...props }, ref, ) => { const { copy, isCopied } = useCopy(textProp) const [isHovered, setIsHovered] = React.useState(false) const icon = isCopied ? (copiedIcon ?? ) : (defaultIcon ?? ( )) return ( {isCopied ? 'Copied!' : 'Copy'} ) }, ) CopyTrigger.displayName = 'CopyTrigger'