Skip to main content

Stencil

The plugin implements a palette of JointJS Elements and allows you to drag them to a Paper.

To learn more about the plugin, check out the Element palette section.

constructor

constructor(opt?: Stencil.Options);

The Stencil constructor accepts several parameters:

canDrag()

(cellView: dia.CellView, evt: dia.Event, groupName: string | null) => boolean;

[optional] A function to determine whether an Element from the Stencil can be dragged by the user or not. All Elements can be dragged by default.

container

[optional] A CSS selector or a DOM element of the container element, which the element being dragged is appended to.

contentOptions

[optional] When the Stencil Papers are resized to fit their content, the dia.Paper.fitToContent() function is used internally. Use this option to modify the behavior.

dragEndClone()

(cell: dia.Cell) => dia.Cell;

[optional] A function that produces an Element clone when the user places the dragged Element into the Paper. It defaults to dia.Cell.clone().

dragStartClone()

(cell: dia.Cell) => dia.Cell;

[optional] A function that produces an Element clone when the user starts dragging. It defaults to dia.Cell.clone().

dropAnimation

[optional] 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:

  • [optional] duration is the duration of the animation in milliseconds (defaults to 150),
  • [optional] easing is the timing function set to 'ease-in-out' by default (all CSS transition timing functions are supported).

Example:

dropAnimation: {
duration: 300,
easing: 'linear'
}

groups

[optional] 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 properties for a given group:

  • [required] label property specifies the name of the group that will be displayed in the UI,
  • [required] index is a number specifying the position of the group among other groups within the Stencil,
  • [optional] closed property tells Stencil whether this group should initially be closed or opened (false by default),
  • [optional] height property specifying the height of the Paper containing the shapes of the group. If not defined, then the height of the Stencil will be used. If none of these heights are defined, the Paper will automatically resize to fit the content (recommended). While doing so, it uses the dia.Paper.fitToContent() method with ui.Stencil.options.contentOptions internally. The group height property allows you to override the global setting for a particular group,
  • [optional] layout property which acts in the same way as ui.Stencil.options.layout. The group layout property allows you to override the global setting for a particular group,
  • [optional] paperOptions property which acts in the same way as ui.Stencil.options.paperOptions. The group paperOptions property allows you to override the global setting for a particular group.

An example of what this object can look like:

groups: {
basic: { label: 'Basic Elements', index: 1 },
text: { label: 'Text', index: 2, closed: true },
advanced: { label: 'Advanced Elements', index: 3, closed: true }
}

groupsToggleButtons

[optional] If set to true, buttons for expanding/collapsing groups (expand all/collapse all) are rendered into the Stencil. It defaults to false.

height

[optional] The height of the Stencil. If no height property is explicitly stated, the default height of the Stencil will be 800px. If height: null is used, the Stencil height will be calculated automatically to fit its content. The dia.Paper.fitToContent() method with ui.Stencil.options.contentOptions is used internally for resizing.

label

[optional] A string or HTML Element rendered at the top of the stencil. It defaults to Stencil.

layout

[optional] This options specifies the layout which the Stencil will use (if any):

  • boolean - If set to true, the Stencil Elements are automatically laid out using the GridLayout plugin. In such case, the following GridLayout settings are used:

    {
    columnWidth: this.options.width / 2 - 10,
    columns: 2,
    rowHeight: 80,
    rowGap: 10,
    columnGap: 10,
    marginX: 10,
    marginY: 10,
    resizeToFit: true
    }
  • object - To set the layout behavior, you can pass your own GridLayout options. For all available options see the aforementioned plugin. Example:

    layout: {
    columnWidth: 100,
    columns: 3,
    rowHeight: 100,
    }
  • function - The layout also can be defined as a function in which case it will be called for each group on initialization and after the Stencil is filtered. The function receives the group Graph as the first parameter and the group definition as the second parameter. Note that the Graph does not contain Elements that don't match the search criteria (if any). Each Graph has the group property set to the group id. Example:

    layout: function(graph, group) {
    const groupName = graph.get('group');
    if (groupName === 'my-group-1') {
    // layout the elements in this group only
    graph.getElements().forEach(function(el, index) {
    el.position(0, index * 100);
    });
    }
    };

