chromium/ash/webui/common/resources/network/network_list.js

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

/**
 * @fileoverview Polymer element for displaying a collapsable list of networks.
 */

import './network_list_item.js';
import '//resources/ash/common/cr_elements/cr_shared_style.css.js';
import '//resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
import '//resources/polymer/v3_0/iron-list/iron-list.js';

import {CrScrollableBehavior, CrScrollableBehaviorInterface} from '//resources/ash/common/cr_scrollable_behavior.js';
import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from '//resources/ash/common/list_property_update_behavior.js';
import {GlobalPolicy} from '//resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
import {microTask, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {getTemplate} from './network_list.html.js';
import {NetworkList} from './network_list_types.js';
import {OncMojo} from './onc_mojo.js';

/**
 * Polymer class definition for 'network-list'.
 */

/**
 * @constructor
 * @extends {PolymerElement}
 * @implements {CrScrollableBehaviorInterface}
 * @implements {ListPropertyUpdateBehaviorInterface}
 */
const NetworkListElementBase = mixinBehaviors(
    [CrScrollableBehavior, ListPropertyUpdateBehavior], PolymerElement);

/** @polymer */
class NetworkListElement extends NetworkListElementBase {
  static get is() {
    return 'network-list';
  }

  static get template() {
    return getTemplate();
  }

  static get properties() {
    return {
      /**
       * The list of network state properties for the items to display.
       * @type {!Array<!OncMojo.NetworkStateProperties>}
       */
      networks: {
        type: Array,
        value() {
          return [];
        },
      },

      /**
       * The list of custom items to display after the list of networks.
       * @type {!Array<!NetworkList.CustomItemState>}
       */
      customItems: {
        type: Array,
        value() {
          return [];
        },
      },

      /** True if action buttons should be shown for the items. */
      showButtons: {
        type: Boolean,
        value: false,
        reflectToAttribute: true,
      },

      /** Whether to show technology badges on mobile network icons. */
      showTechnologyBadge: {type: Boolean, value: true},

      /**
       * Reflects the iron-list selecteditem property.
       * @type {!NetworkList.NetworkListItemType}
       */
      selectedItem: {
        type: Object,
        observer: 'selectedItemChanged_',
      },

      /** Whether cellular activation is unavailable in the current context. */
      activationUnavailable: Boolean,

      /**
       * DeviceState associated with the type of |networks| listed, or undefined
       * if none was provided.
       * @private {!OncMojo.DeviceStateProperties|undefined} deviceState
       */
      deviceState: Object,

      /** @type {!GlobalPolicy|undefined} */
      globalPolicy: Object,

      isBuiltInVpnManagementBlocked: {
        type: Boolean,
        value: false,
      },

      /**
       * Contains |networks| + |customItems|.
       * @private {!Array<!NetworkList.NetworkListItemType>}
       */
      listItems_: {
        type: Array,
        value() {
          return [];
        },
      },

      /**
       * Used by FocusRowBehavior to track the last focused element on a row.
       * @private
       */
      lastFocused_: Object,

      /**
       * Used by FocusRowBehavior to track if the list has been blurred.
       * @private
       */
      listBlurred_: Boolean,

      /** Disables all the network items. */
      disabled: Boolean,
    };
  }

  static get observers() {
    return ['updateListItems_(networks, customItems)'];
  }

  /** @override */
  constructor() {
    super();

    /**
     * @private @type {ResizeObserver} used to observer size changes to this
     *     element
     */
    this.resizeObserver_ = null;

    /** @private {boolean} */
    this.focusRequested_ = false;
  }

  /** @override */
  connectedCallback() {
    super.connectedCallback();

    // This is a required work around to get the iron-list to display on first
    // view. Currently iron-list won't generate item elements on attach if the
    // element is not visible. Because there are some instances where this
    // component might not be visible when the items are bound, we listen for
    // resize events and manually call notifyResize on the iron-list
    this.resizeObserver_ = new ResizeObserver(entries => {
      const networkList =
          /** @type {IronListElement} */ (
              this.shadowRoot.querySelector('#networkList'));
      if (networkList) {
        networkList.notifyResize();
      }
    });
    this.resizeObserver_.observe(this);
  }

  /** @override */
  disconnectedCallback() {
    super.disconnectedCallback();

    this.resizeObserver_.disconnect();
  }

  focus() {
    this.focusRequested_ = true;
    this.focusFirstItem_();
  }

  /** @private */
  updateListItems_() {
    const beforeNetworks =
        this.customItems.filter(n => n.showBeforeNetworksList === true);
    const afterNetworks =
        this.customItems.filter(n => n.showBeforeNetworksList === false);
    const newList = beforeNetworks.concat(this.networks, afterNetworks);

    this.updateList('listItems_', item => item.guid, newList);

    this.updateScrollableContents();
    if (this.focusRequested_) {
      microTask.run(function() {
        this.focusFirstItem_();
      });
    }
  }

  /** @private */
  focusFirstItem_() {
    // Select the first network-list-item if there is one.
    const item = this.shadowRoot.querySelector('network-list-item');
    if (!item) {
      return;
    }
    item.focus();
    this.focusRequested_ = false;
  }
}

customElements.define(NetworkListElement.is, NetworkListElement);