Fix: shortcut can be used throughtout the editor and added custom hook in codeeditor for query panel shortcuts.

This commit is contained in:
devanshu052000 2025-03-07 15:34:31 +05:30
parent 191348efdc
commit f234384f25
5 changed files with 103 additions and 31 deletions

View file

@ -21,6 +21,7 @@ import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { search, searchKeymap, searchPanelOpen } from '@codemirror/search'; import { search, searchKeymap, searchPanelOpen } from '@codemirror/search';
import { handleSearchPanel, SearchBtn } from './SearchBox'; import { handleSearchPanel, SearchBtn } from './SearchBox';
import { useQueryPanelKeyHooks } from './useQueryPanelKeyHooks';
const langSupport = Object.freeze({ const langSupport = Object.freeze({
javascript: javascript(), javascript: javascript(),
@ -64,6 +65,8 @@ const MultiLineCodeEditor = (props) => {
const [editorView, setEditorView] = React.useState(null); const [editorView, setEditorView] = React.useState(null);
const { queryPanelKeybindings } = useQueryPanelKeyHooks(onChange, currentValueRef, 'multiline');
const handleOnBlur = () => { const handleOnBlur = () => {
if (!delayOnChange) return onChange(currentValueRef.current); if (!delayOnChange) return onChange(currentValueRef.current);
setTimeout(() => { setTimeout(() => {
@ -85,6 +88,7 @@ const MultiLineCodeEditor = (props) => {
highlightActiveLine: false, highlightActiveLine: false,
autocompletion: hideSuggestion ?? true, autocompletion: hideSuggestion ?? true,
highlightActiveLineGutter: false, highlightActiveLineGutter: false,
defaultKeymap: false,
completionKeymap: true, completionKeymap: true,
searchKeymap: false, 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([ const customTabKeymap = keymap.of([
{ {
key: 'Tab', key: 'Tab',
@ -208,6 +217,7 @@ const MultiLineCodeEditor = (props) => {
return true; return true;
}, },
}, },
...queryPanelKeybindings,
]); ]);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps

View file

@ -22,6 +22,7 @@ import CodeHinter from './CodeHinter';
import { removeNestedDoubleCurlyBraces } from '@/_helpers/utils'; import { removeNestedDoubleCurlyBraces } from '@/_helpers/utils';
import useStore from '@/AppBuilder/_stores/store'; import useStore from '@/AppBuilder/_stores/store';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { useQueryPanelKeyHooks } from './useQueryPanelKeyHooks';
const SingleLineCodeEditor = ({ componentName, fieldMeta = {}, componentId, ...restProps }) => { const SingleLineCodeEditor = ({ componentName, fieldMeta = {}, componentId, ...restProps }) => {
const { initialValue, onChange, enablePreview = true, portalProps } = restProps; const { initialValue, onChange, enablePreview = true, portalProps } = restProps;
@ -170,6 +171,8 @@ const EditorInput = ({
onInputChange, onInputChange,
}) => { }) => {
const getSuggestions = useStore((state) => state.getSuggestions, shallow); const getSuggestions = useStore((state) => state.getSuggestions, shallow);
const { queryPanelKeybindings } = useQueryPanelKeyHooks(onBlurUpdate, currentValue, 'singleline');
function autoCompleteExtensionConfig(context) { function autoCompleteExtensionConfig(context) {
const hints = getSuggestions(); const hints = getSuggestions();
let word = context.matchBefore(/\w*/); let word = context.matchBefore(/\w*/);
@ -229,7 +232,10 @@ const EditorInput = ({
maxRenderedOptions: 10, 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([ const customTabKeymap = keymap.of([
{ {
key: 'Tab', key: 'Tab',
@ -251,6 +257,7 @@ const EditorInput = ({
} }
}, },
}, },
...queryPanelKeybindings,
]); ]);
const handleOnChange = React.useCallback((val) => { const handleOnChange = React.useCallback((val) => {
@ -395,6 +402,7 @@ const EditorInput = ({
foldGutter: false, foldGutter: false,
highlightActiveLine: false, highlightActiveLine: false,
autocompletion: true, autocompletion: true,
defaultKeymap: false,
completionKeymap: true, completionKeymap: true,
searchKeymap: false, searchKeymap: false,
}} }}

View file

@ -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,
};
};

View file

@ -4,45 +4,23 @@ import { useHotkeys } from 'react-hotkeys-hook';
import { useModuleId } from '@/AppBuilder/_contexts/ModuleContext'; import { useModuleId } from '@/AppBuilder/_contexts/ModuleContext';
const QueryKeyHooks = ({ children, isExpanded }) => { const QueryKeyHooks = ({ children, isExpanded }) => {
const runQuery = useStore((state) => state.queryPanel.runQuery); const runQueryOnShortcut = useStore((state) => state.queryPanel.runQueryOnShortcut);
const selectedQuery = useStore((state) => state.queryPanel.selectedQuery); const previewQueryOnShortcut = useStore((state) => state.queryPanel.previewQueryOnShortcut);
const moduleId = useModuleId(); const moduleId = useModuleId();
const previewQuery = useStore((state) => state.queryPanel.previewQuery);
const selectedDataSource = useStore((state) => state.queryPanel.selectedDataSource);
const queryName = selectedQuery?.name ?? '';
const previewButtonOnClick = () => { useHotkeys(
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(
['mod+enter', 'mod+shift+enter'], ['mod+enter', 'mod+shift+enter'],
(event, handler) => { (event, handler) => {
if (handler.mod && handler.keys[0] === 'enter') { if (handler.mod && handler.keys[0] === 'enter') {
if (handler.shift) { if (handler.shift) {
previewButtonOnClick(); previewQueryOnShortcut(moduleId);
} else runQuery(selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {}, true); } else runQueryOnShortcut();
} }
}, },
{ enabled: isExpanded } { enabled: isExpanded, enableOnFormTags: ['input'] }
); );
return ( return <div className="row main-row">{children}</div>;
<div ref={shortcutRef} tabIndex={-1} className="row main-row">
{children}
</div>
);
}; };
export default QueryKeyHooks; export default QueryKeyHooks;

View file

@ -1028,5 +1028,23 @@ export const createQueryPanelSlice = (set, get) => ({
isQuerySelected: (queryId) => { isQuerySelected: (queryId) => {
return get().queryPanel.selectedQuery?.id === queryId; return get().queryPanel.selectedQuery?.id === queryId;
}, },
runQueryOnShortcut: () => {
const { queryPanel } = get();
const { runQuery, selectedQuery } = queryPanel;
runQuery(selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {}, true);
},
previewQueryOnShortcut: (moduleId = 'canvas') => {
const { queryPanel } = get();
const { previewQuery, selectedQuery, selectedDataSource } = queryPanel;
const query = {
data_source_id: selectedDataSource.id === 'null' ? null : selectedDataSource.id,
pluginId: selectedDataSource.pluginId,
options: { ...selectedQuery?.options },
kind: selectedDataSource.kind,
name: selectedQuery?.name ?? '',
id: selectedQuery?.id,
};
previewQuery(query, false, undefined, moduleId);
},
}, },
}); });