Skip to main content
Glama
index.tsx4.77 kB
'use client'; import { CopyCheck, CopyIcon } from 'lucide-react'; import { type FC, type KeyboardEvent, type MouseEvent, type PropsWithChildren, useState, } from 'react'; import { cn } from '../../utils/cn'; /** * Props for the CopyToClipboard component */ export interface CopyToClipboardProps extends PropsWithChildren { /** * The text to copy to the clipboard when clicked * @example "Hello World" * @example "npm install @intlayer/design-system" */ text: string; /** * Additional CSS classes for the wrapper element * @example "bg-blue-100 text-blue-800" */ className?: string; /** * Accessible label for screen readers when copy operation is available * @default "Copy to clipboard" */ 'aria-label'?: string; /** * Accessible label for screen readers when content has been copied * @default "Copied to clipboard" */ 'aria-copied-label'?: string; /** * Duration in milliseconds to show the "copied" state * @default 2000 */ feedbackDuration?: number; /** * Callback function called when copy operation succeeds */ onCopySuccess?: () => void; /** * Callback function called when copy operation fails */ onCopyError?: (error: Error) => void; /** * Disable the copy to clipboard functionality and return the children * @default false */ disable?: boolean; /** * Prevent the default behavior of the link when clicked * @default true */ preventDefault?: boolean; } export const useCopyToClipboard = ( text?: string, feedbackDuration = 2000, onCopySuccess?: () => void, onCopyError?: (error: Error) => void ) => { const [isCopied, setIsCopied] = useState(false); const copy = async () => { if (!text) return; try { if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(text); } else { onCopyError?.(new Error('Clipboard API not supported in this browser')); return; } setIsCopied(true); setTimeout(() => setIsCopied(false), feedbackDuration); onCopySuccess?.(); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Failed to copy to clipboard'; onCopyError?.(error instanceof Error ? error : new Error(errorMessage)); } }; return { isCopied, copy }; }; /** * CopyToClipboard Component * * A versatile component that allows users to copy text to their clipboard with visual feedback. * Provides accessibility features, customizable feedback duration, and error handling. * * @example * ```tsx * // Basic usage * <CopyToClipboard text="Hello World"> * <span>Click to copy</span> * </CopyToClipboard> * * // With custom styling and callbacks * <CopyToClipboard * text="npm install @intlayer/design-system" * className="bg-gray-100 p-2" * feedbackDuration={3000} * onCopySuccess={() => console.log('Copied!')} * > * <code>npm install @intlayer/design-system</code> * </CopyToClipboard> * ``` * * @component * @accessibility * - Uses proper ARIA labels for copy and copied states * - Supports keyboard navigation (Enter and Space keys) * - Announces state changes to screen readers * - Provides visual focus indicators */ export const CopyToClipboard: FC<CopyToClipboardProps> = ({ text, children, className, 'aria-label': ariaLabel = 'Copy to clipboard', 'aria-copied-label': ariaCopiedLabel = 'Copied to clipboard', feedbackDuration = 2000, onCopySuccess, onCopyError, disable, preventDefault = true, }) => { const { isCopied, copy } = useCopyToClipboard( text, feedbackDuration, onCopySuccess, onCopyError ); if (disable) return <span className={className}>{children}</span>; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Enter' || event.key === ' ') { handleCopy(event); } }; const handleCopy = (event: MouseEvent | KeyboardEvent) => { if (preventDefault) event.preventDefault(); copy(); }; const currentAriaLabel = isCopied ? ariaCopiedLabel : ariaLabel; const IconComponent = isCopied ? CopyCheck : CopyIcon; return ( <span className={cn( 'inline-flex max-w-full cursor-pointer items-center gap-2 rounded-md p-0.5 hover:bg-neutral/10', className )} onClick={handleCopy} onKeyDown={handleKeyDown} role="button" tabIndex={0} aria-label={currentAriaLabel} aria-pressed={isCopied} data-testid="copy-to-clipboard" > <span className="min-w-0 break-all">{children}</span> {text && ( <IconComponent className="ml-1 ml-auto size-4 min-w-4 shrink-0" aria-hidden="true" /> )} </span> ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aymericzip/intlayer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server