Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(473)

Side by Side Diff: chrome/browser/resources/md_bookmarks/folder_node.js

Issue 2820153003: [MD Bookmarks] Add keyboard navigation to sidebar. (Closed)
Patch Set: rebase Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 Polymer({ 5 Polymer({
6 is: 'bookmarks-folder-node', 6 is: 'bookmarks-folder-node',
7 7
8 behaviors: [ 8 behaviors: [
9 bookmarks.StoreClient, 9 bookmarks.StoreClient,
10 ], 10 ],
(...skipping 23 matching lines...) Expand all
34 34
35 /** @private */ 35 /** @private */
36 isSelectedFolder_: { 36 isSelectedFolder_: {
37 type: Boolean, 37 type: Boolean,
38 value: false, 38 value: false,
39 reflectToAttribute: true, 39 reflectToAttribute: true,
40 computed: 'computeIsSelected_(itemId, selectedFolder_, searchActive_)' 40 computed: 'computeIsSelected_(itemId, selectedFolder_, searchActive_)'
41 }, 41 },
42 }, 42 },
43 43
44 listeners: {
45 'keydown': 'onKeydown_',
46 },
47
44 /** @override */ 48 /** @override */
45 attached: function() { 49 attached: function() {
46 this.watch('item_', function(state) { 50 this.watch('item_', function(state) {
47 return state.nodes[this.itemId]; 51 return state.nodes[this.itemId];
48 }.bind(this)); 52 }.bind(this));
49 this.watch('isClosed_', function(state) { 53 this.watch('isClosed_', function(state) {
50 return state.closedFolders.has(this.itemId); 54 return state.closedFolders.has(this.itemId);
51 }.bind(this)); 55 }.bind(this));
52 this.watch('selectedFolder_', function(state) { 56 this.watch('selectedFolder_', function(state) {
53 return state.selectedFolder; 57 return state.selectedFolder;
54 }); 58 });
55 this.watch('searchActive_', function(state) { 59 this.watch('searchActive_', function(state) {
56 return bookmarks.util.isShowingSearch(state); 60 return bookmarks.util.isShowingSearch(state);
57 }); 61 });
58 62
59 this.updateFromStore(); 63 this.updateFromStore();
60 }, 64 },
61 65
62 /** @return {HTMLElement} */ 66 /** @return {HTMLElement} */
67 getFocusTarget: function() {
68 return this.$.container;
69 },
70
71 /** @return {HTMLElement} */
63 getDropTarget: function() { 72 getDropTarget: function() {
64 return this.$.container; 73 return this.$.container;
65 }, 74 },
66 75
67 /** @return {boolean} */ 76 /** @return {boolean} */
68 isTopLevelFolder_: function() { 77 isTopLevelFolder_: function() {
69 return this.depth == 0; 78 return this.depth == 0;
70 }, 79 },
71 80
72 /** 81 /**
73 * @private 82 * @private
83 * @param {!Event} e
84 */
85 onKeydown_: function(e) {
86 var direction = 0;
87 var handled = true;
88 // TODO(calamity): Handle left/right arrow keys.
89 if (e.key == 'ArrowUp') {
90 direction = -1;
91 } else if (e.key == 'ArrowDown') {
92 direction = 1;
93 } else {
94 handled = false;
95 }
96
97 if (direction)
98 this.changeKeyboardSelection_(direction, this.root.activeElement);
99
100 if (!handled)
101 return;
102
103 e.preventDefault();
104 e.stopPropagation();
105 },
106
107 /**
108 * @private
109 * @param {number} direction
110 * @param {!HTMLElement} currentFocus
111 */
112 changeKeyboardSelection_: function(direction, currentFocus) {
113 var newFocusFolderNode = null;
114 var isChildFolderNodeFocused =
115 currentFocus.tagName == 'BOOKMARKS-FOLDER-NODE';
116 var reverse = direction == -1;
117
118 // The current node's successor is its first child when open.
119 if (!isChildFolderNodeFocused && !reverse && !this.isClosed_) {
120 var children = this.getChildFolderNodes_();
121 if (children.length)
122 newFocusFolderNode = children[0];
123 }
124
125 if (isChildFolderNodeFocused) {
126 // Get the next child folder node if a child is focused.
127 if (!newFocusFolderNode) {
128 newFocusFolderNode = this.getNextChild(
129 reverse,
130 /** @type {!BookmarksFolderNodeElement} */ (currentFocus));
131 }
132
133 // The first child's predecessor is this node.
134 if (!newFocusFolderNode && reverse)
135 newFocusFolderNode = this;
136 }
137
138 // If there is no newly focused node, allow the parent to handle the change.
139 if (!newFocusFolderNode) {
140 if (this.itemId != ROOT_NODE_ID)
141 this.getParentFolderNode_().changeKeyboardSelection_(direction, this);
142
143 return;
144 }
145
146 // The root node is not navigable.
147 if (newFocusFolderNode.itemId != ROOT_NODE_ID) {
148 newFocusFolderNode.selectFolder_();
149 newFocusFolderNode.getFocusTarget().focus();
150 }
151 },
152
153 /**
154 * Returns the next or previous visible bookmark node relative to |child|.
155 * @private
tsergeant 2017/05/01 01:35:57 Is this still intended to be @private?
calamity 2017/05/03 03:01:47 Changed the method name.
156 * @param {boolean} reverse
157 * @param {!BookmarksFolderNodeElement} child
158 * @return {BookmarksFolderNodeElement|null} Returns null if there is no child
159 * before/after |child|.
160 */
161 getNextChild: function(reverse, child) {
162 var newFocus = null;
163 var children = this.getChildFolderNodes_();
164
165 var index = children.indexOf(child);
166 assert(index != -1);
167 if (reverse) {
168 // A child node's predecessor is either the previous child's last visible
169 // descendant, or this node, which is its immediate parent.
170 newFocus =
171 index == 0 ? null : children[index - 1].getLastVisibleDescendant_();
172 } else if (index < children.length - 1) {
173 // A successor to a child is the next child.
174 newFocus = children[index + 1];
175 }
176
177 return newFocus;
178 },
179
180 /**
181 * Returns the immediate parent folder node, or null if there is none.
182 * @private
183 * @return {BookmarksFolderNodeElement|null}
184 */
185 getParentFolderNode_: function() {
186 var parentFolderNode = this.parentNode;
187 while (parentFolderNode &&
188 parentFolderNode.tagName != 'BOOKMARKS-FOLDER-NODE') {
189 parentFolderNode = parentFolderNode.parentNode || parentFolderNode.host;
190 }
191 return parentFolderNode || null;
192 },
193
194 /**
195 * @private
196 * @return {BookmarksFolderNodeElement}
197 */
198 getLastVisibleDescendant_: function() {
199 var children = this.getChildFolderNodes_();
200 if (this.isClosed_ || children.length == 0)
201 return this;
202
203 return children.pop().getLastVisibleDescendant_();
204 },
205
206 /**
207 * @private
74 * @return {string} 208 * @return {string}
75 */ 209 */
76 getFolderIcon_: function() { 210 getFolderIcon_: function() {
77 return this.isSelectedFolder_ ? 'bookmarks:folder-open' : 'cr:folder'; 211 return this.isSelectedFolder_ ? 'bookmarks:folder-open' : 'cr:folder';
78 }, 212 },
79 213
80 /** @private */ 214 /** @private */
81 selectFolder_: function() { 215 selectFolder_: function() {
82 this.dispatch(bookmarks.actions.selectFolder(this.item_.id)); 216 this.dispatch(bookmarks.actions.selectFolder(this.item_.id));
83 }, 217 },
84 218
85 /** 219 /**
220 * @private
221 * @return {!Array<!BookmarksFolderNodeElement>}
222 */
223 getChildFolderNodes_: function() {
224 return Array.from(this.root.querySelectorAll('bookmarks-folder-node'));
225 },
226
227 /**
86 * Occurs when the drop down arrow is tapped. 228 * Occurs when the drop down arrow is tapped.
87 * @private 229 * @private
88 * @param {!Event} e 230 * @param {!Event} e
89 */ 231 */
90 toggleFolder_: function(e) { 232 toggleFolder_: function(e) {
91 this.dispatch( 233 this.dispatch(
92 bookmarks.actions.changeFolderOpen(this.item_.id, this.isClosed_)); 234 bookmarks.actions.changeFolderOpen(this.item_.id, this.isClosed_));
93 e.stopPropagation(); 235 e.stopPropagation();
94 }, 236 },
95 237
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
133 return !this.getState().nodes[itemId].url; 275 return !this.getState().nodes[itemId].url;
134 }, 276 },
135 277
136 /** 278 /**
137 * @private 279 * @private
138 * @return {boolean} 280 * @return {boolean}
139 */ 281 */
140 isRootFolder_: function() { 282 isRootFolder_: function() {
141 return this.itemId == ROOT_NODE_ID; 283 return this.itemId == ROOT_NODE_ID;
142 }, 284 },
285
286 /**
287 * @private
288 * @return {string}
289 */
290 getTabIndex_: function() {
291 return this.isSelectedFolder_ ? '0' : '';
292 },
143 }); 293 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698