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 |