import { memo, useEffect, useState, type FC, type PropsWithChildren } 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, }); const Controls: FC> = ({ style, showZoom = true, showFitView = true, showInteractive = true, fitViewOptions, onZoomIn, onZoomOut, onFitView, onInteractiveChange, className, children, position = 'bottom-left', }) => { const store = useStoreApi(); const [isVisible, setIsVisible] = useState(false); const { isInteractive, minZoomReached, maxZoomReached } = useStore(selector, shallow); const { zoomIn, zoomOut, fitView } = useReactFlow(); useEffect(() => { setIsVisible(true); }, []); if (!isVisible) { return null; } 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); }; return ( {showZoom && ( <> )} {showFitView && ( )} {showInteractive && ( {isInteractive ? : } )} {children} ); }; Controls.displayName = 'Controls'; export default memo(Controls);