SpeciesFileGroup/taxonworks

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

Summary

Maintainability
A
1 hr
Test Coverage
/*

To active and use:

Add data-help attribute on the elements to make enable the text legend and bubbles on each element.
Example:

<div data-help="This is a test"></div>

*/

var TW = TW || {}
TW.workbench = TW.workbench || {}
TW.workbench.help = TW.workbench.help || {}

Object.assign(TW.workbench.help, {

  init () {
    const helpAttributes = document.querySelectorAll('[data-help]')

    this.removeElements()
    this.createElements()

    if (helpAttributes.length) {
      this.glowHelpButton()
    }

    Mousetrap.bind('alt+shift+/', () => { this.toggleHelp() })
    this.elementButton.addEventListener('click', () => { this.toggleHelp() })
    this.elementBackground.addEventListener('click', () => { this.toggleHelp() })
  },

  getHeight (element) {
    return parseFloat(getComputedStyle(element, null).height.replace('px', ''))
  },

  getWidth (element) {
    return parseFloat(getComputedStyle(element, null).width.replace('px', ''))
  },

  getOffset (element) {
    const rect = element.getBoundingClientRect()

    return {
      top: rect.top + window.scrollY,
      left: rect.left + window.scrollX
    }
  },

  glowHelpButton () {
    document.querySelector('.help-button').classList.add('help-button-present')
  },

  attachMouseEvent (bubbleElement) {
    bubbleElement.addEventListener('mouseenter', event => {
      const elementLegend = document.querySelector('.help-legend')
      const element = event.target
      const position = this.getOffset(element)

      this.elementLegend.textContent = ''
      this.elementLegend.style.top = `${(position.top + this.getHeight(element))}px`
      this.elementLegend.style.maxWidth = ''
      this.elementLegend.classList.add('help-legend__active')

      this.elementLegend.innerHTML = element.parentElement.getAttribute('data-help')

      const containerLegend = this.getWidth(elementLegend)
      const distanceRight = window.innerWidth - position.left

      if (containerLegend > distanceRight) {
        this.elementLegend.classList.add('tooltip-help-legend-right')
        this.elementLegend.classList.remove('tooltip-help-legend-left')

        this.elementLegend.style.left = ''
        this.elementLegend.style.right = distanceRight - this.getWidth(element) + 'px'
        this.elementLegend.style.maxWidth = window.innerWidth - distanceRight + 'px'
      } else {
        this.elementLegend.classList.remove('tooltip-help-legend-right')
        this.elementLegend.classList.add('tooltip-help-legend-left')
        this.elementLegend.style.left = position.left + 'px'
        this.elementLegend.style.right = ''
      }

      this.hideAllExcept(element.getAttribute('data-bubble-id'))
    })

    bubbleElement.addEventListener('mouseleave', event => {
      const element = event.target

      if (element.classList.contains('help-bubble-tip')) {
        this.elementLegend.textContent = ''
        this.elementLegend.classList.remove('help-legend__active')
        this.elementLegend.style.maxWidth = ''
        this.showAll('.help-bubble-tip')
      }
    })
  },

  createElements () {
    this.elementLegend = document.createElement('div')
    this.elementBackground = document.createElement('div')
    this.elementButton = document.createElement('div')
    this.elementDescription = document.createElement('div')

    this.elementLegend.classList.add('help-legend')
    this.elementBackground.classList.add('help-background')
    this.elementButton.classList.add('help-button')
    this.elementDescription.classList.add('help-button-description')
    this.elementDescription.textContent = 'Help'

    this.elementButton.append(this.elementDescription)

    document.body.append(
      this.elementLegend,
      this.elementBackground,
      this.elementButton
    )
  },

  removeElements () {
    const selectors = [
      '.help-bubble-tip',
      '.help-background',
      '.help-button',
      '.help-legend'
    ]

    selectors.forEach(selector => { this.removeAllElements(selector) })
  },

  addBubbleTips (selector) {
    [...document.querySelectorAll(selector)].forEach((el, i) => {
      const bubbleCreated = el.querySelector('.help-bubble-tip')

      if (!bubbleCreated) {
        el.append(this.createBubble(i + 1))
      } else {
        this.attachMouseEvent(bubbleCreated)
      }
    })
  },

  createBubble (index) {
    const bubbleElement = document.createElement('div')

    bubbleElement.classList.add('help-bubble-tip')
    bubbleElement.setAttribute('data-bubble-id', index)
    bubbleElement.textContent = index

    this.attachMouseEvent(bubbleElement)

    return bubbleElement
  },

  toggleHelp () {
    if (this.isActive()) {
      this.disableHelp()
    } else {
      this.activateHelp()
    }
  },

  removeAllElements (selector) {
    const bubbleEements = document.querySelectorAll(selector)

    bubbleEements.forEach(el => { el.remove() })
  },

  activateHelp () {
    const helpElements = document.querySelectorAll('[data-help]')

    this.addBubbleTips('[data-help]')

    this.elementBackground.classList.add('help-background__active')
    this.elementButton.classList.add('help-button-active')
    this.elementLegend.textContent = ''

    helpElements.forEach(element => {
      element.classList.add('help-tip')
    })

    this.showAll('.help-bubble-tip')
  },

  disableHelp () {
    const helpElements = document.querySelectorAll('[data-help]')
    this.elementBackground.classList.remove('help-background__active')
    this.elementButton.classList.remove('help-button-active')
    this.elementLegend.classList.remove('.help-legend__active')

    helpElements.forEach(element => {
      element.classList.remove('help-tip')
    })

    this.removeAllElements('.help-bubble-tip')
  },

  isActive () {
    return this.elementBackground.classList.contains('help-background__active')
  },

  hideAllExcept (value) {
    const bubbleElements = [...document.querySelectorAll('.help-bubble-tip')]

    bubbleElements.forEach(element => {
      if (element.getAttribute('data-bubble-id') !== value) {
        element.classList.remove('help-bubble-tip__active')
      }
    })
  },

  showAll (className) {
    const bubbleElements = [...document.querySelectorAll(className)]

    bubbleElements.forEach(element => {
      element.classList.add('help-bubble-tip__active')
    })
  }
})

document.addEventListener('turbolinks:load', function () {
  if (document.querySelectorAll('[data-help]').length) {
    TW.workbench.help.init()
  }
})