// Copyright (c) 2011 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.
/**
* @fileoverview This implements a tab control.
*
* An individual tab within a tab control is, unsurprisingly, a Tab.
* Tabs must be explicitly added/removed from the control.
*
* Tab titles are based on the label attribute of each child:
*
* <div>
* <div label='Tab 1'>Hello</div>
* <div label='Tab 2'>World</div>
* </div>
*
* Results in:
*
* ---------------
* | Tab1 | Tab2 |
* | ---------------------------------------
* | Hello World |
* -----------------------------------------
*
*/
cr.define('gpu', function() {
/**
* Creates a new tab element. A tab element is one of multiple tabs
* within a TabControl.
* @constructor
* @param {Object=} opt_propertyBag Optional properties.
* @extends {HTMLDivElement}
*/
var Tab = cr.ui.define('div');
Tab.prototype = {
__proto__: HTMLDivElement.prototype,
decorate: function() {
}
};
/**
* Title for the tab.
* @type {String}
*/
cr.defineProperty(Tab, 'label', cr.PropertyKind.ATTR);
/**
* Whether the item is selected.
* @type {boolean}
*/
cr.defineProperty(Tab, 'selected', cr.PropertyKind.BOOL_ATTR);
/**
* Creates a new tab button element in the tabstrip
* @constructor
* @param {Object=} opt_propertyBag Optional properties.
* @extends {HTMLDivElement}
*/
var TabButton = cr.ui.define('a');
TabButton.prototype = {
__proto__: HTMLAnchorElement.prototype,
decorate: function() {
this.classList.add('tab-button');
this.onclick = function() {
if (this.tab_)
this.parentNode.parentNode.selectedTab = this.tab_;
}.bind(this);
},
get tab() {
return this.tab_;
},
set tab(tab) {
if (this.tab_)
throw Error('Cannot set tab once set.');
this.tab_ = tab;
this.tab_.addEventListener('titleChange', this.onTabChanged_.bind(this));
this.tab_.addEventListener('selectedChange',
this.onTabChanged_.bind(this));
this.onTabChanged_();
},
onTabChanged_: function(e) {
if (this.tab_) {
this.textContent = this.tab_.label;
this.selected = this.tab_.selected;
}
}
};
/**
* Whether the TabButton is selected.
* @type {boolean}
*/
cr.defineProperty(TabButton, 'selected', cr.PropertyKind.BOOL_ATTR);
/**
* Creates a new tab control element.
* @param {Object=} opt_propertyBag Optional properties.
* @constructor
* @extends {HTMLDivElement}
*/
var TabControl = cr.ui.define('div');
TabControl.prototype = {
__proto__: HTMLDivElement.prototype,
selectedTab_: null,
/**
* Initializes the tab control element.
* Any child elements pre-existing on the element will become tabs.
*/
decorate: function() {
this.classList.add('tab-control');
this.tabStrip_ = this.ownerDocument.createElement('div');
this.tabStrip_.classList.add('tab-strip');
this.tabs_ = this.ownerDocument.createElement('div');
this.tabs_.classList.add('tabs');
this.insertBefore(this.tabs_, this.firstChild);
this.insertBefore(this.tabStrip_, this.firstChild);
this.boundOnTabSelectedChange_ = this.onTabSelectedChange_.bind(this);
// Reparent existing tabs to the tabs_ div.
while (this.children.length > 2)
this.addTab(this.children[2]);
},
/**
* Adds an element to the tab control.
*/
addTab: function(tab) {
if (tab.parentNode == this.tabs_)
throw Error('Tab is already part of this control.');
if (!(tab instanceof Tab))
throw Error('Provided element is not instanceof Tab.');
this.tabs_.appendChild(tab);
tab.addEventListener('selectedChange', this.boundOnTabSelectedChange_);
var button = new TabButton();
button.tab = tab;
tab.tabStripButton_ = button;
this.tabStrip_.appendChild(button);
if (this.tabs_.length == 1)
this.tabs_.children[0].selected = true;
},
/**
* Removes a tab from the tab control.
* changing the selected tab if needed.
*/
removeTab: function(tab) {
if (tab.parentNode != this.tabs_)
throw new Error('Tab is not attached to this control.');
tab.removeEventListener('selectedChange', this.boundOnTabSelectedChange_);
if (this.selectedTab_ == tab) {
if (this.tabs_.children.length) {
this.tabs_.children[0].selected = true;
} else {
this.selectedTab_ = undefined;
}
}
this.tabs_.removeChild(tab);
tab.tabStripButton_.parentNode.removeChild(
tab.tabStripButton_);
},
/**
* Gets the currently selected tab element.
*/
get selectedTab() {
return this.selectedTab_;
},
/**
* Sets the currently selected tab element.
*/
set selectedTab(tab) {
if (tab.parentNode != this.tabs_)
throw Error('Tab is not part of this TabControl.');
tab.selected = true;
},
/**
* Hides the previously selected tab element and dispatches a
* 'selectedTabChanged' event.
*/
onTabSelectedChange_: function(e) {
var tab = e.target;
if (!e.newValue) {
// Usually we can ignore this event, as the tab becoming unselected
// needs no corrective action. However, if the currently selected
// tab is deselected, we do need to do some work.
if (tab == this.selectedTab_) {
var previousTab = this.selectedTab_;
var newTab;
for (var i = 0; i < this.tabs_.children.length; ++i) {
if (this.tabs_.children[i] != tab) {
newTab = this.tabs_.children[i];
break;
}
}
if (newTab) {
newTab.selected = true;
} else {
this.selectedTab_ = undefined;
cr.dispatchPropertyChange(
this, 'selectedTab', this.selectedTab_, previousTab);
}
}
} else {
var previousTab = this.selectedTab_;
this.selectedTab_ = tab;
if (previousTab)
previousTab.selected = false;
cr.dispatchPropertyChange(
this, 'selectedTab', this.selectedTab_, previousTab);
}
},
/**
* Returns an array of all the tabs within this control. This is
* not the same as this.children because the actual tab elements are
* attached to the tabs_ element.
*/
get tabs() {
return Array.prototype.slice.call(this.tabs_.children);
}
};
return {
Tab: Tab,
TabControl: TabControl
};
});