LATEST NEWS

‣ Rappid 2.1 released and packed with new features.
‣ Check out our other product AppMixer!

Rappid

Rappid is a powerful and modern toolkit for building visual tools of various kinds. This documentation gives you an overview and many examples on how you can use the plugins of the Rappid toolkit to build groundbreaking applications in a fraction of a time.

If you're looking for a documentation to the core JointJS library, go to the JointJS API reference page.

For more information about the Rappid toolkit, licensing and online purchase, see the About Rappid page.

Browse through the menu on the left to see the documentation to the Rappid plugins.

The Rappid toolkit is split into modules. Every plugin has a module identifier in its name. This helps you in orientating around the framework. Here is the list of modules with a short description:

Installation

Include rappid.min.css and rappid.min.js files to your HTML together with all the Rappid dependencies (jQuery, Lodash and Backbone).

<link rel="stylesheet" type="text/css" href="dist/rappid.min.css">
<script src="lib/jquery/jquery.min.js"></script>
<script src="lib/lodash/dist/lodash.min.js"></script>
<script src="lib/backbone/backbone-min.js"></script>
<script src="dist/rappid.min.js"></script>

It is highly recommended to use the exact same versions of all the dependencies as they are included in the package.

Note that each component documentation page has an Installation section. You do not need to follow the instructions from the section if you installed Rappid as described above (with rappid.min.js, rappid.min.css and all the dependencies). The Installation section of the components is only useful if you do not want to include the whole framework but only some of its components. In this case, you have to include all the dependencies, the JointJS Core library and then only the components you want to use:

<link rel="stylesheet" type="text/css" href="dist/joint.css">
<script src="lib/jquery/jquery.min.js"></script>
<script src="lib/lodash/dist/lodash.min.js"></script>
<script src="lib/backbone/backbone-min.js"></script>
<script src="dist/joint.js"></script>

// And now, for example, only the ui.Stencil component:
<link href="dist/joint.ui.stencil.css" rel="stylesheet" type="text/css">
<script src="dist/joint.ui.stencil.js"></script>
Rappid toolkit Rappid toolkit Rappid toolkit: Charts plugin Rappid toolkit: BPMN plugin Rappid toolkit: Pie charts Rappid toolkit: Donut charts Rappid toolkit: Knobs Rappid toolkit: BPMN Editor Rappid toolkit: Area charts

alg.Dijkstra

alg.Dijkstra is an implementation of the Dijkstra's shortest path algorithm. It can efficiently find the shortest path in both directed and undirected graphs. This implementation uses a binary heap based priority queue. The time complexity of this algorithm is O(|E| + |V| log |V|), where |E| is the number of edges in the graph and |V| is the number of nodes.

Install

Include both joint.alg.dijkstra.js and its priority queue dependency files into your HTML:

<script src="joint.alg.priorityQueue.js"></script>
<script src="joint.alg.dijkstra.js"></script>

Usage

Even though alg.Dijkstra is listed as a separate plugin, you usually don't use it directly. Instead, you can use the dia.GraphUtils plugin which extends the joint.dia.Graph with a convenience method for finding the shortest path between two nodes shortestPath(source, target [, opt]). However, it could be useful to use alg.Dijkstra directly in some situations. The reason is that alg.Dijkstra calculates not only the shortest path between two nodes but the shortest path between a node and all the other nodes in the graph.

joint.alg.Dijkstra is a function that takes a graph represented as an adjacency list, a source node and optionally a weight function. The adjacency list is an object where a key is a node ID and value is an array or IDs of the neighbouring nodes of that node. If you want to use alg.Dijkstra directly on a JointJS graph, you first have to convert the graph into the adjacency list. (This is what the shortestPath() method from the alg.GraphUtils plugin does for you internally.) weight function is a function that takes two nodes and returns a distance between them. How you define distance between two nodes is completely on you. By default, the distance is always 1. Only keep in mind that the Dijkstra's algorithm requires the weight to be a positive integer.

The function returns a special object that encodes the shortest paths between the node provided and all the other nodes in the graph.

var graph = {
    a: ['b', 'c'],
    b: ['d', 'e'],
    c: ['f', 'g'],
    f: ['b'],
    e: ['c'],
    h: ['f', 'g'],
    i: ['h', 'a', 'd', 'g'],
    j: ['a']
};

var previous = joint.alg.Dijkstra(graph, 'a');
// { b: "a", c: "a", e: "b", f: "c" }

The above example shows the result of the joint.alg.Dijkstra function. This special object allows us to get the shortest path to all the nodes in the graph starting at node 'a'. For example, the shortest path to the node 'f' is a path ['a', 'c', 'f']. How did we get there? You simply start from your target node and keep asking the returned object node by node till you reach your source node. In other words, we start by previous['f'] which gives us 'c' Then we ask for previous['c'] which gives us 'a' and we're done since we reached our source node.

API

joint.alg.Dijkstra(adjacencyList, source [, weight]) Find the shortest path between the node source and all the other nodes in the graph represented as adjacencyList. See the Usage section for more info. weight can optionally contain a function that takes two nodes and returns the distance between them. This function defaults to: function(u, v) { return 1; }

alg.PriorityQueue

alg.PriorityQueue is an implementation of the Priority Queue abstract data type. It is like a normal stack or queue, but where each item has assigned a priority (a number). Items with higher priority are served before items with lower priority. This implementation uses binary heap as an internal representation of the queue. The time complexity of all the methods is as follows:

  • create: O(n)
  • insert: O(log n)
  • peek: O(1)
  • peekPriority: O(1)
  • remove: O(log n)
  • isEmpty: O(1)

alg.PriorityQueue is used internally by the alg.Dijkstra algorithm for finding the shortest path in a graph. It is however useful on its own, that's why it is listed as a separate plugin.

Install

Include joint.alg.priorityQueue.js file into your HTML:

<script src="joint.alg.priorityQueue.js"></script>

Usage

The usage of alg.PriorityQueue is pretty simple. You just have to create an object of the joint.alg.PriorityQueue type. Then you can insert, remove or retrieve elements from the queue similarly as you would do with a normal JavaScript array.

var q = new joint.alg.PriorityQueue;    
q.insert(1, 'one');
q.insert(3, 'three');
q.insert(2, 'two');

q.peek()  // the first value is 'one'
q.peekPriority()  // the first priority is 1
q.remove() // the first value was 'one'
q.remove() // the second value was 'two'
q.remove() // the third value was 'three'
q.isEmpty() // true

Moreover, this implementation of the priority queue allows you to update priorities of any item that you have inserted into the queue. This is also known as the decreaseKey operation. For this to work, you have to give each item you insert into the queue a unique ID. This is because in JavaScript, objects do not have unique IDs by default. Therefore, there is no way the alg.PriorityQueue can know which item you'd like to update priority for. Here is an example of priority queue that uses the updatePriority() operation:

var q = new joint.alg.PriorityQueue;
q.insert(1, 'one', 'id1');
q.insert(3, 'three', 'id3');
q.insert(2, 'two', 'id2');

q.peek() // the first value is 'one'
q.updatePriority('id1', 5);
q.peek() // now the first value is 'two'
q.updatePriority('id1', 1);
q.peek() // the first value 'one' got again back to the top

API

joint.alg.PriorityQueue([opt]) Create a priority queue object. If opt.data array is passed, it must be an array of items of the form { priority: Number, value: Object }. In this case, the priority queue will be initialized with this array. It's like calling insert(priority, value) for each item of this array. opt.comparator can optionally be a function that will be used to compare two priorities. The signature of this function is function(a, b). The function should return a value less then 0 if priority a is lower than priority b, value equal to 0 if the priorities are the same and value bigger than 0 if priority a is higher than priority b. The comparator function defaults to: function(a, b) { return a - b }. This function effectively allows you to use any object as a priority in which case it is on you to tell the priority queue how to compare two priorities.
isEmpty() Return true if the priority queue is empty, false otherwise.
insert(priority, value [, id]) Insert a value with priority to the queue. Optionally pass a unique id of this item. Passing unique IDs for each item you insert allows you to use the updatePriority() operation. See the Usage section for details.
peek() Return the value of an item with the highest priority.
peekPriority() Return the highest priority in the queue.
remove() Return the value of an item with the highest priority and remove the item from the queue.
updatePriority(id, priority) Update priority of an item identified by a unique id. You can only use this operation if all the items you inserted to the queue had a unique ID assigned. See the Usage section for details.

com.Channel

The Channel plugin is a very powerful tool that brings real-time collaboration capability to your applications. The plugin synchronises JointJS graphs between clients and server and automatically resolves merge conflicts. Real-time collaboration is only one application of this plugin. Others include push updates, easy persistence, monitoring and more.

The Channel plugin uses Operational transformations to maintain consistency across concurrent updates. This means that even though there are different updates to the same JointJS graph that happened at the same time at different sites (across clients and even server), the Channel plugin makes sure the result, after applying those updates, is exactly the same at all sites while preserving intention.

The Channel plugin runs both in browsers and NodeJS environment and requires a NodeJS server running in order to synchronize clients. The procedure to get the channel plugin up and running is quite straightforward and is described in this document. However, if you, for any reason, do not want to maintain a NodeJS application on your backend, please let us know. Thanks to the cross-domain nature of the Channel plugin, we can run the NodeJS application for you which means that the only thing you have to do is to use the client-side part of the Channel plugin. This allows you to have real-time collaboration in your applications without any backend configuration!

The demo of this plugin can be seen here. If you open the same URL of the Rappid demo (including the hash part that it gave you) in a different browser window or send it to a friend, you should be able to collaborate on the same document in real-time.

Install

In a browser

Include joint.com.channel.js file into your HTML:

<script src="joint.com.channel.js"></script>

On a server

Make sure you have NodeJS installed. NodeJS installation already comes with NPM (Node Package Manager) that we'll use to install all the dependencies. Go to the Rappid package root directory (where the package.json file is located) and run:

npm install

This will install all the necessary dependencies needed by the server side Channel.

Usage

In a browser

In a browser, the only thing necessary is to instantiate the joint.com.Channel constructor function and pass it a url of the server-side channel and the graph we want to be synchronising:

var graph = new joint.dia.Graph;
var channel = new joint.com.Channel({ url: 'ws://localhost:4141', graph: graph });

Note we're using the ws protocol as the Channel plugin uses WebSockets for real-time communication between server and client. If you plan to take advantage of us maintaining the NodeJS server for you, you'll get a dedicated URL that you can use in your channel and you can skip the rest of the Usage section.

On a server

On a server, we use the exact same joint.com.Channel constructor function but this time we pass it a port we'd like the WebSocket server to be listening on:

var joint = require('./index');
var Channel = require('./plugins/com/Channel/joint.com.Channel').Channel;

var graph = new joint.dia.Graph;
var channel = new Channel({ port: 4141, graph: graph });

In this example, we assume the server side script is located in the root directory of the Rappid package so that the paths used in the require()s are valid.

Rooms

The problem with the previous approach is that if you have more clients that should be divided into groups (rooms) each room sharing its own JointJS graph, you'd have to run as many servers as you have rooms each on a different port. This limits the number of rooms you can serve by the number of ports that your OS allows you to allocate. In general, this is not an ideal approach. Instead, we'd rather have just one server running that is able to maintain more graphs and route the communication between clients based on the room they're in. This is exactly what joint.com.ChannelHub does.

ChannelHub runs a WebSocket server and handles client connections. When a client wants to get connected, ChannelHub decides to which Channel it should route the requests to. In this case, the only server running is the ChannelHub. The server side Channels don't create their servers internally. Your server side script changes to something like this:

var channels = {};
var channelHub = new ChannelHub({ port: 4141 });
channelHub.route(function(req, callback) {

    var query = JSON.parse(req.query.query);
    var channel = channels[query.room] || null;

    if (!channel) {
       channel = channels[query.room] = new joint.com.Channel({ graph: new joint.dia.Graph });
    }

    // If an error occurred, call the callback function with the error as the first argument.
    // callback(new Error('Some error has occurred.'));

    // Otherwise, call the callback function with the channel as the second argument.
    callback(null, channel);
});

Notice how we instantiated the Channel. In this case, we don't pass neither a url nor a port. This defines a Channel that is not a client (no url) and it also does not create a WebSocket server internally (no port). Instead, it is the ChannelHub that handles requests and decides to which Channel it should route messages to.

Also, your client side initialization of Channel changes a little bit in order to enable rooms:

var channel = new joint.com.Channel({ url: 'ws://localhost:4141', graph: graph, query: { room: 'A' } });

We pass the query object that contains some data that help the ChannelHub decide to which channel to route the messages to. In this case, we use room: 'A' and on the server, we saw query.room which we base our decision on. This data can be arbitrary. Feel free to use any kind of decision making mechanism.

Configuration

Channel

  • url - URL of the server-side channel to which we want the client to be connected to. If this parameter is passed, the channel is considered to be a client. (Note that you can have a client running in NodeJS too.)
  • port - port number of the channel server. If this parameter is passed, the channel is considered to be a server.
  • reconnectInterval - if a client looses a connection to the server, it tries to reconnect every reconnectInterval milliseconds. The default is 10000ms = 10s. This settings makes sense only for clients.
  • healthCheckInterval - Interval in milliseconds in which a health check is performed on all the sites of a server. Each health check decreases the ttl value of a site if the site socket is disconnected. If the site socket gets connected again, its ttl returns to its original value. The default is a health check performed every 1 minute. Considering the default ttl is 60, this means that if every minute of an hour a site didn't respond (its socket was closed) then such a site is considered dead (not responding anymore) and therefore removed from the channel register. The reason for health checks is the following. Consider you have ChannelHub running your server. This ChannelHub contains couple of Channels each representing a different room. Consider there is 3 clients connected to one room simultaneously editing the same graph. One of the clients doesn't want to work anymore and shuts down his browser or closes the tab with your application. Now the channel of the room still keeps this client in its register as the client might reconnect anytime (the user closed his browser, went for a coffee and came back in ten minutes.) The healthCheckInterval and ttl allows you to configure the maximum waiting time in which the channel should still keep track of the client. This setting makes sense only for server.
  • ttl - time-to-live of a site. Explained above. The actual time to live of a site in milliseconds is ttl * healthCheckInterval. The default ttl is 60. This setting makes sense only for server.
  • query - An object that is send with the initial handshake of a client with a server. This can be an arbitrary JSON object. The query object is especially useful when working with rooms (see above for documentation on using the Channel plugin with rooms). This setting makes sense only for clients.
  • debugLevel - the verbosity of debugging logs. The default 0 meaning no debug messages are printed out. Set to a higher number if you want to get some inside on how the Channel works.

ChannelHub

  • port - port number of the channelHub server.
  • route(req) - this is actually not an option that you pass to the ChannelHub constructor function but a method that you define directly on the channelHub object. The route(req) method receives a connect request from a client and must return a channel object that is supposed to further communicate with that client. The req parameter contains the request object. The req.query.query contains the query object that the client sent during handshake (see above the Channel configuration section). Note that req.query.query is a JSON string so you should convert it to an object with JSON.parse() before accessing its inner properties. Use this object to determine what channel to return.

dia.CommandManager

CommandManager keeps track of graph changes and allows you to travel the history of those changes back and forth. There is no limitation put into the number of levels one can undo/redo.

Installation

Include joint.dia.command.js to your HTML:

<script src="joint.dia.command.js"></script>

Creating CommandManager

var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({ el: $('#paper'), width: 500, height: 500, model: graph });

var commandManager = new joint.dia.CommandManager({ graph: graph });

$('#undo-button').click(function() { commandManager.undo(); });
$('#redo-button').click(function() { commandManager.redo(); });

How does CommandManager work?

CommandManager listens to graph changes and when any cell's attribute is changed it stores the difference (command). CommandManager stores every change made on graph's cells into undoStack and every reverted change into redoStack. It allows you to revert the changes stored in those stacks.

CommandManager API

constructor

The joint.dia.CommandManager constructor takes up to three parameters.

graph

Is the graph the CommandManager listens to.

cmdBeforeAdd

A function evaluated before any command is added. If the function returns false, the command does not get stored. This way you can control which commands do not get registered for undo/redo.

var commandManager = new joint.dia.CommandManager({
    graph: graph,
    cmdBeforeAdd: function(cmdName, cell, graph, options) {
        options = options || {};
        return !options.ignoreCommandManager;
    }
});

// ...

// Note that the last argument to set() is an options object that gets passed to the cmdBeforeAdd() function.
element.set({ foo: 'bar' }, { ignoreCommandManager: true });

cmdNameRegex

A regular expression specifying what cell's attributes the CommandManager listens to. Default regex is /^(?:add|remove|change:\w+)$/.

revertOptionsList

An array of options property names that are meant to be passed in undo actions. It defaults to ['propertyPath'].

commandManager = new joint.dia.CommandManager({
    revertOptionsList: ['myOptionAttribute1']
})
element.set('attribute', 'value', { myOptionAttribute1: 5, myOptionAttribute2: 6 });
commandManager.undo(); // -> calls element.set('attribute', 'prevValue', { myOptionAttribute1: 5 });

Alternatively the revertOptionList can be defined as a function.

revertOptionsList: function(attrValue, attrName) {
    return attrName !== 'doNotPassMe'; /* pass over everything except `doNotPassMe` attribute */
}

applyOptionsList

An array of options property names that are meant to be passed in redo actions. It defaults to ['propertyPath'].

commandManager = new joint.dia.CommandManager({
    applyOptionsList: ['myOptionAttribute1']
})
element.set('attribute', 'value', { myOptionAttribute1: 5, myOptionAttribute2: 6 });
commandManager.undo();
commandManager.redo(); // -> calls element.set('attribute', 'value', { myOptionAttribute1: 5 });

Alternatively the applyOptionList can be defined as a function.

applyOptionsList: function(attrValue, attrName) {
    return attrName !== 'doNotPassMe'; /* pass over everything except `doNotPassMe` attribute */
}

undo

Function undo(opt) travels the history one command back. It accepts an option parameter (opt) that is applied when function makes changes to the graph. e.g commandManager.undo({ undoneBy: 'user123' })

redo

Function redo(opt) go back to whatever was previously undo()ed.

cancel

Function cancel(opt) same as undo() but does not store the undo-ed command to the redoStack. Canceled command therefore cannot be redo-ed.

hasUndo

Function hasUndo() true if there is something in the undoStack.

hasRedo

Function redoUndo() true if there is something in the redoStack

reset

Function reset() clears both undoStack and redoStack.

initBatchCommand

Function initBatchCommand() gives you the ability to gather multiple changes into a single command. These commands could be revert with single undo() call.

From the moment the function is called every change made on graph is not stored into the undoStack. Changes are temporarily kept in the CommandManager until storeBatchCommand() is called.

storeBatchCommand

Calling function storeBatchCommand() tells the CommandManager to store all changes temporarily kept in the undoStack. In order to store changes you have to call this function as many times as initBatchCommand() had been called.

dia.GraphUtils

dia.GraphUtils adds additional methods to the joint.dia.Graph. Currently, it adds only two methods: constructTree() and shortestpath().

Installation

Include joint.dia.graphUtils.js file to your HTML:

<script src="joint.dia.graphUtils.js"></script>

If you plan to use shortestPath() method, you're going to also need to include joint.alg.priorityQueue.js and joint.alg.dijkstra.js plugins:

<script src="joint.alg.priorityQueue.js"></script>
<script src="joint.alg.dijkstra.js"></script>

This will add more methods to the joint.dia.Graph object:

Methods

constructTree(parent, opt, parentElement)

Construct a tree from a JSON object (parent, i.e. the top level node). This method returns an array of cells (elements and links) that constitute the tree. The parent parameter must be a JSON of the form:

{
  name: 'my label', 
  children: [ { name: 'my label 2', children: [...] }, ...] 
}
	

opt.children is the property specifying the children array (default is 'children'). If opt.children is a function, it will be called with the current node as an argument and must return an array of its child nodes. opt.makeElement() is a function that is passed the current tree node and returns a JointJS element for it. opt.makeLink() is a function that is passed a parent and child nodes and returns a JointJS link for the edge. Example:

var cells = graph.constructTree(
    { 
        name: 'my top, 
        children: [ { name: 'my child 1' }, { name: 'my child 2' } ] 
    }, {
    makeElement: function(node) {
	    return new joint.shapes.basic.Rect({
		size: { width: 80, height: 30 },
		attrs: { text: { text: node.name } }
	    });
    },
    makeLink: function(parentElement, childElement) {
	    return new joint.dia.Link({
		source: { id: parentElement.id },
		target: { id: childElement.id }
	    });
    }
});
graph.addCells(cells);
	

This method is used in the JavaScript AST visualizer demo.

shortestPath(source, target [, opt]) Return an array of IDs of nodes on the shortest path between source and target. source and target can either be elements or IDs of elements. opt.weight is an optional function returning a distance between two nodes. It defaults to function(u, v) { return 1 }. If opt.directed is true, the algorithm will take link direction into account. The implementation uses the joint.alg.Dijkstra plugin internally. Please refer to the plugin documentation for more information.

dia.Validator

Validator runs a set of callbacks to determine if a command is valid. This is useful for checking if a certain action in your application does lead to an invalid state of the diagram (from your application specific perspective). You can not only cancel such command but e.g. also give the user a hint that this action is not allowed).

Installation

Include joint.dia.command.js and joint.dia.validator.js to your HTML:

<script src="joint.dia.command.js"></script>
<script src="joint.dia.vaidator.js"></script>

Creating Validator

var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({ el: $('#paper'), width: 500, height: 500, model: graph });

var commandManager = new joint.dia.CommandManager({ graph: graph });
var validator = new joint.dia.Validator({ commandManager: commandManager });
validator.validate('remove', 
    function(err, command, next) {
        if (command.data.type === 'basic.Rect') return next('Rectangles cannot be removed.');
        return next();
    }, 
    function(err, command, next) {
        if (err) console.log(err);
        return next(err);
    }
);

(Those that know the great ExpressJS application framework might have recognized a similar API to what ExpressJS provides for registering URL route handlers.)

Validator API

constructor

The joint.dia.Validator constructor takes two parameters.

commandManager

The CommandManager the validator listens to.

cancelInvalid

Determine whether to cancel an invalid command or not. If set to false, only the invalid event is triggered.

Default is true.

validate

Function validate(action [, callback]*) registers callbacks for a given action.

The validator listens on commands added to the CommandManager and runs this set of callbacks registered for the action. If the last callback returns an error the command is canceled (see joint.dia.CommandManager.cancel()). This behaviour can be suppressed by setting the cancelInvalid to false in options passed to the Validator constructor.

action

Action is the name of the event triggered on the graph (see List of triggered events). Multiple actions may be given separated by whitespaces.
validate("change:source change:target", function() { .. });

callback

callback(err, command, next)

The validation function. An arbitrary number of validation functions can be passed to validate(). Every callback has the following signature:

Where err is an error from the previous callback.

The command parameter is a record from the CommandManager stack. It holds information about an action in the graph.

See below for the structure of a command. This command says that a basic.Rect element has been moved by 50px down.

{
  "action" : "change:position"
  "data": {
    "id":"e0552cf2-fb05-4444-a9eb-3636a4589d64", // id of the cell it was manipulated with
    "type":"basic.Rect", // type of the cell

    // change:attribute events have always these two attributes filled
    "previous": { "position":{"x":50,"y":50} },
    "next": { "position": {"x":50,"y":100}},
  },
  "batch":true, // is command part of a batch command?
  "options":{} // Backbone options that are passed through the listeners when passed as the last argument to the Backbone Model set() method.
}

The add and remove actions have previous and next object empty. They hold all the cell attributes in the attributes object so that the CommandManager is able to reconstruct the whole cell if it has to. See below for an example of such a command structure:

