Events

Events allow the Paper to report on actions happening internally, and give you the opportunity to react to and manipulate these actions. Events allow you to structure listeners to manipulate actions and unlock the full potential of Psiagram.

Examples

Before we dive into the methods and properties of an event, let's touch on some basic examples of what events can achieve.

Simple

First, let's look at a very simple example of a listener for an event.

function addNodeAnnouncement(evt) {
  console.log('Added Node. ID: ' + evt.target.id);
}

myPaper.addListener(PaperEventType.AddNode, addNodeAnnouncement);

In the example above, every time a Node is added to the Paper, it will log the ID of the added Node.

Cancelling events

However, where events really shine is by giving control over the event to any listeners. Here's an example of a listener that stops an event from continuing.

function noTallNodes(evt) {
  const height = evt.target.instance.height;
  if (height > 150) {
    evt.preventDefault();
    evt.stopPropagation();
    console.error('Can not add Node with height > 150px');
  }
}

myPaper.addListener(PaperEventType.AddNode, noTallNodes);

This listener checks the height, and if it exceeds 150px, it calls preventDefault to prevent the default action of the event (adding the Node to Paper) and stopPropagation to prevent the event from propagating to any additional listener functions. We will discuss the various methods and properties of an event below.

Transforming data

One final example, and we will get into the internals of an event. In this example, the listener actually updates the data that will be used by the default action of the event once it has gone through every listener.

function addNodeToCorner(evt) {
  evt.data.x = 0;
  evt.data.y = 0;
}

myPaper.addListener(PaperEventType.AddNode, addNodeToCorner);

For an AddNode event, the default action will be to add the Node to the paper at the coordinates specified. For a case like this, you don't want to prevent the default, nor do you want to stop the event from propagating (in case additional listeners do important actions based on the event). What you do want is to update the data that will be used by the default action once it is fired (once every listener has been called).

For a more complex example, check out the _updateEdgeRoute method within the ManhattanRouting plugin repository (this is an example of transforming data). Now we will take a deeper look into events.

Event API

Each event has a set of properties and methods that allow you to view and manipulate the lifecycle of an event. When used together, these allow for complete control over the actions within Psiagram.

eventType

// To get the event type from an event:
const eventType = evt.eventType;

The event type defines what type of action the event represents. The events that are fired by Paper will have a type defined within the enum PaperEventType. For more information on each of the events fired by Paper, go to the Paper Events section.

enum PaperEventType {
  AddNode = 'add-node',
  MoveNode = 'move-node',
  RemoveNode = 'remove-node',
  AddEdge = 'add-edge',
  MoveEdge = 'move-edge',
  RemoveEdge = 'remove-edge',
  PaperInit = 'paper-init',
  UpdateActiveItem = 'update-active-item',
}

For advanced users: Event types can be any string. If you are writing a plugin or wish to fire a custom event, you can use the advanced Paper method _fireEvent.

target

// To get the target of an event:
const target = evt.target;

The target is whatever the event is acting on. This could simply return undefined in the case of PaperInit, or it could be a stored Node in the case of MoveNode.

data

// To get the data of an event:
const data = evt.data;

The data object contains any values that will be used by the default action once every listener for an event has been called. The default action is called with the data object. Therefore, these can be manipulated to change the result of the default action, as seen in the transforming data example. The data object is different for every action fired. Check the Paper events below to see the specific data each event is fired with.

paper

// To get a reference to the paper that fired the event:
const paper = evt.paper;

This provides a reference to the Paper that fired the event. This can be useful for calling any Paper methods in response to the event.

defaultAction()

// To fire the default action:
evt.defaultAction();

Once all listeners for a particular event have been called, the Paper will call the event's default action with the data object. However, you may wish to call it early using the defaultAction method. This method can only be called once (imagine the issues it would cause if AddNode's default action fired twice). If it is called early using this method, it will not be called again once all event listeners have been called. As such, in many cases this should be used with stopPropagation, since there may be no need for any further listeners to be called once the default action is fired.

preventDefault()

// To prevent the default action from firing:
evt.preventDefault();

Prevent default can be called on the event in order to prevent the default action from being called after every listener is called. This could be used in a validation listener to prevent illegal operations within Paper. In many cases this should be used with stopPropagation, since there may be no need for any further listeners to be called once the default action is cancelled.

stopPropagation()

// To stop Paper from calling any other listeners:
evt.stopPropagation();

Calling this method will prevent any further listeners from being called. If you call this method alone, then once the listener returns the Paper will immediately fire the default action. However, it is usually used in combination with defaultAction or preventDefault to avoid unnecessary calls to other listeners once the default action has been preemptively fired or cancelled.

Paper Events

There are various events that are fired by Paper whenever an action occurs. Each event has a specific target, data object and default action.

AddNode

Target

IPaperStoredNode

Data

{
  x: number;
  y: number;
}

Default action

Add the stored Node at the x and y coordinates.

Move Node

Target

IPaperStoredNode

Data

{
  coords: ICoordinates;
  oldCoords: ICoordinates;
}

Default action

Move the stored Node from oldCoords to coords.

RemoveNode

Target

IPaperStoredNode

Data

undefined

Default action

Remove the target Node.

AddEdge

Target

IPaperStoredEdge

Data

undefined

Default action

Add the Edge to the Paper.

MoveEdge

Target

IPaperStoredEdge

Data

{
  source: {
    node: IPaperStoredNode | null;
    midpoint: ICoordinates | null;
    point: ICoordinates | null;
  };
  target: {
    node: IPaperStoredNode | null;
    midpoint: ICoordinates | null;
    point: ICoordinates | null;
  };
  coords: ICoordinates[];
}

Default action

Move the Edge on the paper. If the source/target is a Node, then node and midpoint will be provided and point will be null within data. If it is a coordinate point, then node and midpoint will be null, and point will be provided within data. The default action is to trim the Edge at the end of either endpoint Node (if there is one), and then connect all of the coordinates.

RemoveEdge

Target

IPaperStoredEdge

Data

undefined

Default action

Remove the target Edge.

PaperInit

Target

undefined

Data

undefined

Default action

No default action. This is only fired once on the last line of the Paper constructor function. It ensures that any listeners know that any internal properties of Paper have been initialized and it is safe to use the Paper methods.

UpdateActiveItem

Target

IActiveItem | null

Data

{
  activeItem: IActiveItem | null;
  oldActiveItem: IActiveItem | null;
}

Default action

Update the active item from oldActiveItem to activeItem. The old item could be null if there was none and an item is becoming active, or the current could be null if there is no current active item.

Last updated