paper

[required] The Paper or PaperScroller instance to which the Elements may be dragged.

paperOptions

[optional] An object or a function adjusting the look and behavior of the Stencil's papers:

  • object - An object with Paper options to adjust the look and behavior of the Stencil's papers. For example:

    paperOptions: {
    elementView: CustomElementView,
    background: { color: 'red' }
    }

    These options can be also used to adjust a single Paper (and takes precedence) when defined inside of a group definition. For example:

    groups: {
    myGroup: {
    index: 1,
    label: 'My Group',
    paperOptions: { color: 'blue' }
    }
    }
  • function - paperOptions may also be specified as a function, which is expected to return the paper options as an object (see above). Only when the paperOptions option is specified as a function it is possible to adjust the Graph of a Stencil Paper by passing a model property, as in this example:

    paperOptions: function() {
    return {
    elementView: CustomElementView,
    model: new dia.Graph({}, { cellNamespace: CustomElementNamespace }),
    background: { color: 'red' }
    }
    }

paperPadding

(deprecated) [optional] A number specifying the padding that will be used in the internal Papers that hold the Elements in the Stencil. It defaults to 10.

scaleClones

[optional] When set to true, dragged clones are automatically scaled based on the current Paper transformations. Note that this option is ignored when snaplines option is applied (it always scales the Elements). It defaults to false.

[optional] An object or a function defining what properties of the Elements in the Stencil will be used for searching. See the Searchable Stencil section for more information:

  • object - 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. Example:

    search: {
    '*': ['attrs/label/text'],
    'standard.Image': ['description'],
    'standard.Path': ['description']
    }
  • function - A function executed on every Cell in the stencil, returning true (matched) or false (unmatched). Example:

    search: function(cell, keyword, group, stencil) {
    const description = cell.get('description');
    return typeof description === 'string' && description.indexOf(keyword) > -1;
    }

snaplines

[optional] An instance of the Snaplines plugin which is responsible for drawing snaplines while the user drags an Element from the Stencil.

width

[required] The width of the Stencil.

Methods

cancelDrag()

stencil.cancelDrag(options?: { dropAnimation?: Stencil.DropAnimation }): void;

Stop the current drag operation. If options.dropAnimation is not specified, the default ui.Stencil.options.dropAnimation is used.

closeGroup()

stencil.closeGroup(name: string): void;

Close group identified by name.

closeGroups()

stencil.closeGroups(): void;

Close all groups.

filter()

stencil.filter(keyword: string, matchCell?: Stencil.MatchCellMap | Stencil.MatchCellCallback): void;

Filter the Stencil Elements based on the given keyword. If matchCell is not specified, the default ui.Stencil.options.search is used.

freeze()

stencil.freeze(opt?: dia.Paper.FreezeOptions): void;

Freeze all the Papers of the Stencil. In this state, the Paper does not automatically re-render upon changes in the Graph. This is useful when for instance the Stencil is collapsed (not visible on the screen). For more information see dia.Paper.freeze().

getGraph()

stencil.getGraph(group?: string): dia.Graph;

Get the Graph associated with the group identified by group. If the Stencil does not use groups, just omit the group parameter to get the only Graph present.

getPaper()

stencil.getPaper(group?: string): dia.Paper;

Get the paper associated with the group identified by group. If the Stencil does not use groups, just omit the group parameter to get the only Paper present.

isDragCanceled()

stencil.isDragCanceled(): boolean;

Was the current drag operation canceled? Returns a boolean value.

isGroupOpen()

stencil.isGroupOpen(name: string): boolean;

Return true if the group identified by name is open. Return false otherwise.

load()

stencil.load(groups: { [groupName: string]: Array<dia.Cell | mvc.ObjectHash> }): void;

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

stencil.load({
group1: [{ type: 'standard.Rectangle' }, { type: 'standard.Ellipse' }],
group2: [{ type: 'standard.Image' }]
});

Note that the Elements could be also defined as plain JavaScript objects. That is useful for example when you store the Stencil configuration in a JSON, which is received from a database.

stencil.load(cells: Array<dia.Cell | mvc.ObjectHash>, groupName?: string): void;

If the method is called with an array it behaves like the loadGroup() function.

loadGroup()

stencil.loadGroup(cells: Array<dia.Cell | mvc.ObjectHash>, groupName?: string): void;

Accept an array of Elements and render them into a single Stencil group. If no groupName is provided, the default group is used.

openGroup()

stencil.openGroup(name: string): void;

Open group identified by name.

openGroups()

stencil.openGroups(): void;

Open all groups.

render()

stencil.render(): this;

Render the Stencil. This creates a stencil.el HTML element which can be added to your HTML, as explained in the Element palette section.

setPaper()

stencil.setPaper(paper: dia.Paper | ui.PaperScroller): void;

Set the target Paper for the Stencil. It tells Stencil to use a different Paper than the one that was passed to it during initialization through ui.Stencil.options.paper option. 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, then you can call stencil.setPaper(otherTab) whenever the active tab changes.

Note that the paper argument can be both a Paper instance or a PaperScroller instance. Stencil can handle both.

startDragging()

stencil.startDragging(cell: dia.Cell, evt: dia.Event | Event): void;

Start dragging an arbitrary Cell (Element or Link). The Cell will be added to the preview Paper as is (i.e. the ui.Stencil.options.dragStartClone() callback will not be called). The ui.Stencil.options.dragEndClone() callback will be called as usual. The evt argument is a DOM event object that defines, using clientX and clientY, where the preview is to be displayed.

Since the cell does not have to be from the Stencil, this method can be used to initiate dragging an Element outside the Stencil (e.g. from an HTML list).

info

You should not render the Stencil if it is never to be attached to the DOM. Calling ui.Stencil.render() would create detached DOM elements and take up memory unnecessarily.

index.html
<ul>
<li data-type="element">Element</li>
<li data-type="link">Link</li>
</ul>
index.js
const stencil = new ui.Stencil({ paper });

const listEl = document.querySelector('ul');
listEl.addEventListener('pointerdown', (evt) => {
const { type } = evt.target.dataset;
let cell;
switch (type) {
case 'element':
cell = new shapes.standard.Rectangle({
size: { width: 100, height: 100 }
});
break;
case 'link':
cell = new shapes.standard.Link({
source: { x: 0, y: 0 },
target: { x: 100, y: 100 }
});
break;
default:
throw new Error(`Unknown type: ${type}`);
}
stencil.startDragging(cell, evt);
});

startListening()

stencil.startListening(): void;

Enable dragging of elements from the stencil.

stopListening()

stencil.stopListening(): this;

Disable dragging of Elements from the Stencil.

toggleGroup()

stencil.toggleGroup(name: string): void;

Toggle group identified by name.

unfreeze()

stencil.unfreeze(opt?: dia.Paper.UnfreezeOptions): void;

Unfreeze all the Papers of the Stencil. For more information see dia.Paper.unfreeze().

Events

The plugin fires the following events:

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 ui.Stencil.options.dropAnimation). This event gives you a chance to react when the invalid drop occurs.

stencil.on('drop:invalid', (
evt: dia.Event,
element: dia.Element
) => {
// Element was dropped to an invalid area
});
  • evt is a mouse event object,
  • element is the Element model that would have been otherwise dropped into the main Paper (also Graph).

element:drag

Triggered while an element is being dragged.

stencil.on('element:drag', (
cloneView: dia.ElementView,
evt: dia.Event,
dropArea: g.Rect,
validDropTarget: boolean
) => {
// the Element is being dragged
});
  • cloneView is an ElementView rendered for the model returned by the ui.Stencil.options.dragStartClone() method,
  • dropArea is the area (in local coordinate system), which the Element would occupy if it were a part of the target Graph,
  • validDropTarget is a boolean value that answers whether the Element is currently over a valid target Paper.

Example usage:

stencil.on('element:drag', (cloneView, evt, dropArea, validDropTarget) => {
cloneView.vel.attr('opacity', validDropTarget ? 1 : 0.3);
});

element:dragend

Triggered when a drag operation ends (such as by releasing a mouse button or by calling the cancelDrag() method).

This event fires regardless of whether the drag completed or was canceled. The dragend event handler can check isDragCanceled() and the validDropTarget parameter to determine whether the drag operation had succeeded or not.

stencil.on('element:dragend', (
cloneView: dia.ElementView,
evt: dia.Event,
dropArea: g.Rect,
validDropTarget: boolean
) => {
// the Element dragging has stopped
});

See element:drag for more information about the parameters passed to the handler function.

element:dragstart

Triggered when the user starts dragging an Element.

stencil.on('element:dragstart', (
cloneView: dia.ElementView,
evt: dia.Event,
dropArea: g.Rect,
validDropTarget: boolean
) => {
// the Element dragging has started
});

See element:drag for more information about the parameters passed to the handler function.

element:drop

Triggered when an Element is dropped onto a valid target Paper.

stencil.on('element:drop', (
elementView: dia.ElementView,
evt: dia.Event,
x: number,
y: number
) => {
// the Element was dropped
});
  • elementView is an ElementView rendered for the model returned by the ui.Stencil.options.dragEndClone() method,
  • x and y are coordinates (in local coordinate system) of the user pointer at the moment of the drop event.

Example usage:

stencil.on('element:drop', (elementView) => {
const halo = new joint.ui.Halo({ cellView: elementView });
halo.render();
});

filter

Triggered when the user uses the search input field to filter Elements in the Stencil.

stencil.on('filter', (
graph: dia.Graph,
groupName: string,
keyword: string
) => {
// the Stencil was filtered
});
  • graph is a set of Elements that matched the filter,
  • groupName is the name of the group in which Elements were filtered,
  • keyword is the current search term.

group:close

Triggered when the user closes an open group.

stencil.on('group:close', (groupName: string) => {
// the group was closed
});
  • groupName is the name of the group which was closed.

group:open

Triggered when the user opens a closed group.

stencil.on('group:open', (groupName: string) => {
// the group was opened
});
  • groupName is the name of the group which was opened.

Types

The plugin uses the following types:

DropAnimation

type DropAnimation = boolean | { duration?: number | string, easing?: string };

Group

interface Group {
label: string | HTMLElement;
index: number;
closed?: boolean;
height?: number;
layout?: boolean | layout.GridLayout.Options | { [key: string]: any };
paperOptions?: (() => dia.Paper.Options) | dia.Paper.Options;
[key: string]: any;
}

LayoutGroupCallback

type LayoutGroupCallback = (graph: dia.Graph, group: Group) => void;

MatchCellCallback

type MatchCellCallback = (cell: dia.Cell, keyword: string, groupId: string, stencil: Stencil) => boolean;

MatchCellMap

type MatchCellMap = { [type: string]: Array<dia.Path> };

Options

interface Options extends mvc.ViewOptions<undefined> {
paper: dia.Paper | ui.PaperScroller;
width?: number;
height?: number;
label?: string | HTMLElement;
groups?: { [key: string]: Stencil.Group };
groupsToggleButtons?: boolean;
dropAnimation?: DropAnimation;
search?: MatchCellMap | MatchCellCallback | null;
layout?: boolean | layout.GridLayout.Options | { [key: string]: any } | LayoutGroupCallback;
snaplines?: ui.Snaplines;
scaleClones?: boolean;
usePaperGrid?: boolean;
dragStartClone?: (cell: dia.Cell) => dia.Cell;
dragEndClone?: (cell: dia.Cell) => dia.Cell;
paperOptions?: (() => dia.Paper.Options) | dia.Paper.Options;
paperDragOptions?: (() => dia.Paper.Options) | dia.Paper.Options;
canDrag?: (cellView: dia.CellView, evt: dia.Event, groupName: string | null) => boolean;
contentOptions?: dia.Paper.FitToContentOptions;
container?: mvc.$Element;
}