chromium/chrome/browser/resources/net_internals/tab_switcher_view.js

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import {$} from 'chrome://resources/js/util.js';

import {addNode, addNodeWithText, setNodeDisplay, setNodePosition} from './util.js';
import {View} from './view.js';

const TAB_LIST_ID = 'tab-list';

/** @type {?TabSwitcherView} */
let instance = null;

/**
 * Controller and View for switching between tabs.
 *
 * The View part of TabSwitcherView displays the contents of the currently
 * selected tab (only one tab can be active at a time).
 *
 * The controller part of TabSwitcherView hooks up a dropdown menu (i.e. HTML
 * SELECT) to control switching between tabs.
 */
export class TabSwitcherView extends View {
  constructor() {
    super();

    this.tabIdToView_ = {};
    this.tabIdToLink_ = {};
    // Map from tab id to the views link visiblity.
    this.tabIdsLinkVisibility_ = new Map();
    this.activeTabId_ = null;
    this.onTabSwitched_ = null;

    // The ideal width of the tab list.  If width is reduced below this, the
    // tab list will be shrunk, but it will be returned to this width once it
    // can be.
    this.tabListWidth_ = $(TAB_LIST_ID).offsetWidth;
  }

  /**
   * @param {!Function} onTabSwitched Callback to run when the
   *                    active tab changes. Called as
   *                    onTabSwitched(oldTabId, newTabId).
   */
  setOnTabSwitched(onTabSwitched) {
    this.onTabSwitched_ = onTabSwitched;
  }

  // ---------------------------------------------
  // Override methods in View
  // ---------------------------------------------

  setGeometry(left, top, width, height) {
    super.setGeometry(this, left, top, width, height);

    const tabListNode = $(TAB_LIST_ID);

    // Set position of the tab list.  Can't use DivView because DivView sets
    // a fixed width at creation time, and need to set the width of the tab
    // list only after its been populated.
    let tabListWidth = this.tabListWidth_;
    if (tabListWidth > width) {
      tabListWidth = width;
    }
    tabListNode.style.position = 'absolute';
    setNodePosition(tabListNode, left, top, tabListWidth, height);

    // Position each of the tab's content areas.
    for (const tabId in this.tabIdToView_) {
      const view = this.tabIdToView_[tabId];
      view.setGeometry(left + tabListWidth, top, width - tabListWidth, height);
    }
  }

  show(isVisible) {
    super.show(isVisible);
    const activeView = this.getActiveTabView();
    if (activeView) {
      activeView.show(isVisible);
    }
  }

  // ---------------------------------------------

  /**
   * Adds a new tab (initially hidden).  To ensure correct tab list sizing,
   * may only be called before first layout.
   *
   * @param {string} tabId The ID to refer to the tab by.
   * @param {!View} view The tab's actual contents.
   * @param {string} name The name for the menu item that selects the tab.
   */
  addTab(tabId, view, name, hash) {
    if (!tabId) {
      throw Error('Must specify a non-false tabId');
    }

    this.tabIdToView_[tabId] = view;
    this.tabIdsLinkVisibility_.set(tabId, true);

    const node = addNodeWithText($(TAB_LIST_ID), 'a', name);
    node.href = hash;
    this.tabIdToLink_[tabId] = node;
    addNode($(TAB_LIST_ID), 'br');

    // Tab content views start off hidden.
    view.show(false);

    this.tabListWidth_ = $(TAB_LIST_ID).offsetWidth;
  }

  getAllTabViews() {
    return this.tabIdToView_;
  }

  getTabView(tabId) {
    return this.tabIdToView_[tabId];
  }

  getActiveTabView() {
    return this.tabIdToView_[this.activeTabId_];
  }

  getActiveTabId() {
    return this.activeTabId_;
  }

  /**
   * Changes the currently active tab to |tabId|. This has several effects:
   *   (1) Replace the tab contents view with that of the new tab.
   *   (2) Update the dropdown menu's current selection.
   *   (3) Invoke the optional onTabSwitched callback.
   */
  switchToTab(tabId) {
    const newView = this.getTabView(tabId);

    if (!newView) {
      throw Error('Invalid tabId');
    }

    const oldTabId = this.activeTabId_;
    this.activeTabId_ = tabId;

    if (oldTabId) {
      this.tabIdToLink_[oldTabId].classList.remove('selected');
      // Hide the previously visible tab contents.
      this.getTabView(oldTabId).show(false);
    }

    this.tabIdToLink_[tabId].classList.add('selected');

    newView.show(this.isVisible());

    if (this.onTabSwitched_) {
      this.onTabSwitched_(oldTabId, tabId);
    }
  }

  static getInstance() {
    return instance || (instance = new TabSwitcherView());
  }
}