mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 17:08:34 +00:00
Fix: shortcut can be used throughtout the editor and added custom hook in codeeditor for query panel shortcuts.
This commit is contained in:
parent
191348efdc
commit
f234384f25
5 changed files with 103 additions and 31 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
58
frontend/src/AppBuilder/CodeEditor/useQueryPanelKeyHooks.js
Normal file
58
frontend/src/AppBuilder/CodeEditor/useQueryPanelKeyHooks.js
Normal 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,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue