From 7632f9907415e80b1030cbb050acd57b8ee1dea6 Mon Sep 17 00:00:00 2001 From: Artem M Date: Thu, 18 Jan 2024 17:04:20 +0500 Subject: [PATCH] context menu refactoring --- .../GenericNode/ContextMenuItem.tsx | 101 ++++++++++++++ .../src/CustomNodes/GenericNode/index.tsx | 120 ++++++++--------- .../components/PageComponent/index.tsx | 123 ++++++++---------- 3 files changed, 205 insertions(+), 139 deletions(-) create mode 100644 df_designer_front/src/CustomNodes/GenericNode/ContextMenuItem.tsx diff --git a/df_designer_front/src/CustomNodes/GenericNode/ContextMenuItem.tsx b/df_designer_front/src/CustomNodes/GenericNode/ContextMenuItem.tsx new file mode 100644 index 00000000..4624a9c1 --- /dev/null +++ b/df_designer_front/src/CustomNodes/GenericNode/ContextMenuItem.tsx @@ -0,0 +1,101 @@ +import * as ContextMenu from "@radix-ui/react-context-menu"; +import { + BoxSelect, + ClipboardPasteIcon, + Combine, + Copy, + FileText, + LayoutGrid, + LucideIcon, + Settings2, + Trash2, +} from "lucide-react"; + +interface ItemProps { + Icon: LucideIcon; + text: string; + hotKey?: string; +} + +interface IMenuProps { + type: + | "copy" + | "paste" + | "toggleGrid" + | "createPreset" + | "selectAll" + | "settings" + | "doc" + | "delete"; + onClick: (e: React.MouseEvent) => void; + hide?: boolean; + disabled?: boolean; +} + +const items: { [item: string]: ItemProps } = { + copy: { + Icon: Copy, + text: "Copy", + hotKey: "Ctrl + C", + }, + paste: { + Icon: ClipboardPasteIcon, + text: "Paste", + hotKey: "Ctrl + V", + }, + toggleGrid: { + Icon: LayoutGrid, + text: "Show/hide grid", + hotKey: "Shift+G", + }, + createPreset: { + Icon: Combine, + text: "Create preset", + hotKey: "Ctrl+G", + }, + selectAll: { + Icon: BoxSelect, + text: "Select all", + hotKey: "Ctrl+A", + }, + settings: { + Icon: Settings2, + text: "Settings", + hotKey: "", + }, + doc: { + Icon: FileText, + text: "Doc", + hotKey: "", + }, + delete: { + Icon: Trash2, + text: "Delete", + hotKey: "Del", + }, +}; + +export const ContextMenuItem = ({ + type, + onClick, + disabled = false, + hide = false, +}: IMenuProps) => { + if (hide) return null; + + const { Icon, text, hotKey } = items[type]; + + return ( + +
+ +

{text}

+
+ {hotKey && {hotKey} } +
+ ); +}; diff --git a/df_designer_front/src/CustomNodes/GenericNode/index.tsx b/df_designer_front/src/CustomNodes/GenericNode/index.tsx index eb74e0a8..fcfe048f 100644 --- a/df_designer_front/src/CustomNodes/GenericNode/index.tsx +++ b/df_designer_front/src/CustomNodes/GenericNode/index.tsx @@ -50,6 +50,7 @@ import EditNodeModal from "../../modals/EditNodeModal"; import _ from "lodash"; import { ManageIcon } from "../../icons/ManageIcon"; import EditLinkModal from "../../modals/editLinkModal"; +import { ContextMenuItem } from "./ContextMenuItem"; export default function GenericNode({ data, @@ -628,10 +629,15 @@ export default function GenericNode({ // e.preventDefault(); // const node = flows.find((flow) => flow.id === tabId).data.nodes.find((node: NodeType) => node.id === data.id) // console.log(node) - setLastCopiedSelection(_.cloneDeep(lastSelection)) // console.log(lastCopiedSelection) + setLastCopiedSelection(_.cloneDeep(lastSelection)) setSuccessData({ title: "Node was succesfully copied!" }) } + const openSettings = (e) => openPopUp( + data.node.base_classes[0] === 'links' + ? + : + ) return ( @@ -848,79 +854,57 @@ export default function GenericNode({ - {data.id !== "LOCAL_NODE" && data.id !== "GLOBAL_NODE" && ( - <> - copy(e)} - className=" context-item"> -
- -

Copy

-
- Ctrl+C -
- {/* { - let bounds = document.getElementById('reactFlowWrapper').getBoundingClientRect() - // console.log(bounds) - const node = reactFlowInstance.getNode(data.id) - // console.log(node) - // console.log(getNodePositionWithOrigin(node)) - const pos = getNodePositionWithOrigin(node) - // console.log(node) - deleteNode(data.id) - setTimeout(() => { - paste( - lastCopiedSelection, - { - x: node.position.x, - y: node.position.y - }) - }, 20); - }} - className=" context-item context-item-disabled"> -
- -

Paste to replace

-
- Ctrl+Shift+V -
*/} - {/* { }} - className=" context-item context-item-disabled"> -
- -

Create preset

-
- Shift+A -
*/} - - )} - e.preventDefault()} onClick={e => openPopUp(data.node.base_classes[0] === 'links' ? : )} - className=" context-item " - > + {/* { + let bounds = document.getElementById('reactFlowWrapper').getBoundingClientRect() + // console.log(bounds) + const node = reactFlowInstance.getNode(data.id) + // console.log(node) + // console.log(getNodePositionWithOrigin(node)) + const pos = getNodePositionWithOrigin(node) + // console.log(node) + deleteNode(data.id) + setTimeout(() => { + paste( + lastCopiedSelection, + { + x: node.position.x, + y: node.position.y + }) + }, 20); + }} + className=" context-item context-item-disabled">
- -

Settings

+ +

Paste to replace

- {/* */} -
- + Ctrl+Shift+V + */} + {/* { }} + className=" context-item context-item-disabled">
- -

Doc

+ +

Create preset

- {/* Ctrl+C */} -
+ Shift+A +
*/} + + + {data.id !== "LOCAL_NODE" && data.id !== "GLOBAL_NODE" && ( - <> - - { deleteNode(data.id) }} className=" context-item "> -
- -

Delete

-
- Del -
- + )} + deleteNode(data.id)} + hide={data.id === "LOCAL_NODE" || data.id === "GLOBAL_NODE"} + />
diff --git a/df_designer_front/src/pages/FlowPage/components/PageComponent/index.tsx b/df_designer_front/src/pages/FlowPage/components/PageComponent/index.tsx index 0b57e8e1..f6da0c3c 100644 --- a/df_designer_front/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/df_designer_front/src/pages/FlowPage/components/PageComponent/index.tsx @@ -44,6 +44,7 @@ import { darkContext } from "../../../../contexts/darkContext"; import dagre from 'dagre'; import LayoutFlow from "../LayoutComponent"; import { useNavigate } from "react-router-dom"; +import { ContextMenuItem } from "../../../../CustomNodes/GenericNode/ContextMenuItem"; const nodeDefTypes = { genericNode: GenericNode, @@ -731,7 +732,37 @@ export default function Page({ flow }: { flow: FlowType }) { // console.log(fe) // }, []) - + const copy = (e) => { + setLastCopiedSelection(_.cloneDeep(lastSelection)); + } + const pasteNode = (e) => { + // let bounds = reactFlowWrapper.current.getBoundingClientRect(); + // const nodesPositions = flow.data.nodes.map((node: NodeType) => node.position) + // if (!(lastSelection.nodes.map((node) => node.id).includes("GLOBAL_NODE") || lastSelection.nodes.map((node) => node.id).includes("LOCAL_NODE"))) { + // if (!isMouseOnNode) { + // if ((nodesPositions[0].x) < (position.x - bounds.left) && (position.x - bounds.left) < (nodesPositions[0].x + 384)) { + // // console.log('1') + // } + // paste(lastCopiedSelection, { + // x: position.x - bounds.left, + // y: position.y - bounds.top, + // }); + // // console.log('first') + // } else { + // setErrorData({ title: "You can't paste node over node! Nodes can't intersect!" }) + // } + // } else setErrorData({ title: "You can't paste Global/Local Node copy!" }) + if (lastCopiedSelection) { + setIsCloneVisible(true); + getNodesSelectionZone(lastCopiedSelection.nodes); + getRightestNode(lastCopiedSelection.nodes); + } + } + const createPreset = (e) => { + if (lastSelection?.nodes?.length > 0) + openPopUp(); + } + return ( <> {isCloneVisible && ( @@ -836,76 +867,26 @@ export default function Page({ flow }: { flow: FlowType }) { - {(lastSelection?.nodes?.length > 0 || lastSelection?.edges?.length > 0) && ( - { - setLastCopiedSelection(_.cloneDeep(lastSelection)) - }} className=" context-item " > -
- -

Copy

-
- Ctrl+C -
- )} - {lastCopiedSelection?.nodes?.length > 0 && ( - { - // let bounds = reactFlowWrapper.current.getBoundingClientRect(); - // const nodesPositions = flow.data.nodes.map((node: NodeType) => node.position) - // if (!(lastSelection.nodes.map((node) => node.id).includes("GLOBAL_NODE") || lastSelection.nodes.map((node) => node.id).includes("LOCAL_NODE"))) { - // if (!isMouseOnNode) { - // if ((nodesPositions[0].x) < (position.x - bounds.left) && (position.x - bounds.left) < (nodesPositions[0].x + 384)) { - // // console.log('1') - // } - // paste(lastCopiedSelection, { - // x: position.x - bounds.left, - // y: position.y - bounds.top, - // }); - // // console.log('first') - // } else { - // setErrorData({ title: "You can't paste node over node! Nodes can't intersect!" }) - // } - // } else setErrorData({ title: "You can't paste Global/Local Node copy!" }) - if ( - lastCopiedSelection - ) { - setIsCloneVisible(true) - getNodesSelectionZone(lastCopiedSelection.nodes) - getRightestNode(lastCopiedSelection.nodes) - } - }} - className=" context-item "> -
- -

Paste

-
- Ctrl+V -
- )} - setGrid(!grid)} className=" context-item " > -
- -

Show/hide grid

-
- Shift+G -
- { if (lastSelection?.nodes?.length > 0) openPopUp() }} - className={` context-item ${lastSelection?.nodes?.length <= 0 && 'context-item-disabled'} `}> -
- -

Create preset

-
- Ctrl+G -
- { - // onSelectionChange({nodes: flow.data.nodes, edges: flow.data.edges}) - selectAllHandler() - }} className=" context-item " > -
- -

Select all

-
- Ctrl+A -
+ + + setGrid(!grid)} /> + +