0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【ReactFlow】双方向ハンドルで自由自在にノードをつなぐ

Posted at

はじめに

ReactFlowのインストールや基礎はこちらをご覧ください。

双方向ハンドルとは

ハンドルとは

ReactFlowのノードとノードを線でつなぐ時、そのつなぐポイントの事を言います。

通常、ReactFlowのカスタムノードという機能を使ってノードをカスタマイズする時、ハンドルの位置を決めることが出来ます。
その時、同時にハンドルが送信側(source)なのか受信側(target)なのかを決める必要があります。
送信側同士で線をつなぐことも受信側から線をつなぐことも出来ません。
しかし、場合によっては一つのハンドルから送信側も受信側も両方行いたい時ってありますよね。

そんな時に用いるのが双方向ハンドルです

実装

app/page.tsxは基本的なカスタムノードの使い方です。
CustomNode.tsxはやってることはとてもシンプルで送信ハンドルと受診ハンドルを重ねているだけです。
こんな単純な事で解決できるんですね

// app/page.tsx
'use client';
import { useCallback, useState, type ChangeEventHandler } from 'react';
import {
  ReactFlow,
  addEdge,
  useNodesState,
  useEdgesState,
  MiniMap,
  Background,
  Controls,
  Panel,
  type Node,
  type Edge,
  type ColorMode,
  type OnConnect,
} from '@xyflow/react';

import '@xyflow/react/dist/style.css';
import './page.css';
import CustomNode from './components/CustomNode';

const nodeTypes = {
  customNode: CustomNode,
};

const initialNodes: Node[] = [
  {
    id: 'A',
    type: 'customNode',
    position: { x: 0, y: 150 },
    data: { label: 'A' },
  },
  {
    id: 'B',
    type: 'customNode',
    position: { x: 250, y: 0 },
    data: { label: 'B' },
  },
  {
    id: 'C',
    type: 'customNode',
    position: { x: 250, y: 150 },
    data: { label: 'C' },
  },
  {
    id: 'D',
    type: 'customNode',
    position: { x: 250, y: 300 },
    data: { label: 'D' },
  },
];

const initialEdges: Edge[] = [
];

export default function FlowChart() {
  const [colorMode, setColorMode] = useState<ColorMode>('dark');
  const [nodes, , onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

  const onConnect: OnConnect = useCallback(
    (params) => setEdges((eds) => addEdge(params, eds)),
    [setEdges],
  );

  const onChange: ChangeEventHandler<HTMLSelectElement> = (evt) => {
    setColorMode(evt.target.value as ColorMode);
  };

  return (
    <div style={{ height: '500px', width: '100%' }} >
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        colorMode={colorMode}
        nodeTypes={nodeTypes}
        fitView
      >
        <MiniMap />
        <Background />
        <Controls />

        <Panel position="top-right">
          <select
            className="xy-theme__select"
            onChange={onChange}
            data-testid="colormode-select"
          >
            <option value="dark">dark</option>
            <option value="light">light</option>
            <option value="system">system</option>
          </select>
        </Panel>
      </ReactFlow>
    </div>
  );
};
// app\components\CustomNode.tsx
import React from 'react';
import { Handle, Position } from '@xyflow/react';

interface CustomNodeProps {
  data: {
    label: string;
  };
}

export default function CustomNode({ data }: CustomNodeProps) {
  return (
    <div style={{
      padding: '10px',
      border: '2px solid #ccc',
      borderRadius: '8px',
      background: '#111',
      minWidth: '100px',
      textAlign: 'center',
      position: 'relative'
    }}>
      {/* 上のハンドル */}
      <Handle
        type="source"
        position={Position.Top}
        id="top"
        style={{ background: '#555' }}
      />
      <Handle
        type="target"
        position={Position.Top}
        id="top"
        style={{ background: '#555' }}
      />
      
      {/* 右のハンドル */}
      <Handle
        type="source"
        position={Position.Right}
        id="right"
        style={{ background: '#555' }}
      />
      <Handle
        type="target"
        position={Position.Right}
        id="right"
        style={{ background: '#555' }}
      />
      
      {/* 下のハンドル */}
      <Handle
        type="source"
        position={Position.Bottom}
        id="bottom"
        style={{ background: '#555' }}
      />
      <Handle
        type="target"
        position={Position.Bottom}
        id="bottom"
        style={{ background: '#555' }}
      />
      
      {/* 左のハンドル */}
      <Handle
        type="source"
        position={Position.Left}
        id="left"
        style={{ background: '#555' }}
      />
      <Handle
        type="target"
        position={Position.Left}
        id="left"
        style={{ background: '#555' }}
      />
      
      <div>{data.label}</div>
    </div>
  );
}

終わりに

とても単純ですが、双方向ハンドルの実装が出来ました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?