Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(86)

Side by Side Diff: chrome/browser/resources/md_downloads/manager.js

Issue 1375333004: MD Downloads: use <iron-list> to render download items (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@iron-list2
Patch Set: merge Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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 cr.define('downloads', function() { 5 cr.define('downloads', function() {
6 var Manager = Polymer({ 6 var Manager = Polymer({
7 is: 'downloads-manager', 7 is: 'downloads-manager',
8 8
9 properties: { 9 properties: {
10 hasDownloads_: { 10 hasDownloads_: {
11 type: Boolean, 11 type: Boolean,
12 value: false, 12 value: false,
13 }, 13 },
14
15 items_: {
16 type: Array,
17 },
14 }, 18 },
15 19
16 /** 20 /**
17 * @return {number} A guess at how many items could be visible at once.
18 * @private
19 */
20 guesstimateNumberOfVisibleItems_: function() {
21 var toolbarHeight = this.$.toolbar.offsetHeight;
22 return Math.floor((window.innerHeight - toolbarHeight) / 46) + 1;
23 },
24
25 /**
26 * @param {Event} e 21 * @param {Event} e
27 * @private 22 * @private
28 */ 23 */
29 onCanExecute_: function(e) { 24 onCanExecute_: function(e) {
30 e = /** @type {cr.ui.CanExecuteEvent} */(e); 25 e = /** @type {cr.ui.CanExecuteEvent} */(e);
31 switch (e.command.id) { 26 switch (e.command.id) {
32 case 'undo-command': 27 case 'undo-command':
33 e.canExecute = this.$.toolbar.canUndo(); 28 e.canExecute = this.$.toolbar.canUndo();
34 break; 29 break;
35 case 'clear-all-command': 30 case 'clear-all-command':
(...skipping 16 matching lines...) Expand all
52 /** @private */ 47 /** @private */
53 onLoad_: function() { 48 onLoad_: function() {
54 cr.ui.decorate('command', cr.ui.Command); 49 cr.ui.decorate('command', cr.ui.Command);
55 document.addEventListener('canExecute', this.onCanExecute_.bind(this)); 50 document.addEventListener('canExecute', this.onCanExecute_.bind(this));
56 document.addEventListener('command', this.onCommand_.bind(this)); 51 document.addEventListener('command', this.onCommand_.bind(this));
57 52
58 // Shows all downloads. 53 // Shows all downloads.
59 downloads.ActionService.getInstance().search(''); 54 downloads.ActionService.getInstance().search('');
60 }, 55 },
61 56
62 /** @private */
63 rebuildFocusGrid_: function() {
64 var activeElement = this.shadowRoot.activeElement;
65
66 var activeItem;
67 if (activeElement && activeElement.tagName == 'downloads-item')
68 activeItem = activeElement;
69
70 var activeControl = activeItem && activeItem.shadowRoot.activeElement;
71
72 /** @private {!cr.ui.FocusGrid} */
73 this.focusGrid_ = this.focusGrid_ || new cr.ui.FocusGrid;
74 this.focusGrid_.destroy();
75
76 var boundary = this.$['downloads-list'];
77
78 this.items_.forEach(function(item) {
79 var focusRow = new downloads.FocusRow(item.content, boundary);
80 this.focusGrid_.addRow(focusRow);
81
82 if (item == activeItem && !cr.ui.FocusRow.isFocusable(activeControl))
83 focusRow.getEquivalentElement(activeControl).focus();
84 }, this);
85
86 this.focusGrid_.ensureRowActive();
87 },
88
89 /** 57 /**
90 * @return {number} The number of downloads shown on the page. 58 * @return {number} The number of downloads shown on the page.
91 * @private 59 * @private
92 */ 60 */
93 size_: function() { 61 size_: function() {
94 return this.items_.length; 62 return this.items_.length;
95 }, 63 },
96 64
97 /** 65 /**
98 * Called when all items need to be updated. 66 * Called when all items need to be updated.
99 * @param {!Array<!downloads.Data>} list A list of new download data. 67 * @param {!Array<!downloads.Data>} list A list of new download data.
100 * @private 68 * @private
101 */ 69 */
102 updateAll_: function(list) { 70 updateAll_: function(list) {
103 var oldIdMap = this.idMap_ || {};
104
105 /** @private {!Object<!downloads.Item>} */ 71 /** @private {!Object<!downloads.Item>} */
106 this.idMap_ = {}; 72 this.idMap_ = {};
107 73
108 /** @private {!Array<!downloads.Item>} */ 74 var items = [];
109 this.items_ = [];
110
111 if (!this.iconLoader_) {
112 var guesstimate = Math.max(this.guesstimateNumberOfVisibleItems_(), 1);
113 /** @private {downloads.ThrottledIconLoader} */
114 this.iconLoader_ = new downloads.ThrottledIconLoader(guesstimate);
115 }
116 75
117 for (var i = 0; i < list.length; ++i) { 76 for (var i = 0; i < list.length; ++i) {
118 var data = list[i]; 77 var data = list[i];
119 var id = data.id; 78 var id = data.id;
120 79
121 // Re-use old items when possible (saves work, preserves focus). 80 this.idMap_[id] = i; // Associated by ID for fast lookup.
esprehn 2015/10/03 05:11:54 I hate right side comments, but I guess this is no
Dan Beam 2015/10/04 08:27:08 JS/closure is fine with them
122 var item = oldIdMap[id] || new downloads.Item(this.iconLoader_);
123 81
124 this.idMap_[id] = item; // Associated by ID for fast lookup. 82 var prev = list[i - 1];
125 this.items_.push(item); // Add to sorted list for order.
126 83
127 // Render |item| but don't actually add to the DOM yet. |this.items_| 84 items.push({
128 // must be fully created to be able to find the right spot to insert. 85 index: i,
129 item.update(data); 86 item: data,
130 87 hideDate: !!prev && prev.date_string == data.date_string,
131 // Collapse redundant dates. 88 });
132 var prev = list[i - 1];
133 item.hideDate = !!prev && prev.date_string == data.date_string;
134
135 delete oldIdMap[id];
136 } 89 }
137 90
138 // Remove stale, previously rendered items from the DOM. 91 this.items_ = items;
139 for (var id in oldIdMap) {
140 if (oldIdMap[id].parentNode)
141 oldIdMap[id].parentNode.removeChild(oldIdMap[id]);
142 delete oldIdMap[id];
143 }
144
145 for (var i = 0; i < this.items_.length; ++i) {
146 var item = this.items_[i];
147 if (item.parentNode) // Already in the DOM; skip.
148 continue;
149
150 var before = null;
151 // Find the next rendered item after this one, and insert before it.
152 for (var j = i + 1; !before && j < this.items_.length; ++j) {
153 if (this.items_[j].parentNode)
154 before = this.items_[j];
155 }
156 // If |before| is null, |item| will just get added at the end.
157 this.$['downloads-list'].insertBefore(item, before);
158 }
159 92
160 var hasDownloads = this.size_() > 0; 93 var hasDownloads = this.size_() > 0;
161 if (!hasDownloads) { 94 if (!hasDownloads) {
162 var isSearching = downloads.ActionService.getInstance().isSearching(); 95 var isSearching = downloads.ActionService.getInstance().isSearching();
163 var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads'; 96 var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads';
164 this.$['no-downloads'].querySelector('span').textContent = 97 this.$['no-downloads'].querySelector('span').textContent =
165 loadTimeData.getString(messageToShow); 98 loadTimeData.getString(messageToShow);
166 } 99 }
167 this.hasDownloads_ = hasDownloads; 100 this.hasDownloads_ = hasDownloads;
168 101
169 if (loadTimeData.getBoolean('allowDeletingHistory')) 102 if (loadTimeData.getBoolean('allowDeletingHistory'))
170 this.$.toolbar.downloadsShowing = this.hasDownloads_; 103 this.$.toolbar.downloadsShowing = this.hasDownloads_;
171 104
172 this.$.panel.classList.remove('loading'); 105 this.$.panel.classList.remove('loading');
173
174 var allReady = this.items_.map(function(i) { return i.readyPromise; });
175 Promise.all(allReady).then(this.rebuildFocusGrid_.bind(this));
176 }, 106 },
177 107
178 /** 108 /**
179 * @param {!downloads.Data} data 109 * @param {!downloads.Data} data
180 * @private 110 * @private
181 */ 111 */
182 updateItem_: function(data) { 112 updateItem_: function(data) {
183 var item = this.idMap_[data.id]; 113 this.set('items_.' + this.idMap_[data.id] + '.item', data);
esprehn 2015/10/03 05:11:54 idMap is more like indexMap right?
Dan Beam 2015/10/04 08:27:08 Done.
184 114 this.$['downloads-list'].fire('iron-resize');
185 var activeControl = this.shadowRoot.activeElement == item ?
186 item.shadowRoot.activeElement : null;
187
188 item.update(data);
189
190 this.async(function() {
191 if (activeControl && !cr.ui.FocusRow.isFocusable(activeControl)) {
192 var focusRow = this.focusGrid_.getRowForRoot(item.content);
193 focusRow.getEquivalentElement(activeControl).focus();
194 }
195 }.bind(this));
196 }, 115 },
197 }); 116 });
198 117
199 Manager.size = function() { 118 Manager.size = function() {
200 return document.querySelector('downloads-manager').size_(); 119 return document.querySelector('downloads-manager').size_();
201 }; 120 };
202 121
203 Manager.updateAll = function(list) { 122 Manager.updateAll = function(list) {
204 document.querySelector('downloads-manager').updateAll_(list); 123 document.querySelector('downloads-manager').updateAll_(list);
205 }; 124 };
206 125
207 Manager.updateItem = function(item) { 126 Manager.updateItem = function(item) {
208 document.querySelector('downloads-manager').updateItem_(item); 127 document.querySelector('downloads-manager').updateItem_(item);
209 }; 128 };
210 129
211 Manager.onLoad = function() { 130 Manager.onLoad = function() {
212 document.querySelector('downloads-manager').onLoad_(); 131 document.querySelector('downloads-manager').onLoad_();
213 }; 132 };
214 133
215 return {Manager: Manager}; 134 return {Manager: Manager};
216 }); 135 });
217 136
218 window.addEventListener('load', downloads.Manager.onLoad); 137 window.addEventListener('load', downloads.Manager.onLoad);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698