🎉 JointJS has new documentation! ðŸ¥³

Rappid - ui

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.
vertical When set to true the toolbar arrange tools from top to bottom instead of left to right. Default is false.

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] }.
action The name of the action the button represents. When the button is clicked, the dialog object triggers an event action:[name], allowing you to react.
content The label of the button (it can be HTML, as well).
position Optional. It can be 'left', 'right', or 'center'. If position is not specified, it is considered to be 'right'. If multiple buttons are provided with the same position, they are placed left-to-right according to the order in which they were provided.
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.
clearAll if set to true (the default value), clear all the existing freeTransforms from the page when a new freeTransform is created. This is the most common behavior as it is assumed that there is only one freeTransform visible on the page at a time. However, some applications might need to have more than one freeTransform visible. In this case, set clearAll to false (and make sure to call remove() once you don't need a freeTransform anymore)
clearOnBlankPointerdown if set to true (the default value), clear the freeTransform when a user clicks the blank area of the paper.

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
rotateEmbedsshould be elements embedded inside the cellView element rotated as well? Default is false
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 than one halo visible. In this case, set clearAll to false (and make sure to call remove() once you don't need a halo anymore)
clearOnBlankPointerdown if set to true (the default value), clear the halo when a user clicks the blank area of the paper.
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.
magnet A function accepting an elementView returning an SVGElement used as a magnet for links created via linking or forking handles.
new joint.dia.Halo({
  cellView: elementView,
  magnet: function(elementView, end) {
    // where `end` is either "source" (linking and forking) or "target" (forking only)
    // connect the link directly to a rectangle element as opposed to the element group
    return elementView.el.querySelector('rect') || elementView.el;
  }
})

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:[name]:contextmenu Triggered when the user invokes contextmenu on a tool handle named [name].
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.
validateInput(element, path, type, inspector) function a function that returns true if the input is valid. If the input is invalid, the value is not saved to the model. The function is passed four arguments. element is the <input> element the user interacted with. path is the path to the input field within the inspector (/ separated). type is the type defined in the options object for that field. Finally, inspector is a reference to the current inspector instance context. The default function checks the validity property of element:
function(element, path, type, inspector) {
    return (element.validity ? element.validity.valid : true);
}
renderFieldContent(options, path, value, inspector) 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. The function is passed four arguments. `options` 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). Finally, `inspector` is a reference to the current inspector instance context.
renderLabel(options, path, inspector) 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 label. In other words, this function allows you to define custom labels. The function is passed three arguments. `options` is the object specified in the `inputs` option of the inspector for a property on the `path` (`/` separated). Finally, `inspector` is a reference to the current inspector instance context.
getFieldValue(attribute, type, inspector) 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. The function is passed three arguments. `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. Finally, `inspector` is a reference to the current inspector instance context.
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.
overwrite boolean Should the input value overwrite the current contents of the cell? Default is `false`, which means that the input attributes are merged with the model's current attributes. Example:
// overwrite: false (default)
Model { existingProperty: 'value1' } Input { newProperty: 'value2' } => Model { newProperty: 'value2', existingProperty: 'value1' }
// overwrite: true
Model { existingProperty: 'value1' } Input { newProperty: 'value2' } => Model { newProperty: 'value2' }
        
options object

an array of options for a several input types (`"select"`, `"select-box"`, `"select-button-group"`, `"color-palette"`). Can be defined by:

content `['option1', 'option2']`
value/content
// select & select-box
[{ value: 'value1', content: 'option1' }, { value: 'value2', content: 'option2' }]
// color-palette
[{ content: red', icon: 'image1.svg' }, { content: 'blue' }]
// select-button-group
[{ value: 'value1', content: 'option1', buttonWidth: 20, icon: 'image.png', iconSelected: 'image2.png', iconWidth: 20, iconHeight: 20 }]
reference `'path'` ( where `cell` property defined by `'path'` is an array of options) i.e.
element.prop('path') // => ['option1', 'option2']
min number
minimum value of a range input. [specific for the `"range"` input type]
minimum number of items in an array. [specific for the `"list"` input type]
max number
maximum value of a range input. [specific for the `"range"` input type]
maximum number of items in an array. [specific for the `"list"` 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' } } }
    }
})

It's also possible to toggle groups with when expressions.

var inspector = new joint.ui.Inspector({
    groups: {
        first: { label: 'F' },
        second: { label: 'S', when: { eq: { 'attribute2': true }}}
    }
});

Validation

The following example shows how to reflect a custom shape's validation functions inside your inspector. The inspector definition provides a custom `validateInput` method. That method then refers to Shape's custom `validateProperty` method, which uses some common regex validators. Notice that this architecture makes the Shape (the model instance) responsible for accepting/rejecting user input data, not the inspector. As such, this arrangement manages to fulfill one of the goals of the JointJS framework, namely the separation of Model-View-Controller components from each other.

(function(joint, Shape) {

    joint.setTheme('modern');

    var paper = new joint.dia.Paper({
        el: document.getElementById('paper'),
        width: 500,
        height: 500
    });

    var shape = new Shape();
    shape.position(150,50);
    shape.size(200,200);
    shape.addTo(paper.model);

    var inspector = joint.ui.Inspector.create('#inspector', {
        cell: shape,
        inputs: shape.getInspectorDefinition(),
        validateInput: function(el, path, type, inspector) {
            var $el = $(el);
            var value = inspector.parse(type, inspector.getFieldValue(el, type), el);
            $el.removeClass('error').parent().find('error').remove();
            var error = shape.validateProperty(path, value);
            if (error) {
                var $error = $('').text(error);
                $el.addClass('error').before($error);
            }
            return !error;
        }
    });

    // run the first validity check
    inspector.updateCell();

})(joint, joint.dia.Element.define('Shape', {

    attrs: {
        body: {
            refWidth: '100%',
            refHeight: '100%',
            fill: '#dddddd',
            stroke: 'lightblue',
            strokeWidth: 2
        }
    },

    phoneNumber: '',
    emailAddress: 'org@client.io'

}, {

    markup: [{
        tagName: 'rect',
        selector: 'body'
    }],

    REGEX_PHONE_NUMBER: /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$/,
    REGEX_HEXCOLOR: /^#([a-f0-9]{3}){1,2}\b/i,
    REGEX_EMAIL_ADDRESS: /^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/,

    validateProperty: function(path, value) {
        switch (path) {
            case 'attrs/body/stroke':
            case 'attrs/body/fill':
                if (this.REGEX_HEXCOLOR.test(value)) break;
                return 'Invalid Color (e.g. #ff0000)';
            case 'attrs/body/strokeWidth':
                if (_.isNumber(value) && value >= 0) break;
                return 'Invalid Stroke Width (A positive number)';
            case 'phoneNumber':
                if (this.REGEX_PHONE_NUMBER.test(value)) break;
                return 'Invalid Phone Number (e.g. 123-456-7890)';
            case 'emailAddress':
                if (this.REGEX_EMAIL_ADDRESS.test(value)) break;
                return 'Invalid Email Address.';
        }
        return null;
    },

    getInspectorDefinition: function() {
        return {
            'attrs/body/fill': {
                type: 'text',
                label: 'Fill Color'
            },
            'attrs/body/stroke': {
                type: 'text',
                label: 'Stroke Color'
            },
            'attrs/body/strokeWidth': {
                type: 'number',
                label: 'Stroke Width'
            },
            'phoneNumber': {
                type: 'text',
                label: 'Phone Number'
            },
            'emailAddress': {
                type: 'text',
                label: 'Email Address'
            }
        };
    }

}));

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 graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({
    el: document.getElementById('paper'),
    width: 500,
    height: 300,
    gridSize: 1,
    model: graph
});

var r1 = new joint.shapes.standard.Rectangle({
    position: { x: 80, y: 80 },
    size: { width: 130, height: 80 },
    attrs: { label: {
        text: 'My Element',
        textDecoration: 'none'
    }}
});
graph.addCell(r1);

var r2 = new joint.shapes.standard.Rectangle({
    position: { x: 290, y: 80 },
    size: { width: 130, height: 80 },
    attrs: { label: {
        text: 'My Second Element',
        textDecoration: 'none'
    }}
});
graph.addCell(r2);

function createInspector(cellView) {

    joint.ui.Inspector.create('.inspector-container', {
        cellView: cellView,
        inputs: {
            attrs: {
                text: {
                    style: {
                        textDecoration: {
                            type: 'select2',
                            group: 'text-decoration',
                            options: ['none', 'underline', 'overline', 'line-through']
                        }
                    },
                    text: {
                        type: 'my-button-set',
                        group: 'text'
                    }
                }
            }
        },
        groups: {
            'text-decoration': { label: 'Text Decoration (Select2)' },
            'text': { label: 'Text' }
        },

        renderFieldContent: function(options, path, value, inspector) {

            switch (options.type) {

                case 'my-button-set':

                    var $buttonSet = $('').css('margin', 20);
                    var $yes = $('').text('Say YES!');
                    var $no = $('').text('Say NO!');
                    $buttonSet.append([$yes, $no]);
                    $buttonSet.data('result', value);
                    // 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($buttonSet, path, options);
                    });
                    $no.on('click', function() {
                        $buttonSet.data('result', 'NO');
                        inspector.updateCell($buttonSet, path, options);
                    });

                    return $buttonSet;

                case 'select2':

                    var $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).
                    setTimeout(function() {
                        $select.show().select2({ data: options.options }).val(value || 'none').trigger('change');
                        $select.data('select2').$container.css('margin', 20);
                        $select.on('change', function() {
                            inspector.updateCell($select, path, options);
                        });
                    }, 0);

                    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  input by default.
        }
    });
}

// start with inspector for `r1`
createInspector(paper.findViewByModel(r1));

// switch inspector at pointerup
paper.on('cell:pointerup', createInspector);

Custom Labels

It is also possible to customize the appearance and behavior of field labels in your inspector. This can be used to create labels with custom HTML - as demonstrated by the myList label (an <a> with <label> inside), which links to an address specified in myList.url. Additonally, our templating functionality can be used to define dynamic labels. We use this for myList.item elements; the '{{index}}' placeholder is replaced with the element's index inside myList when an element is added to the array with the + button.

joint.ui.Inspector.create('#container', {
    cell: model,
    inputs: {
        myList: {
            type: 'list',
            url: 'https://jointjs.com',
            item: {
                type: 'text',
                label: 'Item {{index}}'
            }
        }
    },
    renderLabel: function(opt, path) {
        // returns an HTMLElement (`$el`) as a custom label
        // this method is called for every element inside inspector when being rendered
        // in this case, it is called when the example loads, to create a label for the whole `myList`
        // it is also called whenever the + button is pressed, to create a label for each element added to `myList`

        var $label = document.createElement('label');
        var $el = $label;

        // List numbering:
        var text = opt.label;
        var indexPlaceholder = '{{index}}';
        // is this an inspector element with a `label` text specified (i.e. one of `myList` items)?
        // does this inspector element contain a substring to replace?
        if (text && text.indexOf(indexPlaceholder) > -1) {

            // every input field in the inspector is addressed via a `path`
            // elements added to `myList` are addressed as 'myList/0', 'myList/1', etc.
            // we can use this in a regex to identify list elements
            // we do this by checking if there is a '/' followed by a digit at the end of the path
            var match = path.match(/\/(\d+)$/);
            if (match) {

                // if this is a list element, use its index as index
                // (+1 to make sure the rendered labels start from 1)
                var index = parseInt(match[1], 10) + 1;
                // actually add the human-readable index to the label
                text = text.replace(indexPlaceholder, index);
            }
        }
        // then, set the text of the label to the text we generated

        // else: set it to the raw `path` string
        // (this happens for `myList` itself - so it gets a label that says 'myList')
        $label.textContent = text || path;

        // item labels are hidden via CSS by default, we need to unhide them
        $label.style.cssText = 'display:block !important;';

        // Clickable label:
        // is this an inspector element with an `url` specified (i.e. `myList` itself)?
        if (opt.url) {
            // then create a new  element and add  to it
            var $a = document.createElement('a');
            $a.href = opt.url;
            $a.target = 'blank';
            $a.appendChild($el);
            $el = $a;
        }

        // we have created a custom label
        return $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](#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](http://backbonejs.org/#Events-on). More information about `event` could be found in the [Event description](#joint.ui.Keyboard.event) section.
off(event, callback, [context]) Remove a previously-bound callback function from an object. Same logic as [Backbone.Events.off](http://backbonejs.org/#Events-off). More information about `event` could be found in the [Event description](#joint.ui.Keyboard.event) 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();
});

The downloadable option offers an easy way to download an image:

$('img').on('click', function(evt) {
    new joint.ui.Lightbox({
        title: $(evt.target).attr('title'),
        image: $(evt.target).attr(src),
        downloadable: true
    }).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.
downloadable If true, a default download button is added to the lightbox (after any buttons specified). When clicked, this button triggers the downloadAction ('download' by default).
fileName The filename of downloaded images. By default, the downloaded images are named 'Image'.
downloadAction If the action action:[downloadAction] is triggered on the lightbox, the lightbox image is downloaded to the user computer. By default, the download action is 'download'. Downloaded images will have fileName as filename.
buttons Buttons to show under the lightbox title. Explained in ui.Dialog configuration. Specifically, a custom download button can be created by providing a button that has downloadAction as its action ('download' by default).

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.
updateCurrentView() Updates the navigator's viewport based on the associated paperScroller state. The method is debounced (the invoking is delayed until after 0 milliseconds have elapsed since the last time the method was invoked) and is called automatically when the paperScroller is scrolled or zoomed.

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');

Scrolling the paper

User scrolling of the paper is allowed by default. Two methods are provided to toggle this functionality:

lock()

Lock the current viewport by disabling user scrolling.

unlock()

Enable user scrolling if previously locked.

PaperScroller also provides methods for programmatic scrolling. These methods try scroll the paper so that a specified point on the paper lies at the center of the paper scroller viewport. If this is not possible (e.g. if the requested point lies in a corner of the paper), the paper is scrolled as far as possible without requiring paddings. This means that the requested point will not actually move into the center of the viewport if there is not enough room for it. (Use the center functions to add paddings if necessary and force the requested point to move to the center of paper scroller viewport.)

All scroll methods optionally support animation if an opt.animation object is provided. The object may contain jQuery animation properties, most notably opt.animation.duration - a number specifying the time, in milliseconds, determining how long the animation will run. (Note that animated transitions are better supported with the transition functions.)

scroll(x, y [, opt])

Try to scroll the paper scroller so that the position (x,y) on the paper (in local coordinates) is at the center of the paper scroller viewport. If only one of the coordinates is specified, only scroll in the specified dimension and keep the other coordinate unchanged.

paperScroller.scroll(100, 200); // scroll to the point (100,200)
paperScroller.scroll(100); // scroll to the point (100,[y of center of visible area])
paperScroller.scroll(null, 200); // center at the point ([x of center of visible area],200)
paperScroller.scroll(100, 200, { animation: { duration: 400 }});
paperScroller.scroll(100, null, { animation: { duration: 200, easing: 'linear' }});
paperScroller.scroll(null, 200, { animation: { duration: 600 }});
scrollToContent([opt])

Try to scroll the paper scroller so that the center of paper content is at the center of the paper scroller viewport.

paperScroller.scrollToContent();
paperScroller.scrollToContent({ animation: { duration: 600 }});
scrollToElement(element [, opt])

Try to scroll the paper scroller so that the center of element (an instance of joint.dia.Element) is at the center of the paper scroller viewport.

paperScroller.scrollToElement(element);
paperScroller.scrollToElement(element, { animation: { duration: 600 }});

Centering and positioning paper content

The center methods are more aggressive than the scroll methods. These methods position the paper so that a specific point on the paper lies at the center of the paper scroller viewport, adding paddings around the paper if necessary (e.g. if the requested point lies in a corner of the paper). This means that the requested point will always move into the center of the viewport. (Use the scroll functions to avoid adding paddings and only scroll the paper scroller viewport as far as the paper boundary.)

The position methods are a more general version of the center methods. They position the paper so that a specific point on the paper lies at requested coordinates inside the paper scroller viewport.

Padding can be added with opt.padding, to offset positioned elements away from the edges of client viewport. This stacks up with the x and y coordinates; it also affects the center calculations. The padding value is normalized using the joint.util.normalizeSides function; either a single number may be passed (e.g. padding: 10 applies padding of 10px to all edges), or an object with numeric properties (e.g. padding: { top: 20, horizontal: 10 } applies padding of 20px to the top edge and 10px to the right and left edges).

center([opt])

If no coordinate is specified, position the center of paper to the center of the paper scroller viewport.

paperScroller.center();

Adding 100px of left padding causes the effective center of the paper scroller window to shift by 50px to the right. That is where the center of paper will be positioned.

paperScroller.center({ padding: { left: 100 }});
center(x, y [, opt])

Position the point (x,y) on the paper (in local coordinates) to the center of the paper scroller viewport. If only one of the coordinates is specified, only center along the specified dimension and keep the other coordinate unchanged.

paperScroller.center(100, 200); // center at the point (100,200)
paperScroller.center(100); // center at the point (100,[y of center of visible area])
paperScroller.center(null, 200); // center at the point ([x of center of visible area],200)

Adding 100px of left padding causes the effective center of the paper scroller window to shift by 50px to the right. That is where the point will be positioned.

paperScroller.center(100, 200, { padding: { left: 100 }});
paperScroller.center(100, null, { padding: { left: 100 }});
paperScroller.center(null, 200, { padding: { left: 100 }});
centerContent([opt])

Position the center of paper content to the center of the paper scroller viewport.

paperScroller.centerContent();
paperScroller.centerContent({ padding: { left: 100 }});
centerElement(element [, opt])

Position the center of element (an instance of joint.dia.Element) to the center of the paper scroller viewport.

paperScroller.centerElement(element);
paperScroller.centerContent(element, { padding: { left: 100 }});
positionContent(positionName [, opt])

Align paper content inside the paper scroller viewport as specified by positionName. There are 9 possible positionName values: 'top', 'top-right', 'right', 'bottom-right', 'bottom', 'bottom-left', 'left', 'top-left', 'center'. An appropriate point of the paper content area is positioned to the corresponding point inside the paper scroller viewport. For example, if positionName was 'bottom-left', the bottom-left corner of paper content area is positioned to the bottom-left corner of the paper scroller viewport (and inside requested paper scroller paddings, if provided).

paperScroller.positionContent('bottom-left');
paperScroller.centerContent('bottom-left', { padding: { horizontal: 20, vertical: 10 }});
positionElement(element, positionName [, opt])

Align element (an instance of joint.dia.Element) inside the paper scroller viewport as specified by positionName (detailed above). An appropriate point of the element bbox is positioned to the corresponding point inside the paper scroller viewport, similar to positionContent method.

paperScroller.positionElement(element, 'right');
paperScroller.centerContent('right', { padding: { right: 50 }});
positionRect(rect, positionName [, opt])

Align a custom rect inside the paper scroller viewport as specified by positionName (detailed above). An appropriate point of the rectangle is positioned to the corresponding point inside the paper scroller viewport, similar to positionContent method.

paperScroller.positionElement(element1.getBBox().union(element2.getBBox()), 'left-top');
paperScroller.centerContent(path.bbox(), 'right-top', { padding: { vertical: 10, right: 50 }});
positionPoint(point, x, y [, opt])

Position a custom point on the paper (in local coordinates) to the point (x,y) inside the paper scroller viewport (in client coordinates). This is useful when you need complete control over what to position where. Percentage values are allowed for x and y; the percentages are relative to the paperScroller area (only the area inside padding, if padding is provided). Negative values/percentages cause the algrithm to look from opposite edge (i.e. from right/bottom).

The following example positions the origin of the paper to the origin (top-left point) of the paper scroller viewport:

paperScroller.positionPoint(new g.Point(0,0), 0, 0);
paperScroller.positionPoint(new g.Point(0,0), '-100%', '-100%'); // same result

The following example positions point to the point 25% of the way between the right and the left edge of the paper scroller viewport and 40px above the bottom edge:

paperScroller.positionPoint(point, '25%', -40);

The following example positions point to the point 30px away from right edge of the paper scroller viewport (10px + 20px padding) and 20px above bottom edge (10px + 10px padding):

paperScroller.positionPoint(point, 10, -10, { padding: { horizontal: 20, vertical: 10 }})

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 contained paper more easily:

paperScroller.zoom();

Return the current zoom level of the paper. Technically speaking, the zoom level is the scaling factor (sx) of the paper's SVGElement.

paperScroller.zoom(value [, opt]);

The zoom() method accepts a value by which we want to increase/decrease the scaling factor of the paper. A positive value increases the scaling factor, which causes the paper to be zoomed in. A negative value decreases the scaling factor, which causes the paper to be zoomed out.

You can pass two options to cap the range of possible scaling factors as part of an opt object - min and max. The scaling factor cannot then be decreased below min (if provided) or increased above max (if provided). These options are often useful in practice, with UI buttons bound to the paperScroller's zooming operations:

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

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

Additionally, passing a value for the grid option causes the resulting scaling factor to be rounded to the closest multiple of the grid value.

Passing absolute: true as an option sets the scaling factor directly to the provided value (instead of treating it as a relative value). The constraints imposed by the min, max, and grid are still in effect, however.

paperScroller.zoomToFit([opt]);

Scale the paper so that all of the contents of its graph fit into the user's viewport. For a list of available options, refer to paper.scaleContentToFit() documentation.

paperScroller.zoomToRect(rect [, opt]);

Zoom and reposition the paper so that the area defined by rect (in the paper local coordinates) fits into the user's viewport. For a list of available options, refer to paper.scaleContentToFit() documentation.

Animated transitions

The paperScroller provides few methods for panning and zooming in animated fashion. These are:

paperScroller.transitionToPoint(x, y [, opt])
paperScroller.transitionToPoint(point [, opt])

The method pans and optionally zooms the paperScroller to a given point. It accepts a point defined in the paper local coordinate system and an option object with several properties to control the transition.

scale The zoom level to reach at the end of the transition. It defaults to the current paperScroller zoom level.
duration A string representing the number of seconds or milliseconds a transition animation should take to complete. E.g. '500ms', '2s'. It defaults to '1s'
delay A string representing the amount of time to wait before a transition starts. It defaults to '0s'
timingFunction The option to describe how the intermmediate pan positions and zoom values are calculated. For all available values visit CSS transition-timing-function property documentation. It defaults to 'ease'.
onTransitionEnd A callback function fired with transitionend event as its first parameter when the transition ends.
// Move paper point (x=100, y=100) to the center of the viewport.
paperScroller.transitionToPoint(100, 100);
// Move and scale paper, so the point { x: 100, y: 100 } will appear in the center of the viewport.
paperScroller.transitionToPoint(100, 100, { scale: 2 });
// Move the the center of the `element` to the center of the viewport.
paperScroller.transitionToPoint(element.getBBox().center(), {
    duration: '500ms',
    onTransitionEnd: function(transitionEvent) {
      // do something when the animation ends
    }
});
paperScroller.transitionToRect(rect [, opt]);

The method pans and zooms the paperScroller over a specific period so the given rectangular area is at the end of the transition entirely visible in the viewport. The rectangular area is an object with x, y, width and height properties and defined in the paper local coordinate system. The method accepts all options from transitionToPoint() (but scale) and the following options from the table below.

center An object with x and y properties, which defines the point to be in the center of the viewport when the transition ends. By default the center of the rectangular area is used.
visibility A number to change the percentage coverage of the viewport. The rectangular area covers 100% of the viewport by default (i.e. { visibility: 1 }). For instance 80% coverage could be set with { visibility: .8 }
minScale A number to make the resulting scale always greater than or equal to minScale value.
maxScale A number to make the resulting scale always less than or equal to maxScale value.
scaleGrid A number to make the resulting scale the largest multiple of scaleGrid value less than or equal to the calculated scale.
// Zoom the paperScroller so the given area covers 90% of the viewport.
// Also do not let the paperScroller exceed the zoom level 3.
paperScroller.transitionToRect({
  x: 100,
  y: 100
  width: 200,
  height: 200
}, {
    visibility: 0.9,
    maxScale: 3,
    onTransitionEnd: function(transitionEvent) {
      // do something when the animation ends
    }
});
// Zoom the paperScroller in a way the elements `el1`, `el2`, `el3` cover the entire viewport
// and the center of the `el1` moves to the center of the viewport.
var rect = graph.getCellsBBox([el1, el2, el3]);
paperScroller.transitionToRect(rect, {
    duration: '500ms',
    center: el1.getBBox().center()
});
paperScroller.removeTransition();

When method called, the ongoing (if any) paperScroller transition is removed and the associated onTransitionEnd callback is not fired.

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. contentOptions can be also defined as a function returning the options.

new joint.ui.PaperScroller({
  padding: 0,
  contentOptions: function(paperScroller) {
    var visibleArea = paperScroller.getVisibleArea();
    return {
        padding: {
            bottom: visibleArea.height / 2,
            top: visibleArea.height / 2,
            left: visibleArea.width / 2,
            right: visibleArea.width / 2
        },
        allowNewOrigin: 'any'
    };
  }
});

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.PathDrawer

Path drawer allows users to draw <path> elements.

Installation

Include joint.ui.pathDrawer.css and joint.ui.pathDrawer.js files to your HTML.

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

Create a PathDrawer

First, we create a PathDrawer object:

new joint.ui.PathDrawer({
    target: document.getElementById('example-canvas'),
    pathAttributes: {
        'class': 'example-path'
    }
});
The constructor needs a target, a reference to an <svg> SVGSVGElement on the page, which will act as a drawing area. We can use one we created previously in the DOM, for example:

<svg id="example-canvas" width="800" height="800"></svg>

We can also use the .svg property of an existing Paper.

Configuration

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

target SVGSVGElement Required. The drawing area. Any paths drawn by the user are appended to this <svg> element. They are not removed when the PathDrawer is removed.
pathAttributes Object Optional. An object with CSS attributes of the new path. Default values are:
{
    'class': null,
    'fill': '#ffffff',
    'stroke': '#000000',
    'stroke-width': 1,
    'pointer-events': 'none'
}
If your pathAttributes object sets the 'class' attribute (e.g. to 'example-path'), you can access the path for further styling through the #example-canvas .example-path CSS selector.
startPointMarkup string Optional. The SVG markup of control point overlay elements. Default is '<circle r="5"/>'. (CSS styling should use the .joint-path-drawer .start-point CSS selector.)
snapRadius number Optional. If set to a number greater than zero, the endpoint of the current segment snaps to the x- and y-values of previously drawn segment endpoints. The number determines the distance for snapping.

API

The joint.ui.PathEditor object provides the following methods:

render() Renders the path drawer. Called automatically after object is instantiated.
remove() Removes the path drawer.

For more fine-tuned programmatic control, you can emulate user interaction with delegated or synthetic mouse events. For example, to start drawing a path immediately after a pathDrawer is initialized:

paper.on('blank.pointerdown', function(event) {
    var vCanvas = V('svg', {
        width: 400,
        height: 400
    }).appendTo(this.paper.el);

    var pathDrawer = this.pathDrawer = new joint.ui.PathDrawer({
        el: vCanvas.node
    };

    pathDrawer.onPointerDown(event);
}, this)

Interaction

Users can interact with joint.ui.PathDrawer objects by clicking/touching its canvas:

mousedown touchstart
.start-point Calls onStartPointPointerDown(event). Closes the path.
Calls onPointerDown(event). If this is the first click of a path, creates a new path with a new start point. Otherwise, finishes previous action and starts a new one. If the user does not release the mouse and triggers a bound mousemove/touchmove event, this function starts drawing a curveto control point until a mouseup/touchend event is registered. Otherwise, starts drawing a lineto path segment.
mousemove Calls onPointerMove(event). If the user is currently drawing a path segment, the path endpoint moves with user pointer.
dblclick Calls onDoubleClick(event). Stops path drawing at the location of the double click. The path is not finished with a closepath segement.
contextmenu Calls onContextMenu(event). Stops path drawing at the location of the previous anchor point. The path is not finished with a closepath segment.

Events

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

clear Triggered when the pathDrawer is cleared.
path:create Triggered when a new path is created. The handler is passed a reference to the svgPath SVGPathElement.
path:finish Triggered when a path is finished in a valid manner. The handler is passed a reference to the svgPath SVGPathElement. The following specific events are triggered alongside this generic event:
path:close Triggered when a path is finished by being reconnected with the start point. The handler is passed a reference to the svgPath SVGPathElement.
path:stop Triggered when a path is finished by being stopped without reconnectiong with the start point (e.g. due to a double click or a right click). The handler is passed a reference to the svgPath SVGPathElement.
path:abort Triggered when a path is finished in an invalid manner (e.g. closed with only one anchor point). The handler is passed a reference to the svgPath SVGPathElement.
path:segment:add Triggered when the user adds a new segment to path segment list. The handler is passed a reference to the svgPath SVGPathElement. The following specific events are triggered alongside this generic event:
path:move-segment:add Triggered when the user adds a moveto segment to path segment list. The handler is passed a reference to the svgPath SVGPathElement.
path:line-segment:add Triggered when the user adds a lineto segment to path segment list. The handler is passed a reference to the svgPath SVGPathElement.
path:curve-segment:add Triggered when the user adds a curveto segment to path segment list. The handler is passed a reference to the svgPath SVGPathElement.
path:edit Triggered when the user edits the path. The handler is passed a reference to the svgPath SVGPathElement. The following specific events are triggered alongside this generic event:
path:last-segment:adjust Triggered when the user adjusts last added segment. (This happens, for example, when the user specifies a control point after having added a curveto segment.) The handler is passed a reference to the svgPath SVGPathElement.
path:last-segment:replace-with-curve Triggered when the user replaces last added line segment with a curved segment. (This happens, for example, when the user begins to specify a control point after having added a lineto segment.) The handler is passed a reference to the svgPath SVGPathElement.
path:last-segment:remove Triggered when the user removes last added segment. (This happens, for example, when the user stops drawing with a right-click). The handler is passed a reference to the svgPath SVGPathElement.
path:interact Triggered when the user interacts with drawer overlay elements. The handler is passed a reference to the svgPath SVGPathElement. The following specific events are triggered alongside this generic event:
path:control:adjust Triggered when the user adjusts a control path. (This happens when the user specifies a control for a curveto segment.) The handler is passed a reference to the svgPath SVGPathElement.
path:control:remove Triggered when a control path is removed from the drawer interface. (This happens when the user is finished drawing a curve.) The handler is passed a reference to the svgPath SVGPathElement.

ui.PathEditor

Path editor allows users to edit <path> elements via an editing overlay. Users can interact with the path's segments, anchor points and curve control points.

Installation

Include joint.ui.pathEditor.css and joint.ui.pathEditor.js files to your HTML.

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

Create a PathEditor

First, we create a PathEditor object. The constructor needs a reference to a <path> SVGPathElement on the page.

new joint.dia.PathEditor({
    pathElement: document.querySelector('path')
});

Configuration

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

pathElement SVGPathElement Required. The path element to be edited. The PathEditor will be rendered on top of it.
anchorPointMarkup string Optional. The SVG markup of anchor point overlay elements. Default is '<circle r="2.5"/>'. (CSS styling should use the .joint-path-editor .anchor-point CSS selector.)
controlPointMarkup string Optional. The SVG markup of control point overlay elements. Default is '<circle r="2.5"/>'. (CSS styling should use the .joint-path-editor .control-point CSS selector.)

API

The joint.ui.PathEditor object provides the following methods:

render() Render the path editor overlay. Called automatically after object is instantiated.
remove() Remove the path editor.
adjustAnchorPoint(index, dx, dy, evt [, opt]) Adjust the position of an anchor point. Identified by index, the index of the anchor point within path segment list (starting from 0 for the endpoint of the first moveto segment). The anchor point is translated by (dx,dy). The evt is the original user interaction event.

The opt parameter is optional; it is used by internal methods. It can have one property: dry (indicating that corresponding 'path:editing' and 'path:edit' events will be suppressed).
adjustControlPoint(index, controlPointIndex, dx, dy, evt [, opt]) Adjust the position of a control point. Identified by index, the index of the path segment it is attached to within path segment list (starting from 1 for the first non-moveto segment), and controlPointIndex, the index of the control point within the segment (1 for the first control point - the one visually attached to the previous anchor point, or 2 for the second control point - the one visually attached to this segment's anchor point). The control point is translated by (dx,dy). The evt is the original user interaction event.

The opt parameter is optional; it is used by internal methods. It can have one property: dry (indicating that corresponding 'path:editing'/'path:edit' events will be suppressed).
adjustSegment(index, dx, dy, evt) Adjust the position of a segment. Identified by its index within path segment list (starting from 1 for the first non-moveto segment). The evt is the original user interaction event.
getControlPointLockedStates() Return an array of arrays that records, for every control point (identified as [index][controlPointIndex]), whether it is currently locked with another point (true) or not (false). In tandem with setControlPointLockedStates(lockedStates), the function can be used to preserve locking information across multiple path editor sessions for a given element.
setControlPointLockedStates(lockedStates) Change the locked status of the editor's control points, so they correspond to the information in the given lockedStates list (e.g. the output of the getControlPointLockedStates() function). A true value adds the 'locked' class to the control point element; a false value removes the class.

Interaction API

The following methods are meant to be called in response to user interaction events (e.g. click, touch, dblclick). Some of them are bound with user interactions by default (see the Interaction section), but others are not. Those may be called upon by delegated events from your application. For example:

pathEditor.delegate('contextmenu', '.segment-path', function(event) {
    event.stopPropagation();
    event.preventDefault();

    this.convertSegmentPath(event);
}.bind(pathEditor));
startMoving(event) Bound with 'mousedown'/'touchstart' events by default.

The selected anchor point / control point / segment path starts moving with user pointer. Trigger a corresponding 'path:interact' event.

To start listening for the move() and stopMoving() methods, call delegateDocumentEvents().
move(event) Bound with 'mousemove'/'touchmove' events by default (after delegateDocumentEvents() is called).

The selected anchor point / control point / segment path moves with user pointer. Trigger corresponding 'path:editing' events.
stopMoving(event) Bound with 'mouseup'/'touchend'/'touchcancel' events by default (after delegateDocumentEvents() is called).

The selected anchor point / control point / segment path stops moving with user pointer. Trigger a corresponding 'path:edit' event.

To stop listening for the move() and stopMoving() methods, call undelegateDocumentEvents().
createAnchorPoint(event) Bound with 'dblclick .segment-path' event by default.

If event is targeted on a segment path, create an anchor point at the location of the user click and insert it into the path segment list. Trigger the 'path:anchor-point:create' event.

Note: Due to current limitations, the function does not work properly for closepath segments in paths that have 2 or more closepath segments (the anchor points are created at wrong position).
removeAnchorPoint(event) Bound with 'dblclick .anchor-point' event by default.

If event is targeted on an anchor point, remove it. Trigger the 'path:anchor-point:remove' event. If only one anchor point remains in the path after the removal, also trigger the 'path:invalid' event.
lockControlPoint(event) Bound with 'dblclick .control-point' event by default.

If event is targeted on a control point of a curveto segment, and the control point is immediately adjacent to a control point of another curveto segment, toggle its locked status. Trigger the corresponding 'path:control-point:lock'/'path:control-point:unlock' event.

Note that the locking action necessarily causes a change in the path markup which does not trigger an 'path:edit' event!
addClosePathSegment(event) Not bound with any user interaction by default.

If event is targeted on the first or last anchor point, and the path segment list contains no closepath segment, append a closepath segment to the path segment list.
removeClosePathSegment(event) Not bound with any user interaction by default.

If event is targeted on a closepath segment, remove the segment from the path segment list.
convertSegmentPath(event) Not bound with any user interaction by default.

If event is targeted on a path segment, convert the segment from linear to curved or vice versa. A curveto segment is replaced by a lineto segment. A lineto/closepath segment is replaced by a curveto segment whose control points are coincident with anchor points; a replacement closepath segment is appended to the path segment list if the converted segment was a closepath segment.

Note: Due to current limitations, the function does not work properly for closepath segments in paths that have 2 or more closepath segments (the anchor points are created at wrong position).

Interaction

Users can interact with joint.ui.PathEditor objects by clicking/touching editor overlay elements:

mousedown touchstart
.anchor-point Call onAnchorPointPointerDown(event), which calls startMoving() followed by delegateDocumentEvents() by default.
.control-point Calls onControlPointPointerDown(event), which calls startMoving() followed by delegateDocumentEvents() by default.
.segment-path Calls onSegmentPathPointerDown(event), which calls startMoving() followed by delegateDocumentEvents() by default.

Double-clicking editor overlay elements triggers additional actions:

dblclick
.anchor-point Calls onAnchorPointDoubleClick(event), which calls removeAnchorPoint() by default.
.control-point Calls onControlPointDoubleClick(event), which calls lockControlPoint() by default.
.segment-path Calls onSegmentPathDoubleClick(event), which calls createAnchorPoint() by default.

For the documentation of the called functions, see the Interaction API section.

Events

The joint.ui.PathEditor object triggers various events that you can react on:

clear Triggered when the pathEditor is cleared.
path:interact Triggered when the user interacts with (clicks/touches) the path.

The following specific events are triggered alongside this generic event:
path:anchor-point:select Triggered when the user interacts with an anchor point.

The handler is passed the index of the segment that this anchor point is bound to in the path segment list. The first anchor point on the path has an index of 0.
path:control-point:select Triggered when the user interacts with a control point.

The handler is passed two values. The first value is the index of the segment that this control point is bound to in the path segment list. (Since the first moveto segment cannot have control points, the minimal index is 1.) The second value is the controlPointIndex of the control point. It can be either 1 or 2.
path:segment:select Triggered when the user interacts with a path segment.

The handler is passed the index of the segment in the path segment list. (Since the first moveto segment is not clickable, the minimal index is 1.)
path:control-point:lock Triggered when the user successfully locks a control segment (binds it with a control point in an adjacent curveto path).

The handler is passed two values. The first value is the index of the segment that this control point is bound to in the path segment list. (Since the first moveto segment cannot have control points, the minimal index is 1.) The second value is the controlPointIndex of the control point. It can be either 1 or 2.
path:control-point:unlock Triggered when the user unlocks a control segment (stops its binding with a control point in an adjacent curveto path).

The handler is passed two values. The first value is the index of the segment that this control point is bound to in the path segment list. (Since the first moveto segment cannot have control points, the minimal index is 1.) The second value is the controlPointIndex of the control point. It can be either 1 or 2.
path:editing Triggered while the user is editing the path.

The handler is passed a reference to the updated svgPath SVGPathElement, and evt (the user interaction event).

The following specific events are triggered alongside this generic event:
path:anchor-point:adjusting Triggered while the user is adjusting the position of an anchor point.

The handler is passed a reference to the updated svgPath SVGPathElement, evt (the user interaction event), and an opt object with two properties: index (the index of the adjusted anchor point), and segPoint (the new position of the anchor point).
path:control-point:adjusting Triggered while the user is adjusting the position of a control point.

The handler is passed a reference to the updated svgPath SVGPathElement, evt (the user interaction event), and an opt object with three properties: index (the index of the associated anchor point), controlPointIndex (the index of the adjusted control point), and segPoint (the new position of the anchor point).
path:segment:adjusting Triggered while the user is adjusting the position of a segment.

The handler is passed a reference to the updated svgPath SVGPathElement, evt (the user interaction event), and an opt object with one property: index (the index of the adjusted segment).
path:edit Triggered when the user edits the path.

The handler is passed a reference to the updated svgPath SVGPathElement, and evt (the user interaction event).

The following specific events are triggered alongside this generic event:
path:anchor-point:adjust Triggered when the user adjusts the position of an anchor point.

The handler is passed a reference to the updated svgPath SVGPathElement, evt (the user interaction event), and an opt object with two properties: index (the index of the adjusted anchor point), and segPoint (the new position of the anchor point).
path:control-point:adjust Triggered when the user adjusts the position of a control point.

The handler is passed a reference to the updated svgPath SVGPathElement, evt (the user interaction event), and an opt object with three properties: index (the index of the associated anchor point), controlPointIndex (the index of the adjusted control point), and segPoint (the new position of the anchor point).
path:segment:adjust Triggered when the user adjusts the position of a segment.

The handler is passed a reference to the updated svgPath SVGPathElement, evt (the user interaction event), and an opt object with one property: index (the index of the adjusted segment).
path:anchor-point:create Triggered when the user adds a new anchor point to the path.

The handler is passed a reference to the updated svgPath SVGPathElement, and evt (the user interaction event).
path:anchor-point:remove Triggered when the user removes an anchor point from the path.

The handler is passed a reference to the updated svgPath SVGPathElement, and evt (the user interaction event).
path:segment:convert Triggered when the user converts a lineto path segment into a curveto segment or vice versa.

The handler is passed a reference to the updated svgPath SVGPathElement, and evt (the user interaction event).
path:closepath-segment:add Triggered when the user adds a closepath segment to the path.

The handler is passed a reference to the updated svgPath SVGPathElement, and evt (the user interaction event).
path:closepath-segment:add Triggered when the user removes a closepath segment from the path.

The handler is passed a reference to the updated svgPath SVGPathElement, and evt (the user interaction event).
path:invalid Triggered when user modifications result in an invalid path (e.g. after removing, a path that has only one anchor point).

The handler is passed a reference to the svgPath SVGPathElement, and evt (the user interaction event).

You can react to these events by using the on() method:

pathEditor.on({
    'path:control-point:select': console.log('started moving control point'),
    'path:control-point:adjusting': console.log('moving control point'),
    'path:control-point:adjust': console.log('stopped moving control point')
});

It is also possible to specify a custom context for the on() method. This is useful when you need to handle user interaction on your PathEditor from within another Backbone object, for instance:

pathEditor.on({
    'path:edit': this.onPathEdit.bind(this),
}, this);

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:

options Object[] Optional. An array of items (the buttons). Each item is an object with the following properties:
content string Optional. The content of the item. It can be either a plain string or a string containing HTML code.
value any Optional. The value of the item. It can be a string, a number or any other JavaScript object. By default, the value is considered to be equal to the content property.
attrs Object Optional. A hierarchical object that adds styling to component HTML elements of the option. For example, the following attrs object would add a tooltip to the option:
{
  '.select-button-group-button': {
    'data-tooltip': 'My tooltip'
  }
}
selected boolean Optional. Should this item be selected by default? Default is false.
icon string Optional. A path to an image to be added to the option's button.
iconSelected string Optional. A path to an image to be added to the option's button when it is selected. By default, the selected state uses icon (if provided).
buttonWidth number Optional. The width of the button in pixels.
buttonHeight number Optional. The height of the button in pixels.
iconWidth number Optional. The width of icon in pixels (if provided).
iconHeight number Optional. The height of icon in pixels (if provided).
disabled boolean Optional. Is the user prevented from interacting with this ui.SelectButtonGroup? Default is false.
multi boolean Optional. Is this a multiple-choice set? (That is, is it allowed for multiple buttons to be selected at the same time?) Default is false.
selected number | number[] Optional. Which options should be selected by default?
If multi === false, expects a number - the index of the selected option.
If multi === true, expects an array of numbers - indices of all selected options.
By default, this property is undefined. In that case, the widget determines selected items according to individual options' selected property.
singleDeselect boolean Optional. If multi === false, is the user allowed to deselect the currently-selected icon by clicking on it? Default is false (radio-button-like behavior).
noSelectionValue any Optional. If nothing is selected, what value should be reported by the getSelectionValue() function? Default is undefined.
width number Optional. The width of the whole widget in pixels.
buttonWidth number Optional. The width of each button in pixels. Overridden by individual options' buttonWidth property (if provided).
buttonHeight number Optional. The height of each button in pixels. Overridden by individual options' buttonHeight property (if provided).
iconWidth number Optional. The width of each icon in pixels (for options with an icon property). Overridden by individual options' iconWidth property (if provided).
iconHeight number Optional. The height of each icon in pixels (for options with an icon property). Overridden by individual options' iconHeight property (if provided).

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). If nothing is selected, noSelectionValue is returned (default is undefined).
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).
disable() Prevent to the user's interactions.
enable() Make the button group available to the user's interactions.
isDisabled() Check if the button group is prevented to the user's interactions.

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.
createSelectionBox(element) Create a single selection box above an element.
destroySelectionBox(element) Destroy a single selection box, which is rendered above an element.

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 starts interacting with a selected element, an element that has a selection box above it. The handler gets passed the element view, the DOM event object, x and y paper local coordinates. Use elementView.model if you wish to access the actual element model. See below in the Common Setup section how that can be used.
selection-box:pointermove Triggered when the user is moving with a selected element, an element that has a selection box above it. The handler gets passed the element view, the DOM event object, x and y paper local coordinates.
selection-box:pointerup Triggered when the user stops interacting with a selected element, an element that has a selection box above it. The handler gets passed the element view, the DOM event object, x and y paper local coordinates.

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]
searchobject - An 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']
}
function - A function executed on every cell in the stencil returning true (matched) or false (unmatched).
search: function(cell, keyword, group, stencil) {
    var description = cell.get('description');
    return typeof description === 'string' && description.indexOf(keyword) > -1;
}
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.
paperOptions An object with joint.dia.Paper options to adjust the look and behaviour of the stencil's papers. e.g.
paperOptions: {
  elementView: CustomElementView,
  background: { color: 'red' }
}
This options can be also used to adjust a single paper (and takes precedence) when defined inside of the group definition. e.g.
groups: {
  myGroup: {
    index: 1,
    label: 'My Group',
    paperOptions: { color: 'blue' }
  }
}
To adjust the graph in Stencil papers it's also possible to pass a model as well. Please note, the paperOptions has to be a function in such a case:
paperOptions: function() {
  return {
    elementView: CustomElementView,
    model: new joint.dia.Graph([], { cellNamespace: CustomElementNamespace }),
    background: { color: 'red' }
  }
}

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: (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: (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).
group:open Triggered when the user opens a closed group. The handler has the following signature: (groupName)
group:close Triggered when the user closes an open group. The handler has the following signature: (groupName)

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 auto-sizing on the text inside our elements so that they stretch based on the bounding box inside it.

// RECOMMENDED

paper.on('element:pointerdblclick', function(elementView, evt) {
    joint.ui.TextEditor.edit(evt.target, {
        cellView: elementView,
        textProperty: ['attrs', 'label', 'text'],
        annotationsProperty: ['attrs', 'label', 'annotations']
    });
});

paper.on('link:pointerdblclick', function(linkView, evt) {
    // Editing a link label
    var index = Number(linkView.findAttribute('label-idx', evt.target));
    joint.ui.TextEditor.edit(evt.target, {
        cellView: linkView,
        textProperty: ['labels', index, 'attrs', 'text', 'text'],
        annotationsProperty: ['labels', index, 'attrs', 'text', 'annotations']
    });
});

function autoSize(element) {

    var view = paper.findViewByModel(element);
    var textVel = view.vel.findOne('text');
    // Use bounding box without transformations so that our auto-sizing works
    // even on e.g. rotated element.
    var bbox = textVel.getBBox();
    // 16 = 2*8 which is the translation defined via ref-x ref-y for our rb element.
    element.resize(bbox.width + 16, bbox.height + 16);
}

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.

Source Code

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 placeholder 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.
findAnnotationsUnderCursor(annotations, selectionStart) Find all the annotations in the `annotations` array that the cursor at `selectionStart` position falls into.
findAnnotationsInSelection(annotations, selectionStart, selectionEnd) Find all the annotations that fall into the selection range specified by `selectionStart` and `selectionEnd`. This method assumes the selection range is normalized.
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 Toolbar 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.
autoToggle boolean When set to true, the widgets switch their state (enabled/disabled) automatically based on the their references.
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](#ui.SelectButtonGroup) section.
zoomIn | zoomOut | zoomToFit
minnumber0.2
maxnumber5
stepnumber0.2
autoTogglebooleanfalse

 

Required references:

paperScroller ui.PaperScroller instance

zoomSlider
minnumber20
maxnumber500
stepnumber20
valuenumber100
unitnumber'%'

 

Required references:

paperScroller ui.PaperScroller instance

undo | redo
autoTogglebooleanfalse

 

Required references:

commandManager dia.CommandManager instance

fullscreen

Presents an element (target

targetHTMLElement|string selectorwindow.top.document.body

Every widget has the following API:

enable() Enable the widget. Example:
toolbar.getWidgetByName('undo').enable();
disable() Disable the widget (make the widget not to respond to user actions)
isDisabled() Return true if the widget is disabled. Return false otherwise.

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 mouseover">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>
animation boolean | object When set to true a CSS fade-in animation is applied to the tooltip. Passing an object with any of the following properties allows you to make further adjustments to the animation.
Property Default Description
duration '500ms' CSS animation-duration
delay '400ms' CSS animation-delay
timingFunction 'ease' CSS animation-timing-function

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;.
validateConnection(element, candidateParent, treeLayoutView) A predicate, that returns true if the element, which the user interacts with, can be reconnected to the candidateParent. It defaults to return true;.
reconnectElements(elements, parent, siblingRank, direction, treeLayoutView) A function to override the default connection behaviour (this includes connecting sources and reconnecting children).
translateElements(element, x, y, treeLayoutView) A function to override the default translating behaviour (this includes translating sources and disconnecting children).
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 }
}
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

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.