-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbasic-keyboard-paging.html
More file actions
188 lines (154 loc) · 5.49 KB
/
basic-keyboard-paging.html
File metadata and controls
188 lines (154 loc) · 5.49 KB
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
<!--
Aspect which maps page keys (Page Up, Page Down) into operations that scroll
the component.
The keyboard interaction model generally follows that of Microsoft Windows'
list boxes instead of those in OS X:
* The Page Up/Down and Home/End keys actually move the selection, rather than
just scrolling. The former behavior seems more generally useful for keyboard
users.
* Pressing Page Up/Down will move the selection to the topmost/bottommost
visible item if the selection is not already there. Thereafter, the key will
move the selection up/down by a page, and (per the above point) make the
selected item visible.
@element basic-keyboard-paging
-->
<link rel="import" href="../basic-aspect/basic-aspect.html">
<script>
window.Basic = window.Basic || {};
window.Basic.KeyboardPaging = {
contribute: {
keydown: function(event) {
var handled = false;
switch (event.keyCode) {
case 33: // Page Up
handled = this.collective.pageUp();
break;
case 34: // Page Down
handled = this.collective.pageDown();
break;
}
if (handled) {
event.preventDefault();
event.stopPropagation();
}
},
/**
* Scroll down one page.
*
* @method pageDown
*/
pageDown: function() {
return this._scrollOnePage(true);
},
/**
* Scroll up one page.
*
* @method pageUp
*/
pageUp: function() {
return this._scrollOnePage(false);
}
},
name: 'KeyboardPaging',
// Return the item whose content spans the given y position (relative to the
// top of the list's scrolling client area), or null if not found.
//
// If downward is true, move down the list of items to find the first item
// found at the given y position; if downward is false, move up the list of
// items to find the last item at that position.
_getIndexOfItemAtY: function(y, downward) {
var items = this.collective.items;
var start = downward ? 0 : items.length - 1;
var end = downward ? items.length : 0;
var step = downward ? 1 : -1;
var innermost = this.collective.innermostAttached;
var topOfClientArea = innermost.offsetTop + innermost.clientTop;
var i = start;
var found = false;
while (i !== end) {
var item = items[i];
var itemTop = item.offsetTop - topOfClientArea;
var itemBottom = itemTop + item.offsetHeight;
if (itemTop <= y && itemBottom >= y) {
// Item spans the indicated y coordinate.
found = true;
break;
}
i += step;
}
if (!found) {
return null;
}
// We may have found an item whose padding spans the given y coordinate,
// but whose content is actually above/below that point.
// TODO: If the item has a border, then padding should be included in
// considering a hit.
var itemStyle = getComputedStyle(item);
var itemPaddingTop = parseFloat(itemStyle.paddingTop);
var itemPaddingBottom = parseFloat(itemStyle.paddingBottom);
var contentTop = itemTop + item.clientTop + itemPaddingTop;
var contentBottom = contentTop + item.clientHeight - itemPaddingTop - itemPaddingBottom;
if (downward && contentTop <= y
|| !downward && contentBottom >= y) {
// The indicated coordinate hits the actual item content.
return i;
}
else {
// The indicated coordinate falls within the item's padding. Back up to
// the item below/above the item we found and return that.
i -= step;
return i;
}
},
// Move by one page downward (if downward is true), or upward (if false).
// Return true if we ended up changing the selection, false if not.
// TODO: Better support for horizontal lists.
_scrollOnePage: function(downward) {
var innermost = this.collective.innermostAttached;
if (!innermost) {
return;
}
// Determine the item visible just at the edge of direction we're heading.
// We'll select that item if it's not already selected.
var edge = innermost.scrollTop + (downward ? innermost.clientHeight : 0);
var indexOfItemAtEdge = this._getIndexOfItemAtY(edge, downward);
var selectedIndex = this.collective.selectedIndex;
var newIndex;
if (indexOfItemAtEdge && selectedIndex === indexOfItemAtEdge) {
// The item at the edge was already selected, so scroll in the indicated
// direction by one page. Leave the new item at that edge selected.
var delta = (downward ? 1 : -1) * innermost.clientHeight;
newIndex = this._getIndexOfItemAtY(edge + delta, downward);
}
else {
// The item at the edge wasn't selected yet. Instead of scrolling, we'll
// just select that item. That is, the first attempt to page up/down
// usually just moves the selection to the edge in that direction.
newIndex = indexOfItemAtEdge;
}
if (!newIndex) {
// We can't find an item in the direction we want to travel. Select the
// last item (if moving downward) or first item (if moving upward).
newIndex = (downward ? this.collective.items.length - 1 : 0);
}
if (newIndex !== selectedIndex) {
this.collective.selectedIndex = newIndex;
return true; // We handled the page up/down ourselves.
}
else {
return false; // We didn't do anything.
}
}
};
Polymer({
aspects: [Basic.KeyboardPaging],
behaviors: [Basic.Aspect],
is: 'basic-keyboard-paging',
pageDown: function() {
this.collective.pageDown();
},
pageUp: function() {
this.collective.pageUp();
},
});
</script>