@ngroznykh/papirus 0.1.0 → 0.2.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/README.md +23 -8
- package/README.ru.md +23 -8
- package/dist/core/InteractionManager.d.ts +5 -0
- package/dist/core/InteractionManager.d.ts.map +1 -1
- package/dist/elements/Edge.d.ts +4 -0
- package/dist/elements/Edge.d.ts.map +1 -1
- package/dist/elements/Node.d.ts +6 -0
- package/dist/elements/Node.d.ts.map +1 -1
- package/dist/elements/TextLabel.d.ts +5 -0
- package/dist/elements/TextLabel.d.ts.map +1 -1
- package/dist/elements/nodes/CircleNode.d.ts +2 -1
- package/dist/elements/nodes/CircleNode.d.ts.map +1 -1
- package/dist/elements/nodes/DiamondNode.d.ts +2 -1
- package/dist/elements/nodes/DiamondNode.d.ts.map +1 -1
- package/dist/papirus.js +318 -28
- package/dist/papirus.js.map +1 -1
- package/package.json +5 -2
package/dist/papirus.js
CHANGED
|
@@ -1968,11 +1968,25 @@ class TextLabel {
|
|
|
1968
1968
|
return this._maxWidth;
|
|
1969
1969
|
}
|
|
1970
1970
|
set maxWidth(value) {
|
|
1971
|
+
if (this._maxWidth === value) {
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1971
1974
|
this._maxWidth = value;
|
|
1972
1975
|
this._lines = [];
|
|
1973
1976
|
this._measureDirty = true;
|
|
1974
1977
|
this._onChange?.();
|
|
1975
1978
|
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Internal max width used for automatic wrapping by container elements.
|
|
1981
|
+
*/
|
|
1982
|
+
setAutoMaxWidth(value) {
|
|
1983
|
+
if (this._autoMaxWidth === value) {
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
this._autoMaxWidth = value;
|
|
1987
|
+
this._lines = [];
|
|
1988
|
+
this._measureDirty = true;
|
|
1989
|
+
}
|
|
1976
1990
|
/**
|
|
1977
1991
|
* Label padding
|
|
1978
1992
|
*/
|
|
@@ -2022,8 +2036,9 @@ class TextLabel {
|
|
|
2022
2036
|
}
|
|
2023
2037
|
this.applyStyle(ctx);
|
|
2024
2038
|
const lineHeight = (this._style.fontSize ?? 14) * 1.2;
|
|
2025
|
-
|
|
2026
|
-
|
|
2039
|
+
const effectiveMaxWidth = this._maxWidth ?? this._autoMaxWidth;
|
|
2040
|
+
if (effectiveMaxWidth !== void 0) {
|
|
2041
|
+
const maxWidth = Math.max(0, effectiveMaxWidth - this._margin * 2);
|
|
2027
2042
|
this._lines = this.wrapText(ctx, this._text, Math.max(0, maxWidth - this._padding * 2));
|
|
2028
2043
|
} else {
|
|
2029
2044
|
this._lines = this._text.split("\n");
|
|
@@ -3508,6 +3523,19 @@ class Edge extends Element {
|
|
|
3508
3523
|
}
|
|
3509
3524
|
return this.getPolylineMidpoint(path);
|
|
3510
3525
|
}
|
|
3526
|
+
/**
|
|
3527
|
+
* Get world position of label center along path.
|
|
3528
|
+
*/
|
|
3529
|
+
getLabelPosition() {
|
|
3530
|
+
if (this._path.length < 2) {
|
|
3531
|
+
return null;
|
|
3532
|
+
}
|
|
3533
|
+
const midpoint = this.getPathMidpoint();
|
|
3534
|
+
return {
|
|
3535
|
+
x: midpoint.x,
|
|
3536
|
+
y: midpoint.y + this._labelOffset
|
|
3537
|
+
};
|
|
3538
|
+
}
|
|
3511
3539
|
getPolylineMidpoint(path) {
|
|
3512
3540
|
if (path.length === 0) {
|
|
3513
3541
|
return { x: 0, y: 0 };
|
|
@@ -3578,6 +3606,7 @@ class InteractionManager {
|
|
|
3578
3606
|
this.clipboard = null;
|
|
3579
3607
|
this.pendingPropertyChanges = /* @__PURE__ */ new Map();
|
|
3580
3608
|
this.propertyChangeDebounceMs = 350;
|
|
3609
|
+
this.activeLabelEditor = null;
|
|
3581
3610
|
this.renderer = options.renderer;
|
|
3582
3611
|
this.inputHandler = new InputHandler({
|
|
3583
3612
|
canvas: this.renderer.getCanvas(),
|
|
@@ -3704,6 +3733,7 @@ class InteractionManager {
|
|
|
3704
3733
|
}
|
|
3705
3734
|
}
|
|
3706
3735
|
destroy() {
|
|
3736
|
+
this.finishInlineLabelEdit(false);
|
|
3707
3737
|
this.inputHandler.destroy();
|
|
3708
3738
|
this.overlayCleanup?.();
|
|
3709
3739
|
this.overlayCleanup = null;
|
|
@@ -3724,6 +3754,7 @@ class InteractionManager {
|
|
|
3724
3754
|
this.inputHandler.on("mousemove", (event) => this.handleMouseMove(event));
|
|
3725
3755
|
this.inputHandler.on("mouseup", (event) => this.handleMouseUp(event));
|
|
3726
3756
|
this.inputHandler.on("click", (event) => this.handleClick(event));
|
|
3757
|
+
this.inputHandler.on("dblclick", (event) => this.handleDoubleClick(event));
|
|
3727
3758
|
this.inputHandler.on("wheel", (event) => this.handleWheel(event));
|
|
3728
3759
|
this.inputHandler.on("pan", (event) => this.handlePan(event));
|
|
3729
3760
|
this.inputHandler.on("pinch", (event) => this.handlePinch(event));
|
|
@@ -3861,6 +3892,56 @@ class InteractionManager {
|
|
|
3861
3892
|
}
|
|
3862
3893
|
this.selectionManager.handleClick(event);
|
|
3863
3894
|
}
|
|
3895
|
+
handleDoubleClick(event) {
|
|
3896
|
+
if (this.dragManager.handledMouseDown || this.resizeManager.handledMouseDown || this.connectionManager.connecting) {
|
|
3897
|
+
return;
|
|
3898
|
+
}
|
|
3899
|
+
const point = { x: event.worldX, y: event.worldY };
|
|
3900
|
+
const edgeByLabel = this.getEdgeLabelAtPoint(point);
|
|
3901
|
+
if (edgeByLabel) {
|
|
3902
|
+
const labelPosition = edgeByLabel.getLabelPosition() ?? point;
|
|
3903
|
+
this.startInlineLabelEdit("edge", edgeByLabel.id, edgeByLabel.label?.text ?? "", labelPosition);
|
|
3904
|
+
return;
|
|
3905
|
+
}
|
|
3906
|
+
const hitElement = this.renderer.getElementAtPoint(point);
|
|
3907
|
+
if (!hitElement) {
|
|
3908
|
+
this.finishInlineLabelEdit(true);
|
|
3909
|
+
return;
|
|
3910
|
+
}
|
|
3911
|
+
if ("typeName" in hitElement) {
|
|
3912
|
+
this.startInlineLabelEdit("node", hitElement.id, hitElement.label?.text ?? "", hitElement.getLabelPosition());
|
|
3913
|
+
return;
|
|
3914
|
+
}
|
|
3915
|
+
if ("from" in hitElement && "to" in hitElement) {
|
|
3916
|
+
const labelPosition = hitElement.getLabelPosition() ?? point;
|
|
3917
|
+
this.startInlineLabelEdit("edge", hitElement.id, hitElement.label?.text ?? "", labelPosition);
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
getEdgeLabelAtPoint(point) {
|
|
3921
|
+
const zoom = Math.max(this.renderer.zoom, 1e-4);
|
|
3922
|
+
const maxDistance = 28 / zoom;
|
|
3923
|
+
const maxDistanceSq = maxDistance * maxDistance;
|
|
3924
|
+
let closestEdge = null;
|
|
3925
|
+
let closestDistanceSq = Infinity;
|
|
3926
|
+
for (const edge of this.renderer.edges.values()) {
|
|
3927
|
+
if (!edge.visible || !edge.label) {
|
|
3928
|
+
continue;
|
|
3929
|
+
}
|
|
3930
|
+
const labelPosition = edge.getLabelPosition();
|
|
3931
|
+
if (!labelPosition) {
|
|
3932
|
+
continue;
|
|
3933
|
+
}
|
|
3934
|
+
const dx = point.x - labelPosition.x;
|
|
3935
|
+
const dy = point.y - labelPosition.y;
|
|
3936
|
+
const distanceSq = dx * dx + dy * dy;
|
|
3937
|
+
if (distanceSq > maxDistanceSq || distanceSq >= closestDistanceSq) {
|
|
3938
|
+
continue;
|
|
3939
|
+
}
|
|
3940
|
+
closestDistanceSq = distanceSq;
|
|
3941
|
+
closestEdge = edge;
|
|
3942
|
+
}
|
|
3943
|
+
return closestEdge;
|
|
3944
|
+
}
|
|
3864
3945
|
handleWheel(event) {
|
|
3865
3946
|
this.navigationManager.handleWheel(event);
|
|
3866
3947
|
}
|
|
@@ -3899,6 +3980,109 @@ class InteractionManager {
|
|
|
3899
3980
|
handleKeyUp(event) {
|
|
3900
3981
|
this.navigationManager.handleKeyUp(event);
|
|
3901
3982
|
}
|
|
3983
|
+
startInlineLabelEdit(kind, id, text, worldPosition) {
|
|
3984
|
+
this.finishInlineLabelEdit(true);
|
|
3985
|
+
const screenPoint = this.renderer.worldToScreen(worldPosition.x, worldPosition.y);
|
|
3986
|
+
const textarea = document.createElement("textarea");
|
|
3987
|
+
textarea.value = text;
|
|
3988
|
+
textarea.rows = 1;
|
|
3989
|
+
textarea.spellcheck = false;
|
|
3990
|
+
textarea.setAttribute("aria-label", "Edit label");
|
|
3991
|
+
textarea.style.position = "fixed";
|
|
3992
|
+
textarea.style.left = `${screenPoint.x}px`;
|
|
3993
|
+
textarea.style.top = `${screenPoint.y}px`;
|
|
3994
|
+
textarea.style.transform = "translate(-50%, -50%)";
|
|
3995
|
+
textarea.style.zIndex = "10001";
|
|
3996
|
+
textarea.style.minWidth = "120px";
|
|
3997
|
+
textarea.style.maxWidth = "420px";
|
|
3998
|
+
textarea.style.minHeight = "30px";
|
|
3999
|
+
textarea.style.padding = "6px 8px";
|
|
4000
|
+
textarea.style.border = "1px solid #6366f1";
|
|
4001
|
+
textarea.style.borderRadius = "6px";
|
|
4002
|
+
textarea.style.boxShadow = "0 8px 18px rgba(15, 23, 42, 0.15)";
|
|
4003
|
+
textarea.style.background = "#ffffff";
|
|
4004
|
+
textarea.style.color = "#0f172a";
|
|
4005
|
+
textarea.style.font = "14px sans-serif";
|
|
4006
|
+
textarea.style.lineHeight = "1.4";
|
|
4007
|
+
textarea.style.resize = "none";
|
|
4008
|
+
textarea.style.overflow = "hidden";
|
|
4009
|
+
const resizeTextarea = () => {
|
|
4010
|
+
textarea.style.height = "auto";
|
|
4011
|
+
textarea.style.height = `${Math.max(30, textarea.scrollHeight)}px`;
|
|
4012
|
+
};
|
|
4013
|
+
resizeTextarea();
|
|
4014
|
+
const commitAndClose = () => this.finishInlineLabelEdit(true);
|
|
4015
|
+
const cancelAndClose = () => this.finishInlineLabelEdit(false);
|
|
4016
|
+
const onKeyDown = (e) => {
|
|
4017
|
+
if (e.key === "Escape") {
|
|
4018
|
+
e.preventDefault();
|
|
4019
|
+
cancelAndClose();
|
|
4020
|
+
return;
|
|
4021
|
+
}
|
|
4022
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
4023
|
+
e.preventDefault();
|
|
4024
|
+
commitAndClose();
|
|
4025
|
+
}
|
|
4026
|
+
};
|
|
4027
|
+
const onBlur = () => commitAndClose();
|
|
4028
|
+
const onInput = () => resizeTextarea();
|
|
4029
|
+
textarea.addEventListener("keydown", onKeyDown);
|
|
4030
|
+
textarea.addEventListener("blur", onBlur);
|
|
4031
|
+
textarea.addEventListener("input", onInput);
|
|
4032
|
+
document.body.appendChild(textarea);
|
|
4033
|
+
textarea.focus();
|
|
4034
|
+
textarea.select();
|
|
4035
|
+
this.activeLabelEditor = {
|
|
4036
|
+
kind,
|
|
4037
|
+
id,
|
|
4038
|
+
textarea,
|
|
4039
|
+
cleanup: () => {
|
|
4040
|
+
textarea.removeEventListener("keydown", onKeyDown);
|
|
4041
|
+
textarea.removeEventListener("blur", onBlur);
|
|
4042
|
+
textarea.removeEventListener("input", onInput);
|
|
4043
|
+
textarea.remove();
|
|
4044
|
+
}
|
|
4045
|
+
};
|
|
4046
|
+
}
|
|
4047
|
+
finishInlineLabelEdit(commit) {
|
|
4048
|
+
if (!this.activeLabelEditor) {
|
|
4049
|
+
return;
|
|
4050
|
+
}
|
|
4051
|
+
const { kind, id, textarea, cleanup } = this.activeLabelEditor;
|
|
4052
|
+
this.activeLabelEditor = null;
|
|
4053
|
+
const nextValue = textarea.value;
|
|
4054
|
+
cleanup();
|
|
4055
|
+
if (!commit) {
|
|
4056
|
+
return;
|
|
4057
|
+
}
|
|
4058
|
+
if (kind === "node") {
|
|
4059
|
+
this.changeNodeProperties(id, (node) => {
|
|
4060
|
+
const value = nextValue.trim();
|
|
4061
|
+
if (value.length === 0) {
|
|
4062
|
+
node.label = void 0;
|
|
4063
|
+
return;
|
|
4064
|
+
}
|
|
4065
|
+
if (!node.label) {
|
|
4066
|
+
node.label = value;
|
|
4067
|
+
return;
|
|
4068
|
+
}
|
|
4069
|
+
node.label.text = value;
|
|
4070
|
+
});
|
|
4071
|
+
return;
|
|
4072
|
+
}
|
|
4073
|
+
this.changeEdgeProperties(id, (edge) => {
|
|
4074
|
+
const value = nextValue.trim();
|
|
4075
|
+
if (value.length === 0) {
|
|
4076
|
+
edge.label = void 0;
|
|
4077
|
+
return;
|
|
4078
|
+
}
|
|
4079
|
+
if (!edge.label) {
|
|
4080
|
+
edge.label = value;
|
|
4081
|
+
return;
|
|
4082
|
+
}
|
|
4083
|
+
edge.label.text = value;
|
|
4084
|
+
});
|
|
4085
|
+
}
|
|
3902
4086
|
queuePropertyChange(kind, id, before, after) {
|
|
3903
4087
|
const key = `${kind}:${id}`;
|
|
3904
4088
|
const existing = this.pendingPropertyChanges.get(key);
|
|
@@ -6163,6 +6347,25 @@ class Node extends Element {
|
|
|
6163
6347
|
this._label.render(ctx, bounds);
|
|
6164
6348
|
ctx.globalAlpha = 1;
|
|
6165
6349
|
}
|
|
6350
|
+
/**
|
|
6351
|
+
* Get world position of label center.
|
|
6352
|
+
*/
|
|
6353
|
+
getLabelPosition() {
|
|
6354
|
+
const bounds = this.getBounds();
|
|
6355
|
+
const placement = this._labelPlacement === "auto" ? "center" : this._labelPlacement;
|
|
6356
|
+
switch (placement) {
|
|
6357
|
+
case "top":
|
|
6358
|
+
return { x: bounds.x + bounds.width / 2, y: bounds.y };
|
|
6359
|
+
case "bottom":
|
|
6360
|
+
return { x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height };
|
|
6361
|
+
case "left":
|
|
6362
|
+
return { x: bounds.x, y: bounds.y + bounds.height / 2 };
|
|
6363
|
+
case "right":
|
|
6364
|
+
return { x: bounds.x + bounds.width, y: bounds.y + bounds.height / 2 };
|
|
6365
|
+
default:
|
|
6366
|
+
return { x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height / 2 };
|
|
6367
|
+
}
|
|
6368
|
+
}
|
|
6166
6369
|
/**
|
|
6167
6370
|
* Render icon, label, and ports
|
|
6168
6371
|
*/
|
|
@@ -6170,14 +6373,22 @@ class Node extends Element {
|
|
|
6170
6373
|
ctx.setLineDash([]);
|
|
6171
6374
|
ctx.lineDashOffset = 0;
|
|
6172
6375
|
let bounds = this.getBounds();
|
|
6173
|
-
|
|
6174
|
-
|
|
6376
|
+
let iconBoxSize = this._icon ? this.getIconBoxSize() : void 0;
|
|
6377
|
+
if (this._label) {
|
|
6378
|
+
this._label.setAutoMaxWidth(this.getAutoLabelBounds(bounds, iconBoxSize).width);
|
|
6379
|
+
}
|
|
6380
|
+
let labelSize = this._label ? this._label.measure(ctx) : void 0;
|
|
6175
6381
|
if (labelSize || iconBoxSize) {
|
|
6176
6382
|
this.ensureContentsFit(labelSize, iconBoxSize);
|
|
6177
6383
|
bounds = this.getBounds();
|
|
6384
|
+
iconBoxSize = this._icon ? this.getIconBoxSize() : void 0;
|
|
6385
|
+
if (this._label) {
|
|
6386
|
+
this._label.setAutoMaxWidth(this.getAutoLabelBounds(bounds, iconBoxSize).width);
|
|
6387
|
+
labelSize = this._label.measure(ctx);
|
|
6388
|
+
}
|
|
6178
6389
|
}
|
|
6179
6390
|
let iconBounds = bounds;
|
|
6180
|
-
let labelBounds = bounds;
|
|
6391
|
+
let labelBounds = this.getLabelContainerBounds(bounds);
|
|
6181
6392
|
if (this._icon && iconBoxSize) {
|
|
6182
6393
|
iconBounds = this.getIconBounds(bounds, iconBoxSize, this._icon.placement);
|
|
6183
6394
|
}
|
|
@@ -6186,8 +6397,9 @@ class Node extends Element {
|
|
|
6186
6397
|
const placement = this._icon.placement;
|
|
6187
6398
|
if (isCornerPlacement(placement)) {
|
|
6188
6399
|
iconBounds = this.getIconBounds(bounds, iconBoxSize, placement);
|
|
6189
|
-
labelBounds = bounds;
|
|
6400
|
+
labelBounds = this.getLabelContainerBounds(bounds);
|
|
6190
6401
|
} else {
|
|
6402
|
+
const contentBounds = this.getLabelContainerBounds(bounds);
|
|
6191
6403
|
switch (placement) {
|
|
6192
6404
|
case "top":
|
|
6193
6405
|
iconBounds = {
|
|
@@ -6197,10 +6409,10 @@ class Node extends Element {
|
|
|
6197
6409
|
height: iconBoxSize.height
|
|
6198
6410
|
};
|
|
6199
6411
|
labelBounds = {
|
|
6200
|
-
x:
|
|
6201
|
-
y:
|
|
6202
|
-
width:
|
|
6203
|
-
height: Math.max(0,
|
|
6412
|
+
x: contentBounds.x,
|
|
6413
|
+
y: contentBounds.y + iconBoxSize.height + gap,
|
|
6414
|
+
width: contentBounds.width,
|
|
6415
|
+
height: Math.max(0, contentBounds.height - iconBoxSize.height - gap)
|
|
6204
6416
|
};
|
|
6205
6417
|
break;
|
|
6206
6418
|
case "bottom":
|
|
@@ -6211,10 +6423,10 @@ class Node extends Element {
|
|
|
6211
6423
|
height: iconBoxSize.height
|
|
6212
6424
|
};
|
|
6213
6425
|
labelBounds = {
|
|
6214
|
-
x:
|
|
6215
|
-
y:
|
|
6216
|
-
width:
|
|
6217
|
-
height: Math.max(0,
|
|
6426
|
+
x: contentBounds.x,
|
|
6427
|
+
y: contentBounds.y,
|
|
6428
|
+
width: contentBounds.width,
|
|
6429
|
+
height: Math.max(0, contentBounds.height - iconBoxSize.height - gap)
|
|
6218
6430
|
};
|
|
6219
6431
|
break;
|
|
6220
6432
|
case "left":
|
|
@@ -6225,10 +6437,10 @@ class Node extends Element {
|
|
|
6225
6437
|
height: bounds.height
|
|
6226
6438
|
};
|
|
6227
6439
|
labelBounds = {
|
|
6228
|
-
x:
|
|
6229
|
-
y:
|
|
6230
|
-
width: Math.max(0,
|
|
6231
|
-
height:
|
|
6440
|
+
x: contentBounds.x + iconBoxSize.width + gap,
|
|
6441
|
+
y: contentBounds.y,
|
|
6442
|
+
width: Math.max(0, contentBounds.width - iconBoxSize.width - gap),
|
|
6443
|
+
height: contentBounds.height
|
|
6232
6444
|
};
|
|
6233
6445
|
break;
|
|
6234
6446
|
case "right":
|
|
@@ -6239,16 +6451,16 @@ class Node extends Element {
|
|
|
6239
6451
|
height: bounds.height
|
|
6240
6452
|
};
|
|
6241
6453
|
labelBounds = {
|
|
6242
|
-
x:
|
|
6243
|
-
y:
|
|
6244
|
-
width: Math.max(0,
|
|
6245
|
-
height:
|
|
6454
|
+
x: contentBounds.x,
|
|
6455
|
+
y: contentBounds.y,
|
|
6456
|
+
width: Math.max(0, contentBounds.width - iconBoxSize.width - gap),
|
|
6457
|
+
height: contentBounds.height
|
|
6246
6458
|
};
|
|
6247
6459
|
break;
|
|
6248
6460
|
}
|
|
6249
6461
|
}
|
|
6250
6462
|
} else if (this._label && labelSize) {
|
|
6251
|
-
labelBounds = this.getLabelBounds(bounds, labelSize, this._labelPlacement);
|
|
6463
|
+
labelBounds = this.getLabelBounds(this.getAutoLabelBounds(bounds, iconBoxSize), labelSize, this._labelPlacement);
|
|
6252
6464
|
}
|
|
6253
6465
|
if (this._icon) {
|
|
6254
6466
|
this._icon.render(ctx, iconBounds);
|
|
@@ -6260,9 +6472,10 @@ class Node extends Element {
|
|
|
6260
6472
|
* Minimal size required to fit current contents
|
|
6261
6473
|
*/
|
|
6262
6474
|
getContentMinSize(ctx) {
|
|
6475
|
+
const bounds = this.getBounds();
|
|
6263
6476
|
const labelSize = this._label ? this._label.measure(ctx) : void 0;
|
|
6264
6477
|
const iconBoxSize = this._icon ? this.getIconBoxSize() : void 0;
|
|
6265
|
-
return this.calculateContentMinSize(labelSize, iconBoxSize);
|
|
6478
|
+
return this.calculateContentMinSize(labelSize, iconBoxSize, bounds);
|
|
6266
6479
|
}
|
|
6267
6480
|
/**
|
|
6268
6481
|
* Render resize handles when selected
|
|
@@ -6429,7 +6642,7 @@ class Node extends Element {
|
|
|
6429
6642
|
}
|
|
6430
6643
|
ensureContentsFit(labelSize, iconBoxSize) {
|
|
6431
6644
|
const bounds = this.getBounds();
|
|
6432
|
-
const minSize = this.calculateContentMinSize(labelSize, iconBoxSize);
|
|
6645
|
+
const minSize = this.calculateContentMinSize(labelSize, iconBoxSize, bounds);
|
|
6433
6646
|
if (minSize.width > bounds.width) {
|
|
6434
6647
|
this.width = minSize.width;
|
|
6435
6648
|
}
|
|
@@ -6437,12 +6650,15 @@ class Node extends Element {
|
|
|
6437
6650
|
this.height = minSize.height;
|
|
6438
6651
|
}
|
|
6439
6652
|
}
|
|
6440
|
-
calculateContentMinSize(labelSize, iconBoxSize) {
|
|
6653
|
+
calculateContentMinSize(labelSize, iconBoxSize, bounds) {
|
|
6441
6654
|
let minWidth = 0;
|
|
6442
6655
|
let minHeight = 0;
|
|
6656
|
+
const labelContainer = this.getLabelContainerBounds(bounds);
|
|
6657
|
+
const widthFactor = labelContainer.width > 0 ? bounds.width / labelContainer.width : 1;
|
|
6658
|
+
const heightFactor = labelContainer.height > 0 ? bounds.height / labelContainer.height : 1;
|
|
6443
6659
|
if (labelSize) {
|
|
6444
|
-
minWidth = Math.max(minWidth, labelSize.width);
|
|
6445
|
-
minHeight = Math.max(minHeight, labelSize.height);
|
|
6660
|
+
minWidth = Math.max(minWidth, labelSize.width * widthFactor);
|
|
6661
|
+
minHeight = Math.max(minHeight, labelSize.height * heightFactor);
|
|
6446
6662
|
}
|
|
6447
6663
|
if (iconBoxSize) {
|
|
6448
6664
|
minWidth = Math.max(minWidth, iconBoxSize.width);
|
|
@@ -6468,16 +6684,70 @@ class Node extends Element {
|
|
|
6468
6684
|
const labelVertical = labelPlacement === "top" || labelPlacement === "bottom";
|
|
6469
6685
|
const iconHorizontal = iconPlacement === "left" || iconPlacement === "right";
|
|
6470
6686
|
const iconVertical = iconPlacement === "top" || iconPlacement === "bottom";
|
|
6687
|
+
const labelSharesIconAxis = iconHorizontal && (labelPlacement === "center" || labelPlacement === iconPlacement) || iconVertical && (labelPlacement === "center" || labelPlacement === iconPlacement);
|
|
6471
6688
|
if (labelHorizontal && iconHorizontal && labelPlacement !== iconPlacement) {
|
|
6472
6689
|
minWidth = Math.max(minWidth, iconBoxSize.width + gap + labelSize.width);
|
|
6473
6690
|
}
|
|
6474
6691
|
if (labelVertical && iconVertical && labelPlacement !== iconPlacement) {
|
|
6475
6692
|
minHeight = Math.max(minHeight, iconBoxSize.height + gap + labelSize.height);
|
|
6476
6693
|
}
|
|
6694
|
+
if (labelSharesIconAxis) {
|
|
6695
|
+
if (iconHorizontal) {
|
|
6696
|
+
minWidth = Math.max(minWidth, iconBoxSize.width + gap + labelSize.width);
|
|
6697
|
+
}
|
|
6698
|
+
if (iconVertical) {
|
|
6699
|
+
minHeight = Math.max(minHeight, iconBoxSize.height + gap + labelSize.height);
|
|
6700
|
+
}
|
|
6701
|
+
}
|
|
6477
6702
|
}
|
|
6478
6703
|
}
|
|
6479
6704
|
return { width: minWidth, height: minHeight };
|
|
6480
6705
|
}
|
|
6706
|
+
getLabelContainerBounds(bounds) {
|
|
6707
|
+
return bounds;
|
|
6708
|
+
}
|
|
6709
|
+
getAutoLabelBounds(bounds, iconBoxSize) {
|
|
6710
|
+
const contentBounds = this.getLabelContainerBounds(bounds);
|
|
6711
|
+
if (!this._icon || !iconBoxSize || this._icon.placement === "center") {
|
|
6712
|
+
return contentBounds;
|
|
6713
|
+
}
|
|
6714
|
+
const gap = this._icon.gap;
|
|
6715
|
+
if (isCornerPlacement(this._icon.placement)) {
|
|
6716
|
+
return contentBounds;
|
|
6717
|
+
}
|
|
6718
|
+
switch (this._icon.placement) {
|
|
6719
|
+
case "left":
|
|
6720
|
+
return {
|
|
6721
|
+
x: contentBounds.x + iconBoxSize.width + gap,
|
|
6722
|
+
y: contentBounds.y,
|
|
6723
|
+
width: Math.max(0, contentBounds.width - iconBoxSize.width - gap),
|
|
6724
|
+
height: contentBounds.height
|
|
6725
|
+
};
|
|
6726
|
+
case "right":
|
|
6727
|
+
return {
|
|
6728
|
+
x: contentBounds.x,
|
|
6729
|
+
y: contentBounds.y,
|
|
6730
|
+
width: Math.max(0, contentBounds.width - iconBoxSize.width - gap),
|
|
6731
|
+
height: contentBounds.height
|
|
6732
|
+
};
|
|
6733
|
+
case "top":
|
|
6734
|
+
return {
|
|
6735
|
+
x: contentBounds.x,
|
|
6736
|
+
y: contentBounds.y + iconBoxSize.height + gap,
|
|
6737
|
+
width: contentBounds.width,
|
|
6738
|
+
height: Math.max(0, contentBounds.height - iconBoxSize.height - gap)
|
|
6739
|
+
};
|
|
6740
|
+
case "bottom":
|
|
6741
|
+
return {
|
|
6742
|
+
x: contentBounds.x,
|
|
6743
|
+
y: contentBounds.y,
|
|
6744
|
+
width: contentBounds.width,
|
|
6745
|
+
height: Math.max(0, contentBounds.height - iconBoxSize.height - gap)
|
|
6746
|
+
};
|
|
6747
|
+
default:
|
|
6748
|
+
return contentBounds;
|
|
6749
|
+
}
|
|
6750
|
+
}
|
|
6481
6751
|
getLabelBounds(bounds, labelSize, placement) {
|
|
6482
6752
|
const normalizedPlacement = placement === "auto" ? "center" : placement;
|
|
6483
6753
|
const width = Math.min(labelSize.width, bounds.width);
|
|
@@ -6901,6 +7171,16 @@ class CircleNode extends Node {
|
|
|
6901
7171
|
y: center.y + dy * scale
|
|
6902
7172
|
};
|
|
6903
7173
|
}
|
|
7174
|
+
getLabelContainerBounds(bounds) {
|
|
7175
|
+
const width = bounds.width / Math.SQRT2;
|
|
7176
|
+
const height = bounds.height / Math.SQRT2;
|
|
7177
|
+
return {
|
|
7178
|
+
x: bounds.x + (bounds.width - width) / 2,
|
|
7179
|
+
y: bounds.y + (bounds.height - height) / 2,
|
|
7180
|
+
width,
|
|
7181
|
+
height
|
|
7182
|
+
};
|
|
7183
|
+
}
|
|
6904
7184
|
render(ctx) {
|
|
6905
7185
|
const center = this.getCenter();
|
|
6906
7186
|
const rx = this._width / 2;
|
|
@@ -6952,6 +7232,16 @@ class DiamondNode extends Node {
|
|
|
6952
7232
|
y: center.y + dy * t
|
|
6953
7233
|
};
|
|
6954
7234
|
}
|
|
7235
|
+
getLabelContainerBounds(bounds) {
|
|
7236
|
+
const width = bounds.width / 2;
|
|
7237
|
+
const height = bounds.height / 2;
|
|
7238
|
+
return {
|
|
7239
|
+
x: bounds.x + (bounds.width - width) / 2,
|
|
7240
|
+
y: bounds.y + (bounds.height - height) / 2,
|
|
7241
|
+
width,
|
|
7242
|
+
height
|
|
7243
|
+
};
|
|
7244
|
+
}
|
|
6955
7245
|
render(ctx) {
|
|
6956
7246
|
const center = this.getCenter();
|
|
6957
7247
|
const hw = this._width / 2;
|