OLD | NEW |
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 Loading... |
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 |
| 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 |
96 /** | 238 /** |
97 * @private | 239 * @private |
| 240 * @param {!Event} e |
| 241 */ |
| 242 preventDefault_: function(e) { |
| 243 e.preventDefault(); |
| 244 }, |
| 245 |
| 246 /** |
| 247 * @private |
98 * @param {string} itemId | 248 * @param {string} itemId |
99 * @param {string} selectedFolder | 249 * @param {string} selectedFolder |
100 * @return {boolean} | 250 * @return {boolean} |
101 */ | 251 */ |
102 computeIsSelected_: function(itemId, selectedFolder, searchActive) { | 252 computeIsSelected_: function(itemId, selectedFolder, searchActive) { |
103 return itemId == selectedFolder && !searchActive; | 253 return itemId == selectedFolder && !searchActive; |
104 }, | 254 }, |
105 | 255 |
106 /** | 256 /** |
107 * @private | 257 * @private |
(...skipping 25 matching lines...) Expand all Loading... |
133 return !this.getState().nodes[itemId].url; | 283 return !this.getState().nodes[itemId].url; |
134 }, | 284 }, |
135 | 285 |
136 /** | 286 /** |
137 * @private | 287 * @private |
138 * @return {boolean} | 288 * @return {boolean} |
139 */ | 289 */ |
140 isRootFolder_: function() { | 290 isRootFolder_: function() { |
141 return this.itemId == ROOT_NODE_ID; | 291 return this.itemId == ROOT_NODE_ID; |
142 }, | 292 }, |
| 293 |
| 294 /** |
| 295 * @private |
| 296 * @return {string} |
| 297 */ |
| 298 getTabIndex_: function() { |
| 299 return this.isSelectedFolder_ ? '0' : ''; |
| 300 }, |
143 }); | 301 }); |
OLD | NEW |