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 cdca80866605f6c672c20a96a3b318ed0449ee5d..24aca68b557c8fffc35d48d7bbabbd1768a2a47b 100644 |
| --- a/chrome/browser/resources/md_bookmarks/folder_node.js |
| +++ b/chrome/browser/resources/md_bookmarks/folder_node.js |
| @@ -7,6 +7,7 @@ Polymer({ |
| behaviors: [ |
| bookmarks.StoreClient, |
| + Polymer.IronA11yKeysBehavior, |
| ], |
| properties: { |
| @@ -20,6 +21,11 @@ Polymer({ |
| observer: 'depthChanged_', |
| }, |
| + focusable: { |
| + type: Boolean, |
| + value: false, |
| + }, |
| + |
| /** @type {BookmarkNode} */ |
| item_: Object, |
| @@ -41,6 +47,10 @@ Polymer({ |
| }, |
| }, |
| + listeners: { |
| + 'keydown': 'onKeydown_', |
| + }, |
| + |
| /** @override */ |
| attached: function() { |
| this.watch('item_', function(state) { |
| @@ -57,6 +67,14 @@ Polymer({ |
| }); |
| this.updateFromStore(); |
| + |
| + if (this.selectedFolder_ == this.itemId) |
| + this.fire('folder-node-focus-changed', this); |
| + }, |
| + |
| + /** @return {HTMLElement} */ |
| + getFocusTarget: function() { |
| + return this.$.container; |
| }, |
| /** @return {HTMLElement} */ |
| @@ -65,8 +83,110 @@ Polymer({ |
| }, |
| /** @return {boolean} */ |
| - isPositiveDepth_: function() { |
| - return this.depth >= 0; |
| + isPositiveDepth_: function(depth) { |
| + return depth >= 0; |
| + }, |
| + |
| + /** |
| + * @private |
| + * @param {!Event} e |
| + */ |
| + onKeydown_: function(e) { |
| + var direction = 0; |
| + var handled = true; |
| + 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.
|
| + direction = -1; |
| + } else if (this.keyboardEventMatchesKeys(e, 'down')) { |
| + direction = 1; |
| + } else { |
| + handled = false; |
| + } |
| + |
|
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.
|
| + if (direction) { |
| + handled = |
| + this.changeKeyboardSelection_(direction, this.root.activeElement); |
| + } |
| + |
| + if (!handled) |
| + return; |
| + |
| + e.preventDefault(); |
| + e.stopPropagation(); |
| + }, |
| + |
| + /** |
| + * @private |
| + * @param {number} direction |
| + * @return {boolean} Whether the event should be treated as handled. |
| + */ |
| + changeKeyboardSelection_: function(direction, currentFocus) { |
| + var isCurrentlyFocused = currentFocus == this.$.container; |
| + |
| + // 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, isCurrentlyFocused ? this : currentFocus); |
| + |
| + 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.
|
| + if (newFocusFolderNode) { |
| + this.fire('folder-node-focus-changed', newFocusFolderNode); |
| + newFocusFolderNode.getFocusTarget().focus(); |
| + } else { |
| + // If there is no newly focused node, allow the event to bubble. |
| + return false; |
| + } |
| + } |
| + 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 |
|
tsergeant
2017/04/19 01:42:16
Returns null...when?
calamity
2017/04/19 04:51:02
Done.
|
| + */ |
| + getNextVisibleFolderNode_: function(reverse, current) { |
| + var newFocus = null; |
| + var children = |
| + Array.from(this.root.querySelectorAll('bookmarks-folder-node')); |
| + |
| + // The current node's successor can only be its first child. |
| + if (this == current) { |
| + 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_(); |
| }, |
| /** |
| @@ -140,4 +260,8 @@ Polymer({ |
| isRootFolder_: function() { |
| return this.depth == 0; |
| }, |
| + |
| + getTabIndex_: function(focusable) { |
| + return focusable ? 0 : -1; |
| + }, |
| }); |