UNPKG

react-cool-inview

Version:

React hook to monitor an element enters or leaves the viewport (or another element).

200 lines (169 loc) 6.11 kB
import { useRef, useState, useCallback, useEffect, cloneElement } from 'react'; function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } var useLatest = (function (val) { var ref = useRef(val); ref.current = val; return ref; }); var observerErr = "💡 react-cool-inview: the browser doesn't support Intersection Observer, please install polyfill: https://github.com/wellyshen/react-cool-inview#intersection-observer-polyfill"; var observerWarn = "💡 react-cool-inview: the browser doesn't support Intersection Observer v2, fallback to v1 behavior"; var useInView = function useInView(_temp) { var _ref = _temp === void 0 ? {} : _temp, root = _ref.root, rootMargin = _ref.rootMargin, _ref$threshold = _ref.threshold, threshold = _ref$threshold === void 0 ? 0 : _ref$threshold, trackVisibility = _ref.trackVisibility, delay = _ref.delay, unobserveOnEnter = _ref.unobserveOnEnter, onChange = _ref.onChange, onEnter = _ref.onEnter, onLeave = _ref.onLeave; var _useState = useState({ inView: false, scrollDirection: {} }), state = _useState[0], setState = _useState[1]; var prevInViewRef = useRef(false); var prevPosRef = useRef({}); var observerRef = useRef(); var warnedRef = useRef(false); var onChangeRef = useLatest(onChange); var onEnterRef = useLatest(onEnter); var onLeaveRef = useLatest(onLeave); var ref = useRef(); var unobserve = useCallback(function () { if (observerRef.current) { observerRef.current.disconnect(); prevPosRef.current = {}; } }, []); var observe = useCallback(function (element) { if (element && element !== ref.current) { unobserve(); ref.current = element; } if (observerRef.current && ref.current) observerRef.current.observe(ref.current); }, [unobserve]); var updatePosition = useCallback(function () { if (!ref.current) return; var _ref$current$getBound = ref.current.getBoundingClientRect(), x = _ref$current$getBound.x, y = _ref$current$getBound.y; prevPosRef.current = { x: x, y: y }; }, [ref]); useEffect(function () { if (!("IntersectionObserver" in window) || !("IntersectionObserverEntry" in window)) { console.error(observerErr); return function () { return null; }; } var isActive = true; // eslint-disable-next-line compat/compat observerRef.current = new IntersectionObserver(function (_ref2) { var entry = _ref2[0]; var intersectionRatio = entry.intersectionRatio, isIntersecting = entry.isIntersecting, _entry$boundingClient = entry.boundingClientRect, x = _entry$boundingClient.x, y = _entry$boundingClient.y, isVisible = entry.isVisible; var scrollDirection = {}; var min = Array.isArray(threshold) ? Math.min.apply(Math, threshold) : threshold; var inView = isIntersecting !== undefined ? isIntersecting : intersectionRatio > 0; inView = min > 0 ? intersectionRatio >= min : inView; // @ts-ignore if (x < prevPosRef.current.x) scrollDirection.horizontal = "left"; // @ts-ignore if (x > prevPosRef.current.x) scrollDirection.horizontal = "right"; prevPosRef.current.x = x; // @ts-ignore if (y < prevPosRef.current.y) scrollDirection.vertical = "up"; // @ts-ignore if (y > prevPosRef.current.y) scrollDirection.vertical = "down"; prevPosRef.current.y = y; var e = { entry: entry, scrollDirection: scrollDirection, observe: observe, unobserve: unobserve }; if (trackVisibility) { if (isVisible === undefined && !warnedRef.current) { console.warn(observerWarn); warnedRef.current = true; } if (isVisible !== undefined) inView = isVisible; } if (inView && !prevInViewRef.current) { if (unobserveOnEnter) unobserve(); if (onEnterRef.current) onEnterRef.current(e); } if (!inView && prevInViewRef.current && onLeaveRef.current) onLeaveRef.current(e); if (onChangeRef.current) onChangeRef.current(_extends({}, e, { inView: inView })); if (isActive) setState({ inView: inView, scrollDirection: scrollDirection, entry: entry }); prevInViewRef.current = inView; }, { root: root, rootMargin: rootMargin, threshold: threshold, trackVisibility: trackVisibility, delay: delay }); observe(); return function () { isActive = false; unobserve(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [unobserveOnEnter, root, rootMargin, // eslint-disable-next-line react-hooks/exhaustive-deps JSON.stringify(threshold), trackVisibility, delay, observe, unobserve]); return _extends({}, state, { observe: observe, unobserve: unobserve, updatePosition: updatePosition }); }; var _excluded = ["children"], _excluded2 = ["observe"]; var InView = function InView(_ref) { var children = _ref.children, props = _objectWithoutPropertiesLoose(_ref, _excluded); var _useInView = useInView(props), observe = _useInView.observe, rest = _objectWithoutPropertiesLoose(_useInView, _excluded2); return /*#__PURE__*/cloneElement(children, _extends({ observe: observe }, rest)); }; export { InView, useInView };