Make canvas bigger, start in the middle

This commit is contained in:
Aleksandar Petrov
2025-11-28 18:04:33 -04:00
parent 09d0deb180
commit 3af9cb8543
6 changed files with 62 additions and 20 deletions
@@ -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 (
<div className={styles.canvasWrapper}>
<TransformWrapper
ref={contextTransformRef as any}
ref={contextTransformRef}
initialScale={viewport.zoom}
initialPositionX={viewport.panX}
initialPositionY={viewport.panY}
@@ -69,7 +69,18 @@ export function ExploreMapCanvas() {
wheel={{ step: 0.1 }}
>
<TransformComponent wrapperClass={styles.transformWrapper} contentClass={styles.transformContent}>
<div ref={canvasRef} className={styles.canvas} onClick={handleCanvasClick}>
<div
ref={canvasRef}
className={styles.canvas}
onClick={handleCanvasClick}
onKeyDown={(e) => {
if (e.key === 'Escape') {
dispatch(selectPanel({ panelId: undefined }));
}
}}
role="button"
tabIndex={0}
>
{Object.values(panels).map((panel) => (
<ExploreMapPanelContainer key={panel.id} panel={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)
@@ -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,
@@ -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]);
@@ -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]) => {
@@ -12,18 +12,33 @@ import {
UserCursor,
} from './types';
interface AddPanelPayload {
position?: Partial<PanelPosition>;
viewportSize?: { width: number; height: number };
}
const exploreMapSlice = createSlice({
name: 'exploreMap',
initialState: initialExploreMapState,
reducers: {
addPanel: (state, action: PayloadAction<{ position?: Partial<PanelPosition> }>) => {
addPanel: (state, action: PayloadAction<AddPanelPayload>) => {
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,
};
@@ -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,