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..94cc5e7a665fea47fdf550a04549d56e42d91e4c 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 |
+ * @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 |
@@ -95,6 +237,14 @@ Polymer({ |
/** |
* @private |
+ * @param {!Event} e |
+ */ |
+ preventDefault_: function(e) { |
+ e.preventDefault(); |
+ }, |
+ |
+ /** |
+ * @private |
* @param {string} itemId |
* @param {string} selectedFolder |
* @return {boolean} |
@@ -140,4 +290,12 @@ Polymer({ |
isRootFolder_: function() { |
return this.itemId == ROOT_NODE_ID; |
}, |
+ |
+ /** |
+ * @private |
+ * @return {string} |
+ */ |
+ getTabIndex_: function() { |
+ return this.isSelectedFolder_ ? '0' : ''; |
+ }, |
}); |