|
import copy from 'copy-to-clipboard'; |
|
import { InfoIcon } from 'lucide-react'; |
|
import React, { useRef, useState, RefObject } from 'react'; |
|
import Clipboard from '~/components/svg/Clipboard'; |
|
import CheckMark from '~/components/svg/CheckMark'; |
|
import cn from '~/utils/cn'; |
|
|
|
type CodeBarProps = { |
|
lang: string; |
|
codeRef: RefObject<HTMLElement>; |
|
plugin?: boolean; |
|
error?: boolean; |
|
}; |
|
|
|
type CodeBlockProps = Pick<CodeBarProps, 'lang' | 'plugin' | 'error'> & { |
|
codeChildren: React.ReactNode; |
|
classProp?: string; |
|
}; |
|
|
|
const CodeBar: React.FC<CodeBarProps> = React.memo(({ lang, codeRef, error, 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={cn('ml-auto flex gap-2', error ? 'h-4 w-4 items-start text-white/50' : '')} |
|
onClick={async () => { |
|
const codeString = codeRef.current?.textContent; |
|
if (codeString) { |
|
setIsCopied(true); |
|
copy(codeString); |
|
|
|
setTimeout(() => { |
|
setIsCopied(false); |
|
}, 3000); |
|
} |
|
}} |
|
> |
|
{isCopied ? ( |
|
<> |
|
<CheckMark /> |
|
{error ? '' : 'Copied!'} |
|
</> |
|
) : ( |
|
<> |
|
<Clipboard /> |
|
{error ? '' : 'Copy code'} |
|
</> |
|
)} |
|
</button> |
|
)} |
|
</div> |
|
); |
|
}); |
|
|
|
const CodeBlock: React.FC<CodeBlockProps> = ({ |
|
lang, |
|
codeChildren, |
|
classProp = '', |
|
plugin = null, |
|
error, |
|
}) => { |
|
const codeRef = useRef<HTMLElement>(null); |
|
const language = plugin || error ? 'json' : lang; |
|
|
|
return ( |
|
<div className="w-full rounded-md bg-black text-xs text-white/80"> |
|
<CodeBar lang={lang} codeRef={codeRef} plugin={!!plugin} error={error} /> |
|
<div className={cn(classProp, 'overflow-y-auto p-4')}> |
|
<code |
|
ref={codeRef} |
|
className={cn( |
|
plugin || error ? '!whitespace-pre-wrap' : `hljs language-${language} !whitespace-pre`, |
|
)} |
|
> |
|
{codeChildren} |
|
</code> |
|
</div> |
|
</div> |
|
); |
|
}; |
|
|
|
export default CodeBlock; |
|
|