mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
sql editor context menu
This commit is contained in:
parent
72181e70a1
commit
670e3d127e
9
CHANGELOG.md
Normal file
9
CHANGELOG.md
Normal 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
|
@ -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
|
||||||
|
29
packages/web/src/sqleditor/SqlEditorContextMenu.js
Normal file
29
packages/web/src/sqleditor/SqlEditorContextMenu.js
Normal 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>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -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}>
|
||||||
|
Loading…
Reference in New Issue
Block a user