From 670e3d127e82b0de4cf14df221bb4b6287e384c4 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 14 Feb 2021 10:50:55 +0100 Subject: [PATCH] sql editor context menu --- CHANGELOG.md | 9 +++ packages/web/src/sqleditor/SqlEditor.js | 71 ++++++++++++++----- .../web/src/sqleditor/SqlEditorContextMenu.js | 29 ++++++++ packages/web/src/tabs/QueryTab.js | 4 ++ 4 files changed, 96 insertions(+), 17 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 packages/web/src/sqleditor/SqlEditorContextMenu.js diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..ea638606 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# ChangeLog + +### 3.9.6 +- ADDED: Connect using SSH Tunnel +- ADDED: Connect using SSL +- ADDED: Database connection dialog redesigned +- ADDED: #63 Ctrl+Enter runs query +- ADDED: Published dbgate NPM package +- ADDED: SQL editor context menu diff --git a/packages/web/src/sqleditor/SqlEditor.js b/packages/web/src/sqleditor/SqlEditor.js index aa92d83c..61d08465 100644 --- a/packages/web/src/sqleditor/SqlEditor.js +++ b/packages/web/src/sqleditor/SqlEditor.js @@ -9,6 +9,9 @@ import useShowModal from '../modals/showModal'; import InsertJoinModal from '../modals/InsertJoinModal'; import { getDatabaseInfo } from '../utility/metadataLoaders'; import useTheme from '../theme/useTheme'; +import { useShowMenu } from '../modals/showMenu'; +import SqlEditorContextMenu from './SqlEditorContextMenu'; +import sqlFormatter from 'sql-formatter'; const Wrapper = styled.div` position: absolute; @@ -53,10 +56,12 @@ export default function SqlEditor({ focusOnCreate = false, conid = undefined, database = undefined, + onExecute = undefined, }) { const [containerRef, { height, width }] = useDimensions(); const ownEditorRef = React.useRef(null); const theme = useTheme(); + const showMenu = useShowMenu(); const currentEditorRef = editorRef || ownEditorRef; const showModal = useShowModal(); @@ -73,23 +78,27 @@ export default function SqlEditor({ currentEditorRef.current.editor.focus(); }, [tabVisible, focusOnCreate]); + const handleInsertJoin = async () => { + const dbinfo = await getDatabaseInfo({ conid, database }); + showModal(modalState => ( + { + const editor = currentEditorRef.current.editor; + editor.session.insert(editor.getCursorPosition(), text); + }} + /> + )); + }; + const handleKeyDown = React.useCallback( - async (data, hash, keyString, keyCode, event) => { + (data, hash, keyString, keyCode, event) => { if (keyCode == keycodes.j && event.ctrlKey && !readOnly && tabVisible) { event.preventDefault(); - const dbinfo = await getDatabaseInfo({ conid, database }); - showModal(modalState => ( - { - const editor = currentEditorRef.current.editor; - editor.session.insert(editor.getCursorPosition(), text); - }} - /> - )); + handleInsertJoin(); } if (onKeyDown) onKeyDown(data, hash, keyString, keyCode, event); @@ -100,12 +109,40 @@ export default function SqlEditor({ React.useEffect(() => { if ((onKeyDown || !readOnly) && currentEditorRef.current) { currentEditorRef.current.editor.keyBinding.addKeyboardHandler(handleKeyDown); + + return () => { + currentEditorRef.current.editor.keyBinding.removeKeyboardHandler(handleKeyDown); + }; } - return () => { - currentEditorRef.current.editor.keyBinding.removeKeyboardHandler(handleKeyDown); - }; }, [handleKeyDown]); + const handleFormatCode = () => { + currentEditorRef.current.editor.setValue(sqlFormatter.format(editorRef.current.editor.getValue())); + currentEditorRef.current.editor.clearSelection(); + }; + + const menuRefs = React.useRef(null); + menuRefs.current = { + execute: onExecute, + insertJoin: !readOnly ? handleInsertJoin : null, + toggleComment: !readOnly ? () => currentEditorRef.current.editor.execCommand('togglecomment') : null, + formatCode: !readOnly ? handleFormatCode : null, + }; + const handleContextMenu = React.useCallback(event => { + event.preventDefault(); + showMenu(event.pageX, event.pageY, ); + }, []); + + React.useEffect(() => { + if (currentEditorRef.current) { + currentEditorRef.current.editor.container.addEventListener('contextmenu', handleContextMenu); + + return () => { + currentEditorRef.current.editor.container.removeEventListener('contextmenu', handleContextMenu); + }; + } + }, [handleContextMenu]); + return ( + {!!execute && ( + + Execute query + + )} + {!!insertJoin && ( + + Insert SQL Join + + )} + {!!toggleComment && ( + + Toggle comment + + )} + {!!formatCode && ( + + Format code + + )} + + ); +} diff --git a/packages/web/src/tabs/QueryTab.js b/packages/web/src/tabs/QueryTab.js index 78ca35f1..bbdd26e2 100644 --- a/packages/web/src/tabs/QueryTab.js +++ b/packages/web/src/tabs/QueryTab.js @@ -24,6 +24,7 @@ import useExtensions from '../utility/useExtensions'; import useTimerLabel from '../utility/useTimerLabel'; import { StatusBarItem } from '../widgets/StatusBar'; import ToolbarPortal from '../utility/ToolbarPortal'; +import { useShowMenu } from '../modals/showMenu'; function createSqlPreview(sql) { if (!sql) return undefined; @@ -131,6 +132,8 @@ export default function QueryTab({ sql: selectedText || editorData, }); }; + // const handleExecuteRef = React.useRef(handleExecute); + // handleExecuteRef.current = handleExecute; // const handleCancel = () => { // axios.post('sessions/cancel', { @@ -186,6 +189,7 @@ export default function QueryTab({ editorRef={editorRef} conid={conid} database={database} + onExecute={handleExecute} /> {visibleResultTabs && (