SqlMonacoBlock.tsx87 lines · main
| 1 | import Editor from '@monaco-editor/react' |
| 2 | import { Check, Copy } from 'lucide-react' |
| 3 | import { useMemo, useState } from 'react' |
| 4 | import { Button, cn, copyToClipboard } from 'ui' |
| 5 | |
| 6 | type SqlMonacoBlockProps = { |
| 7 | value?: string |
| 8 | className?: string |
| 9 | wrapperClassName?: string |
| 10 | hideCopy?: boolean |
| 11 | // Fixed height in px. Defaults to 310 to match previous CodeBlock max height |
| 12 | height?: number |
| 13 | // Show line numbers. Defaults to false to match previous CodeBlock |
| 14 | lineNumbers?: 'on' | 'off' |
| 15 | } |
| 16 | |
| 17 | export const SqlMonacoBlock = ({ |
| 18 | value, |
| 19 | className, |
| 20 | wrapperClassName, |
| 21 | height = 310, |
| 22 | lineNumbers = 'off', |
| 23 | hideCopy = false, |
| 24 | }: SqlMonacoBlockProps) => { |
| 25 | const [copied, setCopied] = useState(false) |
| 26 | |
| 27 | const content = useMemo(() => value ?? '', [value]) |
| 28 | |
| 29 | const handleCopy = (value: string) => { |
| 30 | setCopied(true) |
| 31 | copyToClipboard(value) |
| 32 | setTimeout(() => setCopied(false), 1000) |
| 33 | } |
| 34 | |
| 35 | return ( |
| 36 | <div |
| 37 | className={cn('group relative border rounded-md overflow-hidden w-full', wrapperClassName)} |
| 38 | > |
| 39 | <Editor |
| 40 | theme="briven" |
| 41 | language="pgsql" |
| 42 | value={content} |
| 43 | height={height} |
| 44 | className={className} |
| 45 | wrapperProps={{ |
| 46 | className: |
| 47 | '[&_.monaco-editor]:bg-transparent! [&_.monaco-editor-background]:bg-transparent! [&_.monaco-editor]:outline-transparent! [&_.cursor]:hidden!', |
| 48 | }} |
| 49 | options={{ |
| 50 | readOnly: true, |
| 51 | domReadOnly: true, |
| 52 | fontSize: 13, |
| 53 | minimap: { enabled: false }, |
| 54 | lineNumbers, |
| 55 | renderLineHighlight: 'none', |
| 56 | scrollbar: { vertical: 'auto', horizontal: 'auto' }, |
| 57 | overviewRulerLanes: 0, |
| 58 | overviewRulerBorder: false, |
| 59 | glyphMargin: false, |
| 60 | folding: false, |
| 61 | lineDecorationsWidth: 0, |
| 62 | lineNumbersMinChars: lineNumbers === 'off' ? 0 : 3, |
| 63 | wordWrap: 'on', |
| 64 | scrollBeyondLastLine: false, |
| 65 | selectionHighlight: false, |
| 66 | occurrencesHighlight: 'off', |
| 67 | fixedOverflowWidgets: true, |
| 68 | padding: { top: 12, bottom: 12 }, |
| 69 | tabIndex: -1, |
| 70 | }} |
| 71 | /> |
| 72 | |
| 73 | {!hideCopy && ( |
| 74 | <div className="absolute right-2 top-2 opacity-0 group-hover:opacity-100 transition-opacity"> |
| 75 | <Button |
| 76 | type="default" |
| 77 | className="px-1.5" |
| 78 | icon={copied ? <Check /> : <Copy />} |
| 79 | onClick={() => handleCopy(content)} |
| 80 | > |
| 81 | {copied ? 'Copied' : ''} |
| 82 | </Button> |
| 83 | </div> |
| 84 | )} |
| 85 | </div> |
| 86 | ) |
| 87 | } |