🎉 JointJS has new documentation! 🥳
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) :
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.
Include joint.ui.clipboard.js
to your HTML:
<script src="joint.ui.clipboard.js"></script>
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 . |
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. |
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
is a UI widget for displaying dropdowns with color palette.
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>
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);
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:
|
||||||
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 deselectthe 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.
|
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 ).
|
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
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.
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>
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();
});
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:
|
||||||||
---|---|---|---|---|---|---|---|---|---|
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 .
|
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.
|
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
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.
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>
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();
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] } .
|
||||||
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 crossbutton 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.
|
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 ).
|
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
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.
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>
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 });
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.
|
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 ).
|
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. |
---|
FreeTransfrom plugin creates a control panel above an element giving an user the ability to resize an element in any direction or rotate it.
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>
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();
});
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.
|
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. |
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.
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>
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.
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. |
The joint.ui.Halo
constructor function takes an options object as an argument. The options
object can have the following parameters:
cellView | an 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. |
---|---|
type | the 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. |
loopLinkPreferredSide | the preferred side for a self-loop link created from Halo ("top"|"bottom"|"left"|"right" ), default is "top" |
loopLinkWidth | the self-loop link width in pixels, default is 40 |
rotateAngleGrid | the angle increments the rotate action snaps to, default is 15 |
rotateEmbeds | should be elements embedded inside the cellView element rotated as well? Default is false |
boxContent | a 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 |
clearAll | if 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.
|
useModelGeometry | if 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 outof the view and you don't want these sub elements to affect the Halo tools position. In this case, set the useModelGeometry to true . |
clone | a 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.
|
bbox | A 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.
|
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; }
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' })
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.
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.
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.
|
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:
|
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.
|
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.
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>
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
}
}
});
});
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 accordiontype 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 :
|
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 e.g |
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:
|
||||||||||||||||||||
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:
|
||||||||||||||||||||
options | object | an array of options for a several input types (`"select"`, `"select-box"`, `"select-button-group"`, `"color-palette"`). Can be defined by:
|
||||||||||||||||||||
min | number |
|
||||||||||||||||||||
max | number |
|
||||||||||||||||||||
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 modeon 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. |
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.
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.
where:
path | Is a string determining a property of the model (e.g 'attrs/text/text', 'property') | ||||||||||||||||||||||
value | Is a number, string or an array (e.g 13, 'jointjs', [1,3,5]) | ||||||||||||||||||||||
primitive | Can be one of the following:
returns `false` otherwise. |
||||||||||||||||||||||
unary_operator | Accepts exactly one expression
|
||||||||||||||||||||||
multiary_operator | Accepts at least one expression
|
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);
}
}
})
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 }}}
}
});
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'
}
};
}
}));
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);
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;
}
});
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. |
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. |
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. |
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).
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` |
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
})
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.
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>
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();
});
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).
|
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 ).
|
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. |
---|
Paper scroller wraps the paper element and implements scrolling, panning, centering and auto-resizing of the paper content.
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>
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');
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 (
|
scrollToContent([opt]) |
Try to scroll the paper scroller so that the center of paper content is at the center of the paper scroller viewport.
|
scrollToElement(element [, opt]) |
Try to scroll the paper scroller so that the center of
|
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.
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.
|
center(x, y [, opt]) |
Position the point (
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.
|
centerContent([opt]) |
Position the center of paper content to the center of the paper scroller viewport.
|
centerElement(element [, opt]) |
Position the center of
|
positionContent(positionName [, opt]) |
Align paper content inside the paper scroller viewport as specified by
|
positionElement(element, positionName [, opt]) |
Align
|
positionRect(rect, positionName [, opt]) |
Align a custom
|
positionPoint(point, x, y [, opt]) |
Position a custom The following example positions the origin of the paper to the origin (top-left point) of the paper scroller viewport:
The following example positions
The following example positions
|
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).
|
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.
|
isPointVisible(point) | Returns true if the point (e.g. { x: 100, y: 200 } ) is visible in the current paperScroller viewport. It returns false otherwise. |
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.
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.
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 });
Path drawer allows users to draw <path>
elements.
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>
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.
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:
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. |
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)
Users can interact with joint.ui.PathDrawer
objects by clicking/touching its canvas:
mousedown touchstart |
|
||||
---|---|---|---|---|---|
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.
|
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: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: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: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 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.
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>
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')
});
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.)
|
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 firstcontrol point - the one visually attached to the previous anchor point, or 2 for the secondcontrol 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.
|
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). |
Users can interact with joint.ui.PathEditor
objects by clicking/touching editor overlay elements:
mousedown touchstart |
|
---|
Double-clicking editor overlay elements triggers additional actions:
dblclick |
|
---|
For the documentation of the called functions, see the Interaction API section.
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: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: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: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
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).
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>
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
});
});
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 .
|
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
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.
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>
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);
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);
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:
|
||||||||
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 deselectthe 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.
|
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 ).
|
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
is a UI widget for displaying groups of buttons with both single and multi selection support. The buttons can contain arbitrary HTML.
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>
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);
ui.SelectButtonGroup
with iconsThe 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:
|
||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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).
|
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. |
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. |
Selection implements elements selection, moving the selection in one go and manipulating the selection in terms of selecting/deselecting individual elements.
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>
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.
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'));
});
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;
}
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; }
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' })
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:
|
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 outof 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:
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.
|
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. |
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.
|
The following is a common setup of the Selection in applications.
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);
}
});
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);
}
});
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);
});
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.
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>
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()
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.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;
}
Stencil plugin implements a palette of JointJS elements. These elements can be dragged onto a paper.
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>
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.
The joint.ui.Stencil
constructor function takes an options object as an argument. The options
object can have the following parameters:
paper | the joint.dia.Paper or joint.ui.PaperScroller object [mandatory] |
---|---|
width | the width of the stencil [mandatory] |
height | the height of the stencil [mandatory] |
search | object - 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.
function - A function executed on every cell in the stencil returning true (matched) or false (unmatched).
See the Searchable Stencil section for more information.
|
groups | An 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:
|
groupsToggleButtons | If set to true buttons for expanding/collapsing groups (expand all/collapse all) are rendered into the stencil. It defaults to false . |
paperPadding | A number specifying the padding that will be used in the internal papers that hold the elements in the stencil. It defaults to 10 . |
dropAnimation | An 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:
|
label | A string or HTML Element rendered at the top of the stencil. It defaults to `Stencil`. |
layout | if 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.
|
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.
|
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(). |
snaplines | An instance of the Snapline plugin which is responsible for drawing snaplines while the user drags an element from the stencil. |
scaleClones | When 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.
This options can be also used to adjust a single paper (and takes precedence) when defined inside of the group definition. e.g.
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:
|
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.
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:
label | group label. Can be either a string or a html . |
---|---|
index | position 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 }
}
});
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.
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:
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(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. |
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.');
}
});
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
is a powerful (rich-text) inline text editor that can be used on any SVG text element.
The ui.TextEditor
provides the following features:
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>
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 CodeThe 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:
|
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:
|
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:
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:
|
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.
|
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:
|
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:
|
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).
|
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'.
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);
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.
|
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`. |
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. |
checkbox |
|
||||||||||||||||
toggle |
|
||||||||||||||||
separator |
Vertical line by default.
|
||||||||||||||||
label |
|
||||||||||||||||
range |
|
||||||||||||||||
selectBox | Configuration is described in ui.SelectBox section. | ||||||||||||||||
button |
|
||||||||||||||||
inputText |
|
||||||||||||||||
inputNumber |
|
||||||||||||||||
textarea |
|
||||||||||||||||
selectButtonGroup | Configuration is described in [ui.SelectButtonGroup](#ui.SelectButtonGroup) section. | ||||||||||||||||
zoomIn | zoomOut | zoomToFit |
|
||||||||||||||||
zoomSlider |
|
||||||||||||||||
undo | redo |
|
||||||||||||||||
fullscreen |
Presents an element (target
|
Every widget has the following API:
enable() | Enable the widget. Example:
|
disable() | Disable the widget (make the widget not to respond to user actions) |
isDisabled() | Return true if the widget is disabled. Return false otherwise. |
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);
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
}
});
Use tooltips to display information messages anywhere in the UI.
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>
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>
In this demo all the elements with the data attribute tooltip
will display tooltip when hovered.
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 `' '`.
or
|
||||||||||||
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.
|
||||||||||||
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.
|
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
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.
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>
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();
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 outof 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.
|
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. |
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.
|
---|