| 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-list', | 6 is: 'bookmarks-list', |
| 7 | 7 |
| 8 behaviors: [ | 8 behaviors: [ |
| 9 bookmarks.StoreClient, | 9 bookmarks.StoreClient, |
| 10 ], | 10 ], |
| 11 | 11 |
| 12 properties: { | 12 properties: { |
| 13 /** | 13 /** |
| 14 * A list of item ids wrapped in an Object. This is necessary because | 14 * A list of item ids wrapped in an Object. This is necessary because |
| 15 * iron-list is unable to distinguish focusing index 6 from focusing id '6' | 15 * iron-list is unable to distinguish focusing index 6 from focusing id '6' |
| 16 * so the item we supply to iron-list needs to be non-index-like. | 16 * so the item we supply to iron-list needs to be non-index-like. |
| 17 * @private {Array<{id: string}>} | 17 * @private {Array<{id: string}>} |
| 18 */ | 18 */ |
| 19 displayedList_: { | 19 displayedList_: { |
| 20 type: Array, | 20 type: Array, |
| 21 value: function() { | 21 value: function() { |
| 22 // Use an empty list during initialization so that the databinding to | 22 // Use an empty list during initialization so that the databinding to |
| 23 // hide #bookmarksCard takes effect. | 23 // hide #list takes effect. |
| 24 return []; | 24 return []; |
| 25 }, | 25 }, |
| 26 }, | 26 }, |
| 27 | 27 |
| 28 /** @private {Array<string>} */ | 28 /** @private {Array<string>} */ |
| 29 displayedIds_: { | 29 displayedIds_: { |
| 30 type: Array, | 30 type: Array, |
| 31 observer: 'onDisplayedIdsChanged_', | 31 observer: 'onDisplayedIdsChanged_', |
| 32 }, | 32 }, |
| 33 | 33 |
| 34 /** @private */ | 34 /** @private */ |
| 35 searchTerm_: { | 35 searchTerm_: { |
| 36 type: String, | 36 type: String, |
| 37 observer: 'onDisplayedListSourceChange_', | 37 observer: 'onDisplayedListSourceChange_', |
| 38 }, | 38 }, |
| 39 | 39 |
| 40 /** @private */ | 40 /** @private */ |
| 41 selectedFolder_: { | 41 selectedFolder_: { |
| 42 type: String, | 42 type: String, |
| 43 observer: 'onDisplayedListSourceChange_', | 43 observer: 'onDisplayedListSourceChange_', |
| 44 }, | 44 }, |
| 45 }, | 45 }, |
| 46 | 46 |
| 47 listeners: { | 47 listeners: { |
| 48 'click': 'deselectItems_', | 48 'click': 'deselectItems_', |
| 49 'open-item-menu': 'onOpenItemMenu_', | 49 'open-item-menu': 'onOpenItemMenu_', |
| 50 }, | 50 }, |
| 51 | 51 |
| 52 attached: function() { | 52 attached: function() { |
| 53 var list = /** @type {IronListElement} */ (this.$.bookmarksCard); | 53 var list = /** @type {IronListElement} */ (this.$.list); |
| 54 list.scrollTarget = this; | 54 list.scrollTarget = this; |
| 55 | 55 |
| 56 this.watch('displayedIds_', function(state) { | 56 this.watch('displayedIds_', function(state) { |
| 57 return bookmarks.util.getDisplayedList(state); | 57 return bookmarks.util.getDisplayedList(state); |
| 58 }); | 58 }); |
| 59 this.watch('searchTerm_', function(state) { | 59 this.watch('searchTerm_', function(state) { |
| 60 return state.search.term; | 60 return state.search.term; |
| 61 }); | 61 }); |
| 62 this.watch('selectedFolder_', function(state) { | 62 this.watch('selectedFolder_', function(state) { |
| 63 return state.selectedFolder; | 63 return state.selectedFolder; |
| 64 }); | 64 }); |
| 65 this.updateFromStore(); | 65 this.updateFromStore(); |
| 66 | 66 |
| 67 this.$.bookmarksCard.addEventListener( | 67 this.$.list.addEventListener( |
| 68 'keydown', this.onItemKeydown_.bind(this), true); | 68 'keydown', this.onItemKeydown_.bind(this), true); |
| 69 |
| 70 /** @private {function(!Event)} */ |
| 71 this.boundOnHighlightItems_ = this.onHighlightItems_.bind(this); |
| 72 document.addEventListener('highlight-items', this.boundOnHighlightItems_); |
| 73 }, |
| 74 |
| 75 detached: function() { |
| 76 document.removeEventListener( |
| 77 'highlight-items', this.boundOnHighlightItems_); |
| 69 }, | 78 }, |
| 70 | 79 |
| 71 /** @return {HTMLElement} */ | 80 /** @return {HTMLElement} */ |
| 72 getDropTarget: function() { | 81 getDropTarget: function() { |
| 73 return this.$.message; | 82 return this.$.message; |
| 74 }, | 83 }, |
| 75 | 84 |
| 76 /** | 85 /** |
| 77 * Updates `displayedList_` using splices to be equivalent to `newValue`. This | 86 * Updates `displayedList_` using splices to be equivalent to `newValue`. This |
| 78 * allows the iron-list to delete sublists of items which preserves scroll and | 87 * allows the iron-list to delete sublists of items which preserves scroll and |
| (...skipping 22 matching lines...) Expand all Loading... |
| 101 ].concat(additions)); | 110 ].concat(additions)); |
| 102 }.bind(this)); | 111 }.bind(this)); |
| 103 } | 112 } |
| 104 }, | 113 }, |
| 105 | 114 |
| 106 /** @private */ | 115 /** @private */ |
| 107 onDisplayedListSourceChange_: function() { | 116 onDisplayedListSourceChange_: function() { |
| 108 this.scrollTop = 0; | 117 this.scrollTop = 0; |
| 109 }, | 118 }, |
| 110 | 119 |
| 120 /** |
| 121 * Scroll the list so that |itemId| is visible, if it is not already. |
| 122 * @param {string} itemId |
| 123 * @private |
| 124 */ |
| 125 scrollToId_: function(itemId) { |
| 126 var index = this.displayedIds_.indexOf(itemId); |
| 127 var list = this.$.list; |
| 128 if (index >= 0 && index < list.firstVisibleIndex || |
| 129 index > list.lastVisibleIndex) { |
| 130 list.scrollToIndex(index); |
| 131 } |
| 132 }, |
| 133 |
| 111 /** @private */ | 134 /** @private */ |
| 112 emptyListMessage_: function() { | 135 emptyListMessage_: function() { |
| 113 var emptyListMessage = this.searchTerm_ ? 'noSearchResults' : 'emptyList'; | 136 var emptyListMessage = this.searchTerm_ ? 'noSearchResults' : 'emptyList'; |
| 114 return loadTimeData.getString(emptyListMessage); | 137 return loadTimeData.getString(emptyListMessage); |
| 115 }, | 138 }, |
| 116 | 139 |
| 117 /** @private */ | 140 /** @private */ |
| 118 isEmptyList_: function() { | 141 isEmptyList_: function() { |
| 119 return this.displayedList_.length == 0; | 142 return this.displayedList_.length == 0; |
| 120 }, | 143 }, |
| 121 | 144 |
| 122 /** @private */ | 145 /** @private */ |
| 123 deselectItems_: function() { | 146 deselectItems_: function() { |
| 124 this.dispatch(bookmarks.actions.deselectItems()); | 147 this.dispatch(bookmarks.actions.deselectItems()); |
| 125 }, | 148 }, |
| 126 | 149 |
| 127 /** | 150 /** |
| 128 * @param{HTMLElement} el | 151 * @param{HTMLElement} el |
| 129 * @private | 152 * @private |
| 130 */ | 153 */ |
| 131 getIndexForItemElement_: function(el) { | 154 getIndexForItemElement_: function(el) { |
| 132 return this.$.bookmarksCard.modelForElement(el).index; | 155 return this.$.list.modelForElement(el).index; |
| 133 }, | 156 }, |
| 134 | 157 |
| 135 /** | 158 /** |
| 136 * @param {Event} e | 159 * @param {Event} e |
| 137 * @private | 160 * @private |
| 138 */ | 161 */ |
| 139 onOpenItemMenu_: function(e) { | 162 onOpenItemMenu_: function(e) { |
| 140 var index = this.displayedIds_.indexOf( | |
| 141 /** @type {BookmarksItemElement} */ (e.path[0]).itemId); | |
| 142 var list = this.$.bookmarksCard; | |
| 143 // If the item is not visible, scroll to it before rendering the menu. | 163 // If the item is not visible, scroll to it before rendering the menu. |
| 144 if (index < list.firstVisibleIndex || index > list.lastVisibleIndex) | 164 this.scrollToId_(/** @type {BookmarksItemElement} */ (e.path[0]).itemId); |
| 145 list.scrollToIndex(index); | |
| 146 }, | 165 }, |
| 147 | 166 |
| 148 /** | 167 /** |
| 168 * Highlight a list of items by selecting them, scrolling them into view and |
| 169 * focusing the first item. |
| 170 * @param {Event} e |
| 171 * @private |
| 172 */ |
| 173 onHighlightItems_: function(e) { |
| 174 // Ensure that we only select items which are actually being displayed. |
| 175 // This should only matter if an unrelated update to the bookmark model |
| 176 // happens with the perfect timing to end up in a tracked batch update. |
| 177 var toHighlight = /** @type {!Array<string>} */ |
| 178 (e.detail.filter((item) => this.displayedIds_.indexOf(item) != -1)); |
| 179 |
| 180 assert(toHighlight.length > 0); |
| 181 var leadId = toHighlight[0]; |
| 182 this.dispatch( |
| 183 bookmarks.actions.selectAll(toHighlight, this.getState(), leadId)); |
| 184 |
| 185 // Allow iron-list time to render additions to the list. |
| 186 this.async(function() { |
| 187 this.scrollToId_(leadId); |
| 188 var leadIndex = this.displayedIds_.indexOf(leadId); |
| 189 assert(leadIndex != -1); |
| 190 this.$.list.focusItem(leadIndex); |
| 191 }); |
| 192 }, |
| 193 |
| 194 /** |
| 149 * @param {KeyboardEvent} e | 195 * @param {KeyboardEvent} e |
| 150 * @private | 196 * @private |
| 151 */ | 197 */ |
| 152 onItemKeydown_: function(e) { | 198 onItemKeydown_: function(e) { |
| 153 var handled = true; | 199 var handled = true; |
| 154 var list = this.$.bookmarksCard; | 200 var list = this.$.list; |
| 155 var focusMoved = false; | 201 var focusMoved = false; |
| 156 var focusedIndex = | 202 var focusedIndex = |
| 157 this.getIndexForItemElement_(/** @type {HTMLElement} */ (e.target)); | 203 this.getIndexForItemElement_(/** @type {HTMLElement} */ (e.target)); |
| 158 var oldFocusedIndex = focusedIndex; | 204 var oldFocusedIndex = focusedIndex; |
| 159 var cursorModifier = cr.isMac ? e.metaKey : e.ctrlKey; | 205 var cursorModifier = cr.isMac ? e.metaKey : e.ctrlKey; |
| 160 if (e.key == 'ArrowUp') { | 206 if (e.key == 'ArrowUp') { |
| 161 focusedIndex--; | 207 focusedIndex--; |
| 162 focusMoved = true; | 208 focusMoved = true; |
| 163 } else if (e.key == 'ArrowDown') { | 209 } else if (e.key == 'ArrowDown') { |
| 164 focusedIndex++; | 210 focusedIndex++; |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 214 | 260 |
| 215 if (!handled) { | 261 if (!handled) { |
| 216 handled = bookmarks.CommandManager.getInstance().handleKeyEvent( | 262 handled = bookmarks.CommandManager.getInstance().handleKeyEvent( |
| 217 e, this.getState().selection.items); | 263 e, this.getState().selection.items); |
| 218 } | 264 } |
| 219 | 265 |
| 220 if (handled) | 266 if (handled) |
| 221 e.stopPropagation(); | 267 e.stopPropagation(); |
| 222 }, | 268 }, |
| 223 }); | 269 }); |
| OLD | NEW |