Skip to content

Commit 8bd5fd3

Browse files
committed
upd linked-block
1 parent d51fa53 commit 8bd5fd3

File tree

6 files changed

+393
-172
lines changed

6 files changed

+393
-172
lines changed

examples/linked-block.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949

5050
window.metroConnectorSetup = {
5151
deleteButton: true,
52+
arrow: true,
53+
lineStyle: "dotted",
5254
// type: 'zigzag'
5355
}
5456
</script>

lib/metro.js

Lines changed: 174 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -31064,6 +31064,10 @@
3106431064
content: "",
3106531065
showAddButtons: true,
3106631066
resizeHotkey: null,
31067+
connectionType: "curve",
31068+
// line, curve, zigzag
31069+
connectionStyle: "solid",
31070+
// solid, dashed, dotted
3106731071
onAddPoint: Metro2.noop,
3106831072
onRemovePoint: Metro2.noop,
3106931073
onStartConnection: Metro2.noop,
@@ -31328,7 +31332,8 @@
3132831332
}
3132931333
const connectionId = `connection-${Date.now()}`;
3133031334
const connector = Metro2.connector.create(globalConnectionState.sourcePoint[0], targetPoint[0], {
31331-
// type: connector.options.type || "curve",
31335+
type: o2.connectionType,
31336+
lineStyle: o2.connectionStyle,
3133231337
container: globalConnectionState.sourceBlock.element.parent(),
3133331338
id: connectionId,
3133431339
autoUpdate: true
@@ -31798,6 +31803,9 @@
3179831803
id: null,
3179931804
// унікальний ID для з'єднання
3180031805
deleteButton: false,
31806+
arrow: false,
31807+
lineStyle: "solid",
31808+
// solid, dashed, dotted
3180131809
onConnectorCreate: Metro2.noop,
3180231810
onConnectorUpdate: Metro2.noop,
3180331811
onConnectorDestroy: Metro2.noop
@@ -31850,6 +31858,11 @@
3185031858
this.svgElement = sharedSvg;
3185131859
const shape = this._createShape(o2.id, o2.type, sharedSvg);
3185231860
const deleteBtn = this._createDeleteButton(sharedSvg, o2.id);
31861+
if (o2.arrow) {
31862+
const markerId = this._ensureArrowMarker(sharedSvg);
31863+
shape.attr("marker-end", `url(#${markerId})`);
31864+
}
31865+
this._applyLineStyle(shape, o2.lineStyle);
3185331866
this.connections.set(o2.id, {
3185431867
pointA: o2.pointA,
3185531868
pointB: o2.pointB,
@@ -31935,6 +31948,46 @@
3193531948
}
3193631949
return svg;
3193731950
},
31951+
_ensureArrowMarker: (svg) => {
31952+
const ns = "http://www.w3.org/2000/svg";
31953+
const markerId = "connector-arrow";
31954+
let defs = svg.find("defs");
31955+
if (!defs.length) {
31956+
const d4 = document.createElementNS(ns, "defs");
31957+
svg[0].appendChild(d4);
31958+
defs = $7(d4);
31959+
}
31960+
let marker = defs.find(`#${markerId}`);
31961+
if (!marker.length) {
31962+
const m5 = document.createElementNS(ns, "marker");
31963+
m5.setAttribute("id", markerId);
31964+
m5.setAttribute("markerWidth", "6");
31965+
m5.setAttribute("markerHeight", "6");
31966+
m5.setAttribute("refX", "5");
31967+
m5.setAttribute("refY", "3");
31968+
m5.setAttribute("orient", "auto");
31969+
m5.setAttribute("markerUnits", "strokeWidth");
31970+
const line1 = document.createElementNS(ns, "line");
31971+
line1.setAttribute("x1", "1");
31972+
line1.setAttribute("y1", "1");
31973+
line1.setAttribute("x2", "5");
31974+
line1.setAttribute("y2", "3");
31975+
line1.setAttribute("stroke", "context-stroke");
31976+
line1.setAttribute("stroke-linecap", "round");
31977+
const line2 = document.createElementNS(ns, "line");
31978+
line2.setAttribute("x1", "1");
31979+
line2.setAttribute("y1", "5");
31980+
line2.setAttribute("x2", "5");
31981+
line2.setAttribute("y2", "3");
31982+
line2.setAttribute("stroke", "context-stroke");
31983+
line2.setAttribute("stroke-linecap", "round");
31984+
m5.appendChild(line1);
31985+
m5.appendChild(line2);
31986+
defs[0].appendChild(m5);
31987+
marker = $7(m5);
31988+
}
31989+
return markerId;
31990+
},
3193831991
_createShape: (id, type, svg) => {
3193931992
const ns = "http://www.w3.org/2000/svg";
3194031993
let el;
@@ -31990,28 +32043,6 @@
3199032043
});
3199132044
return $7(g5);
3199232045
},
31993-
// Публічні методи
31994-
update: function() {
31995-
const o2 = this.options;
31996-
const connection = this.connections.get(o2.id);
31997-
if (!connection) return;
31998-
switch (o2.type) {
31999-
case "line":
32000-
this._updateLine(connection.pointA, connection.pointB, connection.shape);
32001-
break;
32002-
case "curve":
32003-
this._updateCurve(connection.pointA, connection.pointB, connection.shape);
32004-
break;
32005-
case "zigzag":
32006-
this._updateZigzag(connection.pointA, connection.pointB, connection.shape);
32007-
break;
32008-
}
32009-
this._positionDeleteButton(connection);
32010-
this._fireEvent("connector-update", {
32011-
connection,
32012-
type: o2.type
32013-
});
32014-
},
3201532046
_positionDeleteButton: (connection) => {
3201632047
const { type, shape, deleteBtn } = connection;
3201732048
if (!deleteBtn || !deleteBtn.length || !shape || !shape.length) return;
@@ -32038,46 +32069,6 @@
3203832069
const offsetY = 10;
3203932070
deleteBtn.attr("transform", `translate(${cx - offsetX}, ${cy - offsetY})`);
3204032071
},
32041-
setType: function(type) {
32042-
if (["line", "curve", "zigzag"].indexOf(type) === -1) {
32043-
console.warn("Connector: \u043D\u0435\u0432\u0456\u0434\u043E\u043C\u0438\u0439 \u0442\u0438\u043F \u0437'\u0454\u0434\u043D\u0430\u043D\u043D\u044F:", type);
32044-
return;
32045-
}
32046-
const o2 = this.options;
32047-
const oldType = o2.type;
32048-
o2.type = type;
32049-
const connection = this.connections.get(o2.id);
32050-
const oldShape = connection?.shape;
32051-
const svg = connection?.svg || this.svgElement || this._getOrCreateSharedSVG(o2.container);
32052-
const newShape = this._createShape(o2.id, type, svg);
32053-
if (oldShape?.length) {
32054-
oldShape.remove();
32055-
}
32056-
this.connections.set(o2.id, {
32057-
...connection,
32058-
type,
32059-
old: oldType,
32060-
svg,
32061-
shape: newShape,
32062-
deleteBtn: connection?.deleteBtn
32063-
});
32064-
this.update();
32065-
},
32066-
setPoints: function(pointA, pointB) {
32067-
const o2 = this.options;
32068-
o2.pointA = pointA;
32069-
o2.pointB = pointB;
32070-
this.connections.set(o2.id, {
32071-
...this.connections.get(o2.id),
32072-
pointA,
32073-
pointB
32074-
});
32075-
if (o2.autoUpdate) {
32076-
this._cleanupAutoUpdate();
32077-
this._setupAutoUpdate();
32078-
}
32079-
this.update();
32080-
},
3208132072
// Приватні методи оновлення
3208232073
_updateLine: (pointA, pointB, shape) => {
3208332074
const point1 = $7(pointA);
@@ -32117,44 +32108,45 @@
3211732108
let cp1x, cp1y, cp2x, cp2y;
3211832109
const side1 = parent1.attr("class").match(/(north|south|east|west)-side/)?.[1] || "north";
3211932110
const side2 = parent2.attr("class").match(/(north|south|east|west)-side/)?.[1] || "north";
32111+
const magic = 20;
3212032112
if (side1 === side2) {
3212132113
const controlOffset = Math.max(60, distance * 0.3);
3212232114
switch (side1) {
3212332115
case "north":
3212432116
cp1x = x1;
32125-
cp1y = y1 - controlOffset;
32117+
cp1y = y1 - controlOffset - magic;
3212632118
cp2x = x22;
32127-
cp2y = y22 - controlOffset;
32119+
cp2y = y22 - controlOffset - magic;
3212832120
break;
3212932121
case "south":
3213032122
cp1x = x1;
32131-
cp1y = y1 + controlOffset;
32123+
cp1y = y1 + controlOffset + magic;
3213232124
cp2x = x22;
32133-
cp2y = y22 + controlOffset;
32125+
cp2y = y22 + controlOffset + magic;
3213432126
break;
3213532127
case "east":
32136-
cp1x = x1 + controlOffset;
32128+
cp1x = x1 + controlOffset - magic;
3213732129
cp1y = y1;
32138-
cp2x = x22 + controlOffset;
32130+
cp2x = x22 + controlOffset - magic;
3213932131
cp2y = y22;
3214032132
break;
3214132133
case "west":
32142-
cp1x = x1 - controlOffset;
32134+
cp1x = x1 - controlOffset + magic;
3214332135
cp1y = y1;
32144-
cp2x = x22 - controlOffset;
32136+
cp2x = x22 - controlOffset + magic;
3214532137
cp2y = y22;
3214632138
break;
3214732139
}
3214832140
} else {
3214932141
const direction = this._getDirection(parent1, parent2);
3215032142
if (direction === "horizontal") {
32151-
const controlDistance = Math.abs(dx) * 0.4;
32143+
const controlDistance = Math.abs(dx) * 0.4 + magic;
3215232144
cp1x = x1 + (dx > 0 ? controlDistance : -controlDistance);
3215332145
cp1y = y1;
3215432146
cp2x = x22 - (dx > 0 ? controlDistance : -controlDistance);
3215532147
cp2y = y22;
3215632148
} else {
32157-
const controlDistance = Math.abs(dy) * 0.4;
32149+
const controlDistance = Math.abs(dy) * 0.4 + magic;
3215832150
cp1x = x1;
3215932151
cp1y = y1 + (dy > 0 ? controlDistance : -controlDistance);
3216032152
cp2x = x22;
@@ -32327,10 +32319,119 @@
3232732319
}
3232832320
$7(document).off(".connector." + o2.id);
3232932321
},
32322+
_getStrokeWidth: (shape) => {
32323+
const sw = parseFloat(getComputedStyle(shape[0]).strokeWidth || "1");
32324+
return isFinite(sw) ? sw : 1;
32325+
},
32326+
_getDashArray: function(style, shape) {
32327+
const sw = this._getStrokeWidth(shape);
32328+
switch ((style || "").toLowerCase()) {
32329+
case "dashed":
32330+
return `${Math.max(4, 6 * sw)} ${Math.max(3, 4 * sw)}`;
32331+
case "dotted":
32332+
return `${Math.max(1, 1.5 * sw)} ${Math.max(2, 3 * sw)}`;
32333+
case "solid":
32334+
default:
32335+
return null;
32336+
}
32337+
},
32338+
_applyLineStyle: function(shape, style) {
32339+
const dash = this._getDashArray(style, shape);
32340+
if (dash) {
32341+
shape.attr("stroke-dasharray", dash);
32342+
shape.attr("data-line-style", style);
32343+
} else {
32344+
shape.removeAttr("stroke-dasharray");
32345+
shape.attr("data-line-style", "solid");
32346+
}
32347+
},
32348+
// Публічні методи
32349+
setType: function(type) {
32350+
if (["line", "curve", "zigzag"].indexOf(type) === -1) {
32351+
console.warn("Connector: \u043D\u0435\u0432\u0456\u0434\u043E\u043C\u0438\u0439 \u0442\u0438\u043F \u0437'\u0454\u0434\u043D\u0430\u043D\u043D\u044F:", type);
32352+
return;
32353+
}
32354+
const o2 = this.options;
32355+
const oldType = o2.type;
32356+
o2.type = type;
32357+
const connection = this.connections.get(o2.id);
32358+
const oldShape = connection?.shape;
32359+
const svg = connection?.svg || this.svgElement || this._getOrCreateSharedSVG(o2.container);
32360+
const newShape = this._createShape(o2.id, type, svg);
32361+
if (o2.arrow) {
32362+
const markerId = this._ensureArrowMarker(svg);
32363+
newShape.attr("marker-end", `url(#${markerId})`);
32364+
}
32365+
this._applyLineStyle(newShape, o2.lineStyle);
32366+
if (oldShape?.length) {
32367+
oldShape.remove();
32368+
}
32369+
this.connections.set(o2.id, {
32370+
...connection,
32371+
type,
32372+
old: oldType,
32373+
svg,
32374+
shape: newShape,
32375+
deleteBtn: connection?.deleteBtn
32376+
});
32377+
this.update();
32378+
},
32379+
setPoints: function(pointA, pointB) {
32380+
const o2 = this.options;
32381+
o2.pointA = pointA;
32382+
o2.pointB = pointB;
32383+
this.connections.set(o2.id, {
32384+
...this.connections.get(o2.id),
32385+
pointA,
32386+
pointB
32387+
});
32388+
if (o2.autoUpdate) {
32389+
this._cleanupAutoUpdate();
32390+
this._setupAutoUpdate();
32391+
}
32392+
this.update();
32393+
},
32394+
update: function() {
32395+
const o2 = this.options;
32396+
const connection = this.connections.get(o2.id);
32397+
if (!connection) return;
32398+
switch (o2.type) {
32399+
case "line":
32400+
this._updateLine(connection.pointA, connection.pointB, connection.shape);
32401+
break;
32402+
case "curve":
32403+
this._updateCurve(connection.pointA, connection.pointB, connection.shape);
32404+
break;
32405+
case "zigzag":
32406+
this._updateZigzag(connection.pointA, connection.pointB, connection.shape);
32407+
break;
32408+
}
32409+
this._positionDeleteButton(connection);
32410+
this._fireEvent("connector-update", {
32411+
connection,
32412+
type: o2.type
32413+
});
32414+
},
32415+
setLineStyle: function(style) {
32416+
const allowed = ["solid", "dashed", "dotted"];
32417+
if (allowed.indexOf((style || "").toLowerCase()) === -1) {
32418+
console.warn("Connector: \u043D\u0435\u0432\u0456\u0434\u043E\u043C\u0438\u0439 \u0441\u0442\u0438\u043B\u044C \u043B\u0456\u043D\u0456\u0457:", style);
32419+
return;
32420+
}
32421+
const o2 = this.options;
32422+
o2.lineStyle = style.toLowerCase();
32423+
const connection = this.connections.get(o2.id);
32424+
if (!connection?.shape) return;
32425+
this._applyLineStyle(connection.shape, o2.lineStyle);
32426+
this.update();
32427+
},
3233032428
changeAttribute: function(attr, newValue) {
3233132429
if (attr === "data-type") {
3233232430
this.setType(newValue);
3233332431
}
32432+
if (attr === "data-line-style") {
32433+
this.setLineStyle(newValue);
32434+
}
3233432435
},
3233532436
destroy: function() {
3233632437
const o2 = this.options;

lib/metro.js.map

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

source/components/linked-block/linked-block.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
content: "",
2121
showAddButtons: true,
2222
resizeHotkey: null,
23+
connectionType: "curve", // line, curve, zigzag
24+
connectionStyle: "solid", // solid, dashed, dotted
2325
onAddPoint: Metro.noop,
2426
onRemovePoint: Metro.noop,
2527
onStartConnection: Metro.noop,
@@ -368,7 +370,8 @@
368370
// Створюємо постійне з'єднання типу curve
369371
const connectionId = `connection-${Date.now()}`;
370372
const connector = Metro.connector.create(globalConnectionState.sourcePoint[0], targetPoint[0], {
371-
// type: connector.options.type || "curve",
373+
type: o.connectionType,
374+
lineStyle: o.connectionStyle,
372375
container: globalConnectionState.sourceBlock.element.parent(),
373376
id: connectionId,
374377
autoUpdate: true,

source/components/linked-block/linked-block.less

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
.linked-block {
2525
position: absolute;
2626
display: block;
27-
//min-width: var(--linked-block-min-width);
28-
//min-height: var(--linked-block-min-height);
2927
background-color: var(--linked-block-background);
3028
color: var(--linked-block-color);
3129
border: 1px solid var(--linked-block-border-color);

0 commit comments

Comments
 (0)