Spaces:
Paused
Paused
| import { IconCheck, IconClipboard, IconDownload } from '@tabler/icons-react'; | |
| import { FC, memo, useState } from 'react'; | |
| import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; | |
| import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; | |
| import { useTranslation } from 'next-i18next'; | |
| import { | |
| generateRandomString, | |
| programmingLanguages, | |
| } from '@/utils/app/codeblock'; | |
| interface Props { | |
| language: string; | |
| value: string; | |
| } | |
| export const CodeBlock: FC<Props> = memo(({ language, value }) => { | |
| const { t } = useTranslation('markdown'); | |
| const [isCopied, setIsCopied] = useState<Boolean>(false); | |
| const copyToClipboard = () => { | |
| if (!navigator.clipboard || !navigator.clipboard.writeText) { | |
| return; | |
| } | |
| navigator.clipboard.writeText(value).then(() => { | |
| setIsCopied(true); | |
| setTimeout(() => { | |
| setIsCopied(false); | |
| }, 2000); | |
| }); | |
| }; | |
| const downloadAsFile = () => { | |
| const fileExtension = programmingLanguages[language] || '.file'; | |
| const suggestedFileName = `file-${generateRandomString( | |
| 3, | |
| true, | |
| )}${fileExtension}`; | |
| const fileName = window.prompt( | |
| t('Enter file name') || '', | |
| suggestedFileName, | |
| ); | |
| if (!fileName) { | |
| // user pressed cancel on prompt | |
| return; | |
| } | |
| const blob = new Blob([value], { type: 'text/plain' }); | |
| const url = URL.createObjectURL(blob); | |
| const link = document.createElement('a'); | |
| link.download = fileName; | |
| link.href = url; | |
| link.style.display = 'none'; | |
| document.body.appendChild(link); | |
| link.click(); | |
| document.body.removeChild(link); | |
| URL.revokeObjectURL(url); | |
| }; | |
| return ( | |
| <div className="codeblock relative font-sans text-[16px]"> | |
| <div className="flex items-center justify-between py-1.5 px-4"> | |
| <span className="text-xs lowercase text-white">{language}</span> | |
| <div className="flex items-center"> | |
| <button | |
| className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-white" | |
| onClick={copyToClipboard} | |
| > | |
| {isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />} | |
| {isCopied ? t('Copied!') : t('Copy code')} | |
| </button> | |
| <button | |
| className="flex items-center rounded bg-none p-1 text-xs text-white" | |
| onClick={downloadAsFile} | |
| > | |
| <IconDownload size={18} /> | |
| </button> | |
| </div> | |
| </div> | |
| <SyntaxHighlighter | |
| language={language} | |
| style={oneDark} | |
| customStyle={{ margin: 0 }} | |
| > | |
| {value} | |
| </SyntaxHighlighter> | |
| </div> | |
| ); | |
| }); | |
| CodeBlock.displayName = 'CodeBlock'; | |