diff --git a/packages/joint-core/src/dia/ToolsView.mjs b/packages/joint-core/src/dia/ToolsView.mjs index 32036c72f..180f0889e 100644 --- a/packages/joint-core/src/dia/ToolsView.mjs +++ b/packages/joint-core/src/dia/ToolsView.mjs @@ -44,27 +44,37 @@ export const ToolsView = mvc.View.extend({ update: function(opt) { opt || (opt = {}); - var tools = this.tools; + const tools = this.tools; if (!tools) return this; - var isRendered = this.isRendered; - for (var i = 0, n = tools.length; i < n; i++) { - var tool = tools[i]; + const n = tools.length; + const wasRendered = this.isRendered; + for (let i = 0; i < n; i++) { + const tool = tools[i]; tool.updateVisibility(); if (!tool.isVisible()) continue; - if (!isRendered) { + if (!this.isRendered) { + // There is at least one visible tool + this.isRendered = Array(n).fill(false); + } + if (!this.isRendered[i]) { // First update executes render() tool.render(); + this.isRendered[i] = true; } else if (opt.tool !== tool.cid) { tool.update(); } } + if (!this.isRendered && n > 0) { + // None of the tools is visible + // Note: ToolsView with no tools are always mounted + return this; + } if (!this.isMounted()) { this.mount(); } - if (!isRendered) { + if (!wasRendered) { // Make sure tools are visible (if they were hidden and the tool removed) this.blurTool(); - this.isRendered = true; } return this; }, diff --git a/packages/joint-core/src/linkTools/RotateLabel.mjs b/packages/joint-core/src/linkTools/RotateLabel.mjs index c528f3b1d..533bf3798 100644 --- a/packages/joint-core/src/linkTools/RotateLabel.mjs +++ b/packages/joint-core/src/linkTools/RotateLabel.mjs @@ -49,13 +49,7 @@ export const RotateLabel = Control.extend({ getPosition(view) { const { offset = 0 } = this.options; const { x = 0, y = 0 } = typeof offset === 'number' ? { x: 0, y: offset } : offset; - const model = view.model; - const index = this.options.labelIndex; - const label = model.label(index); - if (!label) { - throw new Error(`No label with index ${index} found.`); - } - + const label = this.getLabel(); const labelPosition = this.getLabelPosition(label); const coords = view.getLabelCoordinates(labelPosition); let { angle = 0, args = {}} = labelPosition; @@ -76,6 +70,12 @@ export const RotateLabel = Control.extend({ return new g.Point(matrix.e, matrix.f); }, + // Override the default `computeVisibility` method to hide the tool if the label is not present. + computeVisibility() { + const visibility = Control.prototype.computeVisibility.apply(this, arguments); + return visibility && !!this.getLabel(); + }, + setPosition(view, coordinates) { const model = view.model; const index = this.options.labelIndex; @@ -93,10 +93,15 @@ export const RotateLabel = Control.extend({ model.prop(['labels', index, 'position', 'angle'], 0); }, + getLabel() { + const { relatedView, options } = this; + return relatedView.model.label(options.labelIndex); + }, + getLabelPosition(label) { - return typeof label.position === 'number' - ? { distance: label.position } - : label.position; + const view = this.relatedView; + const labelPosition = view._normalizeLabelPosition(label.position); + return view._mergeLabelPositionProperty(labelPosition, view._getDefaultLabelPositionProperty()); }, }); diff --git a/packages/joint-core/test/jointjs/dia/linkTools.js b/packages/joint-core/test/jointjs/dia/linkTools.js index 5301baba9..495469cc1 100644 --- a/packages/joint-core/test/jointjs/dia/linkTools.js +++ b/packages/joint-core/test/jointjs/dia/linkTools.js @@ -61,6 +61,23 @@ QUnit.module('linkTools', function(hooks) { assert.ok(toolsView.el.parentNode); assert.ok(toolsView.isMounted()); }); + + QUnit.test('are not mounted when none of the tools is visible', function(assert) { + let isVisible = false; + const button1 = new joint.linkTools.Button({ visibility: () => isVisible }); + const button2 = new joint.linkTools.Button({ visibility: () => false }); + const toolsView = new joint.dia.ToolsView({ tools: [button1, button2] }); + linkView.addTools(toolsView); + assert.notOk(toolsView.el.parentNode); + assert.notOk(toolsView.isMounted()); + toolsView.update(); + assert.notOk(toolsView.el.parentNode); + assert.notOk(toolsView.isMounted()); + isVisible = true; + toolsView.update(); + assert.ok(toolsView.el.parentNode); + assert.ok(toolsView.isMounted()); + }); }); QUnit.module('Visibility', function() { @@ -181,6 +198,27 @@ QUnit.module('linkTools', function(hooks) { }); }); + QUnit.module('RotateLabel', function() { + + QUnit.test('postponed rendering', function(assert) { + const button = new joint.linkTools.RotateLabel({ labelIndex: 0 }); + const toolsView = new joint.dia.ToolsView({ + tools: [button] + }); + linkView.addTools(toolsView); + // The button should not be visible because there is no label yet. + assert.notOk(link.label(0)); + assert.equal(button.el.style.display, 'none'); + assert.notOk(toolsView.isRendered); + // The button should be rendered after the label is added. + link.appendLabel({}); + assert.ok(link.label(0)); + assert.notEqual(button.el.style.display, 'none'); + assert.ok(toolsView.isRendered); + }); + + }); + QUnit.module('TargetAnchor', function() { [{ resetAnchor: true,