Keyboard Shortcuts
The keyboard shortcuts system in ngDiagram lets users perform common actions quickly using customizable key combinations. It supports platform-specific modifier keys (automatically handling Ctrl/Cmd differences), multiple shortcuts per action, and dynamic runtime updates.
Available Actions
Section titled “Available Actions”All available shortcut actions with their default key bindings. See ShortcutActionName for the complete type reference.
| Action | Default Shortcut | Description |
|---|---|---|
boxSelection | Shift held | Enable box selection mode (pointer only) |
cancelInteraction | Escape | Cancel the in-progress interaction (linking, drag, resize, rotate, pan) since v1.3.0 |
copy | Ctrl/Cmd + C | Copy selected elements to clipboard |
cut | Ctrl/Cmd + X | Cut selected elements to clipboard |
deleteSelection | Delete or Backspace | Delete currently selected elements |
keyboardMoveSelectionDown | ↓ | Move selected elements down |
keyboardMoveSelectionLeft | ← | Move selected elements left |
keyboardMoveSelectionRight | → | Move selected elements right |
keyboardMoveSelectionUp | ↑ | Move selected elements up |
keyboardPanDown | ↓ | Pan viewport down |
keyboardPanLeft | ← | Pan viewport left |
keyboardPanRight | → | Pan viewport right |
keyboardPanUp | ↑ | Pan viewport up |
keyboardZoomIn | = | Increase diagram viewport scale |
keyboardZoomOut | - | Decrease diagram viewport scale |
multiSelection | Ctrl/Cmd held | Multi-selection mode (pointer only) |
paste | Ctrl/Cmd + V | Paste elements from clipboard |
redo | Ctrl/Cmd + Y | Redo last undone action (not implemented by default; requires custom model) |
selectAll | Ctrl/Cmd + A | Select all elements in the diagram |
undo | Ctrl/Cmd + Z | Undo last action (not implemented by default; requires custom model) |
zoom | Ctrl/Cmd + Wheel | Increase or decrease diagram viewport scale |
Arrow Keys: Move vs Pan
Section titled “Arrow Keys: Move vs Pan”Each arrow key is bound to two actions: a keyboardMoveSelection action and a keyboardPan action (see the table
above). Exactly one of them handles each key press:
- The selection moves when node dragging is enabled
(
nodeDraggingEnabled) and at least one selected node is visible and draggable. - The viewport pans in every other case: nothing is selected,
nodeDraggingEnabledisfalse, or the selection contains only hidden ordraggable: falsenodes. Panning requiresviewportPanningEnabled.
A selection that cannot move never blocks the arrow keys.
Focus and Tab Order
Section titled “Focus and Tab Order”Shortcuts only work when focus is inside the diagram. The diagram takes focus when the user clicks it, and it is also part of the page’s Tab order, so the user can reach it with the Tab key.
The diagram adds two Tab stops of its own: the diagram container and the watermark link.
Removing the Diagram from the Tab Order since v1.4.0
Section titled “Removing the Diagram from the Tab Order ”If your application manages the focus on its own — for example with a roving tabindex on the nodes — set
tabbable to false. Both Tab stops are then removed from the Tab order:
<ng-diagram [model]="model" [tabbable]="false" />- Both elements stay clickable and keep working with screen readers. Only the Tab stop is removed.
- Shortcuts keep working once focus is inside the diagram, for example after a click.
- Focusable content in your own node and edge templates is not affected. It keeps its own Tab stops, so the application stays in control of them.
Basic Usage
Section titled “Basic Usage”To override, disable, or extend default shortcuts, use the configureShortcuts() helper:
import { configureShortcuts } from 'ng-diagram';
const config = { shortcuts: configureShortcuts([ // Override: Change paste to Ctrl+B { actionName: 'paste', bindings: [{ key: 'b', modifiers: { primary: true } }], }, // Disable: Empty bindings array { actionName: 'undo', bindings: [], }, // Multiple bindings: WSAD + Arrow keys { actionName: 'keyboardMoveSelectionUp', bindings: [{ key: 'w' }, { key: 'ArrowUp' }], }, ]),};You can also merge custom shortcuts with existing ones at runtime:
// Get current shortcuts from the serviceconst currentShortcuts = ngDiagramService.config().shortcuts;
// Merge custom shortcuts with existing onesconst updatedShortcuts = configureShortcuts( [ { actionName: 'paste', bindings: [{ key: 'b', modifiers: { primary: true } }], }, ], currentShortcuts // Pass existing shortcuts as base);
// Update the configurationngDiagramService.updateConfig({ shortcuts: updatedShortcuts });configureShortcuts(customShortcuts, baseShortcuts?) merges your custom shortcuts with defaults:
- First parameter: Custom shortcuts (overrides actions with matching
actionName) - Second parameter: Optional base shortcuts. Defaults are used if omitted.
- Returns: A merged shortcuts array
Unspecified actions retain their existing shortcuts.
Configuration Reference
Section titled “Configuration Reference”Shortcut Structure
Section titled “Shortcut Structure”Keyboard and pointer shortcuts in ngDiagram are defined through the ShortcutDefinition interface. A shortcut definition maps an action name to one or more key bindings (keyboard or modifier-only combinations).
Each shortcut may include:
- One or more keyboard bindings (e.g., Ctrl + C)
- Or one or more modifier-only bindings (e.g., Shift + click for box selection)
See ShortcutDefinition API reference.
Available Modifiers
Section titled “Available Modifiers”interface InputModifiers { primary: boolean; // Ctrl (Windows/Linux) OR Cmd (macOS) - auto-normalized secondary: boolean; // Alt key shift: boolean; // Shift key meta: boolean; // Windows key or Cmd key}Platform normalization:
The primary modifier automatically maps to Ctrl on Windows/Linux and Cmd on macOS,
allowing you to define shortcuts once that work seamlessly across platforms.
Key Names
Section titled “Key Names”Use standard browser key names: 'a', 'b', 'Delete', 'Backspace', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', etc. Keys are case-sensitive.
See MDN KeyboardEvent.key documentation for the complete list of valid key values.
Modifier-Only Shortcuts
Section titled “Modifier-Only Shortcuts”Modifier-only shortcuts (bindings without a key) are used for pointer-based interactions,
such as holding Shift for box selection or holding Ctrl/Cmd and using mouse wheel for zoom in and zoom out.
{ modifiers: { shift: true, }} // Matches when Shift held during clickThese shortcuts ensure that typing keys won’t accidentally trigger diagram actions.
Example
Section titled “Example”import '@angular/compiler';import { Component } from '@angular/core';import { NgDiagramModelService, provideNgDiagram } from 'ng-diagram';import { DiagramComponent } from './diagram.component';
@Component({ imports: [DiagramComponent], template: `<shortcut-manager-example></shortcut-manager-example> `, styles: [ ` :host { display: flex; position: relative; } `, ], providers: [NgDiagramModelService, provideNgDiagram()],})export class DiagramWrapperComponent {}import '@angular/compiler';import { Component } from '@angular/core';import { configureShortcuts, initializeModel, NgDiagramBackgroundComponent, NgDiagramComponent, NgDiagramViewportService, type NgDiagramConfig,} from 'ng-diagram';import { ShortcutButtonsComponent } from './shortcut-buttons/shortcut-buttons.component';
@Component({ selector: 'shortcut-manager-example', imports: [ NgDiagramComponent, NgDiagramBackgroundComponent, ShortcutButtonsComponent, ], template: ` <div class="not-content diagram"> <ng-diagram [model]="model" [config]="config"> <ng-diagram-background /> </ng-diagram> </div> <shortcut-buttons /> `, styleUrl: './diagram.component.scss', providers: [NgDiagramViewportService],})export class DiagramComponent { config = { zoom: { zoomToFit: { onInit: true, padding: 100, }, }, shortcuts: configureShortcuts([ // Custom keyboard shortcuts for moving selection { actionName: 'keyboardMoveSelectionUp', bindings: [{ key: 'w' }, { key: 'ArrowUp' }], }, { actionName: 'keyboardMoveSelectionDown', bindings: [{ key: 's' }, { key: 'ArrowDown' }], }, { actionName: 'keyboardMoveSelectionLeft', bindings: [{ key: 'a' }, { key: 'ArrowLeft' }], }, { actionName: 'keyboardMoveSelectionRight', bindings: [{ key: 'd' }, { key: 'ArrowRight' }], }, ]), } satisfies NgDiagramConfig;
model = initializeModel({ nodes: [ { id: '1', position: { x: 100, y: 100 }, data: { label: 'Node 1', }, }, { id: '2', position: { x: 400, y: 100 }, data: { label: 'Node 2', }, }, { id: '3', position: { x: 250, y: 300 }, data: { label: 'Node 3', }, }, ], });}import { Component, inject, signal } from '@angular/core';import { configureShortcuts, NgDiagramService, type ShortcutDefinition,} from 'ng-diagram';
@Component({ imports: [], selector: 'shortcut-buttons', template: ` <label class="checkbox-label"> <input type="checkbox" [checked]="useCustomShortcut()" (change)="togglePasteShortcut()" /> <span>Use Ctrl/Cmd + B for paste</span> </label> `, styleUrls: ['./shortcut-buttons.component.scss'],})export class ShortcutButtonsComponent { private readonly ngDiagramService = inject(NgDiagramService); protected readonly useCustomShortcut = signal(false);
togglePasteShortcut(): void { this.useCustomShortcut.update((value) => !value);
const currentShortcuts = this.ngDiagramService.config() .shortcuts as ShortcutDefinition[]; const updatedShortcuts = configureShortcuts( [ { actionName: 'paste', bindings: [ { key: this.useCustomShortcut() ? 'b' : 'v', modifiers: { primary: true }, }, ], }, ], currentShortcuts );
this.ngDiagramService.updateConfig({ shortcuts: updatedShortcuts, }); }}:host { position: relative; width: 100%; height: 100%; display: block; // needed for flex children to work, otherwise children renders outside of host}
.diagram { display: flex; height: var(--ng-diagram-height); border: var(--ng-diagram-border); margin-top: 0;}:host { position: absolute; right: 1rem; top: 1rem; display: flex; background-color: var(--ngd-node-bg-primary-default); border: var(--ng-diagram-border); margin-top: 0;
.checkbox-label { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.75rem; border-radius: 0.25rem; user-select: none; cursor: pointer;
input[type='checkbox'] { width: 1rem; height: 1rem; cursor: pointer; }
span { font-size: 0.875rem; } }}