diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 971c9eee1..4e3967f1b 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -50,6 +50,7 @@ "react-cookie": "^4.1.1", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.11", + "react-hotkeys-hook": "^4.5.0", "react-icons": "^5.0.1", "react-laag": "^2.0.5", "react-markdown": "^8.0.7", @@ -10471,6 +10472,15 @@ "react": ">=16.13.1" } }, + "node_modules/react-hotkeys-hook": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.5.0.tgz", + "integrity": "sha512-Samb85GSgAWFQNvVt3PS90LPPGSf9mkH/r4au81ZP1yOIFayLC3QAvqTgGtJ8YEDMXtPmaVBs6NgipHO6h4Mug==", + "peerDependencies": { + "react": ">=16.8.1", + "react-dom": ">=16.8.1" + } + }, "node_modules/react-icons": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz", diff --git a/src/frontend/package.json b/src/frontend/package.json index fd27d8bd6..e298a84fb 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -45,6 +45,7 @@ "react-cookie": "^4.1.1", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.11", + "react-hotkeys-hook": "^4.5.0", "react-icons": "^5.0.1", "react-laag": "^2.0.5", "react-markdown": "^8.0.7", diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index 8a134f48a..9d097cb12 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -31,6 +31,7 @@ import { } from "../../../../utils/reactflowUtils"; import { classNames } from "../../../../utils/utils"; import ToolbarSelectItem from "./toolbarSelectItem"; +import { useHotkeys } from "react-hotkeys-hook"; export default function NodeToolbarComponent({ data, @@ -65,6 +66,92 @@ export default function NodeToolbarComponent({ const hasApiKey = useStoreStore((state) => state.hasApiKey); const validApiKey = useStoreStore((state) => state.validApiKey); + function handleMinimizeWShortcut(e: KeyboardEvent) { + e.preventDefault() + if (isMinimal) { + setShowState((show) => !show); + setShowNode(data.showNode ?? true ? false : true); + return; + } + setNoticeData({ + title: + "Minimization are only available for nodes with one handle or fewer.", + }); + return; + } + + function handleUpdateWShortcut(e: KeyboardEvent) { + e.preventDefault() + if ((hasApiKey || hasStore)) { + handleSelectChange("update"); + } + } + + function handleGroupWShortcut(e: KeyboardEvent) { + e.preventDefault() + if (isGroup) { + handleSelectChange("ungroup"); + } + } + + function handleShareWShortcut(e: KeyboardEvent) { + e.preventDefault() + if (hasApiKey || hasStore) { + setShowconfirmShare((state) => !state); + } + } + + function handleCodeWShortcut(e: KeyboardEvent) { + e.preventDefault() + if (hasCode) return setOpenModal((state) => !state); + setNoticeData({ title: `You can not access ${data.id} code` }); + } + + function handleAdvancedWShortcut(e: KeyboardEvent) { + e.preventDefault() + if (!isGroup) { + setShowModalAdvanced((state) => !state); + } + } + + function handleSaveWShortcut(e: KeyboardEvent) { + e.preventDefault() + if (isSaved) { + setShowOverrideModal((state) => !state); + return; + } + if (hasCode) { + saveComponent(cloneDeep(data), false); + setSuccessData({ title: `${data.id} saved successfully` }); + return; + } + } + + function handleDocsWShortcut(e: KeyboardEvent) { + e.preventDefault(); + if (data.node?.documentation) { + return openInNewTab(data.node?.documentation); + } + setNoticeData({ + title: `${data.id} docs is not available at the moment.`, + }); + } + + function handleDownloadWShortcut(e: KeyboardEvent) { + e.preventDefault(); + downloadNode(flowComponent!); + } + + useHotkeys("ctrl+q", handleMinimizeWShortcut); + useHotkeys("ctrl+u", handleUpdateWShortcut); + useHotkeys("ctrl+g", handleGroupWShortcut); + useHotkeys("ctrl+shift+s", handleShareWShortcut); + useHotkeys("ctrl+shift+u", handleCodeWShortcut); + useHotkeys("ctrl+shift+a", handleAdvancedWShortcut); + useHotkeys("ctrl+s", handleSaveWShortcut); + useHotkeys("ctrl+shift+d", handleDocsWShortcut); + useHotkeys("ctrl+j", handleDownloadWShortcut); + const isMinimal = numberOfHandles <= 1; const isGroup = data.node?.flow ? true : false; @@ -265,112 +352,6 @@ export default function NodeToolbarComponent({ const [openModal, setOpenModal] = useState(false); const hasCode = Object.keys(data.node!.template).includes("code"); - useEffect(() => { - function onKeyDown(event: KeyboardEvent) { - if ( - selected && - (hasApiKey || hasStore) && - (event.ctrlKey || event.metaKey) && - event.key === "u" - ) { - event.preventDefault(); - handleSelectChange("update"); - } - if ( - selected && - isGroup && - (event.ctrlKey || event.metaKey) && - event.key === "g" - ) { - event.preventDefault(); - handleSelectChange("ungroup"); - } - if ( - selected && - (hasApiKey || hasStore) && - (event.ctrlKey || event.metaKey) && - event.shiftKey && - event.key === "S" - ) { - event.preventDefault(); - setShowconfirmShare((state) => !state); - } - - if ( - selected && - (event.ctrlKey || event.metaKey) && - event.shiftKey && - event.key === "Q" - ) { - event.preventDefault(); - if (isMinimal) { - setShowState((show) => !show); - setShowNode(data.showNode ?? true ? false : true); - return; - } - setNoticeData({ - title: - "Minimization are only available for nodes with one handle or fewer.", - }); - } - if ( - selected && - (event.ctrlKey || event.metaKey) && - event.shiftKey && - event.key === "U" - ) { - event.preventDefault(); - if (hasCode) return setOpenModal((state) => !state); - setNoticeData({ title: `You can not access ${data.id} code` }); - } - if ( - selected && - !isGroup && - (event.ctrlKey || event.metaKey) && - event.shiftKey && - event.key === "A" - ) { - event.preventDefault(); - setShowModalAdvanced((state) => !state); - } - if (selected && (event.ctrlKey || event.metaKey) && event.key === "s") { - if (isSaved) { - event.preventDefault(); - return setShowOverrideModal((state) => !state); - } - if (hasCode) { - event.preventDefault(); - saveComponent(cloneDeep(data), false); - setSuccessData({ title: `${data.id} saved successfully` }); - } - } - if ( - selected && - (event.ctrlKey || event.metaKey) && - event.shiftKey && - event.key === "D" - ) { - event.preventDefault(); - if (data.node?.documentation) { - return openInNewTab(data.node?.documentation); - } - setNoticeData({ - title: `${data.id} docs is not available at the moment.`, - }); - } - if (selected && (event.ctrlKey || event.metaKey) && event.key === "j") { - event.preventDefault(); - downloadNode(flowComponent!); - } - } - - document.addEventListener("keydown", onKeyDown); - - return () => { - document.removeEventListener("keydown", onKeyDown); - }; - }, [isSaved, showNode, data.showNode, isMinimal]); - return ( <>
@@ -567,7 +548,7 @@ export default function NodeToolbarComponent({ icon={showNode ? "Minimize2" : "Maximize2"} value={showNode ? "Minimize" : "Expand"} isMac={navigator.userAgent.toUpperCase().includes("MAC")} - shift={true} + shift={false} keyboardKey={"Q"} dataTestId={"minimize-button-nodeToolbar"} />