chromium/ash/webui/common/resources/cr_elements/cr_lazy_render/cr_lazy_render.ts

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

/**
 * @fileoverview
 * cr-lazy-render is a simple variant of dom-if designed for lazy rendering
 * of elements that are accessed imperatively.
 * Usage:
 *   <cr-lazy-render id="menu">
 *     <template>
 *       <heavy-menu></heavy-menu>
 *     </template>
 *   </cr-lazy-render>
 *
 *   this.$.menu.get().show();
 *
 * Forked from
 * ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.ts
 */

import {assert} from '//resources/js/assert.js';
import {html, PolymerElement, TemplateInstanceBase, templatize} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';

export class CrLazyRenderElement<T extends HTMLElement> extends PolymerElement {
  static get is() {
    return 'cr-lazy-render';
  }

  static get template() {
    return html`<slot></slot>`;
  }

  private child_: T|null = null;
  private instance_: TemplateInstanceBase|null = null;

  /**
   * Stamp the template into the DOM tree synchronously
   * @return Child element which has been stamped into the DOM tree.
   */
  override get(): T {
    if (!this.child_) {
      this.render_();
    }
    assert(this.child_);
    return this.child_;
  }

  /**
   * @return The element contained in the template, if it has
   *   already been stamped.
   */
  getIfExists(): (T|null) {
    return this.child_;
  }

  private render_() {
    const template =
        (this.shadowRoot!.querySelector('slot')!.assignedNodes({flatten: true})
             .filter(n => n.nodeType === Node.ELEMENT_NODE)[0]) as
        HTMLTemplateElement;

    const TemplateClass = templatize(template, this, {
      mutableData: false,
      forwardHostProp: this._forwardHostPropV2,
    });
    const parentNode = this.parentNode;
    if (parentNode && !this.child_) {
      this.instance_ = new TemplateClass();
      this.child_ = this.instance_.root.firstElementChild as T;
      parentNode.insertBefore(this.instance_.root, this);
    }
  }

  /* eslint-disable-next-line @typescript-eslint/naming-convention */
  _forwardHostPropV2(prop: string, value: object) {
    if (this.instance_) {
      this.instance_.forwardHostProp(prop, value);
    }
  }
}

customElements.define(CrLazyRenderElement.is, CrLazyRenderElement);