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 to150
), - [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 thedia.Paper.fitToContent()
method withui.Stencil.options.contentOptions
internally. The groupheight
property allows you to override the global setting for a particular group, - [optional]
layout
property which acts in the same way asui.Stencil.options.layout
. The grouplayout
property allows you to override the global setting for a particular group, - [optional]
paperOptions
property which acts in the same way asui.Stencil.options.paperOptions
. The grouppaperOptions
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 thepaperOptions
option is specified as a function it is possible to adjust the Graph of a Stencil Paper by passing amodel
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
.
search
[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) orfalse
(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).
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.
<ul>
<li data-type="element">Element</li>
<li data-type="link">Link</li>
</ul>
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 theui.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 theui.Stencil.options.dragEndClone()
method,x
andy
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;
}