1<!DOCTYPE html> 2<!-- 3Copyright (c) 2016 The Chromium Authors. All rights reserved. 4Use of this source code is governed by a BSD-style license that can be 5found in the LICENSE file. 6--> 7<link rel="import" href="/tracing/base/iteration_helpers.html"> 8<link rel="import" href="/tracing/base/settings.html"> 9<link rel="import" href="/tracing/ui/base/dropdown.html"> 10 11<polymer-element name="tr-ui-b-grouping-table-groupby-picker"> 12 <template> 13 <style> 14 :host { 15 display: flex; 16 flex-direction: row; 17 align-items: center; 18 } 19 groups { 20 -webkit-user-select: none; 21 display: flex; 22 flex-direction: row; 23 padding-left: 10px; 24 } 25 26 group, possible-group { 27 display: span; 28 padding-right: 10px; 29 padding-left: 10px; 30 } 31 32 group { 33 border-left: 1px solid rgba(0,0,0,0); 34 cursor: move; 35 } 36 37 group.dragging { 38 opacity: 0.2; 39 } 40 41 group.drop-targeted { 42 border-left: 1px solid black; 43 } 44 45 46 #remove { 47 cursor: default; 48 } 49 50 #remove:not([hovered]) { 51 visibility: hidden; 52 } 53 </style> 54 <groups> 55 </groups> 56 <tr-ui-b-dropdown id="add-group"></tr-ui-b-dropdown> 57 </template> 58</polymer-element> 59 60<template id="tr-ui-b-grouping-table-groupby-picker-group-template"> 61 <span id="key"></span> 62 <span id="remove">×</span> 63</template> 64 65<script> 66'use strict'; 67 68tr.exportTo('tr.ui.b', function() { 69 var THIS_DOC = document._currentScript.ownerDocument; 70 71 Polymer('tr-ui-b-grouping-table-groupby-picker', { 72 created: function() { 73 this.needsInit_ = true; 74 this.defaultGroupKeys_ = undefined; 75 this.possibleGroups_ = []; 76 this.settingsKey_ = []; 77 78 this.currentGroupKeys_ = undefined; 79 80 this.dragging_ = false; 81 }, 82 83 get defaultGroupKeys() { 84 return this.defaultGroupKeys_; 85 }, 86 87 set defaultGroupKeys(defaultGroupKeys) { 88 if (!this.needsInit_) 89 throw new Error('Already initialized.'); 90 this.defaultGroupKeys_ = defaultGroupKeys; 91 this.maybeInit_(); 92 }, 93 94 get possibleGroups() { 95 return this.possibleGroups_; 96 }, 97 98 set possibleGroups(possibleGroups) { 99 if (!this.needsInit_) 100 throw new Error('Already initialized.'); 101 this.possibleGroups_ = possibleGroups; 102 this.maybeInit_(); 103 }, 104 105 get settingsKey() { 106 return this.settingsKey_; 107 }, 108 109 set settingsKey(settingsKey) { 110 if (!this.needsInit_) 111 throw new Error('Already initialized.'); 112 this.settingsKey_ = settingsKey; 113 this.maybeInit_(); 114 }, 115 116 maybeInit_: function() { 117 if (!this.needsInit_) 118 return; 119 120 if (this.settingsKey_ === undefined) 121 return; 122 if (this.defaultGroupKeys_ === undefined) 123 return; 124 if (this.possibleGroups_ === undefined) 125 return; 126 127 this.needsInit_ = false; 128 129 var addGroupEl = this.shadowRoot.querySelector('#add-group'); 130 addGroupEl.iconElement.textContent = 'Add another...'; 131 132 this.currentGroupKeys = tr.b.Settings.get( 133 this.settingsKey_, this.defaultGroupKeys_); 134 }, 135 136 get currentGroupKeys() { 137 return this.currentGroupKeys_; 138 }, 139 140 get currentGroups() { 141 var groupsByKey = {}; 142 this.possibleGroups_.forEach(function(group) { 143 groupsByKey[group.key] = group; 144 }); 145 return this.currentGroupKeys_.map(function(groupKey) { 146 return groupsByKey[groupKey]; 147 }); 148 }, 149 150 set currentGroupKeys(currentGroupKeys) { 151 if (this.currentGroupKeys_ === currentGroupKeys) 152 return; 153 154 if (!(currentGroupKeys instanceof Array)) 155 throw new Error('Must be array'); 156 157 this.currentGroupKeys_ = currentGroupKeys; 158 this.updateGroups_(); 159 160 tr.b.Settings.set( 161 this.settingsKey_, this.currentGroupKeys_); 162 163 var e = new tr.b.Event('current-groups-changed'); 164 this.dispatchEvent(e); 165 }, 166 167 updateGroups_: function() { 168 var groupsEl = this.shadowRoot.querySelector('groups'); 169 var addGroupEl = this.shadowRoot.querySelector('#add-group'); 170 171 groupsEl.textContent = ''; 172 addGroupEl.textContent = ''; 173 174 var unusedGroups = {}; 175 var groupsByKey = {}; 176 this.possibleGroups_.forEach(function(group) { 177 unusedGroups[group.key] = group; 178 groupsByKey[group.key] = group; 179 }); 180 181 this.currentGroupKeys_.forEach(function(key) { 182 delete unusedGroups[key]; 183 }); 184 185 // Create groups. 186 var groupTemplateEl = THIS_DOC.querySelector( 187 '#tr-ui-b-grouping-table-groupby-picker-group-template'); 188 this.currentGroupKeys_.forEach(function(key, index) { 189 var group = groupsByKey[key]; 190 var groupEl = document.createElement('group'); 191 groupEl.groupKey = key; 192 groupEl.appendChild(document.importNode(groupTemplateEl.content, true)); 193 groupEl.querySelector('#key').textContent = group.label; 194 groupsEl.appendChild(groupEl); 195 196 this.configureRemoveButtonForGroup_(groupEl); 197 this.configureDragAndDropForGroup_(groupEl); 198 }, this); 199 200 // Adjust dropdown. 201 tr.b.iterItems(unusedGroups, function(key, group) { 202 var groupEl = document.createElement('possible-group'); 203 groupEl.textContent = group.label; 204 groupEl.addEventListener('click', function() { 205 var newKeys = this.currentGroupKeys.slice(); 206 newKeys.push(key); 207 this.currentGroupKeys = newKeys; 208 addGroupEl.close(); 209 }.bind(this)); 210 addGroupEl.appendChild(groupEl); 211 }, this); 212 213 // Hide dropdown if needed. 214 if (tr.b.dictionaryLength(unusedGroups) == 0) { 215 addGroupEl.style.display = 'none'; 216 } else { 217 addGroupEl.style.display = ''; 218 } 219 }, 220 221 configureRemoveButtonForGroup_: function(groupEl) { 222 var removeEl = groupEl.querySelector('#remove'); 223 removeEl.addEventListener('click', function() { 224 var newKeys = this.currentGroupKeys.slice(); 225 var i = newKeys.indexOf(groupEl.groupKey); 226 newKeys.splice(i, 1); 227 this.currentGroupKeys = newKeys; 228 }.bind(this)); 229 230 groupEl.addEventListener('mouseenter', function() { 231 removeEl.setAttribute('hovered', true); 232 }); 233 groupEl.addEventListener('mouseleave', function() { 234 removeEl.removeAttribute('hovered'); 235 }); 236 }, 237 238 configureDragAndDropForGroup_: function(groupEl) { 239 var groupsEl = groupEl.parentElement; 240 241 groupEl.setAttribute('draggable', true); 242 243 groupEl.addEventListener('dragstart', function(e) { 244 e.dataTransfer.setData('groupKey', groupEl.groupKey); 245 groupEl.querySelector('#remove').removeAttribute('hovered'); 246 groupEl.classList.add('dragging'); 247 this.dragging_ = true; 248 }.bind(this)); 249 250 groupEl.addEventListener('dragend', function(e) { 251 console.log(e.type, groupEl.groupKey); 252 for (var i = 0; i < groupsEl.children.length; i++) 253 groupsEl.children[i].classList.remove('drop-targeted'); 254 groupEl.classList.remove('dragging'); 255 this.dragging_ = false; 256 }.bind(this)); 257 258 // Drop targeting. 259 groupEl.addEventListener('dragenter', function(e) { 260 if (!this.dragging_) 261 return; 262 groupEl.classList.add('drop-targeted'); 263 if (this.dragging_) 264 e.preventDefault(); 265 }.bind(this)); 266 267 groupEl.addEventListener('dragleave', function(e) { 268 if (!this.dragging_) 269 return; 270 groupEl.classList.remove('drop-targeted'); 271 e.preventDefault(); 272 }.bind(this)); 273 274 275 // Drop logic. 276 groupEl.addEventListener('dragover', function(e) { 277 if (!this.dragging_) 278 return; 279 e.preventDefault(); 280 groupEl.classList.add('drop-targeted'); 281 }.bind(this)); 282 283 groupEl.addEventListener('drop', function(e) { 284 if (!this.dragging_) 285 return; 286 287 var srcKey = e.dataTransfer.getData('groupKey'); 288 var dstKey = groupEl.groupKey; 289 290 if (srcKey === dstKey) 291 return; 292 293 var newKeys = this.currentGroupKeys_.slice(); 294 295 var srcIndex = this.currentGroupKeys_.indexOf(srcKey); 296 newKeys.splice(srcIndex, 1); 297 298 var dstIndex = this.currentGroupKeys_.indexOf(dstKey); 299 newKeys.splice(dstIndex, 0, srcKey); 300 301 this.currentGroupKeys = newKeys; 302 303 e.dataTransfer.clearData(); 304 e.preventDefault(); 305 e.stopPropagation(); 306 }.bind(this)); 307 } 308 }); 309 310 return { 311 }; 312}); 313</script> 314