LogoLogo
v4.4
  • Docs
  • Connector Configuration
  • Knowledge Base
  • Changelog
v4.4
  • Introduction
  • Migration from 4.3
  • Overview
    • Introduction
    • Component
    • Flow
    • End User Guide
  • Component Definition
    • Basic Structure
    • Manifest
      • name
      • label
      • icon
      • marker
      • author
      • description
      • auth
      • authConfig
      • quota
      • properties
      • inPorts
      • outPorts
      • firePatterns
      • tick
      • private
      • webhook
      • state
      • localization
    • Behaviour
    • Dependencies
    • Authentication
    • Quotas & Limits
    • Configuration
    • Example Component
  • Customizing UI
    • Custom Inspector Fields
    • Custom Theme
    • Custom Strings
    • Custom API
    • Custom Component Strings
    • Custom Component Shapes
  • Appmixer hosted
    • Getting started
    • Creating Custom Components
    • Using Appmixer SDK
    • Using Appmixer API
    • Using Oauth applications
  • Appmixer Self-Managed
    • Installation
    • Getting Started
    • Custom Component: HelloAppmixer
    • Using Appmixer SDK
    • Using Appmixer API
    • Using OAuth applications
    • Installation GCP
    • System Webhooks
    • Configuration
    • Appmixer Architecture
    • Appmixer Deployment Models
  • API
    • ACL
    • Accounts
    • Apps
    • Authentication
    • Charts
    • Config
    • Data Stores
    • Files
    • Flows
    • Insights
    • Modifiers
    • People Task
    • Public Files
    • Service Configuration
    • Unprocessed Messages
    • User
    • Variables
  • Appmixer SDK
    • Introduction
    • Installation
    • Quick Start
    • Constructor
    • API Module
    • UI & Widgets
      • Flow Manager
      • Designer
      • Insights Logs
      • Insights Chart Editor
      • Insights Dashboard
      • Accounts
      • Storage
      • People Tasks
      • Connectors
      • Integrations
      • Wizard
    • Developer mode
  • Appmixer Backoffice
    • Getting Started
    • Services
    • Quotas
    • Public Files
    • System Configuration
    • Modules
  • Tutorials
    • Managing Authentication
    • Sharing Flows
    • Flows Metadata & Filtering
    • People Tasks
    • Customizing modifiers
    • Setting ACL
    • Integration Templates
    • Installing and updating modules
    • Custom Webhook Trigger
    • Appmixer Virtual Users
  • Appmixer CLI
    • Appmixer CLI
  • App Registration
    • Azure Cognitive Services
    • DeepAI
    • Google
    • Highrise
    • Hubspot
    • Microsoft
    • Salesforce
    • Schoology
    • Screenshot API
    • Slack
    • Trello
    • Typeform
    • Utilities
      • Email
      • Language
      • Tasks
      • Test
      • Weather
    • Zoho
  • Connectors
    • Connector Request
Powered by GitBook
On this page
  • Usage
  • Definition

Was this helpful?

Export as PDF
  1. Customizing UI

Custom Component Shapes

Fully customize appearance of components in diagrams.

PreviousCustom Component StringsNextGetting started

Last updated 2 years ago

Was this helpful?

Usage

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.

Definition

Key

Description

options.updateCallback

An optional method called before the component is updated. Accepts element argument.

attributes

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.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.tools

Special selectors

Dynamic properties of the component, such as label and icon, are mapped to optional selectors in the markup.

Selector

tagName

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.

Special attributes

Key

Value

Description

event

"remove"

The entry acts as a remove button.

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        
    }
});

Element attributes, see:

orts attributes of

Link attributes, see:

Link attributes, see:

Manifest
jointjs.dia.Cell.define
P
joint.dia.Element.ports.interface
jointjs.dia.Link
jointjs.linkTools