first commit
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { CodeSnippet } from '@/lib/data';
|
||||
import { Check, Copy, Download, FileCode, Terminal } from 'lucide-react';
|
||||
|
||||
interface CodeBlockProps {
|
||||
snippets: CodeSnippet[];
|
||||
}
|
||||
|
||||
export function CodeBlock({ snippets }: CodeBlockProps) {
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
if (!snippets || snippets.length === 0) return null;
|
||||
|
||||
const currentSnippet = snippets[activeTab] || snippets[0];
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(currentSnippet.code);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (err) {
|
||||
console.error('Copy error:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDownloadFile = () => {
|
||||
const element = document.createElement('a');
|
||||
const file = new Blob([currentSnippet.code], { type: 'text/plain;charset=utf-8' });
|
||||
element.href = URL.createObjectURL(file);
|
||||
element.download = currentSnippet.fileName.split('/').pop() || 'code.txt';
|
||||
document.body.appendChild(element);
|
||||
element.click();
|
||||
document.body.removeChild(element);
|
||||
};
|
||||
|
||||
const lines = currentSnippet.code.split('\n');
|
||||
|
||||
return (
|
||||
<div className="w-full rounded-2xl bg-slate-950 border border-slate-800 shadow-2xl overflow-hidden my-6">
|
||||
|
||||
{/* Tab Navigation Header */}
|
||||
<div className="flex flex-wrap items-center justify-between bg-slate-900/90 px-4 py-2.5 border-b border-slate-800 gap-3">
|
||||
|
||||
{/* File Tabs */}
|
||||
<div className="flex items-center gap-1.5 overflow-x-auto py-1 scrollbar-none">
|
||||
{snippets.map((snip, index) => (
|
||||
<button
|
||||
key={snip.fileName}
|
||||
onClick={() => setActiveTab(index)}
|
||||
className={`flex items-center gap-2 px-3.5 py-1.5 rounded-lg text-xs font-mono font-medium transition-all ${
|
||||
activeTab === index
|
||||
? 'bg-slate-800 text-white shadow-sm border border-slate-700/80'
|
||||
: 'text-slate-400 hover:text-slate-200 hover:bg-slate-800/40'
|
||||
}`}
|
||||
>
|
||||
<FileCode className={`w-3.5 h-3.5 ${activeTab === index ? 'text-red-400' : 'text-slate-500'}`} />
|
||||
<span>{snip.fileName}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={handleDownloadFile}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 text-xs font-semibold transition-colors border border-slate-700/60"
|
||||
title="Download File"
|
||||
>
|
||||
<Download className="w-3.5 h-3.5 text-slate-400" />
|
||||
<span className="hidden sm:inline">Download</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-semibold transition-all border ${
|
||||
copied
|
||||
? 'bg-emerald-950/80 text-emerald-300 border-emerald-800/80'
|
||||
: 'bg-red-600 hover:bg-red-500 text-white border-red-500 shadow-sm shadow-red-600/20'
|
||||
}`}
|
||||
>
|
||||
{copied ? (
|
||||
<>
|
||||
<Check className="w-3.5 h-3.5 text-emerald-400" />
|
||||
<span>Copied!</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Copy className="w-3.5 h-3.5" />
|
||||
<span>Copy Code</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Snippet Description bar */}
|
||||
{currentSnippet.description && (
|
||||
<div className="px-4 py-2 bg-slate-900/50 border-b border-slate-800/60 text-xs text-slate-400 font-sans flex items-center gap-2">
|
||||
<Terminal className="w-3.5 h-3.5 text-red-400 flex-shrink-0" />
|
||||
<span>{currentSnippet.description}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Code Display Area with Line Numbers */}
|
||||
<div className="p-4 overflow-x-auto font-mono text-xs leading-relaxed max-h-[480px] bg-slate-950">
|
||||
<table className="w-full border-collapse">
|
||||
<tbody>
|
||||
{lines.map((line, idx) => (
|
||||
<tr key={idx} className="hover:bg-slate-900/40 transition-colors group">
|
||||
<td className="w-10 select-none text-right pr-4 text-slate-600 group-hover:text-slate-500 font-mono text-[11px]">
|
||||
{idx + 1}
|
||||
</td>
|
||||
<td className="whitespace-pre text-slate-200 pl-2">
|
||||
{line}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user