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

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

Issue 2820153003: [MD Bookmarks] Add keyboard navigation to sidebar. (Closed)
Patch Set: Created 3 years, 8 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 Polymer.IronA11yKeysBehavior,
10 ], 11 ],
11 12
12 properties: { 13 properties: {
13 itemId: { 14 itemId: {
14 type: String, 15 type: String,
15 observer: 'updateFromStore', 16 observer: 'updateFromStore',
16 }, 17 },
17 18
18 depth: { 19 depth: {
19 type: Number, 20 type: Number,
20 observer: 'depthChanged_', 21 observer: 'depthChanged_',
21 }, 22 },
22 23
24 focusable: {
25 type: Boolean,
26 value: false,
27 },
28
23 /** @type {BookmarkNode} */ 29 /** @type {BookmarkNode} */
24 item_: Object, 30 item_: Object,
25 31
26 /** @private */ 32 /** @private */
27 isClosed_: Boolean, 33 isClosed_: Boolean,
28 34
29 /** @private */ 35 /** @private */
30 selectedFolder_: String, 36 selectedFolder_: String,
31 37
32 /** @private */ 38 /** @private */
33 searchActive_: Boolean, 39 searchActive_: Boolean,
34 40
35 /** @private */ 41 /** @private */
36 isSelectedFolder_: { 42 isSelectedFolder_: {
37 type: Boolean, 43 type: Boolean,
38 value: false, 44 value: false,
39 reflectToAttribute: true, 45 reflectToAttribute: true,
40 computed: 'computeIsSelected_(itemId, selectedFolder_, searchActive_)' 46 computed: 'computeIsSelected_(itemId, selectedFolder_, searchActive_)'
41 }, 47 },
42 }, 48 },
43 49
50 listeners: {
51 'keydown': 'onKeydown_',
52 },
53
44 /** @override */ 54 /** @override */
45 attached: function() { 55 attached: function() {
46 this.watch('item_', function(state) { 56 this.watch('item_', function(state) {
47 return state.nodes[this.itemId]; 57 return state.nodes[this.itemId];
48 }.bind(this)); 58 }.bind(this));
49 this.watch('isClosed_', function(state) { 59 this.watch('isClosed_', function(state) {
50 return state.closedFolders.has(this.itemId); 60 return state.closedFolders.has(this.itemId);
51 }.bind(this)); 61 }.bind(this));
52 this.watch('selectedFolder_', function(state) { 62 this.watch('selectedFolder_', function(state) {
53 return state.selectedFolder; 63 return state.selectedFolder;
54 }); 64 });
55 this.watch('searchActive_', function(state) { 65 this.watch('searchActive_', function(state) {
56 return bookmarks.util.isShowingSearch(state); 66 return bookmarks.util.isShowingSearch(state);
57 }); 67 });
58 68
59 this.updateFromStore(); 69 this.updateFromStore();
70
71 if (this.selectedFolder_ == this.itemId)
72 this.fire('folder-node-focus-changed', this);
60 }, 73 },
61 74
62 /** @return {HTMLElement} */ 75 /** @return {HTMLElement} */
76 getFocusTarget: function() {
77 return this.$.container;
78 },
79
80 /** @return {HTMLElement} */
63 getDropTarget: function() { 81 getDropTarget: function() {
64 return this.$.container; 82 return this.$.container;
65 }, 83 },
66 84
67 /** @return {boolean} */ 85 /** @return {boolean} */
68 isPositiveDepth_: function() { 86 isPositiveDepth_: function(depth) {
69 return this.depth >= 0; 87 return depth >= 0;
70 }, 88 },
71 89
72 /** 90 /**
91 * @private
92 * @param {!Event} e
93 */
94 onKeydown_: function(e) {
95 var direction = 0;
96 var handled = true;
97 if (this.keyboardEventMatchesKeys(e, 'up')) {
tsergeant 2017/04/19 01:42:16 Seems like overkill to use IronA11yKeysBehavior ju
calamity 2017/04/19 04:51:02 Done.
98 direction = -1;
99 } else if (this.keyboardEventMatchesKeys(e, 'down')) {
100 direction = 1;
101 } else {
102 handled = false;
103 }
104
tsergeant 2017/04/19 01:42:16 Is left/right going to be handled here too? Maybe
calamity 2017/04/19 04:51:02 Yeah. It's in a follow-up.
105 if (direction) {
106 handled =
107 this.changeKeyboardSelection_(direction, this.root.activeElement);
108 }
109
110 if (!handled)
111 return;
112
113 e.preventDefault();
114 e.stopPropagation();
115 },
116
117 /**
118 * @private
119 * @param {number} direction
120 * @return {boolean} Whether the event should be treated as handled.
121 */
122 changeKeyboardSelection_: function(direction, currentFocus) {
123 var isCurrentlyFocused = currentFocus == this.$.container;
124
125 // Get the next folder node. If there is no next element, the event will
126 // bubble to the parent node which will handle it.
127 var newFocusFolderNode = this.getNextVisibleFolderNode_(
128 direction == -1, isCurrentlyFocused ? this : currentFocus);
129
130 if (!newFocusFolderNode || newFocusFolderNode.itemId != ROOT_NODE_ID) {
tsergeant 2017/04/19 01:42:16 This can be simplified a little bit: if (!newFocu
calamity 2017/04/19 04:51:02 Done.
131 if (newFocusFolderNode) {
132 this.fire('folder-node-focus-changed', newFocusFolderNode);
133 newFocusFolderNode.getFocusTarget().focus();
134 } else {
135 // If there is no newly focused node, allow the event to bubble.
136 return false;
137 }
138 }
139 return true;
140 },
141
142 /**
143 * Returns the next or previous visible bookmark node relative to |current|.
144 * @private
145 * @param {boolean} reverse
146 * @param {BookmarksFolderNodeElement} current Either this node, or an
147 * immediate child.
148 * @return {HTMLElement|null} Returns null
tsergeant 2017/04/19 01:42:16 Returns null...when?
calamity 2017/04/19 04:51:02 Done.
149 */
150 getNextVisibleFolderNode_: function(reverse, current) {
151 var newFocus = null;
152 var children =
153 Array.from(this.root.querySelectorAll('bookmarks-folder-node'));
154
155 // The current node's successor can only be its first child.
156 if (this == current) {
157 if (!reverse && children.length && !this.isClosed_)
158 newFocus = children[0];
159
160 return newFocus;
161 }
162
163 var index = children.indexOf(current);
164 assert(index != -1);
165 if (reverse) {
166 // A child node's predecessor is either the previous child's last visible
167 // descendant, or this node, which is its immediate parent.
168 newFocus =
169 index == 0 ? this : children[index - 1].getLastVisibleDescendant_();
170 } else if (index < children.length - 1) {
171 // A successor to a child is the next child.
172 newFocus = children[index + 1];
173 }
174
175 return newFocus;
176 },
177
178 /**
179 * @private
180 * @return {BookmarksFolderNodeElement}
181 */
182 getLastVisibleDescendant_: function() {
183 var children =
184 Array.from(this.root.querySelectorAll('bookmarks-folder-node'));
185
186 if (this.isClosed_ || children.length == 0)
187 return this;
188
189 return children.pop().getLastVisibleDescendant_();
190 },
191
192 /**
73 * @private 193 * @private
74 * @return {string} 194 * @return {string}
75 */ 195 */
76 getFolderIcon_: function() { 196 getFolderIcon_: function() {
77 return this.isSelectedFolder_ ? 'bookmarks:folder-open' : 'cr:folder'; 197 return this.isSelectedFolder_ ? 'bookmarks:folder-open' : 'cr:folder';
78 }, 198 },
79 199
80 /** @private */ 200 /** @private */
81 selectFolder_: function() { 201 selectFolder_: function() {
82 this.dispatch(bookmarks.actions.selectFolder(this.item_.id)); 202 this.dispatch(bookmarks.actions.selectFolder(this.item_.id));
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
133 return !this.getState().nodes[itemId].url; 253 return !this.getState().nodes[itemId].url;
134 }, 254 },
135 255
136 /** 256 /**
137 * @private 257 * @private
138 * @return {boolean} 258 * @return {boolean}
139 */ 259 */
140 isRootFolder_: function() { 260 isRootFolder_: function() {
141 return this.depth == 0; 261 return this.depth == 0;
142 }, 262 },
263
264 getTabIndex_: function(focusable) {
265 return focusable ? 0 : -1;
266 },
143 }); 267 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698