laomao/bingo
0
1'use client'2 3import { FC, memo } from 'react'4import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'5import { coldarkDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'6 7import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard'8import { IconCheck, IconCopy, IconDownload } from '@/components/ui/icons'9import { Button } from '@/components/ui/button'10 11interface Props {12 language: string13 value: string14}15 16interface languageMap {17 [key: string]: string | undefined18}19 20export const programmingLanguages: languageMap = {21 javascript: '.js',22 python: '.py',23 java: '.java',24 c: '.c',25 cpp: '.cpp',26 'c++': '.cpp',27 'c#': '.cs',28 ruby: '.rb',29 php: '.php',30 swift: '.swift',31 'objective-c': '.m',32 kotlin: '.kt',33 typescript: '.ts',34 go: '.go',35 perl: '.pl',36 rust: '.rs',37 scala: '.scala',38 haskell: '.hs',39 lua: '.lua',40 shell: '.sh',41 sql: '.sql',42 html: '.html',43 css: '.css'44 // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component45}46 47export const generateRandomString = (length: number, lowercase = false) => {48 const chars = 'ABCDEFGHJKLMNPQRSTUVWXY3456789' // excluding similar looking characters like Z, 2, I, 1, O, 049 let result = ''50 for (let i = 0; i < length; i++) {51 result += chars.charAt(Math.floor(Math.random() * chars.length))52 }53 return lowercase ? result.toLowerCase() : result54}55 56const CodeBlock: FC<Props> = memo(({ language, value }) => {57 const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 })58 59 const downloadAsFile = () => {60 if (typeof window === 'undefined') {61 return62 }63 const fileExtension = programmingLanguages[language] || '.file'64 const suggestedFileName = `file-${generateRandomString(65 3,66 true67 )}${fileExtension}`68 const fileName = window.prompt('Enter file name' || '', suggestedFileName)69 70 if (!fileName) {71 // User pressed cancel on prompt.72 return73 }74 75 const blob = new Blob([value], { type: 'text/plain' })76 const url = URL.createObjectURL(blob)77 const link = document.createElement('a')78 link.download = fileName79 link.href = url80 link.style.display = 'none'81 document.body.appendChild(link)82 link.click()83 document.body.removeChild(link)84 URL.revokeObjectURL(url)85 }86 87 const onCopy = () => {88 if (isCopied) return89 copyToClipboard(value)90 }91 92 return (93 <div className="codeblock relative w-full bg-zinc-950 font-sans">94 <div className="flex w-full items-center justify-between bg-zinc-800 px-6 py-2 pr-4 text-zinc-100">95 <span className="text-xs lowercase">{language}</span>96 <div className="flex items-center space-x-1">97 <Button98 variant="ghost"99 className="hover:bg-zinc-800 focus-visible:ring-1 focus-visible:ring-slate-700 focus-visible:ring-offset-0"100 onClick={downloadAsFile}101 size="icon"102 >103 <IconDownload />104 <span className="sr-only">Download</span>105 </Button>106 <Button107 variant="ghost"108 size="icon"109 className="text-xs hover:bg-zinc-800 focus-visible:ring-1 focus-visible:ring-slate-700 focus-visible:ring-offset-0"110 onClick={onCopy}111 >112 {isCopied ? <IconCheck /> : <IconCopy />}113 <span className="sr-only">Copy code</span>114 </Button>115 </div>116 </div>117 <SyntaxHighlighter118 language={language}119 style={coldarkDark}120 PreTag="div"121 showLineNumbers122 customStyle={{123 margin: 0,124 width: '100%',125 background: 'transparent',126 padding: '1.5rem 1rem'127 }}128 codeTagProps={{129 style: {130 fontSize: '0.9rem',131 fontFamily: 'var(--font-mono)'132 }133 }}134 >135 {value}136 </SyntaxHighlighter>137 </div>138 )139})140CodeBlock.displayName = 'CodeBlock'141 142export { CodeBlock }143 