{
  "action": "add",
  "data" : {
    "id" : "28de715b-62a7-4130-a729-1bcf7dbb1f2b",
    "type":"basic.Circle",

    //empty attributes
    "previous":{},
    "next":{},

    // all cells attributes
    "attributes": {
      "type":"basic.Circle",
      "size": {
        "width":50,
       "height":30
      },
     "position":{"x":1220,"y":680},
     "id":"28de715b-62a7-4130-a729-1bcf7dbb1f2b",
     "attrs" : {
       "circle" : {
         "width":50,
         "height":30
       }
     }
  "batch":true,
  "options" : {
    "add":true,
    "merge":false,
    "remove":false
  }
}

The next parameter is a function accepting one argument - an error passed to the next callback in a row.

Calling next() function means going to the next callback in the order passed to the validate() method. If a call to the next() function is omitted, the validation for the specified action stops.

format.GEXF

This plugins imports a graph represented as an XML string in the GEXF format and makes it a JointJS graph.

The plugin contains only one function joint.format.gexf.toCellsArray(xmlString, makeElement, makeLink), where xmlString is an XML string representing a graph in GEXF format, makeElement is a function that takes an object with id, width, height and label properties and returns a JointJS shape for this node. makeLink is a function that takes an object with source and target properties and returns a JointJS link for that edge.

Installation

Include joint.format.gexf.js file into your HTML:

<script src="joint.format.gexf.js"></script>

Usage

var cells = joint.format.gexf.toCellsArray(
    someGEXF_XML_String,
    function makeElement(attrs) {
        return new joint.shapes.basic.Rect({
           id: attrs.id,
           size: { width: attrs.width, height: attrs.height },
           attrs: { text: { text: attrs.label } }
        });
    },
    function makeLink(attrs) {
        return new joint.dia.Link({
            source: { id: attrs.source },
            target: { id: attrs.target }
        });
    }
);

graph.resetCells(cells);

format.Print

The Print plugin extends the joint.dia.Paper with the following method that, when called, prepares the paper for printing and initiates printing using the browser print wizard:

  • print([options]) - prints the paper using the browser print dialog box.

Where options is an object with the following possible values:

  • padding - the padding space on all sides of the paper area to be printed. Default is 5. The padding and also be an object with the following properties for finer grained control of the padding on each side: { paddingLeft: [number], paddingTop: [number], paddingRight: [number], paddingBottom: [number] }
  • detachBody - By default, the Print plugin prepares the paper for printing by detaching the entire body from the document leaving only the paper. This makes sure nothing else is printed except the paper itself. For most applications, this works well and there is nothing else you have to do. However, for some applications that might not be desirable. In such cases, you can set detachBody to false in order to disable the default behavior. If you do that, you should provide CSS styles that position the paper on the screen and hide elements that should not be printed. The printarea class name is added to the paper holder which you can use in your CSS to style the paper in such cases. See the example below:
@media print {

    .printarea {
        position: absolute;
        left: 0px;
        top: 0px;
    }

    .stencil, .inspector, .toolbar {
        display: none;
    }
}

Install

Include print.css and joint.format.print.js files into your HTML:

<link rel="stylesheet" type="text/css" href="print.css">
<script src="joint.format.print.js"></script>

Note that this plugin (if detachBody is set to true which it is by default) requires all the stylesheets on the page are in the <head> element. This is because when the body is detached from the document, stylesheets that were inside that body won't have any effect and so might be missing when the browser generates the resulting document for printing.

Usage

$('#btn').click(function() {
  paper.print()
});

If you'd like to add your custom text or logo to all the printed papers, you can use the following method in your CSS:

@media print {
    body:before {
       content: 'My Cool Company';
       top: -5mm;
    }
}

This adds the text My Cool Company to the top of the printed document. For adding a logo image, just set width, height and background-image properties on the :before pseudo-element.

format.Raster

This plugin adds new methods to the joint.dia.Paper object for exporting paper to PNG and JPEG raster formats on the client side:

toPNG(callback [, options]) Executes the callback function with a PNG data URI representing the content of the paper as the first parameter.
toJPEG(callback [, options]) Executes the callback function with a JPEG data URI string representing the content of the paper as the first parameter.
toDataURL(callback [, options]) The general version of two previous methods. Executes the callback function with a data URI string representing the content of the paper in specified MIME type as the first parameter.

The available options are:

width number Set the width of the image in pixels.
height number Set the height of the image in pixels.
size number Size multiplicator for generating raster images in a higher resolution. It expects a string in the form of [Number] + 'x' describing how many times the image width and height should be multipicated. It defaults to '1x'.
backgroundColor string The image background color.
padding number The space between the image border and the image content. It can also be an object with left, top, right and bottom properties
type string The standard MIME type for the image format to return (only applies to toDataURL()).
quality number The quality level of a JPEG image compression in the range of 0.0 to 1.0 (only applies to toJPEG()).

Installation

Include joint.format.svg.js and joint.format.raster.js file into your HTML:

<script src="joint.format.raster.js"></script>

(Note that this plugin requires the SVG Export.)

To get the plugin to work across all modern browsers include also canvg library and its dependencies. All these files are part of the Rappid toolkit, in the Raster/lib directory:

<script src="format/Raster/lib/rgbcolor.js"></script>
<script src="format/Raster/lib/StackBlur.js"></script>
<script src="format/Raster/lib/canvg.js"></script>

Usage

paper.toPNG(function(imageData) { sendToServer(imageData); });

paper.toJPEG(function(imageData) { offerForDownload(imageData); }, {
    width: 640,
    height: 320,
    quality: 0.7
});

format.SVG

This plugins adds two new methods to the joint.dia.Paper object:

  • toSVG(callback[, opt]) - Converts the content of the paper to an SVG string and calls the callback with the SVG string as the first parameter.
  • openAsSVG() - Opens a new window with SVG representing the content of the paper.

Installation

Include joint.format.svg.js file into your HTML:

<script src="joint.format.svg.js"></script>

Usage

paper.toSVG(function(svgString) {
    sendToServer(svgString);
    offerForDownload(svgString);
});

Configuration

The toSVG(callback[, opt]) method takes an option object opt with the following parameters:

preserveDimensions boolean By default, the resulting SVG document has set width and height to 100%. If you'd like to have the dimensions to be set to the actual content width and height, set preserveDimensions to true.
area g.Rect An area, that the resulting SVG should display. The value is an object, which contains x,y,width and height, describing the origin and the size of a rectangular area on the paper ignoring paper transformations (as no scale, translate or rotate is applied). It defaults to the paper content bounding box - paper.getContentBBox().
convertImagesToDataUris boolean Converts all contained images into Data URI format. It defaults to false.

layout.ForceDirected

ForceDirected plugin implements automatic layouts for graphs using a force-directed approach. This is useful for, usually larger, undirected graphs.

Installation

Include the joint.layout.ForceDirected.js file into your HTML:

<script src="joint.layout.ForceDirected.js"></script>

Usage

ForceDirected layout plugin auto-layouts graphs based on three forces: repulsive force (forces nodes to move out of each other), attractive force (tries to get nodes together like a spring) and a gravity force (tendency of the nodes to move to a certain point).

The ForceDirected constructor accepts a couple of parameters for configuring the layout. The gravityCenter parameter is the point the nodes tend to move to. This is usually set to the center of the area where the nodes are to be laid out. charge parameter affects the repulsive force. Bigger the parameter is, bigger the repulsive force. linkDistance and linkStrength both affect the attractive force (you can think of it as parameters of a spring that tights together two nodes). Bigger the linkStrength, bigger the attractive force between two connected nodes. On the other hand, smaller the linkDistance, shorter links you allow and so the attractive force is bigger between two connected nodes.

There is no one-fits-all set of parameters that would give great result for all possible graphs. Therefore, it is suggested that you to play with the parameters, try to set different values so that it gives a good result for your use case.

var graphLayout = new joint.layout.ForceDirected({
    graph: graph,
    width: 600, height: 400,
    gravityCenter: { x: 300, y: 200 },
    charge: 180,
    linkDistance: 30
});

graphLayout.start();

_.each(_.range(100), function() { graphLayout.step(); });

Note: It is recommended to use the requestAnimationFrame for stepping the layout.

layout.GridLayout

GridLayout implements automatic layouts for graphs. This is useful in many scenarios, one of them being e.g. automatically positioning elements in Stencil.

Installation

Include the joint.layout.GridLayout.js file into your HTML:

<script src="joint.layout.GridLayout.js"></script>

Usage

GridLayout plugin exposes one function joint.layout.GridLayout.layout(graphOrElements, options). The first parameter graphOrElements is the joint.dia.Graph or an array of joint.dia.Elements we want to layout. The second parameter options is an object that contains various options for configuring the layout.

graph.addCells([
  new joint.shapes.basic.Rect({ size: { width: 80, height: 50 }}),
  new joint.shapes.basic.Rect({ size: { width: 50, height: 50 }}),
  new joint.shapes.basic.Circle({ size: { width: 80, height: 50 }}),
  new joint.shapes.basic.Circle({ size: { width: 50, height: 50 }})
]);

// Layout the entire graph
joint.layout.GridLayout.layout(graph, {
  columns: 2,
  columnWidth: 100,
  rowHeight: 70
});

// Layout the circles with minimal resulting `y` coordinate equals 100.
var circles = graph.getElements().filter(function(el) {
    return el instanceof joint.shapes.basic.Circle;
});
joint.layout.GridLayout.layout(circles, {
    columns: 2,
    marginY: 100
});

Here are the options for configuring the GridLayout:

  • columns - Number of columns. It defaults to 1.
  • columnWidth - Width of a column. It defaults to a width of the widest element.
  • rowHeight - Height of a row. It defaults to a height of the highest element.
  • dx - Shifts the elements horizontally by a given amount. It defaults to 0.
  • dy - Shifts the elements vertically by a given amount. It defaults to 0.
  • centre - Positions the elements in the centre of a grid cell. It defaults to true.
  • resizeToFit - Resizes the elements to fit a grid cell, preserving the aspect ratio (default: false)
  • marginX - Sets the origin (x coordinate) of the most top-left element. It defaults to 0.
  • marginY - Sets the origin (y coordinate) of the most top-left element. It defaults to 0.

layout.TreeLayout

layout.TreeLayout is a layout algorithm for tree-like graphs. It's great for displaying org charts, mind maps, class hierarchies and other tree structures. The layout.TreeLayout is strong in combination with the ui.TreeLayoutView which implements drag&drop functionality useful when editing a tree structure.

Installation

Include joint.layout.treeLayout.js file to your HTML:

<script src="joint.layout.treeLayout.js"></script>

Usage

Create an object of layout.TreeLayout type passing your graph (object of type joint.dia.Graph) as a parameter. Then you can just call the layout() method and the graph will be automatically laid out. It's important to note that the position of the root element of the tree will stay the same as it was before the layout. This allows you to move the tree to a position you want or have your tree be laid out around the root.

var graphLayout = new joint.layout.TreeLayout({
    graph: graph,
    parentGap: 20,
    siblingGap: 20
});

// Root element position stays the same after the layout.
// In this demo, we assume the root is the first element.
var root = graph.getElements()[0].position(200, 200);

graphLayout.layout();

Configuration

The following table lists options that you can pass to the layout.TreeLayout constructor function:

graph The JointJS graph object on which you want to perform the layout.
parentGap The distance between a parent element and its children. Defaults to 20.
siblingGap The distance between siblings. Defaults to 20.
firstChildGap The distance between the first sibling and its parent. It's applicable only for diagonal layouts ('TL', 'TR','BL', 'BR'). It defaults to 20.
direction The default direction of the layout. It can be set to one of the following values: 'L', 'R', 'T', 'B' (for left-to-right, right-to-left, top-to-bottom and bottom-to-top layouts) or diagonal variants 'TL', 'TR','BL', 'BR' (for top-left, top-right, bottom-left and bottom-right). It defaults to 'R'.

The direction defines how an element connects its parent. For instance element.set('direction', 'R') will appear on the right side to the parent element. Note that, this option is only used for nodes that do not have a direction property explicitelly set.

filter(children, parent, opt)

An arbitrary element or sub-tree can be skipped by the layout. By providing a filter function, one can control, which elements will be laid out and which not.

The filter function returns an array of elements to be laid out and runs for every single parent element in the graph.

Parameters:
  • children is an array of siblings
  • parent is the parent of the children or null, when children are roots
  • opt is the option object passed to layout() method when calling the layout.

// e.g. a collapse branch is not visible in the paper but resides in the graph.
function(children) {
    return children.filter(function(child) {
        return child.get('visible');
    });
}

No element is filtered out by default.

updatePosition(element, position, opt) A function responsible for setting the resulting position on the elements. It calls element.set('position', position, opt); by default. It can be used for positioning the elements in an animated manner. For instance:
element.transition('position', position, {
    delay: 300,
    valueFunction: joint.util.interpolate.object,
});
updateVertices(link, vertices, opt) A function responsible for setting the resulting vertices on the links. It calls link.set('vertices', vertices, opt); by default. If the option is defined as null no vertices will be set by the layout.
attributeNames An object that maps element attributes accepted by the layout to user defined attribute names. Useful for resolving conflicts when an attribute is already in use by the application logic. e.g Setting { siblingRank: 'index' } will make the layout look for the index attribute instead of siblingRank when trying to figure out the order of siblings.

The tree layout also reads some element properties allowing for a fine control of the layout engine. These are:

direction

The direction of the layout for this specific node. Note the tree layout can handle even mixed layout directions on a per-node basis.

See the globally defined direction in the TreeLayout constructor function configuration.

siblingRank The index of the element among its siblings.
offset The additinal distance from the parent (the real distance is a sum of parentGap - defined globally in the TreeLayout constructor function configuration. - and the offset).
margin Extends the area occupied by the element on both sibling sides.
element.set({
    direction: 'R',
    size: { width: 100, height: 100 },
    margin: 20
});
// as the `element` siblings are located above and under the element,
// it will in fact occupied an area of { width: 100, height: 140 }
prevSiblingGap The additional gap next to the element sub-tree on the side of the previous sibling. The previous sibling is defined as the sibling with the closest smaller siblingRank.
nextSiblingGap The additional gap next to the element sub-tree on the side of the next sibling. The next sibling is defined as the sibling with the closest larger siblingRank.

API

layout([opt])

Layout the graph in a tree. It iterates over all graph sources (elements without a parent) and layout them as separate trees.

The option object opt, which will be passed down to all operations on the graph made by the layout call. e.g. treeLayout.layout({ myFlag: true }) will internally set an element position as element.position(x, y, { myFlag: true });.

shapes.bpmn

The BPMN plugin provides you with set of Business Process Model and Notation 2.0 shapes. They are designed to be easily configurable with the ui.Inspector plugin.

The plugin consists of flow objects (Activities, Events, Gateways), connection objects (Flows), swim lanes (Pools) and artifacts (DataObjects, Groups, Annotation). It also covers models introduced in version 2.0 as Conversations, Choreographies and Messages.

Install

Include joint.shapes.bpmn.js file to your HTML:

<script src="joint.shapes.bpmn.js"></script>

joint.shapes.bpmn.Activity

The Activity attributes:

activityType Changes the type of the shape.
'task' | 'transaction' | 'event-sub-process' | 'call-activity'
content A text inside the element (i.e 'My Content').
icon An icon name (list of icons).
subProcess Any truthy value makes the sub-process icon visible (sub-process handling).
attrs Allows to apply custom SVG attributes (fill, stroke, opacity, etc.)
'.body' (rect), '.label' (text)

Usage:

var activity = new joint.shapes.bpmn.Activity({
    attrs: {
        '.body': { fill: 'gold', stroke: 'black' },
        '.label': { text: 'My Activity' }
    },
    content: 'A content of the Activity',
    activityType: 'transaction'
});

joint.shapes.bpmn.Annotation

The Annotation attributes:

content A text inside the element (i.e 'My Content').
attrs Allows to apply custom SVG attributes (fill, stroke, opacity, etc.)
'.body' (rect), '.stroke' (path)

Usage:

var annotation = new joint.shapes.bpmn.Annotation({
    attrs: {
        '.body': { fill: 'gold' },
        '.stroke': { stroke: 'silver' }
    },
    content: 'A content of the Annotation'
});

joint.shapes.bpmn.Choreography

The Choreography attributes:

content A text inside the element (i.e 'My Content').
initiatingParticipant An index or name of the participant (case-sensitive). ( i.e 'participant 1' )
participants An array of participant names ( i.e ['participant 1','participant 2'] ).
subProcess Any truthy value makes the sub-process icon visible (sub-process handling).
attrs Allows to apply custom SVG attributes (fill, stroke, opacity, etc.)
'.body' (rect), '.label' (text), '.participant-rect' (rect), '.participant-label' (text)

Usage:

var choreography = new joint.shapes.bpmn.Choreography({
    attrs: {
        '.body': { fill: 'gold', stroke: 'black' },
        '.label': { fill: 'gray', text: 'My Choreography' },
        '.participant-rect': { fill: 'silver' }
    },
    participants: ['partic1', 'partic2', 'partic3'],
    initiatingParticipant: 2, // ='partic3',
    content: 'A content of the Choreography'
});

joint.shapes.bpmn.Conversation

The Conversation attributes:

conversationType Changes the type of the shape.
'conversation' | 'call-conversation'
attrs Allows to apply custom SVG attributes (fill, stroke, opacity, etc.)
'.body' (rect), '.label' (text)

Usage:

var conversation = new joint.shapes.bpmn.Conversation({
    attrs: {
        '.body': { fill: 'gold', stroke: 'black' },
        '.label': { fill: 'gray', text: 'My Conversation' }
    },
    conversationType: 'call-conversation'
});

joint.shapes.bpmn.DataObject

The Data Object attributes:

attrs Allows to apply custom SVG attributes (fill, stroke, opacity, etc.)
'.body' (rect), '.label' (text)

Usage:

var dataObject = new joint.shapes.bpmn.DataObject({
    attrs: {
        '.body': { fill: 'gold', stroke: 'black' },
        '.label': { fill: 'gray', text: 'My Data Object' }
    }
});

joint.shapes.bpmn.Event

The Event attributes:

eventType Changes the type of the shape.
'start' | 'end' | 'intermediate'
icon An icon name (list of icons).
attrs Allows to apply custom SVG attributes (fill, stroke, opacity, etc.)
'.body' (circle), '.label' (text)

Usage:

var event = new joint.shapes.bpmn.Event({
    attrs: {
        '.body': { fill: 'gold', stroke: 'black' },
        '.label': { fill: 'gray', text: 'My Event' }
    },
    icon: 'message',
    eventType: 'end'
});

joint.shapes.bpmn.Flow

The Flow as the only one inherits from joint.dia.Link.

The Annotation attributes:

flowType Changes the type of the link.
'default' | 'conditional' | 'normal' | 'association' | 'conversation' | 'message'

Usage:

var flow = new joint.shapes.bpmn.Flow({
    source: { id: task.id },
    target: { id: annotation.id },
    flowType: 'association'
});

joint.shapes.bpmn.Gateway

The Gateway attributes:

icon An icon name (list of icons).
attrs Allows to apply custom SVG attributes (fill, stroke, opacity, etc.)
'.body' (polygon), '.label' (text)

Usage:

var gateway = new joint.shapes.bpmn.Gateway({
    attrs: {
        '.body': { fill: 'gold', stroke: 'black' },
        '.label': { fill: 'gray', text: 'My Gateway' }
    },
    icon: 'cross'
});

joint.shapes.bpmn.Group

The Group attributes:

attrs Allows to apply custom SVG attributes (fill, stroke, opacity, etc.)
'.body', '.label', '.label-rect'

Usage:

var group = new joint.shapes.bpmn.Group({
    attrs: {
        '.label-rect': { fill: 'gold', stroke: 'black' },
        '.label': { fill: 'gray' }
    }
});

joint.shapes.bpmn.Message

The Message attributes:

attrs '.body' (polygon), '.label' (text)

Usage:

var message = new joint.shapes.bpmn.Message({
    attrs: {
        '.body': { fill:'gold', stroke: 'black' },
        '.label': { fill: 'gray' }
    }
});

joint.shapes.bpmn.Pool

The Pool attributes:

lanes An object consist of a label, headerWidth (optional, defaults to 20) and an array of sublanes (optional).
A sublane is an object consist of a label, a name (optional), headerWidth (optional) and an array of sublanes (optional).
attrs Allows to apply custom SVG attributes (fill, stroke, opacity, etc.)
'.body' (rect), '.header' (rect), '.lane-body' (rect), '.lane-header' (rect), '.blackbox-label' (text)

Usage:

var pool = new joint.shapes.bpmn.Pool({
    attrs: {
        '.header': { fill:'gold' },
        '.lane-header': { fill:'silver' },
        '.myClass .lane-body': { fill: 'goldenrod' } // customize a specific lane body color
    },
    lanes: {
        label: 'My Pool',
        sublanes: [
            {
                label: 'lane 1',
                name: 'myClass' // this lane has an unique css class and therefore can be targeted
                                // in attrs above
            },
            {
                label: 'lane 2',
                headerWidth: 40, // sets the header size for `lane2` to 40px
                sublanes: [] // it may consist of another sublanes
            }
        ]
    }
});

Each rendered lane DOM element has class name lane and attribute data-lane-path, that references corresponding lane configuration inside the model's lanes attribute. e.g. data-lane-path="lanes/sublanes/1" for the `lane 2` in the example above. It's useful when one needs to know, what lane a user just clicked on.

paper.on('element:pointerup', function(elementView, evt) {
    var pool = elementView.model;
    if (pool.get('type') === 'bpmn.Pool') {
       var lanePath = $(evt.target).closest('.lane').data('lane-path') || 'lanes';
       console.log(pool.prop(lanePath + '/label')); // log the lane label
    }
});

joint.shapes.bpmn.icons

The joint.shapes.bpmn.icons namespace includes several icons that can be used within the shapes.

none No image.
message
user
plus
cross
circle
service

Usage:

  gateway.set('icon', 'circle');
    var task = new joint.shapes.bpmn.Activity({ icon: 'user' });

Adding custom icons can be made by inserting them into the joint.shapes.bpmn.icons namespace.

  joint.shapes.bpmn.icons['myIcon'] = 'pathToImage'; // or data URI

    // later on in the code
    gateway.set('icon', 'myIcon');

Sub-process handling

The subProcess value is stored as a data attribute on the sub-process icon (SVG path) element. An interaction with the sub-process icon can be therefore caught and handled on the joint.dia.Paper. In the example bellow the model's subProcess value is shown after an user doubleclicks the icon.

conversation.set('subProcess', 'My sub-process ID');

paper.on('cell:pointerdblclick', function(cellView, evt) {

    var subProcess = $(evt.target).data('sub-process');

    if (subProcess) {
        alert(subProcess);
    }
});

// a doubleclick on conversation's sub-process icon will alert 'My sub-process ID'

shapes.chart

The Chart plugin provides you with a set of flexible, good looking and interactive charts that you can use in your applications. Note that from the JointJS/Rappid perspective, a chart is just another JointJS cell. This means that you can add it to your diagram, resize it, rotate it, clone it, connect it with other elements, serialize/deserialize it to/from JSON format or export it to SVG, PNG or JPEG. No other library can give you this flexibility!

Rappid toolkit: Area charts

Rappid toolkit: Charts plugin

Rappid toolkit: Pie charts

Rappid toolkit: Donut charts

Rappid toolkit: Knobs

Install

Include joint.shapes.chart.js file to your HTML:

<script src="joint.shapes.chart.js"></script>

Chart Types

The following is a list of currently supported chart types:

shapes.chart.knob

chart.Knob is a great component for displaying numbers in a fancy way. They can also be animated to better visualize changes in values.


Knob chart variations

Create a basic Knob

The code snippet below creates a basic Knob. The Knob contains only one value.

var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({ el: $('#paper'), width: 200, height: 200, model: graph });

var knob = new joint.shapes.chart.Knob({
    position: { x: 20, y: 20 },
    size: { width: 100, height: 100 },
    min: 0, max: 100, value: 80,
    fill: '#2c97de'
});
graph.addCell(knob);

Styling the Knob

To set color of the knob, simply pass fill attribute with color of your choice to the knob constructor. Moreover, as it is common in JointJS/Rappid, you can style any graphical element of the knob shape. For example, you might want to change the font family or other font properties of the legend. For this, you can customize the knob using the usual JointJS approach, via the attrs object or the attr() method. This means that by just knowing the structure of the SVG elements the knob is built from, you have full flexibility in styling your knob. Only SVG and CSS standards are your limits.

Let's have a look at some examples demonstrating different knob styles. For the full SVG structure of the knob, please see the section Chart SVG Structure in this document. Once you know the structure, it is easy to make CSS selectors referencing the SVG elements that you want to style.

In the example above, we showed how to pass styling in the attrs object when creating a new knob but you can also change attributes of an existing knob:

knob.attr('.slice .slice-fill/stroke', 'black')
knob.attr('.slice .slice-fill/stroke-width', 2)
knob.attr('.legend-slice text/font-size', 16)

Note that you can use not only fill but a whole range of other SVG attributes to style your knobs. Moreover, JointJS provides a facility to use gradients and filters. You can take advantage of them to make your charts even prettier:

var knob = new joint.shapes.chart.Knob({
    ...
    attrs: {
        '.data': {
            filter: { name: 'dropShadow', args: { dx: 0, dy: 0, blur: 3, color: 'black' } }
        }
    }
    ...
});

Multiple values

The Knob chart supports multiple series. The value, fill, min and max properties of the Knob constructor accept arrays. Simply pass more values into the arrays and the Knob chart will automatically render multiple series as in the following example:

var chart = new joint.shapes.chart.Knob({
    ...
    value: [30, 60, 90], 
    fill: ['#F2C500', '#4CC3D9', '#E94B35'], 
    min: 0, max: 100
});

Tooltips

Tooltips are a great way to show contextual information in your charts. The following demo shows how to use the ui.Tooltip plugin with combination with Knob chart:

IMPORTANT: For tooltips to work, you must have the ui.Tooltip plugin installed.

When setting up tooltips, we no longer work with the chart model. Instead, we request the paper for the view that represents the chart. This view triggers mousemove, and mouseout events that we use to hide and show our tooltip. The mousemove event handler gets passed two important arguments: slice and event. These two arguments mean the following:

  • slice - an object with useful information about the slice the event was triggered on. This object contains the following properties: sliceIndex, serieIndex, value, percentage, fill, offset, angle, startAngle, endAngle, outerRadius, innerRadius, and label. Most of the time, for tooltips, the important ones are value and label.
  • event - This is the DOM event object. For tooltips, the two important properties are clientX and clientY. We use these to render the tooltip at a particular position.

All these three arguments help us render our tooltip.

Animation

In general, you can animate any property of the Knob chart (change its value over a certain period of time). In the following demo, we will show how to animate some properties of the Knobs. Using the same technique, you can animate an arbitrary property.

knob.transition('value', 100, { duration: 1000, timingFunction: joint.util.timing.exponential });
knob.transition('pieHole', .4, { duration: 1000, timingFunction: joint.util.timing.exponential });

You can find more about JointJS transitions in the API reference.

Knob Options

This is the full list of options that you can pass to the Knob constructor function.

  • value - a number or an array of numbers (in case of multiple series) representing the value of the knob.
  • fill - a color or an array of colors (in case of multiple series) representing the color of the knob. If multiple series are used, the number of items in the value array and fill array should match. If they don't match, the first color from the fill array will be taken wherever a color on the index matching the current serie value is missing.
  • min - a minimum value of the knob. min together with max specify the range for the value. The same logic for multiple series as described above for fill applies.
  • max - a maximum value of the knob. The same logic for multiple series as described above for fill applies.
  • serieDefaults - an object with various properties specifying how series should be rendered. As Knob inherits from Pie chart, all of the properties described in Pie serie object applies here as well.
  • sliceDefaults - an object with various properties specifying how slices should be rendered. As Knob inherits from Pie chart, all of the properties described in Pie slice object applies here as well.

Knob API

chart.Knob

As chart.Knob inherits from chart.Pie all of the chart.Pie API methods apply here as well.

chart.KnobView

As chart.Knob inherits from chart.Pie all of the chart.PieView API methods apply here as well.

chart.KnobView events

As chart.Knob inherits from chart.Pie all of the chart.Pie events apply here as well.

Knob SVG structure

As chart.Knob inherits from chart.Pie, the SVG structure described in chart.Pie SVG structure applies here as well.

shapes.chart.matrix

chart.Matrix is great for displaying co-occurrences in a matrix.


Matrix chart

Create a Matrix Chart

The code snippet below creates a basic Matrix chart with three rows and three columns.

var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({ el: $('#paper'), width: 500, height: 500, model: graph });

var matrix = new joint.shapes.chart.Matrix({
    position: { x: 50, y: 50 },
    size: { width: 170, height: 170 },
    cells: [
        [{ fill: 'red' }, { fill: 'black' }, { fill: 'black' }],
        [{ fill: 'black' }, { fill: 'red', rx: 50, ry: 50 }, { fill: 'black' }],
        [{ fill: 'black' }, { fill: 'black' }, { fill: 'red' }]
    ],
    labels: {
        rows: [{ text: '1' }, { text: '2' }, { text: '3' }],
        columns: [{ text: 'A' }, { text: 'B' }, { text: 'C' }],
    }
});
graph.addCell(matrix);

Styling the Matrix

All the cells in the matrix are SVG rectangles. The objects in the cells array can contain any SVG attributes that will be used to style those rectangles. The most common one is fill which allows you to set the fill color of the rectangle. Another useful ones are radii rx and ry which can effectively turn the rectangles into circles if the radii specified are big enough (as we saw in the above example).

However, there is more we can do to make our matrices much prettier. For example, we can use opacity to make our cells semi-transparent, set stroke, style labels, grid lines or the background of the matrix.

Real-world example

In our previous examples, we were trying to show the basics of working with Matrix chart. However, the real power of Matrix charts is to show co-occurrences. The following chart is somewhat bigger and shows the relationships from the Les Miserables book by Victor Hugo. The demo also integrates the ui.Tooltip plugin to show how we can display tooltips when cells are hovered.

Conway's Game of Life example

The following example shows the great Conway's Game of Life cellular automaton.

Matrix Options

This is the full list of options that you can pass to the Matrix constructor function.

  • cells - an array of cells each being an object that can contain SVG attributes for the cell rectangle. If a cell is undefined, it is not rendered. If the matrix is sparse, setting empty cells to an undefined value significantly improves performance when rendering large matrices.
  • labels - an object containing rows and columns arrays. These arrays contain objects defining the text for the labels on the respective axis. The most important ones are text (the actual label), font-size, font-family and fill but you can use any other SVG attribute for styling the labels.

Matrix SVG structure

Knowing the SVG structure of the matrix allows you to further style the matrix via the attrs object or attr() method.

shapes.chart.pie

chart.Pie is a great chart type for displaying numerical proportions. Donut charts, multiple series, animation, changing look & feel of any part of the chart, drop shadows and other filters and gradients are all supported.

Create a basic Pie chart

The code snippet below creates a basic Pie chart. The pie chart contains only one serie and its legend is displayed by default.

var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({ el: $('#paper'), width: 500, height: 500, model: graph });

var chart = new joint.shapes.chart.Pie({
    position: { x: 50, y: 10 },
    size: { width: 100, height: 100 },
    series: [ { data: [
        { value: 40, label: 'Organic', fill: '#8bce5d' },
        { value: 20, label: 'Email', fill: '#53abdd' },
        { value: 20, label: 'Social', fill: '#c377b1' },
        { value: 20, label: 'Referral', fill: '#ffe891' }
    ]}]
});
graph.addCell(chart);

Styling Pie Slices

Normally, you can just pass a color of each slice in the fill property of the slice object as you can see in the example above. However, as it is common in JointJS/Rappid, you can style any graphical element of the chart shape. For example, you might want to change the font family or other font properties of the slice labels, or the legend. For this, you can customize the chart using the usual JointJS approach, via the attrs object or the attr() method. This means that by just knowing the structure of the SVG elements the chart is built from, you have full flexibility in styling your chart. Only SVG and CSS standards are your limits. Following the JointJS philosophy, we don't try to invent special parameters. Instead, we try to follow SVG and CSS standards as much as we can.

Let's have a look at one snippet of the SVG structure of the charts that is used to render a slice. For the full SVG structure of charts, please see the section Chart SVG Structure in this document. Once you know the structure, it is easy to make CSS selectors referencing the SVG elements that you want to style. We'll show an example of that below. the SVG structure of one serie is the following:

<g class="slice serie-[index of the serie] slice-[index of the slice]">
    <path class="slice-fill">
    <path class="slice-border">
    <text class="slice-inner-label">
</g>

Knowing that, we can, for example, change color and size of the text label of the first slice:

var chart = new joint.shapes.chart.Pie({
    ... /* the same as before */ ...
    attrs: {
        '.slice-0 .slice-inner-label': { fill: 'black', 'font-size': 20 }
    }
});

In the example above, we showed how to pass styling in the attrs object when creating a new chart but you can also change attributes of an existing chart:

chart.attr('.slice-0 .slice-inner-label/fill', 'blue')

Note that you can use not only fill but a whole range of other SVG attributes to style your charts. Moreover, JointJS provides a facility to use gradients and filters. You can take advantage of them to make your charts even prettier:

var chart = new joint.shapes.chart.Pie({
    ...
    series: [ { data: [
        { value: 40, label: 'Organic', fill: {
            type: 'linearGradient', stops: [
                { offset: '0%', color: '#b4f200' }, 
		{ offset: '80%', color: '#759d00' } 
	    ],
            attrs: { x1: '0%', y1: '0%', x2: '0%', y2: '100%' } 
	} },
        ...
    ]}],
    ...
});

Multiple series

The Pie chart supports multiple series. Simply add more series to the series array. This is useful for creating pie charts that, for example, compare data (from different sources, periods, ...).

var chart = new joint.shapes.chart.Pie({
    ...
    series: [
        { label: '2014', data: [
		{ value: 20.3, label: 'IE', fill: '#4CC3D9' },
		{ value: 18.3, label: 'Firefox', fill: '#F16745' },
		{ value: 34.2, label: 'Chrome', fill: '#7BC8A4' }
        ]},
        { label: '2013', data: [
		{ value: 27.5, label: 'IE', fill: '#4CC3D9' },
		{ value: 20, label: 'Firefox', fill: '#F16745' },
		{ value: 30, label: 'Chrome', fill: '#7BC8A4' }
        ]},
        ...
    ],
    ...
});

All series have automatically generated legends. This legend is interactive. Based on the effect you defined, the user can either hover or click on the labels in the legend and offset (or enlarge) the associated slices in the chart.

Exploding a Slice

Slices in the Pie chart can be separated from the rest (exploded) by setting the offset property of the slice object:

Donut Charts

The difference between Donut and Pie charts is very small, the only thing that differs is the hole inside the pie. While there is no hole for Pie chart, Donut charts have a hole that can be set arbitrarily. The pieHole property can either be a floating point number between 0 and 1, in which case the size of the hole is proportional to the radius of the pie chart. If the pieHole property is bigger than 1, it is considered to be an absolute size of the hole in pixels:

Tooltips

Tooltips are a great way to show contextual information in your charts. The following demo shows how to use the ui.Tooltip plugin with combination with Pie chart:

IMPORTANT: For tooltips to work, you must have the ui.Tooltip plugin installed.

When setting up tooltips, we no longer work with the chart model. Instead, we request the paper for the view that represents the chart. This view triggers mousemove, and mouseout events that we use to hide and show our tooltip. The mousemove event handler gets passed two important arguments: slice and event. These two arguments mean the following:

  • slice - an object with useful information about the slice the event was triggered on. This object contains the following properties: sliceIndex, serieIndex, value, percentage, fill, offset, angle, startAngle, endAngle, outerRadius, innerRadius, and label. Most of the time, for tooltips, the important ones are value and label.
  • event - This is the DOM event object. For tooltips, the two important properties are clientX and clientY. We use these to render the tooltip at a particular position.

Legend

The chart legend is auto-generated and contains labels and colors of all the data series. The legend is interactive by default and allows the user to highlight the slices displayed in the chart while hovering the legend items. This sections shows you how you can customize the legend and its position.

The legend is rendered as an SVG group element with the class legend. Inside this group, we have legend-items each containing an SVG circle and text elements. Please refer to the Chart SVG structure section for the full SVG structure of the chart. This is all you need to know to be able to style the legend. By default, the lenged is positioned on the top right corner of the pie chart.

The styling of the legend item text (name of the associated slice) and the legend item circle can be done the usual JointJS way, through standard SVG attributes as you can see below. Another useful properties for styling the legend items are legendLabelLineHeight, legendLabelMargin and labelLineHeight. The first two can be defined in the slice objects (or in the sliceDefaults for all slices) while the third one is specific to the legend item for the serie and therefore is defined in the serie object (or in serieDefaults).

// Positioning to the top (80px above the chart and centered).
chart3.attr('.legend/ref-y', -80);
chart3.attr('.legend/ref-x', .5);
chart3.attr('.legend/x-alignment', -.5);

// Styling of the legend text and circle.
chart3.prop('sliceDefaults/legendLabel', '{label} is {value}%');
chart3.prop('sliceDefaults/legendLabelLineHeight', 8);
chart3.prop('sliceDefaults/legendLabelMargin', 25);
chart3.attr('.legend-slice text/fill', '#336699');
chart3.attr('.legend-slice text/font-weight', 'bold');
chart3.attr('.legend-slice text/text-decoration', 'underline');
chart3.attr('.legend-slice text/font-size', 13);
chart3.attr('.legend-slice circle/r', 7);
chart3.attr('.legend-slice circle/stroke', 'black');
chart3.attr('.legend-slice circle/stroke-width', 1);

Animation

In general, you can animate any property of the Pie chart (change its value over a certain period of time). In the following demo, we will show how to animate some properties of the Pie chart. Using the same technique, you can animate an arbitrary property.

chart.transition('pieHole', .6, { duration: 700 });
chart.transition('series/0/data/0/offset', 50, { duration: 700 });
chart.transition('attrs/.slice-inner-label/font-size', 30, { duration: 700 });
chart.transition('series/0/data/0/fill', '#ff0000', {
    duration: 700,
    valueFunction: joint.util.interpolate.hexColor
});

You can find more about JointJS transitions in the API reference.

Effects

The effect applied when the user either clicks or hover a slice in the pie can be customized. There are two actions that trigger an effect (onClickEffect and onHoverEffect) and two types of effects: enlarge and offset. Try to interact (hover/click slices) with the pie chart in the demo below to see the different effects applied.

var chart = new joint.shapes.chart.Pie({
    ...
    sliceDefaults: {
        onClickEffect: { type: 'offset', offset: 20 },
        onHoverEffect: { type: 'enlarge', scale: 1.5 }
    }
});

Chart Options

This is the full list of the options that you can pass to the chart constructor function.

  • series - an array of series.
  • pieHole - the size of the hole in the center of the pie. If the number is in the 0..1 interval, it is considered to be proportional to the pie radius. If it is bigger than 1, it is considered to by the absolute radius in pixels.
  • serieDefaults - an object with various properties specifying how series should be rendered. All properties of this object are overruled by properties of the same names in the serie object.
  • sliceDefaults - an object with various properties specifying how slices should be rendered. All properties of this object are overruled by properties of the same names in the slice object.

Serie

These are items of the series array.

  • name - the name of the serie. This name is added as a CSS class to the serie SVG element. You can take advantage of this CSS class for further styling.
  • label - the serie label (used in the auto-generated legend)
  • data - an array of slices
  • startAngle - the angle in degrees the serie slices start to draw.
  • degree - the total degre of the serie in the pie.
  • showLegend - true if the legend for the serie should be shown, false otherwise.
  • labelLineHeight - the line height of the serie label in the auto-generated legend

When the above properties are defined in the serieDefaults options object, they will be applied to all the series unless overruled by properties in the serie object.

Slice

These are items of the data array of the serie object.

  • value - a number specifying the value for the slice data point.
  • label - the label for the slice. This lable is used in the legend.
  • fill - the fill color (or gradient) of the slice.
  • innerLabel - the template for the label rendered inside the slice. The format for the template is derived from the Python format() function. Properties are enclosed in curly brackets and formats can be specified using the Python Format Specification Mini-Language. For example:
    '{label}: {value:.2f}'  // 'My Slice: 2.26', i.e. value is rounded to two decimal numbers.
    See the API reference for more examples on number formatting. You can use the following properties in your template: value, label, percentage, angle, startAngle and endAngle.
  • innerLabelMargin - the gap between the middle of the text label and the serie border in the pie. If value between 0...1 is used, it is considered to be proportional to the outer radius. If the number is bigger than 1, it is considered to be an absolute distance in pixels.
  • legendLabel - the template for the label for the slice used in the legend. Uses the same formatting as innerLabel.
  • legendLabelLineHeight - the line height of the slice label in the legend.
  • legendLabelMargin - the gap between the circle representing the slice color and the slice label in the legend.
  • offset - the offset of the slice. This option is useful for exploding slices and makes sense only for the outermost slices of the pie. If offset is between 0...1 is used, it is considered to be proportional to the outer radius. If the number is bigger than 1, it is considered to be an absolute distance in pixels.
  • onClickEffect - the effect applied when the user clicks on the slice. This is an object with the property type that can be either 'enlarge' or 'offset'. If 'enlarge' type is used, the object can have an additional property scale which is the scaling factor for the slice enlargement. If 'offset' type is used, the object can have an additional property offset which is the offset of the slice.
  • onHoverEffect - the effect applied when the user hovers with his mouse cursor over the slice. This is an object with the property type that can be either 'enlarge' or 'offset'. If 'enlarge' type is used, the object can have an additional property scale which is the scaling factor for the slice enlargement. If 'offset' type is used, the object can have an additional property offset which is the offset of the slice.

When the above properties are defined in the sliceDefaults options object, they will be applied to all the slices unless overruled by properties in the slice object.

Chart API

chart.Pie

addSlice(slice, serieIndex [, opt]) Append a slice slice to the serie identified by serieIndex. opt can be an arbitrary object that is passed to the onchange handlers of the JointJS element.
editSlice(slice, sliceIndex, serieIndex [, opt]) Replace a slice in the pie by a new slice. The serie is identified by serieIndex in which the slice at index sliceIndex will be replaced. opt can be an arbitrary object that is passed to the onchange handlers of the JointJS element.

chart.PieView

To access the view for a pie chart, use: paper.findViewByModel(chart).

clickSlice(sliceIndex, serieIndex) Simulate a click on the slice. Note that this triggers the effects that you defined in the onClickEffect in the slice object.
mouseOverSlice(sliceIndex, serieIndex) Simulate a mouse over on the slice. Note that this triggers the effects that you defined in the onHoverEffect in the slice object.

chart.PieView events

To set a listener for the pie view events, use: paper.findViewByModel(chart).on('mousemove', myFunction).

mousemove Continuously triggered when the user hovers over a slice. The handler is passed slice and event objects.
mouseover Triggered when the user hovers over a slice. The handler is passed slice and event objects.
mouseout Triggered when the user leaves a slice. The handler is passed slice and event objects.
click Triggered when the user clicks a slice. The handler is passed slice and event objects.

Chart SVG structure

The following is the chart SVG structure that you can take as a reference when styling chart elements.

<g class="background">
    <rect/>
    <text/>
</g>
<g class="data">
    <g class="slice serie-[index of the serie] slice-[index of the slice] [serie name?] [slice name?] [outer?] [hover?] [clicked?]" data-serie="[index of the serie]" data-slice="[index of the slice]" data-value="[value of the slice]">
        <path class="slice-fill"/>
        <path class="slice-border"/>
        <text class="slice-inner-label"/>
    </g>
    <!-- ... possibly more slices -->
</g>
<g class="foreground">
    <rect/>
    <text class="caption"/>
    <text class="subcaption"/>
    <g class="legend">
        <g class="legend-items">
            <g class="legend-serie [serie name?]" data-serie="[index of the associated serie]">
                <text/>
            </g>
            <g class="legend-slice [slice name?]" data-serie="[index of the associated serie]" data-slice="[index of the associated slice]">
                <circle/>
                <text/>
            </g>
            <!-- ... possibly more legend items  -->
        </g>
    </g>
</g>

Selector examples

chart.attr('.legend-slice text/text-decoration' , 'underline')  // Underline a legend label.
chart.attr('.legend-slice text/font-size' , 13)  // Set a font size of all the legend texts for slices.
chart.attr('.slice-fill/stroke' , 'gray')  // Set a stroke color of a slice.
chart.attr('.slice-fill/stroke-width' , 1)  // Set a stroke width of a slice.
chart.attr('.slice-3 .slice-fill/filter', { name: 'dropShadow', args: { dx: 2, dy: 2, blur: 3 } })

shapes.chart.plot

The chart.Plot chart allows you to create Line, Bar and Area charts or their combinations.

var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({ el: $('#paper'), width: 500, height: 500, model: graph });

var chart = new joint.shapes.chart.Plot({
    position: { x: 50, y: 50 },
    size: { width: 300, height: 100 },
    series: [ { name: 'myserie', data: [{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 2 }, { x: 4, y: 3 }] } ]
});
graph.addCell(chart);

Series styling

All the elements of the chart can be customized using the usual JointJS approach, via the attrs object or the attr() method. This means that by just knowing the structure of the SVG elements the chart is built from, you have full flexibility in styling your chart. Following the JointJS philosophy, we don't try to invent special parameters. Instead, we try to follow SVG and CSS standards as much as we can in the way things are styled.

Let's have a look at one snippet of the SVG structure of the charts that is used to render a serie. For the full SVG structure of charts, please see the section Chart SVG Structure in this document. Once you know the structure, it is easy to make CSS selectors referencing the SVG elements that you want to style. We'll show an example of that below. the SVG structure of one serie is the following:

<g class="serie [name of the serie]">
    <path>
    <g class="points">
        <g class="point">
            <circle>
            <text>
        </g>
        <!-- ... possibly more points -->
    </g>
</g>

Knowing that, we can, for example, set the stroke color of the serie SVG path like this:

var chart = new joint.shapes.chart.Plot({
    position: { x: 50, y: 50 }, size: { width: 300, height: 100 },
    series: [ { name: 'myserie', data: [{ x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 2 }, { x: 4, y: 3 }] } ],
    attrs: {
        '.myserie path': { stroke: 'green', 'stroke-width': 3 }
    }
});

In the example above, we showed how to pass styling when creating a new chart but you can also change attributes of an existing chart:

chart.attr('.myserie path/stroke', 'red');

Note that not only you can change strokes but you can also use other SVG presentational attributes. An interesting one is the fill attribute. By setting the fill attribute on our serie path, we effectively render our serie as an Area chart. In the following demos, we even use the JointJS gradients for the fill attribute making our charts even prettier:

Multiple series

The Chart plugin supports multiple series. Simply add more series to the series array. As you might have noticed, series objects have the name property. This name is then set as a class name on the serie SVG grouping element. This allows you to style series based on their names as we've already shown in the previous demos. In the following example, we plot multiple series and style them differently based on their names.

All series have automatically generated legends. This legend is interactive. You can click on the labels in the legend and turn off and on the rendering of the associated serie in the chart.

Markings

Markings (a.k.a trend lines) are vertical or horizontal lines or rectangles highlighting a certain area in the chart. You can have an unlimited number of markings in your charts. Markings are defined in the markings array with coordinates of the start and end of the marking.

The following is a snippet from the options passed to the chart constructor that defines four markings and styles them. For the full source code, please follow the link below the demo.

markings: [
    { name: 'target', start: { y: 3.2 }, label: 'My Target' },
    { name: 'current', start: { x: 4 } },
    { name: 'previous', start: { x: 2.8 }, end: { x: 3.2 } },
    { name: 'next', start: { x: 4.8, y: 3.5 }, end: { x: 5.2, y: 2.5 } }
],
attrs: {
    '.marking.target rect': { fill: 'red' },
    '.marking.next rect': { fill: '#3498DB', rx: 1000, ry: 1000, 'fill-opacity': .6, stroke: 'black' },
    '.marking.previous rect': { fill: '#27AE60', 'fill-opacity': .8 }
}

Guidelines

Guidelines are lines that appear when you hover over the chart with your mouse pointer. They help the user orient himself in the chart. Guidelines are disabled by default but can be very easily turned on.

Here is the snippet from the attrs object that enables guidelines:

attrs: {
    '.guideline': { 'stroke-dasharray': '3,1', 'stroke-width': .8, display: 'block' },
    '.x-guideline': { stroke: 'red' },
    '.y-guideline': { stroke: 'green' }
}

Hover over the chart to see the guidelines following your mouse cursor.

Tooltips

Tooltips are a great way to show contextual information in your charts. The JointJS Chart plugin also provides a facility to get to the closest points to the mouse cursor from all the series data points. Moreover, the chart view offers the renderPoint() method that makes it easy to display a point marker at the place of any data point from any serie. Let's start with a demo and then see how this can be implemented.

IMPORTANT: For tooltips to work, you must have the ui.Tooltip plugin installed.

When setting up tooltips, we no longer work with the chart model. Instead, we request the paper for the view that represents the chart. This view triggers mouseover and mouseout events that we use to hide and show our tooltip. The mouseover event handler gets passed three important arguments: dataPoint, clientPoint and closestPoints. These three arguments mean the following:

  • dataPoint - the point in the chart area. This point is interpolated based on the minimum and maximum x and y values and the position of the mouse cursor in the chart.
  • clientPoint - basically the mouse event clientX and clientY coordinates but transformed to the coordinate system of the chart view.
  • closestPoints - an array of from each data serie that are the closest points to the mouse cursor. Each object in this array is of the form: { x: [number], y: [number], serie: [serie object] }.

All these three arguments help us render our tooltip. Here is the relevant code snippet from the above demo:

Legend

The chart legend is auto-generated and contains labels and colors of all the data series. The legend is also interactive by default and allows the user to turn on and off data series displayed in the chart. This sections shows you how you can customize the legend and its position and also how to turn series on and off programmatically.

The legend is rendered as an SVG group element with the class legend. Inside this group, we have legend-items each containing an SVG circle and text elements. Please refer to the Chart SVG structure section for the full SVG structure of the chart. This is all you need to know to be able to style the legend. Moreover, we have prepared a syntactic sugar method (legendPosition()) for positioning the legend so that you don't have to deal with the low-level positioning using the JointJS special attributes:

chart.legendPosition('nw');

Also, the styling of the legend item text (name of the associated serie), the legend item circle and the their look in the disabled state (when the serie is turned off) can be done the usual JointJS way:

chart.attr('.legend-item text/fill', 'red');
chart.attr('.legend-item circle/r', 6);
chart.attr('.legend-item circle/stroke', 'black');
chart.attr('.legend-item.disabled text/fill', 'blue');

Real-time data

The Channel plugin supports real-time data plotting out-of-the-box. Every change to the series array triggers an update of the chart view and new series are rendered. However, the Chart plugin provides two methods that are handy when dealing with real-time data: addPoint(p, serieName, op) and lastPoint(serieName). Use addPoint() to add a new data point to a serie identified by serieName. This method automatically triggers an update and the associated view is re-rendered:

setInterval(function() {
    chart.addPoint({ x: chart.lastPoint('myserie').x + 1, y: Math.random() * 2 + 2 }, 'myserie', { maxLen: 6 });
}, 600);

Fixed axis

By default, the chart plugin automatically choses the minimum and maximum values on each of the axis based on the minimum/maximum data points. By specifying min and max values in the axis object, you can define the precise minimum and maximum values for the axis:

var chart = new joint.shapes.chart.Plot({
    ...
    axis: {
        'y-axis': { min: -3, max: 10 },
        'x-axis': { min: -5, max: 10 }
    }
    ...
})

Time series

By going through the previous sections, you already know all you need to be able to plot time series. There is nothing special about plotting time series except of being able to format the data/times on the x axis. For that, the Chart plugin allows you to define your own formatting functions for the tick labels. This means that for representing time series data, your data point x coordinates should be in the UNIX time format. Then you just provide a function that formats this timestamp into a more readable representation:

var chart = new joint.shapes.chart.Plot({
    ...
    series: [
        { name: 'temp', data: [{ x: 1397560472344, y: 21.5 }, ... ] }
    ],
    axis: {
        'x-axis': {
            tickFormat: function(t) {
                var d = new Date(t);
                return (d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getHours() + '/' + d.getMinutes()
            }
        },
        'y-axis': { 
            tickFormat: function(v) { return joint.util.format.number('.1f', v) + ' dgC'} 
        }
    }
    ...
});

TIP: We recommend using the great Moment.js JavaScript library for parsing, validating, manipulating and formatting dates.

Bar series

The chart plugin supports bar series. To turn a serie to a bar serie, just set the bars option to true:

var chart = new joint.shapes.chart.Plot({
    ...
    series: [
        { name: 'myserie', bars: true, data: [{ x: 2, y: 20 }, ... ] }
    ]
    ...
});

There is couple of recommended settings when working with bar charts, namely padding, align and barWidth. padding is not specific to bar charts but is especially useful when using this kind of chart. This is because when bars are rendered, they must be aligned to a certain value on the x axis. By default, the top-left corner of each bar is at the position of the associated x data point value. That means that without any padding, the last bar won't be visible (only a very tiny fraction of its left border). Therefore, we have to set at least right padding to make the bar visible. The align property of bars object controls the alignment of the bars with respect to the associated x data point value. Possible values are "left" (the default), "right" and "middle". barWidth controls the width of the bars. If the value is a floating point number between 0 and 1, it is used to set the final width of each bar proportionally to the calculated width. If the value is an integer higher than 1, it is considered to be an absolute width in pixels. For a full description of the bar options, please see the Serie options object, especially the bars object.

Bar & Line series intermixed

Bar and Line series can be rendered together as part of one chart.

var chart = new joint.shapes.chart.Plot({
    // ...
    series: [
        { name: 'mybars', bars: { barWidth: .7 }, data: [{ x: 1, y: 15 }/* ... */ ] },
        { name: 'myotherbars', bars: { barWidth: .7 }, data: [{ x: 1, y: 12 }/* ... */ ] },
        { name: 'myline', interpolate: 'bezier', data: [{ x: 1, y: 20 }/* ... */ ] }
    ]
    // ...
});

As you can see in the code above, we have two bar series and one line serie as part of one chart. It is important to note that if there are more than one bar serie in the chart, by default, the bars of the different series will be rendered one over another. For cases where this is not desirable, you can offset the bars of one of the series as shown in the demo below:

As you might have noticed in the demo above, negative values for the series are sully supported.

Chart Options

This is the full list of the options that you can pass to the chart constructor function.

  • series - an array of series.
  • markings - an array of markings. Markings can be both vertical or horizontal lines or rectangles.
  • axis - axis definition. Currently, the object can contain only x-axis and y-axis properties representing one axis.

Serie

These are items of the series array.

  • name - the name of the serie. This name is added as a CSS class to the serie container (the SVG group element).
  • label - the serie label (used in the auto-generated legend)
  • data - an array of data points of the form { x: [number], y: [number] }
  • interpolate - the interpolation method. Interpolation methods include:
    • linear - the default interpolation.
    • bezier - interpolation with a bezier curve.
    • step - step function
    • stepBefore - step function
    • stepAfter - step function
  • bars - controls bar series. If set to true, the series will be rendered as a bar serie using default options. You can override the default options by setting bars to an object that can have the following properties (all are optional):
    • align - the alignment of the bars with respect to the x data point values. Possible values are "middle" (default), "left" and "right".
    • barWidth - controls the width of the bars. If the value is a floating point number between 0 and 1, it is used to set the final width of each bar proportionally to the calculated width. The calculated width is computed such as, for a consecutive x values on the axis, the sum of the widths of all the bars equals the width of the chart area. If the value is an integer higher than 1, it is considered to be an absolute width in pixels.
    • top-rx - the top x radius of the bars. Together with the top-ry value, this allows you to define border radius for the bars.
    • top-ry - the top y radius of the bars.
  • showLegend - controls whether a legend item for the serie should be shown or not. If false, the legend item will be skipped. If it is a function, it should return true if the legend item should be shown, false otherwise. The function has the following signature: function(serie, stats), where serie is the serie object and stats is an object that holds some interesting statistics for the serie useful for deciding whether to show the legend item or not. The statistics are: decreasingX, decreasingY, maxX, maxY, minX, minY, nonDecreasingX and nonDecreasingY.
  • legendLabelLineHeight - controls the legend label line height. It defaults to 16.

Markings

These are items of the markings array.

  • start - the start coordinate of the marking. An object of the form { x: [number], y: [number] }. If only x is given, a vertical marking is rendered at that x-coordinate. If only y is given, a horizontal marking is rendered at that y coordinate.
  • end - the end coordinate of the marking. An object of the form { x: [number], y: [number] }. If only x is given, a vertical marking is rendered at that x-coordinate. If only y is given, a horizontal marking is rendered at that y coordinate. If both start and end objects are defined, a rectangular marking starting at start coordinates ending at end coordinates is rendered. (TIP: if you style your marking with high rx and ry values, you can make the marking look like a circle.)
  • name - the name of the marking. This name is added as a CSS class to the marking container (the SVG group element). This makes it easier to style it.
  • label - the marking label. This is an auto-positioned text element.
  • attrs - additional SVG attributes that will be set on the marking container SVG element. This is sometimes useful if you want to store additional data associated with the marking in its DOM element. For example, when using tooltips.

Axis

These are properties of the axis object.

  • tickFormat - a format of the tick labels. This can be either a string in the Python Format Specification Mini-Language or a function that takes the value the tick represents and returns the label for that tick. Example:
    // floating point value with one digit after the decimal point
    { axis: { 'x-axis': { tickFormat: '.1f' } } }
    // currency
    { axis: { 'y-axis': { tickFormat: function(v) { return '$' + joint.util.format.number('.2f', v) } } }
  • tickSuffix - a string or a function that will be used as a suffix of the resulting formatted tick label. If it is a function, it is passed the tick value as an argument.
  • min - the precise minimum value for the scale. Specifying min and max values for axis prevents the chart plugin to automatically choose the minimum and maximum values for the axis, which is the default. See the Fixed axis section for more information.
  • max - the precise maximum value for the scale.
  • ticks - the number of ticks on the axis. Default is 10.
  • tickStep - the modulus for tick display in the x axis. Default is 1, therefore, all ticks are displayed. Set this value to a higher number to filter out ticks. For example, setting this number to 10 makes the axis render only every 10-th tick. Note that this option only works for the x-axis.

Chart API

legendPosition(position [, opt]) Set position of the legend. position is a string that can be one of n, nw, w, sw, s, se, e, ne, nne, nn, nnw, nnww, nww, ww, sww, ssww, ssw, ss, sse, ssee, see, ee, nee, nnee. See the Legend section for more info. opt.padding can be used to set the distance between the legend and the chart edges. The default is 10px.
addPoint(p, serieName [, opt]) Append point p to the serie identified by serieName. If opt.maxLen is set and the number of points in the serie is higher than maxLen, shift the data in the serie.
lastPoint(serieName) Return the last point in the serie identified by serieName.
firstPoint(serieName) Return the first point in the serie identified by serieName.

chart.PlotView

To access the view for a plot chart, use: paper.findViewByModel(chart).

renderPoint(p, serie) Render a point at the coordinates defined in p. serie is the object from the chart.Plot series array and defines the serie for which the point should be rendered. Return the SVG DOM element for the point. See the Tooltips section on why this method is useful.
getSerieColor(serieName) Return the color used to draw the serie identified by serieName.
hideSerie(serieName) Hide serie identified by serieName.
showSerie(serieName) Show serie identified by serieName.
closestPoints(x) Return an array of the closest points from all the series in the chart for a given x coordinate.

Chart SVG structure

The following is the chart SVG structure that you can take as a reference when styling chart elements.

Selector examples

chart.attr('.caption/text', 'My Chart')  // Set chart caption.
chart.attr('.subcaption/text', '2014')  // Set chart sub caption.
chart.attr('.caption/fill', 'red')  // Set chart caption text color.
chart.attr('.caption/font-size', 20)  // Set chart caption font size.
chart.attr('.caption/ref-x, 10)  // Move chart caption 10px from the left edge of the chart.
chart.attr('.guideline/display, 'block')  // Display guidelines.
chart.attr('.x-axis .tick line/stroke' , 'green')  // Set the color of the ticks on the x axis.
chart.attr('.y-axis .tick text/fill' , 'blue')  // Set the color of the tick labels on the y axis.
chart.attr('.legend-item[data-serie="myserie"]/text-decoration' , 'underline')  // Underline a legend label.
chart.attr('.point/display' , 'none')  // Hide the data points.
chart.attr('.data .myserie .bar[data-x="5"]/fill' , 'blue')  // Set blue fill color for only the bar with x value 5.

storage.Local

storage.Local plugin gives you a convenient way to store documents (especially JointJS graphs and cells) to the client-side HTML 5 localStorage. This browser storage allows you to store data in the browser similar to cookies but with a greatly enhanced capacity and no information stored in the HTTP request header.

The standard HTML 5 localStorage is a simple key-value store where both keys and values are strings. Therefore, it is on the programmer to invent a way of storing and retrieving more complex data structures. storage.Local make this abstraction for you and its API, though overly simplified, resembles APIs for common NoSQL document object stores such as MongoDB.

Install

Include joint.storage.local.js file into your HTML:

<script src="joint.storage.local.js"></script>

Usage

joint.storage.Local is a singleton object with three basic methods: insert(), find() and remove(). It behaves like a single database with collections of objects. You can add, remove and search objects in each collection. Objects can be any JavaScript objects or JointJS elements, links or graphs. Currently, joint.storage.Local indexes objects in collections by their IDs only. It is important to note that all the methods are asynchronous. This means that the callback is defered until the current JavaScript call stack has cleared (similar to setTimeout with a delay of 0). This is because we want to keep the API similar to other NoSQL databases and so if you later decide to store your documents in e.g. MongoDB instead, you don't have to make drastic changes to your application. Instead, you can just write a simple replacement for joint.storage.Local and store your documents e.g. via AJAX on the server.

var graph = new joint.dia.Graph;
(new joint.shapes.basic.Rect).addTo(graph);

// Insert a graph into the collection 'graphs'.
joint.storage.Local.insert('graphs', graph);
// Find all the graphs in the collection 'graphs'.
joint.storage.Local.find('graphs', {}, function(err, graphs) {});
// Find a graph with ID 'mygraph' in the collection 'graphs'.
joint.storage.Local.find('graphs', { id: 'mygraph' }, function(err, graphs) {});
// Remove all the graphs from the collection 'graphs'.
joint.storage.Local.remove('graphs', {}, function(err) {});
// Remove a graph from the collection 'graphs'.
joint.storage.Local.remove('graphs', { id: 'mygraph' }, function(err) {});

API

insert(collection, doc [, callback]) Insert a document doc into the collection. Optionally pass a callback(err, doc). If the document doc didn't have an id property, one is automatically created and will be part of the doc object returned in the callback.
find(collection, query, callback) Find a document in collection. query can currently be either empty in which case all the documents from the collection are returned or it can contain id of a document in which case only a document with that id is returned. callback signature is: callback(err, docs).
remove(collection, query [, callback]) Remove a document from the collection. query can currently be either empty in which case all the documents from the collection are removed or it can contain an id of the document to be removed. callback signature is: callback(err).

ui

Rappid now comes with a variety of themes ("material", "modern", "dark", and the default theme). It is possible to set the theme globally (applied to all UI components) and to set a different theme for each UI component individually.

Setting the theme globally:

joint.setTheme('material');

This will update the theme for all the components currently rendered, as well as for any components that are rendered from now on.

Setting the theme for an individual component:

var colorPalette = new joint.ui.ColorPalette({
    theme: 'material',
    options: [
        { content: '#90C3D4' },
        { content: '#D4A190' },
        { content: '#A1D490' },
        { content: '#ffffff' }
    ]
});
document.body.appendChild(colorPalette.render().el);

And to change the theme of a component that has already been rendered:

colorPalette.setTheme('modern');

Here you can view all the Rappid UI plugins for every theme (you can also open it in a separate window) :

ui.Clipboard

Clipboard implements copy-pasting of cells. Optionally, clipboard also supports storing cells in an HTML 5 localStorage so that copy-pasting of cells works despite a browser tab being refreshed or closed/reopened. Additionally, clipboard is also able to copy cells from one paper and paste them to another. However, the determination of the targeted paper is left to the application layer.

Installation

Include joint.ui.clipboard.js to your HTML:

<script src="joint.ui.clipboard.js"></script>

Clipboard Configuration

Instantiating a clipboard is a matter of creating a joint.ui.Clipboard object:

var clipboard = new joint.ui.Clipboard({ useLocalStorage: false });>
link Set of attributes to be set on each copied link on every pasteCells() call. It is defined as an object. e.g. { z: 1 }.
translate An increment that is added to the pasted elements position on every pasteCells() call. It can be either a number or an object with dx and dy attributes. It defaults to { dx: 20, dy: 20 }.
useLocalStorage Set to false if you don't want to use window.localStorage for storing copied cells. It defaults to true.

Clipboard API

copyElements(collection, graph, opt) Store the elements from the collection along with all links, which are connected by both source and target to these elements. The options opt passed as a parameter can override the global options.
cutElements(collection, graph, opt) Similar to copyElements() but it also removes all copied cells from the graph.
pasteCells(graph, opt) Add copies of stored cells to the graph. The method can be called multiple times and it always produces new copies.
clear() Clear all stored cells.
isEmpty() Return true when there is no cells stored on the Clipboard instance. Returns false otherwise.

Clipboard Setup

The next step is hooking these methods to relevant user events.

var selection = new joint.ui.Selection({ paper: paper });
var keyboard = new joint.ui.Keyboard;

keyboard.on('ctrl+c', function() { clipboard.copyElements(selection.collection, paper.model); });
keyboard.on('ctrl+x', function() { clipboard.cutElements(selection.collection, paper.model); });
keyboard.on('ctrl+v', function() { clipboard.pasteCells(paper.model); });

Note that the selection.collection is a Backbone.Collection that stores the selected elements. The clipboard is probably the best when used in combination with the Selection JointJS plugin. We suggest you to look at the documentation to the Selection plugin for further details on using selections.

Also, it is advisable to look at our Kitchen Sink application to see how the Selection and Clipboard work together.

ui.ColorPalette

ui.ColorPalette is a UI widget for displaying dropdowns with color palette.

Installation

Include joint.ui.colorPalette.js and joint.ui.colorPalette.css files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.colorPalette.css">
<script src="joint.ui.colorPalette.js"></script>

Usage

Create an object of ui.ColorPalette type, render it with the render() method and append the generated HTML element anywhere in your DOM:

var colorPalette = new joint.ui.ColorPalette({
    options: [
	{ content: '#000000' },
	{ content: '#FFFFFF' },
	{ content: 'transparent', icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGoAAABrCAYAAACffRcyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABPxJREFUeNrsnc9rU1kUx+/LS2LTBtI4VMiqjYjiQgQ3IoIUuhvGUmYzUBgV8SdWLS7sooLgxm0XgjKg0P+gMDC7ggjDzGwGOt1psS2WBptioiYVmrbxnviiSc17eaPvvdzz7vcLlyTk5vVyPtzzzv3mtDWq1arYrVwuNyAfxuUYkaNfQEFoWY4ZOaYymczS7jeNRlASUC9NlOMs4tZRTdNGkcCKX4GyID2V4yjipITm5Bisw4oAkrIiFk8tNp9AWekOkNSENVVLfaurq1Q4LCImSisbtao7R5mmWTQMYxPx8l6yRohvb2/3tpk2HrVKcFtA6XT6WSwWKyOk/qlSqfQUCoVTDsBGIk7nJEAKRhRjirXDlP6I024CpGBhUczt3rcFhXtS8HKKeQTh4aHot35Q3vyObW1tpdvNSyQSi8lk8iWu1yFQtGg59rWbJyuZNVzv+4XUx0QABVCa68+/0mJi8oj4/Y99Xlwuioj6BOn8lSHx4UOs9vr0j2vYUYrJ/G++6zOkRKIihn9aQepTzV14viB6Lo5lP0N68mhWnDxRACjFIP1w9YYwNjZMryEBlNeQSmVR7e7e9hoSib44rLasMqLRtb6+vlm7D5ZKpf3ycNfj4qCY2tnZ2dM2t5tmSY4NbtejexKlO9pJBOndbw/ym4cPtd0ArRyMfD4/ZHeo/uaqz61N4vTDGxWPx9dSqdQ8q+tRdXfhGhUOtXRnPH44u3nwwDE/HAykPi9KcB/uSQDFEBJAMYEEUEwgAdT3OA4BQgIoBRwHgGLiOAAUE8fBrXzvmaATPR0W3TgEdPhU5Xo1x+Hy9WbHQR5m5QnZk/XJOa8DAeW2h0A3x8Ht+pD6GJfgAMUcEkDZQSLdmfhbFUgAZQfp9q1/xK+jKyotUXtQTY5DHdLYlZeqrVNrUE2Og8KQtAbV5DgoDklbUI2OAwdIJO16Jhp7HOh1eXLizcbPw1tBrw89E24dB2snSUjZTqwPPRP/pwRXPN3pB4o5JD1AhQBS+EGFBFKoQXFxHLQGxclx0BYUN8dBS1CR9yWRuneflePgVqHpmTCKb83kxWvZyOJSV4PjkJUn0KwK62txgNawZyKfj4vRc0Pi1UpXpx0H9EzYRzAuRn4hSL1hS3fhAaUJJN6gNILEF5RmkFiCoupON0jsQNE5KXnmQlY3SKxAEaS9V2+K+jlJJ0hsQNUhxV4sCB0h1VK+6j0TNcdBprtdjkPgPQ7omQiZ46Bfz4SGJTg/UIDEABQgMQAFSOqD0tVxYAVKZ8eBDSjdHQcWoOA4uFfHeia49Tjo2TMRYschPD0TKMEZgAIkBqAAiQEoQFIfFBwHBqDgODAABceBASg4Dj7cQrzumQhrj0O4eibgODDomUAJzuAeBUhMQF0aOw5IHECVy3FA8lfe/HvXu5P/1h4V+husANVKAMTXmYAACqnPD9EJ3M3hzm0PgW7XCwyU29/6wPWQ+nCPghQAVa1W4whPsHKKuS0oeUPsrVQqPQhfMKJYU8ydQC3bvVkoFE4BVjCQKNYOU5ap6puR46bdrlpfXx82TbNoGMYmQupPunPaSZZm6BveAflkESFTWtlIJpNZkk+mEQtlNU2M6sXEuBxziIlymrPYfKr6JLGifBgELOUgDVpsvpTnDbCQBhVId42QSIasOr6alcvlBqwtNyJHP+IWiJatCnzKqhua1BIUpJ4+CjAAVnYzLhKE5pcAAAAASUVORK5CYII=' },
	{ content: '#B3B3B3' },
	{ content: '#808080' },
	{ content: '#4D4D4D' },
	{ content: '#E6E6E6' },
	{ content: '#FFC7C9' },
	{ content: '#FFA0A4' },
	{ content: '#E3686D' },
	{ content: '#D71920' },
	{ content: '#FFE3D1' },
	{ content: '#FFCBA8' },
	{ content: '#FFAB73' },
	{ content: '#F58235' }
    ]
});
document.body.appendChild(colorPalette.render().el);

Configuration

The following table lists options that you can pass to the ui.ColorPalette constructor function. You might notice that many of the options are similar to the ui.SelectBox widget. This is because the ui.ColorPalette inherits from this widget.

width The width of the color palette in pixels.
options An array of items. Each item is an object with the following properties:
content The color in hex or other color formats (rgb, rgba).
icon A path to an image (note that it can also be an image embedded via the Data URI Scheme). The icon can be handy for representing the transparent color.
selected true if the item should be selected by default. If non of the items are selected, the first item is selected by default.
selected The index of the item which should be selected by default. If this value is undefined (which it is by default), the ui.ColorPalette widget looks up the selected item from the options array (by using the selected boolean property). If nothing is selected, the first item in the options array is selected by default.
keyboardNavigation true (the default) if you want the ui.ColorPalette to provide a keyboard navigation (up/down/left/right/enter/escape keys are supported).
selectBoxOptionsClass When the color palette is opened, the ui.ColorPalette generates an HTML container that contains all the items. This container is then appended to the document body (or target if provided). Sometimes, you want to provide custom styling to this color palette container. selectBoxOptionsClass gives you a chance to define a CSS class that will be set on this container so that you can style it in your CSS.
target ui.ColorPalette generates an HTML container that contains all the color palette items. This container is by default appended to the document body. In some situations (e.g. if you want the color palette to scroll with some other container), you want to append this container to a different DOM element. The target option is exactly for that. It accepts an HTML element that will be used as a container for the generated color palette items.
placeholder In some cases, you want to deselect the color palette and show a placeholder in place of the selected item. This is when you don't want any of the items to be selected. In this case, set the selected index to -1 and the placeholder to any HTML you want to display in the selected item window. This placeholder has the selected-box-placeholder CSS class that you can use for further styling.

API

render() Render the color palette. Note that once you render the color palette, you can use the el property that points to the container HTML element and append it anywhere in the DOM (e.g. $(document.body).append(colorPalette.el)).
getSelection() Get the current selection. The selection points to one of the items from the options array.
getSelectionValue() Get the current selection value (the color).
getSelectionIndex() Get the index in the options array of the item that is currently selected.
select(index, opt) Select an item. index is the index to the options array. opt is optional and can be an arbitrary object that will be passed to the option:select event handler.
selectByValue(color) Select an item by color.
isOpen() Returns true if the color palette is currently opened. false otherwise.
toggle() Programmatically toggle the color palette.
open() Programmatically open the color palette.
close() Programmatically close the color palette.
remove() Remove the color palette from the DOM.
on(event, handler [, context]) Register a handler function that will be called whenever event is triggered. The optional context is an object that will be the context of the handler function (the this).

Events

The ui.ColorPalette object triggers various events that you can react on. Events can be handled by using the on() method (see above).

option:hover Triggered when the user hovers over one of the color items. The handler is passed the option object and the index of the option in the options array.
option:select Triggered when the user selects an item. The handler is passed the option object and the index of the option in the options array. If you selected the item with the select(index, opt) method, the third argument to the event handler will be your opt object.
option:mouseout Triggered when the user leaves an item with mouse cursor. The handler is passed an event object as the only argument.
close Triggered when the color palette gets closed.

ui.ContextToolbar

ui.ContextToolbar is a UI widget for displaying context toolbars (usually a set of buttons) below a certain target element (HTML or SVG). There can always be only one context toolbar opened at a time. This is automatically handled by the ui.ContextToolbar component which simplifies the process and makes sure you don't have to keep track of opened context toolbars yourself.

Installation

Include joint.ui.contextToolbar.js and joint.ui.contextToolbar.css files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.contextToolbar.css">
<script src="joint.ui.contextToolbar.js"></script>

Usage

Create an object of joint.ui.ContextToolbar type, add tool buttons and call the render() method in order to open the context toolbar.

var ct = new joint.ui.ContextToolbar({
    tools: [
        { action: 'yes', content: 'Yes' },
        { action: 'no', content: 'No' },
        { action: 'maybe', content: 'Maybe' },
        { action: 'sure', content: 'Sure' }
    ],
    target: this
});

ct.on('action:yes', function() { alert('Yes') });
ct.on('action:no', function() { alert('No') });
ct.on('action:maybe', function() { alert('Maybe') });
ct.on('action:sure', function() { alert('Sure') });

ct.render();

A common usage is to open the context toolbar on click on a certain element (either HTML or SVG):

$('circle').on('click', function() {

    var circle = this;

    var ct = new joint.ui.ContextToolbar({
	tools: [
	    { action: 'hide', content: 'Hide' },
	    { action: 'info', content: 'Info' },
	    { action: 'remove', content: 'Remove' }
	],
        target: circle
    });

    ct.on('action:hide', ct.remove, ct);
    ct.on('action:remove', function() {
        V(circle).remove();
    });
    ct.on('action:info', function() {
        alert('This is an SVG circle');
    });

    ct.render();
});

Configuration

The following table lists options that you can pass to the ui.ContextToolbar constructor function:

tools An array of tools. Each tool is an object that can have the following properties:
actionThe name of the action. This action is then triggered on the context toolbar object so that you can react on it later.
contentThe content of the button. It can be a text or an arbitrary HTML.
iconOptionally, instead of passing a content, you can just pass a path to an image.
attrsAn object with attributes that will be applied on the generated button. Useful for adding your own custom attributes such as { 'data-tooltip': 'My Tooltip'}.
target The target element (either HTML or SVG).
padding The gap between the target element an the context toolbar. Default is 20.
autoClose Determines if the context toolbar should be closed if the user clicked outside of the area of the context toolbar. Default is true.

API

render() Render the context toolbar onto the screen.
remove() Remove the context toolbar. Usually, you don't have to call this method as the context toolbar is closed automatically if the user clicked outside of the area of the context toolbar (unless you set the autoClose option to false).
on(event, handler [, context]) Register a handler function that will be called whenever event is triggered. The optional context is an object that will be the context of the handler function (the this).
joint.ui.ContextToolbar.close() This is a static method that closes any context toolbar that is currently open.
joint.ui.ContextToolbar.update() This is a static method that updates the position of the opened context toolbar (if any). Call this method if you target element changes its position while you still want to keep the context toolbar opened.

Events

The ui.ContextToolbar object triggers events that correspond to the actions that you defined in the tools array. Events can be handled by using the on() method (see above).

action:[action] Triggered when the user clicks on one of the buttons in the context toolbar.

ui.Dialog

ui.Dialog is a UI widget for displaying both modal and non-modal dialog boxes. It's a little window that overlays all other elements on the page. ui.Dialog supports an arbitrary HTML content, allows you to define buttons in an easy way and can even be inlined on the page in any HTML container. The dialog can be optionally draggable.

Installation

Include joint.ui.dialog.js and joint.ui.dialog.css files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.dialog.css">
<script src="joint.ui.dialog.js"></script>

Usage

Simply create an object of ui.Dialog type and call open() method on it:

var dialog = new joint.ui.Dialog({
    width: 300,
    title: 'My title',
    content: 'This is my dialog box.'
});
dialog.open();

Configuration

The following table lists options that you can pass to the ui.Dialog constructor function:

width The width of the dialog in pixels.
title The title of the dialog.
content The content of the dialog. It can be a string or HTML or even a DOM element.
type The type of the dialog. This effectively sets a CSS class on the dialog HTML container. The predefined dialog types are: 'info', 'alert', 'warning', 'success' and 'neutral'.
buttons Buttons of the dialog. buttons is an array of objects of the form { action: String, content: String [, position: String] }, where action is the name of the action the button represents and content is the label of the button (it can be HTML too). When the button is clicked, the dialog object triggers an event action:[name] allowing you to react. The optional position property can either be 'left' or 'right'. If the position is not specified, it is considered to be 'right'.
draggable If draggable is true, the dialog window will be draggable by the user. It defaults to false.
closeButton If closeButton is false, the dialog window will not display the default cross button in the top right corner allowing the user to close the dialog. It defaults to true.
closeButtonContent The HTML content of the little close button in the top right corner of the dialog window. It defaults to a cross (×).
modal If false, the dialog window will not be made modal. In other words, the user will still be able to interact with other elements on the page. It defaults to true.
inlined If true, the dialog window will be inlined in a container that you can pass to the open() method.

API

open() Open the dialog window.
close() Close the dialog window.
on(event, handler [, context]) Register a handler function that will be called whenever event is triggered. The optional context is an object that will be the context of the handler function (the this).

Events

The ui.Dialog object triggers events when the user clicks one of its buttons. Events can be handled by using the on() method.

action:[name] Triggered when the user clicks a button in the dialog with action named [name].

ui.FlashMessage

ui.FlashMessage is a UI component for displaying flash messages. This is very useful for informing the user that something has happened. As you will see later on on this page, ui.FlashMessage plugin provides a quick way of displaying messages without you having to worry about closing the message boxes or cascading them on top of each other.

Installation

Include joint.ui.flashMessage.js and joint.ui.flashMessage.css files and joint.ui.dialog.js and joint.ui.dialog.css dependencies (ui.FlashMessage inherits from ui.Dialog) to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.dialog.css">
<link rel="stylesheet" type="text/css" href="joint.ui.flashMessage.css">
<script src="joint.ui.dialog.js"></script>
<script src="joint.ui.flashMessage.js"></script>

Usage

There are two ways to display flash messages. One is by creating an object of joint.ui.FlashMessage type and call the open() method on it. However, for convenience, there is also a static method joint.ui.FlashMessage.open() that is much shorter:

// The longer method of displaying a flash message.
(new joint.ui.FlashMessage({
    title: 'Message',
    type: 'alert',
    content: 'An error occurred. Try again later.'
})).open();
// A shorter alternative.
joint.ui.FlashMessage.open('I am a flash message.');

Couple more examples of common usage:

$('#btn-display').on('click', function() {
    (new joint.ui.FlashMessage({
	title: 'Message',
        type: 'alert',
	content: 'An error occurred. Try again later.'
    })).open();
});

$('#btn-display-width').on('click', function() {
    (new joint.ui.FlashMessage({
        width: 150,
	title: 'Message',
	content: 'An error occurred. Try again later.'
    })).open();
});

$('#btn-display-modal').on('click', function() {
    (new joint.ui.FlashMessage({
        type: 'alert',
        closeAnimation: false,
        modal: true,
	title: 'Modal Message',
	content: 'This is a modal Flash message requiring the user to close the message manually.'
    })).open();
});

$('#btn-close-all').on('click', function() {
    joint.ui.FlashMessage.close();
});

joint.ui.FlashMessage.open('ui.FlashMessage 1');
joint.ui.FlashMessage.open('ui.FlashMessage alert', '', { type: 'alert', closeAnimation: { delay: 1000 } });
joint.ui.FlashMessage.open('ui.FlashMessage warning', '', { type: 'warning', closeAnimation: { delay: 2000 } });
joint.ui.FlashMessage.open('ui.FlashMessage success', '', { type: 'success', closeAnimation: { delay: 3000 } });
joint.ui.FlashMessage.open('ui.FlashMessage neutral', '', { type: 'neutral' });
joint.ui.FlashMessage.open('ui.FlashMessage info', '', { type: 'info' });
joint.ui.FlashMessage.open('ui.FlashMessage close delay 3s', '', { type: 'neutral', closeAnimation: { delay: 3000 } });
joint.ui.FlashMessage.open('ui.FlashMessage with title', 'Title');
joint.ui.FlashMessage.open('ui.FlashMessage without close animation', '', { closeAnimation: false });

Configuration

The following table lists options that you can pass to the ui.FlashMessage constructor function:

title The title of the flash message.
type The type of the flash message. One of 'alert' (default), 'warning', 'success', 'neutral'.
content The content of the flash message. It can be either a text or an arbitrary HTML.
modal Set to true if you want your flash message to be modal.
closeAnimation Set to false if you don't want the flash message to be closed in an animated fashion. It can also be an object of the form { delay: Number, duration: Number } specifying properties of the close animation.

API

open() Open the flash message.
joint.ui.FlashMessage.open(content, title, opt) Open a flash message (the shorter way). opt can contain any option that you can pass to the joint.ui.FlashMessage constructor function (see the Configuration section for details).
on(event, handler [, context]) Register a handler function that will be called whenever event is triggered. The optional context is an object that will be the context of the handler function (the this).

Events

The ui.FlashMessage object triggers events that you can react on. Events can be handled by using the on() method (see above).

close:animation:complete Triggered when the flash message is actually removed from the DOM, i.e. when the close animation finishes.

ui.FreeTransform

FreeTransfrom plugin creates a control panel above an element giving an user the ability to resize an element in any direction or rotate it.

Installation

Include joint.ui.freetransform.css, joint.ui.freetransform.js and joint.dia.freetransform.js files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.freeTransform.css">
<script src="joint.dia.freeTransform.js"></script>
<script src="joint.ui.freeTransform.js"></script>

Usage

The usage of the ui.FreeTransform plugin is pretty straightforward. Just instantiate an object of the joint.ui.FreeTransform type and pass in the view for the element you want to have the free transform handles hooked onto:

var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({ el: $('#paper'), width: 500, height: 500, model: graph });

paper.on('cell:pointerup', function(cellView) {
    // We don't want to transform links.
    if (cellView.model instanceof joint.dia.Link) return;

    var freeTransform = new joint.ui.FreeTransform({ cellView: cellView });
    freeTransform.render();
});

Configuration

The joint.ui.FreeTransform constructor accepts the following options:

cellView The view for the element you want the free transform handles to be displayed for.
preserveAspectRatio Set to true if you want the resizing to preserve the aspect ratio of the element. Default is false.
allowRotation Set to false if you want the rotating handle to be omitted from the handles. Default is true.
minWidth The minimum width allowed for the element when resizing. Default is 0.
maxWidth The maximum width allowed for the element when resizing. Default is Infinity.
minHeight The minimum height allowed for the element when resizing. Default is 0.
maxHeight The maximum height allowed for the element when resizing. Default is Infinity.
rotateAngleGrid The steps in angle when rotating the element. Default is 15.
allowOrthogonalResize Set to false if you only want the four corner handles to be displayed. Default is true.

API

The joint.ui.FreeTransform objects provide the following methods:

render() Render the free transform widget. You should call this right after you instantiate the free transform object.
remove() Remove the free transform widget.
joint.ui.FreeTransform.clear(paper) Clear all the free transform widgets from a JointJS paper.

ui.Halo

Halo creates a control panel above an element with various tools. This gives the user control over your elements with tools located right at hand.

Installation

Include joint.ui.halo.css and joint.ui.halo.js files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.halo.css">
    <script src="joint.ui.halo.js"></script>

Creating a Halo

var graph = new joint.dia.Graph;
    var paper = new joint.dia.Paper({ el: $('#paper'), width: 500, height: 500, model: graph });

    paper.on('cell:pointerup', function(cellView) {
        // We don't want a Halo for links.
        if (cellView.model instanceof joint.dia.Link) return;

        var halo = new joint.ui.Halo({ cellView: cellView });
        halo.render();
    });

The joint.ui.Halo constructor takes a graph and paper objects + it also requires the view of a cell we want to display the halo above.

Default handles (tools)

The ui.Halo control panel has the following built-in tools. To add custom tools, please see the section Customizing halo tools.

remove Remove the element.
clone Drag to clone the element.
link Drag to link the element with another element. Note that the new link will be created based on the defaultLink option from the joint.dia.Paper object.
fork Drag to clone the element and connect it with cloned element in one go.
unlink Remove all the links coming in/out of the element.
resize Resize the element.
rotate Rotate the element.

Configuration

The joint.ui.Halo constructor function takes an options object as an argument. The options object can have the following parameters:

cellViewan element or link view. Previously, Halo accepted also paper and graph options but this is now deprecated as those can always be inferred from cellView.paper and cellView.paper.model properties.
typethe type of the Halo control panel. The default value is 'surrounding'. Another possible values are 'pie' in which case the Halo will be displayed as a pie menu and 'toolbar' in which case the Halo tools are displayed above the element in a small toolbar. See the demos below.
loopLinkPreferredSidethe preferred side for a self-loop link created from Halo ("top"|"bottom"|"left"|"right"), default is "top"
loopLinkWidththe self-loop link width in pixels, default is 40
rotateAngleGridthe angle increments the rotate action snaps to, default is 15
boxContenta string (text or HTML) or a function (of the form boxContent(cellView, boxDOMElement)) that returns an HTML string with the content that will be used in the information box below an element. By default, the box displayes the x, y coordinates and width and height dimensions of the element. If boxContent is set to false (or an empty string), the information box will be hidden
clearAllif set to true (the default value), clear all the existing halos from the page when a new halo is created. This is the most common behavior as it is assumed that there is only one halo visible on the page at a time. However, some applications might need to have more that one halo visible. In this case, set clearAll to false (and make sure to call remove() once you don't need a halo anymore)
useModelGeometryif set to true, the model position and dimensions will be used as a basis for the Halo tools position. By default, this is set to false which causes the Halo tools position be based on the bounding box of the element view. Sometimes though, your shapes can have certain SVG sub elements that stick out of the view and you don't want these sub elements to affect the Halo tools position. In this case, set the useModelGeometry to true.
clonea function with signature function(cell, opt). This function will be called when cloning or forking actions take place and it should return a clone of the original cell. This is useful e.g. if you want the clone to be moved by an offset after the user clicks the clone handle. The default function is: function(cell, opt) { return cell.clone().unset('z') }.
pieSliceAngle(only valid for the 'pie' type of Halo) The angle of one slice in the pie menu. It defaults to 45.
pieStartAngleOffset(only valid for the 'pie' type of Halo) The angular offset of the first handle in the pie menu. It defaults to 0.
pieIconSize(only valid for the 'pie' type of Halo) The size in pixels of the icon in the pie menu. It defaults to 14.
pieToggles(only valid for the 'pie' type of Halo) An array of pie toggle buttons. Usually, there's only one (the default) but you can have as many as you want. The default value is [{ name: 'default', position: 'e' }]. Each item in the array is an object of the form { name: [name of you toggle], position: [one of e/w/s/n] }. The name is passed in the options object in the state:close and state:open events triggered by the Halo when the pie toggle is clicked.
bboxA bounding box within which the Halo view will be rendered.

Disabling halo tools

Built-in tools are remove, resize, rotate, clone, fork, link and unlink. You can call the removeHandle() method to remove any of these tools just by passing its name:

halo.removeHandle('clone');

Alternatively, you can disable tools via CSS:

.halo .handle.clone { display: none; } /* disables the clone tool */

Also, the same way you would hide halo tools, as explained above, you can reposition them. For example, to reposition the remove icon tool to the bottom left corner, you could do:

.halo .handle.remove {
       left: -25px;
       bottom: -25px;
       top: auto;
       right: auto;
    }
Note the auto values for the top/right coordinates are important in order to cancel out the default values defined in the Halo plugin.

Another way to remove/reposition tool handles in the Halo is in JavaScript:

halo.removeHandle('clone');
    halo.changeHandle('remove', { position: 'se' });

The code above removes the clone tool from the Halo and re-positions the remove tool to the bottom right (south-east).

Each Halo can also be styled in CSS (disabling tools, repositioning, changing icons, ...) based on the type of element the Halo is displayed for. The Halo <div> container stores the type of the element in its data-type attribute. This makes it easy to target a halo for a certain type of element in your CSS. For instance, let's say we want to hide the remove tool only for an element of type "basic.Rect". We can do this:

.halo[data-type="basic.Rect"] .remove { display: none; }

Customizing halo tools

Halo provides three methods for adding, removing and changing custom tools: addHandle(), removeHandle() and changeHandle(). Use the addHandle() method to add new tools to your halo:

halo.addHandle({ name: 'myaction', position: 's', icon: 'myaction.png' });
    halo.on('action:myaction:pointerdown', function(evt) {
        evt.stopPropagation();
        alert('My custom action.');
    });

In the example above, we added a new tool named myaction, positioned the tool to the south (bottom-center) and used our own icon myaction.png. When the user clicks on our tool, Halo triggers an event named action:[name]:pointerdown. We can handle the event by listening on the halo object. Similarly, the Halo triggers action:[name]:pointermove and action:[name]:pointerup events. This gives us a high flexibility in implementing our own actions.

For removing a halo tool, use the removeHandle(name) method:

halo.removeHandle('myaction')

changeHandle() method allows us to change halo tool handles. For instance, if we want to change a position of the clone tool, we could use:

halo.changeHandle('clone', { position: 'se' })

Pie menu type of Halo

To display the Halo control panel as a pie menu, just set the type option to 'pie':

new joint.ui.Halo({ cellView: myElementView, type: 'pie' })

You can still add your own actions as you would normally do with the default 'surrounding' type of Halo.

Toolbar type of Halo

To display the Halo control panel as a small toolbar above an element, just set the type option to 'toolbar':

new joint.ui.Halo({ cellView: myElementView, type: 'toolbar' })
You can still add your own actions as you would normally do with the default 'surrounding' type of Halo.

It is also possible to use the Halo plugin with links, as the following demo illustrates.

API

addHandle(opt) Add a custom tool to the Halo. opt.name is the name of the custom tool. This name will be also set as a CSS class to the handle DOM element making it easy to select it your CSS stylesheet. opt.position is a string that specifies a position of the tool handle. Possible values are n, nw, w, sw, s, se, e and ne. opt.icon is a URL of the icon used to render the tool. This icons is set as a background image on the tool handle DOM element.
addHandles(handles) Add multiple handles in one go. This calls addHandle() internally for each item of the handles array.
removeHandle(name) Remove a tool handle named name from the Halo.
removeHandles() Remove all handles from the Halo.
changeHandle(name, opt) Change a tool handle named name in the Halo. The opt object can contain the same parameters as with the addHandle() method except of the name property. opt parameters will be merged with those defined previously.
render() Render the Halo. This must be called after the Halo object is instantiated.
on(event, callback) Register a handler (callback) for an event See the Halo Events section for the list of events the Halo object triggers.
toggleState([toggleName]) Toggle (open/close) the sate of the Halo (applicable for the pie type of Halo). toggleName is the name of the pie toggle as defined in the pieToggles option. If toggleName is not passed, all pie menus change the state (the most common usage as there is usually only one pie toggle).
isOpen([toggleName]) Return true if the Halo is open (applicable for the pie type of Halo). toggleName is the name of the pie toggle as defined in the pieToggles option. If toggleName is not passed, return true is any of the pie menus is open.
remove() Remove/destroy the Halo. Note that by default, the Halo removes itself when the user clicks on a blank area in the paper. In some cases, however, it is useful to remove the Halo programmatically.
joint.ui.Halo.clear(paper) Remove all the Halo panels (without having to have a reference to a particular halo object) from the paper. Note that this is a static function in the joint.ui.Halo namespace rather than a method of a halo object.

Events

The Halo object triggers events when the user manipulates its tools. These events can be handled by using the Halo on() method.

action:[name]:pointerdown Triggered when the user clicks (touches) on a tool handle named [name].
action:[name]:pointermove Triggered when the user moves with mouse cursor after a tool handle named [name] was mousedowned (touched).
action:[name]:pointerup Triggered when the user releases his mouse cursor after a tool handle named [name] was mousedowned (touched).
action:link:add

Triggered after the user finished creating a link with the default link tool. This is useful if you want to do something with the link after it has been created. For example, to prevent the user from creating loose links (links that do not have both source and target set), you can do:

halo.on('action:link:add', function(link) {
        if (!link.get('source').id || !link.get('target').id) {
            link.remove();
        }
    });
state:open Triggered when the user opens the Halo (applicable for the pie type of Halo). The handler is passed the pie toggle name as defined in pieToggles option.
state:close Triggered when the user closes the Halo (applicable for the pie type of Halo). The handler is passed the pie toggle name as defined in pieToggles option.

ui.Inspector

Inspector plugin creates an editor and viewer of a cell model properties. It creates a two-way data-binding between the cell model and a generated HTML form with input fields. These input fields can be defined in a declarative fashion using a plain JavaScript object. Not only visual attributes can be configured for editing in the inspector but also custom data that will be set on the cell model! This all makes the inspector an extremely flexible and useful widget for use in applications.

Install

Include joint.ui.inspector.css and joint.ui.inspector.js files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.inspector.css"/>
<script src="joint.ui.inspector.js"></script>

Create an inspector

You can create the Inspector instance as follows:

var inspector = new joint.ui.Inspector(options).render();
inspector.$el.appendTo($('body'))

Usually the applications display only one inspector at the time, which is used for various application models and differs in the configuration of input fields only. For this common use-case we've introduced a static helper - joint.ui.Inspector.create(). It makes sure the previous instance (if there is any) is properly removed, it creates a new one and renders it into the DOM. It also keeps track on open/closed groups and restore them based on the last used state. It can be used as follows:

joint.ui.Inspector.create('#inspector', options);

For more information about the create method visit Inspector Methods section.

paper.on('element:pointerdown', function(elementView) {
    // open the inspector when the user interacts with an element
    joint.ui.Inspector.create('#inspector', {
        cell: cellView.model,
        inputs: {
            attrs: {
                circle: {
                    fill: {
                        type: 'color-palette',
                        options: [
                            { content: '#FFFFFF' },
                            { content: '#FF0000' },
                            { content: '#00FF00' },
                            { content: '#0000FF' },
                            { content: '#000000' }
                        ],
                        label: 'Fill color',
                        group: 'presentation',
                        index: 1
                    },
                    stroke: {
                        type: 'color-palette',
                        options: [
                            { content: '#FFFFFF' },
                            { content: '#FF0000' },
                            { content: '#00FF00' },
                            { content: '#0000FF' },
                            { content: '#000000' }
                        ],
                        label: 'Outline color',
                        group: 'presentation',
                        index: 2
                    },
                    'stroke-width': {
                        type: 'range',
                        min: 0,
                        max: 50,
                        unit: 'px',
                        label: 'Outline thickness',
                        group: 'presentation',
                        index: 3
                    }
                },
                text: {
                    text: {
                        type: 'textarea',
                        label: 'Text',
                        group: 'text',
                        index: 1
                    },
                    'font-size': {
                        type: 'range',
                        min: 5,
                        max: 30,
                        label: 'Font size',
                        group: 'text',
                        index: 2
                    },
                    'font-family': {
                        type: 'select',
                        options: ['Arial', 'Times New Roman', 'Courier New'],
                        label: 'Font family',
                        group: 'text',
                        index: 3
                    }
                }
            }
        },
        groups: {
        presentation: {
            label: 'Presentation',
            index: 1
        },
        text: {
            label: 'Text',
            index: 2
        }
        }
    });
});

Configuration

There are two ways how to create an instance of the inspector. Either using joint.ui.Inspector.create(container, options) or directly create an instance with new joint.ui.Inspector(options). The inspector can be configured by the options object with the following properties:

cellView joint.dia.CellView an element or a link view [mandatory]
cell joint.dia.Cell an arbitrary Backbone model (including cells: elements and links) [mutually exclusive with cellView]. Use either cellView or cell. If you use cell, you can take advantage of the Inspector widget for an arbitrary Backbone model.
inputs object an object that mimics the structure of a cell model. Only instead of the final values, it contains an input type definition. See below for further explanation.
groups object an object that contains group identifiers as keys and group options as values. These groups define a grouping in the inspector panel. See below for further information.
live boolean tells the inspector whether it should update the cell properties as a reaction on the "onchange" event triggered on the form inputs. true by default. However, if you don't want the cell properties to update immediately when the user leaves the input fields but rather update the cell in bulk or as a reaction on user pressing an Update button somewhere in your application, set live to false and call inspector.updateCell() manually when you want to.
multiOpenGroups boolean true (the default) to allow the user to have multiple groups opened at the same time in the Inspector. Set to false for the classical accordion type of the inspector where only one group can be opened at a time.
renderFieldContent(options, path, value) function if defined, this function can return an HTML, DOM element or jQuery object that will be injected into the inspector in place of the field. In other words, this function allows you to define custom fields. options argument is the object specified in the inputs option of the inspector for a property on the path (/ separated). value is the value read from the associated cell at the time of rendering of the field (it also takes into account defaultValue and valueRegExp from the options object for this field).
getFieldValue(attribute, type) function if defined, this function can return an object of the form { value: [value read from a custom field] } or undefined. The value is the current value of a custom field and will be then set on the associated cell. This function is especially useful in combination with the renderFieldContent() function and tells the inspector how to read a value from a custom field. attribute is the DOM element container of the inspector field (i.e. the value returned from renderFieldContent() if used) and type is the type defined in the options object for this field.
stateKey(model) function

A function that should return some unique identifier for saving/restoring the state of the groups (ie. opened/closed). The function returns the element id by default i.e. each element has its own state.

e.g function(model) { return model.get('type'); } would store the state per element type. i.e. all basic.Rect would share the state.

storeGroupsState boolean

Save the group state when set to true. It stores the information which groups are opened and which are closed just before the inspector gets closed. Group state can be restored by the restoreGroupsState option when the inspector is created. It defaults to true.

Applicable only when used with the create method.
restoreGroupsState boolean

Restore the group state when set to true (if there is any state previously stored). It defaults to true.

Applicable only when used with the create method.

Options properties storeGroupsState / restoreGroupsState is applicable only if they are passed into the static create method. Otherwise you can use API methods storeGroupsState() and restoreGroupsState() for manipulating with the group states.

As it was mentioned, inputs is an object that mimics the properties structure of cell models. Only instead of the final values, it contains the input definition that tells the inspector how it should render the form input associated with the cell property. The options object of inputs can contain the following parameters:

type object

a type of the input [mandatory]. The supported types are:

number creates an HTML 5 number input field. Special properties are min and max.
text creates a text input field.
textarea creates a textarea.
content-editable creates a content editable div (resize automatically as user types)
range creates an HTML 5 range input field. Special properties are min, max, step and unit.
color creates an HTML 5 color input field.
select creates a select box. Special properties are options array that contains options for the select box.
toggle creates a toggle (checkbox).
list creates a widget for adding/removing items from an array.
object creates inputs for properties of an object.
label string Label for the form input.
group string Group the input belongs to.
index number Index of the input within its group.
defaultValue object a value that will be used in the input field in case the associated cell property is undefined.
valueRegExp object a regular expression used to extract (and set) a property value on a cell. Use with combination with defaultValue to make sure the inspector does not try to extract something from an undefined value.
options object

an array of options for a select type [specific for the "select" input type]. Can be defined by:

content ['option1', 'option2']
value/content [{ value: 'value1', content: 'option1' }, { value: 'value2', content: 'option2' }]
reference 'path' ( where cell property defined by 'path' is an array of options)
min number a minimum value of a range input [specific for the "range" input type].
max number a maximum value of a range input [specific for the "range" input type].
item object a definition of an item of a list. [specific for the "list" type]
properties object an object containing definitions of an object properties. [specific for the "object" type]
attrs object an object of the form [selector] : { [attributeName] : [attributeValue], ... }. This object allows you to set arbitrary HTML attributes on the generated HTML for this input. Useful if you want to mark certain input fields or store some content in them, for example for display in a tooltip.
when object an object containing conditions that determine if this input should be shown or hidden based on the values of other inputs. For more information see chapter expressions.
previewMode object true to enable preview mode on the widget. This option is only supported by the 'select-box', 'color-palette' and 'select-button-group' types.

In preview mode, when the user hovers over one of the items in the widget (e.g. being it a dropdown item in case of the 'select-box' type), the Inspector still changes the connected model but triggers the change with the dry flag set to true. You can then react on this by using:

myCell.on('change:myProperty', function(cell, change, opt) {
    if (opt.dry) {
        /* do something when in preview mode */
    } else {
        sendToDatabase(JSON.stringify(graph));
    }
})

This is useful, for example, if your application wants to reflect the values of the hovered items in the diagram but does not want to store the change to the database.

Note that the list and object types allows you to create inputs in the Inspector for an arbitrary nested objects. For example, If your cell contains an object as a property (cell.set('myobject', { first: 'John', last: 'Good' }))), you can instruct the inspector to use the object type for myobject property and then define types for each of the nested properties of that object:

var inspector = new joint.ui.Inspector({
    cellView: cellView,
    inputs: {
        myobject: {
            type: 'object',
            properties: {
                first: { type: 'text' },
                last: { type: 'text' }
            }
        }
    },
    groups: {}
});

Each group options object in the groups object can contain the following parameters:

label A label for the group. This label will be displayed as a header of the group section in the accordion-like inspector.
index An index of the group within other groups. Use this to put the groups in a certain order.
closed If set to true, the group will be closed by default.

Expressions

The inspector uses expressions to switch the visibility of its inputs (configurable through when parameter) based on the values of other inputs.

When an expression is evaluated to false (condition is not met) the input using this when clause will be hidden. Otherwise, this input will be shown.

Definition of Expressions

An expression when evaluated in a model context returns a boolean (true/false) based on certain model attributes. The expressions are defined recursively as follows.

  • { <primitive> : { <path> : <value> }} is an expression
  • { <unary-operator> : expression } is an expression
  • { <multiary-operator> : [expression-1, expression-2, .. , expression-n] } for n > 0 is an expression

where:

pathIs a string determining a property of the model (e.g 'attrs/text/text', 'property')
valueIs a number, string or an array (e.g 13, 'jointjs', [1,3,5])
primitive

Can be one of the following:

eqreturns true if <value> and the value under <path> equals (== operator)
equalreturns true if <value> and the value under <path> equals (using _.isEqual() internally)
nereturns true if <value> and the value under <path> don't equal
regexreturns true if regular expression made of <value> matches the value under <path>
textreturns true if <value> is a substring of the value under <path>
ltreturns true if the value under <path> is less than <value>
ltereturn true if the value under <path> is less than or equal to <value>
gt return true if the value under <path> is greater than <value>
gte returns true if the value under <path> is greater than or equal to <value>
inreturns true if <value> is an array and contains the value under <path>
ninreturns true if <value> is an array and doesn't contain the value under <path>

returns false otherwise.

unary_operator

Accepts exactly one expression

notreturns the negation of an expression
multiary_operator

Accepts at least one expression

and returns a conjunction of all the expressions (i.e. expr1 && exp2)
or returns a disjunction of all the expressions (i.e. expr1 || exp2)
nor returns the negation of a disjunction of all the expressions (i.e. !(expr1 || exp2))

Custom Operators in Expressions

As you can see above, the inspector provides a good list of useful primitive operators (eq, lt, in, ...). However, sometimes it is not enough and applications have special requirements on when fields in the inspector should be hidden/displayed based on other fields. To meet this requirements while still taking advantage of the inspector configurability through expressions, ui.Inspector provides a way to define your own custom operators. This can be done through the operators option on the inspector. The operators is an object of the form: { [operator]: function(cell, value, *arguments) }, where the function should return true if the operator condition is met, false otherwise. The cell argument is the cell associated with the inspector, value is the value of the referenced field and *arguments is anything you pass to the operator in its configuration.

For example, let's say you want to show an inspector field only when a value of another input field is longer (has more characters) than a value of a property set on the associated cell:

var inspector = new joint.ui.Inspector({
    cell: mycell,
    inputs: {
        title: { type: 'text' },
        description: { type: 'text', when: { longerThan: { 'title': 'titleThreshold' } } },
    },
    operators: {
        longerThan: function(cell, value, prop) {
            // prop === 'titleThreshold'
            return value.length > cell.prop(prop);
        }
    }
})

The example above displays the description field only when the title is longer than some threshold that we have stored in another property on the cell model, the titleThreshold property. Now whenever the user types a text into the title input field in the inspector and that text is longer than cell.get('titleThreshold'), the description field appears (and vice versa, if the text is shorter than titleThreshold, the description field gets hidden. This is great but there is a small problem with this. If the titleThreshold property changes on the cell model (e.g. due to some other change somewhere else in the application), than this change does not affect the visibility of the description field. In order to fix this, we have to tell the inspector that there are dependencies that could affect the resolution of the expression in the when clause. Here's a more correct example that does exactly that:

var inspector = new joint.ui.Inspector({
    cell: mycell,
    inputs: {
        title: { type: 'text' },
        description: {
            type: 'text',
            when: {
                longerThan: { 'title': 'titleThreshold' },
                dependencies: ['titleThreshold']
            }
        },
    },
    operators: {
        longerThan: function(cell, value, prop) {
            // prop === 'titleThreshold'
            return value.length > cell.prop(prop);
        }
    }
})

Examples on using Expressions

Here is an example of some expressions:

{ eq: { 'size/width': 300 }}
{ regex: { 'attrs/text/text' : 'JointJS|Rappid' }}
{ lt: { 'count': 10 }}
{ in: { 'index': [0,2,4] }}
{ not: { eq: { 'day': 'Monday' }}}
{ and: [{ gte: { 'position/x': 100 }}, { lte: { 'position/x': 400 }}]}

Imagine a scenario where you have a "select" input type with options "email" and "tel". Below this input field, you want to show either a text or a number input field based on the value of the select input. Assuming your cell properties structure is as follows: { contact_option: "email", contact_email: "", contact_tel: "" }, your inspector could look like this:

var inspector = new joint.ui.Inspector({
    cell: mycell,
    inputs: {
        contact_option: { type: 'select', options: ['email', 'tel'] },
        contact_email: { type: 'text', when: { eq: { 'contact_option': 'email' } } },
        contact_tel: { type: 'number', when: { eq: { 'contact_option': 'tel' } } }
    }
})

Custom Fields

The inspector has a useful built-in set of ready-to-use field types. However, in some cases, you might want to render your own custom fields (or integrate a third party widget) while still you'd like to take advantage of the two-way data binding and configuration that the inspector provides. For this, inspector provides a facility (through renderFieldContent(options, path, value) and getFieldValue(attribute, type) functions). The following example shows how to render two buttons in a single custom field and how to integrate the Select2 widget for advanced select boxes:

var inspector = new joint.ui.Inspector({
    cellView: cellView,
    inputs: {
        attrs: {
            text: {
                style: {
                    'text-decoration': { type: 'select2', group: 'text-decoration', options: ['none', 'underline', 'overline', 'line-through'] }
                },
                text: { type: 'my-button-set', group: 'text' }
            }
        }
    },
    groups: {
        'text-decoration': { label: 'Text Decoration' },
        'text': { label: 'Text' }
    },
    renderFieldContent: function(options, path, value) {

        if (path === 'attrs/text/text') {

            var $buttonSet = $('<div>').css('margin', 20);
            var $yes = $('<button>Say YES!</button>');
            var $no = $('<button>Say NO!</button>');
            $buttonSet.append([$yes, $no]);

            // When the user clicks one of the buttons, set the result to our field attribute
            // so that we can access it later in `getFieldValue()`.
            $yes.on('click', function() {
                $buttonSet.data('result', 'YES');
                inspector.updateCell();
            });
            $no.on('click', function() {
                $buttonSet.data('result', 'NO');
                inspector.updateCell();
            });
            return $buttonSet;
        }

        if (path === 'attrs/text/style/text-decoration') {

            var $select = $('<select></select>').width(170).hide();
            // select2 requires the element to be in the live DOM.
            // Therefore, postpone the select2 initialization for after we
            // add the Inspector container to the live DOM (see below).
            _.defer(function() {
                $select.show().select2({ data: options.options }).val(value || 'none').trigger('change');
                $select.data('select2').$container.css('margin', 20);
            });
            return $select;
        }
    },
    getFieldValue: function(attribute, type) {

        if (type === 'my-button-set') {
            return { value: $(attribute).data('result') };
        }
        // Note that for our select2 select, we do not need to write
        // a special value extraction code. This is because
        // Inspector will use the .val() method on the <select> input by default.
    }
});
inspector.render();
$('.inspector-container').html(inspector.el);

Inspector Events

The Inspector object triggers events when the user changes its inputs or when the Inspector needs to re-render itself partially. These events can be handled by using the Inspector on() method.

render Triggered when the Inspector re-renders itself partially. If you're adding event listeners or your own custom HTML into the inspector, you should always do it inside the render event handler.
change:[path to the changed property] Triggered when the user changes a property of a cell through the inspector. The handler is passed the value under the property path and the input DOM element as arguments.

API

static

create(container, options) A helper for creating the inspector, where container is an HTMLElement or a selector (container is a DOM placeholder where the Inspector will be rendered into). For more information about options please visit ui.Inspector.configuration section. It returns the joint.ui.Inspector instance.
close() A helper for closing the inspector created via create method above.

public

render() Render the inspector based on the options passed to the constructor function. Note that this does not add the inspector to the live DOM tree. This must be done manually by appending the inspector DOM element (stored in the el property) to a live DOM element on the page.
updateCell() Manually update the associated celll based on the current values in the inspector. This is especially useful if you use the inspector with the live mode disabled. See the Configuration section for more information.
remove() Remove the inspector from the DOM. This should be called once you're finished with using the inspector.
openGroup(name) Open group by name.
closeGroup(name) Close group by name.
toggleGroup(name) Toggle group by name.
openGroups() Open all groups.
closeGroups() Close all groups.
storeGroupsState() Save the current group state - information which groups are opened and which are closed. The key for storing the state is determined by the stateKey.
restoreGroupsState() Apply the stored state - open/close groups according this state. The actual state is looked up by the current stateKey.
getGroupsState() Get the current group state - array of closed groups.

ui.Keyboard

The ui.Keyboard is an essential plugin for creating keyboard shortcuts in your Rappid application. It allows you to bind custom handlers (functions) to arbitrary keyboard events (e.g a single keystroke, key combination).

API

on(event, callback, [context]) Bind a callback function to an object. The callback will be invoked whenever the keyboard event is fired. Same logic as Backbone.Events.on. More information about event could be found in the Event description section.
off(event, callback, [context]) Remove a previously-bound callback function from an object. Same logic as Backbone.Events.off. More information about event could be found in the Event description section.
enable() Start tracking keyboard events (enabled by default).
disable() Stop tracking keyboard events.
isActive(name, event) Check if key name is currently pressed. event is the DOM Event KeyboardEvent. It is available for modifiers only - alt, ctrl, shift, command

Event description

The shortcut can be defined as a string, either a single keystroke e.g. enter, esc, up, down, left or a combination like ctr+c, ctrl+alt+t etc. If you need to be more specific - in terms of keydown, keyup, keypress events - you can also define shortcut as keydown:a, keyup:enter or even keyup:ctrl+x.

Multiple shortcuts bound to a single handler can be defined as a list of shortcuts, separated by a space.

keyboard.on('ctrl+v shift+insert', pasteHandler);

Please note that all Backbone event methods also support an event map syntax.

keyboard.on({
    'enter': addNewHandler,
    'ctrl+enter' : createNewHandler,
    'up down left right': moveHandler
})

Demo

ui.Lightbox

ui.Lightbox is a UI component for displaying images by filling the screen and dimming out the rest of the application. ui.Lightbox component nicely auto-adjusts based on the size of the screen.

Installation

Include joint.ui.lightbox.js and joint.ui.lightbox.css files and joint.ui.dialog.js and joint.ui.dialog.css dependencies (ui.Lightbox inherits from ui.Dialog) to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.dialog.css">
<link rel="stylesheet" type="text/css" href="joint.ui.lightbox.css">
<script src="joint.ui.dialog.js"></script>
<script src="joint.ui.lightbox.js"></script>

Usage

Create an object of joint.ui.Lightbox type and call the open() method in order to open the lightbox.

var lightbox = new joint.ui.Lightbox({
    title: 'Image caption goes here.',
    image: 'images/lightbox.png'
});
lightbox.open();

Another common usage is opening images in lightbox on click:

$('img').on('click', function(evt) {
    new joint.ui.Lightbox({
        title: $(evt.target).attr('title'),
        image: $(evt.target).attr(src)
    }).open();
});

Configuration

The following table lists options that you can pass to the ui.Lightbox constructor function:

title The image caption.
image The path or URL to the image.
top The distance from the top edge of the image to the top of the screen. Default is 100.
windowArea The maximum percentage of the screen covered by lightbox. Default is 0.8.
closeAnimation Set to false if you don't want the lightbox to be closed in an animated fashion.
closeButton Set to false if you don't want to display the small close button in the top left corner of the lightbox. Note that the lightbox will still be closeable by clicking anywhere outside the lightbox area.

API

open() Open the lightbox.
close() Close the lightbox.
on(event, handler [, context]) Register a handler function that will be called whenever event is triggered. The optional context is an object that will be the context of the handler function (the this).

Events

The ui.Lightbox object triggers various events that you can react on. Events can be handled by using the on() method (see above).

close:animation:complete Triggered when the lightbox is actually removed from the DOM, i.e. when the close animation finishes.

ui.Navigator

ui.Navigator is a UI widget that displays a smaller view into a larger diagram. It's a quick way for users to navigate in diagrams with pannable and resizable rectangle.

Installation

Include joint.ui.navigator.js and joint.ui.navigator.css files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.navigator.css">
<script src="joint.ui.navigator.js"></script>

Usage

Simply create an object of ui.Navigator type, append the navigator HTML DOM element to any container and call the render() method. The navigator requires a ui.PaperScroller object as a parameter.

var nav = new joint.ui.Navigator({
    paperScroller: paperScroller,
    width: 300,
    height: 200,
    padding: 10,
    zoomOptions: { max: 2, min: 0.2 }
});
nav.$el.appendTo('#navigator');
nav.render();

Configuration

The following table lists options that you can pass to the ui.Navigator constructor function:

paperScroller The paper scroller. See ui.PaperScroller plugin for more details.
width The width of the navigator view.
height The height of the navigator view.
padding The initial padding between the small paper inside the navigator and the navigator view rectangle edges.
zoomOptions An object with parameters controlling the zoom functions. zoomOptions.min and zoomOptions.max determine the minimum and the maximum zoom allowed when zooming via dragging the rectangle corner inside the navigator.
paperConstructor The type of the paper. Defaults to joint.dia.Paper. This is only useful if you use a different paper type for rendering your graphics. In case you inherit from joint.dia.Paper to implement some specific rendering, you can just pass your constructor function to this parameter and use the paperOptions parameter to pass additional options to your special paper.
paperOptions Options object that will be passed to the internal paper used for rendering graphics.

API

render() Render the navigator. You should call this method after you have appended the navigator view root HTML element to your container. See the Usage section.
remove() Remove the navigator and clean up all its registered event handlers. Call this once you do not need the navigator anymore.

ui.PaperScroller

Paper scroller wraps the paper element and implements scrolling, panning, centering and auto-resizing of the paper content.

Installation

Include joint.ui.paperScroller.js and joint.ui.paperScroller.css files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.paperScroller.css">
<script src="joint.ui.paperScroller.js"></script>

Create a PaperScroller

PaperScroller creates a window to the paper area. This way, the actual paper can be large but the user will see only the PaperScroller area which he can scroll & pan. First, we create a PaperScroller object. Then we pass the automatically created PaperScroller element as a container for our paper. Once we have our paper object created, we pass it back to the PaperScroller as it needs a reference to the actual paper.

var paper = new joint.dia.Paper({
    width: 2000,
    height: 2000,
    model: graph
});

var paperScroller = new joint.ui.PaperScroller({
    paper: paper
});

$('#paper-container').append(paperScroller.render().el);

The next step is to hook the startPanning method of the paper scroller on the blank:pointerdown paper event which will cause panning whenever the user drags a blank area of the paper:

paper.on('blank:pointerdown', paperScroller.startPanning);

In order to help users understand what action will happen when they drag the paperScroller, use setCursor(cursor) method. It sets the mouse cursor, which is displayed when the mouse pointer is over the PaperScroller. It accepts any valid CSS cursor property value. Alternatively the cursor can be passed to the constructor. It defaults to 'default' (an arrow).

var paperScroller = new joint.ui.PaperScroller({
    paper: paper,
    cursor: 'grab'
});
// Or dynamically change cursor e.g. when user is about to select elements.
paperScroller.setCursor('crosshair');

Centering the paper content

If desired, the PaperScroller can be centered so that the window through which the user sees the actual paper is in the middle of the, possibly large, paper area. This can be achieved by calling the center() method of the PaperScroller:

paperScroller.center();

Method center() can be also called with parameters, which make the window center to the point (x,y) on the paper area.

paperScroller.center(x,y);
To center the window to the middle of the content (i.e. viewport) use centerContent().
paperScroller.centerContent();

Scrolling the paper

ui.PaperScroller provides methods for programmatic scrolling. These are:

scroll(x, y [, opt])Scroll the paper scroller to the position specified by x and y coordinates. If opt.animation is passed, the scroll will be animated. The opt.animation is an object of the form { duration: [Number] } where duration is the time in milliseconds determining how long the animation will run.
scrollToElement(element [, opt])Scroll the paper scroller to the position that is determined by the center of the element (an instance of joint.dia.Element). If opt.animation is passed, the scroll will be animated. The opt.animation is an object of the form { duration: [Number] } where duration is the time in milliseconds determining how long the animation will run.
centerElement(element)Scroll the paper scroller to the position that is determined by the center of the element (an instance of joint.dia.Element). The difference from scrollToElement() is that this method always puts the element in the center extending the paper with paddings if needed.

Paper visiblity

ui.PaperScroller provides methods for checking whether elements or points are visible and not scrolled outside the viewport.

getVisibleArea() Returns the size and origin of the current paperScroller viewport. The returned value is a g.Rect object, which contains x, y, width and height describing the visible area of the paper ignoring paper transformations (as would no scale, translate or rotate be applied).
// return elements currenly visible in the paperScroller viewport.
var visibleElements = graph.findModelsInArea(paperScroller.getVisibleArea());
isElementVisible(element[, opt]) Returns true if the element is visible in the current paperScroller viewport. It returns false otherwise. Use { strict: [Boolean] } option for toggling complete true vs. partial false visibility. It's non-strict by default.
// Scroll to an element only if it is not completely in the viewport.
if (!paperScroller.isElementVisible(rect, { strict: true })) {
    paperScroller.scrollToElement(rect);
}
isPointVisible(point) Returns true if the point (e.g. { x: 100, y: 200 }) is visible in the current paperScroller viewport. It returns false otherwise.

Zooming the paper

The PaperScroller exposes an API to scale the paper inside more easier. Method zoom() accepts a value we want the paper to be zoomed by. Optionally we can specify maximum ({ max: [value] }) and minimum ({ min: [value] }) value, a grid ({ grid: [value] }) the resulting value will be rounded to and whether the input value should be considered as a value we want to zoom the PaperScroller to ({ absolute: true } ) by passing those in the opt parameter.

paperScroller.zoom(value, [opt]);

Setting the certain options can be useful for example when we binding buttons to perform a zooming.

$('#zoom-in').on('click', function() {
   paperScroller.zoom(0.2, { max: 4 });
});

$('#zoom-out').on('click', function() {
   paperScroller.zoom(-0.2, { min: 0.2 });
});

Sometimes we want to scale the content of the paper so it fits the window through which the user sees the actual paper. For this purposes the PaperScroller implements zoomToFit() method. For the available options please see paper.scaleContentToFit() method as it is being used in the body of this method.

paperScroller.zoomToFit([opt]);

Paper auto-resize/shrink

The PaperScroller constructor takes an optional parameter autoResizePaper. When this parameter is set to true, the PaperScroller automatically resizes the paper so that it fits the content inside it. This makes it possible to have a fixed (even smaller) size of the paper and when the user drags an element outside this area, the PaperScroller extends this paper in order to accommodate for the new element. By default, the ui.PaperScroller resizes the paper by the paper width/height. If you want the paper to extend by a different amount, pass the baseWidth/baseHeight options to the ui.PaperScroller constructor function. The paper adjustment is internally handled by calculating proper parameters for the joint.dia.Paper:fitToContent method. You can further adjust the behaviour by setting the contentOptions object to the ui.PaperScroller constructor function. This object can contain additional parameters that will be mixed with the calculated ones before calling the joint.dia.Paper:fitToContent method. This is handy if you, for example, want to set the maximum width and height of the paper. In this case, you can just set contentOptions: { maxWidth: 3000, maxHeight: 3000 } to the ui.PaperScroller constructor function.

Try how this works in the following demo by zooming the paper out so that you see its borders and move your element outside the paper area. You should see how the paper gets automatically resized. When you drag the element back to its original location, the PaperScroller resizes the paper back to its original size.

var paperScroller = new joint.ui.PaperScroller({ autoResizePaper: true });

ui.Popup

ui.Popup is a UI widget for displaying contextual information (or even other diagrams) below a certain target element (HTML or SVG). There can always be only one popup opened at a time. This is automatically handled by the ui.Popup component which simplifies the process and makes sure you don't have to keep track of opened popups yourself. This component is very similar to the ui.ContextToolbar component (in fact, it inherits from it) with the only difference that ui.Popup does not contain a set of buttons but an arbitrary HTML (which might even contain another diagram).

Installation

Include joint.ui.popup.js and joint.ui.popup.css files and the joint.ui.contextToolbar.js and joint.ui.contextToolbar.css dependencies to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.contextToolbar.css">
<link rel="stylesheet" type="text/css" href="joint.ui.popup.css">
<script src="joint.ui.contextToolbar.js"></script>
<script src="joint.ui.popup.js"></script>

Usage

Create an object of joint.ui.Popup type, specify content and call the render() method in order to open the popup.

(new joint.ui.Popup({
    content: 'I am a ui.Popup',
    target: this
})).render();

A common usage is to open the popup on click on a certain element (either HTML or SVG) and display either an image or another diagram:

$('#btn-open-image').on('click', function() {
    (new joint.ui.Popup({
        content: '',
        target: this
    })).render();
});

$('#btn-open-diagram').on('click', function() {
    (new joint.ui.Popup({
        content: function(el) {
            var graph = new joint.dia.Graph;
            var paper = new joint.dia.Paper({
                width: 200,
                height: 100,
                gridSize: 1,
                model: graph
            });
            $(el).append(paper.el);
            var r1 = new joint.shapes.basic.Rect({ position: { x: 10, y: 10 }, size: { width: 50, height: 30 }, attrs: { text: { text: 'r1' }, rect: { fill: '#FE854F' } } });
            var r2 = new joint.shapes.basic.Rect({ position: { x: 90, y: 40 }, size: { width: 50, height: 30 }, attrs: { text: { text: 'r2' }, rect: { fill: '#7C68FC' } } });
            var l = new joint.dia.Link({ source: { id: r1.id }, target: { id: r2.id } });
            graph.addCells([r1, r2, l]);
        },
        target: this
    })).render();
});

$('circle').on('click', function() {
    var popup = new joint.ui.Popup({
        events: {
            'click .btn-cancel': 'remove',
            'click .btn-change': function() {
                var strokeWidth = parseInt(this.$('.inp-stroke-width').val(), 10);
                var fill = this.$('.inp-fill').val();
                V(this.options.target).attr({ fill: fill, 'stroke-width': strokeWidth });
            }
        },
        content: [
            '<div>',
            'Fill: <input class="inp-fill" type="color" value="#FEB663"> <br><br>',
            'Stroke width: <input class="inp-stroke-width" type="number" value="5"> <br><br>',
            '<button class="btn-cancel">Cancel</button>',
            '<button class="btn-change">Change</button>',
            '</div>'
        ].join(''),
        target: this
    });
});

Configuration

The following table lists options that you can pass to the ui.Popup constructor function:

content The content of the popup. It can be a text or an arbitrary HTML.
target The target element (either HTML or SVG).
padding The gap between the target element an the popup. Default is 20.
autoClose Determines if the popup should be closed if the user clicked outside of the area of the popup. Default is true.

API

render() Render the popup onto the screen.
remove() Remove the popup. Usually, you don't have to call this method as the popup is closed automatically if the user clicked outside of the area of the popup (unless you set the autoClose option to false).
on(event, handler [, context]) Register a handler function that will be called whenever event is triggered. The optional context is an object that will be the context of the handler function (the this).
joint.ui.Popup.close() This is a static method that closes any poup that is currently open.
joint.ui.Popup.update() This is a static method that updates the position of the opened popup (if any). Call this method if you target element changes its position while you still want to keep the popup opened.

ui.SelectBox

ui.SelectBox is a UI widget for displaying dropdowns. Items in the dropdown can contain arbitrary HTML. Because items in dropdowns often contain images, ui.SelectBox provides an easy way to add icons to your dropdown items. The ui.SelectoBox also provides different open policies and support for keyboard navigation.

Installation

Include joint.ui.selectBox.js and joint.ui.selectBox.css files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.selectBox.css">
<script src="joint.ui.selectBox.js"></script>

Usage

Create an object of ui.SelectBox type, render it with the render() method and append the generated HTML element anywhere in your DOM:

var selectBox = new joint.ui.SelectBox({
        width: 200,
        options: [
            { content: 'Amsterdam' },
            { content: 'Prague', selected: true },
            { content: 'London' },
            { content: 'San Francisco' },
            { content: 'New York' }
        ]
    });

    document.body.appendChild(selectBox.render().el);

ui.SelectBox with icons

var selectBox = new joint.ui.SelectBox({
        width: 250,
        options: [
            { icon: '/path/to/some-image.png', content: 'Some text content', selected: true },
            { icon: '/path/to/some-other-image.png', content: 'Some other text' }
        ]
    });

    document.body.appendChild(selectBox.render().el);

Configuration

The following table lists options that you can pass to the ui.SelectBox constructor function:

width The width of the dropdown in pixels.
options An array of items. Each item is an object with the following properties:
content The content of the item. It can be either a string or an HTML.
value The value of the item. It can be a string, a number or any other JavaScript object. If the value is not defined, the value is considered to be equal to the content property.
icon A path to an image. The icon is prepended to the item and is given the select-box-option-icon CSS class.
selected true if the item should be selected by default. If non of the items are selected, the first item is selected by default.
selected The index of the item which should be selected by default. If this value is undefined (which it is by default), the ui.SelectBox widget looks up the selected item from the options array (by using the selected boolean property). If nothing is selected, the first item in the options array is selected by default.
keyboardNavigation true (the default) if you want the ui.SelectBox to provide a keyboard navigation (up/down/left/right/enter/escape keys are supported).
selectBoxOptionsClass When the dropdown is opened, the ui.SelectBox generates an HTML container that contains all the items. This container is then appended to the document body (or target if provided). Sometimes, you want to provide custom styling to this dropdown container. selectBoxOptionsClass gives you a chance to define a CSS class that will be set on this container so that you can style it in your CSS.
target ui.SelectBox generates an HTML container that contains all the dropdown items. This container is by default appended to the document body. In some situations (e.g. if you want the dropdown to scroll with some other container), you want to append this container to a different DOM element. The target option is exactly for that. It accepts an HTML element that will be used as a container for the generated dropdown items.
openPolicy openPolicy determines how the dropdown will open when clicked. It can be one of 'auto', 'above', 'coverAbove', 'below', 'coverBelow' and 'selected'.
placeholder In some cases, you want to deselect the dropdown and show a placeholder in place of the selected item. This is when you don't want any of the items to be selected. In this case, set the selected index to -1 and the placeholder to any HTML you want to display in the selected item window. This placeholder has the selected-box-placeholder CSS class that you can use for further styling.

API

render() Render the dropdown. Note that once you render the dropdown, you can use the el property that points to the container HTML element and append it anywhere in the DOM (e.g. $(document.body).append(selectBox.el)).
getSelection() Get the current selection. The selection points to one of the items from the options array.
getSelectionValue() Get the current selection value.
getSelectionIndex() Get the index in the options array of the item that is currently selected.
select(index, opt) Select an item. index is the index to the options array. opt is optional and can be an arbitrary object that will be passed to the option:select event handler.
selectByValue(value) Select an item by value. The selected item will be chosen based on a strict equality of the value passed an a value (content) of any of the items.
isOpen() Returns true if the dropdown is currently opened. false otherwise.
toggle() Programmatically toggle the dropdown.
open() Programmatically open the dropdown.
close() Programmatically close the dropdown.
remove() Remove the dropdown from the DOM.
disable() Disable the dropdown.
enable() Enable the dropdown.
isDisabled() Return true if the dropdown is disabled.
on(event, handler [, context]) Register a handler function that will be called whenever event is triggered. The optional context is an object that will be the context of the handler function (the this).

Events

The ui.SelectBox object triggers various events that you can react on. Events can be handled by using the on() method (see above).

option:hover Triggered when the user hovers over one of the items. The handler is passed the option object and the index of the option in the options array.
option:select Triggered when the user selects an item. The handler is passed the option object and the index of the option in the options array. If you selected the item with the select(index, opt) method, the third argument to the event handler will be your opt object.
option:mouseout Triggered when the user leaves an item with mouse cursor. The handler is passed an event object as the only argument.
close Triggered when the dropdown gets closed.

ui.SelectButtonGroup

ui.SelectButtonGroup is a UI widget for displaying groups of buttons with both single and multi selection support. The buttons can contain arbitrary HTML.

Installation

Include joint.ui.selectButtonGroup.js and joint.ui.selectButtonGroup.css files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.selectButtonGroup.css">
<script src="joint.ui.selectButtonGroup.js"></script>

Usage

Create an object of ui.SelectButtonGroup type, render it with the render() method and append the generated HTML element anywhere in your DOM:

var buttonGroup = new joint.ui.SelectButtonGroup({
    multi: true,
    selected: [1, 3],
    options: [
        { value: 'line-through', content: 'S' },
        { value: 'underline', content: 'U' },
        { value: 'italic', content: 'I' },
        { value: 'bold', content: 'B' }
    ]
});
document.body.appendChild(buttonGroup.render().el);

More examples

ui.SelectButtonGroup with icons

Configuration

The following table lists options that you can pass to the ui.SelectButtonGroup constructor function:

width The width of the whole button group in pixels.
options An array of items (the buttons). Each item is an object with the following properties:
content The content of the item. It can be either a string or an HTML.
value The value of the item. It can be a string, a number or any other JavaScript object. If the value is not defined, the value is considered to be equal to the content property.
icon A path to an image that represents the button.
iconSelected A path to an image that represents the selected state of the button. If iconSelected is not defined, the selected state is the same as icon.
selected true if the item should be selected by default.
buttonWidth The width of the button in pixels.
buttonHeight The height of the button in pixels.
iconWidth The width of the icon (if it is used) in pixels.
iconHeight The height of the icon (if it is used) in pixels.
selected The index (or array of indices if multi is true) of the item which should be selected by default. If this value is undefined (which it is by default), the ui.SelectButtonGroup widget looks up the selected item(s) from the options array (by using the selected boolean property).
multi Set to true to enable multiple selection (multiple buttons can be selected at the same time). multi is false by default.
buttonWidth The width of the buttons in pixels. This can be overridden by buttonWidth set for an item in the options array.
buttonHeight The height of the buttons in pixels. This can be overridden by buttonHeight set for an item in the options array.
iconWidth The width of the icons (if they are used) in pixels. This can be overridden by iconWidth set for an item in the options array (only for items that have the icon option specified).
iconHeight The height of the icons (if they are used) in pixels. This can be overridden by iconHeight set for an item in the options array (only for items that have the icon option specified).

API

render() Render the button group. Note that once you render the button group, you can use the el property that points to the container HTML element and append it anywhere in the DOM (e.g. $(document.body).append(selectButtonGroup.el)).
getSelection() Get the current selection. The selection points to an item(s) from the options array.
getSelectionValue() Get the current selection value(s).
select(index, opt) Select an item. index is the index to the options array. opt is optional and can be an arbitrary object that will be passed to the option:select event handler.
selectByValue(value) Select an item by value. The selected item will be chosen based on a strict equality of the value passed an a value (content) of any of the items.
deselect() Deselect all selected items.
remove() Remove the button group from the DOM.
on(event, handler [, context]) Register a handler function that will be called whenever event is triggered. The optional context is an object that will be the context of the handler function (the this).

Events

The ui.SelectButtonGroup object triggers various events that you can react on. Events can be handled by using the on() method (see above).

option:hover Triggered when the user hovers over one of the items. The handler is passed the option object and the index of the option in the options array.
option:select Triggered when the user selects an item. The handler is passed the option object and the index of the option in the options array. If you selected the item with the select(index, opt) method, the third argument to the event handler will be your opt object.
mouseout Triggered when the user leaves the entiner button group with mouse cursor. The handler is passed an event object as the only argument.

ui.Selection

Selection implements elements selection, moving the selection in one go and manipulating the selection in terms of selecting/deselecting individual elements.

Installation

Include joint.ui.selection.js and joint.ui.selection.css files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.selection.css">
<script src="joint.ui.selection.js"></script>

Initialize Selection

The Selection internally stores selected cells in a collection. This is a normal Backbone Collection. It can be accessed via collection attribute. This view takes care of rendering the bounding rectangle during the selection action, determining which elements fall into this rectangle and also provides methods for selecting/deselecting individual elements. The view also listens to the underlying collection and updates selection boxes when an element is removed/added/reset i.e. selection.collection.add(element); finds the view for the element on the paper and render a selection box above it.


 var selection = new joint.ui.Selection({
    paper: paper,
    collection: new MyCollection // optional, a custom collection that inherits from Backbone.Collection
});

The next step is to hook the selection actions to the relevant events triggered on the paper and selection:

// Initiate selecting when the user grabs the blank area of the paper.
paper.on('blank:pointerdown', selection.startSelecting);

// Select an element if CTRL/Meta key is pressed while the element is clicked.
paper.on(element:pointerup', function(cellView, evt) {
    if (evt.ctrlKey || evt.metaKey) {
        selection.collection.add(cellView.model);
    }
});

// Unselect an element if the CTRL/Meta key is pressed while a selected element is clicked.
selection.on('selection-box:pointerdown', function(elementView, evt) {
    if (evt.ctrlKey || evt.metaKey) {
        this.selection.collection.remove(elementView.model);
    }
});

Note that the selection-box:pointerdown event is triggered on the selection view when the mouse cursor is pressed above a selected element.

Selection Collection

As our selection collection is just a normal Backbone Collection, we can take advantage of the Backbone methods. For instance:

To select an element.

selection.collection.add(element);
selection.collection.add(element, { silent: true}); // add element to the collection, but renders no selection box.

To deselect an element.

selection.collection.remove(element);

To deselect all elements.

selection.collection.reset([]);

To select multiple elements and deselect all the other elements.

selection.collection.reset([element1, element2]);

To identify elements that are currently in the selection.

selection.collection.on('reset add', function() {
    // Print types of all the elements in the selection.
    console.log(selection.collection.pluck('type'));
});

Customizing the look of Selection

The bounding rectangle of the active selection, the rectangles above the selected elements and the rectangle around all the elements once the selection is made can all be styled in CSS. The selection bounding rectangle is a <div> element with the .joint-selection class. The rectangles above the selected elements are also <div> elements having the .selection-box class and the final rectangle surrounding all the selected element is a <div> with class .selection-wrapper. An example of a custom styling might look like the following:

.joint-selection {
   background-color: red;
   border: 2px dashed red;
   opacity: .7;
}
.selection-box {
   border: 1px solid #8e44ad;
   margin-top: -4px;
   margin-left: -4px;
   box-shadow: 2px 2px 5px #8e44ad;
}
.selection-wrapper {
   border: 1px dotted #8e44ad;
}

Disabling Selection tools

As you might have noticed, each selection is surrounded by a rectangle and offers three built-in icon tools by default: remove, rotate and resize. To disable any of these tools you may add a line similar to the following to your css:

.joint-selection .handle.rotate { display: none; } /* disables the rotate tool */

Another way to remove tool handles is via JavaScript:

selection.removeHandle('rotate');

To quickly disable all the tools (hide them) and also to hide the rectangular box around all the selected elements, you can simply do:

.selection-wrapper { display: none; }

Customizing Selection tools

Selection provides three methods for adding, removing and changing custom tools: addHandle(), removeHandle() and changeHandle(). Use the addHandle() method to add new tools to your selection:

selection.addHandle({ name: 'myaction', position: 's', icon: 'myaction.png' });
selection.on('action:myaction:pointerdown', function(evt) {
    evt.stopPropagation();
    alert('My custom action.');
});

In the example above, we added a new tool named myaction, positioned the tool to the south (bottom-center) and used our own icon myaction.png. When the user clicks on our tool, Selection triggers an event named action:[name]:pointerdown. We can handle the event by listening on the Selection object. Similarly, the Selection triggers action:[name]:pointermove and action:[name]:pointerup events. This gives us a high flexibility in implementing our own actions. You might have noticed that this API is exactly the same as in the ui.Halo plugin. The difference is that in Selection, we can have actions that manipulate multiple elements in one go.

For removing a tool, use the removeHandle(name) method:

selection.removeHandle('myaction')

changeHandle() method allows us to change tools. For instance, if we want to change a position of the remove tool, we could use:

selection.changeHandle('remove', { position: 'ne' })

Selection Configuration

paper (required) The JointJS paper the Selection should operate on.
graph The JointJS graph the Selection should operate on. If no graph is passed paper.model is used.
collection A Backbone Collection object used to store selected cells. If no collection is passed new Backbone.Collection is used.
filter Either an array in which case it can contain (even intermixed) cells or cell types that will be ignored by the selection. This is useful if you have certain elements in the diagram that are not supposed to get selected. It can also be function in which case it will be called with a cell as an argument and should return true if that cell should be filtered out of the selection. The function gives you more flexibility in deciding whether an element can or cannot be selected. Example:
filter: ['basic.Rect', 'mymodule.HiddenShape']
boxContent(cellView, boxDOMElement) A function that returns an HTML string with the content that will be used in the information box below the selection. By default, the box shows the number of selected elements.
useModelGeometry If set to true, the cells position and dimensions will be used as a basis for the Selection wrapping rectangle position. By default, this is set to false which causes the Selection wrapping rectangle position be based on the bounding box of the selected element views. Sometimes though, your shapes can have certain SVG sub elements that stick out of the view and you don't want these sub elements to affect the Selection wrapping rectangle position. In this case, set the useModelGeometry to true.
strictSelection If set to true, only elements that are fully contained in the selection rectangle will be selected as opposed to the default behaviour that selects elements whose bounding box intersects the selection rectangle.
allowTranslate If set to false, users are not allowed to move selected elements around by dragging selection boxes. It defaults to true.
rotateAngleGrid When selected elements are rotated the resulting angles are snapped to this value. It defaults to 15 degrees.
handles A array of objects defining the tools around the selection. This array defaults to:
[
     { name: 'remove', position: 'nw', events: {
         pointerdown: 'removeElements'
     } },
     { name: 'rotate', position: 'sw', events: {
         pointerdown: 'startRotating',
         pointermove: 'doRotate',
         pointerup: 'stopBatch' }
     }
]
Normally, you do not need to set this array. It's better to use the addHandle(), removeHandle() and changeHandle() methods described below in the API section.

Selection API

addHandle(opt) Add a custom tool to the Selection. opt.name is the name of the custom tool. This name will be also set as a CSS class to the handle DOM element making it easy to select it your CSS stylesheet. opt.position is a string that specifies a position of the tool handle. Possible values are n, nw, w, sw, s, se, e and ne. opt.icon is a URL of the icon used to render the tool. This icons is set as a background image on the tool handle DOM element.
removeHandle(name) Remove a tool handle named name from the Selection.
changeHandle(name, opt) Change a tool handle named name in the Selection. The opt object can contain the same parameters as with the addHandle() method except of the name property. opt parameters will be merged with those defined previously.
on(event, callback) Register a handler (callback) for an event See the Selection Events section for the list of events the Selection object triggers.
startSelecting(evt) Initiate bulk selection. See below in the Common Setup section how that can be used.

Selection Events

The Selection object triggers events when the user manipulates its tools. These events can be handled by using the Selection on() method.

action:[name]:pointerdown Triggered when the user clicks (touches) on a tool handle named [name].
action:[name]:pointermove Triggered when the user moves with mouse cursor after a tool handle named [name] was mousedowned (touched).
action:[name]:pointerup Triggered when the user releases his mouse cursor after a tool handle named [name] was mousedowned (touched).
selection-box:pointerdown Triggered when the user clicks on a selected element, an element that has a selection box above it. The hanlder gets passed the element view and the DOM event object. Use elementView.model if you wish to access the actual element model. See below in the Common Setup section how that can be used.

Common Setup of the Selection in Applications Explained

The following is a common setup of the Selection in applications.

Cherry picking elements

For cherry picking elements - when the user clicks on an element holding the [CTRL] key, we register a handler for the cell:pointerup event on the paper, which is triggered when the user releases his mouse above a cell (either element or a link - links get filtered out in our case). In this handler, we add the element to our selection collection and create a selection box around that element.

paper.on(element:pointerup', function(elementView, evt) {
    if (evt.ctrlKey || evt.metaKey) {
        selection.collection.add(elementView.model);
    }
});

Releasing selection on cherry-picked elements

To implement the reverse action of cherry-picking, i.e. when the user clicks a selected element while holding the [CTRL] key, we register a handler for the selection-box:pointerdown event on the Selection (see Selection events for reference). In this handler, we remove the element from our selection collection and destroy the selection box.

selection.on('selection-box:pointerdown', function(elementView, evt) {
    if (evt.ctrlKey || evt.metaKey) {
        selection.collection.remove(elementView.model);
    }
});

Initiating bulk selection

Last thing we want to setup is the bulk selection that should happen when the user drags a blank area in the paper:

paper.on('blank:pointerdown', selection.startSelecting);

TIP: some applications might not want the user to be able to create selections when dragging a blank area in the paper. This is because they might have a different action for this, let's say panning the paper. In that case, you can, for example, start bulk selection only when the [SHIFT] key is being hold:

paper.on('blank:pointerdown', function(evt) {
    if (evt.shiftKey) selection.startSelecting(evt);
});

ui.Snaplines

Snaplines (a.k.a Guidelines) are great for guiding the user in aligning elements in your application. Snaplines are vertical and horizontal lines that appear when the user is dragging an element that is vertically or horizontally close enough to some other element.

Installation

Include joint.ui.snaplines.css and joint.ui.snaplines.js files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.snaplines.css">
<script src="joint.ui.snaplines.js"></script>

Usage

Enabling snaplines is as easy as creating a joint.ui.Snaplines object and calling startListening() method on that object:

var snaplines = new joint.ui.Snaplines({ paper: paper })
snaplines.startListening()

Demo

Configuration

The joint.ui.Snaplines constructor function takes the following parameters:

  • paper - The JointJS paper (joint.dia.Paper) object.
  • distance - The distance in pixels between edges (center) of other elements for snapping applies. The default is 10.
  • filter - filter allows you to filter out elements taken into account for snapping. It can either be an array in which case the array is considered to contain cell types (or elements directly, they can even be intermixed) that should be filtered out from snapping. Or it can be a function with the following signature: function(element) in which case the function should return true if the element should not be taken into account for snapping.

Styling Snaplines

Snaplines can be customized in CSS. Snapline is a <div> HTML element that has the snapline CSS class set. Moreover, you can customize horizontal and vertical snaplines differently by using the horizontal and vertical CSS class:

.snapline.horizontal {
    border-bottom: 2px dashed red;
    box-shadow: 0 0 2px black;
}
.snapline.vertical {
    border-right: 2px dotted blue;
    box-shadow: 0 0 2px black;
}

ui.Stencil

Stencil plugin implements a palette of JointJS elements. These elements can be dragged onto a paper.

Installation

Include joint.ui.stencil.js and joint.ui.stencil.css to your HTML:

<link href="joint.ui.stencil.css" rel="stylesheet" type="text/css">
<script src="joint.ui.stencil.js"></script>

Creating a stencil

var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({
    el: $('#paper'),
    width: 500,
    height: 300,
    model: graph
});

var stencil = new joint.ui.Stencil({
    paper: paper,
    width: 200,
    height: 300
});

$('#stencil-holder').append(stencil.render().el);

The joint.ui.Stencil constructor takes a paper object and the dimensions of the stencil area. The next thing to do is rendering the stencil widget (stencil.render()) and appending the stencil element to a holder which can be any element in your HTML. Please see the demo for reference on how to do that if you're in doubts.

Stencil Configuration

The joint.ui.Stencil constructor function takes an options object as an argument. The options object can have the following parameters:

paperthe joint.dia.Paper or joint.ui.PaperScroller object [mandatory]
widththe width of the stencil [mandatory]
heightthe height of the stencil [mandatory]
searchAn object defining what properties of the elements in the stencil will be used for searching. The object has element types as keys (or * wildcard denoting any type) and arrays with element property paths as values. The current element values under those property paths will be used by stencil to find a match between the search term and those values.
search: {
    '*': ['attrs/text/text'],
    'basic.Image': ['description'],
    'basic.Path': ['description']
}
See the Searchable Stencil section for more information.
groupsAn object that defines the groups in the stencil (if any). The keys of this object are strings that uniquely identify the groups and values are objects that contain label, index and closed properties. label property specifies the name of the group that will be displayed in the UI, index is a number specifying the position of the group among other groups within the stencil and the optional closed property tells stencil whether this group should be closed or opened by default. An example of this object can look like:
groups: {
    basic: { label: 'Basic Elements', index: 1 },
    text: { label: 'Text', index: 2, closed: true },
    advanced: { label: 'Advanced Elements', index: 3, closed: true }
}
groupsToggleButtonsIf set to true buttons for expanding/collapsing groups (expand all/collapse all) are rendered into the stencil. It defaults to false.
paperPaddingA number specifying the padding that will be used in the internal papers that hold the elements in the stencil. It defaults to 10.
dropAnimationAn object defining an animation that will be performed on an element that is dropped outside the target paper area. It defaults to undefined meaning that there will be no animation. The dropAnimation object can have duration and easing properties where duration is the duration of the animation in milliseconds (defaults to 150) and easing is either "swing" (the default) or "linear". Example:
dropAnimation: {
    duration: 300,
    easing: 'linear'
}
labelA string or HTML Element rendered at the top of the stencil. It defaults to `Stencil`.
layoutif set to true the stencil elements are automatically layed out using the Grid Layout plugin. To adjust the layout behaviour pass an object defining the layout configuration. For all available options see the aforementioned plugin. The option is valid only if the default layoutGroup option is used.
layout: {
    columnWidth: 100,
    columns: 3,
    rowHeight: 100,
}
layoutGroup(graph, group)a function that actually lays the elements out. It accepts a graph (1st parameter) and the group (2nd) that the graph elements belongs to. The function is called for each group on initialization and after the stencil is filtered. Note that the graph does not contain elements that don't match the search criterium (if any). It defaults to the Grid Layout.
layoutGroup: function(graph, group) {
      _.each(graph.getElements(), function(el, index) {
          el.position(0, index * 100);
      });
});
dragStartClone(element)A function that produces an element clone when the user starts dragging. It defaults to element.clone().
dragEndClone(element)A function that produces an element clone when the user places the dragged element into the paper. It defaults to element.clone().
snaplinesAn instance of the Snapline plugin which is responsible for drawing snaplines while the user drags an element from the stencil.
scaleClonesWhen set to true dragged clones are automatically scaled based on the current paper transformations. Note, that this option is ignored when snaplines option applied (it always scales the elements). It defaults to false.

Populating Stencil with elements

var r = new joint.shapes.basic.Rect({
    position: { x: 10, y: 10 }, size: { width: 50, height: 30 }
});
var c = new joint.shapes.basic.Circle({
    position: { x: 70, y: 10 }, size: { width: 50, height: 30 }
});

// Stencil is rendered in the DOM now.

// load the elements into the default group
stencil.load([r, c]);

// load the elements into multiple groups
stencil.load({ group1: [r], group2: [c] });

// load the elements into the `group2` group
stencil.loadGroup([r, c], 'group2');

As you can see, there is multiple way how to populate the stencil with elements. Use either stencil.load() or stencil.loadGroup() which make sure the elements are rendered into the stencil.

Note: Populating the stencil with elements needs to be done after the stencil is rendered in the DOM.

Stencil groups (a.k.a. Accordion)

Creating an accordion-like Stencil is as easy as passing the groups object to the stencil constructor. groups object is a hash table mapping group identifiers to the configuration of the particular group. Each group configuration object contains these properties:

labelgroup label. Can be either a string or a html.
indexposition of the group in the stencil.
closed[optional] when true, this group will be initially closed.
height[optional] the height of the paper containing the shapes of the group. If not defined, then the height of the stencil will be used. If non of these height are defined, the paper will be automatically scaled to fit its content (recommended).
layout[optional] same as the stencil layout option. It allows you to override the global settings for a particular group.
var stencil = new joint.ui.Stencil({
     graph: graph,
     paper: paper,
     width: 200,
     groups: {
          one: { label: 'First group', index: 1 },
          two: { label: 'Second group', index: 2, closed: true }
     }
  });

Searchable Stencil

The Stencil allows users to filter the elements by an arbitrary keyword. That is useful especially when the palette contains a lot of elements. To enable this add search: { /* rules */ } option to the Stencil constructor. The rules determine what attributes we match a given keyword with and are defined by an object with ELEMENT_TYPE: [ATTRIBUTE_PATH1, ATTRIBUTE_PATH2, ..] key-value pairs. Where ELEMENT_TYPE is either an element type (e.g basic.Rect) or '*' to match any type. ATTRIBUTE_PATH is a path to element's attribute or property (e.g 'attrs/text/text', 'description').

var stencil = new joint.ui.Stencil({
    search: {
        '*': ['attrs/text/text'],
        'basic.Image': ['description'],
        'basic.Path': ['description']
    }
});

By default all the unmatched elements and groups are hidden (display: none; is set on them). Alternatively you can add .joint-stencil .joint-element.unmatched { display: block; } and .joint-stencil .group.unmatched { display: block } rules to your css stylesheet in order to have the unmatched elements translucent.

The Stencil triggers the 'filter' event with a matched subset of elements wrapped in joint.dia.Graph as the first argument every time the elements get filtered.

API

load(groupedElements)

Accept an object with multiple key-value pairs, where key is the name of a group and value an array of elements to be rendered into that group. For instance:

stencil.load({
    group1: [{ type: 'basic.Rect'},{ type: 'basic.Circle' }],
    group2: [{ type: 'basic.Image' }]
});

Note that the elements could be also defined as plain javascript objects. That is useful for example when you store the stencil configuration in a JSON, which is received from a DB.

If the method is called with an array it behaves like the loadGroup below.

loadGroup(elements [, group]) Accept an array of elements and render them into a single stencil group. If no group provided, the default group is used.
getGraph(groupName) Get the graph associated with the group identified by groupName. If the stencil does not use groups, just omit the groupName parameter to get the only graph present.
getPaper(groupName) Get the paper associated with the group identified by groupName. If the stencil does not use groups, just omit the groupName parameter to get the only paper present.
setPaper(paper) Set the target paper for the stencil. It tells stencil to use a different paper than the one that was passed to it in the initialization through the options object. This is useful if you have a stencil instantiated and want to change the target paper dynamically. For example, if you have tabs each having its own paper with its own diagram but you want to use only one stencil, they you can call setPaper(myTab2) whenever the active tab changes. Note that the paper argument can be both a joint.dia.Paper object or the joint.ui.PaperScroller object, the stencil can handle both.
openGroup(groupName) Open group groupName.
closeGroup(groupName) Close group groupName.
toggleGroup(groupName) Toggle group groupName.
isGroupOpen(groupName) Return true if the group with the groupName is open. Return false otherwise.
openGroups() Open all groups.
closeGroups() Close all groups.
stopListening() Disable dragging of elements from the stencil.
startListening() Enable dragging of elements from the stencil.

Reacting on elements added to the paper

Many times, you might want to perform some action as a reaction on a new element dragged from the stencil to the paper. This can be achieved by usual means, i.e. by reacting on new elements added to the graph:

graph.on('add', function(cell, collection, opt) {
      // The stencil adds the `stencil` property to the option object with value
      // set to a client id (`cid`) of the stencil view.
      if (opt.stencil) {
          console.log('A cell with id', cell.id, 'was just added to the paper from the stencil.');
      }
  });

Events

The Stencil object triggers events that you can react on in your applications. These events can be handled by using the Stencil on(eventName, handler) method.

filter Triggered when the user uses the search input field to filter elements in the stencil. The handler has the following signature: function(graph, groupName, keyword) {} where graph is a set of elements that matched the filter (wrapped in joint.dia.Graph), groupName is the name of the group in which elements where filtered and keyword is the current search term.
drop:invalid Triggered when the user drops an element from the stencil to an invalid area. Invalid area is defined as an area that is outside of the visible paper area so that the dropped element is not accepted by the target paper. Such an element is then either immediately disposed or returned to its original position in stencil in an animated fashion (see the dropAnimation option). This event gives you a chance to react when the invalid drop occurs. The handler has the following signature: function(evt, element) {}, where evt is a mouse event object and element is the element model that would have been otherwise dropped into the main paper (and graph for that matter).

ui.TextEditor

ui.TextEditor is a powerful (rich-text) inline text editor that can be used on any SVG text element. The ui.TextEditor provides the following features:

  • rich-text editing of SVG text
  • caret & selections
  • caret & selections can be styled in CSS
  • double-click to select words, triple-click to select the entire text
  • keyboard navigation native to the underlying OS
  • API for programmatic access
  • support for editing scaled & rotated text
  • automatic URL hyperlinks detection and styling

Installation

Include joint.ui.textEditor.js and joint.ui.textEditor.css files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.textEditor.css">
<script src="joint.ui.textEditor.js"></script>

Usage

In the following example, we will show how to start inline text editing when the user double-clicks a text inside a JointJS element or a link. First we handle the cell:pointerdblclick event triggered by the paper. Inside our handler, we call the joint.ui.TextEditor.edit() method which starts the text editor on the SVG text under the target of the event. The only parameters we have to pass to this method is the SVG text DOM element, the cell view and the property path that will the text editor use to store the resulting text. Moreover, we also show how easy it is to apply autosizing on the text inside our elements so that they stretch based on the bounding box inside it.

// RECOMMENDED

paper.on('cell:pointerdblclick', function(cellView, evt) {
    joint.ui.TextEditor.edit(evt.target, {
        cellView: cellView,
        textProperty: cellView.model.isLink() ? 'labels/0/attrs/text/text' : 'attrs/text/text'
    });
});

function autosize(element) {

    var view = paper.findViewByModel(element);
    var text = view.$('text')[0];
    // Use bounding box without transformations so that our autosizing works
    // even on e.g. rotated element.
    var bbox = V(text).bbox(true);
    // Give the element some padding on the right/bottom.
    element.resize(bbox.width + 50, bbox.height + 50);
}

graph.on('change:attrs', function(cell) { autosize(cell) });

Note that there are two methods for starting up a text editor. The one we showed above (via the joint.ui.TextEditor.edit() function) is the easiest and recommended. The joint.ui.TextEditor.edit() function is a helper method that does a lot of things for us. First, it finds the nearest SVG <text> DOM element. Then it creates an instance of the joint.ui.TextEditor type and binds handlers for the 'text:change' event where it stores the new text content to your JointJS cells (elements or links). It also deals with annotations in case of rich text editing and more.

However, for completeness, we show another way to start text editing and that is by manually creating the instance of the joint.ui.TextEditor (not recommended if you don't know what you're doing):

// NOT RECOMMENDED

var ed;
paper.on('cell:pointerdblclick', function(cellView, evt) {
    var text = joint.ui.TextEditor.getTextElement(evt.target);
    if (text) {
        if (ed) ed.remove();   // Remove old editor if there was one.
        ed = new joint.ui.TextEditor({ text: text });
        ed.render(paper.el);

        ed.on('text:change', function(newText) {
            // Set the new text to the property that you use to change text in your views.
            cellView.model.attr('text/text', newText);
        });
    }
});

Note that using the first method, you can still access the actual instance of the text editor via joint.ui.TextEditor.ed property.

See the source code of the demo below for a complete example on using the inline text editor, including inline text editing of link labels.

Configuration

The following table lists options that you can pass to the ui.TextEditor.edit(el, opt) method (this is the recommended way to start you text editor):

el SVG <text> element or any of its descendants. Usually, you pass the event target object when the user doubleclicks your JointJS element/link (see examples above).
opt.cellView The view for your cell. Usually, you either have a direct access to this view (in the paper triggered 'cell:pointerclick' or 'cell:pointerdblclick' events) but you can always find a view for any JointJS element or link by using paper.findViewByModel(cell).
opt.textProperty The path to the text property that causes the element/link to re-render the text inside it. (e.g. 'attrs/text/text').
opt.annotationsProperty The path to the property that holds the text annotations. (e.g. 'attrs/text/annotations'). (Rich-text specific.)
opt.placeholder Set to true to show a placeholder when the text is empty. The placeholder text and visual look can be styled in CSS as follows:
.text-editor .caret.placeholder:before {        // The caret.
    background-color: red;
}
.text-editor .caret.placeholder:after {         // The placholder text.
    content: 'Enter text...';
    font-style: italic;
    color: blue;
}
opt.annotations Annotations that will be set on the ui.TextEditor instance. (Rich-text specific.)
opt.annotateUrls Set to true to enable the text editor to automatically detect URL hyperlinks and annotate them using the default urlAnnotation.
opt.urlAnnotation The default annotation for detected URL hyperlinks:
{
    attrs: {
        'class': 'url-annotation',
        fill: 'lightblue',
        'text-decoration': 'underline'
    }
}
opt.useNativeSelection ui.TextEditor uses native selections for selecting text. This can be disabled by setting useNativeSelection to false in which case ui.TextEditor renders its own selections. This is an advanced feature that is useful only in very rare occasions. For example, if you, for any reason, need to select text in two different elements at the same time, you should set the useNativeSelection to false. This is because if native selections are used, two or more text elements cannot have focus at the same time.
opt.textareaAttributes Set attributes on the underlying (invisible) textarea that is used internally by the text editor to handle keyboard text input. This is an advanced option and is useful in very rare situations. The default value is:
textareaAttributes: {
    autocorrect: 'off',
    autocomplete: 'off',
    autocapitalize: 'off',
    spellcheck: 'false',
    tabindex: '0'
}
Some jQuery UI users reported an issue with autocomplete attribute being set on the textarea, resulting in the following error thrown in the jQuery UI: Uncaught Error: cannot call methods on autocomplete prior to initialization; attempted to call method 'off'. In this case, you might want to set your own textareaAttributes to overcome this issue (in this specific case, omitting the autocomplete attribute).

The following table lists options that you can pass to the ui.TextEditor constructor function (only for advanced use):

text SVG <text> element. It is recommended to use the joint.ui.TextEditor.getTextElement(el) method to find the container <text> SVG element wrapping all the lines. You can use this method directly on the evt.target element in your event handlers and just check if the returned value is truthy.
placeholder Set to true to show a placeholder when the text is empty. The placeholder text and visual look can be styled in CSS as follows:
.text-editor .caret.placeholder:before {        // The caret.
    background-color: red;
}
.text-editor .caret.placeholder:after {         // The placholder text.
    content: 'Enter text...';
    font-style: italic;
    color: blue;
}

API

The following table lists methods that can be used on the joint.ui.TextEditor instance. If you, however, use the recommended way of creating the text editor via the joint.ui.TextEditor.edit() function, you usually do not want to deal with the actual instance (even though you can as you can always access the actual instance via joint.ui.TextEditor.ed property). To make it easy to work with the instance as with a black-box, joint.ui.TextEditor constructor exposes many of the methods directly (internally, it just proxies the methods to the actual instance.) The table below differentiates the instance methods from the class methods by using the full namespace access for the class methods (such as joint.ui.TextEditor.getAnnotations() = class method - vs getAnnotations() = instance method).

render([root]) Render the Text Editor. Call this method right after you have constructed the text editor instance and you're ready to display the inline text editor. If root (HTML DOM element) is specified, the text editor HTML container will be appended to that element instead of to the document.body. This is useful if you want the text editor to scroll with some other container.
remove() Remove the Text Editor. Call this when you're done with inline text editing.
selectAll() Programmatically select all the text inside the text editor.
select(selectionStart, selectionEnd) Programmatically select portion of the text inside the text editor starting at selectionStart ending at selectionEnd. This method automatically swaps selectionStart and selectionEnd if they are in a wrong order.
deselect() Programmatically deselect all the selected text inside the text editor.
getSelectionStart() Return the start character position of the current selection.
getSelectionEnd() Return the end character position of the current selection.
getSelectionRange() Return an object of the form { start: Number, end: Number } containing the start and end position of the current selection. Note that the start and end positions are returned normalized. This means that the start index will always be lower than the end index even though the user started selecting the text from the end back to the start.
getSelectionLength() Return the number of characters in the current selection.
getSelection() Return the selected text.
setCaret(charNum [, opt]) Programmatically set the caret position. If opt.silent is true, the text editor will not trigger the 'caret:change' event.
hideCaret() Programmatically hide the caret.
getTextContent() Return the text content (including new line characters) inside the text editor.
getWordBoundary(charNum) Return the start and end character positions for a word under charNum character position.
getURLBoundary(charNum) Return the start and end character positions for a URL under charNum character position. Return undefined if there was no URL recognized at the charNum index.
getNumberOfChars() Return the number of characters in the text.
getCharNumFromEvent(evt) Return the character position the user clicked on. If there is no such a position found, return the last one.
setCurrentAnnotation(attrs) This method stores annotation attributes that will be used for the very next insert operation. This is useful, for example, when we have a toolbar and the user changes text to e.g. bold. At this point, we can just call setCurrentAnnotation({ 'font-weight': 'bold' }) and let the text editor know that once the user starts typing, the text should be bold. Note that the current annotation will be removed right after the first text operation came. This is because after that, the next inserted character will already inherit properties from the previous character which is our 'bold' text. (Rich-text specific.)
setAnnotations(annotations) Set annotations of the text inside the text editor. These annotations will be modified during the course of using the text editor. (Rich-text specific.)
getAnnotations() Return the annotations of the text inside the text editor. (Rich-text specific.)
getCombinedAnnotationAttrsAtIndex(index, annotations) Get the combined (merged) attributes for a character at the position index taking into account all the annotations that apply. (Rich-text specific.)
getSelectionAttrs(range, annotations) Find a common annotation among all the annotations that fall into the range (an object with start and end properties - normalized). For characters that don't fall into any of the annotations, assume defaultAnnotation (default annotation does not need start and end properties). The common annotation denotes the attributes that all the characters in the range share. If any of the attributes for any character inside range differ, undefined is returned. This is useful e.g. when your toolbar needs to reflect the text attributes of a selection. (Rich-text specific.)
joint.ui.TextEditor.getTextElement(el) Return the containing SVG <text> element closest to the SVG element el. This is especially handy in event handlers where you can just pass evt.target element to this function and it will return the element that you can directly use as a parameter to the ui.TextElement constructor function. If no such element is exists, the function returns undefined.
joint.ui.TextEditor.edit(el, opt) See the Configuration section for details.
joint.ui.TextEditor.close() Close the currently opened text editor (if there was one). Note that before actually removing the editor (and if the annotateUrls option is set to true), the close() method tries to find if there was a URL hyperlink at the current cursor position and annotates it. The reason is that normally, URL hyperlinks are detected after the user types a non-visible character such as SPACE or new line. In this case though, the user closed the editor by e.g. clicking outside the element/link and so if there was a URL hyperlink at the end of the text content, it would not have been annotated.
joint.ui.TextEditor.applyAnnotation(annotations) Apply annotations to the current selection. This is useful if the user has something selected in the text editor and then changes e.g. the font size via a toolbar. In this case, you construct the annotations array from the toolbar changes and apply it using this function on the current selection.
joint.ui.TextEditor.setCurrentAnnotation(attributes) See the instance setCurrentAnnotation() method above.
joint.ui.TextEditor.getAnnotations() See the instance getAnnotations() method above.
joint.ui.TextEditor.setCaret(charNum, opt) See the instance setCaret() method above.
joint.ui.TextEditor.deselect() See the instance deselect() method above.
joint.ui.TextEditor.selectAll() See the instance selectAll() method above.
joint.ui.TextEditor.select(start, end) See the instance select() method above.
joint.ui.TextEditor.getNumberOfChars() See the instance getNumberOfChars() method above.
joint.ui.TextEditor.getCharNumFromEvent(evt) See the instance getCharNumFromEvent() method above.
joint.ui.TextEditor.getWordBoundary() See the instance getWordBoundary() method above.
joint.ui.TextEditor.findAnnotationsUnderCursor() See the instance findAnnotationsUnderCursor() method above except the annotations and index arguments are automatically added so this method does not accept any arguments.
joint.ui.TextEditor.findAnnotationsInSelection() See the instance findAnnotationsInSelection() method above except the annotations and start and end index of the selection are automatically added so this method does not accept any arguments.
joint.ui.TextEditor.getSelectionAttrs(annotations) See the instance getSelectionAttrs() method above except the range is automatically added.
joint.ui.TextEditor.getSelectionLength() See the instance getSelectionLength() method above.
joint.ui.TextEditor.getSelectionRange() See the instance getSelectionRange() method above.

Events

The following table lists events that are triggered on the ui.TextEditor instance but also on the joint.ui.TextEditor class (which is useful if you use the recommended way of creating a text editor via the joint.ui.TextEditor.edit() function):

text:change Triggered when the text inside the text editor changes. This is the most important event and the one you want to always react on. The callback has the following signature: function(newText) and is therefore called with the new text as a parameter. You are assumed to set the new text to the property on your JointJS element/link that is used for storing the associated text.
select:change Triggered when the text inside the text editor is being selected (or if the user double-clicked to select a word or triple-clicked to select the entire text). The callback has the following signature: function(selectionStart, selectionEnd). For example, you can see the selected text like this:
ed.on('select:change', function(selectionStart, selectionEnd) {
    var text = ed.getTextContent().substring(selectionStart, selectionEnd);
});
select:changed Triggered when the user is finished selecting text inside the text editor. The callback has the following signature: function(selectionStart, selectionEnd). For example, you can see the selected text like this:
ed.on('select:changed', function(selectionStart, selectionEnd) {
    var text = ed.getTextContent().substring(selectionStart, selectionEnd);
});
caret:change Triggered when the caret has changed position. The position is passed to the callback as the only argument.
caret:out-of-range Triggered when the caret is outside the visible text area. This is a very special event that you don't usually deal with. The only situation this event can occur is when ui.TextEditor is used on a text rendered along a path (see Vectorizer#text(str, { textPath: '' }))). In this case, if the user moves his cursor outside the visible text area, caret:out-of-range event is triggered so that the programmer has chance to react (if he wants to because these situations are handled seamlessly in ui.TextEditor by hiding the caret).

ui.Toolbar

With the Toolbar plugin you can easily enrich your application with various tools. Either use built-in controls to manage other Rappid plugins (e.g zoom for PaperScroller, undo/redo for CommandManager) or custom tools of your desire - just choose a type of UI primitive ('slider', 'input', 'button' etc.) and attach an action. Toolbar is a container for this tools - called 'widgets'.

Create a Toolbar


var toolbar = new joint.ui.Toolbar({
    tools: [
        { type: 'checkbox' },
        { type: 'range', name: 'slider', min: 0, max: 10, step: 1 },
        { type: 'separator' },
        { type: 'toggle', name: 'toggle', label: ''},
        'separator',  // also possible, use defaults
        { type: 'inputText' },
        { type: 'button', name: 'ok', text: 'Ok' },
        { type: 'button', name: 'cancel', text: 'Cancel' },
        { type: 'separator' }
    ]
});

$('body').append(toolbar.render().el);

Toolbar configuration

tools Array<object> | Array<string> An array of tools defined as a plain JavasScript object. For more information visit Tools configuration section.
references object key => value storage. Some widgets require additional references for their functionality. Whenever you use such a widget (e.g. undo/redo requires joint.dia.CommandManager, zoom requires joint.ui.PaperScroller) you need to pass the required reference into the toolbar as well.
groups object

Split the widgets defined in tools option into groups. Groups could be aligned and ordered in the context of the Toolbar.

groups.align string Can be left or right.
groups.index number Set the order of the group.

Tool configuration

type string Set the type of the widget. Available widgets are listed in the Toolbar widgets section.
name string name is used to differentiate multiple widgets with the same type and has to be unique in the context of the toolbar. Note that the toolbar uses the name as a prefix for events triggered on widgets. More information can be found in the Events section.
group string Name of the group where a widget belongs to.
attrs object JointJS style attribute definition. Additional DOM element attributes can be defined here.
theme string Allow to override the global theme.

Toolbar API

getWidgetByName(name) <joint.ui.Widget> Return an instance of joint.ui.Widget with the matching name.
getWidgets() Array<joint.ui.Widget> Return an array of joint.ui.Widget instances included in the toolbar.

Toolbar widgets

checkbox
nametypedefault
valueboolean 
labelstring 
toggle
valueboolean
labelstring
separator Vertical line by default.
widthnumber
label
textstring
range
valueboolean
unitstring
minnumber
maxnumber
stepnumber
selectBox Configuration is described in ui.SelectBox section.
button
textstring
inputText
labelstring
valuestring
inputNumber
labelstring
valuenumber
minnumber
maxnumber
textarea
labelstring
valuestring
selectButtonGroup Configuration is described in ui.SelectButtonGroup section.
zoomIn | zoomOut | zoomToFit
minnumber0.2
maxnumber5
stepnumber0.2

 

Required references:

paperScroller joint.ui.PaperScroller instance

zoomSlider
minnumber20
maxnumber500
stepnumber20
valuenumber100
unitnumber'%'

 

Required references:

paperScroller joint.ui.PaperScroller instance

undo | redo

No additional options.

Required references:

commandManager joint.dia.CommandManager instance

Events

The toolbar behaves as a proxy for widgets events. An event triggered on a single widget could be also caught on the Toolbar instance. Event names are prefixed with the widget's name followed by a colon : to recognize from which tool (widget) is the event coming from.

var toolbar = new joint.ui.Toolbar({
    tools: [
        { type: 'toggle', name: 'toggle' },
        { type: 'button', name: 'ok', text: 'Ok' },
        { type: 'button', name: 'cancel', text: 'Cancel' },
    ]
});

toolbar.on('toggle:change', function(value, event) {
    console.log(value, event);
});

toolbar.on('ok:pointerclick', function(event) {
    console.log('ok clicked');
});

toolbar.on('cancel:pointerclick', function(event) {
    console.log('cancel clicked');
});

$('body').append(toolbar.render().el);

Demo


var paperScroller = new joint.ui.PaperScroller({
    paper: paper,
    autoResizePaper: true
});

var commandManager = new joint.dia.CommandManager({
    graph: graph
});

var toolbar = new joint.ui.Toolbar({
    // initialize tools with default settings
    tools: ['zoomIn', 'zoomOut', 'zoomToFit', 'zoomSlider', 'undo', 'redo'],
    references: {
        paperScroller: paperScroller,
        commandManager: commandManager
    }
});

ui.Tooltip

Use tooltips to display information messages anywhere in the UI.

Installation

Include joint.ui.tooltip.css and joint.ui.tooltip.js files to your HTML:

<link rel="stylesheet" type="text/css" href="joint.ui.tooltip.css">
<script src="joint.ui.tooltip.js"></script>

Creating a Tooltip

A tooltip can be created with the joint.ui.Tooltip constructor. One instance of joint.ui.Tooltip manages multiple tooltips. Tooltips has configuration on html elements, as data attributes. Configuration passed in constructor became global settings for the particular tooltip. Setting defined as a data attribute won't override the global setting defined in the constructor. Newly added elements with a known definition are initialized automatically, right after append.

// js part - initialize tooltip
new joint.ui.Tooltip({
    target: '[data-tooltip]',
    direction: 'auto',
    padding: 10
});
<!-- html part - tooltip configuration -->
<label data-tooltip="Top directed tooltip" data-tooltip-position="top">Hover to see top tooltip</label>
<label data-tooltip="Left directed tooltip" data-tooltip-position="left">Hover to see left tooltip</label>
<label data-tooltip="Right directed tooltip" data-tooltip-position="right">Hover to see right tooltip</label>
<label data-tooltip="Bottom directed tooltip" data-tooltip-position="bottom">Hover to see bottom tooltip</label>

Result

In this demo all the elements with the data attribute tooltip will display tooltip when hovered.

Configuration

The joint.ui.Tooltip constructor takes the following parameters:

target string | Element | jQuery A CSS selector or DOM element or jQuery object that is the target for the tooltip. When the user hovers over this element with his mouse cursor, the tooltip appears.
rootTarget string | Element | jQuery A CSS selector or DOM element or jQuery object that is the root target for the tooltip. When using the rootTarget, the event handlers that trigger the tooltip display will be delegated (as opposed to directly-bound). This is useful when the target is a selector pointing to elements that might get removed from the DOM and then put back again. The tooltip tries to find the selected elements specified by the selector in the target option when it is initialized (during instantiation) and than uses these element as the target for the tooltip. If later on such elements are removed from the DOM the tooltip looses a reference to the target. Therefore, we can prevent this by using rootTarget or selecting the container element and target selector for selecting the real target of the tooltip at the time of the "mouseover" event.
content string | Element | jQuery | function(element) The content of the tooltip. It can be an arbitrary string/HTML or a function
padding string | Element | jQuery | function(element) The distance between target element and the tooltip.
position string | function(element) The position of the element relative to the tooltip. left means the element is on the left of the shown tooltip. Can be either top, left, bottom, right
direction string | function(element) The direction of the tooltip arrow. Can be either top, right, bottom, left or auto. auto sets the arrow accordingly to 'position' property. Arrows are disabled if the direction is a falsy value or off
positionSelector string | Element | jQuery | function(element) A CSS selector or DOM element or jQuery object or a function used to compute the position of the edge of the tooltip. It defines bounding box which the tooltip mustn't to get across.
trigger string Event which makes the tooltip show up. Can be either hover, focus or click. This option cannot be set as a data attribute on the DOM element.
minResizedWidth number A minimal width of the tooltip. The tooltip width can be resized down to the minResizedWidth. If the available space is smaller than the minResizedWidth, the direction of the tooltip is changed to its opposite direction (left tooltip is swapped to right, top to bottom and vice versa). Set minResizedWidth to a falsy value if no automatic resizing or direction swapping should take a place. It defaults to 100.
hideTrigger string Event which makes the tooltip disappear. Can be any of the DOM event. Separate multiple events with ' '.
<label data-tooltip="tooltip text" data-tooltip-hide-trigger="mouseout mouseouver">HIDE ON MOUSEOUT</label>
or
new joint.ui.Tooltip({
    rootTarget: '.click-tooltip',
    target: '[data-tooltip]',
    trigger: 'click',
    hideTrigger: 'mouseout'
});
viewport object | function(element) viewport defines the boundary for the tooltip. It guarantees that the tooltip's content will be rendered inside the viewport bounding box.
selector string | jQuery | Element CSS selector, Element or jQuery object defining the viewport DOM object. The element triggering tooltip should be rendered inside this element.
padding number Minimal distance from the viewport boundaries.
dataAttributePrefix string | function(element) Defines prefix for HTML data attributes with the tooltip configuration.
new joint.ui.Tooltip({
    target: '[custom-def]',
    dataAttributePrefix: 'custom-def'
});
matches with
<label data-custom-def="Tooltip content" data-custom-def-position-selector=".box2" data-custom-def-position="bottom">BOTTOM</label>

Tooltips in Halo

It's useful to show the user a hint on the Halo icon tools. Fortunately, it's very easy. For instance:


paper.on('cell:pointerup', function(cellView) {
    // We don't want a Halo for links.
    if (cellView.model instanceof joint.dia.Link) return;
    var halo = new joint.ui.Halo({ graph: graph, paper: paper, cellView: cellView });

    halo.changeHandle('remove', {
        attrs: {
            '.handle': {
                'data-tooltip-class-name': 'small',
                'data-tooltip': 'Click to remove the object',
                'data-tooltip-position': 'right'
            }
        }
    });

});

// run this once
new joint.ui.Tooltip({
    rootTarget: document.body,
    target: [data-tooltip],
    padding: 15
});

Notice we use the 'small' className in our example. Rappid tooltips come with two build-in sizes for tooltips: small and normal. In order to enable the small version use 'small' as a className.

ui.TreeLayoutView

ui.TreeLayoutView is a view on a tree-like graph that implements common tree manipulation interface like detaching and attaching branches, disconnecting nodes from the tree to create floating nodes and more.

Installation

Include joint.ui.treeLayoutView.js and its dependency joint.layout.treeLayout.js to your HTML:

<script src="joint.layout.treeLayout.js"></script>
<script src="joint.ui.treeLayoutView.js"></script>

Usage

First you need to create an object of layout.TreeLayout type. ui.TreeLayoutView is dependent on this and expects this object in its model parameter:

var graphLayout = new joint.layout.TreeLayout({
    graph: graph,
    parentGap: 50,
    siblingGap: 50
});

var treeLayoutView = new joint.ui.TreeLayoutView({
    paper: paper,
    model: graphLayout
});

graphLayout.layout();

Configuration

The following table lists options that you can pass to the ui.TreeLayoutView constructor function:

model Instance of joint.layout.TreeLayout. See layout.TreeLayout plugin for more details.
paper JointJS paper object.
useModelGeometry If set to true, the cells position and dimensions will be used as a basis for the TreeLayoutView wrapping rectangle position. By default, this is set to false which causes the TreeLayoutView wrapping rectangle position be based on the bounding box of the selected element views. Sometimes though, your shapes can have certain SVG sub elements that stick out of the view and you don't want these sub elements to affect the TreeLayoutView wrapping rectangle position. In this case, set the useModelGeometry to true.
clone(element) A function, that is applied on the element to create a clone for dragging. It defaults to return element.clone();.
canInteract(element) A predicate, that returns true if the element, which the user interacts with, can be actually dragged. It defaults to return true;.
previewAttrs An object with SVG attributes to be applied onto the preview element. The preview consists of 3 components - child, parent and link. e.g.
{
    child: { opacity: 0.5 },
    link: { opacity: 0.5 },
    parent: { rx: 20, ry: 20 }
}

API

startDragging(elementOrElements) Initiates dragging of a single or multiple elements (dia.Element or an array of dia.Element). Useful when combinded with the Selection plugin to drag & drop multiple nodes at once.