blob: 4949ac22527c5239ee66f198d72ca5d05dea9a6f [file] [log] [blame]
<!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_">&times;</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>