sql editor context menu

This commit is contained in:
Jan Prochazka 2021-02-14 10:50:55 +01:00
parent 72181e70a1
commit 670e3d127e
4 changed files with 96 additions and 17 deletions

9
CHANGELOG.md Normal file
View File

@ -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

View File

@ -9,6 +9,9 @@ import useShowModal from '../modals/showModal';
import InsertJoinModal from '../modals/InsertJoinModal'; import InsertJoinModal from '../modals/InsertJoinModal';
import { getDatabaseInfo } from '../utility/metadataLoaders'; import { getDatabaseInfo } from '../utility/metadataLoaders';
import useTheme from '../theme/useTheme'; import useTheme from '../theme/useTheme';
import { useShowMenu } from '../modals/showMenu';
import SqlEditorContextMenu from './SqlEditorContextMenu';
import sqlFormatter from 'sql-formatter';
const Wrapper = styled.div` const Wrapper = styled.div`
position: absolute; position: absolute;
@ -53,10 +56,12 @@ export default function SqlEditor({
focusOnCreate = false, focusOnCreate = false,
conid = undefined, conid = undefined,
database = undefined, database = undefined,
onExecute = undefined,
}) { }) {
const [containerRef, { height, width }] = useDimensions(); const [containerRef, { height, width }] = useDimensions();
const ownEditorRef = React.useRef(null); const ownEditorRef = React.useRef(null);
const theme = useTheme(); const theme = useTheme();
const showMenu = useShowMenu();
const currentEditorRef = editorRef || ownEditorRef; const currentEditorRef = editorRef || ownEditorRef;
const showModal = useShowModal(); const showModal = useShowModal();
@ -73,23 +78,27 @@ export default function SqlEditor({
currentEditorRef.current.editor.focus(); currentEditorRef.current.editor.focus();
}, [tabVisible, focusOnCreate]); }, [tabVisible, focusOnCreate]);
const handleInsertJoin = async () => {
const dbinfo = await getDatabaseInfo({ conid, database });
showModal(modalState => (
<InsertJoinModal
sql={currentEditorRef.current.editor.getValue()}
modalState={modalState}
engine={engine}
dbinfo={dbinfo}
onInsert={text => {
const editor = currentEditorRef.current.editor;
editor.session.insert(editor.getCursorPosition(), text);
}}
/>
));
};
const handleKeyDown = React.useCallback( const handleKeyDown = React.useCallback(
async (data, hash, keyString, keyCode, event) => { (data, hash, keyString, keyCode, event) => {
if (keyCode == keycodes.j && event.ctrlKey && !readOnly && tabVisible) { if (keyCode == keycodes.j && event.ctrlKey && !readOnly && tabVisible) {
event.preventDefault(); event.preventDefault();
const dbinfo = await getDatabaseInfo({ conid, database }); handleInsertJoin();
showModal(modalState => (
<InsertJoinModal
sql={currentEditorRef.current.editor.getValue()}
modalState={modalState}
engine={engine}
dbinfo={dbinfo}
onInsert={text => {
const editor = currentEditorRef.current.editor;
editor.session.insert(editor.getCursorPosition(), text);
}}
/>
));
} }
if (onKeyDown) onKeyDown(data, hash, keyString, keyCode, event); if (onKeyDown) onKeyDown(data, hash, keyString, keyCode, event);
@ -100,12 +109,40 @@ export default function SqlEditor({
React.useEffect(() => { React.useEffect(() => {
if ((onKeyDown || !readOnly) && currentEditorRef.current) { if ((onKeyDown || !readOnly) && currentEditorRef.current) {
currentEditorRef.current.editor.keyBinding.addKeyboardHandler(handleKeyDown); currentEditorRef.current.editor.keyBinding.addKeyboardHandler(handleKeyDown);
return () => {
currentEditorRef.current.editor.keyBinding.removeKeyboardHandler(handleKeyDown);
};
} }
return () => {
currentEditorRef.current.editor.keyBinding.removeKeyboardHandler(handleKeyDown);
};
}, [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, <SqlEditorContextMenu {...menuRefs.current} />);
}, []);
React.useEffect(() => {
if (currentEditorRef.current) {
currentEditorRef.current.editor.container.addEventListener('contextmenu', handleContextMenu);
return () => {
currentEditorRef.current.editor.container.removeEventListener('contextmenu', handleContextMenu);
};
}
}, [handleContextMenu]);
return ( return (
<Wrapper ref={containerRef}> <Wrapper ref={containerRef}>
<AceEditor <AceEditor

View File

@ -0,0 +1,29 @@
import React from 'react';
import { DropDownMenuItem, DropDownMenuDivider } from '../modals/DropDownMenu';
export default function SqlEditorContextMenu({ execute, insertJoin, toggleComment, formatCode }) {
return (
<>
{!!execute && (
<DropDownMenuItem onClick={execute} keyText="F5 or Ctrl+Enter">
Execute query
</DropDownMenuItem>
)}
{!!insertJoin && (
<DropDownMenuItem onClick={insertJoin} keyText="Ctrl+J">
Insert SQL Join
</DropDownMenuItem>
)}
{!!toggleComment && (
<DropDownMenuItem onClick={toggleComment} keyText="Ctrl+/">
Toggle comment
</DropDownMenuItem>
)}
{!!formatCode && (
<DropDownMenuItem onClick={formatCode} >
Format code
</DropDownMenuItem>
)}
</>
);
}

View File

@ -24,6 +24,7 @@ import useExtensions from '../utility/useExtensions';
import useTimerLabel from '../utility/useTimerLabel'; import useTimerLabel from '../utility/useTimerLabel';
import { StatusBarItem } from '../widgets/StatusBar'; import { StatusBarItem } from '../widgets/StatusBar';
import ToolbarPortal from '../utility/ToolbarPortal'; import ToolbarPortal from '../utility/ToolbarPortal';
import { useShowMenu } from '../modals/showMenu';
function createSqlPreview(sql) { function createSqlPreview(sql) {
if (!sql) return undefined; if (!sql) return undefined;
@ -131,6 +132,8 @@ export default function QueryTab({
sql: selectedText || editorData, sql: selectedText || editorData,
}); });
}; };
// const handleExecuteRef = React.useRef(handleExecute);
// handleExecuteRef.current = handleExecute;
// const handleCancel = () => { // const handleCancel = () => {
// axios.post('sessions/cancel', { // axios.post('sessions/cancel', {
@ -186,6 +189,7 @@ export default function QueryTab({
editorRef={editorRef} editorRef={editorRef}
conid={conid} conid={conid}
database={database} database={database}
onExecute={handleExecute}
/> />
{visibleResultTabs && ( {visibleResultTabs && (
<ResultTabs sessionId={sessionId} executeNumber={executeNumber}> <ResultTabs sessionId={sessionId} executeNumber={executeNumber}>