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

Unified Diff: tracing/tracing/ui/base/tab_view.html

Issue 2023283002: [polymer] Rewrite the analysis tab view (Closed) Base URL: git@github.com:zeptonaut/catapult.git@polymer10_rewrite_tab_view2
Patch Set: Code review Created 4 years, 7 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tracing/tracing/ui/analysis/analysis_view_test.html ('k') | tracing/tracing/ui/base/tab_view_test.html » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tracing/tracing/ui/base/tab_view.html
diff --git a/tracing/tracing/ui/base/tab_view.html b/tracing/tracing/ui/base/tab_view.html
index a9615663af31edae3eaaccc8b97fb458b92cee7e..288fa0779cd15151edf6e177ce7b26c33ae05dcf 100644
--- a/tracing/tracing/ui/base/tab_view.html
+++ b/tracing/tracing/ui/base/tab_view.html
@@ -5,452 +5,142 @@ Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
+<!--
+@fileoverview A series of tabs for the analysis view that controls which
+analysis sub-view is being displayed.
+
+We follow a fairly standard web convention of backing our tabs with hidden radio
+buttons but visible radio button labels (the tabs themselves) which toggle the
+input element when clicked. Using hidden radio buttons makes sense, as both tabs
+and radio buttons are input elements that allow user selection through clicking
+and limit users to having one option selected at a time.
+-->
<dom-module id='tr-ui-a-tab-view'>
<template>
<style>
- :host {
- display: flex;
- flex-flow: column nowrap;
- overflow: hidden;
- box-sizing: border-box;
- }
-
- tab-strip.hidden {
- display: none;
- }
-
- tab-strip {
- background-color: rgb(236, 236, 236);
- border-bottom: 1px solid #8e8e8e;
- display: flex;
- flex: 0 0 auto;
- flex-flow: row;
- overflow-x: auto;
- padding: 0 10px 0 10px;
+ #selection_description, #tabs {
font-size: 12px;
}
- tab-button {
- display: block;
- flex: 0 0 auto;
- padding: 4px 15px 1px 15px;
- margin-top: 2px;
- }
-
- tab-button[selected=true] {
- background-color: white;
- border: 1px solid rgb(163, 163, 163);
- border-bottom: none;
- padding: 3px 14px 1px 14px;
- }
-
- tabs-content-container {
- display: flex;
- flex: 1 1 auto;
- overflow: auto;
- width: 100%;
+ #selection_description {
+ display: inline-block;
+ font-weight: bold;
+ margin: 9px 0px 4px 20px;
}
- tabs-content-container ::content > * {
- flex: 1 1 auto;
+ #tabs {
+ display: block;
+ border-top: 1px solid #8e8e8e;
+ border-bottom: 1px solid #8e8e8e;
+ background-color: #ececec;
+ overflow: hidden;
+ margin: 0;
}
- tabs-content-container ::content > *:not([selected]) {
+ #tabs input[type=radio] {
display: none;
}
- button-label {
- display: inline;
+ #tabs tab label {
+ cursor: pointer;
+ display: inline-block;
+ border: 1px solid #ececec;
+ margin: 5px 0px 0px 15px;
+ padding: 3px 10px 3px 10px;
}
- tab-strip-heading {
- display: block;
- flex: 0 0 auto;
- padding: 4px 15px 1px 15px;
- margin-top: 2px;
- margin-before: 20px;
- margin-after: 10px;
- }
- #tsh {
- display: inline;
- font-weight: bold;
+ #tabs input[type=radio]:checked ~ label {
+ background-color: white;
+ border: 1px solid #8e8e8e;
+ border-bottom: 1px solid white;
}
</style>
-
- <tab-strip>
- <tab-strip-heading id="tshh">
- <span id="tsh"></span>
- </tab-strip-heading>
- <template is="dom-repeat" items="{{tabs_}}">
- <tab-button
- id="{{item.id}}"
- on-click="tabButtonSelectHandler_"
- selected="{{computeTabSelectState_(selectedTab_, item)}}">
- <button-label>{{computeTabLabel_(item)}}</button-label>
- </tab-button>
+ <div id='tabs'>
+ <label id=selection_description>[[label_]]</label>
+ <template is=dom-repeat items=[[subViews_]]>
+ <tab>
+ <input type=radio name=tabs id$=[[item.tagName]]
+ on-change='onTabChanged_'
+ checked$='[[isChecked_(item)]]' />
+ <label for$=[[item.tagName]]>[[item.tabLabel]]</label>
+ </tab>
</template>
- </tab-strip>
-
- <tabs-content-container id='content-container'>
- <content></content>
- </tabs-content-container>
-
+ </div>
+ <div id='subView'></div>
+ <content>
+ </content>
</template>
</dom-module>
<script>
'use strict';
-window.TracingAnalysisTabView = Polymer({
+
+Polymer({
is: 'tr-ui-a-tab-view',
properties: {
- tabs_: {
+ label_: {
+ type: String,
+ value: () => ''
+ },
+ subViews_: {
type: Array,
value: () => []
},
- selectedTab_: {
- type: Object,
- value: () => {}
- }
- },
-
- ready: function() {
- this.$.tshh.style.display = 'none';
-
- // A tab is represented by the following tuple:
- // (id, label, content, observer, savedScrollTop, savedScrollLeft).
- // The properties are used in the following way:
- // id: Uniquely identifies a tab. It is the same number as the index
- // in the tabs array. Used primarily by the on-click event attached
- // to buttons.
- // label: A string, representing the label printed on the tab button.
- // content: The light-dom child representing the contents of the tab.
- // The content is appended to this tab-view by the user.
- // observers: The observers attached to the content node to watch for
- // attribute changes. The attributes of interest are: 'selected',
- // and 'tab-label'.
- // savedScrollTop/Left: Used to return the scroll position upon switching
- // tabs. The values are generally saved when a tab switch occurs.
- //
- // The order of the tabs is relevant for the tab ordering.
-
- // Register any already existing children.
- for (var i = 0; i < Polymer.dom(this).children.length; i++)
- this.processAddedChild_(Polymer.dom(this).children[i]);
-
- // In case the user decides to add more tabs, make sure we watch for
- // any child mutations.
- this.childrenObserver_ = new MutationObserver(
- this.childrenUpdated_.bind(this));
- this.childrenObserver_.observe(
- this.$['content-container'], { childList: 'true' });
- },
-
- get tabStripHeadingText() {
- return Polymer.dom(this.$.tsh).textContent;
- },
-
- set tabStripHeadingText(tabStripHeadingText) {
- Polymer.dom(this.$.tsh).textContent = tabStripHeadingText;
- if (!!tabStripHeadingText)
- this.$.tshh.style.display = '';
- else
- this.$.tshh.style.display = 'none';
- },
-
- get selectedTab() {
- // Make sure we process any pending children additions / removals, before
- // trying to select a tab. Otherwise, we might not find some children.
- this.childrenUpdated_(
- this.childrenObserver_.takeRecords(), this.childrenObserver_);
-
- // Do not give access to the user to the inner data structure.
- // A user should only be able to mutate the added tab content.
- var selectedTab = this.get('selectedTab_');
- if (selectedTab)
- return selectedTab.content;
-
- return undefined;
- },
-
- set selectedTab(content) {
- // Make sure we process any pending children additions / removals, before
- // trying to select a tab. Otherwise, we might not find some children.
- this.childrenUpdated_(
- this.childrenObserver_.takeRecords(), this.childrenObserver_);
-
- if (content === undefined || content === null) {
- this.changeSelectedTabById_(undefined);
- return;
- }
-
- // Search for the specific node in our tabs list.
- // If it is not there print a warning.
- var contentTabId = undefined;
- for (var i = 0; i < this.tabs_.length; i++) {
- if (this.get('tabs_.' + i).content === content) {
- contentTabId = this.get('tabs_.' + i).id;
- break;
- }
- }
-
- if (contentTabId === undefined)
- return;
-
- this.changeSelectedTabById_(contentTabId);
- },
-
- get tabsHidden() {
- var ts = Polymer.dom(this.root).querySelector('tab-strip');
- return ts.classList.contains('hidden');
+ selectedSubView_: Object
},
- set tabsHidden(tabsHidden) {
- tabsHidden = !!tabsHidden;
- var ts = Polymer.dom(this.root).querySelector('tab-strip');
- if (tabsHidden)
- ts.classList.add('hidden');
- else
- ts.classList.remove('hidden');
+ set label(newLabel) {
+ this.set('label_', newLabel);
},
get tabs() {
- return this.tabs_.map(function(tabObject) {
- return tabObject.content;
- });
+ return this.get('subViews_');
},
- /**
- * Function called on light-dom child addition.
- */
- processAddedChild_: function(child) {
- var observerAttributeSelected = new MutationObserver(
- this.childAttributesChanged_.bind(this));
- var observerAttributeTabLabel = new MutationObserver(
- this.childAttributesChanged_.bind(this));
- var tabObject = {
- id: this.tabs_.length,
- content: child,
- label: child.getAttribute('tab-label'),
- observers: {
- forAttributeSelected: observerAttributeSelected,
- forAttributeTabLabel: observerAttributeTabLabel
- }
- };
- // this.tabs_.push(tabObject);
- this.push('tabs_', tabObject);
- if (child.hasAttribute('selected')) {
- // When receiving a child with the selected attribute, if we have no
- // selected tab, mark the child as the selected tab, otherwise keep
- // the previous selection.
- if (this.get('selectedTab_'))
- Polymer.dom(child).removeAttribute('selected');
- else
- this.setSelectedTabById_(tabObject.id);
- }
-
- // This is required because the user might have set the selected
- // property before we got to process the child.
- var previousSelected = child.selected;
-
- var tabView = this;
-
- Object.defineProperty(
- child,
- 'selected', {
- configurable: true,
- set: function(value) {
- if (value) {
- tabView.changeSelectedTabById_(tabObject.id);
- return;
- }
-
- var wasSelected = (tabView.get('selectedTab_') === tabObject);
- if (wasSelected)
- tabView.changeSelectedTabById_(undefined);
- },
- get: function() {
- return this.hasAttribute('selected');
- }
- });
-
- if (previousSelected)
- child.selected = previousSelected;
-
- observerAttributeSelected.observe(child, {
- attributeFilter: ['selected']
- });
- observerAttributeTabLabel.observe(child, {
- attributeFilter: ['tab-label']
- });
+ get selectedSubView() {
+ return this.selectedSubView_;
},
- /**
- * Function called on light-dom child removal.
- */
- processRemovedChild_: function(child) {
- for (var i = 0; i < this.get('tabs_').length; i++) {
- var tab = this.get('tabs_.' + i);
- // Make sure ids are the same as the tab position after removal.
- tab.id = i;
- if (tab.content === child) {
- tab.observers.forAttributeSelected.disconnect();
- tab.observers.forAttributeTabLabel.disconnect();
- // The user has removed the currently selected tab.
- if (tab === this.get('selectedTab_')) {
- this.clearSelectedTab_();
- this.fire('selected-tab-change');
- }
- Polymer.dom(child).removeAttribute('selected');
- delete child.selected;
- // Remove the observer since we no longer care about this child.
- this.splice('tabs_', i, 1);
- i--;
- }
- }
- },
-
-
- /**
- * This function handles child attribute changes. The only relevant
- * attributes for the tab-view are 'tab-label' and 'selected'.
- */
- childAttributesChanged_: function(mutations, observer) {
- var tabObject = undefined;
- // First figure out which child has been changed.
- for (var i = 0; i < this.tabs_.length; i++) {
- var observers = this.get('tabs_.' + i).observers;
- if (observers.forAttributeSelected === observer ||
- observers.forAttributeTabLabel === observer) {
- tabObject = this.get('tabs_.' + i);
- break;
- }
- }
-
- // This should not happen, unless the user has messed with our internal
- // data structure.
- if (!tabObject)
+ set selectedSubView(subView) {
+ if (subView === this.selectedSubView_)
return;
- // Next handle the attribute changes.
- for (var i = 0; i < mutations.length; i++) {
- var node = tabObject.content;
- // 'tab-label' attribute has been changed.
- if (mutations[i].attributeName === 'tab-label')
- tabObject.label = node.getAttribute('tab-label');
- // 'selected' attribute has been changed.
- if (mutations[i].attributeName === 'selected') {
- // The attribute has been set.
- var nodeIsSelected = node.hasAttribute('selected');
- if (nodeIsSelected)
- this.changeSelectedTabById_(tabObject.id);
- else
- this.changeSelectedTabById_(undefined);
- }
- }
- },
+ if (this.selectedSubView_)
+ Polymer.dom(this.$.subView).removeChild(this.selectedSubView_);
- /**
- * This function handles light-dom additions and removals from the
- * tab-view component.
- */
- childrenUpdated_: function(mutations, observer) {
- mutations.forEach(function(mutation) {
- for (var i = 0; i < mutation.removedNodes.length; i++)
- this.processRemovedChild_(mutation.removedNodes[i]);
- for (var i = 0; i < mutation.addedNodes.length; i++)
- this.processAddedChild_(mutation.addedNodes[i]);
- }, this);
- },
+ this.set('selectedSubView_', subView);
- /**
- * Handler called when a click event happens on any of the tab buttons.
- */
- tabButtonSelectHandler_: function(event) {
- this.changeSelectedTabById_(event.currentTarget.id);
- },
-
- /**
- * This does the actual work. :)
- */
- changeSelectedTabById_: function(id) {
- var newTab = id !== undefined ? this.get('tabs_.' + id) : undefined;
- var changed = this.get('selectedTab_') !== newTab;
- this.saveCurrentTabScrollPosition_();
- this.clearSelectedTab_();
- if (id !== undefined) {
- this.setSelectedTabById_(id);
- this.restoreCurrentTabScrollPosition_();
- }
-
- if (changed)
- this.fire('selected-tab-change');
- },
-
- /**
- * This function updates the currently selected tab based on its internal
- * id. The corresponding light-dom element receives the selected attribute.
- */
- setSelectedTabById_: function(id) {
- this.set('selectedTab_', this.get('tabs_.' + id));
- // Disconnect observer while we mutate the child.
- this.get('selectedTab_').observers.forAttributeSelected.disconnect();
- Polymer.dom(this.get('selectedTab_').content)
- .setAttribute('selected', 'selected');
- // Reconnect the observer to watch for changes in the future.
- this.get('selectedTab_').observers.forAttributeSelected.observe(
- this.get('selectedTab_').content, { attributeFilter: ['selected'] });
+ if (subView)
+ Polymer.dom(this.$.subView).appendChild(subView);
aiolos (Not reviewing) 2016/06/01 17:40:46 You *might* need to flush the dom here, depending
charliea (OOO until 10-5) 2016/06/01 18:08:47 Acknowledged. I think my preference here is to onl
aiolos (Not reviewing) 2016/06/01 18:19:57 sgtm.
+ this.fire('selected-tab-change');
},
- saveTabStates: function() {
- // Scroll positions of unselected tabs have already been saved.
- this.saveCurrentTabScrollPosition_();
+ clearSubViews: function() {
+ this.splice('subViews_', 0, this.subViews_.length);
+ this.selectedSubView = undefined;
},
- saveCurrentTabScrollPosition_: function() {
- var selectedTab = this.get('selectedTab_');
- if (selectedTab) {
- selectedTab.content._savedScrollTop =
- this.$['content-container'].scrollTop;
- selectedTab.content._savedScrollLeft =
- this.$['content-container'].scrollLeft;
+ addSubView: function(subView) {
+ if (!(subView instanceof HTMLElement) ||
+ !subView.behaviors ||
+ subView.behaviors.indexOf(Catapult.tr_ui_a_sub_view_behavior) < 0) {
+ throw new Error('Sub-view being added must be a registered Polymer ' +
+ 'element with the sub-view behavior');
}
- },
- restoreCurrentTabScrollPosition_: function() {
- var selectedTab = this.get('selectedTab_');
- if (selectedTab) {
- this.$['content-container'].scrollTop =
- selectedTab.content._savedScrollTop || 0;
- this.$['content-container'].scrollLeft =
- selectedTab.content._savedScrollLeft || 0;
- }
- },
+ if (!this.selectedSubView_)
+ this.selectedSubView = subView;
- /**
- * This function clears the currently selected tab. This handles removal
- * of the selected attribute from the light-dom element.
- */
- clearSelectedTab_: function() {
- var selectedTab = this.get('selectedTab_');
- if (selectedTab) {
- // Disconnect observer while we mutate the child.
- selectedTab.observers.forAttributeSelected.disconnect();
- Polymer.dom(selectedTab.content).removeAttribute('selected');
- // Reconnect the observer to watch for changes in the future.
- selectedTab.observers.forAttributeSelected.observe(
- selectedTab.content, { attributeFilter: ['selected'] });
- this.set('selectedTab_', undefined);
- }
+ this.push('subViews_', subView);
},
- computeTabLabel_: function(tab) {
- return tab.label ? tab.label : 'No Label';
+ onTabChanged_: function(event) {
+ this.selectedSubView = event.model.item;
},
- computeTabSelectState_: function(selectedTab, tab) {
- return selectedTab.id === tab.id;
+ isChecked_: function(subView) {
+ return this.selectedSubView_ === subView;
}
-
});
</script>
« no previous file with comments | « tracing/tracing/ui/analysis/analysis_view_test.html ('k') | tracing/tracing/ui/base/tab_view_test.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698