From 39ff7d4b209ec6ede8ff3970d0dca6c689fde27c Mon Sep 17 00:00:00 2001 From: Aleksandar Petrov <8142643+aleks-p@users.noreply.github.com> Date: Thu, 4 Dec 2025 09:15:44 -0400 Subject: [PATCH] Add cursor modes --- .../components/ExploreMapCanvas.tsx | 22 ++++- .../components/ExploreMapFloatingToolbar.tsx | 85 ++++++++++++++++++- .../features/explore-map/state/crdtSlice.ts | 10 +++ .../features/explore-map/state/selectors.ts | 7 ++ .../app/features/explore-map/state/types.ts | 4 + 5 files changed, 121 insertions(+), 7 deletions(-) diff --git a/public/app/features/explore-map/components/ExploreMapCanvas.tsx b/public/app/features/explore-map/components/ExploreMapCanvas.tsx index 1660a8ca0cf..98790292b1a 100644 --- a/public/app/features/explore-map/components/ExploreMapCanvas.tsx +++ b/public/app/features/explore-map/components/ExploreMapCanvas.tsx @@ -10,7 +10,7 @@ import { useTransformContext } from '../context/TransformContext'; import { useCursorSync } from '../hooks/useCursorSync'; import { useCursorViewportTracking } from '../hooks/useCursorViewportTracking'; import { selectPanel as selectPanelCRDT, updateViewport as updateViewportCRDT, selectMultiplePanels as selectMultiplePanelsCRDT } from '../state/crdtSlice'; -import { selectPanels, selectFrames, selectViewport, selectCursors, selectSelectedPanelIds, selectMapUid, selectPostItNotes } from '../state/selectors'; +import { selectPanels, selectFrames, selectViewport, selectCursors, selectSelectedPanelIds, selectMapUid, selectPostItNotes, selectCursorMode } from '../state/selectors'; import { EdgeCursorIndicator } from './EdgeCursorIndicator'; import { ExploreMapComment } from './ExploreMapComment'; @@ -46,6 +46,7 @@ export function ExploreMapCanvas() { const cursors = useSelector((state) => selectCursors(state.exploreMapCRDT)); const selectedPanelIds = useSelector((state) => selectSelectedPanelIds(state.exploreMapCRDT)); const mapUid = useSelector((state) => selectMapUid(state.exploreMapCRDT)); + const cursorMode = useSelector((state) => selectCursorMode(state.exploreMapCRDT)); // Initialize cursor sync const { updatePosition, updatePositionImmediate } = useCursorSync({ @@ -131,6 +132,11 @@ export function ExploreMapCanvas() { const handleCanvasMouseDown = useCallback( (e: React.MouseEvent) => { + // Don't allow selection in hand mode (panning mode) + if (cursorMode === 'hand') { + return; + } + // Only start selection if clicking directly on canvas (not on panels) if (e.target !== e.currentTarget) { return; @@ -166,7 +172,7 @@ export function ExploreMapCanvas() { }); setIsSelecting(true); }, - [contextTransformRef, viewport] + [contextTransformRef, viewport, cursorMode] ); const handleCanvasMouseMove = useCallback( @@ -325,7 +331,7 @@ export function ExploreMapCanvas() { panning={{ disabled: false, excluded: ['panel-drag-handle', 'react-rnd'], - allowLeftClickPan: false, + allowLeftClickPan: cursorMode === 'hand', allowRightClickPan: false, allowMiddleClickPan: true, }} @@ -333,7 +339,10 @@ export function ExploreMapCanvas() { doubleClick={{ disabled: true }} wheel={{ step: 0.1 }} > - +
{ transformWrapper: css({ width: '100%', height: '100%', + }), + pointerCursor: css({ + cursor: 'default', + }), + handCursor: css({ cursor: 'grab', '&:active': { cursor: 'grabbing', diff --git a/public/app/features/explore-map/components/ExploreMapFloatingToolbar.tsx b/public/app/features/explore-map/components/ExploreMapFloatingToolbar.tsx index 01ec529dc1e..5015b844ea1 100644 --- a/public/app/features/explore-map/components/ExploreMapFloatingToolbar.tsx +++ b/public/app/features/explore-map/components/ExploreMapFloatingToolbar.tsx @@ -13,9 +13,8 @@ import prometheusLogoSvg from 'app/plugins/datasource/prometheus/img/prometheus_ import tempoLogoSvg from 'app/plugins/datasource/tempo/img/tempo_logo.svg'; import { useDispatch, useSelector } from 'app/types/store'; -import { addPanel, addFrame, addPostItNote } from '../state/crdtSlice'; -import { selectPanels, selectMapUid, selectViewport, selectSelectedPanelIds } from '../state/selectors'; -import { selectPanels, selectMapUid } from '../state/selectors'; +import { addPanel, addFrame, addPostItNote, setCursorMode } from '../state/crdtSlice'; +import { selectPanels, selectMapUid, selectViewport, selectSelectedPanelIds, selectCursorMode } from '../state/selectors'; import { AddPanelAction } from './AssistantComponents'; @@ -33,6 +32,7 @@ export function ExploreMapFloatingToolbar() { const mapUid = useSelector((state) => selectMapUid(state.exploreMapCRDT)); const viewport = useSelector((state) => selectViewport(state.exploreMapCRDT)); const selectedPanelIds = useSelector((state) => selectSelectedPanelIds(state.exploreMapCRDT)); + const cursorMode = useSelector((state) => selectCursorMode(state.exploreMapCRDT)); const handleAddPanel = useCallback(() => { dispatch( @@ -171,6 +171,14 @@ export function ExploreMapFloatingToolbar() { setIsOpen(false); }, [dispatch, currentUsername]); + const handleSetPointerMode = useCallback(() => { + dispatch(setCursorMode({ mode: 'pointer' })); + }, [dispatch]); + + const handleSetHandMode = useCallback(() => { + dispatch(setCursorMode({ mode: 'hand' })); + }, [dispatch]); + // Build context for assistant const canvasContext = useMemo(() => { const panelsArray = Object.values(panels); @@ -310,6 +318,38 @@ CRITICAL: return (
+
+ + +
+