@ngroznykh/papirus 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/DiagramRenderer.d.ts +6 -0
- package/dist/core/DiagramRenderer.d.ts.map +1 -1
- package/dist/core/InteractionManager.d.ts +3 -0
- package/dist/core/InteractionManager.d.ts.map +1 -1
- package/dist/core/SelectionManager.d.ts.map +1 -1
- package/dist/elements/Node.d.ts +28 -0
- package/dist/elements/Node.d.ts.map +1 -1
- package/dist/elements/NodeImage.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/papirus.js +263 -96
- package/dist/papirus.js.map +1 -1
- package/package.json +1 -1
package/dist/papirus.js
CHANGED
|
@@ -570,6 +570,14 @@ class SelectionManager extends EventEmitter {
|
|
|
570
570
|
}
|
|
571
571
|
return;
|
|
572
572
|
}
|
|
573
|
+
const node = element;
|
|
574
|
+
if (typeof node.getBadgeAtPoint === "function") {
|
|
575
|
+
const badge = node.getBadgeAtPoint(point);
|
|
576
|
+
if (badge !== null) {
|
|
577
|
+
this.renderer.emit("nodeBadgeClick", element.id, badge.id);
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
573
581
|
if (event.ctrlKey || event.metaKey) {
|
|
574
582
|
this.toggleSelection(element.id);
|
|
575
583
|
} else {
|
|
@@ -5002,6 +5010,7 @@ class InteractionManager {
|
|
|
5002
5010
|
this.overlayDragSession = null;
|
|
5003
5011
|
this.handledOverlayMouseDown = false;
|
|
5004
5012
|
this.renderer = options.renderer;
|
|
5013
|
+
this.navigationOnly = options.navigationOnly ?? false;
|
|
5005
5014
|
this.inputHandler = new InputHandler({
|
|
5006
5015
|
canvas: this.renderer.getCanvas(),
|
|
5007
5016
|
screenToWorld: (x, y) => this.renderer.screenToWorld(x, y)
|
|
@@ -5153,13 +5162,15 @@ class InteractionManager {
|
|
|
5153
5162
|
setupEvents(options) {
|
|
5154
5163
|
this.overlayCleanup = this.renderer.addOverlayRenderer((ctx) => {
|
|
5155
5164
|
this.selectionManager.renderSelectionRect(ctx);
|
|
5156
|
-
this.
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
const
|
|
5161
|
-
|
|
5162
|
-
node
|
|
5165
|
+
if (!this.navigationOnly) {
|
|
5166
|
+
this.dragManager.renderAlignmentGuides(ctx);
|
|
5167
|
+
this.connectionManager.renderPreview(ctx);
|
|
5168
|
+
this.connectionManager.renderHoverAnchors(ctx);
|
|
5169
|
+
for (const id of this.selectionManager.selectedIds) {
|
|
5170
|
+
const node = this.renderer.getNode(id);
|
|
5171
|
+
if (node) {
|
|
5172
|
+
node.renderResizeHandles(ctx);
|
|
5173
|
+
}
|
|
5163
5174
|
}
|
|
5164
5175
|
}
|
|
5165
5176
|
});
|
|
@@ -5173,68 +5184,70 @@ class InteractionManager {
|
|
|
5173
5184
|
this.inputHandler.on("pinch", (event) => this.handlePinch(event));
|
|
5174
5185
|
this.inputHandler.on("keydown", (event) => this.handleKeyDown(event, options));
|
|
5175
5186
|
this.inputHandler.on("keyup", (event) => this.handleKeyUp(event));
|
|
5176
|
-
this.
|
|
5177
|
-
this.
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
const
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
}
|
|
5185
|
-
});
|
|
5186
|
-
this.dragManager.on("dragend", (nodeIds) => {
|
|
5187
|
-
this.connectionManager.enableHover();
|
|
5188
|
-
const nodePositions = /* @__PURE__ */ new Map();
|
|
5189
|
-
for (const id of nodeIds) {
|
|
5190
|
-
const node = this.renderer.getNode(id);
|
|
5191
|
-
const before = this.dragStartPositions.get(id);
|
|
5192
|
-
if (!node || !before) continue;
|
|
5193
|
-
const after = { x: node.x, y: node.y };
|
|
5194
|
-
if (before.x !== after.x || before.y !== after.y) {
|
|
5195
|
-
nodePositions.set(id, { before, after });
|
|
5196
|
-
}
|
|
5197
|
-
}
|
|
5198
|
-
if (nodePositions.size > 0) {
|
|
5199
|
-
this.historyManager.execute(
|
|
5200
|
-
new MoveNodesCommand((id) => this.renderer.getNode(id), nodePositions)
|
|
5201
|
-
);
|
|
5202
|
-
}
|
|
5203
|
-
});
|
|
5204
|
-
this.connectionManager.on("edgeReconnectStart", (edge, endpoint, original) => {
|
|
5205
|
-
this.reconnectOrigins.set(edge.id, { endpoint, original: { ...original } });
|
|
5206
|
-
});
|
|
5207
|
-
this.connectionManager.on("edgeReconnect", (edge, endpoint) => {
|
|
5208
|
-
const origin = this.reconnectOrigins.get(edge.id);
|
|
5209
|
-
if (!origin || origin.endpoint !== endpoint) {
|
|
5210
|
-
return;
|
|
5211
|
-
}
|
|
5212
|
-
const before = origin.original;
|
|
5213
|
-
const after = endpoint === "start" ? edge.from : edge.to;
|
|
5214
|
-
if (this.endpointsEqual(before, after)) {
|
|
5215
|
-
this.reconnectOrigins.delete(edge.id);
|
|
5216
|
-
return;
|
|
5217
|
-
}
|
|
5218
|
-
this.historyManager.execute({
|
|
5219
|
-
execute: () => {
|
|
5220
|
-
if (endpoint === "start") {
|
|
5221
|
-
edge.from = { ...after };
|
|
5222
|
-
} else {
|
|
5223
|
-
edge.to = { ...after };
|
|
5187
|
+
if (!this.navigationOnly) {
|
|
5188
|
+
this.dragManager.on("dragstart", (nodeIds) => {
|
|
5189
|
+
this.connectionManager.disableHover();
|
|
5190
|
+
this.dragStartPositions.clear();
|
|
5191
|
+
for (const id of nodeIds) {
|
|
5192
|
+
const node = this.renderer.getNode(id);
|
|
5193
|
+
if (node) {
|
|
5194
|
+
this.dragStartPositions.set(id, { x: node.x, y: node.y });
|
|
5224
5195
|
}
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5196
|
+
}
|
|
5197
|
+
});
|
|
5198
|
+
this.dragManager.on("dragend", (nodeIds) => {
|
|
5199
|
+
this.connectionManager.enableHover();
|
|
5200
|
+
const nodePositions = /* @__PURE__ */ new Map();
|
|
5201
|
+
for (const id of nodeIds) {
|
|
5202
|
+
const node = this.renderer.getNode(id);
|
|
5203
|
+
const before = this.dragStartPositions.get(id);
|
|
5204
|
+
if (!node || !before) continue;
|
|
5205
|
+
const after = { x: node.x, y: node.y };
|
|
5206
|
+
if (before.x !== after.x || before.y !== after.y) {
|
|
5207
|
+
nodePositions.set(id, { before, after });
|
|
5232
5208
|
}
|
|
5233
|
-
|
|
5209
|
+
}
|
|
5210
|
+
if (nodePositions.size > 0) {
|
|
5211
|
+
this.historyManager.execute(
|
|
5212
|
+
new MoveNodesCommand((id) => this.renderer.getNode(id), nodePositions)
|
|
5213
|
+
);
|
|
5234
5214
|
}
|
|
5235
5215
|
});
|
|
5236
|
-
this.
|
|
5237
|
-
|
|
5216
|
+
this.connectionManager.on("edgeReconnectStart", (edge, endpoint, original) => {
|
|
5217
|
+
this.reconnectOrigins.set(edge.id, { endpoint, original: { ...original } });
|
|
5218
|
+
});
|
|
5219
|
+
this.connectionManager.on("edgeReconnect", (edge, endpoint) => {
|
|
5220
|
+
const origin = this.reconnectOrigins.get(edge.id);
|
|
5221
|
+
if (!origin || origin.endpoint !== endpoint) {
|
|
5222
|
+
return;
|
|
5223
|
+
}
|
|
5224
|
+
const before = origin.original;
|
|
5225
|
+
const after = endpoint === "start" ? edge.from : edge.to;
|
|
5226
|
+
if (this.endpointsEqual(before, after)) {
|
|
5227
|
+
this.reconnectOrigins.delete(edge.id);
|
|
5228
|
+
return;
|
|
5229
|
+
}
|
|
5230
|
+
this.historyManager.execute({
|
|
5231
|
+
execute: () => {
|
|
5232
|
+
if (endpoint === "start") {
|
|
5233
|
+
edge.from = { ...after };
|
|
5234
|
+
} else {
|
|
5235
|
+
edge.to = { ...after };
|
|
5236
|
+
}
|
|
5237
|
+
this.renderer.markDirty();
|
|
5238
|
+
},
|
|
5239
|
+
undo: () => {
|
|
5240
|
+
if (endpoint === "start") {
|
|
5241
|
+
edge.from = { ...before };
|
|
5242
|
+
} else {
|
|
5243
|
+
edge.to = { ...before };
|
|
5244
|
+
}
|
|
5245
|
+
this.renderer.markDirty();
|
|
5246
|
+
}
|
|
5247
|
+
});
|
|
5248
|
+
this.reconnectOrigins.delete(edge.id);
|
|
5249
|
+
});
|
|
5250
|
+
}
|
|
5238
5251
|
this.historyManager.on("change", () => {
|
|
5239
5252
|
this.renderer.markDirty();
|
|
5240
5253
|
});
|
|
@@ -5242,14 +5255,16 @@ class InteractionManager {
|
|
|
5242
5255
|
handleMouseDown(event) {
|
|
5243
5256
|
this.handledScrollbarMouseDown = false;
|
|
5244
5257
|
this.handledOverlayMouseDown = false;
|
|
5245
|
-
if (this.
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5258
|
+
if (!this.navigationOnly) {
|
|
5259
|
+
if (this.resizeManager.handleMouseDown(event)) {
|
|
5260
|
+
return;
|
|
5261
|
+
}
|
|
5262
|
+
if (this.connectionManager.tryStartReconnection(event)) {
|
|
5263
|
+
return;
|
|
5264
|
+
}
|
|
5265
|
+
if (this.connectionManager.tryStartConnectionAtPoint(event)) {
|
|
5266
|
+
return;
|
|
5267
|
+
}
|
|
5253
5268
|
}
|
|
5254
5269
|
const overlayDrag = this.renderer.beginOverlayDrag(event.screenX, event.screenY);
|
|
5255
5270
|
if (overlayDrag) {
|
|
@@ -5283,6 +5298,9 @@ class InteractionManager {
|
|
|
5283
5298
|
if (this.navigationManager.handleMouseDown(event)) {
|
|
5284
5299
|
return;
|
|
5285
5300
|
}
|
|
5301
|
+
if (this.navigationOnly) {
|
|
5302
|
+
return;
|
|
5303
|
+
}
|
|
5286
5304
|
if (this.dragManager.handleMouseDown(event)) {
|
|
5287
5305
|
return;
|
|
5288
5306
|
}
|
|
@@ -5302,6 +5320,9 @@ class InteractionManager {
|
|
|
5302
5320
|
}
|
|
5303
5321
|
}
|
|
5304
5322
|
const overScrollbar = this.renderer.updateScrollbarHover(event.screenX, event.screenY);
|
|
5323
|
+
this.renderer.updateBadgeHover(
|
|
5324
|
+
overScrollbar ? { x: -1e9, y: -1e9 } : { x: event.worldX, y: event.worldY }
|
|
5325
|
+
);
|
|
5305
5326
|
if (this.overlayDragSession) {
|
|
5306
5327
|
const moved = this.renderer.updateOverlayDrag(
|
|
5307
5328
|
this.overlayDragSession,
|
|
@@ -5330,14 +5351,16 @@ class InteractionManager {
|
|
|
5330
5351
|
if (overScrollbar) {
|
|
5331
5352
|
return;
|
|
5332
5353
|
}
|
|
5333
|
-
if (this.
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5354
|
+
if (!this.navigationOnly) {
|
|
5355
|
+
if (this.resizeManager.handleMouseMove(event)) {
|
|
5356
|
+
return;
|
|
5357
|
+
}
|
|
5358
|
+
if (this.connectionManager.handleMouseMove(event)) {
|
|
5359
|
+
return;
|
|
5360
|
+
}
|
|
5361
|
+
if (this.dragManager.handleMouseMove(event)) {
|
|
5362
|
+
return;
|
|
5363
|
+
}
|
|
5341
5364
|
}
|
|
5342
5365
|
if (this.selectionManager.selectionRectangle !== null) {
|
|
5343
5366
|
this.selectionManager.updateSelectionRect({ x: event.worldX, y: event.worldY });
|
|
@@ -5356,14 +5379,16 @@ class InteractionManager {
|
|
|
5356
5379
|
this.renderer.setScrollbarActiveAxis(null);
|
|
5357
5380
|
return;
|
|
5358
5381
|
}
|
|
5359
|
-
if (this.
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5382
|
+
if (!this.navigationOnly) {
|
|
5383
|
+
if (this.resizeManager.handleMouseUp()) {
|
|
5384
|
+
return;
|
|
5385
|
+
}
|
|
5386
|
+
if (this.connectionManager.handleMouseUp(event)) {
|
|
5387
|
+
return;
|
|
5388
|
+
}
|
|
5389
|
+
if (this.dragManager.handleMouseUp(event)) {
|
|
5390
|
+
return;
|
|
5391
|
+
}
|
|
5367
5392
|
}
|
|
5368
5393
|
if (this.selectionManager.selectionRectangle !== null) {
|
|
5369
5394
|
this.selectionManager.endSelectionRect();
|
|
@@ -5389,6 +5414,9 @@ class InteractionManager {
|
|
|
5389
5414
|
if (this.dragManager.handledMouseDown || this.resizeManager.handledMouseDown || this.connectionManager.connecting) {
|
|
5390
5415
|
return;
|
|
5391
5416
|
}
|
|
5417
|
+
if (this.navigationOnly) {
|
|
5418
|
+
return;
|
|
5419
|
+
}
|
|
5392
5420
|
if (this.connectionManager.handleDoubleClick(event)) {
|
|
5393
5421
|
return;
|
|
5394
5422
|
}
|
|
@@ -5451,16 +5479,19 @@ class InteractionManager {
|
|
|
5451
5479
|
handleKeyDown(event, options) {
|
|
5452
5480
|
const isCtrlOrMeta = event.ctrlKey || event.metaKey;
|
|
5453
5481
|
const key = event.code.startsWith("Key") ? event.code.slice(3).toLowerCase() : event.key.toLowerCase();
|
|
5482
|
+
this.navigationManager.handleKeyDown(event);
|
|
5483
|
+
if (this.handleViewportNavigationKey(event)) {
|
|
5484
|
+
return;
|
|
5485
|
+
}
|
|
5486
|
+
if (this.navigationOnly) {
|
|
5487
|
+
return;
|
|
5488
|
+
}
|
|
5454
5489
|
if (isCtrlOrMeta && (key === "z" || key === "y")) {
|
|
5455
5490
|
this.flushPendingPropertyChanges();
|
|
5456
5491
|
}
|
|
5457
5492
|
if (this.historyManager.handleKeyDown(event)) {
|
|
5458
5493
|
return;
|
|
5459
5494
|
}
|
|
5460
|
-
this.navigationManager.handleKeyDown(event);
|
|
5461
|
-
if (this.handleViewportNavigationKey(event)) {
|
|
5462
|
-
return;
|
|
5463
|
-
}
|
|
5464
5495
|
if (this.keymap.deleteKeys.includes(event.key)) {
|
|
5465
5496
|
event.preventDefault();
|
|
5466
5497
|
this.deleteSelection();
|
|
@@ -6713,6 +6744,30 @@ class DiagramRenderer extends EventEmitter {
|
|
|
6713
6744
|
}
|
|
6714
6745
|
return void 0;
|
|
6715
6746
|
}
|
|
6747
|
+
/**
|
|
6748
|
+
* Update badge hover state and canvas cursor based on pointer position.
|
|
6749
|
+
* Call from mousemove to show hover highlight and pointer cursor over badges.
|
|
6750
|
+
*/
|
|
6751
|
+
updateBadgeHover(worldPoint) {
|
|
6752
|
+
const element = this.getElementAtPoint(worldPoint);
|
|
6753
|
+
let hoveredNodeId = null;
|
|
6754
|
+
let hoveredIndex = -1;
|
|
6755
|
+
const node = element;
|
|
6756
|
+
if (element && typeof node.getBadgeAtPoint === "function") {
|
|
6757
|
+
const badge = node.getBadgeAtPoint(worldPoint);
|
|
6758
|
+
if (badge !== null) {
|
|
6759
|
+
hoveredNodeId = node.id;
|
|
6760
|
+
hoveredIndex = badge.index;
|
|
6761
|
+
}
|
|
6762
|
+
}
|
|
6763
|
+
const cursor = hoveredNodeId !== null ? "pointer" : "";
|
|
6764
|
+
if (this.canvas.style.cursor !== cursor) {
|
|
6765
|
+
this.canvas.style.cursor = cursor;
|
|
6766
|
+
}
|
|
6767
|
+
for (const n of this._nodes.values()) {
|
|
6768
|
+
n.setBadgeHover(n.id === hoveredNodeId ? hoveredIndex : -1);
|
|
6769
|
+
}
|
|
6770
|
+
}
|
|
6716
6771
|
/**
|
|
6717
6772
|
* Mark the diagram as needing re-render
|
|
6718
6773
|
*/
|
|
@@ -8025,11 +8080,11 @@ function tintSvg(svgText, strokeColor, fillColor) {
|
|
|
8025
8080
|
const all = [root, ...Array.from(root.querySelectorAll("*"))];
|
|
8026
8081
|
for (const el of all) {
|
|
8027
8082
|
const stroke = el.getAttribute("stroke");
|
|
8028
|
-
|
|
8083
|
+
const fill = el.getAttribute("fill");
|
|
8084
|
+
if (strokeColor && (stroke === null || stroke.toLowerCase() !== "none")) {
|
|
8029
8085
|
el.setAttribute("stroke", strokeColor);
|
|
8030
8086
|
}
|
|
8031
|
-
|
|
8032
|
-
if (fillColor && fill !== null && fill.toLowerCase() !== "none") {
|
|
8087
|
+
if (fillColor && (fill === null || fill.toLowerCase() !== "none")) {
|
|
8033
8088
|
el.setAttribute("fill", fillColor);
|
|
8034
8089
|
}
|
|
8035
8090
|
const style = el.getAttribute("style");
|
|
@@ -8196,6 +8251,9 @@ class NodeImage {
|
|
|
8196
8251
|
};
|
|
8197
8252
|
}
|
|
8198
8253
|
}
|
|
8254
|
+
const BADGE_SIZE = 15;
|
|
8255
|
+
const BADGE_OFFSET = 4;
|
|
8256
|
+
const BADGE_GAP = 4;
|
|
8199
8257
|
function isValidAnchorId(id) {
|
|
8200
8258
|
return /^(top|right|bottom|left):\d+$/.test(id);
|
|
8201
8259
|
}
|
|
@@ -8217,7 +8275,10 @@ class Node extends Element {
|
|
|
8217
8275
|
styleClass: options.styleClass
|
|
8218
8276
|
});
|
|
8219
8277
|
this._ports = [];
|
|
8278
|
+
this._badges = [];
|
|
8220
8279
|
this._anchorCache = null;
|
|
8280
|
+
this._badgeImageCache = /* @__PURE__ */ new Map();
|
|
8281
|
+
this._hoveredBadgeIndex = -1;
|
|
8221
8282
|
this._defaultSize = { width: options.width, height: options.height };
|
|
8222
8283
|
this._nodeStyle = { ...DEFAULT_NODE_STYLE, ...options.style };
|
|
8223
8284
|
this._showPortsAlways = options.showPortsAlways ?? false;
|
|
@@ -8242,6 +8303,10 @@ class Node extends Element {
|
|
|
8242
8303
|
this.addPort(portOptions);
|
|
8243
8304
|
}
|
|
8244
8305
|
}
|
|
8306
|
+
if (options.badges !== void 0 && options.badges.length > 0) {
|
|
8307
|
+
this._badges = options.badges.map((b) => ({ id: b.id, iconUrl: b.iconUrl }));
|
|
8308
|
+
this.ensureBadgeImagesLoaded();
|
|
8309
|
+
}
|
|
8245
8310
|
}
|
|
8246
8311
|
/**
|
|
8247
8312
|
* Node style
|
|
@@ -8307,6 +8372,46 @@ class Node extends Element {
|
|
|
8307
8372
|
}
|
|
8308
8373
|
this.markDirty();
|
|
8309
8374
|
}
|
|
8375
|
+
/**
|
|
8376
|
+
* Badges shown in top-left corner of node (e.g. interactive property icons)
|
|
8377
|
+
*/
|
|
8378
|
+
get badges() {
|
|
8379
|
+
return this._badges;
|
|
8380
|
+
}
|
|
8381
|
+
set badges(value) {
|
|
8382
|
+
this._badges = Array.isArray(value) ? value.map((b) => ({ id: b.id, iconUrl: b.iconUrl })) : [];
|
|
8383
|
+
this.ensureBadgeImagesLoaded();
|
|
8384
|
+
this.markDirty();
|
|
8385
|
+
}
|
|
8386
|
+
/**
|
|
8387
|
+
* Set which badge index is under the pointer (-1 for none). Used for hover highlight and cursor.
|
|
8388
|
+
*/
|
|
8389
|
+
setBadgeHover(index) {
|
|
8390
|
+
if (this._hoveredBadgeIndex === index) return;
|
|
8391
|
+
this._hoveredBadgeIndex = index;
|
|
8392
|
+
this.markDirty();
|
|
8393
|
+
}
|
|
8394
|
+
/**
|
|
8395
|
+
* Return badge at world point, or null if point is not over a badge.
|
|
8396
|
+
*/
|
|
8397
|
+
getBadgeAtPoint(worldPoint) {
|
|
8398
|
+
if (this._badges.length === 0) {
|
|
8399
|
+
return null;
|
|
8400
|
+
}
|
|
8401
|
+
const bounds = this.getBounds();
|
|
8402
|
+
const contentBounds = this.getLabelContainerBounds(bounds);
|
|
8403
|
+
const localX = worldPoint.x - (contentBounds.x + BADGE_OFFSET);
|
|
8404
|
+
const localY = worldPoint.y - (contentBounds.y + BADGE_OFFSET);
|
|
8405
|
+
for (let i = 0; i < this._badges.length; i++) {
|
|
8406
|
+
const badge = this._badges[i];
|
|
8407
|
+
if (badge === void 0) continue;
|
|
8408
|
+
const x = i * (BADGE_SIZE + BADGE_GAP);
|
|
8409
|
+
if (localX >= x && localX <= x + BADGE_SIZE && localY >= 0 && localY <= BADGE_SIZE) {
|
|
8410
|
+
return { id: badge.id, index: i };
|
|
8411
|
+
}
|
|
8412
|
+
}
|
|
8413
|
+
return null;
|
|
8414
|
+
}
|
|
8310
8415
|
/**
|
|
8311
8416
|
* Add a port to this node
|
|
8312
8417
|
*/
|
|
@@ -8526,6 +8631,7 @@ class Node extends Element {
|
|
|
8526
8631
|
ctx.setLineDash([]);
|
|
8527
8632
|
ctx.lineDashOffset = 0;
|
|
8528
8633
|
let bounds = this.getBounds();
|
|
8634
|
+
this.renderBadges(ctx, bounds);
|
|
8529
8635
|
const iconBoxSize = this._icon ? this.getIconBoxSize() : void 0;
|
|
8530
8636
|
if (this._label) {
|
|
8531
8637
|
this._label.setAutoMaxWidth(this.getLabelContainerBounds(bounds).width);
|
|
@@ -8549,6 +8655,67 @@ class Node extends Element {
|
|
|
8549
8655
|
this.renderLabel(ctx, labelBounds);
|
|
8550
8656
|
this.renderPorts(ctx);
|
|
8551
8657
|
}
|
|
8658
|
+
ensureBadgeImagesLoaded() {
|
|
8659
|
+
for (const badge of this._badges) {
|
|
8660
|
+
const url = badge.iconUrl;
|
|
8661
|
+
if (!url || this._badgeImageCache.has(url)) {
|
|
8662
|
+
continue;
|
|
8663
|
+
}
|
|
8664
|
+
const img = new Image();
|
|
8665
|
+
img.decoding = "async";
|
|
8666
|
+
this._badgeImageCache.set(url, { img, loaded: false });
|
|
8667
|
+
img.onload = () => {
|
|
8668
|
+
const entry = this._badgeImageCache.get(url);
|
|
8669
|
+
if (entry) {
|
|
8670
|
+
entry.loaded = true;
|
|
8671
|
+
this.markDirty();
|
|
8672
|
+
}
|
|
8673
|
+
};
|
|
8674
|
+
img.onerror = () => {
|
|
8675
|
+
this.markDirty();
|
|
8676
|
+
};
|
|
8677
|
+
img.src = url;
|
|
8678
|
+
}
|
|
8679
|
+
}
|
|
8680
|
+
renderBadges(ctx, bounds) {
|
|
8681
|
+
if (this._badges.length === 0) {
|
|
8682
|
+
return;
|
|
8683
|
+
}
|
|
8684
|
+
const contentBounds = this.getLabelContainerBounds(bounds);
|
|
8685
|
+
const x0 = contentBounds.x + BADGE_OFFSET;
|
|
8686
|
+
const y0 = contentBounds.y + BADGE_OFFSET;
|
|
8687
|
+
const radius = 2;
|
|
8688
|
+
for (let i = 0; i < this._badges.length; i++) {
|
|
8689
|
+
const badge = this._badges[i];
|
|
8690
|
+
if (badge === void 0) continue;
|
|
8691
|
+
const x = x0 + i * (BADGE_SIZE + BADGE_GAP);
|
|
8692
|
+
const isHovered = this._hoveredBadgeIndex === i;
|
|
8693
|
+
if (isHovered) {
|
|
8694
|
+
ctx.save();
|
|
8695
|
+
ctx.fillStyle = "rgba(0, 0, 0, 0.08)";
|
|
8696
|
+
ctx.beginPath();
|
|
8697
|
+
ctx.roundRect(x, y0, BADGE_SIZE, BADGE_SIZE, radius);
|
|
8698
|
+
ctx.fill();
|
|
8699
|
+
ctx.restore();
|
|
8700
|
+
}
|
|
8701
|
+
const entry = this._badgeImageCache.get(badge.iconUrl);
|
|
8702
|
+
if (entry?.loaded && entry.img.naturalWidth > 0) {
|
|
8703
|
+
ctx.save();
|
|
8704
|
+
const img = entry.img;
|
|
8705
|
+
const sw = img.naturalWidth;
|
|
8706
|
+
const sh = img.naturalHeight;
|
|
8707
|
+
const scale = Math.min(BADGE_SIZE / sw, BADGE_SIZE / sh, 1);
|
|
8708
|
+
const dw = sw * scale;
|
|
8709
|
+
const dh = sh * scale;
|
|
8710
|
+
const dx = x + (BADGE_SIZE - dw) / 2;
|
|
8711
|
+
const dy = y0 + (BADGE_SIZE - dh) / 2;
|
|
8712
|
+
ctx.imageSmoothingEnabled = true;
|
|
8713
|
+
ctx.imageSmoothingQuality = "high";
|
|
8714
|
+
ctx.drawImage(img, 0, 0, sw, sh, dx, dy, dw, dh);
|
|
8715
|
+
ctx.restore();
|
|
8716
|
+
}
|
|
8717
|
+
}
|
|
8718
|
+
}
|
|
8552
8719
|
/**
|
|
8553
8720
|
* Minimal size required to fit current contents
|
|
8554
8721
|
*/
|