1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
/**
* @file
* A Backbone View that provides the visual view of a contextual link.
*/
(function (Drupal, Backbone) {
/**
* @deprecated in drupal:9.4.0 and is removed from drupal:12.0.0. There is no
* replacement.
*/
Drupal.contextual.VisualView = Backbone.View.extend(
/** @lends Drupal.contextual.VisualView# */ {
/**
* Events for the Backbone view.
*
* @return {object}
* A mapping of events to be used in the view.
*/
events() {
// Prevents delay and simulated mouse events.
const touchEndToClick = function (event) {
event.preventDefault();
event.target.click();
};
// Used for tracking the presence of touch events. When true, the
// mousemove and mouseenter event handlers are effectively disabled.
// This is used instead of preventDefault() on touchstart as some
// touchstart events are not cancelable.
let touchStart = false;
return {
touchstart() {
// Set to true so the mouseenter events that follows knows to not
// execute any hover related logic.
touchStart = true;
},
mouseenter() {
// We only want mouse hover events on non-touch.
if (!touchStart) {
this.model.focus();
}
},
mousemove() {
// Because there are scenarios where there are both touchscreens
// and pointer devices, the touchStart flag should be set back to
// false after mouseenter and mouseleave complete. It will be set to
// true if another touchstart event occurs.
touchStart = false;
},
'click .trigger': function () {
this.model.toggleOpen();
},
'touchend .trigger': touchEndToClick,
'click .contextual-links a': function () {
this.model.close().blur();
},
'touchend .contextual-links a': touchEndToClick,
};
},
/**
* Renders the visual view of a contextual link. Listens to mouse & touch.
*
* @constructs
*
* @augments Backbone.View
*/
initialize() {
this.listenTo(this.model, 'change', this.render);
},
/**
* {@inheritdoc}
*
* @return {Drupal.contextual.VisualView}
* The current contextual visual view.
*/
render() {
const isOpen = this.model.get('isOpen');
// The trigger should be visible when:
// - the mouse hovered over the region,
// - the trigger is locked,
// - and for as long as the contextual menu is open.
const isVisible =
this.model.get('isLocked') ||
this.model.get('regionIsHovered') ||
isOpen;
this.$el
// The open state determines if the links are visible.
.toggleClass('open', isOpen)
// Update the visibility of the trigger.
.find('.trigger')
.toggleClass('visually-hidden', !isVisible);
// Nested contextual region handling: hide any nested contextual triggers.
if ('isOpen' in this.model.changed) {
this.$el
.closest('.contextual-region')
.find('.contextual .trigger:not(:first)')
.toggle(!isOpen);
}
return this;
},
},
);
})(Drupal, Backbone);
|