OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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('cr.ui', function() { | 5 cr.define('cr.ui', function() { |
6 // require cr.ui.define | 6 // require cr.ui.define |
7 // require cr.ui.limitInputWidth | 7 // require cr.ui.limitInputWidth |
8 | 8 |
9 /** | 9 /** |
10 * The number of pixels to indent per level. | 10 * The number of pixels to indent per level. |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
57 this.addEventListener('mousedown', this.handleMouseDown); | 57 this.addEventListener('mousedown', this.handleMouseDown); |
58 this.addEventListener('dblclick', this.handleDblClick); | 58 this.addEventListener('dblclick', this.handleDblClick); |
59 this.addEventListener('keydown', this.handleKeyDown); | 59 this.addEventListener('keydown', this.handleKeyDown); |
60 | 60 |
61 this.setAttribute('role', 'group'); | 61 this.setAttribute('role', 'group'); |
62 }, | 62 }, |
63 | 63 |
64 /** | 64 /** |
65 * Returns the tree item that are children of this tree. | 65 * Returns the tree item that are children of this tree. |
66 */ | 66 */ |
67 get items() { return this.children; }, | 67 get items() { |
| 68 return this.children; |
| 69 }, |
68 | 70 |
69 /** | 71 /** |
70 * Adds a tree item to the tree. | 72 * Adds a tree item to the tree. |
71 * @param {!cr.ui.TreeItem} treeItem The item to add. | 73 * @param {!cr.ui.TreeItem} treeItem The item to add. |
72 */ | 74 */ |
73 add: function(treeItem) { this.addAt(treeItem, 0xffffffff); }, | 75 add: function(treeItem) { |
| 76 this.addAt(treeItem, 0xffffffff); |
| 77 }, |
74 | 78 |
75 /** | 79 /** |
76 * Adds a tree item at the given index. | 80 * Adds a tree item at the given index. |
77 * @param {!cr.ui.TreeItem} treeItem The item to add. | 81 * @param {!cr.ui.TreeItem} treeItem The item to add. |
78 * @param {number} index The index where we want to add the item. | 82 * @param {number} index The index where we want to add the item. |
79 */ | 83 */ |
80 addAt: function(treeItem, index) { | 84 addAt: function(treeItem, index) { |
81 this.insertBefore(treeItem, this.children[index]); | 85 this.insertBefore(treeItem, this.children[index]); |
82 treeItem.setDepth_(this.depth + 1); | 86 treeItem.setDepth_(this.depth + 1); |
83 }, | 87 }, |
84 | 88 |
85 /** | 89 /** |
86 * Removes a tree item child. | 90 * Removes a tree item child. |
87 * | 91 * |
88 * TODO(dbeam): this method now conflicts with HTMLElement#remove(), which | 92 * TODO(dbeam): this method now conflicts with HTMLElement#remove(), which |
89 * is why the @param is optional. Rename. | 93 * is why the @param is optional. Rename. |
90 * | 94 * |
91 * @param {!cr.ui.TreeItem=} treeItem The tree item to remove. | 95 * @param {!cr.ui.TreeItem=} treeItem The tree item to remove. |
92 */ | 96 */ |
93 remove: function(treeItem) { | 97 remove: function(treeItem) { |
94 this.removeChild(/** @type {!cr.ui.TreeItem} */ (treeItem)); | 98 this.removeChild(/** @type {!cr.ui.TreeItem} */ (treeItem)); |
95 }, | 99 }, |
96 | 100 |
97 /** | 101 /** |
98 * The depth of the node. This is 0 for the tree itself. | 102 * The depth of the node. This is 0 for the tree itself. |
99 * @type {number} | 103 * @type {number} |
100 */ | 104 */ |
101 get depth() { return 0; }, | 105 get depth() { |
| 106 return 0; |
| 107 }, |
102 | 108 |
103 /** | 109 /** |
104 * Handles click events on the tree and forwards the event to the relevant | 110 * Handles click events on the tree and forwards the event to the relevant |
105 * tree items as necesary. | 111 * tree items as necesary. |
106 * @param {Event} e The click event object. | 112 * @param {Event} e The click event object. |
107 */ | 113 */ |
108 handleClick: function(e) { | 114 handleClick: function(e) { |
109 var treeItem = findTreeItem(/** @type {!Node} */ (e.target)); | 115 var treeItem = findTreeItem(/** @type {!Node} */ (e.target)); |
110 if (treeItem) | 116 if (treeItem) |
111 treeItem.handleClick(e); | 117 treeItem.handleClick(e); |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 if (itemToSelect) { | 185 if (itemToSelect) { |
180 itemToSelect.selected = true; | 186 itemToSelect.selected = true; |
181 e.preventDefault(); | 187 e.preventDefault(); |
182 } | 188 } |
183 }, | 189 }, |
184 | 190 |
185 /** | 191 /** |
186 * The selected tree item or null if none. | 192 * The selected tree item or null if none. |
187 * @type {cr.ui.TreeItem} | 193 * @type {cr.ui.TreeItem} |
188 */ | 194 */ |
189 get selectedItem() { return this.selectedItem_ || null; }, | 195 get selectedItem() { |
| 196 return this.selectedItem_ || null; |
| 197 }, |
190 set selectedItem(item) { | 198 set selectedItem(item) { |
191 var oldSelectedItem = this.selectedItem_; | 199 var oldSelectedItem = this.selectedItem_; |
192 if (oldSelectedItem != item) { | 200 if (oldSelectedItem != item) { |
193 // Set the selectedItem_ before deselecting the old item since we only | 201 // Set the selectedItem_ before deselecting the old item since we only |
194 // want one change when moving between items. | 202 // want one change when moving between items. |
195 this.selectedItem_ = item; | 203 this.selectedItem_ = item; |
196 | 204 |
197 if (oldSelectedItem) | 205 if (oldSelectedItem) |
198 oldSelectedItem.selected = false; | 206 oldSelectedItem.selected = false; |
199 | 207 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
275 decorate: function() { | 283 decorate: function() { |
276 var labelId = | 284 var labelId = |
277 'tree-item-label-autogen-id-' + treeItemAutoGeneratedIdCounter; | 285 'tree-item-label-autogen-id-' + treeItemAutoGeneratedIdCounter; |
278 this.labelElement.id = labelId; | 286 this.labelElement.id = labelId; |
279 this.setAttribute('aria-labelledby', labelId); | 287 this.setAttribute('aria-labelledby', labelId); |
280 }, | 288 }, |
281 | 289 |
282 /** | 290 /** |
283 * The tree items children. | 291 * The tree items children. |
284 */ | 292 */ |
285 get items() { return this.lastElementChild.children; }, | 293 get items() { |
| 294 return this.lastElementChild.children; |
| 295 }, |
286 | 296 |
287 /** | 297 /** |
288 * The depth of the tree item. | 298 * The depth of the tree item. |
289 * @type {number} | 299 * @type {number} |
290 */ | 300 */ |
291 depth_: 0, get depth() { return this.depth_; }, | 301 depth_: 0, |
| 302 get depth() { |
| 303 return this.depth_; |
| 304 }, |
292 | 305 |
293 /** | 306 /** |
294 * Sets the depth. | 307 * Sets the depth. |
295 * @param {number} depth The new depth. | 308 * @param {number} depth The new depth. |
296 * @private | 309 * @private |
297 */ | 310 */ |
298 setDepth_: function(depth) { | 311 setDepth_: function(depth) { |
299 if (depth != this.depth_) { | 312 if (depth != this.depth_) { |
300 this.rowElement.style.WebkitPaddingStart = | 313 this.rowElement.style.WebkitPaddingStart = |
301 Math.max(0, depth - 1) * INDENT + 'px'; | 314 Math.max(0, depth - 1) * INDENT + 'px'; |
302 this.depth_ = depth; | 315 this.depth_ = depth; |
303 var items = this.items; | 316 var items = this.items; |
304 for (var i = 0, item; item = items[i]; i++) { | 317 for (var i = 0, item; item = items[i]; i++) { |
305 item.setDepth_(depth + 1); | 318 item.setDepth_(depth + 1); |
306 } | 319 } |
307 } | 320 } |
308 }, | 321 }, |
309 | 322 |
310 /** | 323 /** |
311 * Adds a tree item as a child. | 324 * Adds a tree item as a child. |
312 * @param {!cr.ui.TreeItem} child The child to add. | 325 * @param {!cr.ui.TreeItem} child The child to add. |
313 */ | 326 */ |
314 add: function(child) { this.addAt(child, 0xffffffff); }, | 327 add: function(child) { |
| 328 this.addAt(child, 0xffffffff); |
| 329 }, |
315 | 330 |
316 /** | 331 /** |
317 * Adds a tree item as a child at a given index. | 332 * Adds a tree item as a child at a given index. |
318 * @param {!cr.ui.TreeItem} child The child to add. | 333 * @param {!cr.ui.TreeItem} child The child to add. |
319 * @param {number} index The index where to add the child. | 334 * @param {number} index The index where to add the child. |
320 */ | 335 */ |
321 addAt: function(child, index) { | 336 addAt: function(child, index) { |
322 this.lastElementChild.insertBefore(child, this.items[index]); | 337 this.lastElementChild.insertBefore(child, this.items[index]); |
323 if (this.items.length == 1) | 338 if (this.items.length == 1) |
324 this.hasChildren = true; | 339 this.hasChildren = true; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
363 while (t && !(t instanceof Tree)) { | 378 while (t && !(t instanceof Tree)) { |
364 t = t.parentItem; | 379 t = t.parentItem; |
365 } | 380 } |
366 return t; | 381 return t; |
367 }, | 382 }, |
368 | 383 |
369 /** | 384 /** |
370 * Whether the tree item is expanded or not. | 385 * Whether the tree item is expanded or not. |
371 * @type {boolean} | 386 * @type {boolean} |
372 */ | 387 */ |
373 get expanded() { return this.hasAttribute('expanded'); }, | 388 get expanded() { |
| 389 return this.hasAttribute('expanded'); |
| 390 }, |
374 set expanded(b) { | 391 set expanded(b) { |
375 if (this.expanded == b) | 392 if (this.expanded == b) |
376 return; | 393 return; |
377 | 394 |
378 var treeChildren = this.lastElementChild; | 395 var treeChildren = this.lastElementChild; |
379 | 396 |
380 if (b) { | 397 if (b) { |
381 if (this.mayHaveChildren_) { | 398 if (this.mayHaveChildren_) { |
382 this.setAttribute('expanded', ''); | 399 this.setAttribute('expanded', ''); |
383 this.setAttribute('aria-expanded', 'true'); | 400 this.setAttribute('aria-expanded', 'true'); |
(...skipping 26 matching lines...) Expand all Loading... |
410 while (pi && !(pi instanceof Tree)) { | 427 while (pi && !(pi instanceof Tree)) { |
411 pi.expanded = true; | 428 pi.expanded = true; |
412 pi = pi.parentItem; | 429 pi = pi.parentItem; |
413 } | 430 } |
414 }, | 431 }, |
415 | 432 |
416 /** | 433 /** |
417 * The element representing the row that gets highlighted. | 434 * The element representing the row that gets highlighted. |
418 * @type {!HTMLElement} | 435 * @type {!HTMLElement} |
419 */ | 436 */ |
420 get rowElement() { return this.firstElementChild; }, | 437 get rowElement() { |
| 438 return this.firstElementChild; |
| 439 }, |
421 | 440 |
422 /** | 441 /** |
423 * The element containing the label text and the icon. | 442 * The element containing the label text and the icon. |
424 * @type {!HTMLElement} | 443 * @type {!HTMLElement} |
425 */ | 444 */ |
426 get labelElement() { return this.firstElementChild.lastElementChild; }, | 445 get labelElement() { |
| 446 return this.firstElementChild.lastElementChild; |
| 447 }, |
427 | 448 |
428 /** | 449 /** |
429 * The label text. | 450 * The label text. |
430 * @type {string} | 451 * @type {string} |
431 */ | 452 */ |
432 get label() { return this.labelElement.textContent; }, | 453 get label() { |
433 set label(s) { this.labelElement.textContent = s; }, | 454 return this.labelElement.textContent; |
| 455 }, |
| 456 set label(s) { |
| 457 this.labelElement.textContent = s; |
| 458 }, |
434 | 459 |
435 /** | 460 /** |
436 * The URL for the icon. | 461 * The URL for the icon. |
437 * @type {string} | 462 * @type {string} |
438 */ | 463 */ |
439 get icon() { | 464 get icon() { |
440 return getComputedStyle(this.labelElement).backgroundImage.slice(4, -1); | 465 return getComputedStyle(this.labelElement).backgroundImage.slice(4, -1); |
441 }, | 466 }, |
442 set icon(icon) { | 467 set icon(icon) { |
443 return this.labelElement.style.backgroundImage = url(icon); | 468 return this.labelElement.style.backgroundImage = url(icon); |
444 }, | 469 }, |
445 | 470 |
446 /** | 471 /** |
447 * Whether the tree item is selected or not. | 472 * Whether the tree item is selected or not. |
448 * @type {boolean} | 473 * @type {boolean} |
449 */ | 474 */ |
450 get selected() { return this.hasAttribute('selected'); }, | 475 get selected() { |
| 476 return this.hasAttribute('selected'); |
| 477 }, |
451 set selected(b) { | 478 set selected(b) { |
452 if (this.selected == b) | 479 if (this.selected == b) |
453 return; | 480 return; |
454 var rowItem = this.firstElementChild; | 481 var rowItem = this.firstElementChild; |
455 var tree = this.tree; | 482 var tree = this.tree; |
456 if (b) { | 483 if (b) { |
457 this.setAttribute('selected', ''); | 484 this.setAttribute('selected', ''); |
458 rowItem.setAttribute('selected', ''); | 485 rowItem.setAttribute('selected', ''); |
459 this.reveal(); | 486 this.reveal(); |
460 this.labelElement.scrollIntoViewIfNeeded(false); | 487 this.labelElement.scrollIntoViewIfNeeded(false); |
461 if (tree) | 488 if (tree) |
462 tree.selectedItem = this; | 489 tree.selectedItem = this; |
463 } else { | 490 } else { |
464 this.removeAttribute('selected'); | 491 this.removeAttribute('selected'); |
465 rowItem.removeAttribute('selected'); | 492 rowItem.removeAttribute('selected'); |
466 if (tree && tree.selectedItem == this) | 493 if (tree && tree.selectedItem == this) |
467 tree.selectedItem = null; | 494 tree.selectedItem = null; |
468 } | 495 } |
469 }, | 496 }, |
470 | 497 |
471 /** | 498 /** |
472 * Whether the tree item has children. | 499 * Whether the tree item has children. |
473 * @type {boolean} | 500 * @type {boolean} |
474 */ | 501 */ |
475 get mayHaveChildren_() { return this.hasAttribute('may-have-children'); }, | 502 get mayHaveChildren_() { |
| 503 return this.hasAttribute('may-have-children'); |
| 504 }, |
476 set mayHaveChildren_(b) { | 505 set mayHaveChildren_(b) { |
477 var rowItem = this.firstElementChild; | 506 var rowItem = this.firstElementChild; |
478 if (b) { | 507 if (b) { |
479 this.setAttribute('may-have-children', ''); | 508 this.setAttribute('may-have-children', ''); |
480 rowItem.setAttribute('may-have-children', ''); | 509 rowItem.setAttribute('may-have-children', ''); |
481 } else { | 510 } else { |
482 this.removeAttribute('may-have-children'); | 511 this.removeAttribute('may-have-children'); |
483 rowItem.removeAttribute('may-have-children'); | 512 rowItem.removeAttribute('may-have-children'); |
484 } | 513 } |
485 }, | 514 }, |
486 | 515 |
487 /** | 516 /** |
488 * Whether the tree item has children. | 517 * Whether the tree item has children. |
489 * @type {boolean} | 518 * @type {boolean} |
490 */ | 519 */ |
491 get hasChildren() { return !!this.items[0]; }, | 520 get hasChildren() { |
| 521 return !!this.items[0]; |
| 522 }, |
492 | 523 |
493 /** | 524 /** |
494 * Whether the tree item has children. | 525 * Whether the tree item has children. |
495 * @type {boolean} | 526 * @type {boolean} |
496 */ | 527 */ |
497 set hasChildren(b) { | 528 set hasChildren(b) { |
498 var rowItem = this.firstElementChild; | 529 var rowItem = this.firstElementChild; |
499 this.setAttribute('has-children', b); | 530 this.setAttribute('has-children', b); |
500 rowItem.setAttribute('has-children', b); | 531 rowItem.setAttribute('has-children', b); |
501 if (b) { | 532 if (b) { |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
540 // non editable. | 571 // non editable. |
541 switch (e.key) { | 572 switch (e.key) { |
542 case 'Escape': | 573 case 'Escape': |
543 input.value = text; | 574 input.value = text; |
544 // fall through | 575 // fall through |
545 case 'Enter': | 576 case 'Enter': |
546 self.tree.focus(); | 577 self.tree.focus(); |
547 } | 578 } |
548 } | 579 } |
549 | 580 |
550 function stopPropagation(e) { e.stopPropagation(); } | 581 function stopPropagation(e) { |
| 582 e.stopPropagation(); |
| 583 } |
551 | 584 |
552 if (editing) { | 585 if (editing) { |
553 this.selected = true; | 586 this.selected = true; |
554 this.setAttribute('editing', ''); | 587 this.setAttribute('editing', ''); |
555 this.draggable = false; | 588 this.draggable = false; |
556 | 589 |
557 // We create an input[type=text] and copy over the label value. When | 590 // We create an input[type=text] and copy over the label value. When |
558 // the input loses focus we set editing to false again. | 591 // the input loses focus we set editing to false again. |
559 input = this.ownerDocument.createElement('input'); | 592 input = this.ownerDocument.createElement('input'); |
560 input.value = text; | 593 input.value = text; |
561 if (labelEl.firstChild) | 594 if (labelEl.firstChild) |
562 labelEl.replaceChild(input, labelEl.firstChild); | 595 labelEl.replaceChild(input, labelEl.firstChild); |
563 else | 596 else |
564 labelEl.appendChild(input); | 597 labelEl.appendChild(input); |
565 | 598 |
566 input.addEventListener('keydown', handleKeydown); | 599 input.addEventListener('keydown', handleKeydown); |
567 input.addEventListener( | 600 input.addEventListener('blur', (function() { |
568 'blur', (function() { this.editing = false; }).bind(this)); | 601 this.editing = false; |
| 602 }).bind(this)); |
569 | 603 |
570 // Make sure that double clicks do not expand and collapse the tree | 604 // Make sure that double clicks do not expand and collapse the tree |
571 // item. | 605 // item. |
572 var eventsToStop = ['mousedown', 'mouseup', 'contextmenu', 'dblclick']; | 606 var eventsToStop = ['mousedown', 'mouseup', 'contextmenu', 'dblclick']; |
573 eventsToStop.forEach(function(type) { | 607 eventsToStop.forEach(function(type) { |
574 input.addEventListener(type, stopPropagation); | 608 input.addEventListener(type, stopPropagation); |
575 }); | 609 }); |
576 | 610 |
577 // Wait for the input element to recieve focus before sizing it. | 611 // Wait for the input element to recieve focus before sizing it. |
578 var rowElement = this.rowElement; | 612 var rowElement = this.rowElement; |
(...skipping 17 matching lines...) Expand all Loading... |
596 } else { | 630 } else { |
597 labelEl.textContent = value; | 631 labelEl.textContent = value; |
598 if (value != this.oldLabel_) { | 632 if (value != this.oldLabel_) { |
599 cr.dispatchSimpleEvent(this, 'rename', true); | 633 cr.dispatchSimpleEvent(this, 'rename', true); |
600 } | 634 } |
601 } | 635 } |
602 delete this.oldLabel_; | 636 delete this.oldLabel_; |
603 } | 637 } |
604 }, | 638 }, |
605 | 639 |
606 get editing() { return this.hasAttribute('editing'); } | 640 get editing() { |
| 641 return this.hasAttribute('editing'); |
| 642 } |
607 }; | 643 }; |
608 | 644 |
609 /** | 645 /** |
610 * Helper function that returns the next visible tree item. | 646 * Helper function that returns the next visible tree item. |
611 * @param {cr.ui.TreeItem} item The tree item. | 647 * @param {cr.ui.TreeItem} item The tree item. |
612 * @return {cr.ui.TreeItem} The found item or null. | 648 * @return {cr.ui.TreeItem} The found item or null. |
613 */ | 649 */ |
614 function getNext(item) { | 650 function getNext(item) { |
615 if (item.expanded) { | 651 if (item.expanded) { |
616 var firstChild = item.items[0]; | 652 var firstChild = item.items[0]; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
660 if (item.expanded && item.hasChildren) { | 696 if (item.expanded && item.hasChildren) { |
661 var lastChild = item.items[item.items.length - 1]; | 697 var lastChild = item.items[item.items.length - 1]; |
662 return getLastHelper(lastChild); | 698 return getLastHelper(lastChild); |
663 } | 699 } |
664 return item; | 700 return item; |
665 } | 701 } |
666 | 702 |
667 // Export | 703 // Export |
668 return {Tree: Tree, TreeItem: TreeItem}; | 704 return {Tree: Tree, TreeItem: TreeItem}; |
669 }); | 705 }); |
OLD | NEW |