JointJS - Playground
prevent contextmenu.
https://github.com/clientIO/joint/issues/2658
HTML
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.7.5/joint.min.css" />
<link rel="stylesheet" type="text/css" href="sample.css" />
<div class="toolbar">
<button id="btn-create-part">Create Element</button>
<button id="btn-create-port">Create Port (Square)</button>
<span id="status"></span>
</div>
<div id="paper"></div>
<!-- Dependencies -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.4.1/backbone-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.7.5/joint.min.js"></script>
<!-- Sample Logic -->
<script src="sample.js"></script>
CSS
body {
font-family: sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
.toolbar {
padding: 10px;
background: #f0f0f0;
border-bottom: 1px solid #ccc;
display: flex;
gap: 10px;
align-items: center;
}
#paper {
flex: 1;
overflow: auto;
background: #f7f7f7;
position: relative;
}
button {
padding: 8px 12px;
cursor: pointer;
}
/* Port Styling */
.joint-port-body {
stroke: #333;
fill: #fff;
stroke-width: 1;
}
.joint-port-label {
font-size: 10px;
font-family: Arial, sans-serif;
fill: #333;
}
/* Resize Handle */
.resize-handle {
fill: #333;
stroke: #fff;
stroke-width: 1;
cursor: nwse-resize;
display: none;
/* Hidden by default, shown on selection */
}
.joint-element.selected .resize-handle {
display: block;
}
JavaScript
const { dia, shapes, util } = joint;
// 1. Initialize Graph and Paper
const graph = new dia.Graph();
const paper = new dia.Paper({
model: graph,
el: document.getElementById('paper'),
width: '100%',
height: '100%',
background: { color: '#F8F9FA' },
gridSize: 10,
drawGrid: true,
async: true,
defaultConnectionPoint: { name: 'boundary' },
linkPinning: false,
validateConnection: function (cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
if (cellViewS === cellViewT) return false;
return cellViewT.model.isElement() || magnetT !== undefined;
}
});
// 2. State Management
let selectedView = null;
let selectedPortId = null;
let selectedPortLabelId = null; // New: Track selected label
let drawingLink = null;
let isResizing = false;
let resizeStartPosition = null;
let resizeStartSize = null;
let draggingPort = null;
let draggingPortLabel = null;
let selectedPortView = null;
let resizingPort = null;
let resizingPortStart = null;
const defaultPortGroups = {
'left-port-group': {
position: { name: 'manual', args: { x: 0, y: 0 } },
attrs: {
z: 'auto',
show: true,
direction: '',
portLabel: {
fontSize: 12,
dominantBaseline: 'hanging',
lineHeight: 16,
displayEmpty: true
},
portBody: {
magnet: 'passive',
x: -11.5,
y: -11.5,
width: 23,
height: 23,
fill: '#ffffff',
rx: 3,
ry: 3,
stroke: '#2a2a2a'
},
portLabelRect: {
fill: 'transparent',
stroke: 'transparent',
strokeWidth: 1
},
portLabelLine: {
stroke: '#2a2a2a',
strokeWidth:...