All files index.js

92% Statements 23/25
93.75% Branches 15/16
81.82% Functions 9/11
91.67% Lines 22/24
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165                                                                                                                                      1x                             3x     3x           3x   3x 1x 2x 2x     3x             2x             1x                 11x 11x   11x 7x               2x 1x     2x 1x               1x                     13x 16x          
import React, { Component } from 'react';
import PropTypes from 'prop-types';
 
/**
 * Track the visitibity of the wrapped components
 *
 * <br>
 * [![codecov](https://codecov.io/gh/AvraamMavridis/react-intersection-visible/branch/master/graph/badge.svg)](https://codecov.io/gh/AvraamMavridis/react-intersection-visible) [![Build Status](https://travis-ci.org/AvraamMavridis/react-intersection-visible.svg?branch=master)](https://travis-ci.org/AvraamMavridis/react-intersection-visible) [![Greenkeeper badge](https://badges.greenkeeper.io/AvraamMavridis/react-intersection-visible.svg)](https://greenkeeper.io/)
 * <br>
 *
 * @export
 * @class IntersectionVisible
 * @extends {Component}
 */
export default class IntersectionVisible extends Component {
  static propTypes = {
    /** Enable/disable the component */
    active: PropTypes.bool,
 
    /** The wrapped component */
    children: PropTypes.oneOfType([ PropTypes.node, PropTypes.func ]),
 
    /** Class passed to the wrapper */
    className: PropTypes.string,
 
    /**
     * Gets called when the wrapped component is visible
     *
     * @param {IntersectionObserverEntry} entries - <a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry">Read more...</a>
     */
    onHide: PropTypes.func,
 
    /**
     * Gets called when wrapped component interesects
     *
     * @param {IntersectionObserverEntry} entries - <a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry">Read more...</a>
     */
    onIntersect: PropTypes.func.isRequired,
 
    /**
     * Gets called when the wrapped component is visible
     *
     * @param {IntersectionObserverEntry} entries - <a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry">Read more...</a>
     */
    onShow: PropTypes.func,
 
    /**
     * Options passed to configure the listener
    */
    options: PropTypes.shape({
      /** The element that is used as the viewport for checking visiblity of the target. Must be the ancestor of the target. Defaults to the browser viewport if not specified or if null. */
      root: PropTypes.node,
 
      /**
       * Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left). The values can be percentages. This set of values serves to grow or shrink each side of the root element's bounding box before computing intersections. Defaults to all zeros.
      */
      rootMargin: PropTypes.string,
      /**
       * Either a single number or an array of numbers which indicate at what percentage of the target's visibility the observer's callback should be executed. If you only want to detect when visibility passes the 50% mark, you can use a value of 0.5. If you want the callback run every time visibility passes another 25%, you would specify the array [0, 0.25, 0.5, 0.75, 1]. The default is 0 (meaning as soon as even one pixel is visible, the callback will be run). A value of 1.0 means that the threshold isn't considered passed until every pixel is visible.
      */
      threshold: PropTypes.oneOfType([ PropTypes.number, PropTypes.arrayOf(PropTypes.number) ])
    })
  };
 
  static defaultProps = {
    active: true,
    className: 'intersection-visible-wrapper',
    onHide: () => null,
    onShow: () => null,
    onIntersect: () => null,
  };
 
  /**
   * Handles the visibility changes
   *
   * @param {array} entries
   */
  handleObserverUpdate = entries => {
    const {
      onIntersect,
      onShow,
      onHide
    } = this.props;
    const {
      intersectionRect
    } = entries[0];
    const {
      top,
      left,
      bottom,
      right
    } = intersectionRect;
 
    if ([ top, bottom, left, right ].some(Boolean) && onShow) {
      onShow(entries);
    } else Eif (onHide) {
      onHide(entries);
    }
 
    onIntersect(entries);
  };
 
  /**
   * Starts the observer
   */
  startObserving() {
    this.observer.observe(this.node);
  }
 
  /**
   * Stops the observer
   */
  stopObserving() {
    this.observer.unobserve(this.node);
  }
 
  /**
   * Start the observer when the component is mounted
   */
  componentDidMount() {
    const {
      options
    } = this.props;
    this.observer = new IntersectionObserver(this.handleObserverUpdate, options);
 
    if (this.props.active) {
      this.startObserving();
    }
  }
 
  /**
   * Update observer state on prop changes
   */
  componentDidUpdate(prevProps) {
    if (this.props.active && !prevProps.active) {
      this.startObserving();
    }
 
    if (!this.props.active && prevProps.active) {
      this.stopObserving();
    }
  }
 
  /**
   * Stop the observer on unmounting
   */
  componentWillUnmount() {
    this.observer.disconnect();
  }
 
  /**
   * Render component
   *
   * @returns {JSX.Element}
   */
  render() {
    const {
      className
    } = this.props;
    return <div className={className} ref={node => this.node = node}>
      {this.props.children}
    </div>;
  }
}