Skip to main content

Element palette

The plugin to implement a palette of JointJS Elements is called Stencil. These Elements can be dragged onto a Paper.

Installation

Import the Stencil from the ui namespace and create a new instance of it. Pass an instance of a dia.Paper to the constructor.

import { dia, shapes, ui } from '@joint/plus';

const graph = new dia.Graph({}, { cellNamespace: shapes });
const paper = new dia.Paper({
el: document.getElementById('paper'),
width: 500,
height: 300,
model: graph,
cellViewNamespace: shapes
});

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

document.getElementById('stencil-holder').appendChild(stencil.render().el);
There is also a UMD version available

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

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

And access the Stencil through the joint.ui namespace:

index.js
const graph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });
const paper = new joint.dia.Paper({
el: document.getElementById('paper'),
width: 500,
height: 300,
model: graph,
cellViewNamespace: joint.shapes
});

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

document.getElementById('stencil-holder').appendChild(stencil.render().el);

How does Stencil work?

The 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 HTML element (el) 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.

Populating Stencil with Elements

const r = new shapes.standard.Rectangle({
position: { x: 10, y: 10 }, size: { width: 50, height: 30 }
});
const c = new shapes.standard.Ellipse({
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 are multiple ways how to populate the Stencil with Elements. Use either stencil.load() or stencil.loadGroup() which make sure the Elements are rendered into the Stencil.

info

Populating the Stencil with Elements needs to be done after the Stencil is rendered in the DOM.

Drag elements from the Stencil and drop them into the Paper area. If you drop an element outside of the Paper area, it is automatically dismissed:

Stencil groups (a.k.a. Accordion)

Creating an accordion-like Stencil is as easy as passing a groups object to the Stencil constructor. The groups object is a hash table that maps group identifiers to the configuration of the particular group. For example:

const stencil = new ui.Stencil({
graph: graph,
paper: paper,
width: 200,
groups: {
one: { label: 'First group', index: 1 },
two: { label: 'Second group', index: 2, closed: true }
}
});

Groups can be collapsed/expanded via the UI but also programmatically:

Searchable Stencil

The Stencil allows users to filter the Elements by an arbitrary keyword. That is useful especially when the palette contains a lot of Elements. To enable this add search: { /* rules */ } option to the Stencil constructor.

The rules determine what attributes we match a given keyword with and are defined by an object with ELEMENT_TYPE: [ATTRIBUTE_PATH1, ATTRIBUTE_PATH2, ..] key-value pairs.

  • where ELEMENT_TYPE is either an Element type (e.g standard.Rectangle) or '*' to match any type,
  • and ATTRIBUTE_PATH is a path to Element's attribute or property (e.g 'attrs/label/text', 'description').
const stencil = new ui.Stencil({
search: {
'*': ['attrs/label/text'],
'standard.Image': ['description'],
'standard.Path': ['description']
}
});

The rules can be alternatively defined as a function, which, upon search, will be invoked for each Element - with the Element model, string keyword, the group id the Element belongs to, and the Stencil instance as its arguments:

const stencil = new ui.Stencil({
search: function(element, keyword, groupId, stencil) {
return element.get('type').includes(keyword) || groupId.includes(keyword);
}
});

Try to search for "t" for example - you will see the Stencil being dynamically updated to show only the matched Elements:

By default, all 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 make the unmatched elements translucent.

Here's a list of all CSS classes toggled by filtering:

ClassNameCSS SelectorDescription
stencil-filtered.joint-stencil.stencil-filteredA keyword was provided. The Stencil is filtered.
not-found.joint-stencil.not-foundThere are no results for the given keyword.
unmatched.joint-stencil .group.unmatchedA Stencil group of which none of the Elements match the filter.

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

The default text of the Stencil can be modified as shown below:

// No Matches Found
stencil.el.dataset.textNoMatchesFound = 'No matches found!';
// Search Placeholder
stencil.el.querySelector('.search').placeholder = 'My Placeholder';

Reacting on Elements added to the Paper

Many times, you might want to perform some action as a reaction on a new Element dragged from the Stencil to the Paper. This can be achieved by usual means, i.e. by reacting on new Elements added to the Graph:

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

Events

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