| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| |
| <link rel="import" href="/tracing/base/iteration_helpers.html"> |
| <link rel="import" href="/tracing/base/settings.html"> |
| <link rel="import" href="/tracing/ui/base/dropdown.html"> |
| |
| <dom-module id='tr-ui-b-grouping-table-groupby-picker'> |
| <template> |
| <style> |
| :host { |
| display: flex; |
| align-items: center; |
| } |
| |
| :host(:not([vertical])), :host(:not([vertical])) groups { |
| flex-direction: row; |
| } |
| |
| :host([vertical]), :host([vertical]) groups { |
| flex-direction: column; |
| } |
| |
| groups { |
| -webkit-user-select: none; |
| display: flex; |
| } |
| |
| possible-group { |
| display: span; |
| padding-right: 10px; |
| padding-left: 10px; |
| } |
| </style> |
| |
| <groups id="groups"></groups> |
| <tr-ui-b-dropdown id="add_group"></tr-ui-b-dropdown> |
| </template> |
| </dom-module> |
| |
| <dom-module id="tr-ui-b-grouping-table-groupby-picker-group"> |
| <template> |
| <style> |
| :host { |
| white-space: nowrap; |
| border: 3px solid white; |
| background-color: #dddddd; |
| cursor: move; |
| } |
| |
| :host(:not([vertical])) { |
| display: inline; |
| } |
| |
| :host([vertical]) { |
| display: block; |
| } |
| |
| :host(:not([vertical]).drop-before) { |
| border-left: 3px solid black; |
| } |
| |
| :host([vertical].drop-before) { |
| border-top: 3px solid black; |
| } |
| |
| :host(:not([vertical]).drop-after) { |
| border-right: 3px solid black; |
| } |
| |
| :host([vertical].drop-after) { |
| border-bottom: 3px solid black; |
| } |
| |
| :host([dragging]) { |
| opacity: 0.5; |
| } |
| |
| #remove { |
| visibility: hidden; |
| padding-left: 3px; |
| width: 20px; |
| height: 20px; |
| cursor: auto; |
| } |
| |
| #key { |
| padding-right: 3px; |
| } |
| </style> |
| |
| <!-- TODO(eakuefner): Use an iron-icon here once |
| https://github.com/catapult-project/catapult/issues/2772 is fixed. --> |
| <span id="remove" on-click="remove_">×</span> |
| <span id="key"></span> |
| </template> |
| </dom-module> |
| |
| <script> |
| 'use strict'; |
| |
| tr.exportTo('tr.ui.b', function() { |
| var THIS_DOC = document.currentScript.ownerDocument; |
| |
| Polymer({ |
| is: 'tr-ui-b-grouping-table-groupby-picker-group', |
| |
| created: function() { |
| this.picker_ = undefined; |
| this.group_ = undefined; |
| }, |
| |
| ready: function() { |
| this.setAttribute('draggable', true); |
| this.addEventListener('mouseover', this.onMouseOver_.bind(this)); |
| this.addEventListener('mouseleave', this.onMouseLeave_.bind(this)); |
| this.addEventListener('dragstart', this.onDragStart_.bind(this)); |
| this.addEventListener('dragover', this.onDragOver_.bind(this)); |
| }, |
| |
| set group(g) { |
| this.group_ = g; |
| this.$.key.textContent = g.label; |
| }, |
| |
| get key() { |
| return this.group_.key; |
| }, |
| |
| get picker() { |
| return this.picker_; |
| }, |
| |
| set picker(picker) { |
| this.picker_ = picker; |
| this.vertical = picker.vertical; |
| }, |
| |
| // TODO(benjhayden): Use data-binding? |
| get vertical() { |
| return this.getAttribute('vertical'); |
| }, |
| |
| set vertical(vertical) { |
| if (vertical) |
| this.setAttribute('vertical', true); |
| else |
| this.removeAttribute('vertical'); |
| }, |
| |
| onMouseOver_: function(event) { |
| this.$.remove.style.visibility = 'visible'; |
| }, |
| |
| onMouseLeave_: function(event) { |
| this.$.remove.style.visibility = 'hidden'; |
| }, |
| |
| onDragStart_: function(event) { |
| event.dataTransfer.effectAllowed = 'move'; |
| this.setAttribute('dragging', true); |
| }, |
| |
| onDragOver_: function(event) { |
| event.preventDefault(); // Allows us to drop. |
| event.dataTransfer.dropEffect = 'move'; |
| |
| this.picker.clearDragIndicators_(); |
| if (this.picker.shouldDropBefore_(this, event)) { |
| this.classList.add('drop-before'); |
| if (this.previousElementSibling) |
| this.previousElementSibling.classList.add('drop-after'); |
| } else { |
| this.classList.add('drop-after'); |
| if (this.nextElementSibling) |
| this.nextElementSibling.classList.add('drop-before'); |
| } |
| return false; |
| }, |
| |
| remove_: function(event) { |
| var newKeys = this.picker.currentGroupKeys.slice(); |
| newKeys.splice(newKeys.indexOf(this.key), 1); |
| this.picker.currentGroupKeys = newKeys; |
| } |
| }); |
| |
| Polymer({ |
| is: 'tr-ui-b-grouping-table-groupby-picker', |
| |
| created: function() { |
| this.currentGroupKeys_ = undefined; |
| this.defaultGroupKeys_ = undefined; |
| this.possibleGroups_ = []; |
| this.settingsKey_ = []; |
| }, |
| |
| ready: function() { |
| Polymer.dom(this.$.add_group.iconElement).textContent = 'Add another...'; |
| this.addEventListener('dragend', this.onDragEnd_.bind(this)); |
| this.addEventListener('drop', this.onDrop_.bind(this)); |
| }, |
| |
| get defaultGroupKeys() { |
| return this.defaultGroupKeys_; |
| }, |
| |
| set vertical(vertical) { |
| if (vertical) |
| this.setAttribute('vertical', true); |
| else |
| this.removeAttribute('vertical'); |
| |
| for (var groupEl of this.$.groups.childNodes) |
| groupEl.vertical = vertical; |
| }, |
| |
| get vertical() { |
| return this.getAttribute('vertical'); |
| }, |
| |
| set defaultGroupKeys(defaultGroupKeys) { |
| this.defaultGroupKeys_ = defaultGroupKeys; |
| this.maybeInit_(); |
| }, |
| |
| get possibleGroups() { |
| return this.possibleGroups_; |
| }, |
| |
| set possibleGroups(possibleGroups) { |
| this.possibleGroups_ = possibleGroups; |
| this.maybeInit_(); |
| }, |
| |
| get settingsKey() { |
| return this.settingsKey_; |
| }, |
| |
| set settingsKey(settingsKey) { |
| this.settingsKey_ = settingsKey; |
| this.maybeInit_(); |
| }, |
| |
| maybeInit_: function() { |
| if (!this.settingsKey_ || |
| !this.defaultGroupKeys_ || |
| !this.possibleGroups_) { |
| return; |
| } |
| |
| this.currentGroupKeys = tr.b.Settings.get( |
| this.settingsKey_, this.defaultGroupKeys_); |
| }, |
| |
| get currentGroupKeys() { |
| return this.currentGroupKeys_; |
| }, |
| |
| get currentGroups() { |
| var groupsByKey = {}; |
| for (var group of this.possibleGroups_) |
| groupsByKey[group.key] = group; |
| return this.currentGroupKeys.map(groupKey => groupsByKey[groupKey]); |
| }, |
| |
| set currentGroupKeys(currentGroupKeys) { |
| if (this.currentGroupKeys_ === currentGroupKeys) |
| return; |
| |
| if (!(currentGroupKeys instanceof Array)) |
| throw new Error('Must be array'); |
| |
| var availableGroupKeys = new Set(); |
| for (var group of this.possibleGroups_) |
| availableGroupKeys.add(group.key); |
| this.currentGroupKeys_ = currentGroupKeys.filter( |
| k => availableGroupKeys.has(k)); |
| this.updateGroups_(); |
| |
| tr.b.Settings.set( |
| this.settingsKey_, this.currentGroupKeys_); |
| |
| this.dispatchEvent(new tr.b.Event('current-groups-changed')); |
| }, |
| |
| /** |
| * @return {undefined|Element} |
| */ |
| get draggingGroupElement() { |
| for (var group of this.$.groups.children) |
| if (group.getAttribute('dragging')) |
| return group; |
| return undefined; |
| }, |
| |
| shouldDropBefore_: function(groupEl, event) { |
| var dragBoxRect = this.draggingGroupElement.getBoundingClientRect(); |
| var dropBoxRect = groupEl.getBoundingClientRect(); |
| // compare horizontally if drag and drop overlap vertically |
| var dragVertRange = tr.b.Range.fromExplicitRange( |
| dragBoxRect.top, dragBoxRect.bottom); |
| var dropVertRange = tr.b.Range.fromExplicitRange( |
| dropBoxRect.top, dropBoxRect.bottom); |
| if (dragVertRange.intersectsRangeInclusive(dropVertRange)) |
| return event.clientX < ((dropBoxRect.left + dropBoxRect.right) / 2); |
| return event.clientY < ((dropBoxRect.top + dropBoxRect.bottom) / 2); |
| }, |
| |
| clearDragIndicators_: function() { |
| for (var groupEl of this.$.groups.children) { |
| groupEl.classList.remove('drop-before'); |
| groupEl.classList.remove('drop-after'); |
| } |
| }, |
| |
| onDragEnd_: function(event) { |
| this.clearDragIndicators_(); |
| for (var groupEl of this.$.groups.children) |
| groupEl.removeAttribute('dragging'); |
| }, |
| |
| onDrop_: function(event) { |
| event.stopPropagation(); // stops the browser from redirecting. |
| |
| var draggingGroupEl = this.draggingGroupElement; |
| var dropBeforeEl = undefined; |
| var dropAfterEl = undefined; |
| for (var groupEl of this.$.groups.children) { |
| if (groupEl.classList.contains('drop-before')) { |
| dropBeforeEl = groupEl; |
| break; |
| } |
| if (groupEl.classList.contains('drop-after')) { |
| dropAfterEl = groupEl; |
| break; |
| } |
| } |
| |
| if (!dropBeforeEl && !dropAfterEl) |
| return; |
| |
| this.$.groups.removeChild(draggingGroupEl); |
| var lastGroupEl = this.$.groups.children[ |
| this.$.groups.children.length - 1]; |
| |
| if (dropBeforeEl) |
| this.$.groups.insertBefore(draggingGroupEl, dropBeforeEl); |
| else if (dropAfterEl === lastGroupEl) |
| this.$.groups.appendChild(draggingGroupEl); |
| else |
| this.$.groups.insertBefore(draggingGroupEl, dropAfterEl.nextSibling); |
| |
| var currentGroupKeys = []; |
| for (var group of this.$.groups.children) |
| currentGroupKeys.push(group.key); |
| this.currentGroupKeys = currentGroupKeys; |
| return false; |
| }, |
| |
| updateGroups_: function() { |
| Polymer.dom(this.$.groups).textContent = ''; |
| Polymer.dom(this.$.add_group).textContent = ''; |
| |
| var unusedGroups = {}; |
| var groupsByKey = {}; |
| for (var group of this.possibleGroups_) { |
| unusedGroups[group.key] = group; |
| groupsByKey[group.key] = group; |
| } |
| |
| for (var key of this.currentGroupKeys_) |
| delete unusedGroups[key]; |
| |
| // Create groups. |
| for (var key of this.currentGroupKeys_) { |
| var group = groupsByKey[key]; |
| var groupEl = document.createElement( |
| 'tr-ui-b-grouping-table-groupby-picker-group'); |
| groupEl.picker = this; |
| groupEl.group = group; |
| Polymer.dom(this.$.groups).appendChild(groupEl); |
| } |
| |
| // Adjust dropdown. |
| tr.b.iterItems(unusedGroups, function(key, group) { |
| var groupEl = document.createElement('possible-group'); |
| Polymer.dom(groupEl).textContent = group.label; |
| groupEl.addEventListener('click', function() { |
| var newKeys = this.currentGroupKeys.slice(); |
| newKeys.push(key); |
| this.currentGroupKeys = newKeys; |
| this.$.add_group.close(); |
| }.bind(this)); |
| Polymer.dom(this.$.add_group).appendChild(groupEl); |
| }, this); |
| |
| // Hide dropdown if needed. |
| if (tr.b.dictionaryLength(unusedGroups) == 0) { |
| this.$.add_group.style.display = 'none'; |
| } else { |
| this.$.add_group.style.display = ''; |
| } |
| } |
| }); |
| |
| return { |
| }; |
| }); |
| </script> |