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

Side by Side Diff: chrome/browser/resources/md_bookmarks/store.js

Issue 2639453002: [MD Bookmarks] Add Select for Bookmarks. (Closed)
Patch Set: clean up Created 3 years, 11 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 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 var BookmarksStore = Polymer({ 5 var BookmarksStore = Polymer({
6 is: 'bookmarks-store', 6 is: 'bookmarks-store',
7 7
8 properties: { 8 properties: {
9 /** @type {BookmarkTreeNode} */ 9 /** @type {BookmarkTreeNode} */
10 rootNode: { 10 rootNode: {
(...skipping 19 matching lines...) Expand all
30 * selected folder. 30 * selected folder.
31 * @type {Array<BookmarkTreeNode>} 31 * @type {Array<BookmarkTreeNode>}
32 */ 32 */
33 displayedList: { 33 displayedList: {
34 type: Array, 34 type: Array,
35 notify: true, 35 notify: true,
36 readOnly: true, 36 readOnly: true,
37 }, 37 },
38 38
39 idToNodeMap_: Object, 39 idToNodeMap_: Object,
40
41 prevSelectedItemIndex_: Number,
calamity 2017/01/24 06:39:17 Hmm. I got confused by this thinking it was the ac
jiaxi 2017/01/25 03:26:10 Done.
40 }, 42 },
41 43
42 /** @private {Object} */ 44 /** @private {Object} */
43 documentListeners_: null, 45 documentListeners_: null,
44 46
45 /** @override */ 47 /** @override */
46 attached: function() { 48 attached: function() {
47 this.documentListeners_ = { 49 this.documentListeners_ = {
48 'selected-folder-changed': this.onSelectedFolderChanged_.bind(this),
49 'folder-open-changed': this.onFolderOpenChanged_.bind(this), 50 'folder-open-changed': this.onFolderOpenChanged_.bind(this),
50 'search-term-changed': this.onSearchTermChanged_.bind(this), 51 'search-term-changed': this.onSearchTermChanged_.bind(this),
52 'select-item': this.onItemSelected_.bind(this),
53 'selected-folder-changed': this.onSelectedFolderChanged_.bind(this),
51 }; 54 };
52 for (var event in this.documentListeners_) 55 for (var event in this.documentListeners_)
53 document.addEventListener(event, this.documentListeners_[event]); 56 document.addEventListener(event, this.documentListeners_[event]);
54 }, 57 },
55 58
56 /** @override */ 59 /** @override */
57 detached: function() { 60 detached: function() {
58 for (var event in this.documentListeners_) 61 for (var event in this.documentListeners_)
59 document.removeEventListener(event, this.documentListeners_[event]); 62 document.removeEventListener(event, this.documentListeners_[event]);
60 }, 63 },
(...skipping 23 matching lines...) Expand all
84 this.idToNodeMap_ = {}; 87 this.idToNodeMap_ = {};
85 this.rootNode.path = 'rootNode'; 88 this.rootNode.path = 'rootNode';
86 BookmarksStore.generatePaths(rootNode, 0); 89 BookmarksStore.generatePaths(rootNode, 0);
87 BookmarksStore.initNodes(this.rootNode, this.idToNodeMap_); 90 BookmarksStore.initNodes(this.rootNode, this.idToNodeMap_);
88 this.fire('selected-folder-changed', this.rootNode.children[0].id); 91 this.fire('selected-folder-changed', this.rootNode.children[0].id);
89 }, 92 },
90 93
91 /** @private */ 94 /** @private */
92 deselectFolders_: function() { 95 deselectFolders_: function() {
93 this.unlinkPaths('displayedList'); 96 this.unlinkPaths('displayedList');
94 this.set(this.idToNodeMap_[this.selectedId].path + '.isSelected', false); 97 this.set(
98 this.idToNodeMap_[this.selectedId].path + '.isSelectedFolder', false);
95 this.selectedId = null; 99 this.selectedId = null;
96 }, 100 },
97 101
98 /** 102 /**
99 * @param {BookmarkTreeNode} folder 103 * @param {BookmarkTreeNode} folder
100 * @private 104 * @private
101 * @return {boolean} 105 * @return {boolean}
102 */ 106 */
103 isAncestorOfSelected_: function(folder) { 107 isAncestorOfSelected_: function(folder) {
104 if (!this.selectedId) 108 if (!this.selectedId)
105 return false; 109 return false;
106 110
107 var selectedNode = this.idToNodeMap_[this.selectedId]; 111 var selectedNode = this.idToNodeMap_[this.selectedId];
108 return selectedNode.path.startsWith(folder.path); 112 return selectedNode.path.startsWith(folder.path);
109 }, 113 },
110 114
111 /** @private */ 115 /** @private */
112 updateSearchDisplay_: function() { 116 updateSearchDisplay_: function() {
113 if (this.searchTerm == '') { 117 if (this.searchTerm == '') {
114 this.fire('selected-folder-changed', this.rootNode.children[0].id); 118 this.fire('selected-folder-changed', this.rootNode.children[0].id);
115 } else { 119 } else {
116 chrome.bookmarks.search(this.searchTerm, function(results) { 120 chrome.bookmarks.search(this.searchTerm, function(results) {
121 this.clearSelectedItems_();
122 this.prevSelectedItemIndex_ = undefined;
123
117 if (this.selectedId) 124 if (this.selectedId)
118 this.deselectFolders_(); 125 this.deselectFolders_();
119 126
127 for (var i = 0; i < results.length; i++)
128 results[i].searchResultIndex = i;
129
120 this._setDisplayedList(results); 130 this._setDisplayedList(results);
121 }.bind(this)); 131 }.bind(this));
122 } 132 }
123 }, 133 },
124 134
125 /** @private */ 135 /** @private */
126 updateSelectedDisplay_: function() { 136 updateSelectedDisplay_: function() {
127 // Don't change to the selected display if ID was cleared. 137 // Don't change to the selected display if ID was cleared.
128 if (!this.selectedId) 138 if (!this.selectedId)
129 return; 139 return;
130 140
141 this.clearSelectedItems_();
142 this.prevSelectedItemIndex_ = undefined;
143
131 var selectedNode = this.idToNodeMap_[this.selectedId]; 144 var selectedNode = this.idToNodeMap_[this.selectedId];
132 this.linkPaths('displayedList', selectedNode.path + '.children'); 145 this.linkPaths('displayedList', selectedNode.path + '.children');
133 this._setDisplayedList(selectedNode.children); 146 this._setDisplayedList(selectedNode.children);
134 }, 147 },
135 148
136 /** 149 /**
137 * Remove all descendants of a given node from the map. 150 * Remove all descendants of a given node from the map.
138 * @param {string} id 151 * @param {string} id
139 * @private 152 * @private
140 */ 153 */
141 removeDescendantsFromMap_: function(id) { 154 removeDescendantsFromMap_: function(id) {
142 var node = this.idToNodeMap_[id]; 155 var node = this.idToNodeMap_[id];
143 if (!node) 156 if (!node)
144 return; 157 return;
145 158
146 if (node.children) { 159 if (node.children) {
147 for (var i = 0; i < node.children.length; i++) 160 for (var i = 0; i < node.children.length; i++)
148 this.removeDescendantsFromMap_(node.children[i].id); 161 this.removeDescendantsFromMap_(node.children[i].id);
149 } 162 }
150 delete this.idToNodeMap_[id]; 163 delete this.idToNodeMap_[id];
151 }, 164 },
152 165
166 /**
167 * Remove all selected items in the list.
168 * @private
169 */
170 clearSelectedItems_: function() {
171 if (!this.displayedList)
172 return;
173
174 for (var i = 0; i < this.displayedList.length; i++) {
175 if (!this.displayedList[i].isSelectedItem)
176 continue;
177
178 this.set('displayedList.#' + i + '.isSelectedItem', false);
179 }
180 },
181
182 /**
183 * Return the index in the search result of an item.
184 * @param {BookmarkTreeNode} item
185 * @return {number}
186 * @private
187 */
188 getIndexInList_: function(item) {
189 if (this.searchTerm)
190 return item.searchResultIndex;
191
192 return item.index;
calamity 2017/01/24 06:39:17 I think this can just be return item.searchResultI
jiaxi 2017/01/25 03:26:10 This will return undefined if we select the first
193 },
194
195 isInDisplayedList_: function(id) {
196 if (this.searchTerm) {
197 for (var i = 0; i < this.displayedList.length; i++) {
198 if (this.displayedList[i].id == id)
199 return true;
200 }
201 return false;
202 }
calamity 2017/01/24 06:39:17 You may want to think about caching this informati
jiaxi 2017/01/25 03:26:10 Done.
203
204 return (this.idToNodeMap_[id].parentId == this.selectedId);
calamity 2017/01/24 06:39:17 nit: No parens.
jiaxi 2017/01/25 03:26:10 Done.
205 },
153 //////////////////////////////////////////////////////////////////////////////// 206 ////////////////////////////////////////////////////////////////////////////////
154 // bookmarks-store, bookmarks API event listeners: 207 // bookmarks-store, bookmarks API event listeners:
155 208
156 /** 209 /**
157 * Callback for when a bookmark node is removed. 210 * Callback for when a bookmark node is removed.
158 * If a folder is selected or is an ancestor of a selected folder, the parent 211 * If a folder is selected or is an ancestor of a selected folder, the parent
159 * of the removed folder will be selected. 212 * of the removed folder will be selected.
160 * @param {string} id The id of the removed bookmark node. 213 * @param {string} id The id of the removed bookmark node.
161 * @param {!{index: number, 214 * @param {!{index: number,
162 * parentId: string, 215 * parentId: string,
163 * node: BookmarkTreeNode}} removeInfo 216 * node: BookmarkTreeNode}} removeInfo
164 */ 217 */
165 onBookmarkRemoved_: function(id, removeInfo) { 218 onBookmarkRemoved_: function(id, removeInfo) {
166 if (this.isAncestorOfSelected_(this.idToNodeMap_[id])) { 219 chrome.bookmarks.getSubTree(removeInfo.parentId, function(parentNode) {
calamity 2017/01/24 06:39:17 Call this parentNodes so it's not confused with th
jiaxi 2017/01/25 03:26:11 Done.
167 this.fire('selected-folder-changed', removeInfo.parentId); 220 var parentNode = parentNode[0];
168 } 221 var isAncestor = this.isAncestorOfSelected_(this.idToNodeMap_[id]);
222 var isInDisplayedList = this.isInDisplayedList_(id);
169 223
170 var parentNode = this.idToNodeMap_[removeInfo.parentId]; 224 // Update the tree.
171 this.splice(parentNode.path + '.children', removeInfo.index, 1); 225 this.removeDescendantsFromMap_(id);
calamity 2017/01/24 06:39:17 Should this be replacing the parent node? Is this
jiaxi 2017/01/25 03:26:11 Discussed offline. This will remove all the extra
172 this.removeDescendantsFromMap_(id); 226 parentNode.path = this.idToNodeMap_[parentNode.id].path;
173 BookmarksStore.generatePaths(parentNode, removeInfo.index); 227 BookmarksStore.generatePaths(parentNode, 0);
228 BookmarksStore.initNodes(parentNode, this.idToNodeMap_);
229 this.set(parentNode.path, parentNode)
174 230
175 // Regenerate the search list if its displayed. 231 // Update selectedId if the removed node is an ancestor of the current
176 if (this.searchTerm) 232 // selected node.
177 this.updateSearchDisplay_(); 233 if (isAncestor)
234 this.fire('selected-folder-changed', removeInfo.parentId);
235
236 // Only update the displayedList if the removed node is in the
237 // displayedList.
238 if (isInDisplayedList) {
calamity 2017/01/24 06:39:17 Invert and early return.
jiaxi 2017/01/25 03:26:11 Done.
239 if (this.prevSelectedItemIndex_ == this.displayedList.length - 1)
240 this.prevSelectedItemIndex_--;
241
242 if (this.searchTerm) {
243 chrome.bookmarks.search(this.searchTerm, function(results) {
244 for (var i = 0; i < results.length; i++)
245 results[i].searchResultIndex = i;
calamity 2017/01/24 06:39:17 This code should really be shared with the searchi
jiaxi 2017/01/25 03:26:11 Done.
246
247 this._setDisplayedList(results);
248
249 this.set(
250 'displayedList.#' + this.prevSelectedItemIndex_ +
251 '.isSelectedItem',
252 true);
calamity 2017/01/24 06:39:17 What if prevSelectedItemIndex is null?
jiaxi 2017/01/25 03:26:11 The prevSelectedItemIndex(anchorIndex) can only be
253 }.bind(this));
254 } else {
255 this._setDisplayedList(parentNode.children);
256
257 this.set(
258 'displayedList.#' + this.prevSelectedItemIndex_ +
259 '.isSelectedItem',
260 true);
261 }
262 }
263 }.bind(this));
178 }, 264 },
179 265
180 /** 266 /**
181 * Called when the title of a bookmark changes. 267 * Called when the title of a bookmark changes.
182 * @param {string} id The id of changed bookmark node. 268 * @param {string} id The id of changed bookmark node.
183 * @param {!Object} changeInfo 269 * @param {!Object} changeInfo
184 */ 270 */
185 onBookmarkChanged_: function(id, changeInfo) { 271 onBookmarkChanged_: function(id, changeInfo) {
186 if (changeInfo.title) 272 if (changeInfo.title)
187 this.set(this.idToNodeMap_[id].path + '.title', changeInfo.title); 273 this.set(this.idToNodeMap_[id].path + '.title', changeInfo.title);
(...skipping 19 matching lines...) Expand all
207 * Selects the folder specified by the event and deselects the previously 293 * Selects the folder specified by the event and deselects the previously
208 * selected folder. 294 * selected folder.
209 * @param {CustomEvent} e 295 * @param {CustomEvent} e
210 * @private 296 * @private
211 */ 297 */
212 onSelectedFolderChanged_: function(e) { 298 onSelectedFolderChanged_: function(e) {
213 if (this.searchTerm) 299 if (this.searchTerm)
214 this.searchTerm = ''; 300 this.searchTerm = '';
215 301
216 // Deselect the old folder if defined. 302 // Deselect the old folder if defined.
217 if (this.selectedId) 303 if (this.selectedId && this.idToNodeMap_[this.selectedId])
218 this.set(this.idToNodeMap_[this.selectedId].path + '.isSelected', false); 304 this.set(
305 this.idToNodeMap_[this.selectedId].path + '.isSelectedFolder', false);
219 306
220 var selectedId = /** @type {string} */ (e.detail); 307 var selectedId = /** @type {string} */ (e.detail);
221 var newFolder = this.idToNodeMap_[selectedId]; 308 var newFolder = this.idToNodeMap_[selectedId];
222 this.set(newFolder.path + '.isSelected', true); 309 this.set(newFolder.path + '.isSelectedFolder', true);
223 this.selectedId = selectedId; 310 this.selectedId = selectedId;
224 }, 311 },
225 312
226 /** 313 /**
227 * Handles events that open and close folders. 314 * Handles events that open and close folders.
228 * @param {CustomEvent} e 315 * @param {CustomEvent} e
229 * @private 316 * @private
230 */ 317 */
231 onFolderOpenChanged_: function(e) { 318 onFolderOpenChanged_: function(e) {
232 var folder = this.idToNodeMap_[e.detail.id]; 319 var folder = this.idToNodeMap_[e.detail.id];
233 this.set(folder.path + '.isOpen', e.detail.open); 320 this.set(folder.path + '.isOpen', e.detail.open);
234 if (!folder.isOpen && this.isAncestorOfSelected_(folder)) 321 if (!folder.isOpen && this.isAncestorOfSelected_(folder))
235 this.fire('selected-folder-changed', folder.id); 322 this.fire('selected-folder-changed', folder.id);
236 }, 323 },
324
325 /**
326 * Select a single item in the list.
327 * @param {BookmarkTreeNode} item
328 * @private
329 */
330 onSingleItemSelected_: function(item) {
331 this.clearSelectedItems_();
332 this.prevSelectedItemIndex_ = this.getIndexInList_(item);
333 this.set(
334 'displayedList.#' + this.prevSelectedItemIndex_ + '.isSelectedItem',
335 true);
336 },
337
338 /**
339 * Select multiple items based on |prevSelectedItemIndex_| and the selected
340 * item. If |prevSelectedItemIndex_| is not set, single select the item.
341 * @param {BookmarkTreeNode} item
342 * @private
343 */
344 onMultipleItemsShiftSelected_: function(item) {
345 this.clearSelectedItems_();
346 var startIndex, endIndex;
347 if (this.prevSelectedItemIndex_ == undefined) {
348 this.prevSelectedItemIndex_ = this.getIndexInList_(item);
349 startIndex = this.prevSelectedItemIndex_;
350 endIndex = this.prevSelectedItemIndex_;
351 } else {
352 var currIndex = this.getIndexInList_(item);
calamity 2017/01/24 06:39:17 nit: selectedIndex.
jiaxi 2017/01/25 03:26:11 Done.
353 startIndex = Math.min(this.prevSelectedItemIndex_, currIndex);
354 endIndex = Math.max(this.prevSelectedItemIndex_, currIndex);
355 }
356 for (var i = startIndex; i <= endIndex; i++)
357 this.set('displayedList.#' + i + '.isSelectedItem', true);
358 },
359
360 /**
361 * Select multiple items with the index of the last elected item as
362 * |prevSelectedItemIndex_|.
363 * @param {BookmarkTreeNode} item
364 * @private
365 */
366 onMultipleItemsCtrlSelected_: function(item) {
367 this.prevSelectedItemIndex_ = this.getIndexInList_(item);
368 this.set(
369 'displayedList.#' + this.prevSelectedItemIndex_ + '.isSelectedItem',
370 true);
371 },
372
373 /**
374 * Select item according to keyboard behaviours.
375 * @param {CustomEvent} e
376 * @private
377 */
378 onItemSelected_: function(e) {
379 if (e.detail.shiftKey)
380 this.onMultipleItemsShiftSelected_(e.detail.item);
381 else if (e.detail.ctrlKey)
382 this.onMultipleItemsCtrlSelected_(e.detail.item);
383 else
384 this.onSingleItemSelected_(e.detail.item);
385 },
237 }); 386 });
238 387
239 //////////////////////////////////////////////////////////////////////////////// 388 ////////////////////////////////////////////////////////////////////////////////
240 // bookmarks-store, static methods: 389 // bookmarks-store, static methods:
241 390
242 /** 391 /**
243 * Stores the path from the store to a node inside the node. 392 * Stores the path from the store to a node inside the node.
244 * @param {BookmarkTreeNode} bookmarkNode 393 * @param {BookmarkTreeNode} bookmarkNode
245 * @param {number} startIndex 394 * @param {number} startIndex
246 */ 395 */
247 BookmarksStore.generatePaths = function(bookmarkNode, startIndex) { 396 BookmarksStore.generatePaths = function(bookmarkNode, startIndex) {
248 if (!bookmarkNode.children) 397 if (!bookmarkNode.children)
249 return; 398 return;
250 399
251 for (var i = startIndex; i < bookmarkNode.children.length; i++) { 400 for (var i = startIndex; i < bookmarkNode.children.length; i++) {
252 bookmarkNode.children[i].path = bookmarkNode.path + '.children.#' + i; 401 bookmarkNode.children[i].path = bookmarkNode.path + '.children.#' + i;
253 BookmarksStore.generatePaths(bookmarkNode.children[i], 0); 402 BookmarksStore.generatePaths(bookmarkNode.children[i], 0);
254 } 403 }
255 }; 404 };
256 405
257 /** 406 /**
258 * Initializes the nodes in the bookmarks tree as follows: 407 * Initializes the nodes in the bookmarks tree as follows:
259 * - Populates |idToNodeMap_| with a mapping of all node ids to their 408 * - Populates |idToNodeMap_| with a mapping of all node ids to their
260 * corresponding BookmarkTreeNode. 409 * corresponding BookmarkTreeNode.
261 * - Sets all the nodes to not selected and open by default. 410 * - Sets all the nodes to not selected and open by default.
262 * @param {BookmarkTreeNode} bookmarkNode 411 * @param {BookmarkTreeNode} bookmarkNode
263 * @param {Object=} idToNodeMap 412 * @param {Object=} idToNodeMap
264 */ 413 */
265 BookmarksStore.initNodes = function(bookmarkNode, idToNodeMap) { 414 BookmarksStore.initNodes = function(bookmarkNode, idToNodeMap) {
415 bookmarkNode.isSelectedItem = false;
266 if (idToNodeMap) 416 if (idToNodeMap)
267 idToNodeMap[bookmarkNode.id] = bookmarkNode; 417 idToNodeMap[bookmarkNode.id] = bookmarkNode;
268 418
269 if (bookmarkNode.url) 419 if (bookmarkNode.url)
270 return; 420 return;
271 421
272 bookmarkNode.isSelected = false; 422 bookmarkNode.isSelectedFolder = false;
273 bookmarkNode.isOpen = true; 423 bookmarkNode.isOpen = true;
274 for (var i = 0; i < bookmarkNode.children.length; i++) 424 for (var i = 0; i < bookmarkNode.children.length; i++)
275 BookmarksStore.initNodes(bookmarkNode.children[i], idToNodeMap); 425 BookmarksStore.initNodes(bookmarkNode.children[i], idToNodeMap);
276 }; 426 };
OLDNEW
« no previous file with comments | « chrome/browser/resources/md_bookmarks/sidebar.html ('k') | chrome/test/data/webui/md_bookmarks/item_test.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698