SpeciesFileGroup/taxonworks

View on GitHub
app/assets/javascripts/workbench/pinboard.js

Summary

Maintainability
C
1 day
Test Coverage
var TW = TW || {}
TW.workbench = TW.workbench || {}
TW.workbench.pinboard = TW.workbench.pinboard || {}

Object.assign(TW.workbench.pinboard, {
  storage: undefined,

  init() {
    this.storage = TW.workbench.storage.newStorage()
    this.storage.changeNamespace('/workbench/pinboard')
    this.toggleSectionEvent = this.toggleSection.bind(this)
    this.removePinItemsFromSectionEvent =
      this.removePinItemsFromSection.bind(this)

    this.loadHeaderStatus()
    this.setDefaultClass()
    this.handleEvents()
  },

  createPinboardItem(pinObject) {
    const template = document.createElement('template')
    template.innerHTML = `
      <li 
        class="slide-panel-category-item"
        data-insert="false"
        data-pinboard-object-id="${pinObject.pinned_object_id}"
        data-pinboard-item-id="${pinObject.id}"
        data-pin-item="${pinObject.id}"
        id="order_${pinObject.id}">
        <div class="handle flex-separate middle ui-sortable-handle">
          <a href="${pinObject.pinned_object.object_url}">
            ${pinObject.pinned_object.object_tag}
          </a>
        </div>
        <div class="pinboard-dropdown">
          <div class="pinboard-menu-bar"></div>
          <div class="pinboard-menu-bar"></div>
          <div class="pinboard-menu-bar"></div>
          <div class="itemOptions pinboard-dropdown-content gap-small">
            ${this.createDocuments(pinObject)}
            <a
              href="${pinObject.object_url}"
              class="remove circle-button button-delete"
              data-remote="true" rel="nofollow"
              data-method="delete">
              Remove
            </a>
            <a
              class="circle-button button-pinboard-default button-submit option-default"
              title="Make default"
              data-remote="true"
              rel="nofollow"
              data-method="put"
              href="/pinboard_items/${
                pinObject.id
              }?pinboard_item%5Bis_inserted%5D=true">
              Make default
            </a>
          </div> 
        </div> 
      </li>'
      `.trim()

    return template.content.firstChild
  },

  createDocuments(pinObject) {
    return pinObject.pinned_object_documents
      ? pinObject.pinned_object_documents.map(
          (document) => `
        <span class="pdfviewerItem flexbox gap-small">
          <a
            class="circle-button"
            data-pdfviewer="${document.document_file}"
            data-sourceid="${pinObject.pinned_object.id}">
            PDF Viewer
          </a>
        </span>
        `
        )
      : ''
  },

  createCategory(title) {
    const template = document.createElement('template')
    template.innerHTML = `
      <div id="order_${title}">
        <div class="slide-panel-category-header">${title}</div>
        <ul class="slide-panel-category-content"
          data-pinboard-section="${title}"
          data-sortable
          data-sortable-items="li"
          data-sortable-on-change-url="/pinboard_items/update_position"
        >
        </ul>
      </div>`.trim()

    return template.content.firstChild
  },

  toggleSection(event) {
    const element = event.target

    if (element.classList.contains('slide-panel-category-header')) {
      const sectionElement = element.parentNode.querySelector(
        '.slide-panel-category-content'
      )
      const sectionName = sectionElement.getAttribute('data-pinboard-section')

      this.storage.setItem(sectionName, !this.isExpanded(sectionName))
      sectionElement.classList.toggle('hidden')

      event.preventDefault()
    }
  },

  removePinItemsFromSection(event) {
    const pinboardSection = event.target

    if (pinboardSection.getAttribute('data-delete-all-pinboard-section')) {
      event.preventDefault()
      this.cleanPinboardItems({
        type: pinboardSection.getAttribute('data-delete-all-pinboard-type'),
        section: pinboardSection.getAttribute(
          'data-delete-all-pinboard-section'
        )
      })
    }
  },

  isExpanded(section) {
    return !!this.storage.getItem(section)
  },

  cleanPinboardItems({ type, section }) {
    const CSRFToken = document
      .querySelector('meta[name="csrf-token"]')
      .getAttribute('content')
    const headers = { 'X-CSRF-Token': CSRFToken }
    const dialogMessage = `You're trying to destroy all pinboard items of ${section} section. Are you sure want to proceed?`

    if (window.confirm(dialogMessage)) {
      fetch(`/pinboard_items/clear?klass=${type}`, { method: 'POST', headers })
        .then((response) => {
          if (response.ok) {
            return response.json()
          }

          throw response
        })
        .then(() => {
          const element = document.querySelector(
            `[data-pinboard-section="${section}"]`
          )

          if (element) {
            element.parentElement.remove()
            TW.workbench.alert.create(
              'Pinboard Items were successfully destroyed',
              'notice'
            )
          }
        })
        .catch(() => {})
    }
  },

  getInsertedPin(object) {
    const section = document.querySelector(
      `[data-pinboard-section="${object.pinned_object_section}"]`
    )

    return section && section.querySelector('[data-insert="true"]')
  },

  setDefaultClass() {
    document
      .querySelectorAll('[data-panel-name="pinboard"] [data-insert]')
      .forEach((element) => {
        if (element.getAttribute('data-insert') === 'true') {
          element.classList.add('pinboard-default-item')
        } else {
          element.classList.remove('pinboard-default-item')
        }
      })
  },

  loadHeaderStatus() {
    document
      .querySelectorAll('.slide-panel-category-header')
      .forEach((element) => {
        const content = element.parentNode.querySelector(
          '.slide-panel-category-content'
        )
        const sectionName = content.getAttribute('data-pinboard-section')

        if (this.isExpanded(sectionName)) {
          content.classList.toggle('hidden')
        }
      })
  },

  removeItem(id) {
    const element = document.querySelector(`[data-pinboard-item-id="${id}"]`)
    const section = element.parentNode

    if (section.querySelectorAll('li').length > 1) {
      element.remove()
    } else {
      section.parentNode.remove()
    }

    this.eventPinboardRemove(id)
  },

  addToPinboard(object) {
    const insertedItem = this.getInsertedPin(object)
    const pinboardItemElement = this.createPinboardItem(object)
    let sectionElement = document.querySelector(
      `[data-pinboard-section="${object.pinned_object_section}"]`
    )

    document
      .querySelectorAll('.slide-pinboard .empty-message')
      .forEach((element) => {
        element.remove()
      })

    if (insertedItem) {
      this.changeLink(insertedItem, false)
    }

    if (!sectionElement) {
      sectionElement = this.createCategory(object.pinned_object_section)
      sectionElement.querySelector('ul').append(pinboardItemElement)
      document.querySelector('#pinboard').append(sectionElement)
    } else {
      sectionElement.append(pinboardItemElement)
    }
    this.changeLink(pinboardItemElement, object.is_inserted)
    this.setDefaultClass()
    this.eventPinboardAdd(object)

    if (object.is_inserted) {
      this.eventPinboardInsert(object)
    }
  },

  changeLink(pinElement, inserted) {
    pinElement.setAttribute('data-insert', inserted)
    pinElement
      .querySelector('.itemOptions')
      .replaceChild(
        this.createDefaultLink(pinElement.dataset.pinboardItemId, !inserted),
        pinElement.querySelector('.itemOptions .option-default')
      )
  },

  createDefaultLink(id, inserted) {
    const newEl = document.createElement('a')

    newEl.innerHTML = inserted ? 'Make default' : 'Disable default'
    newEl.setAttribute(
      'href',
      `/pinboard_items/${id}?pinboard_item%5Bis_inserted%5D=${inserted}`
    )
    newEl.setAttribute('data-remote', 'true')
    newEl.setAttribute('rel', 'nofollow')
    newEl.setAttribute('title', inserted ? 'Make default' : 'Disable default')
    newEl.setAttribute('data-method', 'put')
    newEl.classList.add(
      'circle-button',
      'button-pinboard-default',
      inserted ? 'button-submit' : 'button-delete',
      'option-default'
    )

    return newEl
  },

  eventPinboardRemove(id) {
    const event = new CustomEvent('pinboard:remove', {
      detail: {
        id
      }
    })
    document.dispatchEvent(event)
  },

  eventPinboardAdd(object) {
    const event = new CustomEvent('pinboard:add', {
      detail: {
        id: object.id,
        type: object.pinned_object_type,
        object_id: object.pinned_object_id
      }
    })
    document.dispatchEvent(event)
  },

  eventPinboardInsert(object) {
    const event = new CustomEvent('pinboard:insert', {
      detail: {
        id: object.id,
        type: object.pinned_object_type,
        object_id: object.pinned_object_id,
        is_inserted: object.is_inserted
      }
    })
    document.dispatchEvent(event)
  },

  handleEvents() {
    const element = document.querySelector('[data-panel-name="pinboard"]')

    element.addEventListener('click', this.toggleSectionEvent)
    element.addEventListener('click', this.removePinItemsFromSectionEvent)
  },

  removeEvents() {
    const element = document.querySelector('[data-panel-name="pinboard"]')

    element.removeEventListener('click', this.toggleSectionEvent)
    element.removeEventListener('click', this.removePinItemsFromSectionEvent)
  }
})

document.addEventListener('turbolinks:load', () => {
  if (document.querySelectorAll('[data-panel-name="pinboard"]').length) {
    TW.workbench.pinboard.removeEvents()
    TW.workbench.pinboard.init()
  }
})