Files
pragma-stack/frontend/src/components/dev/CodeSnippet.tsx
Felipe Cardoso 96df7edf88 Refactor useAuth hook, settings components, and docs for formatting and readability improvements
- Consolidated multi-line arguments into single lines where appropriate in `useAuth`.
- Improved spacing and readability in data processing across components (`ProfileSettingsForm`, `PasswordChangeForm`, `SessionCard`).
- Applied consistent table and markdown formatting in design system docs (e.g., `README.md`, `08-ai-guidelines.md`, `00-quick-start.md`).
- Updated code snippets to ensure adherence to Prettier rules and streamlined JSX structures.
2025-11-10 11:03:45 +01:00

174 lines
4.8 KiB
TypeScript

/* istanbul ignore file */
/**
* CodeSnippet Component
* Displays syntax-highlighted code with copy-to-clipboard functionality
* This file is excluded from coverage as it's a demo/showcase component
*/
'use client';
import { useState } from 'react';
import { Check, Copy } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
interface CodeSnippetProps {
code: string;
language?: 'tsx' | 'typescript' | 'javascript' | 'css' | 'bash' | 'json';
title?: string;
showLineNumbers?: boolean;
highlightLines?: number[];
className?: string;
}
/**
* CodeSnippet - Syntax-highlighted code block with copy button
*
* @example
* <CodeSnippet
* title="Button Component"
* language="tsx"
* code={`<Button variant="default">Click me</Button>`}
* showLineNumbers
* />
*/
export function CodeSnippet({
code,
language = 'tsx',
title,
showLineNumbers = false,
highlightLines = [],
className,
}: CodeSnippetProps) {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error('Failed to copy code:', err);
}
};
const lines = code.split('\n');
return (
<div className={cn('relative group', className)}>
{/* Header */}
{(title || language) && (
<div className="flex items-center justify-between rounded-t-lg border border-b-0 bg-muted/50 px-4 py-2">
<div className="flex items-center gap-2">
{title && <span className="text-sm font-medium text-foreground">{title}</span>}
{language && <span className="text-xs text-muted-foreground">({language})</span>}
</div>
<Button
variant="ghost"
size="sm"
onClick={handleCopy}
className="h-7 gap-1 px-2 opacity-0 transition-opacity group-hover:opacity-100"
aria-label="Copy code"
>
{copied ? (
<>
<Check className="h-3 w-3" />
<span className="text-xs">Copied!</span>
</>
) : (
<>
<Copy className="h-3 w-3" />
<span className="text-xs">Copy</span>
</>
)}
</Button>
</div>
)}
{/* Code Block */}
<div
className={cn(
'relative overflow-x-auto rounded-lg border bg-muted/30',
title || language ? 'rounded-t-none' : ''
)}
>
{/* Copy button (when no header) */}
{!title && !language && (
<Button
variant="ghost"
size="sm"
onClick={handleCopy}
className="absolute right-2 top-2 z-10 h-7 gap-1 px-2 opacity-0 transition-opacity group-hover:opacity-100"
aria-label="Copy code"
>
{copied ? (
<>
<Check className="h-3 w-3" />
<span className="text-xs">Copied!</span>
</>
) : (
<>
<Copy className="h-3 w-3" />
<span className="text-xs">Copy</span>
</>
)}
</Button>
)}
<pre className="p-4 text-sm">
<code className={cn('font-mono', `language-${language}`)}>
{showLineNumbers ? (
<div className="flex">
{/* Line numbers */}
<div className="mr-4 select-none border-r pr-4 text-right text-muted-foreground">
{lines.map((_, idx) => (
<div key={idx} className="leading-6">
{idx + 1}
</div>
))}
</div>
{/* Code lines */}
<div className="flex-1">
{lines.map((line, idx) => (
<div
key={idx}
className={cn(
'leading-6',
highlightLines.includes(idx + 1) && 'bg-accent/20 -mx-4 px-4'
)}
>
{line || ' '}
</div>
))}
</div>
</div>
) : (
code
)}
</code>
</pre>
</div>
</div>
);
}
/**
* CodeGroup - Group multiple related code snippets
*
* @example
* <CodeGroup>
* <CodeSnippet title="Component" code="..." />
* <CodeSnippet title="Usage" code="..." />
* </CodeGroup>
*/
export function CodeGroup({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) {
return <div className={cn('space-y-4', className)}>{children}</div>;
}