Skip to content

Commit 3706574

Browse files
authored
feat(dia.ToolView): add visibility option callback (#2745)
1 parent dd0db32 commit 3706574

File tree

5 files changed

+197
-31
lines changed

5 files changed

+197
-31
lines changed

packages/joint-core/src/dia/ToolView.mjs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const ToolView = mvc.View.extend({
66
className: 'tool',
77
svgElement: true,
88
_visible: true,
9+
_visibleExplicit: true,
910

1011
init: function() {
1112
var name = this.name;
@@ -30,16 +31,40 @@ export const ToolView = mvc.View.extend({
3031
return this.name;
3132
},
3233

34+
// Evaluate the visibility of the tool and update the `display` CSS property
35+
updateVisibility: function() {
36+
const isVisible = this.computeVisibility();
37+
this.el.style.display = isVisible ? '' : 'none';
38+
this._visible = isVisible;
39+
},
40+
41+
// Evaluate the visibility of the tool. The method returns `true` if the tool
42+
// should be visible in the DOM.
43+
computeVisibility() {
44+
if (!this.isExplicitlyVisible()) return false;
45+
const { visibility } = this.options;
46+
if (typeof visibility !== 'function') return true;
47+
return !!visibility.call(this, this.relatedView, this);
48+
},
49+
3350
show: function() {
34-
this.el.style.display = '';
35-
this._visible = true;
51+
this._visibleExplicit = true;
52+
this.updateVisibility();
3653
},
3754

3855
hide: function() {
39-
this.el.style.display = 'none';
40-
this._visible = false;
56+
this._visibleExplicit = false;
57+
this.updateVisibility();
58+
},
59+
60+
// The method returns `false` if the `hide()` method was called on the tool.
61+
isExplicitlyVisible: function() {
62+
return !!this._visibleExplicit;
4163
},
4264

65+
// The method returns `false` if the tool is not visible (it has `display: none`).
66+
// This can happen if the `hide()` method was called or the tool is not visible
67+
// because of the `visibility` option was evaluated to `false`.
4368
isVisible: function() {
4469
return !!this._visible;
4570
},

packages/joint-core/src/dia/ToolsView.mjs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ export const ToolsView = mvc.View.extend({
4949
var isRendered = this.isRendered;
5050
for (var i = 0, n = tools.length; i < n; i++) {
5151
var tool = tools[i];
52+
tool.updateVisibility();
53+
if (!tool.isVisible()) continue;
5254
if (!isRendered) {
5355
// First update executes render()
5456
tool.render();
55-
} else if (opt.tool !== tool.cid && tool.isVisible()) {
57+
} else if (opt.tool !== tool.cid) {
5658
tool.update();
5759
}
5860
}
@@ -87,9 +89,12 @@ export const ToolsView = mvc.View.extend({
8789
if (!tools) return this;
8890
for (var i = 0, n = tools.length; i < n; i++) {
8991
var tool = tools[i];
90-
if (tool !== blurredTool && !tool.isVisible()) {
92+
if (tool !== blurredTool && !tool.isExplicitlyVisible()) {
9193
tool.show();
92-
tool.update();
94+
// Check if the tool is conditionally visible too
95+
if (tool.isVisible()) {
96+
tool.update();
97+
}
9398
}
9499
}
95100
return this;

packages/joint-core/test/jointjs/dia/linkTools.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,124 @@ QUnit.module('linkTools', function(hooks) {
6363
});
6464
});
6565

66+
QUnit.module('Visibility', function() {
67+
68+
QUnit.test('isVisible()', function(assert) {
69+
const remove = new joint.linkTools.Remove();
70+
assert.ok(remove.isVisible());
71+
remove.hide();
72+
assert.notOk(remove.isVisible());
73+
remove.show();
74+
assert.ok(remove.isVisible());
75+
});
76+
77+
QUnit.test('updateVisibility()', function(assert) {
78+
const remove = new joint.linkTools.Remove();
79+
const toolsView = new joint.dia.ToolsView({ tools: [remove] });
80+
linkView.addTools(toolsView);
81+
assert.notEqual(getComputedStyle(remove.el).display, 'none');
82+
remove.hide();
83+
assert.equal(getComputedStyle(remove.el).display, 'none');
84+
remove.show();
85+
assert.notEqual(getComputedStyle(remove.el).display, 'none');
86+
});
87+
88+
QUnit.module('option: visibility', function(assert) {
89+
90+
QUnit.test('is visible or hidden', function(assert) {
91+
let isVisible = true;
92+
const visibilitySpy = sinon.spy(() => isVisible);
93+
const removeButton = new joint.linkTools.Remove({
94+
visibility: visibilitySpy
95+
});
96+
const otherButton = new joint.linkTools.Button();
97+
const toolsView = new joint.dia.ToolsView({
98+
tools: [
99+
removeButton,
100+
otherButton
101+
]
102+
});
103+
linkView.addTools(toolsView);
104+
105+
// Initial state.
106+
assert.notEqual(getComputedStyle(removeButton.el).display, 'none');
107+
assert.ok(removeButton.isVisible());
108+
assert.equal(visibilitySpy.callCount, 1);
109+
assert.ok(visibilitySpy.calledWithExactly(linkView, removeButton));
110+
assert.ok(visibilitySpy.calledOn(removeButton));
111+
112+
// Visibility function should be called on update.
113+
isVisible = false;
114+
toolsView.update();
115+
assert.equal(getComputedStyle(removeButton.el).display, 'none');
116+
assert.notOk(removeButton.isVisible());
117+
assert.ok(removeButton.isExplicitlyVisible());
118+
assert.equal(visibilitySpy.callCount, 2);
119+
assert.ok(visibilitySpy.calledWithExactly(linkView, removeButton));
120+
assert.ok(visibilitySpy.calledOn(removeButton));
121+
122+
// Other button should not be affected by the visibility function.
123+
assert.notEqual(getComputedStyle(otherButton.el).display, 'none');
124+
assert.ok(otherButton.isVisible());
125+
126+
// Focus & blur on other button should not change the visibility of
127+
// the remove button.
128+
toolsView.focusTool(otherButton);
129+
assert.equal(getComputedStyle(removeButton.el).display, 'none');
130+
assert.notOk(removeButton.isVisible());
131+
assert.notOk(removeButton.isExplicitlyVisible());
132+
toolsView.blurTool(otherButton);
133+
assert.equal(getComputedStyle(removeButton.el).display, 'none');
134+
assert.notOk(removeButton.isVisible());
135+
assert.ok(removeButton.isExplicitlyVisible());
136+
137+
isVisible = true;
138+
toolsView.update();
139+
toolsView.focusTool(otherButton);
140+
assert.equal(getComputedStyle(removeButton.el).display, 'none');
141+
assert.notOk(removeButton.isVisible());
142+
assert.notOk(removeButton.isExplicitlyVisible());
143+
toolsView.blurTool(otherButton);
144+
assert.notEqual(getComputedStyle(removeButton.el).display, 'none');
145+
assert.ok(removeButton.isVisible());
146+
assert.ok(removeButton.isExplicitlyVisible());
147+
});
148+
149+
QUnit.test('it\'s not updated when hidden', function(assert) {
150+
const button1 = new joint.linkTools.Button({
151+
visibility: () => false
152+
});
153+
const button2 = new joint.linkTools.Button({
154+
visibility: () => true
155+
});
156+
const button1UpdateSpy = sinon.spy(button1, 'update');
157+
const button2UpdateSpy = sinon.spy(button2, 'update');
158+
const toolsView = new joint.dia.ToolsView({
159+
tools: [button1, button2]
160+
});
161+
linkView.addTools(toolsView);
162+
assert.equal(button1.update.callCount, 0);
163+
assert.equal(button2.update.callCount, 1);
164+
toolsView.update();
165+
assert.equal(button1.update.callCount, 0);
166+
assert.equal(button2.update.callCount, 2);
167+
button1.show();
168+
button2.hide();
169+
toolsView.update();
170+
assert.equal(button1.update.callCount, 0);
171+
assert.equal(button2.update.callCount, 2);
172+
toolsView.focusTool(null); // hide all
173+
assert.equal(button1.update.callCount, 0);
174+
assert.equal(button2.update.callCount, 2);
175+
toolsView.blurTool(null); // show all
176+
assert.equal(button1.update.callCount, 0);
177+
assert.equal(button2.update.callCount, 3);
178+
button1UpdateSpy.restore();
179+
button2UpdateSpy.restore();
180+
});
181+
});
182+
});
183+
66184
QUnit.module('TargetAnchor', function() {
67185
[{
68186
resetAnchor: true,

packages/joint-core/test/ts/toolsView.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ const toolsView = new dia.ToolsView({
2626

2727
toolsView.configure({ component: false });
2828

29+
new linkTools.Button({
30+
visibility: (view) => view.getConnectionLength() > 100,
31+
});
32+
33+
new elementTools.Button({
34+
visibility: (view) => view.model.size().width > 100,
35+
});
36+
2937
interface RadiusControlOptions extends elementTools.Control.Options {
3038
testOption?: number;
3139
}

0 commit comments

Comments
 (0)