Add CodeBlockEditor component for enhanced code editing experience

- Introduced CodeBlockEditor component to support multiple programming languages with syntax highlighting using CodeMirror.
- Implemented features such as customizable styles, read-only mode, and line number visibility.
- Added support for JSON object parsing and modal display for minimal view.
- Integrated theme context for dark mode support and improved user interaction with onChange handling.
- Defined prop types for better type checking and documentation.
This commit is contained in:
Tom Butcher 2025-08-18 00:58:30 +01:00
parent a9a2801e27
commit 4201f2b4a3

View File

@ -0,0 +1,192 @@
import React, { useMemo, useState } from 'react'
import PropTypes from 'prop-types'
import CodeMirror from '@uiw/react-codemirror'
import { javascript } from '@codemirror/lang-javascript'
import { python } from '@codemirror/lang-python'
import { json } from '@codemirror/lang-json'
import { css } from '@codemirror/lang-css'
import { html } from '@codemirror/lang-html'
import { markdown } from '@codemirror/lang-markdown'
import { sql } from '@codemirror/lang-sql'
import { java } from '@codemirror/lang-java'
import { cpp } from '@codemirror/lang-cpp'
import { rust } from '@codemirror/lang-rust'
import { go } from '@codemirror/lang-go'
import { php } from '@codemirror/lang-php'
import { yaml } from '@codemirror/lang-yaml'
import { xml } from '@codemirror/lang-xml'
import { oneDark } from '@codemirror/theme-one-dark'
import { useThemeContext } from '../context/ThemeContext'
import { Button, Modal, Typography } from 'antd'
const { Link } = Typography
export default function CodeBlockEditor({
code = '',
language = 'javascript',
style = {},
className = '',
readOnly = false,
onChange = null,
height = 'auto',
showLineNumbers = true,
disabled = false,
minimal = false
}) {
const { isDarkMode } = useThemeContext()
const [codeMirrorOpen, setCodeMirrorOpen] = useState(false)
var editorCode = code
if (typeof code == 'object' && language == 'json') {
editorCode = JSON.stringify(code, null, 2)
}
console.log(editorCode)
// Map language to CodeMirror extension
const languageExtension = useMemo(() => {
switch (language.toLowerCase()) {
case 'javascript':
case 'js':
return javascript()
case 'python':
case 'py':
return python()
case 'json':
return json()
case 'xml':
return xml()
case 'ejs':
return xml()
case 'html':
return html()
case 'css':
return css()
case 'markdown':
case 'md':
return markdown()
case 'sql':
return sql()
case 'java':
return java()
case 'cpp':
case 'c++':
case 'c':
return cpp()
case 'rust':
case 'rs':
return rust()
case 'go':
return go()
case 'php':
return php()
case 'yaml':
case 'yml':
return yaml()
default:
return javascript() // Default fallback
}
}, [language])
const handleOnChange = (value) => {
if (typeof code == 'object' && language == 'json') {
console.log('Parsing object', JSON.parse(value))
onChange(JSON.parse(value))
} else {
onChange(value)
}
}
const codeMirror = (
<div
style={{
border: '1px solid #85858541',
...style
}}
className={className}
>
<CodeMirror
value={editorCode}
height={height}
theme={isDarkMode ? oneDark : 'light'}
extensions={[languageExtension]}
readOnly={readOnly || disabled}
onChange={handleOnChange}
basicSetup={{
lineNumbers: showLineNumbers,
foldGutter: true,
dropCursor: false,
allowMultipleSelections: false,
indentOnInput: false,
syntaxHighlighting: true,
bracketMatching: true,
closeBrackets: true,
autocompletion: !readOnly,
rectangularSelection: false,
crosshairCursor: false,
highlightActiveLine: !readOnly,
highlightSelectionMatches: false,
closeBracketsKeymap: false,
searchKeymap: false,
foldKeymap: false,
completionKeymap: !readOnly,
lintKeymap: false
}}
style={{
fontSize: '14px',
fontFamily: 'Monaco, Menlo, "Ubuntu Mono", monospace'
}}
/>
</div>
)
return (
<>
{minimal ? (
<div style={{ maxWidth: '300px' }}>
<Link
onClick={() => {
setCodeMirrorOpen(true)
}}
>
<pre>{editorCode.slice(-64).trim()}...</pre>
</Link>
<Modal
closeIcon={false}
open={codeMirrorOpen}
width={800}
footer={
<Button
onClick={() => {
setCodeMirrorOpen(false)
}}
>
Close
</Button>
}
>
{codeMirror}
</Modal>
</div>
) : (
codeMirror
)}
</>
)
}
CodeBlockEditor.propTypes = {
code: PropTypes.string.isRequired,
language: PropTypes.oneOfType([
PropTypes.string,
PropTypes.arrayOf(PropTypes.string)
]),
style: PropTypes.object,
className: PropTypes.string,
readOnly: PropTypes.bool,
onChange: PropTypes.func,
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
showLineNumbers: PropTypes.bool,
disabled: PropTypes.bool,
minimal: PropTypes.bool
}