znamenica/dneslov

View on GitHub
app/components/IconLoryModal.jsx

Summary

Maintainability
A
0 mins
Test Coverage
import { Component } from 'react'
import { lory } from 'lory.js'

import PureImage from 'PureImage'

export default class IconLoryModal extends Component {
   static defaultProps = {
      links: [],
      onLorySlideFrom: null,
   }

   state = {}

   static getDerivedStateFromProps(props, state) {
      if (state.prevProps !== props) {
         return { loadCounter: 0, prevProps: props }
      } else {
         return null
      }
   }

   // system
   constructor(props) {
      super(props)

      this.currentImgIndex = 0
      this.leftOffset = 0
      this.loryResize = this.loryResize.bind(this)
      this.onDocumentClick = this.onDocumentClick.bind(this)
   }

   componentDidMount() {
      this.$lory.addEventListener('after.lory.init', this.loryResize, { passive: true })
      this.$lory.addEventListener('on.lory.resize', this.loryResize, { passive: true })
      document.addEventListener('click', this.onDocumentClick, { passive: true })

   }

   componentWillUnmount() {
      this.$lory.removeEventListener('after.lory.init', this.loryResize)
      this.$lory.removeEventListener('on.lory.resize', this.loryResize)
      document.removeEventListener('click', this.onDocumentClick)
   }

   // events
   stateChanged() {
      this.componentReady()
   }

   componentReady() {
      console.debug("[componentReady] <<<")
      this.modal = M.Modal.init(this.$modal, {})
      console.log("[componentReady] **", this.modal)
      this.lory = lory(this.$lory, {
         slideSpeed: 750,
         enableMouseEvents: true,
         ease: 'cubic-bezier(0.455, 0.03, 0.515, 0.955)',
         rewind: true,
      })
   }

   loryResize(a,e) {
      this.imagePaneWidth = this.getImagePaneWidth()
      this.checkPrevNextState()
   }

   openModal(index) {
      console.debug("[openModal] ** open")
      this.modal.open()
      this.imagePaneWidth = this.getImagePaneWidth()
      this.rollTo(index)
   }

   onDocumentClick(e) {
      // TODO click to transparent area of the modal, get fired to the modal, not the
      // page. Fix it then.
      if (! e.cancelBubble && this.isOpen() && ! e.target.closest('.modal')) {
         this.modal.close()
      }
   }

   onImageCompleted() {
      this.setState((prevState) => { return { loadCounter: prevState.loadCounter + 1 }}, this.stateChanged.bind(this))
   }

   // props
   rollTo(index) {
      let lis = [...this.$ul.querySelectorAll('li')],
          width = this.$lory.clientWidth

      this.leftOffset = Math.min(lis[index].offsetLeft, this.imagePaneWidth - width)
      console.debug("[rollTo]", width, - this.leftOffset + 'px')

      this.$ul.style.left = - this.leftOffset + 'px'
      this.checkPrevNextState()
   }

   isOpen() {
      return this.$modal && this.$modal.classList.contains('open')
   }

   getItemBounds() {
      let sum = 0

      this._itemBounds ||= [ 0, ...[...this.$ul.querySelectorAll('li')].map((li) => {
         sum = sum +
               li.clientWidth +
               parseInt(getComputedStyle(li).marginLeft) +
               parseInt(getComputedStyle(li).marginRight)

         return sum
      }) ]

      return this._itemBounds
   }

   onPrevClick() {
      let lis = [...this.$ul.querySelectorAll('li')],
          itemBounds = this.getItemBounds(),
          width = this.$lory.clientWidth,
          proposeLeftOffset = this.leftOffset - width,
          leftPos = itemBounds.reduce((res, leftPos) => {
            return res !== null ? res :
                  (leftPos > proposeLeftOffset) ?
                   leftPos : null
          }, null)

      console.debug("[prev]", itemBounds, proposeLeftOffset, this.$ul.offsetLeft, width, leftPos, - this.leftOffset + 'px')
      this.leftOffset = lis[itemBounds.indexOf(leftPos)].offsetLeft
      this.props.onLorySlideFrom(itemBounds.indexOf(leftPos))

      this.$ul.style.left = - this.leftOffset + 'px'
      this.checkPrevNextState()
   }

