# Canvas Panel - Technical Documentation This documentation provides a comprehensive technical overview of the Canvas visualization panel in Grafana. ## Table of Contents 1. [Overview](#overview) 2. [Architecture](#architecture) 3. [File Structure](#file-structure) 4. [Key Components](#key-components) 5. [External Dependencies](#external-dependencies) 6. [Configuration Schema](#configuration-schema) 7. [Element System](#element-system) 8. [Connection System](#connection-system) 9. [Editing System](#editing-system) 10. [Performance Considerations](#performance-considerations) 11. [Current Limitations and TODOs](#current-limitations-and-todos) 12. [Migration System](#migration-system) 13. [Evolution History](#evolution-history) --- ## Overview The Canvas panel is a native Grafana visualization that allows users to create custom layouts with explicit element placement. Unlike standard Grafana visualizations, Canvas provides a freeform design surface where elements can be positioned, resized, rotated, and connected to each other. **Key Capabilities:** - Freeform element placement with drag-and-drop - Multiple element types (shapes, icons, text, metric values, etc.) - Visual connections between elements with customizable styling - Constraint-based responsive layouts - Inline editing mode for direct manipulation - Pan and zoom functionality (feature-toggled) - Data-driven element styling via dimensions - One-click links and actions - Tooltip support **Plugin Metadata:** - ID: `canvas` - Type: `panel` - Description: "Explicit element placement" --- ## Architecture ### High-Level Architecture ``` ┌─────────────────────────────────────────────────────────────────┐ │ CanvasPanel │ │ (React Component - Entry Point) │ │ │ │ ┌─────────────────────────────────────────────────────────────┐│ │ │ Scene ││ │ │ (Runtime Manager - Core Orchestrator) ││ │ │ ││ │ │ ┌───────────────┐ ┌───────────────┐ ┌──────────────────┐ ││ │ │ │ RootElement │ │ Connections │ │ Moveable/ │ ││ │ │ │ (FrameState) │ │ (SVG Lines) │ │ Selecto │ ││ │ │ │ │ │ │ │ (Interactions) │ ││ │ │ │ ┌───────────┐ │ │ │ │ │ ││ │ │ │ │FrameState │ │ │ │ │ │ ││ │ │ │ │ Elements │ │ │ │ │ │ ││ │ │ │ │ ┌───────┐ │ │ │ │ │ │ ││ │ │ │ │ │Element│ │ │ │ │ │ │ ││ │ │ │ │ │State │ │ │ │ │ │ │ ││ │ │ │ │ └───────┘ │ │ │ │ │ │ ││ │ │ │ └───────────┘ │ │ │ │ │ ││ │ │ └───────────────┘ └───────────────┘ └──────────────────┘ ││ │ └─────────────────────────────────────────────────────────────┘│ └─────────────────────────────────────────────────────────────────┘ ``` ### Data Flow 1. **Panel Props** → `CanvasPanel` receives props from Grafana's panel system 2. **Scene Creation** → Scene is instantiated with options and callbacks 3. **Root Element** → Root element tree is built from saved configuration 4. **Data Update** → Panel data flows through dimension context to update element styling 5. **User Interaction** → Moveable/Selecto handle drag/resize/select operations 6. **State Changes** → Element changes propagate back through Scene to save model 7. **Rendering** → React re-renders triggered by revision IDs and state changes ### State Management The Canvas uses a hybrid state management approach: - **RxJS Subjects** for reactive updates (selection, edit mode, moved events) - **React state** for UI-level concerns (inline edit open, refresh counter) - **Mutable class state** for performance-critical element positioning - **Revision IDs** (`revId`) to trigger React re-renders when needed --- ## File Structure ``` public/app/ ├── plugins/panel/canvas/ │ ├── CanvasPanel.tsx # Main panel component │ ├── module.tsx # Plugin registration and options │ ├── panelcfg.gen.ts # Generated TypeScript types from schema │ ├── panelcfg.cue # CUE schema definition │ ├── types.ts # TypeScript type definitions │ ├── utils.ts # Utility functions │ ├── migrations.ts # Panel migration handlers │ ├── globalStyles.ts # Global CSS styles │ ├── plugin.json # Plugin metadata │ ├── components/ │ │ ├── CanvasContextMenu.tsx # Right-click context menu │ │ ├── CanvasTooltip.tsx # Hover tooltips │ │ ├── SetBackground.tsx # Background configuration UI │ │ └── connections/ │ │ ├── ConnectionAnchors.tsx # Connection anchor points │ │ ├── ConnectionAnchors2.tsx # Pan/zoom version │ │ ├── Connections.tsx # Connection rendering │ │ ├── Connections2.tsx # Pan/zoom version │ │ ├── ConnectionSVG.tsx # SVG connection lines │ │ └── ConnectionSVG2.tsx # Pan/zoom version │ └── editor/ │ ├── connectionEditor.tsx # Connection property editor │ ├── LineStyleEditor.tsx # Connection line styling │ ├── options.ts # Editor option utilities │ ├── panZoomHelp.tsx # Pan/zoom help text │ ├── element/ │ │ ├── elementEditor.tsx # Element property editor │ │ ├── ActionsEditor.tsx # Action configuration │ │ ├── APIEditor.tsx # API call configuration │ │ ├── ButtonStyleEditor.tsx # Button styling │ │ ├── ConstraintSelectionBox.tsx │ │ ├── DataLinksEditor.tsx # Data link configuration │ │ ├── ParamsEditor.tsx # Parameter editing │ │ ├── PlacementEditor.tsx # Position/size editor │ │ ├── QuickPositioning.tsx # Quick position buttons │ │ └── utils.ts │ ├── inline/ │ │ ├── InlineEdit.tsx # Draggable inline editor │ │ ├── InlineEditBody.tsx # Inline editor content │ │ └── TabsEditor.tsx # Tab-based editing │ └── layer/ │ ├── layerEditor.tsx # Layer management │ ├── tree.ts # Tree data structures │ ├── TreeNavigationEditor.tsx # Tree view │ └── TreeNodeTitle.tsx # Tree node rendering │ └── features/canvas/ ├── element.ts # CanvasElementItem interface ├── frame.ts # CanvasFrameOptions type ├── registry.ts # Element type registry ├── types.ts # Shared types ├── elements/ │ ├── button.tsx # Button element │ ├── cloud.tsx # Cloud shape │ ├── droneFront.tsx # Drone visualization │ ├── droneSide.tsx # Drone visualization │ ├── droneTop.tsx # Drone visualization │ ├── ellipse.tsx # Ellipse/circle shape │ ├── icon.tsx # Icon element │ ├── metricValue.tsx # Metric value display │ ├── notFound.tsx # Fallback element │ ├── parallelogram.tsx # Parallelogram shape │ ├── rectangle.tsx # Rectangle shape │ ├── text.tsx # Text element │ ├── triangle.tsx # Triangle shape │ ├── windTurbine.tsx # Wind turbine visualization │ └── server/ │ ├── server.tsx # Server element │ └── types/ │ ├── database.tsx │ ├── single.tsx │ ├── stack.tsx │ └── terminal.tsx └── runtime/ ├── ables.tsx # Custom Moveable abilities ├── element.tsx # ElementState class ├── frame.tsx # FrameState class ├── root.tsx # RootElement class ├── scene.tsx # Scene class (main runtime) ├── sceneAbleManagement.ts # Moveable/Selecto setup └── sceneElementManagement.ts # Element DOM manipulation ``` --- ## Key Components ### CanvasPanel (`CanvasPanel.tsx`) The main React component that serves as the entry point for the Canvas panel. **Responsibilities:** - Instantiates and manages the Scene object - Handles panel lifecycle (mount, update, unmount) - Subscribes to panel edit events - Manages inline edit and background setting modals - Coordinates between React and Scene state **Key State:** ```typescript interface State { refresh: number; // Trigger re-renders openInlineEdit: boolean; // Inline editor visibility openSetBackground: boolean; // Background modal visibility contextMenuAnchorPoint: AnchorPoint; moveableAction: boolean; // Connection update flag } ``` ### Scene (`runtime/scene.tsx`) The core runtime manager that orchestrates all canvas functionality. **Responsibilities:** - Element tree management (root, frames, elements) - Selection state via RxJS ReplaySubject - Data context for dimension resolution - Moveable/Selecto lifecycle management - Connection state management - Pan/zoom via InfiniteViewer (feature-toggled) - Save model generation **Key Properties:** ```typescript class Scene { root: RootElement; // Element tree root byName: Map<string, ElementState>; // Element lookup by name selection: ReplaySubject<ElementState[]>; // Current selection moved: Subject<number>; // Drag/resize events connections: Connections; // Connection manager moveable?: Moveable; // Drag/resize handler selecto?: Selecto; // Selection handler infiniteViewer?: InfiniteViewer; // Pan/zoom (feature-toggled) context: DimensionContext; // Data dimension resolver // ... many more properties } ``` ### ElementState (`runtime/element.tsx`) Represents a single canvas element's runtime state. **Responsibilities:** - Element positioning and sizing via CSS - Constraint-based layout calculations - Data-driven styling (background, border, text) - Event handling (click, hover, drag, resize, rotate) - Link and action execution - Tooltip management **Key Methods:** - `applyLayoutStylesToDiv()` - Apply CSS positioning - `setPlacementFromConstraint()` - Calculate position from constraints - `updateData()` - Update element from data context - `applyDrag()`, `applyResize()`, `applyRotate()` - Moveable event handlers - `renderElement()` - React render method ### FrameState (`runtime/frame.tsx`) Container for grouping multiple elements. **Responsibilities:** - Child element management - Layer reordering - Duplicate/delete operations - Recursive data updates - Save model aggregation ### RootElement (`runtime/root.tsx`) Special frame that serves as the canvas root. **Key Differences from FrameState:** - Always 100% width/height - No placement/constraint options saved - Direct scene reference - Change callback for save propagation --- ## External Dependencies ### Moveable **Package:** `moveable` **Purpose:** Drag, resize, and rotate functionality Moveable provides the core manipulation capabilities: - Draggable elements - Resizable elements with handles - Rotatable elements - Snapping to other elements - Custom "ables" for dimension/constraint overlays ### Selecto **Package:** `selecto` **Purpose:** Element selection handling Selecto manages: - Click-to-select - Marquee/box selection - Multi-select with shift key - Selection state coordination with Moveable ### InfiniteViewer **Package:** `infinite-viewer` **Purpose:** Pan and zoom functionality (feature-toggled) When `canvasPanelPanZoom` feature toggle is enabled: - Mouse wheel zoom - Drag to pan - Zoom to fit content - Middle-click/Ctrl+right-click panning ### RxJS **Package:** `rxjs` **Purpose:** Reactive state management Used for: - Selection state (`ReplaySubject`) - Edit mode state (`BehaviorSubject`) - Move events (`Subject`) - Subscription management --- ## Configuration Schema ### Options Interface ```typescript interface Options { inlineEditing: boolean; // Enable direct manipulation panZoom: boolean; // Enable pan and zoom zoomToContent: boolean; // Auto-zoom to fit showAdvancedTypes: boolean; // Show experimental elements tooltip: CanvasTooltip; // Tooltip configuration root: { // Element tree name: string; type: 'frame'; elements: CanvasElementOptions[]; }; } ``` ### Element Options ```typescript interface CanvasElementOptions { name: string; // Unique element name type: string; // Element type ID config?: unknown; // Type-specific config placement?: Placement; // Position and size constraint?: Constraint; // Responsive behavior background?: BackgroundConfig; // Background styling border?: LineConfig; // Border styling connections?: CanvasConnection[]; // Outgoing connections links?: DataLink[]; // Data links actions?: Action[]; // Clickable actions } ``` ### Constraint System ```typescript enum HorizontalConstraint { Left = 'left', Right = 'right', LeftRight = 'leftright', // Stretch Center = 'center', Scale = 'scale', // Percentage-based } enum VerticalConstraint { Top = 'top', Bottom = 'bottom', TopBottom = 'topbottom', // Stretch Center = 'center', Scale = 'scale', // Percentage-based } ``` --- ## Element System ### Element Registry Elements are registered in `features/canvas/registry.ts`: **Default Elements:** - `metricValue` - Display field values with formatting - `text` - Static or dynamic text - `ellipse` - Circle/ellipse shape - `rectangle` - Rectangle shape - `icon` - SVG icons from Grafana icon library - `server` - Server visualization with status indicators - `triangle` - Triangle shape - `cloud` - Cloud shape - `parallelogram` - Parallelogram shape **Advanced/Experimental Elements:** - `button` - Clickable button with API actions - `windTurbine` - Animated wind turbine - `droneTop`, `droneFront`, `droneSide` - Drone visualizations ### Creating Custom Elements Elements implement the `CanvasElementItem` interface: ```typescript interface CanvasElementItem<TConfig, TData> { id: string; name: string; description: string; display: ComponentType<CanvasElementProps<TConfig, TData>>; defaultSize?: Placement; hasEditMode?: boolean; getNewOptions: (options?) => Omit<CanvasElementOptions<TConfig>, 'type' | 'name'>; prepareData?: (ctx: DimensionContext, options: CanvasElementOptions<TConfig>) => TData; registerOptionsUI?: PanelOptionsSupplier<CanvasElementOptions<TConfig>>; customConnectionAnchors?: Array<{x: number; y: number}>; standardEditorConfig?: StandardEditorConfig; } ``` --- ## Connection System Connections allow visual links between elements with the following features: - **Source/Target Anchors:** Normalized coordinates (-1 to 1) from element center - **Vertex Support:** Add intermediate points to create custom paths - **Styling:** Color, width, dash style, animation - **Direction Arrows:** Forward, reverse, both, or none - **Data Binding:** Dynamic colors and sizes from data ### Connection Data Structure ```typescript interface CanvasConnection { source: ConnectionCoordinates; // Source anchor point target: ConnectionCoordinates; // Target anchor point targetName?: string; // Target element name path: ConnectionPath; // Currently only 'straight' color?: ColorDimensionConfig; // Line color size?: ScaleDimensionConfig; // Line width lineStyle?: LineStyleConfig; // Solid/dashed/dotted vertices?: ConnectionCoordinates[]; // Intermediate points direction?: DirectionDimensionConfig; radius?: ScaleDimensionConfig; // Corner radius } ``` --- ## Editing System ### Inline Editor The inline editor provides direct manipulation without opening the panel editor: - **Draggable/Resizable window** persisted to localStorage - **Tab-based interface:** Element management, Selected element, Selected connection - **Tree navigation** for element hierarchy - **Quick access** to element properties ### Context Menu Right-click context menu provides: - Add element - Duplicate selected - Delete selected - Move to top/bottom of layer - Open inline editor - Set background ### Element Selection Selection handling coordinates between: - Selecto (click/marquee selection) - Moveable (manipulation targets) - Scene selection subject (state propagation) - Panel options editor (property display) --- ## Performance Considerations ### Optimization Strategies 1. **Revision IDs:** Elements use `revId` counters to trigger targeted re-renders instead of full tree updates 2. **Mutable State:** Position and style changes update DOM directly via `applyLayoutStylesToDiv()` rather than through React state 3. **Data Update Gating:** `ignoreDataUpdate` flag prevents redundant data processing during drag operations 4. **Selective Rendering:** Connection updates only trigger when affected elements move (`connectionsNeedUpdate()`) 5. **Lazy Initialization:** Moveable/Selecto are initialized after DOM elements are ready via `setTimeout` ### Known Performance Concerns 1. **Large Element Counts:** The constraint calculation system recalculates positions for all elements on resize 2. **Streaming Data:** Frequent data updates can cause excessive re-renders; elements should debounce when appropriate 3. **Connection Rendering:** SVG connection paths are recalculated on every move event 4. **Feature Toggle Impact:** Pan/zoom mode (`canvasPanelPanZoom`) adds InfiniteViewer overhead --- ## Current Limitations and TODOs ### From Code Analysis #### Scene/Runtime TODOs: - **Dashboard Scenes Migration:** "TODO: Will need to update this approach for dashboard scenes migration (new dashboard edit experience)" - `scene.tsx:117` - **Style Application Workarounds:** Multiple TODOs about workarounds for applying styles after size updates - `scene.tsx:234-240` - **Connection Anchor Stacking:** "Remove this after dealing with the connection anchors stacking context issue" - `scene.tsx:240` #### Element TODOs: - **SVG Element Handling:** "TODO: This is a hack, we should have a better way to handle this" - `element.tsx:242, 368` - **Rotation Constraints:** "TODO: Fix behavior for top+bottom, left+right, center, and scale constraints" for rotated elements - `element.tsx:415` - **Action Variables:** "TODO: implement actionVars" in confirmation modal - `element.tsx:1047` #### Connection TODOs: - **Vertex Removal:** "TODO for vertex removal, clear out originals?" - `Connections.tsx:444`, `Connections2.tsx:414` - **Coordinate Conversion:** "TODO: Break this out into util function and add tests" - `Connections.tsx:245` #### Editor TODOs: - **Element-Specific Icons:** "TODO: Implement element specific icons" in tree navigation - `TreeNavigationEditor.tsx:100` - **Frame Functionality:** "TODO: This functionality is currently kinda broken / no way to decouple / delete created frames" - `TreeNavigationEditor.tsx:128` - **Non-Root Navigation:** "TODO: the non-root nav option" - `layerEditor.tsx:69` #### Utility TODOs: - **Division by Zero:** "TODO look into a better way to avoid division by zero" - `utils.ts:194` - **Style Parsing:** "TODO: there sould be a better way than parseFloat" - `utils.ts:243` - **Row Index:** "@TODO revisit, currently returning last row index for field" - `utils.ts:325` - **Color Migration:** "@TODO Remove after v10.x" for color string migration - `utils.ts:117` #### Accessibility TODOs: - **Reduced Motion:** "TODO: figure out what styles to apply when prefers-reduced-motion is set" for drone/turbine animations - `droneTop.tsx:184, droneSide.tsx:125, droneFront.tsx:126` ### Known Limitations 1. **Frame Duplication:** Cannot duplicate frame elements (nested groups) 2. **Connection Path Types:** Only straight line connections supported (no curved/orthogonal) 3. **Element Nesting:** Frame nesting is feature-flagged and partially implemented 4. **Rotation + Constraints:** Rotation behaves unexpectedly with certain constraint combinations 5. **Pan/Zoom + Editing:** Some editing interactions conflict with pan/zoom gestures --- ## Migration System The canvas panel includes a migration handler (`migrations.ts`) for backward compatibility: ### Migration History | Version | Migration | |---------|-----------| | Initial | Rename `text-box` type to `rectangle` | | 11.0 | Ellipse refactor: Move element-specific background/border to standard config | | ≤11.3 | Actions: Move `action.options` to `action.fetch` | | ≤11.6 | One-click: Migrate `oneClickMode` to first link/action's `oneClick` property | | ≤12.2 | Connection direction: Convert string direction to dimension config format | --- ## Evolution History ### 2021 - Foundation - **September 2021:** Initial alpha canvas panel with basic interfaces (#37279) - **October 2021:** Element movement/resize, layer editor, multi-layer support - **November 2021:** Button element with onClick support - **December 2021:** Name-based element UIDs ### 2022 - Core Features - **April 2022:** Major constraint system overhaul, scale + center constraints - **May 2022:** Inline editing, context menu, frames (replacing "groups") - **July 2022:** Tree view navigation, improved context menu UX - **August 2022:** Metric value element, inline element settings - **September-November 2022:** Root context menu, selection fixes ### 2023 - Connections & Polish - **January 2023:** Connection positioning improvements, tooltip for data links - **March-April 2023:** Connection properties based on data, schema migration - **May 2023:** File restructuring, panel edit mode fixes ### 2024 - Advanced Features - **January 2024:** Pan and zoom functionality (#76705) - **March 2024:** Infinite pan/zoom, element rotation (#83295, #84968) - **April-May 2024:** New basic shapes (triangle, cloud, parallelogram), vertex control for connections - **June 2024:** Improved tooltips, scene refactoring - **August 2024:** Connection line animation, direction options ### 2025 - Refinement - **February 2025:** One-click links and actions (#99616) - **July-August 2025:** Pan/zoom improvements, tooltip options, dynamic connection direction - **September 2025:** Infinity authentication for actions - **November 2025:** Component refactoring, accessibility improvements ### Key Architectural Changes 1. **Groups → Frames (May 2022):** Renamed internal "group" concept to "frame" for clarity 2. **Constraint System Overhaul (April 2022):** Complete rewrite of responsive layout system 3. **Schema Migration (May 2023):** Moved to CUE-based schema with generated TypeScript 4. **Pan/Zoom Architecture (2024):** Added InfiniteViewer with parallel implementation (`Connections2.tsx`, etc.) 5. **Actions System (2024-2025):** Added support for API actions with one-click execution --- ## Additional Resources - [Official Canvas Documentation](https://grafana.com/docs/grafana/latest/panels-visualizations/visualizations/canvas/) - [GitHub Issues - Canvas](https://github.com/grafana/grafana/issues?q=is%3Aissue+label%3Aarea%2Fpanel%2Fcanvas) - [Feature Request Template](https://github.com/grafana/grafana/issues/new?assignees=&labels=type%2Ffeature-request,area%2Fpanel%2Fcanvas&title=Canvas:&projects=grafana-dataviz&template=1-feature_requests.md)