-
Notifications
You must be signed in to change notification settings - Fork 8
/
index.js
85 lines (71 loc) · 2.66 KB
/
index.js
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
import Vue from 'vue';
export var version = '2.2.2';
var compatible = (/^2\./).test(Vue.version);
if (!compatible) {
Vue.util.warn('VueClickaway ' + version + ' only supports Vue 2.x, and does not support Vue ' + Vue.version);
}
// @SECTION: implementation
var HANDLER = '_vue_clickaway_handler';
function bind(el, binding, vnode) {
unbind(el, binding);
var vm = vnode.context;
var callback = binding.value;
if (typeof callback !== 'function') {
if (process.env.NODE_ENV !== 'production') {
Vue.util.warn(
'v-' + binding.name + '="' +
binding.expression + '" expects a function value, ' +
'got ' + callback
);
}
return;
}
// @NOTE: Vue binds directives in microtasks, while UI events are dispatched
// in macrotasks. This causes the listener to be set up before
// the "origin" click event (the event that lead to the binding of
// the directive) arrives at the document root. To work around that,
// we ignore events until the end of the "initial" macrotask.
// @REFERENCE: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
// @REFERENCE: https://github.com/simplesmiler/vue-clickaway/issues/8
var initialMacrotaskEnded = false;
setTimeout(function() {
initialMacrotaskEnded = true;
}, 0);
el[HANDLER] = function(ev) {
// @NOTE: this test used to be just `el.contains`, but working with path is better,
// because it tests whether the element was there at the time of
// the click, not whether it is there now, that the event has arrived
// to the top.
// @NOTE: `.path` is non-standard, the standard way is `.composedPath()`
var path = ev.path || (ev.composedPath ? ev.composedPath() : undefined);
if (initialMacrotaskEnded && (path ? path.indexOf(el) < 0 : !el.contains(ev.target))) {
return callback.call(vm, ev);
}
};
if (binding.arg) {
document.documentElement.addEventListener(binding.arg, el[HANDLER], false);
}
else { // default state, if no argument is passed
document.documentElement.addEventListener('click', el[HANDLER], false);
}
}
function unbind(el, binding) {
if (binding.arg) {
document.documentElement.removeEventListener(binding.arg, el[HANDLER], false);
}
else { // default state, if no argument is passed
document.documentElement.removeEventListener('click', el[HANDLER], false);
}
delete el[HANDLER];
}
export var directive = {
bind: bind,
update: function(el, binding, vnode) {
if (binding.value === binding.oldValue) return;
bind(el, binding, vnode);
},
unbind: unbind,
};
export var mixin = {
directives: { onClickaway: directive },
};