-
Notifications
You must be signed in to change notification settings - Fork 0
/
panels.js
111 lines (96 loc) · 3.4 KB
/
panels.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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// toggleable tabbed panels
(function(){
// get nearest parent element matching selector
var closestParent = Element.prototype.closest ? function(el, selector) {
return el.closest(selector);
} : (function() {
var el = HTMLElement.prototype;
var matches = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
return function(el, selector) {
while (el && el.nodeType === 1) {
if (matches.call(el, selector)) {
return el;
}
el = el.parentNode;
}
return null;
};
})();
// handle events whose target matches a selector
// works even on elements created after the event listener was added:
//
// delegateEvent('.todo .remove', 'click', function(removeBtn) {
// removeTodo(removeBtn.parentNode);
// });
function delegateEvent(selector, eventType, handler, elementScope) {
(elementScope || document).addEventListener(eventType, function(event) {
var listeningTarget = closestParent(event.target, selector);
if (listeningTarget) {
handler.call(listeningTarget, event);
}
});
}
// Get descendants of scopeEl with className that have no
// .tabbed-panels ancestor which is also a descendant of scopeEl
// This is needed to support nesting of tabbed panels.
function getElements(className, scopeEl) {
return [].filter.call(scopeEl.getElementsByClassName(className), function(child) {
var el = child;
while ((el = el.parentNode)) {
if (el === scopeEl) break;
if (el.classList.contains('tabbed-panels')) return false;
}
return true;
});
}
function isAInB(a, b) {
return a === b || b.contains(a);
}
delegateEvent('.tab', 'click', function() {
var clickedTab = this;
var parent = closestParent(clickedTab, '.tabbed-panels');
if (!parent) return;
var tabs = getElements('tab', parent);
var panels = getElements('panel', parent);
var closeable = parent.classList.contains('closeable');
var clickedTabI = tabs.indexOf(clickedTab);
// non-clicked tabs: deactivate
tabs.forEach(function(tab) {
if (tab != clickedTab) tab.classList.remove('active');
});
// clicked tab: if closeable, toggle its active state, otherwise activate
var isActive = clickedTab.classList[closeable ? 'toggle' : 'add']('active');
// parent: if not closeable or if a tab is active, activate parent, otherwise deactivate
parent.classList[(!closeable || isActive) ? 'add' : 'remove']('active');
// panels: activate clicked tab's panel, deactivate other panels
panels.forEach(function(panel, panelI) {
if (panelI === clickedTabI) {
panel.classList[closeable ? 'toggle' : 'add']('active');
} else {
panel.classList.remove('active');
}
});
});
window.addEventListener('click', function(event) {
var parents = [].slice.call(document.querySelectorAll('.tabbed-panels.hovering.closeable.active'));
parents.forEach(function(parent) {
var tabs = getElements('tab', parent);
var panels = getElements('panel', parent);
// exit if click was in a tab or panel
if (tabs.some(function(tab) {
return isAInB(event.target, tab);
}) || panels.some(function(panel) {
return isAInB(event.target, panel);
})) return;
// otherwise unactivate everything
for (var i = 0; i < tabs.length; i++) {
if (tabs[i].classList.contains('active')) {
parent.classList.remove('active');
tabs[i].classList.remove('active');
panels[i].classList.remove('active');
return;
}
}
});
});
})();