Chromium Code Reviews| Index: chrome/browser/resources/md_bookmarks/folder_node.js |
| diff --git a/chrome/browser/resources/md_bookmarks/folder_node.js b/chrome/browser/resources/md_bookmarks/folder_node.js |
| index a6b2e938eacf4047277f5fdd4acb9ed91c86848e..fcf331a798acbf05815f4bf153e07d31062b87f6 100644 |
| --- a/chrome/browser/resources/md_bookmarks/folder_node.js |
| +++ b/chrome/browser/resources/md_bookmarks/folder_node.js |
| @@ -41,6 +41,10 @@ Polymer({ |
| }, |
| }, |
| + listeners: { |
| + 'keydown': 'onKeydown_', |
| + }, |
| + |
| /** @override */ |
| attached: function() { |
| this.watch('item_', function(state) { |
| @@ -60,6 +64,11 @@ Polymer({ |
| }, |
| /** @return {HTMLElement} */ |
| + getFocusTarget: function() { |
| + return this.$.container; |
| + }, |
| + |
| + /** @return {HTMLElement} */ |
| getDropTarget: function() { |
| return this.$.container; |
| }, |
| @@ -71,6 +80,131 @@ Polymer({ |
| /** |
| * @private |
| + * @param {!Event} e |
| + */ |
| + onKeydown_: function(e) { |
| + var direction = 0; |
| + var handled = true; |
| + // TODO(calamity): Handle left/right arrow keys. |
| + if (e.key == 'ArrowUp') { |
| + direction = -1; |
| + } else if (e.key == 'ArrowDown') { |
| + direction = 1; |
| + } else { |
| + handled = false; |
| + } |
| + |
| + if (direction) |
| + this.changeKeyboardSelection_(direction, this.root.activeElement); |
| + |
| + if (!handled) |
| + return; |
| + |
| + e.preventDefault(); |
| + e.stopPropagation(); |
| + }, |
| + |
| + /** |
| + * @private |
| + * @param {number} direction |
| + * @param {!HTMLElement} currentFocus |
| + */ |
| + changeKeyboardSelection_: function(direction, currentFocus) { |
| + var newFocusFolderNode = null; |
| + var isChildFolderNodeFocused = |
| + currentFocus.tagName == 'BOOKMARKS-FOLDER-NODE'; |
| + var reverse = direction == -1; |
| + |
| + // The current node's successor is its first child when open. |
| + if (!isChildFolderNodeFocused && !reverse && !this.isClosed_) { |
| + var children = this.getChildFolderNodes_(); |
| + if (children.length) |
| + newFocusFolderNode = children[0]; |
| + } |
| + |
| + if (isChildFolderNodeFocused) { |
| + // Get the next child folder node if a child is focused. |
| + if (!newFocusFolderNode) { |
| + newFocusFolderNode = this.getNextChild( |
| + reverse, |
| + /** @type {!BookmarksFolderNodeElement} */ (currentFocus)); |
| + } |
| + |
| + // The first child's predecessor is this node. |
| + if (!newFocusFolderNode && reverse) |
| + newFocusFolderNode = this; |
| + } |
| + |
| + // If there is no newly focused node, allow the parent to handle the change. |
| + if (!newFocusFolderNode) { |
| + if (this.itemId != ROOT_NODE_ID) |
| + this.getParentFolderNode_().changeKeyboardSelection_(direction, this); |
| + |
| + return; |
| + } |
| + |
| + // The root node is not navigable. |
| + if (newFocusFolderNode.itemId != ROOT_NODE_ID) { |
| + newFocusFolderNode.selectFolder_(); |
| + newFocusFolderNode.getFocusTarget().focus(); |
| + } |
| + }, |
| + |
| + /** |
| + * Returns the next or previous visible bookmark node relative to |child|. |
| + * @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.
|
| + * @param {boolean} reverse |
| + * @param {!BookmarksFolderNodeElement} child |
| + * @return {BookmarksFolderNodeElement|null} Returns null if there is no child |
| + * before/after |child|. |
| + */ |
| + getNextChild: function(reverse, child) { |
| + var newFocus = null; |
| + var children = this.getChildFolderNodes_(); |
| + |
| + var index = children.indexOf(child); |
| + assert(index != -1); |
| + if (reverse) { |
| + // A child node's predecessor is either the previous child's last visible |
| + // descendant, or this node, which is its immediate parent. |
| + newFocus = |
| + index == 0 ? null : children[index - 1].getLastVisibleDescendant_(); |
| + } else if (index < children.length - 1) { |
| + // A successor to a child is the next child. |
| + newFocus = children[index + 1]; |
| + } |
| + |
| + return newFocus; |
| + }, |
| + |
| + /** |
| + * Returns the immediate parent folder node, or null if there is none. |
| + * @private |
| + * @return {BookmarksFolderNodeElement|null} |
| + */ |
| + getParentFolderNode_: function() { |
| + var parentFolderNode = this.parentNode; |
| + while (parentFolderNode && |
| + parentFolderNode.tagName != 'BOOKMARKS-FOLDER-NODE') { |
| + parentFolderNode = parentFolderNode.parentNode || parentFolderNode.host; |
| + } |
| + return parentFolderNode || null; |
| + }, |
| + |
| + /** |
| + * @private |
| + * @return {BookmarksFolderNodeElement} |
| + */ |
| + getLastVisibleDescendant_: function() { |
| + var children = this.getChildFolderNodes_(); |
| + if (this.isClosed_ || children.length == 0) |
| + return this; |
| + |
| + return children.pop().getLastVisibleDescendant_(); |
| + }, |
| + |
| + /** |
| + * @private |
| * @return {string} |
| */ |
| getFolderIcon_: function() { |
| @@ -83,6 +217,14 @@ Polymer({ |
| }, |
| /** |
| + * @private |
| + * @return {!Array<!BookmarksFolderNodeElement>} |
| + */ |
| + getChildFolderNodes_: function() { |
| + return Array.from(this.root.querySelectorAll('bookmarks-folder-node')); |
| + }, |
| + |
| + /** |
| * Occurs when the drop down arrow is tapped. |
| * @private |
| * @param {!Event} e |
| @@ -140,4 +282,12 @@ Polymer({ |
| isRootFolder_: function() { |
| return this.itemId == ROOT_NODE_ID; |
| }, |
| + |
| + /** |
| + * @private |
| + * @return {string} |
| + */ |
| + getTabIndex_: function() { |
| + return this.isSelectedFolder_ ? '0' : ''; |
| + }, |
| }); |