import { memo } from 'react'; import cc from 'classcat'; import { shallow } from 'zustand/shallow'; import { useStore, useStoreApi } from '../../hooks/useStore'; import { useReactFlow } from '../../hooks/useReactFlow'; import { Panel } from '../../components/Panel'; import { type ReactFlowState } from '../../types'; import { PlusIcon } from './Icons/Plus'; import { MinusIcon } from './Icons/Minus'; import { FitViewIcon } from './Icons/FitView'; import { LockIcon } from './Icons/Lock'; import { UnlockIcon } from './Icons/Unlock'; import { ControlButton } from './ControlButton'; import type { ControlProps } from './types'; const selector = (s: ReactFlowState) => ({ isInteractive: s.nodesDraggable || s.nodesConnectable || s.elementsSelectable, minZoomReached: s.transform[2] <= s.minZoom, maxZoomReached: s.transform[2] >= s.maxZoom, }); function ControlsComponent({ style, showZoom = true, showFitView = true, showInteractive = true, fitViewOptions, onZoomIn, onZoomOut, onFitView, onInteractiveChange, className, children, position = 'bottom-left', orientation = 'vertical', 'aria-label': ariaLabel = 'React Flow controls', }: ControlProps) { const store = useStoreApi(); const { isInteractive, minZoomReached, maxZoomReached } = useStore(selector, shallow); const { zoomIn, zoomOut, fitView } = useReactFlow(); const onZoomInHandler = () => { zoomIn(); onZoomIn?.(); }; const onZoomOutHandler = () => { zoomOut(); onZoomOut?.(); }; const onFitViewHandler = () => { fitView(fitViewOptions); onFitView?.(); }; const onToggleInteractivity = () => { store.setState({ nodesDraggable: !isInteractive, nodesConnectable: !isInteractive, elementsSelectable: !isInteractive, }); onInteractiveChange?.(!isInteractive); }; const orientationClass = orientation === 'horizontal' ? 'horizontal' : 'vertical'; return ( {showZoom && ( <> )} {showFitView && ( )} {showInteractive && ( {isInteractive ? : } )} {children} ); } ControlsComponent.displayName = 'Controls'; export const Controls = memo(ControlsComponent);