😄

React FlowでControlsの既存のボタンを置き換える方法

2024/12/30に公開

React FlowでControlsの既存のボタンを置き換える方法

React Flowのデフォルトのコントロールパネルにはzoomin, zoomout, fitview, toggle interactiveの4つのボタンが表示されている。その4つのボタンのスタイルやアイコンが気に入らない場合、置き換えることができることを知ったのでメモ。

React Flowとは

フローチャートや組織図などのチャートを簡単に描くことができるコンポーネント

公式ページより
A customizable React component for building node-based editors and interactive diagrams

実装

zoomin, zoomout, fitviewボタンに関してはあらかじめhookが用意されていてそれをonClickに渡したら簡単に実装できる。
toggle interactiveは独自にhookを作ることで実装できる。

今回はshadcnのtooltip, lucide-reactのアイコンを使用して実装しました。

zoomin

const ZoomInControl = () => {
  const {
    zoomIn
  } = useReactFlow();

  const onZoomInHander = () => {
    zoomIn();
  }

  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          <ControlButton
            onClick={() => onZoomInHander()}
            >
            <PlusIcon />
          </ControlButton>
        </TooltipTrigger>
        <TooltipContent>
          ズームイン
        </TooltipContent>
      </Tooltip>
    </TooltipProvider>
  )
}

zoomout

const ZoomOutControl = () => {
  const {
    zoomOut
  } = useReactFlow();

  const onZoomOutHandler = () => {
    zoomOut();
  }

  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          <ControlButton
            onClick={() => onZoomOutHandler()}
            >
            <MinusIcon />
          </ControlButton>
        </TooltipTrigger>
        <TooltipContent>
          ズームアウト
        </TooltipContent>
      </Tooltip>
    </TooltipProvider>
  )
}

fitview

const FitViewControl = () => {
  const {
    fitView
  } = useReactFlow();
  
  const onFitViewHandler = () => {
    fitView()
  };

  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          <ControlButton
            onClick={() => onFitViewHandler()}
            >
            <MaximizeIcon />
          </ControlButton>
        </TooltipTrigger>
        <TooltipContent>
          画面幅に合わせる
        </TooltipContent>
      </Tooltip>
    </TooltipProvider>
  )
}

toogle interactive

interactiveボタンをクリックしたときの大まかな挙動は以下の通り。

  • 各ノードを接続することができる
  • ノードは動かせない
  • 画面は動かすことができる

booleanのstateを使用することで、通常はノードを動かせるが、ロック中はノードを動かすことができないという挙動を実装することができる

const InterActivityControl = (props: { isLock: boolean, onInterActivityHandler: () => void }) => {
  const {
    isLock,
    onInterActivityHandler
  } = props;

  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          <ControlButton
            onClick={() => onInterActivityHandler()}
            >
            {isLock ? (
              <LockIcon 
                className='!fill-white'
                />
            ) : (
              <LockOpenIcon 
                className='!fill-white'
                />
            )}
          </ControlButton>
        </TooltipTrigger>
        <TooltipContent>
          画面をロック
        </TooltipContent>
      </Tooltip>
    </TooltipProvider>
  )
}

onNodesChangeにtrue, false時で条件分岐でノードに関する関数を渡すようにしている

const App = () => {
  const [isLock, setLock] = useState(false)
  const [nodes, setNodes, onNodesChange] = useNodesState<Node>(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>(initialEdges);

  const onInterActivityHandler = () => {
    setLock((lock) => !lock)
  }

  return (
    <ReactFlow 
      nodes={nodes}
      edges={edges}
      onNodesChange={isLock ? () => {} : onNodesChange}
      onEdgesChange={onEdgesChange}
      >
      <Controls>
        <ZoomInControl />
        <ZoomOutControl />
        <FitViewControl />
        <InterActivityControl 
          isLock={isLock} 
          onInterActivityHandler={onInterActivityHandler} 
          />
      </Controls>
    </ReactFlow>
  )
}

独自のボタン

最後に

間違っていることがあれば、コメントに書いていただけると幸いです。
よろしくお願いいたします。

GitHubで編集を提案

Discussion