[ReactFlow] 다이어그램툴 드래그앤드랍 기능추가 #4

김하정·2024년 5월 29일
2
post-thumbnail

🍄 드래그 앤 드랍 구현원리

드래드 앤 드랍과 관련하여 리액트에서 다음과 같은 두개의 이벤트를 활용해보고자 한다.

- onDrop : 드래그가 끝난 후, 객체를 놓는 장소에 이벤트 발생
- omDragOver : 드래그 시 마우스가 객체 위에 있는 경우 이벤트 발생

두개의 이벤트에 대한 설명은 다음 링크를 참조바란다.

onDragOver 이벤트 관련
onDrop 이벤트 관련

위 두가지를 함께 사용해보려고 한다.
먼저 onDragOver 이벤트는 다음과 같이 작성할 수 있다.

🍄 onDragOver 작성하기

 const onDragOver = (event: React.DragEvent) => {
    event.preventDefault();
    event.dataTransfer.dropEffect = 'move';
  };

드래그가 가능하려면 해당 태그에 draggable 이 있어야 한다.

🍄 onDrop 작성하기

다음으로 드래그한 모형을 내려두었을 때에 해당 모형이 node 에 추가하는 이벤트를 만들어보겠다.
onDrop 함수에 작성해주면 된다.


  const [nodes, setNodes, onNodesChange] = useNodesState<Node[]>([]);
  const { screenToFlowPosition } = useReactFlow();

  const onDrop = useCallback(
    (event: React.DragEvent) => {
      event.preventDefault();
      // 추가하게 될 node 작성
      const newNode = {
        id: `${v1()}`,
        type: 'custom',
        position: screenToFlowPosition({
          x: event.clientX,
          y: event.clientY
        }),
        data: {}
      };

      // 기존 nodes 에 추가하기
      setNodes((nds) => nds.concat(newNode));
    },
    [screenToFlowPosition,setNodes]
  );

id의 경우, 중복되지 않기 위해서 uuid 를 사용하였는데 new Date로 현재 시간을 id 로 생성하여도 겹치지 않을 것 같다 😅

🍄 드래그앤드랍 핸들러 props 내려주기

setNodes가 ReactFlowExample에 있기 때문에 해당 이벤트들을 ReactFlowExample에서 만든 후, ReactFlowCanvas에 내려주었다.

👻 ReactFlowExample.tsx

import { useNodesState, useEdgesState, useReactFlow } from "reactflow";
import { v1 } from "uuid";
import "reactflow/dist/style.css";

import EditorTools from "./EditorTools";
import ReactFlowCanvas from "./ReactFlowCanvas";
import { useCallback } from "react";

const ReactFlowExample = () => {
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
  const { screenToFlowPosition } = useReactFlow();
  const onDragOver = (event: React.DragEvent) => {
    event.preventDefault();
    event.dataTransfer.dropEffect = "move";
  };

  const onDrop = useCallback(
    (event: React.DragEvent) => {
      event.preventDefault();
      const newNode = {
        id: `${v1()}`,
        type: "custom",
        position: screenToFlowPosition({
          x: event.clientX,
          y: event.clientY,
        }),
        data: {},
      };

      setNodes((nds) => nds.concat(newNode));
    },
    [screenToFlowPosition, setNodes]
  );

  return (
    <div style={{ width: "100vw", height: "100vh" }}>
      <EditorTools />
      <ReactFlowCanvas
        nodesState={{
          nodes,
          setNodes,
          onNodesChange,
        }}
        edgesState={{
          edges,
          setEdges,
          onEdgesChange,
        }}
        onDrop={onDrop}
        onDragOver={onDragOver}
      />
    </div>
  );
};
export default ReactFlowExample;

👻 ReactFlowCanvas.tsx

import ReactFlow, {
  Background,
  Controls,
  MiniMap,
  Node,
  Edge,
  Connection,
  addEdge,
} from "reactflow";
import styled from "styled-components";
import CustomNode from "./CustomNode";
import { useCallback } from "react";

const NodeTypes = {
  custom: CustomNode,
};

interface IProps {
  nodesState: {
    nodes: Node<Node[], string | undefined>[];
    setNodes: React.Dispatch<
      React.SetStateAction<Node<Node[], string | undefined>[]>
    >;
    onNodesChange: unknown;
  };
  edgesState: {
    edges: Edge<Edge[]>[];
    setEdges: React.Dispatch<React.SetStateAction<Edge<Edge[]>[]>>;
    onEdgesChange: unknown;
  };
  onDrop: (event: React.DragEvent) => void;
  onDragOver: (event: React.DragEvent) => void;
}

const ReactFlowCanvas = ({
  nodesState,
  edgesState,
  onDrop,
  onDragOver,
}: IProps) => {
  const { nodes, onNodesChange } = nodesState;
  const { edges, setEdges, onEdgesChange } = edgesState;
  const onConnect = useCallback(
    (params: Edge | Connection) => setEdges((eds) => addEdge(params, eds)),
    [setEdges]
  );
  return (
    <ReactFlowStyled
      nodes={nodes}
      edges={edges}
      onNodesChange={onNodesChange}
      onEdgesChange={onEdgesChange}
      onConnect={onConnect}
      nodeTypes={NodeTypes}
      onDragOver={onDragOver}
      onDrop={onDrop}
    >
      <Controls />
      <MiniMap />
      <Background gap={12} size={1} />
    </ReactFlowStyled>
  );
};
export default ReactFlowCanvas;

const ReactFlowStyled = styled(ReactFlow)`
  .react-flow__node-custom {
    width: 120px;
    height: 120px;
    background: conic-gradient(
      from -160deg at 50% 50%,
      #e92a67 0deg,
      #a853ba 120deg,
      #2a8af6 240deg,
      #e92a67 360deg
    );
    border-radius: 100%;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
  }
`;

🍄 최종화면

🍄 다음으로 해야할 일! Custom 노드 만들기!

위 영상을 보면 끌어내렸을 때 앞서 만들었던 custom node가 나타나는것들 알 수 있다!
setNode를 할때에 type을 "custom"으로 등록했기 때문이다.

다음 포스팅에서는 editTools 에 잡아둔 도형의 형태로 custom 된 노드들을 등록하여 해당 노드가 드래그앤드랍되었을 때에 올바른 타입으로 등록 될 수 있도록 작업해보고자 한다! 🙇🏻‍♀️

profile
web developer

0개의 댓글