| 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) { |
| |
| 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'; |
|
|