Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@skava/ui / src / components / features / ClickBoundary / ClickBoundary.tsx
Size: Mime:
import React from 'react'
import { MouseEvent, ReactChild } from 'react'
import toClassName from 'classnames'
import { isFunction } from 'exotic'
import { ClickBoundaryProps, ClickBoundaryRef } from './typings'
import { onClick } from './deps'

class ClickBoundary extends React.PureComponent<ClickBoundaryProps> {
  isListening: boolean = false
  isActive: boolean = false
  dom: ClickBoundaryRef

  /**
   * only happens in browser, `willMount` in server
   */
  subscribeListener() {
    if (this.isActive && !this.isListening) {
      // @note - document.body
      window.addEventListener('click', this.handleClick)
      window.addEventListener('ontouchstart', this.handleClick)
      this.isListening = true
    }
  }

  /**
   * only happens browser
   */
  unSubscribeListener() {
    if (this.isListening) {
      // remember to remove all events to avoid memory leaks
      window.removeEventListener('click', this.handleClick)
      window.removeEventListener('ontouchstart', this.handleClick)
      this.isListening = false
    }
  }

  /**
   * @todo update with forwardRef
   */
  setRef = (dom: Element | any) => {
    this.dom = dom
    if (isFunction(this.props.innerRef)) {
      this.props.innerRef(dom)
    }
  }

  handleMouseEnter = () => {
    this.isActive = false
  }

  handleMouseLeave = () => {
    this.isActive = true
    this.subscribeListener()
  }

  handleClick = (event: MouseEvent<any> | any) => {
    onClick(event, this.props, this.dom)
    this.isActive = false
    this.unSubscribeListener()
  }

  renderChildren(children: any, attributes: any) {
    const onChild = (child: ReactChild) => {
      // 2. safety
      if (typeof child === 'string' || typeof child === 'number') {
        return child
      }

      // 3. merge props
      const props = {
        ...attributes,
        ...child.props,
      }

      return React.cloneElement(child, props)
    }
    return React.Children.map(children, onChild)
  }

  render() {
    const {
      className,
      children,
      onClickOutside,
      nowrap,
      ...remainingProps
    } = this.props

    const attributes = {
      ...remainingProps,
      isActive: this.isActive,
      onMouseEnter: this.handleMouseEnter,
      onMouseLeave: this.handleMouseLeave,
      className: toClassName('click-boundary', className),
    }

    if (nowrap) {
      return this.renderChildren(children, attributes)
    } else {
      return (
        <div {...attributes} ref={this.setRef}>
          {this.props.children}
        </div>
      )
    }
  }
}

export default ClickBoundary
export { ClickBoundary }