sozsoft-platform/ui/src/views/developerKit/SqlEditor.tsx
2026-07-06 12:44:26 +03:00

244 lines
7.1 KiB
TypeScript

import { useRef, forwardRef, useImperativeHandle } from 'react'
import Editor, { Monaco } from '@monaco-editor/react'
import { useConfig } from '@/components/ui/ConfigProvider'
import type { editor } from 'monaco-editor'
interface SqlEditorProps {
value: string
onChange: (value: string | undefined) => void
onExecute?: () => void
onSave?: () => void
readOnly?: boolean
height?: string
}
export interface SqlEditorRef {
getSelectedText: () => string
preserveSelection: () => any
restoreSelection: (selection: any) => void
}
const SqlEditor = forwardRef<SqlEditorRef, SqlEditorProps>((
{
value,
onChange,
onExecute,
onSave,
readOnly = false,
}: SqlEditorProps,
ref
) => {
const { mode } = useConfig()
const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null)
const monacoRef = useRef<Monaco | null>(null)
useImperativeHandle(ref, () => ({
getSelectedText: () => {
if (!editorRef.current) return ''
const selection = editorRef.current.getSelection()
if (!selection) return ''
return editorRef.current.getModel()?.getValueInRange(selection) || ''
},
preserveSelection: () => {
if (!editorRef.current) return null
return editorRef.current.getSelection()
},
restoreSelection: (selection: any) => {
if (!editorRef.current || !selection) return
editorRef.current.setSelection(selection)
editorRef.current.revealRangeInCenter(selection)
},
}))
const handleEditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => {
editorRef.current = editor
monacoRef.current = monaco
// Add keyboard shortcuts
editor.addCommand(monaco.KeyCode.F5, () => {
if (onExecute) onExecute()
})
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
if (onSave) onSave()
})
// Configure SQL language features
monaco.languages.registerCompletionItemProvider('sql', {
provideCompletionItems: (model: any, position: any) => {
const word = model.getWordUntilPosition(position)
const range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn,
}
const suggestions = [
// SQL Keywords
{
label: 'SELECT',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'SELECT ',
range: range,
},
{
label: 'FROM',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'FROM ',
range: range,
},
{
label: 'WHERE',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'WHERE ',
range: range,
},
{
label: 'INSERT INTO',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'INSERT INTO ',
range: range,
},
{
label: 'UPDATE',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'UPDATE ',
range: range,
},
{
label: 'DELETE FROM',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'DELETE FROM ',
range: range,
},
{
label: 'CREATE TABLE',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'CREATE TABLE ',
range: range,
},
{
label: 'CREATE PROCEDURE',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'CREATE PROCEDURE ${1:ProcedureName}\nAS\nBEGIN\n\t$0\nEND',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: range,
},
{
label: 'CREATE FUNCTION',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText:
'CREATE FUNCTION ${1:FunctionName}()\nRETURNS ${2:ReturnType}\nAS\nBEGIN\n\t$0\nEND',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: range,
},
{
label: 'CREATE VIEW',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'CREATE VIEW ${1:ViewName}\nAS\n\tSELECT $0',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: range,
},
{
label: 'JOIN',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'JOIN ',
range: range,
},
{
label: 'LEFT JOIN',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'LEFT JOIN ',
range: range,
},
{
label: 'RIGHT JOIN',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'RIGHT JOIN ',
range: range,
},
{
label: 'INNER JOIN',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'INNER JOIN ',
range: range,
},
{
label: 'GROUP BY',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'GROUP BY ',
range: range,
},
{
label: 'ORDER BY',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'ORDER BY ',
range: range,
},
{
label: 'HAVING',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'HAVING ',
range: range,
},
]
return { suggestions: suggestions }
},
})
// Focus the editor
editor.focus()
}
const editorOptions: editor.IStandaloneEditorConstructionOptions = {
readOnly: readOnly,
minimap: { enabled: true },
fontSize: 14,
lineNumbers: 'on',
roundedSelection: false,
scrollBeyondLastLine: false,
automaticLayout: true,
tabSize: 2,
wordWrap: 'on',
folding: true,
glyphMargin: true,
lineDecorationsWidth: 10,
lineNumbersMinChars: 3,
renderLineHighlight: 'all',
scrollbar: {
vertical: 'visible',
horizontal: 'visible',
useShadows: false,
verticalHasArrows: false,
horizontalHasArrows: false,
verticalScrollbarSize: 10,
horizontalScrollbarSize: 10,
},
suggest: {
showKeywords: true,
showSnippets: true,
},
}
return (
<div className="h-full w-full relative">
<div className="absolute inset-0">
<Editor
height="100%"
defaultLanguage="sql"
value={value}
onChange={onChange}
onMount={handleEditorDidMount}
theme={mode === 'dark' ? 'vs-dark' : 'light'}
options={editorOptions}
/>
</div>
</div>
)
})
SqlEditor.displayName = 'SqlEditor'
export default SqlEditor