AlchemyCMS/alchemy_cms

View on GitHub
app/javascript/alchemy_admin/link_dialog.js

Summary

Maintainability
A
0 mins
Test Coverage
import { translate } from "alchemy_admin/i18n"

// Represents the link Dialog that appears, if a user clicks the link buttons
// in TinyMCE or on an Ingredient that has links enabled (e.g. Picture)
//
export class LinkDialog extends Alchemy.Dialog {
  #onCreateLink

  constructor(link) {
    const url = new URL(Alchemy.routes.link_admin_pages_path, window.location)
    const parameterMapping = {
      url: link.url,
      selected_tab: link.type,
      link_title: link.title,
      link_target: link.target
    }

    // searchParams.set would also add undefined values
    Object.keys(parameterMapping).forEach((key) => {
      if (parameterMapping[key]) {
        url.searchParams.set(key, parameterMapping[key])
      }
    })

    super(url.href, {
      size: "600x320",
      title: translate("Link")
    })
  }

  /**
   *  Called from Dialog class after the url was loaded
   */
  replace(data) {
    // let Dialog class handle the content replacement
    super.replace(data)
    this.#attachEvents()
  }

  /**
   * make the open method a promise
   * maybe in a future version the whole Dialog will respond with a promise result if the dialog is closing
   * @returns {Promise<unknown>}
   */
  open() {
    super.open()
    return new Promise((resolve) => (this.#onCreateLink = resolve))
  }

  /**
   * Attaches click events to forms in the link dialog.
   */
  #attachEvents() {
    // enable the dom selection in internal link tab
    const internalForm = document.querySelector(
      '[data-link-form-type="internal"]'
    )
    const attachmentSelect = document.querySelector(
      '[data-link-form-type="file"] alchemy-attachment-select'
    )

    internalForm.addEventListener("Alchemy.RemoteSelect.Change", (e) => {
      this.#updatePage(e.detail.added)
    })

    attachmentSelect.addEventListener("Alchemy.RemoteSelect.Change", (e) => {
      const attachment = e.detail.added
      document.getElementById("file_link").value = attachment
        ? attachment.url
        : ""
    })

    document.querySelectorAll("[data-link-form-type]").forEach((form) => {
      form.addEventListener("submit", (e) => {
        e.preventDefault()
        this.#submitForm(e.target.dataset.linkFormType)
      })
    })
  }

  /**
   * update page select and set anchor select
   * @param page
   */
  #updatePage(page = null) {
    const internalLink = document.getElementById("internal_link")
    const domIdSelect = document.querySelector(
      '[data-link-form-type="internal"] alchemy-dom-id-api-select'
    )

    internalLink.value = page ? page.url_path : ""
    domIdSelect.page = page ? page.id : undefined
  }

  /**
   * submit the form itself
   * @param linkType
   */
  #submitForm(linkType) {
    const elementAnchor = document.getElementById("element_anchor")
    let url = document.getElementById(`${linkType}_link`).value

    if (linkType === "internal" && elementAnchor.value !== "") {
      // remove possible fragments on the url and attach the fragment (which contains the #)
      url = url.replace(/#\w+$/, "") + elementAnchor.value
    } else if (linkType === "external" && !url.match(Alchemy.link_url_regexp)) {
      // show validation error and prevent link creation
      this.#showValidationError()
      return
    }

    // Create the link
    this.#onCreateLink({
      url: url.trim(),
      title: document.getElementById(`${linkType}_link_title`).value,
      target: document.getElementById(`${linkType}_link_target`)?.value,
      type: linkType
    })
    this.close()
  }

  /**
   * Shows validation errors
   */
  #showValidationError() {
    const errors = document.getElementById("errors")
    errors.querySelector("ul").innerHTML =
      `<li>${Alchemy.t("url_validation_failed")}</li>`
    errors.style.display = "block"
  }
}