diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index a14b124c7..8e0d70f24 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -823,16 +823,16 @@ export const defaultShortcuts = [ }, { name: "Open playground", - shortcut: "Ctrl K" + shortcut: "Ctrl + k" }, { name: "Undo", - shortcut: "Ctrl Z" + shortcut: "Ctrl + z" }, { name: "Redo", - shortcut: "Ctrl Y" + shortcut: "Ctrl + y" } ]; -export const unavailableShortcutss = ["Ctrl + Shift + a", "Ctrl + q", "Ctrl + Shift + u", "Ctrl + c", "Ctrl + d", "Ctrl + Shift + s", "Ctrl + Shift + d", "Ctrl + s", "Backspace", "Ctrl + K", "Ctrl + Z", "Ctrl + Y"]; +export const unavailableShortcutss = ["CTRL + SHIFT + A", "CTRL + Q", "CTRL + SHIFT + U", "CTRL + C", "CTRL + D", "CTRL + SHIFT + S", "CTRL + SHIFT + D", "CTRL + S", "BACKSPACE", "CTRL + K", "CTRL + Z", "CTRL + Y"]; diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 7883317ad..8b10d577b 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -1,5 +1,5 @@ import _, { cloneDeep } from "lodash"; -import { MouseEvent, useCallback, useEffect, useRef, useState } from "react"; +import { KeyboardEvent, MouseEvent, useCallback, useEffect, useRef, useState } from "react"; import ReactFlow, { Background, Connection, @@ -37,6 +37,8 @@ import { import { getRandomName, isWrappedWithClass } from "../../../../utils/utils"; import ConnectionLineComponent from "../ConnectionLineComponent"; import SelectionMenu from "../SelectionMenuComponent"; +import { useHotkeys } from "react-hotkeys-hook"; +import { useShortcutsStore } from "../../../../stores/shortcuts"; const nodeTypes = { genericNode: GenericNode, @@ -140,125 +142,13 @@ export default function Page({ const setNode = useFlowStore((state) => state.setNode); useEffect(() => { - const onKeyDown = (event: KeyboardEvent) => { - const selectedNode = nodes.filter((obj) => obj.selected); - if ( - selectionMenuVisible && - (event.ctrlKey || event.metaKey) && - !event.shiftKey && - event.key === "g" - ) { - event.preventDefault(); - handleGroupNode(); - } - if ( - (event.ctrlKey || event.metaKey) && - event.key === "p" && - !event.shiftKey && - selectedNode.length > 0 - ) { - event.preventDefault(); - setNode(selectedNode[0].id, (old) => ({ - ...old, - data: { - ...old.data, - node: { - ...old.data.node, - frozen: old.data?.node?.frozen ? false : true, - }, - }, - })); - } - if ( - (event.ctrlKey || event.metaKey) && - !event.shiftKey && - event.key === "d" && - selectedNode.length > 0 - ) { - event.preventDefault(); - paste( - { nodes: selectedNode, edges: [] }, - { - x: position.current.x, - y: position.current.y, - } - ); - } - if (!isWrappedWithClass(event, "noundo")) { - if ( - ((event.key === "y" && !event.shiftKey) || (event.key === "z" && event.shiftKey)) && - (event.ctrlKey || event.metaKey) - ) { - event.preventDefault(); // prevent the default action - redo(); - } else if (event.key === "z" && (event.ctrlKey || event.metaKey) && !event.shiftKey) { - event.preventDefault(); - undo(); - } - } - if ( - !isWrappedWithClass(event, "nocopy") && - window.getSelection()?.toString().length === 0 - ) { - if ( - (event.ctrlKey || event.metaKey) && - !event.shiftKey && - event.key === "c" && - lastSelection - ) { - event.preventDefault(); - setLastCopiedSelection(_.cloneDeep(lastSelection)); - } else if ( - (event.ctrlKey || event.metaKey) && - !event.shiftKey && - event.key === "x" && - lastSelection - ) { - event.preventDefault(); - setLastCopiedSelection(_.cloneDeep(lastSelection), true); - } else if ( - (event.ctrlKey || event.metaKey) && - !event.shiftKey && - event.key === "v" && - lastCopiedSelection - ) { - event.preventDefault(); - takeSnapshot(); - paste(lastCopiedSelection, { - x: position.current.x, - y: position.current.y, - }); - } else if ( - (event.ctrlKey || event.metaKey) && - !event.shiftKey && - event.key === "g" && - lastSelection - ) { - event.preventDefault(); - } - } - if (!isWrappedWithClass(event, "nodelete")) { - if ( - (event.key === "Delete" || event.key === "Backspace") && - lastSelection - ) { - event.preventDefault(); - takeSnapshot(); - deleteNode(lastSelection.nodes.map((node) => node.id)); - deleteEdge(lastSelection.edges.map((edge) => edge.id)); - } - } - }; - const handleMouseMove = (event) => { position.current = { x: event.clientX, y: event.clientY }; }; - document.addEventListener("keydown", onKeyDown); document.addEventListener("mousemove", handleMouseMove); return () => { - document.removeEventListener("keydown", onKeyDown); document.removeEventListener("mousemove", handleMouseMove); }; }, [lastCopiedSelection, lastSelection, takeSnapshot, selectionMenuVisible]); @@ -279,6 +169,93 @@ export default function Page({ }; }, []); + + function handleUndo(e: KeyboardEvent) { + e.preventDefault() + if (!isWrappedWithClass(e, "noundo")) { + undo(); + } + } + + function handleRedo(e: KeyboardEvent) { + e.preventDefault() + if (!isWrappedWithClass(e, "noundo")) { + redo(); + } + } + + function handleGroup(e: KeyboardEvent) { + e.preventDefault() + if (selectionMenuVisible) { + handleGroupNode() + } + } + + function handleDuplicate(e: KeyboardEvent) { + const selectedNode = nodes.filter((obj) => obj.selected); + e.preventDefault() + if (selectedNode.length > 0) { + paste( + { nodes: selectedNode, edges: [] }, + { + x: position.current.x, + y: position.current.y, + } + ); + } + } + + function handleCopy(e: KeyboardEvent) { + e.preventDefault() + if (!isWrappedWithClass(e, "nocopy") && + window.getSelection()?.toString().length === 0 && lastSelection) { + setLastCopiedSelection(_.cloneDeep(lastSelection)); + } + } + + function handleCut(e: KeyboardEvent) { + e.preventDefault() + if (!isWrappedWithClass(e, "nocopy") && + window.getSelection()?.toString().length === 0 && lastSelection) { + setLastCopiedSelection(_.cloneDeep(lastSelection), true); + } + } + + function handlePaste(e: KeyboardEvent) { + e.preventDefault() + if (!isWrappedWithClass(e, "nocopy") && + window.getSelection()?.toString().length === 0 && lastCopiedSelection) { + takeSnapshot(); + paste(lastCopiedSelection, { + x: position.current.x, + y: position.current.y, + }); + } + } + + function handleDelete(e: KeyboardEvent) { + e.preventDefault() + if (!isWrappedWithClass(event, "nodelete") && lastSelection) { + takeSnapshot(); + deleteNode(lastSelection.nodes.map((node) => node.id)); + deleteEdge(lastSelection.edges.map((edge) => edge.id)); + } + } + + const undoAction = useShortcutsStore(state => state.undo); + const redoAction = useShortcutsStore(state => state.redo); + const copyAction = useShortcutsStore(state => state.copy); + + useHotkeys(undoAction, handleUndo); + useHotkeys(redoAction, handleRedo); + useHotkeys("mod+g", handleGroup); + useHotkeys("mod+d", handleDuplicate); + useHotkeys(copyAction, handleCopy); + useHotkeys("mod+x", handleCut); + useHotkeys("mod+v", handlePaste); + useHotkeys("backspace", handleDelete); + useHotkeys("delete", handleDelete); + useEffect(() => { setSHowCanvas( Object.keys(templates).length > 0 && Object.keys(types).length > 0 diff --git a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx index 214062935..64e9914fa 100644 --- a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx @@ -32,20 +32,20 @@ export default function EditShortcutButton({ children, shortcut, defaultShortcut const [key, setKey] = useState(isMac ? "Meta" : 'Ctrl'); - const availableFields = Array.from(componentFields).filter( - (field) => !unavaliableFields.has(field) - ); - const addGlobalVariable = useGlobalVariablesStore( - (state) => state.addGlobalVariable - ); - function canEditCombination(newCombination: string): boolean { - return !unavaliableShortcuts.includes(newCombination); - } + let canSave = true; + unavaliableShortcuts.forEach((s) => { + if (s.toLowerCase() === newCombination.toLowerCase()) { + canSave = false + } + }) + return canSave; + } const setUniqueShortcut = useShortcutsStore(state => state.updateUniqueShortcut); function editCombination(): void { + console.log(canEditCombination(key)) if (canEditCombination(key)) { const newCombination = defaultShortcuts.map((s) => { if (s.name === shortcut[0]) { @@ -54,33 +54,31 @@ export default function EditShortcutButton({ children, shortcut, defaultShortcut return {name: s.name, shortcut: s.shortcut}; }) const unavailable = unavaliableShortcuts.map((s) => { - if (s === defaultCombination) return s = key; + if (s.toLowerCase() === defaultCombination.toLowerCase()) return s = key.toUpperCase(); return s; }) const fixCombination = key.split(" ") fixCombination[0] = "mod" const shortcutName = shortcut[0].split(" ")[0].toLowerCase(); - console.log(shortcutName) setUniqueShortcut(shortcutName, fixCombination.join("").toLowerCase()) setShortcuts(newCombination, unavailable) setOpen(false) setSuccessData({title: `${shortcut[0]} shortcut successfully changed`}) - setKey(isMac ? "Meta" : 'Ctrl') + setKey(isMac ? "META" : 'CTRL') return; } setErrorData({title: "Error saving key combination", list: ["This combination already exists!"]}) } useEffect(() => { - if (!open) setKey(isMac ? "Meta" : 'Ctrl') + if (!open) setKey(isMac ? "META" : 'CTRL') }, [open, setOpen]) useEffect(() => { function onKeyDown(e: KeyboardEvent) { e.preventDefault() - console.log(e.key) - if (key.includes(e.key)) return; - setKey(oldKey => `${oldKey} + ${e.key}`) + if (key.toUpperCase().includes(e.key.toUpperCase())) return; + setKey(oldKey => `${oldKey.toUpperCase()} + ${e.key.toUpperCase()}`) } document.addEventListener("keydown", onKeyDown); @@ -107,8 +105,8 @@ export default function EditShortcutButton({ children, shortcut, defaultShortcut {children}
-
- {key} +
+ {key.toUpperCase()}
diff --git a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx index e30ce1021..106a3e779 100644 --- a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx @@ -14,19 +14,6 @@ import EditShortcutButton from "./EditShortcutButton"; import { useShortcutsStore } from "../../../../stores/shortcuts"; export default function ShortcutsPage() { - const advancedShortcut = "Ctrl + Shift + A"; - const minizmizeShortcut = "Ctrl + Shift + Q"; - const codeShortcut = "Ctrl + Shift + C"; - const copyShortcut = "Ctrl + C"; - const duplicateShortcut = "Ctrl + D"; - const shareShortcut = "Ctrl + Shift + S"; - const docsShortcut = "Ctrl + Shift + D"; - const saveShortcut = "Ctrl + S"; - const deleteShortcut = "Backspace"; - const interactionShortcut = "Ctrl + K"; - const undoShortcut = "Ctrl + Z"; - const redoShortcut = "Ctrl + Y"; - const [selectedRows, setSelectedRows] = useState([]); const shortcuts = useShortcutsStore(state => state.shortcuts) @@ -48,56 +35,7 @@ export default function ShortcutsPage() { }, ]); - const [nodesRowData, setNodesRowData] = useState([ - { - name: "Advanced Settings Component", - shortcut: advancedShortcut, - }, - { - name: "Minimize Component", - shortcut: minizmizeShortcut, - }, - { - name: "Code Component", - shortcut: codeShortcut, - }, - { - name: "Copy Component", - shortcut: copyShortcut, - }, - { - name: "Duplicate Component", - shortcut: duplicateShortcut, - }, - { - name: "Share Component", - shortcut: shareShortcut, - }, - { - name: "Docs Component", - shortcut: docsShortcut, - }, - { - name: "Save Component", - shortcut: saveShortcut, - }, - { - name: "Delete Component", - shortcut: deleteShortcut, - }, - { - name: "Open Playground", - shortcut: interactionShortcut, - }, - { - name: "Undo", - shortcut: undoShortcut, - }, - { - name: "Redo", - shortcut: redoShortcut, - }, - ]); + const [nodesRowData, setNodesRowData] = useState>([]); useEffect(() => { setNodesRowData(shortcuts) @@ -107,6 +45,8 @@ export default function ShortcutsPage() { const [open, setOpen] = useState(false); return ( + +
@@ -125,7 +65,7 @@ export default function ShortcutsPage() {
- +
- - - Flow - Shortcuts relating to the flow. - - - { - setSelectedRows(event.api.getSelectedRows().map((row) => row.name)); - }} - suppressRowClickSelection={true} - domLayout="autoHeight" - pagination={false} - columnDefs={colDefs} - rowData={nodesRowData} - /> - -
); diff --git a/src/frontend/src/stores/shortcuts.ts b/src/frontend/src/stores/shortcuts.ts index 66a21c106..18a3f1f18 100644 --- a/src/frontend/src/stores/shortcuts.ts +++ b/src/frontend/src/stores/shortcuts.ts @@ -8,6 +8,9 @@ export const useShortcutsStore = create((set, get) => ({ setShortcuts: (newShortcuts, unavailable) => { set({shortcuts: newShortcuts, unavailableShortcuts: unavailable} ); }, + undo: "mod+z", + redo: "mod+y", + open: "mod+k", advanced: "mod+shift+a", minimize: "mod+shift+q", code: "mod+shift+u", diff --git a/src/frontend/src/types/store/index.ts b/src/frontend/src/types/store/index.ts index fa193c501..c009e14bc 100644 --- a/src/frontend/src/types/store/index.ts +++ b/src/frontend/src/types/store/index.ts @@ -21,6 +21,9 @@ export type StoreComponentResponse = { export type shortcutsStoreType = { updateUniqueShortcut: (name: string, combination: string) => void; + open: string; + undo: string; + redo: string; advanced: string; minimize: string; code: string;