diff --git a/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx b/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx index 033d266e03..44259ce3a8 100644 --- a/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx +++ b/frontend/src/AppBuilder/CodeEditor/MultiLineCodeEditor.jsx @@ -21,6 +21,7 @@ import useStore from '@/AppBuilder/_stores/store'; import { shallow } from 'zustand/shallow'; import { search, searchKeymap, searchPanelOpen } from '@codemirror/search'; import { handleSearchPanel, SearchBtn } from './SearchBox'; +import { useQueryPanelKeyHooks } from './useQueryPanelKeyHooks'; const langSupport = Object.freeze({ javascript: javascript(), @@ -64,6 +65,8 @@ const MultiLineCodeEditor = (props) => { const [editorView, setEditorView] = React.useState(null); + const { queryPanelKeybindings } = useQueryPanelKeyHooks(onChange, currentValueRef, 'multiline'); + const handleOnBlur = () => { if (!delayOnChange) return onChange(currentValueRef.current); setTimeout(() => { @@ -85,6 +88,7 @@ const MultiLineCodeEditor = (props) => { highlightActiveLine: false, autocompletion: hideSuggestion ?? true, highlightActiveLineGutter: false, + defaultKeymap: false, completionKeymap: true, searchKeymap: false, }; @@ -187,7 +191,12 @@ const MultiLineCodeEditor = (props) => { }; } - const customKeyMaps = [...defaultKeymap, ...completionKeymap, ...searchKeymap]; + const customKeyMaps = [ + ...defaultKeymap.filter((keyBinding) => keyBinding.key !== 'Mod-Enter'), // Remove default keybinding for Mod-Enter + ...completionKeymap, + ...searchKeymap, + ]; + const customTabKeymap = keymap.of([ { key: 'Tab', @@ -208,6 +217,7 @@ const MultiLineCodeEditor = (props) => { return true; }, }, + ...queryPanelKeybindings, ]); // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx b/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx index 1243f26f43..09489a6e2f 100644 --- a/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx +++ b/frontend/src/AppBuilder/CodeEditor/SingleLineCodeEditor.jsx @@ -22,6 +22,7 @@ import CodeHinter from './CodeHinter'; import { removeNestedDoubleCurlyBraces } from '@/_helpers/utils'; import useStore from '@/AppBuilder/_stores/store'; import { shallow } from 'zustand/shallow'; +import { useQueryPanelKeyHooks } from './useQueryPanelKeyHooks'; const SingleLineCodeEditor = ({ componentName, fieldMeta = {}, componentId, ...restProps }) => { const { initialValue, onChange, enablePreview = true, portalProps } = restProps; @@ -170,6 +171,8 @@ const EditorInput = ({ onInputChange, }) => { const getSuggestions = useStore((state) => state.getSuggestions, shallow); + const { queryPanelKeybindings } = useQueryPanelKeyHooks(onBlurUpdate, currentValue, 'singleline'); + function autoCompleteExtensionConfig(context) { const hints = getSuggestions(); let word = context.matchBefore(/\w*/); @@ -229,7 +232,10 @@ const EditorInput = ({ maxRenderedOptions: 10, }); - const customKeyMaps = [...defaultKeymap, ...completionKeymap]; + const customKeyMaps = [ + ...defaultKeymap.filter((keyBinding) => keyBinding.key !== 'Mod-Enter'), // Remove default keybinding for Mod-Enter + ...completionKeymap, + ]; const customTabKeymap = keymap.of([ { key: 'Tab', @@ -251,6 +257,7 @@ const EditorInput = ({ } }, }, + ...queryPanelKeybindings, ]); const handleOnChange = React.useCallback((val) => { @@ -395,6 +402,7 @@ const EditorInput = ({ foldGutter: false, highlightActiveLine: false, autocompletion: true, + defaultKeymap: false, completionKeymap: true, searchKeymap: false, }} diff --git a/frontend/src/AppBuilder/CodeEditor/useQueryPanelKeyHooks.js b/frontend/src/AppBuilder/CodeEditor/useQueryPanelKeyHooks.js new file mode 100644 index 0000000000..1a41a7f19b --- /dev/null +++ b/frontend/src/AppBuilder/CodeEditor/useQueryPanelKeyHooks.js @@ -0,0 +1,58 @@ +import { useModuleId } from '@/AppBuilder/_contexts/ModuleContext'; +import useStore from '@/AppBuilder/_stores/store'; +import { useCallback, useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; + +export const useQueryPanelKeyHooks = (onChange, value, type) => { + const queryPanelHeight = useStore((state) => state.queryPanel.queryPanelHeight); + const runQueryOnShortcut = useStore((state) => state.queryPanel.runQueryOnShortcut); + const previewQueryOnShortcut = useStore((state) => state.queryPanel.previewQueryOnShortcut); + const moduleId = useModuleId(); + const location = useLocation(); + const { pathname } = location; + + const [queryPanelKeybindings, setQueryPanelKeybindings] = useState([]); + + const handleRunQuery = useCallback( + (view) => { + const isEditor = pathname.includes('/apps/'); + if (queryPanelHeight !== 0 && isEditor) { + onChange(type === 'multiline' ? value.current : value); + runQueryOnShortcut(); + } + return true; + }, + [queryPanelHeight, onChange, runQueryOnShortcut, value] + ); + + const handlePreviewQuery = useCallback( + (view) => { + const isEditor = pathname.includes('/apps/'); + if (queryPanelHeight !== 0 && isEditor) { + onChange(type === 'multiline' ? value.current : value); + previewQueryOnShortcut(moduleId); + } + return true; + }, + [queryPanelHeight, moduleId, onChange, previewQueryOnShortcut, value] + ); + + useEffect(() => { + setQueryPanelKeybindings([ + { + key: 'Mod-Enter', + preventDefault: true, + run: handleRunQuery, + }, + { + key: 'Mod-Shift-Enter', + preventDefault: true, + run: handlePreviewQuery, + }, + ]); + }, [handleRunQuery, handlePreviewQuery]); + + return { + queryPanelKeybindings, + }; +}; diff --git a/frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx b/frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx index 06d8958cab..13e29d5531 100644 --- a/frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx +++ b/frontend/src/AppBuilder/QueryPanel/QueryKeyHooks.jsx @@ -4,45 +4,23 @@ import { useHotkeys } from 'react-hotkeys-hook'; import { useModuleId } from '@/AppBuilder/_contexts/ModuleContext'; const QueryKeyHooks = ({ children, isExpanded }) => { - const runQuery = useStore((state) => state.queryPanel.runQuery); - const selectedQuery = useStore((state) => state.queryPanel.selectedQuery); + const runQueryOnShortcut = useStore((state) => state.queryPanel.runQueryOnShortcut); + const previewQueryOnShortcut = useStore((state) => state.queryPanel.previewQueryOnShortcut); const moduleId = useModuleId(); - const previewQuery = useStore((state) => state.queryPanel.previewQuery); - const selectedDataSource = useStore((state) => state.queryPanel.selectedDataSource); - const queryName = selectedQuery?.name ?? ''; - const previewButtonOnClick = () => { - const _options = { ...selectedQuery.options }; - const query = { - data_source_id: selectedDataSource.id === 'null' ? null : selectedDataSource.id, - pluginId: selectedDataSource.pluginId, - options: _options, - kind: selectedDataSource.kind, - name: queryName, - id: selectedQuery?.id, - }; - previewQuery(query, false, undefined, moduleId).catch(({ error, data }) => { - console.log(error, data); - }); - }; - - const shortcutRef = useHotkeys( + useHotkeys( ['mod+enter', 'mod+shift+enter'], (event, handler) => { if (handler.mod && handler.keys[0] === 'enter') { if (handler.shift) { - previewButtonOnClick(); - } else runQuery(selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {}, true); + previewQueryOnShortcut(moduleId); + } else runQueryOnShortcut(); } }, - { enabled: isExpanded } + { enabled: isExpanded, enableOnFormTags: ['input'] } ); - return ( -