diff --git a/public/app/features/explore-map/components/ExploreMapCanvas.tsx b/public/app/features/explore-map/components/ExploreMapCanvas.tsx index 91dc14ed17b..22923459101 100644 --- a/public/app/features/explore-map/components/ExploreMapCanvas.tsx +++ b/public/app/features/explore-map/components/ExploreMapCanvas.tsx @@ -1,6 +1,6 @@ import { css } from '@emotion/css'; import { useCallback, useRef } from 'react'; -import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch'; +import { ReactZoomPanPinchRef, TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2 } from '@grafana/ui'; @@ -37,7 +37,7 @@ export function ExploreMapCanvas() { ); const handleTransformChange = useCallback( - (ref: any) => { + (ref: ReactZoomPanPinchRef) => { dispatch( updateViewport({ zoom: ref.state.scale, @@ -52,7 +52,7 @@ export function ExploreMapCanvas() { return (
-
+
{ + if (e.key === 'Escape') { + dispatch(selectPanel({ panelId: undefined })); + } + }} + role="button" + tabIndex={0} + > {Object.values(panels).map((panel) => ( ))} @@ -105,8 +116,8 @@ const getStyles = (theme: GrafanaTheme2) => { }), canvas: css({ position: 'relative', - width: '5000px', - height: '5000px', + width: '10000px', + height: '10000px', backgroundImage: ` linear-gradient(${theme.colors.border.weak} 1px, transparent 1px), linear-gradient(90deg, ${theme.colors.border.weak} 1px, transparent 1px) diff --git a/public/app/features/explore-map/components/ExploreMapPanelContainer.tsx b/public/app/features/explore-map/components/ExploreMapPanelContainer.tsx index 9eb316de364..8109f315e45 100644 --- a/public/app/features/explore-map/components/ExploreMapPanelContainer.tsx +++ b/public/app/features/explore-map/components/ExploreMapPanelContainer.tsx @@ -1,6 +1,6 @@ import { css, cx } from '@emotion/css'; import { useCallback, useRef } from 'react'; -import { DraggableData, Rnd, RndResizeCallback } from 'react-rnd'; +import { Rnd, RndDragCallback, RndResizeCallback } from 'react-rnd'; import { GrafanaTheme2 } from '@grafana/data'; import { t } from '@grafana/i18n'; @@ -32,8 +32,8 @@ export function ExploreMapPanelContainer({ panel }: ExploreMapPanelContainerProp const viewport = useSelector((state) => state.exploreMap.viewport); const isSelected = selectedPanelId === panel.id; - const handleDragStop = useCallback( - (_e: MouseEvent | TouchEvent, data: DraggableData) => { + const handleDragStop: RndDragCallback = useCallback( + (_e, data) => { dispatch( updatePanelPosition({ panelId: panel.id, diff --git a/public/app/features/explore-map/components/ExploreMapToolbar.tsx b/public/app/features/explore-map/components/ExploreMapToolbar.tsx index d89e9e98790..842b1ae8a1d 100644 --- a/public/app/features/explore-map/components/ExploreMapToolbar.tsx +++ b/public/app/features/explore-map/components/ExploreMapToolbar.tsx @@ -21,7 +21,12 @@ export function ExploreMapToolbar() { const viewport = useSelector((state) => state.exploreMap.viewport); const handleAddPanel = useCallback(() => { - dispatch(addPanel({})); + dispatch(addPanel({ + viewportSize: { + width: window.innerWidth, + height: window.innerHeight + } + })); }, [dispatch]); const handleResetCanvas = useCallback(() => { @@ -47,8 +52,14 @@ export function ExploreMapToolbar() { const handleResetZoom = useCallback(() => { if (transformRef?.current) { - // Reset both scale and position to initial values - transformRef.current.setTransform(0, 0, 1, 200); + // Reset to center of canvas (5000, 5000) + // Calculate position to center the viewport + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; + const panX = -(5000 - viewportWidth / 2); + const panY = -(5000 - viewportHeight / 2); + + transformRef.current.setTransform(panX, panY, 1, 200); } }, [transformRef]); diff --git a/public/app/features/explore-map/hooks/useCanvasPersistence.ts b/public/app/features/explore-map/hooks/useCanvasPersistence.ts index 80918511116..1234a4214dc 100644 --- a/public/app/features/explore-map/hooks/useCanvasPersistence.ts +++ b/public/app/features/explore-map/hooks/useCanvasPersistence.ts @@ -6,7 +6,7 @@ import { createErrorNotification, createSuccessNotification } from 'app/core/cop import { useDispatch, useSelector } from 'app/types/store'; import { loadCanvas } from '../state/exploreMapSlice'; -import { ExploreMapState, SerializedExploreState } from '../state/types'; +import { ExploreMapState, initialExploreMapState, SerializedExploreState } from '../state/types'; const STORAGE_KEY = 'grafana.exploreMap.state'; @@ -73,6 +73,7 @@ export function useCanvasPersistence() { // Enrich with Explore state before exporting const enrichedState: ExploreMapState = { ...exploreMapState, + viewport: initialExploreMapState.viewport, // Don't export viewport state - use initial centered viewport cursors: {}, // Don't export cursor state - it's ephemeral panels: Object.fromEntries( Object.entries(exploreMapState.panels).map(([panelId, panel]) => { diff --git a/public/app/features/explore-map/state/exploreMapSlice.ts b/public/app/features/explore-map/state/exploreMapSlice.ts index 4707427f64e..9aed3476a9e 100644 --- a/public/app/features/explore-map/state/exploreMapSlice.ts +++ b/public/app/features/explore-map/state/exploreMapSlice.ts @@ -12,18 +12,33 @@ import { UserCursor, } from './types'; +interface AddPanelPayload { + position?: Partial; + viewportSize?: { width: number; height: number }; +} + const exploreMapSlice = createSlice({ name: 'exploreMap', initialState: initialExploreMapState, reducers: { - addPanel: (state, action: PayloadAction<{ position?: Partial }>) => { + addPanel: (state, action: PayloadAction) => { const panelId = uuidv4(); const exploreId = generateExploreId(); + + // Calculate center of current viewport in canvas coordinates + const viewportSize = action.payload.viewportSize || { width: 1920, height: 1080 }; + const canvasCenterX = (-state.viewport.panX + viewportSize.width / 2) / state.viewport.zoom; + const canvasCenterY = (-state.viewport.panY + viewportSize.height / 2) / state.viewport.zoom; + + const panelWidth = 600; + const panelHeight = 400; + const offset = Object.keys(state.panels).length * 30; + const defaultPosition: PanelPosition = { - x: 100 + Object.keys(state.panels).length * 50, - y: 100 + Object.keys(state.panels).length * 50, - width: 600, - height: 400, + x: canvasCenterX - panelWidth / 2 + offset, + y: canvasCenterY - panelHeight / 2 + offset, + width: panelWidth, + height: panelHeight, zIndex: state.nextZIndex, }; diff --git a/public/app/features/explore-map/state/types.ts b/public/app/features/explore-map/state/types.ts index 5809afabf1f..4862a8f6c44 100644 --- a/public/app/features/explore-map/state/types.ts +++ b/public/app/features/explore-map/state/types.ts @@ -50,8 +50,12 @@ export interface ExploreMapState { export const initialExploreMapState: ExploreMapState = { viewport: { zoom: 1, - panX: 0, - panY: 0, + // Center the viewport at canvas center (5000, 5000) + // The pan values are offsets, so we need to calculate based on viewport size + // At zoom 1, we want canvas position 5000 to be at screen center + // Initial position assumes typical viewport of ~1920x1080 + panX: -4040, // -(5000 - 1920/2) = -4040 + panY: -4460, // -(5000 - 1080/2) = -4460 }, panels: {}, selectedPanelId: undefined,