GoJS provides a comprehensive tool system for handling user interactions like selection, dragging, linking, and editing. The tool system is managed by the ToolManager and provides both built-in tools and extensibility for custom interactions.
Central coordinator for all diagram tools, managing tool activation and user input routing.
class ToolManager extends Tool {
// Selection Tools
clickSelectingTool: ClickSelectingTool;
dragSelectingTool: DragSelectingTool;
// Movement and Manipulation Tools
draggingTool: DraggingTool;
resizingTool: ResizingTool;
rotatingTool: RotatingTool;
// Link Tools
linkingTool: LinkingTool;
relinkingTool: RelinkingTool;
linkReshapingTool: LinkReshapingTool;
// Editing Tools
textEditingTool: TextEditingTool;
contextMenuTool: ContextMenuTool;
// Navigation Tools
panningTool: PanningTool;
// Custom Tools
mouseDownTools: List<Tool>;
mouseMoveTools: List<Tool>;
mouseUpTools: List<Tool>;
// Timing and Behavior
hoverDelay: number;
toolTipDuration: number;
standardMouseOver: (() => void) | null;
standardMouseOut: (() => void) | null;
// Input State
currentTool: Tool;
defaultTool: Tool;
mouseDownPoint: Point;
lastInput: InputEvent;
}Handles moving parts by dragging with support for copying, grouping, and grid snapping.
class DraggingTool extends Tool {
// Drag Behavior
copiesEffectively: boolean;
copiesTree: boolean;
dragsTree: boolean;
dragsLink: boolean;
// Grid and Snapping
isGridSnapEnabled: boolean;
gridSnapCellSize: Size;
gridSnapCellSpot: Spot;
gridSnapOrigin: Point;
// Drag State
draggedParts: Map<Part, DraggingInfo>;
copiedParts: Map<Part, Part>;
// Validation
mayMove(): boolean;
mayCopy(): boolean;
computeEffectiveCollection(parts: Iterable<Part>): Map<Part, DraggingInfo>;
// Methods
doActivate(): void;
doMouseMove(): void;
doMouseUp(): void;
doDragOver(pt: Point, obj: GraphObject): void;
doDropOnto(pt: Point, obj: GraphObject): void;
}
interface DraggingInfo {
point: Point;
part: Part;
}Creates new links by dragging from one node to another with validation and visual feedback.
class LinkingTool extends Tool {
// Link Creation
temporaryLink: Link;
temporaryFromNode: Node;
temporaryFromPort: GraphObject;
temporaryToNode: Node;
temporaryToPort: GraphObject;
// Behavior
direction: LinkingDirection;
portGravity: number;
delay: number;
// Validation
isLinkValid(fromnode: Node, fromport: GraphObject, tonode: Node, toport: GraphObject): boolean;
isUnconnectedLinkValid(link: Link): boolean;
// Visual Feedback
temporaryTool: Tool;
archetypeLinkData: ObjectData;
// Methods
findLinkablePort(): GraphObject | null;
insertLink(fromnode: Node, fromport: GraphObject, tonode: Node, toport: GraphObject): Link | null;
}
enum LinkingDirection {
Either = 'Either',
ForwardsOnly = 'ForwardsOnly',
BackwardsOnly = 'BackwardsOnly'
}Provides resize handles for changing part dimensions with aspect ratio and size constraints.
class ResizingTool extends Tool {
// Handle Configuration
handleArchetype: GraphObject;
minSize: Size;
maxSize: Size;
// Resize Behavior
cellSize: Size;
isGridSnapEnabled: boolean;
oppositePoint: Point;
// State
handle: GraphObject;
adornedObject: GraphObject;
originalBounds: Rect;
// Methods
updateAdornments(part: Part): void;
resize(newr: Rect): void;
computeResize(p: Point): Size;
computeMinPoolSize(group: Group): Size;
}Handles in-place text editing with validation and custom editor support.
class TextEditingTool extends Tool {
// Editor Configuration
textBlock: TextBlock;
defaultTextEditor: HTMLElement;
starting: TextEditingStarting;
state: TextEditingState;
// Validation
textValidation: ((tb: TextBlock, oldstr: string, newstr: string) => boolean) | null;
// Events
selectsTextOnActivate: boolean;
// Methods
doActivate(): void;
acceptText(reason: TextEditingAccept): boolean;
doMouseUp(): void;
// State Management
isActive: boolean;
currentTextEditor: HTMLElement;
}
enum TextEditingStarting {
SingleClick = 'SingleClick',
SingleClickSelected = 'SingleClickSelected',
DoubleClick = 'DoubleClick'
}
enum TextEditingState {
None = 'None',
Active = 'Active',
Editing = 'Editing'
}
enum TextEditingAccept {
Enter = 'Enter',
Tab = 'Tab',
LostFocus = 'LostFocus',
MouseDown = 'MouseDown',
NotCancelled = 'NotCancelled'
}Usage Examples:
// Configure text editing behavior
diagram.toolManager.textEditingTool.starting = go.TextEditingStarting.SingleClick;
diagram.toolManager.textEditingTool.selectsTextOnActivate = true;
// Custom text validation
diagram.toolManager.textEditingTool.textValidation = (tb, oldstr, newstr) => {
// Limit text length and disallow empty strings
return newstr.length > 0 && newstr.length <= 50;
};Tools for selecting parts using click or drag operations.
class ClickSelectingTool extends Tool {
standardMouseSelect(): void;
selectPart(part: Part, extend: boolean): void;
}
class DragSelectingTool extends Tool {
box: Part;
delay: number;
isPartialInclusion: boolean;
computeBoxBounds(): Rect;
selectInBox(r: Rect): void;
}Usage Examples:
// Configure selection behavior
diagram.toolManager.clickSelectingTool.standardMouseSelect = function() {
// Custom selection logic
const part = this.findSelectableObject();
if (part) {
if (this.diagram.lastInput.control) {
// Toggle selection with Ctrl
part.isSelected = !part.isSelected;
} else {
// Normal selection
this.diagram.select(part);
}
}
};
// Customize drag selection box
diagram.toolManager.dragSelectingTool.box =
new go.Part()
.add(new go.Shape({
name: 'SHAPE',
fill: 'rgba(0,128,255,0.3)',
stroke: 'blue'
}));class CustomClickTool extends go.Tool {
constructor() {
super();
this.name = 'CustomClick';
}
canStart(): boolean {
if (!super.canStart()) return false;
const diagram = this.diagram;
const e = diagram.lastInput;
// Only start on double-click
return e.clickCount >= 2;
}
doMouseUp(): void {
const obj = this.diagram.findObjectAt(this.diagram.lastInput.documentPoint);
if (obj && obj.part) {
console.log('Double-clicked on:', obj.part.data);
// Custom double-click logic here
}
this.stopTool();
}
}
// Add custom tool to diagram
diagram.toolManager.mouseDownTools.insertAt(0, new CustomClickTool());// Check tool states
if (diagram.toolManager.draggingTool.isActive) {
console.log('Currently dragging');
}
if (diagram.toolManager.textEditingTool.state === go.TextEditingState.Editing) {
console.log('Currently editing text');
}
// Programmatically activate tools
if (diagram.toolManager.linkingTool.canStart()) {
diagram.toolManager.linkingTool.doStart();
}// Customize dragging behavior
diagram.toolManager.draggingTool.isGridSnapEnabled = true;
diagram.toolManager.draggingTool.gridSnapCellSize = new go.Size(20, 20);
diagram.toolManager.draggingTool.copiesEffectively = true;
// Custom linking validation
diagram.toolManager.linkingTool.isLinkValid = function(fromnode, fromport, tonode, toport) {
// Prevent linking to same node
if (fromnode === tonode) return false;
// Only allow certain node types to connect
const fromType = fromnode.data.type;
const toType = tonode.data.type;
return (fromType === 'source' && toType === 'process') ||
(fromType === 'process' && toType === 'sink');
};
// Custom resize constraints
diagram.toolManager.resizingTool.minSize = new go.Size(20, 20);
diagram.toolManager.resizingTool.maxSize = new go.Size(200, 200);