😄
React FlowでControlsの既存のボタンを置き換える方法
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>
)
}
最後に
間違っていることがあれば、コメントに書いていただけると幸いです。
よろしくお願いいたします。
Discussion