| 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; |
|
|