123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- // Copyright 2008 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /**
- * @fileoverview Tab bar UI component.
- *
- * @author attila@google.com (Attila Bodis)
- * @see ../demos/tabbar.html
- */
- goog.provide('goog.ui.TabBar');
- goog.provide('goog.ui.TabBar.Location');
- goog.require('goog.ui.Component.EventType');
- goog.require('goog.ui.Container');
- goog.require('goog.ui.Container.Orientation');
- // We need to include following dependency because of the magic with
- // goog.ui.registry.setDecoratorByClassName
- goog.require('goog.ui.Tab');
- goog.require('goog.ui.TabBarRenderer');
- goog.require('goog.ui.registry');
- /**
- * Tab bar UI component. A tab bar contains tabs, rendered above, below,
- * before, or after tab contents. Tabs in tab bars dispatch the following
- * events:
- * <ul>
- * <li>{@link goog.ui.Component.EventType.ACTION} when activated via the
- * keyboard or the mouse,
- * <li>{@link goog.ui.Component.EventType.SELECT} when selected, and
- * <li>{@link goog.ui.Component.EventType.UNSELECT} when deselected.
- * </ul>
- * Clients may listen for all of the above events on the tab bar itself, and
- * refer to the event target to identify the tab that dispatched the event.
- * When an unselected tab is clicked for the first time, it dispatches both a
- * {@code SELECT} event and an {@code ACTION} event; subsequent clicks on an
- * already selected tab only result in {@code ACTION} events.
- *
- * @param {goog.ui.TabBar.Location=} opt_location Tab bar location; defaults to
- * {@link goog.ui.TabBar.Location.TOP}.
- * @param {goog.ui.TabBarRenderer=} opt_renderer Renderer used to render or
- * decorate the container; defaults to {@link goog.ui.TabBarRenderer}.
- * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for document
- * interaction.
- * @constructor
- * @extends {goog.ui.Container}
- */
- goog.ui.TabBar = function(opt_location, opt_renderer, opt_domHelper) {
- this.setLocation(opt_location || goog.ui.TabBar.Location.TOP);
- goog.ui.Container.call(
- this, this.getOrientation(),
- opt_renderer || goog.ui.TabBarRenderer.getInstance(), opt_domHelper);
- this.listenToTabEvents_();
- };
- goog.inherits(goog.ui.TabBar, goog.ui.Container);
- goog.tagUnsealableClass(goog.ui.TabBar);
- /**
- * Tab bar location relative to tab contents.
- * @enum {string}
- */
- goog.ui.TabBar.Location = {
- // Above tab contents.
- TOP: 'top',
- // Below tab contents.
- BOTTOM: 'bottom',
- // To the left of tab contents (to the right if the page is right-to-left).
- START: 'start',
- // To the right of tab contents (to the left if the page is right-to-left).
- END: 'end'
- };
- /**
- * Tab bar location; defaults to {@link goog.ui.TabBar.Location.TOP}.
- * @type {goog.ui.TabBar.Location}
- * @private
- */
- goog.ui.TabBar.prototype.location_;
- /**
- * Whether keyboard navigation should change the selected tab, or just move
- * the highlight. Defaults to true.
- * @type {boolean}
- * @private
- */
- goog.ui.TabBar.prototype.autoSelectTabs_ = true;
- /**
- * The currently selected tab (null if none).
- * @type {goog.ui.Control?}
- * @private
- */
- goog.ui.TabBar.prototype.selectedTab_ = null;
- /**
- * @override
- */
- goog.ui.TabBar.prototype.enterDocument = function() {
- goog.ui.TabBar.superClass_.enterDocument.call(this);
- this.listenToTabEvents_();
- };
- /** @override */
- goog.ui.TabBar.prototype.disposeInternal = function() {
- goog.ui.TabBar.superClass_.disposeInternal.call(this);
- this.selectedTab_ = null;
- };
- /**
- * Removes the tab from the tab bar. Overrides the superclass implementation
- * by deselecting the tab being removed. Since {@link #removeChildAt} uses
- * {@link #removeChild} internally, we only need to override this method.
- * @param {string|goog.ui.Component} tab Tab to remove.
- * @param {boolean=} opt_unrender Whether to call {@code exitDocument} on the
- * removed tab, and detach its DOM from the document (defaults to false).
- * @return {goog.ui.Control} The removed tab, if any.
- * @override
- */
- goog.ui.TabBar.prototype.removeChild = function(tab, opt_unrender) {
- // This actually only accepts goog.ui.Controls. There's a TODO
- // on the superclass method to fix this.
- this.deselectIfSelected(/** @type {goog.ui.Control} */ (tab));
- return goog.ui.TabBar.superClass_.removeChild.call(this, tab, opt_unrender);
- };
- /**
- * @return {goog.ui.TabBar.Location} Tab bar location relative to tab contents.
- */
- goog.ui.TabBar.prototype.getLocation = function() {
- return this.location_;
- };
- /**
- * Sets the location of the tab bar relative to tab contents.
- * @param {goog.ui.TabBar.Location} location Tab bar location relative to tab
- * contents.
- * @throws {Error} If the tab bar has already been rendered.
- */
- goog.ui.TabBar.prototype.setLocation = function(location) {
- // setOrientation() will take care of throwing an error if already rendered.
- this.setOrientation(goog.ui.TabBar.getOrientationFromLocation(location));
- this.location_ = location;
- };
- /**
- * @return {boolean} Whether keyboard navigation should change the selected tab,
- * or just move the highlight.
- */
- goog.ui.TabBar.prototype.isAutoSelectTabs = function() {
- return this.autoSelectTabs_;
- };
- /**
- * Enables or disables auto-selecting tabs using the keyboard. If auto-select
- * is enabled, keyboard navigation switches tabs immediately, otherwise it just
- * moves the highlight.
- * @param {boolean} enable Whether keyboard navigation should change the
- * selected tab, or just move the highlight.
- */
- goog.ui.TabBar.prototype.setAutoSelectTabs = function(enable) {
- this.autoSelectTabs_ = enable;
- };
- /**
- * Highlights the tab at the given index in response to a keyboard event.
- * Overrides the superclass implementation by also selecting the tab if
- * {@link #isAutoSelectTabs} returns true.
- * @param {number} index Index of tab to highlight.
- * @protected
- * @override
- */
- goog.ui.TabBar.prototype.setHighlightedIndexFromKeyEvent = function(index) {
- goog.ui.TabBar.superClass_.setHighlightedIndexFromKeyEvent.call(this, index);
- if (this.autoSelectTabs_) {
- // Immediately select the tab.
- this.setSelectedTabIndex(index);
- }
- };
- /**
- * @return {goog.ui.Control?} The currently selected tab (null if none).
- */
- goog.ui.TabBar.prototype.getSelectedTab = function() {
- return this.selectedTab_;
- };
- /**
- * Selects the given tab.
- * @param {goog.ui.Control?} tab Tab to select (null to select none).
- */
- goog.ui.TabBar.prototype.setSelectedTab = function(tab) {
- if (tab) {
- // Select the tab and have it dispatch a SELECT event, to be handled in
- // handleTabSelect() below.
- tab.setSelected(true);
- } else if (this.getSelectedTab()) {
- // De-select the currently selected tab and have it dispatch an UNSELECT
- // event, to be handled in handleTabUnselect() below.
- this.getSelectedTab().setSelected(false);
- }
- };
- /**
- * @return {number} Index of the currently selected tab (-1 if none).
- */
- goog.ui.TabBar.prototype.getSelectedTabIndex = function() {
- return this.indexOfChild(this.getSelectedTab());
- };
- /**
- * Selects the tab at the given index.
- * @param {number} index Index of the tab to select (-1 to select none).
- */
- goog.ui.TabBar.prototype.setSelectedTabIndex = function(index) {
- this.setSelectedTab(/** @type {goog.ui.Tab} */ (this.getChildAt(index)));
- };
- /**
- * If the specified tab is the currently selected tab, deselects it, and
- * selects the closest selectable tab in the tab bar (first looking before,
- * then after the deselected tab). Does nothing if the argument is not the
- * currently selected tab. Called internally when a tab is removed, hidden,
- * or disabled, to ensure that another tab is selected instead.
- * @param {goog.ui.Control?} tab Tab to deselect (if any).
- * @protected
- */
- goog.ui.TabBar.prototype.deselectIfSelected = function(tab) {
- if (tab && tab == this.getSelectedTab()) {
- var index = this.indexOfChild(tab);
- // First look for the closest selectable tab before this one.
- for (var i = index - 1;
- tab = /** @type {goog.ui.Tab} */ (this.getChildAt(i)); i--) {
- if (this.isSelectableTab(tab)) {
- this.setSelectedTab(tab);
- return;
- }
- }
- // Next, look for the closest selectable tab after this one.
- for (var j = index + 1;
- tab = /** @type {goog.ui.Tab} */ (this.getChildAt(j)); j++) {
- if (this.isSelectableTab(tab)) {
- this.setSelectedTab(tab);
- return;
- }
- }
- // If all else fails, just set the selection to null.
- this.setSelectedTab(null);
- }
- };
- /**
- * Returns true if the tab is selectable, false otherwise. Only visible and
- * enabled tabs are selectable.
- * @param {goog.ui.Control} tab Tab to check.
- * @return {boolean} Whether the tab is selectable.
- * @protected
- */
- goog.ui.TabBar.prototype.isSelectableTab = function(tab) {
- return tab.isVisible() && tab.isEnabled();
- };
- /**
- * Handles {@code SELECT} events dispatched by tabs as they become selected.
- * @param {goog.events.Event} e Select event to handle.
- * @protected
- */
- goog.ui.TabBar.prototype.handleTabSelect = function(e) {
- if (this.selectedTab_ && this.selectedTab_ != e.target) {
- // Deselect currently selected tab.
- this.selectedTab_.setSelected(false);
- }
- this.selectedTab_ = /** @type {goog.ui.Tab} */ (e.target);
- };
- /**
- * Handles {@code UNSELECT} events dispatched by tabs as they become deselected.
- * @param {goog.events.Event} e Unselect event to handle.
- * @protected
- */
- goog.ui.TabBar.prototype.handleTabUnselect = function(e) {
- if (e.target == this.selectedTab_) {
- this.selectedTab_ = null;
- }
- };
- /**
- * Handles {@code DISABLE} events displayed by tabs.
- * @param {goog.events.Event} e Disable event to handle.
- * @protected
- */
- goog.ui.TabBar.prototype.handleTabDisable = function(e) {
- this.deselectIfSelected(/** @type {goog.ui.Tab} */ (e.target));
- };
- /**
- * Handles {@code HIDE} events displayed by tabs.
- * @param {goog.events.Event} e Hide event to handle.
- * @protected
- */
- goog.ui.TabBar.prototype.handleTabHide = function(e) {
- this.deselectIfSelected(/** @type {goog.ui.Tab} */ (e.target));
- };
- /**
- * Handles focus events dispatched by the tab bar's key event target. If no tab
- * is currently highlighted, highlights the selected tab or the first tab if no
- * tab is selected either.
- * @param {goog.events.Event} e Focus event to handle.
- * @protected
- * @override
- */
- goog.ui.TabBar.prototype.handleFocus = function(e) {
- if (!this.getHighlighted()) {
- this.setHighlighted(
- this.getSelectedTab() ||
- /** @type {goog.ui.Tab} */ (this.getChildAt(0)));
- }
- };
- /**
- * Subscribes to events dispatched by tabs.
- * @private
- */
- goog.ui.TabBar.prototype.listenToTabEvents_ = function() {
- // Listen for SELECT, UNSELECT, DISABLE, and HIDE events dispatched by tabs.
- this.getHandler()
- .listen(this, goog.ui.Component.EventType.SELECT, this.handleTabSelect)
- .listen(
- this, goog.ui.Component.EventType.UNSELECT, this.handleTabUnselect)
- .listen(this, goog.ui.Component.EventType.DISABLE, this.handleTabDisable)
- .listen(this, goog.ui.Component.EventType.HIDE, this.handleTabHide);
- };
- /**
- * Returns the {@link goog.ui.Container.Orientation} that is implied by the
- * given {@link goog.ui.TabBar.Location}.
- * @param {goog.ui.TabBar.Location} location Tab bar location.
- * @return {goog.ui.Container.Orientation} Corresponding orientation.
- */
- goog.ui.TabBar.getOrientationFromLocation = function(location) {
- return location == goog.ui.TabBar.Location.START ||
- location == goog.ui.TabBar.Location.END ?
- goog.ui.Container.Orientation.VERTICAL :
- goog.ui.Container.Orientation.HORIZONTAL;
- };
- // Register a decorator factory function for goog.ui.TabBars.
- goog.ui.registry.setDecoratorByClassName(
- goog.ui.TabBarRenderer.CSS_CLASS,
- function() { return new goog.ui.TabBar(); });
|