|
import React, { useRef, useState, RefObject } from 'react'; |
|
import { Clipboard, CheckMark } from '~/components'; |
|
import { InfoIcon } from 'lucide-react'; |
|
import { cn } from '~/utils/'; |
|
|
|
interface CodeBarProps { |
|
lang: string; |
|
codeRef: RefObject<HTMLElement>; |
|
plugin?: boolean; |
|
} |
|
|
|
const CodeBar: React.FC<CodeBarProps> = React.memo(({ lang, codeRef, plugin = null }) => { |
|
const [isCopied, setIsCopied] = useState(false); |
|
return ( |
|
<div className="relative flex items-center rounded-tl-md rounded-tr-md bg-gray-800 px-4 py-2 font-sans text-xs text-gray-200"> |
|
<span className="">{lang}</span> |
|
{plugin ? ( |
|
<InfoIcon className="ml-auto flex h-4 w-4 gap-2 text-white/50" /> |
|
) : ( |
|
<button |
|
className="ml-auto flex gap-2" |
|
onClick={async () => { |
|
const codeString = codeRef.current?.textContent; |
|
if (codeString) { |
|
navigator.clipboard.writeText(codeString).then(() => { |
|
setIsCopied(true); |
|
setTimeout(() => setIsCopied(false), 3000); |
|
}); |
|
} |
|
}} |
|
> |
|
{isCopied ? ( |
|
<> |
|
<CheckMark /> |
|
Copied! |
|
</> |
|
) : ( |
|
<> |
|
<Clipboard /> |
|
Copy code |
|
</> |
|
)} |
|
</button> |
|
)} |
|
</div> |
|
); |
|
}); |
|
|
|
interface CodeBlockProps { |
|
lang: string; |
|
codeChildren: string; |
|
classProp?: string; |
|
plugin?: boolean; |
|
} |
|
|
|
const CodeBlock: React.FC<CodeBlockProps> = ({ |
|
lang, |
|
codeChildren, |
|
classProp = '', |
|
plugin = null, |
|
}) => { |
|
const codeRef = useRef<HTMLElement>(null); |
|
const language = plugin ? 'json' : lang; |
|
|
|
return ( |
|
<div className="rounded-md bg-black"> |
|
<CodeBar lang={lang} codeRef={codeRef} plugin={!!plugin} /> |
|
<div className={cn(classProp, 'overflow-y-auto p-4')}> |
|
<code ref={codeRef} className={`hljs !whitespace-pre language-${language}`}> |
|
{codeChildren} |
|
</code> |
|
</div> |
|
</div> |
|
); |
|
}; |
|
|
|
export default CodeBlock; |
|
|