From 1f614ccca626eb9648b6a757849e9567b7e32dd0 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 9 Jan 2025 10:55:59 -0300 Subject: [PATCH] fix: Add factor to prevent overlap (#5426) * fix: add random factor to prevent overlap * update package.lock * feat: Add positionDictionary and related functions to FlowStoreType * feat: Add buildPositionDictionary function to reactflowUtils.ts * feat: Add buildPositionDictionary function to reactflowUtils.ts This commit adds the buildPositionDictionary function to reactflowUtils.ts. This function is used to build a dictionary of positions for nodes in the flow. It is necessary for managing the positions of nodes and preventing overlap. * fix: Remove random factor from paste function in PageComponent * [autofix.ci] apply automated fixes * feat: Add setPositionDictionary function to PageComponent This commit adds the setPositionDictionary function to the PageComponent in order to set the position dictionary for the flow. This function is used to update the position dictionary when the canvas is moved or resized. Co-authored-by: [Author Name] * [autofix.ci] apply automated fixes * Refactor position calculation in useFlowStore * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../components/PageComponent/index.tsx | 13 +++++++++- src/frontend/src/stores/flowStore.ts | 26 +++++++++++++++++++ src/frontend/src/types/zustand/flow/index.ts | 6 +++++ src/frontend/src/utils/reactflowUtils.ts | 8 ++++++ 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 5c618bd41..754b61d4a 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -86,6 +86,9 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { const templates = useTypesStore((state) => state.templates); const setFilterEdge = useFlowStore((state) => state.setFilterEdge); const reactFlowWrapper = useRef(null); + const setPositionDictionary = useFlowStore( + (state) => state.setPositionDictionary, + ); const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance); const setReactFlowInstance = useFlowStore( @@ -338,7 +341,15 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { // 👇 make moving the canvas undoable autoSaveFlow(); updateCurrentFlow({ nodes }); - }, [takeSnapshot, autoSaveFlow, nodes, edges, reactFlowInstance]); + setPositionDictionary({}); + }, [ + takeSnapshot, + autoSaveFlow, + nodes, + edges, + reactFlowInstance, + setPositionDictionary, + ]); const onSelectionDragStart: SelectionDragHandler = useCallback(() => { // 👇 make dragging a selection undoable diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts index dbf51ce11..56215a677 100644 --- a/src/frontend/src/stores/flowStore.ts +++ b/src/frontend/src/stores/flowStore.ts @@ -31,6 +31,7 @@ import { import { FlowStoreType, VertexLayerElementType } from "../types/zustand/flow"; import { buildFlowVerticesWithFallback } from "../utils/buildUtils"; import { + buildPositionDictionary, checkChatInput, cleanEdges, detectBrokenEdgesEdges, @@ -52,6 +53,19 @@ import { useTypesStore } from "./typesStore"; // this is our useStore hook that we can use in our components to get parts of the store and call actions const useFlowStore = create((set, get) => ({ + positionDictionary: {}, + setPositionDictionary: (positionDictionary) => { + set({ positionDictionary }); + }, + isPositionAvailable: (position: { x: number; y: number }) => { + if ( + get().positionDictionary[position.x] && + get().positionDictionary[position.x] === position.y + ) { + return false; + } + return true; + }, fitViewNode: (nodeId) => { if (get().reactFlowInstance && get().nodes.find((n) => n.id === nodeId)) { get().reactFlowInstance?.fitView({ nodes: [{ id: nodeId }] }); @@ -211,6 +225,7 @@ const useFlowStore = create((set, get) => ({ hasIO: inputs.length > 0 || outputs.length > 0, flowPool: {}, currentFlow: flow, + positionDictionary: {}, }); }, setIsBuilding: (isBuilding) => { @@ -386,6 +401,17 @@ const useFlowStore = create((set, get) => ({ y: position.y, }); + let internalPostionDictionary = get().positionDictionary; + if (Object.keys(internalPostionDictionary).length === 0) { + internalPostionDictionary = buildPositionDictionary(get().nodes); + } + while (!get().isPositionAvailable(insidePosition)) { + insidePosition.x += 10; + insidePosition.y += 10; + } + internalPostionDictionary[insidePosition.x] = insidePosition.y; + get().setPositionDictionary(internalPostionDictionary); + selection.nodes.forEach((node: AllNodeType) => { // Generate a unique node ID let newId = getNodeId(node.data.type); diff --git a/src/frontend/src/types/zustand/flow/index.ts b/src/frontend/src/types/zustand/flow/index.ts index 680a142ca..8cdaffa07 100644 --- a/src/frontend/src/types/zustand/flow/index.ts +++ b/src/frontend/src/types/zustand/flow/index.ts @@ -53,6 +53,12 @@ export type FlowPoolType = { }; export type FlowStoreType = { + //key x, y + positionDictionary: { [key: number]: number }; + isPositionAvailable: (position: { x: number; y: number }) => boolean; + setPositionDictionary: (positionDictionary: { + [key: number]: number; + }) => void; fitViewNode: (nodeId: string) => void; autoSaveFlow: (() => void) | undefined; componentsToUpdate: string[]; diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index b90fafd78..8aa4dff72 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -1746,3 +1746,11 @@ export function someFlowTemplateFields( export function checkHasToolMode(template: APITemplateType) { return template && Object.values(template).some((field) => field.tool_mode); } + +export function buildPositionDictionary(nodes: AllNodeType[]) { + const positionDictionary = {}; + nodes.forEach((node) => { + positionDictionary[node.position.x] = node.position.y; + }); + return positionDictionary; +}