Custom Component Shapes
Fully customize appearance of components in diagrams.
Last updated
Fully customize appearance of components in diagrams.
Last updated
Shape registration in the Appmixer SDK:
var appmixer = new Appmixer({
componentShapes: {
action: myCustomComponentShape,
trigger: myCustomComponentShape,
myUniqueShape: myCustomComponentShape
},
});
Use "action" and "trigger" keys to override defaults. You can define a custom shape for any component in the system, shape reference in a component manifest looks like this:
{
shape: "myUniqueShape"
}
Built-in shapes are action
, trigger
, actionVertical
and triggerVertical
.
Key | Description |
---|---|
Dynamic properties of the component, such as label and icon, are mapped to optional selectors in the markup.
var customComponentShape = {
attributes: {
size: {
height: 72,
width: 72
},
markup: [
{
tagName: 'rect',
selector: 'body'
},
{
tagName: 'text',
selector: 'label'
},
{
tagName: 'image',
selector: 'icon'
},
{
tagName: 'g',
selector: 'element-halo',
children: [
{
tagName: 'image',
selector: 'element-halo-copy',
children: [
{
tagName: 'title',
selector: 'element-halo-copy-tooltip'
}
]
},
{
tagName: 'image',
selector: 'element-halo-cut',
children: [
{
tagName: 'title',
selector: 'element-halo-cut-tooltip'
}
]
},
{
tagName: 'image',
selector: 'element-halo-remove',
children: [
{
tagName: 'title',
selector: 'element-halo-remove-tooltip'
}
]
}
]
}
],
attrs: {
body: {
fill: 'white',
stroke: 'black',
strokeWidth: 1,
refWidth: 1,
refHeight: 1
},
icon: {
ref: 'body',
refX: 0.5,
refY: 0.5,
xAlignment: 'middle',
yAlignment: 'middle',
width: 24,
height: 24,
clipPath: 'url(#icon-clip)'
},
label: {
ref: 'body',
textAnchor: 'middle',
refX: 0.5,
refY: '100%',
refY2: 12,
xAlignment: 'middle',
fontFamily: 'sans-serif',
fontWeight: 'bold',
fontSize: 14,
fill: 'black'
},
'element-halo': {
display: 'none',
ref: 'body',
width: 68,
height: 44,
refX: 0.5,
refY: 0,
refY2: -24,
xAlignment: 'middle'
},
'element-halo-copy': {
event: 'element-copy',
x: 0,
y: 0,
width: 20,
height: 20,
xlinkHref: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii01IC0yIDI0IDI0IiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaW5ZTWluIiBjbGFzcz0iaWNvbl9faWNvbiI+PHBhdGggZD0iTTUgMnYyaDRWMkg1em02IDBoMWEyIDIgMCAwIDEgMiAydjE0YTIgMiAwIDAgMS0yIDJIMmEyIDIgMCAwIDEtMi0yVjRhMiAyIDAgMCAxIDItMmgxYTIgMiAwIDAgMSAyLTJoNGEyIDIgMCAwIDEgMiAyem0wIDJhMiAyIDAgMCAxLTIgMkg1YTIgMiAwIDAgMS0yLTJIMnYxNGgxMFY0aC0xek00IDhoNmExIDEgMCAwIDEgMCAySDRhMSAxIDAgMSAxIDAtMnptMCA1aDZhMSAxIDAgMCAxIDAgMkg0YTEgMSAwIDAgMSAwLTJ6Ij48L3BhdGg+PC9zdmc+',
cursor: 'pointer'
},
'element-halo-cut': {
event: 'element-cut',
x: 24,
y: 0,
width: 20,
height: 20,
cursor: 'pointer',
xlinkHref: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0yLjUgLTIuNSAyNCAyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiIgY2xhc3M9Imljb25fX2ljb24iPjxwYXRoIGQ9Ik05LjEwNyAxMC41NTRMNy4zNjMgMTIuNjNhMy45OTMgMy45OTMgMCAwIDEtLjE5MiA0Ljg5IDQuMDA0IDQuMDA0IDAgMCAxLTUuNjM1LjQ5MiAzLjk5MSAzLjk5MSAwIDAgMS0uNDkzLTUuNjI4IDQuMDA1IDQuMDA1IDAgMCAxIDQuNzg4LTEuMDM4TDcuODAxIDkgMi40MTMgMi41ODdhLjk5OC45OTggMCAwIDEgLjEyMy0xLjQwNyAxIDEgMCAwIDEgMS40MDkuMTIzbDUuMTYyIDYuMTQ0IDUuMTYxLTYuMTQ0YTEgMSAwIDAgMSAxLjQxLS4xMjMuOTk4Ljk5OCAwIDAgMSAuMTIzIDEuNDA3TDEwLjQxMiA5bDEuOTcgMi4zNDVhNC4wMDUgNC4wMDUgMCAwIDEgNC43ODggMS4wMzggMy45OTEgMy45OTEgMCAwIDEtLjQ5MyA1LjYyOCA0LjAwNCA0LjAwNCAwIDAgMS01LjYzNS0uNDkyIDMuOTkzIDMuOTkzIDAgMCAxLS4xOTItNC44OWwtMS43NDMtMi4wNzV6bS02LjI4NSA1LjkyN2EyIDIgMCAwIDAgMi41NzEtMy4wNiAyLjAwMiAyLjAwMiAwIDAgMC0yLjgxOC4yNDZjLS43MS44NDUtLjYgMi4xMDUuMjQ3IDIuODE0em0xMi41NyAwYTEuOTk2IDEuOTk2IDAgMCAwIC4yNDYtMi44MTQgMi4wMDIgMi4wMDIgMCAwIDAtMi44MTctLjI0NiAxLjk5NiAxLjk5NiAwIDAgMC0uMjQ3IDIuODE0Yy43MS44NDUgMS45NzIuOTU1IDIuODE4LjI0NnoiPjwvcGF0aD48L3N2Zz4'
},
'element-halo-remove': {
event: 'element-remove',
x: 48,
y: 0,
width: 20,
height: 20,
cursor: 'pointer',
xlinkHref: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0zIC0yIDI0IDI0IiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaW5ZTWluIiBjbGFzcz0iaWNvbl9faWNvbiI+PHBhdGggZD0iTTYgMlYxYTEgMSAwIDAgMSAxLTFoNGExIDEgMCAwIDEgMSAxdjFoNGEyIDIgMCAwIDEgMiAydjFhMiAyIDAgMCAxLTIgMmgtLjEzM2wtLjY4IDEwLjJhMyAzIDAgMCAxLTIuOTkzIDIuOEg1LjgyNmEzIDMgMCAwIDEtMi45OTMtMi43OTZMMi4xMzcgN0gyYTIgMiAwIDAgMS0yLTJWNGEyIDIgMCAwIDEgMi0yaDR6bTEwIDJIMnYxaDE0VjR6TTQuMTQxIDdsLjY4NyAxMC4wNjhhMSAxIDAgMCAwIC45OTguOTMyaDYuMzY4YTEgMSAwIDAgMCAuOTk4LS45MzRMMTMuODYyIDdoLTkuNzJ6TTcgOGExIDEgMCAwIDEgMSAxdjdhMSAxIDAgMCAxLTIgMFY5YTEgMSAwIDAgMSAxLTF6bTQgMGExIDEgMCAwIDEgMSAxdjdhMSAxIDAgMCAxLTIgMFY5YTEgMSAwIDAgMSAxLTF6Ij48L3BhdGg+PC9zdmc+'
},
'element-halo-copy-tooltip': {
fontFamily: 'nunitosans-regular, Helvetica, Arial, sans-serif',
fontSize: 13
},
'element-halo-cut-tooltip': {
fontFamily: 'nunitosans-regular, Helvetica, Arial, sans-serif',
fontSize: 13
},
'element-halo-remove-tooltip': {
fontFamily: 'nunitosans-regular, Helvetica, Arial, sans-serif',
fontSize: 13
}
}
},
ports: {
attributes: {
in: {
position: {
name: 'left'
},
attrs: {
'.port-body': {
r: 5,
strokeWidth: 2,
stroke: 'black',
fill: 'white'
},
'connection-port-label': {
fill: 'black',
fontFamily: 'sans-serif',
fontSize: 12
}
},
label: {
position: {
name: 'left',
args: {
x: -8,
y: 14
}
},
markup: [
{
tagName: 'text',
selector: 'connection-port-label'
}
]
}
},
out: {
position: {
name: 'right'
},
attrs: {
'.port-body': {
r: 5,
strokeWidth: 2,
stroke: 'black',
fill: 'white'
},
'connection-port-label': {
fill: 'black',
fontFamily: 'sans-serif',
fontSize: 12
}
},
label: {
position: {
name: 'right',
args: {
x: 8,
y: 14
}
},
markup: [
{
tagName: 'text',
selector: 'connection-port-label'
}
]
}
}
}
},
link: {
attributes: {
router: {
name: 'metro'
},
connector: {
name: 'rounded',
args: {
radius: 8
}
},
markup: [
{
tagName: 'path',
selector: 'wrapper'
},
{
tagName: 'path',
selector: 'line'
}
],
tools: [
{
z: 100,
distance: '50%',
event: 'link-remove',
markup: [
{
tagName: 'circle',
selector: 'button',
attributes: {
cx: -0.5,
cy: -0.5,
r: 10,
fill: config.colors.bodyStrokeActive,
cursor: 'pointer'
}
},
{
tagName: 'path',
selector: 'icon',
attributes: {
pointerEvents: 'none',
transform: 'translate(-6.4 -7)',
d: 'M8,1.333h3.333A.667.667,0,0,1,12,2V3.333A.667.667,0,0,1,11.333,4H.667A.667.667,0,0,1,0,3.333V2a.667.667,0,0,1,.667-.667H4V.667A.667.667,0,0,1,4.667,0H7.333A.667.667,0,0,1,8,.667Zm2.533,4-.409,6.133a2,2,0,0,1-2,1.867H3.884a2,2,0,0,1-2-1.864L1.47,5.333ZM4.667,6A.667.667,0,0,0,4,6.667v4.667a.667.667,0,0,0,1.333,0V6.667A.667.667,0,0,0,4.667,6ZM7.333,6a.667.667,0,0,0-.667.667v4.667a.667.667,0,0,0,1.333,0V6.667A.667.667,0,0,0,7.333,6Z',
fill: 'white'
}
}
]
}
],
attrs: {
line: {
pointerEvents: 'none',
connection: true,
stroke: 'black',
strokeWidth: 1.5,
strokeDasharray: '4 4',
fill: 'transparent'
},
wrapper: {
connection: true,
cursor: 'pointer',
stroke: 'transparent',
strokeWidth: 24,
fill: 'transparent'
}
}
}
},
states: {
'@active': {
attributes: {
attrs: {
body: {
stroke: 'blue',
strokeWidth: 3
},
label: {
fill: 'black'
},
'element-halo': {
display: 'initial'
}
}
},
ports: {
attributes: {
in: {
attrs: {
'.port-body': {
stroke: 'blue'
}
}
},
out: {
attrs: {
'.port-body': {
stroke: 'blue'
}
}
}
}
},
link: {
attributes: {
attrs: {
line: {
strokeDasharray: 0,
stroke: 'blue'
}
}
}
}
},
'@invalid': {
attributes: {
attrs: {
body: {
stroke: 'red',
strokeWidth: 2
},
label: {
fill: 'red'
}
}
},
ports: {
attributes: {
in: {
attrs: {
'.port-body': {
stroke: 'red'
}
}
},
out: {
attrs: {
'.port-body': {
stroke: 'red'
}
}
}
}
}
}
}
};
var customSelectionShape = {
attributes: {
markup: [
{
tagName: 'rect',
selector: 'body'
},
{
tagName: 'g',
selector: 'halo',
children: [
{
tagName: 'image',
selector: 'halo-copy'
},
{
tagName: 'image',
selector: 'halo-cut'
},
{
tagName: 'image',
selector: 'halo-remove'
}
]
}
],
attrs: {
body: {
pointerEvents: 'none',
fill: 'rgba(0, 0, 0, 0.1)',
stroke: 'black',
strokeWidth: 1
},
halo: {
ref: 'body',
width: 68,
height: 44,
refX: 0.5,
refY: 0,
refY2: -24,
xAlignment: 'middle'
},
'halo-copy': {
event: 'element-copy',
x: 0,
y: 0,
width: 20,
height: 20,
xlinkHref: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii01IC0yIDI0IDI0IiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaW5ZTWluIiBjbGFzcz0iaWNvbl9faWNvbiI+PHBhdGggZD0iTTUgMnYyaDRWMkg1em02IDBoMWEyIDIgMCAwIDEgMiAydjE0YTIgMiAwIDAgMS0yIDJIMmEyIDIgMCAwIDEtMi0yVjRhMiAyIDAgMCAxIDItMmgxYTIgMiAwIDAgMSAyLTJoNGEyIDIgMCAwIDEgMiAyem0wIDJhMiAyIDAgMCAxLTIgMkg1YTIgMiAwIDAgMS0yLTJIMnYxNGgxMFY0aC0xek00IDhoNmExIDEgMCAwIDEgMCAySDRhMSAxIDAgMSAxIDAtMnptMCA1aDZhMSAxIDAgMCAxIDAgMkg0YTEgMSAwIDAgMSAwLTJ6Ij48L3BhdGg+PC9zdmc+',
cursor: 'pointer'
},
'halo-cut': {
event: 'element-cut',
x: 24,
y: 0,
width: 20,
height: 20,
cursor: 'pointer',
xlinkHref: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0yLjUgLTIuNSAyNCAyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiIgY2xhc3M9Imljb25fX2ljb24iPjxwYXRoIGQ9Ik05LjEwNyAxMC41NTRMNy4zNjMgMTIuNjNhMy45OTMgMy45OTMgMCAwIDEtLjE5MiA0Ljg5IDQuMDA0IDQuMDA0IDAgMCAxLTUuNjM1LjQ5MiAzLjk5MSAzLjk5MSAwIDAgMS0uNDkzLTUuNjI4IDQuMDA1IDQuMDA1IDAgMCAxIDQuNzg4LTEuMDM4TDcuODAxIDkgMi40MTMgMi41ODdhLjk5OC45OTggMCAwIDEgLjEyMy0xLjQwNyAxIDEgMCAwIDEgMS40MDkuMTIzbDUuMTYyIDYuMTQ0IDUuMTYxLTYuMTQ0YTEgMSAwIDAgMSAxLjQxLS4xMjMuOTk4Ljk5OCAwIDAgMSAuMTIzIDEuNDA3TDEwLjQxMiA5bDEuOTcgMi4zNDVhNC4wMDUgNC4wMDUgMCAwIDEgNC43ODggMS4wMzggMy45OTEgMy45OTEgMCAwIDEtLjQ5MyA1LjYyOCA0LjAwNCA0LjAwNCAwIDAgMS01LjYzNS0uNDkyIDMuOTkzIDMuOTkzIDAgMCAxLS4xOTItNC44OWwtMS43NDMtMi4wNzV6bS02LjI4NSA1LjkyN2EyIDIgMCAwIDAgMi41NzEtMy4wNiAyLjAwMiAyLjAwMiAwIDAgMC0yLjgxOC4yNDZjLS43MS44NDUtLjYgMi4xMDUuMjQ3IDIuODE0em0xMi41NyAwYTEuOTk2IDEuOTk2IDAgMCAwIC4yNDYtMi44MTQgMi4wMDIgMi4wMDIgMCAwIDAtMi44MTctLjI0NiAxLjk5NiAxLjk5NiAwIDAgMC0uMjQ3IDIuODE0Yy43MS44NDUgMS45NzIuOTU1IDIuODE4LjI0NnoiPjwvcGF0aD48L3N2Zz4'
},
'halo-remove': {
event: 'element-remove',
x: 48,
y: 0,
width: 20,
height: 20,
cursor: 'pointer',
xlinkHref: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0zIC0yIDI0IDI0IiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaW5ZTWluIiBjbGFzcz0iaWNvbl9faWNvbiI+PHBhdGggZD0iTTYgMlYxYTEgMSAwIDAgMSAxLTFoNGExIDEgMCAwIDEgMSAxdjFoNGEyIDIgMCAwIDEgMiAydjFhMiAyIDAgMCAxLTIgMmgtLjEzM2wtLjY4IDEwLjJhMyAzIDAgMCAxLTIuOTkzIDIuOEg1LjgyNmEzIDMgMCAwIDEtMi45OTMtMi43OTZMMi4xMzcgN0gyYTIgMiAwIDAgMS0yLTJWNGEyIDIgMCAwIDEgMi0yaDR6bTEwIDJIMnYxaDE0VjR6TTQuMTQxIDdsLjY4NyAxMC4wNjhhMSAxIDAgMCAwIC45OTguOTMyaDYuMzY4YTEgMSAwIDAgMCAuOTk4LS45MzRMMTMuODYyIDdoLTkuNzJ6TTcgOGExIDEgMCAwIDEgMSAxdjdhMSAxIDAgMCAxLTIgMFY5YTEgMSAwIDAgMSAxLTF6bTQgMGExIDEgMCAwIDEgMSAxdjdhMSAxIDAgMCAxLTIgMFY5YTEgMSAwIDAgMSAxLTF6Ij48L3BhdGg+PC9zdmc+'
}
}
}
};
new Appmixer({
componentShapes: {
action: customComponentShape,
trigger: customComponentShape,
selection: customSelectionShape
}
});
Selector | tagName | Description |
---|---|---|
Key | Value | Description |
---|---|---|
label
text
Contains a label of the component.
icon
image
Contains an icon of the component.
element-halo-copy-tooltip
title
"Copy" button tooltip.
element-halo-cut-tooltip
title
"Cut" button tooltip.
element-halo-remove-tooltip
title
"Remove" button tooltip.
event
"remove"
The entry acts as a remove button.
options.updateCallback
An optional method called before the component is updated. Accepts element
argument.
attributes
Element attributes, see: jointjs.dia.Cell.define
states
Definition for particular states of the component. The object structure is the same as the current scope (except for the "states" entry).
@active
- the component is being modified
@invalid
- the component configuration is invalid
@referenced
- the component is highlighted
@unchecked
- the component is selected
@checked
- the component is deselected
@running
- the flow is in a running state
ports.attributes
Ports attributes of joint.dia.Element.ports.interface
ports.states
Definition for particular states of individual ports.
The object structure is the same as the current scope (except for the "states" entry).
@connected
- the port is connected
link.attributes
Link attributes, see: jointjs.dia.Link
link.tools
Link attributes, see: jointjs.linkTools