import { ANIMATION_MEDIUM_MS, TLPointerEventInfo, Vec, getPointerInfo, normalizeWheel, releasePointerCapture, setPointerCapture, useEditor, useIsDarkMode, } from '@tldraw/editor' import * as React from 'react' import { MinimapManager } from './MinimapManager' /** @public */ export function DefaultMinimap() { const editor = useEditor() const rCanvas = React.useRef(null!) const rPointing = React.useRef(false) const minimapRef = React.useRef() React.useEffect(() => { const minimap = new MinimapManager(editor, rCanvas.current) minimapRef.current = minimap return minimapRef.current.close }, [editor]) const onDoubleClick = React.useCallback( (e: React.MouseEvent) => { if (!editor.getCurrentPageShapeIds().size) return if (!minimapRef.current) return const point = minimapRef.current.minimapScreenPointToPagePoint( e.clientX, e.clientY, false, false ) const clampedPoint = minimapRef.current.minimapScreenPointToPagePoint( e.clientX, e.clientY, false, true ) minimapRef.current.originPagePoint.setTo(clampedPoint) minimapRef.current.originPageCenter.setTo(editor.getViewportPageBounds().center) editor.centerOnPoint(point, { duration: ANIMATION_MEDIUM_MS }) }, [editor] ) const onPointerDown = React.useCallback( (e: React.PointerEvent) => { if (!minimapRef.current) return const elm = e.currentTarget setPointerCapture(elm, e) if (!editor.getCurrentPageShapeIds().size) return rPointing.current = true minimapRef.current.isInViewport = false const point = minimapRef.current.minimapScreenPointToPagePoint( e.clientX, e.clientY, false, false ) const clampedPoint = minimapRef.current.minimapScreenPointToPagePoint( e.clientX, e.clientY, false, true ) const _vpPageBounds = editor.getViewportPageBounds() minimapRef.current.isInViewport = _vpPageBounds.containsPoint(clampedPoint) if (minimapRef.current.isInViewport) { minimapRef.current.originPagePoint.setTo(clampedPoint) minimapRef.current.originPageCenter.setTo(_vpPageBounds.center) } else { const delta = Vec.Sub(_vpPageBounds.center, _vpPageBounds.point) const pagePoint = Vec.Add(point, delta) minimapRef.current.originPagePoint.setTo(pagePoint) minimapRef.current.originPageCenter.setTo(point) editor.centerOnPoint(point, { duration: ANIMATION_MEDIUM_MS }) } function release(e: PointerEvent) { if (elm) { releasePointerCapture(elm, e) } rPointing.current = false document.body.removeEventListener('pointerup', release) } document.body.addEventListener('pointerup', release) }, [editor] ) const onPointerMove = React.useCallback( (e: React.PointerEvent) => { if (!minimapRef.current) return const point = minimapRef.current.minimapScreenPointToPagePoint( e.clientX, e.clientY, e.shiftKey, true ) if (rPointing.current) { if (minimapRef.current.isInViewport) { const delta = minimapRef.current.originPagePoint .clone() .sub(minimapRef.current.originPageCenter) editor.centerOnPoint(Vec.Sub(point, delta)) return } editor.centerOnPoint(point) } const pagePoint = minimapRef.current.getPagePoint(e.clientX, e.clientY) const screenPoint = editor.pageToScreen(pagePoint) const info: TLPointerEventInfo = { type: 'pointer', target: 'canvas', name: 'pointer_move', ...getPointerInfo(e), point: screenPoint, isPen: editor.getInstanceState().isPenMode, } editor.dispatch(info) }, [editor] ) const onWheel = React.useCallback( (e: React.WheelEvent) => { const offset = normalizeWheel(e) editor.dispatch({ type: 'wheel', name: 'wheel', delta: offset, point: new Vec(e.clientX, e.clientY), shiftKey: e.shiftKey, altKey: e.altKey, ctrlKey: e.metaKey || e.ctrlKey, }) }, [editor] ) const isDarkMode = useIsDarkMode() React.useEffect(() => { // need to wait a tick for next theme css to be applied // otherwise the minimap will render with the wrong colors setTimeout(() => { minimapRef.current?.updateColors() minimapRef.current?.render() }) }, [isDarkMode]) return (
) }