Skip to content

Commit 1e12053

Browse files
committed
feat(tpls): add keyboard navigation
- use arrow left and right to navigate path components - use arrow up and down to navigate file components
1 parent 6032dce commit 1e12053

File tree

4 files changed

+292
-20
lines changed

4 files changed

+292
-20
lines changed

src/tpl/asset/main.css

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ body {
1818
font-variant-ligatures: none;
1919
font-kerning: none;
2020
hyphens: none;
21+
padding-bottom: 1em;
2122
}
2223

2324
form {
@@ -39,12 +40,16 @@ a {
3940
outline: 0;
4041
}
4142

43+
a:hover {
44+
background: #f5f5f5;
45+
}
46+
4247
a:focus {
43-
background: #fffaee;
48+
background: #fffae0;
4449
}
4550

46-
a:hover {
47-
background: #f5f5f5;
51+
a:hover:focus {
52+
background: #faf7ea;
4853
}
4954

5055
input, button {

src/tpl/asset/main.css.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ font-size: 0.625em;
1818
font-variant-ligatures: none;
1919
font-kerning: none;
2020
hyphens: none;
21+
padding-bottom: 1em;
2122
}
2223
form {
2324
margin: 0;
@@ -35,12 +36,15 @@ color: #000;
3536
text-decoration: none;
3637
outline: 0;
3738
}
38-
a:focus {
39-
background: #fffaee;
40-
}
4139
a:hover {
4240
background: #f5f5f5;
4341
}
42+
a:focus {
43+
background: #fffae0;
44+
}
45+
a:hover:focus {
46+
background: #faf7ea;
47+
}
4448
input, button {
4549
min-width: 0;
4650
margin: 0;

src/tpl/asset/main.js

Lines changed: 145 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,138 @@
11
(function () {
2+
var classNone = 'none';
3+
var classHeader = 'header';
4+
5+
function enableKeyboardNavigate() {
6+
if (
7+
!document.querySelector ||
8+
!document.addEventListener ||
9+
!document.body.classList ||
10+
!document.body.parentElement
11+
) {
12+
return;
13+
}
14+
15+
var pathList = document.body.querySelector('.path-list');
16+
var itemList = document.body.querySelector('.item-list');
17+
if (!pathList && !itemList) {
18+
return;
19+
}
20+
21+
function getFocusableSibling(container, isPrev) {
22+
if (!container) {
23+
return
24+
}
25+
var focusA = container.querySelector(':focus');
26+
var focusLI = focusA;
27+
while (focusLI && focusLI.tagName !== 'LI') {
28+
focusLI = focusLI.parentElement;
29+
}
30+
if (!focusLI) {
31+
if (isPrev) {
32+
focusLI = container.firstElementChild;
33+
} else {
34+
focusLI = container.lastElementChild;
35+
}
36+
}
37+
if (!focusLI) {
38+
return;
39+
}
40+
41+
var siblingLI = focusLI;
42+
do {
43+
if (isPrev) {
44+
siblingLI = siblingLI.previousElementSibling;
45+
if (!siblingLI) {
46+
siblingLI = container.lastElementChild;
47+
}
48+
} else {
49+
siblingLI = siblingLI.nextElementSibling;
50+
if (!siblingLI) {
51+
siblingLI = container.firstElementChild;
52+
}
53+
}
54+
} while (siblingLI !== focusLI && (
55+
siblingLI.classList.contains(classNone) ||
56+
siblingLI.classList.contains(classHeader)
57+
));
58+
59+
if (siblingLI) {
60+
var newFocusA = siblingLI.querySelector('a');
61+
return newFocusA;
62+
}
63+
}
64+
65+
var UP = 'Up';
66+
var DOWN = 'Down';
67+
var LEFT = 'Left';
68+
var RIGHT = 'Right';
69+
70+
var ARROW_UP = 'ArrowUp';
71+
var ARROW_DOWN = 'ArrowDown';
72+
var ARROW_LEFT = 'ArrowLeft';
73+
var ARROW_RIGHT = 'ArrowRight';
74+
75+
var ARROW_UP_CODE = 38;
76+
var ARROW_DOWN_CODE = 40;
77+
var ARROW_LEFT_CODE = 37;
78+
var ARROW_RIGHT_CODE = 39;
79+
80+
var skipTags = ['INPUT', 'BUTTON', 'TEXTAREA'];
81+
82+
document.addEventListener('keydown', function (e) {
83+
if (
84+
e.ctrlKey ||
85+
e.altKey ||
86+
e.shiftKey ||
87+
e.metaKey ||
88+
skipTags.indexOf(e.target.tagName) >= 0
89+
) {
90+
return;
91+
}
92+
93+
var newFocusEl;
94+
if (e.key) {
95+
switch (e.key) {
96+
case LEFT:
97+
case ARROW_LEFT:
98+
newFocusEl = getFocusableSibling(pathList, true);
99+
break;
100+
case RIGHT:
101+
case ARROW_RIGHT:
102+
newFocusEl = getFocusableSibling(pathList, false);
103+
break;
104+
case UP:
105+
case ARROW_UP:
106+
newFocusEl = getFocusableSibling(itemList, true);
107+
break;
108+
case DOWN:
109+
case ARROW_DOWN:
110+
newFocusEl = getFocusableSibling(itemList, false);
111+
break;
112+
}
113+
} else if (e.keyCode) {
114+
switch (e.keyCode) {
115+
case ARROW_LEFT_CODE:
116+
newFocusEl = getFocusableSibling(pathList, true);
117+
break;
118+
case ARROW_RIGHT_CODE:
119+
newFocusEl = getFocusableSibling(pathList, false);
120+
break;
121+
case ARROW_UP_CODE:
122+
newFocusEl = getFocusableSibling(itemList, true);
123+
break;
124+
case ARROW_DOWN_CODE:
125+
newFocusEl = getFocusableSibling(itemList, false);
126+
break;
127+
}
128+
}
129+
if (newFocusEl) {
130+
e.preventDefault();
131+
newFocusEl.focus();
132+
}
133+
});
134+
}
135+
2136
function enableDragUpload() {
3137
if (!document.querySelector || !document.addEventListener || !document.body.classList) {
4138
return;
@@ -71,6 +205,9 @@
71205
return;
72206
}
73207

208+
var selectorNone = '.' + classNone;
209+
var selectorNotNone = ':not(' + selectorNone + ')';
210+
74211
// event handler
75212
var timeoutId;
76213
var lastFilterText = '';
@@ -80,30 +217,30 @@
80217
return;
81218
}
82219

83-
var itemsSelector = '.item-list > li:not(.header):not(.parent)';
220+
var itemsSelector = '.item-list > li:not(.' + classHeader + '):not(.parent)';
84221
var items, i;
85222

86223
if (!filterText) { // filter cleared, show all items
87-
itemsSelector += '.none';
224+
itemsSelector += selectorNone;
88225
items = document.body.querySelectorAll(itemsSelector);
89226
for (i = items.length - 1; i >= 0; i--) {
90-
items[i].classList.remove('none');
227+
items[i].classList.remove(classNone);
91228
}
92229
} else {
93230
if (filterText.indexOf(lastFilterText) >= 0) { // increment search, find in visible items
94-
itemsSelector += ':not(.none)';
231+
itemsSelector += selectorNotNone;
95232
} else if (lastFilterText.indexOf(filterText) >= 0) { // decrement search, find in hidden items
96-
itemsSelector += '.none';
233+
itemsSelector += selectorNone;
97234
}
98235

99236
items = document.body.querySelectorAll(itemsSelector);
100237
for (i = items.length - 1; i >= 0; i--) {
101238
var item = items[i];
102239
var name = item.querySelector('.name');
103240
if (name && name.textContent.toLowerCase().indexOf(filterText) < 0) {
104-
item.classList.add('none');
241+
item.classList.add(classNone);
105242
} else {
106-
item.classList.remove('none');
243+
item.classList.remove(classNone);
107244
}
108245
}
109246
}
@@ -205,6 +342,7 @@
205342
}, false);
206343
}
207344

345+
enableKeyboardNavigate();
208346
enableDragUpload();
209347
enableFilter();
210348
enableNonRefreshDelete();

0 commit comments

Comments
 (0)