Files
ayristech-youtube/components/CodeBlock.tsx
T
2026-07-23 16:55:44 +03:00

129 lines
4.6 KiB
TypeScript

'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>
);
}