import type { Point } from '@visx/point'; import type { ProvidedZoom, Translate } from '@visx/zoom/lib/types'; import { equals, gt, pick } from 'ramda'; import type React from 'react'; import { useCallback, useState } from 'react'; import type { ZoomState } from './models'; export interface UseMinimapProps { height: number; isDraggingFromContainer: boolean; minimapScale: number; scale: number; width: number; zoom: ProvidedZoom & ZoomState; } interface UseMinimapState { dragEnd: () => void; dragStart: (e: React.MouseEvent) => void; move: (e: React.MouseEvent) => void; transformTo: (e: React.MouseEvent) => void; zoomInOut: (e: React.WheelEvent) => void; } export const useMinimap = ({ width, height, zoom, minimapScale, isDraggingFromContainer, scale }: UseMinimapProps): UseMinimapState => { const [startPoint, setStartPoint] = useState | null>( null ); const [startTranslate, setStartTranslate] = useState(null); const getMatrixPoint = useCallback( ( event: | React.MouseEvent | React.WheelEvent, newScale?: number ): { x: number; y: number } => { const hasScale = scale > 1; const point = { x: event.nativeEvent.offsetX * (1 / minimapScale), y: event.nativeEvent.offsetY * (1 / minimapScale) }; const dx = -( point.x * (newScale || zoom.transformMatrix.scaleX) - width / 2 ); const dy = -( point.y * (newScale || zoom.transformMatrix.scaleY) - height / 2 ); return { x: !hasScale ? dx : dx * scale - width / 2, y: !hasScale ? dy : dy * scale - height / 2 }; }, [zoom.transformMatrix, scale, width, height, minimapScale] ); const transformTo = useCallback( (e: React.MouseEvent): void => { if (!equals(e.buttons, 1)) { return; } const { x, y } = getMatrixPoint(e); zoom.setTransformMatrix({ ...zoom.transformMatrix, translateX: x, translateY: y }); }, [zoom.transformMatrix, getMatrixPoint, zoom.setTransformMatrix] ); const dragStart = (e: React.MouseEvent): void => { if (!equals(e.buttons, 1) || isDraggingFromContainer) { return; } setStartPoint(getMatrixPoint(e)); setStartTranslate(pick(['translateX', 'translateY'], zoom.transformMatrix)); }; const dragEnd = (): void => { setStartPoint(null); setStartTranslate(null); }; const move = useCallback( (e: React.MouseEvent): void => { if (!startPoint || !startTranslate) { return; } const { x, y } = getMatrixPoint(e); const diffX = startPoint.x - x; const diffY = startPoint.y - y; zoom.setTransformMatrix({ ...zoom.transformMatrix, translateX: startTranslate.translateX - diffX, translateY: startTranslate.translateY - diffY }); }, [ zoom.transformMatrix, startPoint, getMatrixPoint, startTranslate, zoom.setTransformMatrix ] ); const zoomInOut = useCallback( (e: React.WheelEvent): void => { const isZoomIn = gt(0, e.deltaY); const newScaleX = isZoomIn ? zoom.transformMatrix.scaleX + 0.1 : zoom.transformMatrix.scaleX - 0.1; const newScaleY = isZoomIn ? zoom.transformMatrix.scaleX + 0.1 : zoom.transformMatrix.scaleX - 0.1; const { x, y } = getMatrixPoint(e, newScaleX); const diffX = x - zoom.transformMatrix.translateX; const diffY = y - zoom.transformMatrix.translateY; zoom.setTransformMatrix({ ...zoom.transformMatrix, scaleX: newScaleX, scaleY: newScaleY, translateX: zoom.transformMatrix.translateX + diffX / 4, translateY: zoom.transformMatrix.translateY + diffY / 4 }); }, [zoom.transformMatrix, getMatrixPoint, zoom.setTransformMatrix] ); return { dragEnd, dragStart, move, transformTo, zoomInOut }; };