chromium/ash/webui/diagnostics_ui/resources/diagnostics_sticky_banner.ts

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

import 'chrome://resources/ash/common/cr_elements/icons.html.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import './diagnostics_shared.css.js';

import {assert} from 'chrome://resources/js/assert.js';
import {PolymerElementProperties} from 'chrome://resources/polymer/v3_0/polymer/interfaces.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {getTemplate} from './diagnostics_sticky_banner.html.js';

export type ShowCautionBannerEvent = CustomEvent<{message: string}>;

declare global {
  interface HTMLElementEventMap {
    'show-caution-banner': ShowCautionBannerEvent;
    'dismiss-caution-banner': CustomEvent<void>;
  }
}

export class DiagnosticsStickyBannerElement extends PolymerElement {
  static get is(): string {
    return 'diagnostics-sticky-banner';
  }

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

  static get properties(): PolymerElementProperties {
    return {
      bannerMessage: {
        type: String,
        value: '',
        notify: true,
      },

      scrollingClass: {
        type: String,
        value: '',
      },

      scrollTimerId: {
        type: Number,
        value: -1,
      },
    };
  }

  bannerMessage: string;
  protected scrollingClass: string;
  private scrollTimerId: number;

  override connectedCallback(): void {
    super.connectedCallback();
    window.addEventListener(
        'show-caution-banner',
        (e) => this.showCautionBannerHandler((e as CustomEvent)));
    window.addEventListener(
        'dismiss-caution-banner', this.dismissCautionBannerHandler);
    window.addEventListener('scroll', this.scrollClassHandler);
  }

  override disconnectedCallback(): void {
    super.disconnectedCallback();
    window.removeEventListener(
        'show-caution-banner',
        (e) => this.showCautionBannerHandler((e as CustomEvent)));
    window.removeEventListener(
        'dismiss-caution-banner', this.dismissCautionBannerHandler);
    window.removeEventListener('scroll', this.scrollClassHandler);
  }

  /**
   * Event callback for 'show-caution-banner' which is triggered from routine-
   * section. Event will contain message to display on message property of
   * event found on path `event.detail.message`.
   */
  private showCautionBannerHandler = (e: ShowCautionBannerEvent): void => {
    assert(e.detail.message);
    this.bannerMessage = e.detail.message;
  };

  /**
   * Event callback for 'dismiss-caution-banner' which is triggered from
   * routine-section.
   */
  private dismissCautionBannerHandler = (): void => {
    this.bannerMessage = '';
  };

  /**
   * Event callback for 'scroll'.
   */
  private scrollClassHandler = (): void => {
    this.onScroll();
  };

  /**
   * Event handler for 'scroll' to ensure shadow and elevation of banner is
   * correct while scrolling. Timer is used to clear class after 300ms.
   */
  private onScroll(): void {
    if (!this.bannerMessage) {
      return;
    }

    // Reset timer since we've received another 'scroll' event.
    if (this.scrollTimerId !== -1) {
      this.scrollingClass = 'elevation-2';
      clearTimeout(this.scrollTimerId);
    }

    // Remove box shadow from banner since the user has stopped scrolling
    // for at least 300ms.
    this.scrollTimerId = window.setTimeout(() => this.scrollingClass = '', 300);
  }

  getScrollingClassForTesting(): string {
    return this.scrollingClass;
  }

  getScrollTimerIdForTesting(): number {
    return this.scrollTimerId;
  }

}

declare global {
  interface HTMLElementTagNameMap {
    'diagnostics-sticky-banner': DiagnosticsStickyBannerElement;
  }
}

customElements.define(
    DiagnosticsStickyBannerElement.is, DiagnosticsStickyBannerElement);