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 cdca80866605f6c672c20a96a3b318ed0449ee5d..75890da0efe0efc98f1f45f7c737b6cacdfd6825 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,115 @@ 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) { |
+ handled = |
+ this.changeKeyboardSelection_(direction, this.root.activeElement); |
+ } |
+ |
+ if (!handled) |
+ return; |
+ |
+ e.preventDefault(); |
+ e.stopPropagation(); |
+ }, |
+ |
+ /** |
+ * @private |
+ * @param {number} direction |
+ * @param {HTMLElement} currentFocus |
+ * @return {boolean} Whether the event should be treated as handled. |
+ */ |
+ changeKeyboardSelection_: function(direction, currentFocus) { |
+ var isChildFolderNodeFocused = |
+ currentFocus.tagName == 'BOOKMARKS-FOLDER-NODE'; |
+ |
+ // Get the next folder node. If there is no next element, the event will |
+ // bubble to the parent node which will handle it. |
+ var newFocusFolderNode = this.getNextVisibleFolderNode_( |
+ direction == -1, |
+ isChildFolderNodeFocused ? |
+ /** @type {BookmarksFolderNodeElement} */ (currentFocus) : |
+ this); |
+ |
+ // If there is no newly focused node, allow the event to bubble. |
+ if (!newFocusFolderNode) |
+ return false; |
+ |
+ if (newFocusFolderNode.itemId != ROOT_NODE_ID) { |
+ newFocusFolderNode.selectFolder_(); |
+ newFocusFolderNode.getFocusTarget().focus(); |
+ } |
+ |
+ return true; |
+ }, |
+ |
+ /** |
+ * Returns the next or previous visible bookmark node relative to |current|. |
+ * @private |
+ * @param {boolean} reverse |
+ * @param {BookmarksFolderNodeElement} current Either this node, or an |
+ * immediate child. |
+ * @return {HTMLElement|null} Returns null if the next node is not within this |
+ * folder node. |
+ */ |
+ getNextVisibleFolderNode_: function(reverse, current) { |
+ var newFocus = null; |
+ var children = |
+ Array.from(this.root.querySelectorAll('bookmarks-folder-node')); |
tsergeant
2017/04/19 07:32:18
Pull this out into a helper getter, since it's use
calamity
2017/04/28 06:15:47
Done.
|
+ |
+ // The current node's successor can only be its first child. |
+ if (this == current) { |
tsergeant
2017/04/19 07:32:18
I find it confusing that this is packaged into thi
calamity
2017/04/28 06:15:47
Hmm, ok. I've changed things here a little bit. WD
tsergeant
2017/05/01 01:35:57
Yeah, I think this is a bit better. It's still a c
|
+ if (!reverse && children.length && !this.isClosed_) |
+ newFocus = children[0]; |
+ |
+ return newFocus; |
+ } |
+ |
+ var index = children.indexOf(current); |
+ 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 ? this : 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; |
+ }, |
+ |
+ /** |
+ * @private |
+ * @return {BookmarksFolderNodeElement} |
+ */ |
+ getLastVisibleDescendant_: function() { |
+ var children = |
+ Array.from(this.root.querySelectorAll('bookmarks-folder-node')); |
+ |
+ if (this.isClosed_ || children.length == 0) |
+ return this; |
+ |
+ return children.pop().getLastVisibleDescendant_(); |
+ }, |
+ |
+ /** |
+ * @private |
* @return {string} |
*/ |
getFolderIcon_: function() { |
@@ -140,4 +258,12 @@ Polymer({ |
isRootFolder_: function() { |
return this.depth == 0; |
}, |
+ |
+ /** |
+ * @private |
+ * @return {string} |
+ */ |
+ getTabIndex_: function() { |
+ return this.isSelectedFolder_ ? '0' : ''; |
+ }, |
}); |