// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/* eslint-disable @devtools/no-lit-render-outside-of-view */

import {html, nothing, render} from '../../lit/lit.js';

import cardStyles from './card.css.js';

/**
 * A simple card component to display a Material card with a heading and content.
 *
 * Usage is simple:
 *
 * ```
 * // Instantiate programmatically:
 * const card = document.createElement('devtools-card');
 * card.heading = 'My awesome card';
 * card.append(content1, content2);
 *
 * // Use within a template:
 * html`
 *   <devtools-card heading="My awesome card">
 *     <div>content1</div>
 *     <div>content2</div>
 *   </devtools-card>
 * `;
 * ```
 *
 * The heading can be further customized with a prefix and a suffix if needed.
 * These are arbitrary children that can be slotted into the `"heading-prefix"`
 * and `"heading-suffix"` slots if required. Example:
 *
 * ```
 * html`
 *   <devtools-card heading="Rich heading">
 *     <devtools-icon name="folder" slot="heading-prefix"></devtools-icon>
 *     <devtools-button slot="heading-suffix">Remove</devtools-button>
 *   </devtools-card>
 * `;
 * ```
 *
 * @property heading - The `"heading"` attribute is reflect as property.
 * @attribute heading - The heading text.
 */
export class Card extends HTMLElement {
  static readonly observedAttributes = ['heading'];

  readonly #shadow = this.attachShadow({mode: 'open'});

  constructor() {
    super();
    this.#render();
  }

  /**
   * Yields the value of the `"heading"` attribute of this `Card`.
   *
   * @returns the value of the `"heading"` attribute or `null` if the attribute
   *          is absent.
   */
  get heading(): string|null {
    return this.getAttribute('heading');
  }

  /**
   * Changes the value of the `"heading"` attribute of this `Card`. If you pass
   * `null`, the `"heading"` attribute will be removed from this element.
   *
   * @param heading the new heading of `null` to unset.
   */
  set heading(heading: string|null) {
    if (heading) {
      this.setAttribute('heading', heading);
    } else {
      this.removeAttribute('heading');
    }
  }

  attributeChangedCallback(_name: string, oldValue: string|null, newValue: string|null): void {
    if (oldValue !== newValue) {
      this.#render();
    }
  }

  #render(): void {
    render(
        html`
        <style>${cardStyles}</style>
        <div id="card">
          <div id="heading">
            <slot name="heading-prefix"></slot>
            <div role="heading" aria-level="2">${this.heading ?? nothing}</div>
            <slot name="heading-suffix"></slot>
          </div>
          <slot id="content"></slot>
        </div>`,
        this.#shadow, {host: this});
  }
}

customElements.define('devtools-card', Card);

declare global {
  interface HTMLElementTagNameMap {
    'devtools-card': Card;
  }
}
