extension/Shared (Extension)/Resources/SearchComponent.mjs

Summary

Maintainability
Test Coverage
const template = document.createElement("template")
template.innerHTML = `<style>
:host {
  display: inline-block;
}
input {
  width: 100%;
}
</style>
<input list="values" type="search" part="input">
<datalist id="values">
</datalist>`

export class SearchComponent extends HTMLElement {
  /**
   * @readonly
   * @type {string}
   */
  static NAME = "rr0-search"

  #siteIndex

  #loading = false

  /**
   * @type string
   */
  #prefix

  constructor () {
    super()
    this.shadow = this.attachShadow({ mode: "closed" })
    this.shadow.appendChild(template.content.cloneNode(true))
  }

  connectedCallback () {
    this.onmouseover = this.#siteSearchLoad.bind(this)
    const input = this.shadow.querySelector("input")
    input.oninput = this.#siteSearchChange.bind(this)
    this.#prefix = this.getAttribute("url-prefix") || "https://rr0.org/"
    input.placeholder = this.getAttribute("placeholder") || "Recherche"
  }

  #siteSearchChange (e) {
    if (e.inputType === "insertReplacementText" || e.inputType == null) {
      const value = e.target.value.trim()
      const pageIndex = this.#siteIndex.pages.findIndex(page => page.title === value)
      if (pageIndex >= 0) {
        const page = this.#siteIndex.pages[pageIndex]
        window.location.href = this.#prefix + page.url
      }
    }
  }

  #siteSearchLoad () {
    if (!this.#siteIndex && !this.#loading) {
      this.#loading = true
      fetch(this.#prefix + "search/index.json").then(async (response) => {
        const datalist = this.shadow.querySelector("#values")
        if (response.ok) {
          this.#siteIndex = await response.json()
          for (const page of this.#siteIndex.pages) {
            const option = document.createElement("option")
            option.value = page.title
            datalist.append(option)
          }
        }
        this.#loading = false
      })
    }
  }
}

const name = SearchComponent.NAME
if (!customElements.get(name)) {
  customElements.define(name, SearchComponent)
}