   onNextClick() {
      let sum = 0,
          lis = [...this.$ul.querySelectorAll('li')],
          itemBounds = this.getItemBounds(),
          width = this.$lory.clientWidth,
          rightPos = itemBounds.reduce((res, rightPos) => {
            return res !== null ? res :
                  (rightPos > width - this.$ul.offsetLeft) ?
                   rightPos : null
          }, null)

      this.leftOffset = Math.min(lis[itemBounds.indexOf(rightPos) - 1].offsetLeft,
                                 itemBounds.reduceRight(a => a) - width)
      this.props.onLorySlideFrom(itemBounds.indexOf(rightPos) - 1)
      console.debug("[next]", this.$ul.offsetLeft, width, rightPos, - this.leftOffset + 'px')

      this.$ul.style.left = - this.leftOffset + 'px'
      this.checkPrevNextState()
   }

   checkPrevNextState() {
      if (this.isAtLeftBorder()) {
         this.$prev.classList.add('hidden')
      } else {
         this.$prev.classList.remove('hidden')
      }

      if (this.isAtRightBorder()) {
         this.$next.classList.add('hidden')
      } else {
         this.$next.classList.remove('hidden')
      }
   }

   getImagePaneWidth() {
      return [...this.$ul.querySelectorAll('li')].reduce((width, li) => {
         return width +
                li.clientWidth +
                parseInt(getComputedStyle(li).marginLeft) +
                parseInt(getComputedStyle(li).marginRight)
      }, 0)
   }

   isAtLeftBorder() {
      console.debug("[isAtLeftBorder] **" , this.$ul.offsetLeft, this.leftOffset, this.imagePaneWidth, - this.$ul.offsetLeft + this.$lory.clientWidth, this.leftOffset + this.$lory.clientWidth
)
      return this.leftOffset <= 0
   }

   isAtRightBorder() {
      console.debug("[isAtRightBorder] **" , this.$ul.offsetLeft, this.leftOffset, this.imagePaneWidth, - this.$ul.offsetLeft + this.$lory.clientWidth, this.leftOffset + this.$lory.clientWidth
)
      return this.imagePaneWidth <= this.leftOffset + this.$lory.clientWidth
   }

   render() {
      console.log("[render] * state", this.state)

      return (
         <div
            className='modal'
            id='slider-modal'
            ref={e => this.$modal = e} >
            <div className='modal-content'>
               <div
                  className='lory_slider js_lory_slider'
                  ref={e => this.$lory = e} >
                  <div
                     className='frame js_frame'>
                     <ul
                        ref={e => this.$ul = e}
                        className='slides js_slides'>
                        {this.props.links.map((link) =>
                           <li
                              key={link.id} >
                              <PureImage
                                 wrapperClassName='icon'
                                 src={link.url}
                                 alt={link.description}
                                 onLoad={this.onImageCompleted.bind(this)} /></li>)}</ul></div>
                  <div
                     ref={e => this.$prev = e}
                     className='prev waves-effect'>
                     <svg
                        onClick={this.onPrevClick.bind(this)}
                        xmlns="http://www.w3.org/2000/svg"
                        width="50"
                        height="50"
                        viewBox="0 0 501.5 501.5">
                        <g>
                           <path
                              fill="#DEB3AA"
                              d="M302.67 90.877l55.77 55.508L254.575 250.75 358.44 355.116l-55.77 55.506L143.56 250.75z" /></g></svg></div>
                  <div
                     ref={e => this.$next = e}
                     className='next waves-effect'>
                     <svg
                        onClick={this.onNextClick.bind(this)}
                        xmlns="http://www.w3.org/2000/svg"
                        width="50"
                        height="50"
                        viewBox="0 0 501.5 501.5">
                        <g>
                           <path
                              fill="#DEB3AA"
                              d="M199.33 410.622l-55.77-55.508L247.425 250.75 143.56 146.384l55.77-55.507L358.44 250.75z" /></g></svg></div>
                  <a
                     className='modal-action modal-close waves-effect btn-flat pulse chip'>
                        Закрыть</a></div></div></div>)}}