File size: 2,164 Bytes
3720e61 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | import React, { useState, useMemo } from "react";
import { Check, Copy } from "lucide-react";
import Prism from "prismjs";
interface CodeBlockProps {
language: string;
value: string;
}
export const CodeBlock: React.FC<CodeBlockProps> = ({ language, value }) => {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(value);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error("Failed to copy code block content: ", err);
}
};
const highlightedHtml = useMemo(() => {
const lang = (language || "markup").toLowerCase();
const grammar = Prism.languages[lang] || Prism.languages.markup;
try {
return Prism.highlight(value, grammar, lang);
} catch (e) {
return value;
}
}, [language, value]);
return (
<div className="my-4 rounded-xl border border-slate-800 bg-[#09090B] overflow-hidden shadow-md font-sans">
{/* Header bar */}
<div className="flex items-center justify-between px-4 py-1.5 bg-black/35 border-b border-white/[0.03] select-none">
<span className="text-[10px] font-mono text-slate-400 uppercase tracking-wider font-bold">
{language || "code"}
</span>
<button
type="button"
onClick={handleCopy}
className="text-[9px] text-slate-400 hover:text-white flex items-center gap-1 transition px-2 py-0.5 rounded hover:bg-slate-800 cursor-pointer font-bold font-mono"
>
{copied ? (
<>
<Check className="h-3 w-3 text-emerald-450" />
<span>COPIED</span>
</>
) : (
<>
<Copy className="h-3 w-3 text-blue-500" />
<span>COPY CODE</span>
</>
)}
</button>
</div>
{/* Code body */}
<pre className="p-4 overflow-x-auto text-xs m-0 font-mono text-left scrollbar-thin">
<code
className={`language-${language}`}
dangerouslySetInnerHTML={{ __html: highlightedHtml }}
/>
</pre>
</div>
);
};
|