UNPKG

react-intersection-observer

Version:

Monitor if a component is inside the viewport, using IntersectionObserver API

331 lines (326 loc) 9.69 kB
"use strict"; "use client"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); // src/index.tsx var index_exports = {}; __export(index_exports, { InView: () => InView, defaultFallbackInView: () => defaultFallbackInView, observe: () => observe, useInView: () => useInView }); module.exports = __toCommonJS(index_exports); // src/InView.tsx var React = __toESM(require("react")); // src/observe.ts var observerMap = /* @__PURE__ */ new Map(); var RootIds = /* @__PURE__ */ new WeakMap(); var rootId = 0; var unsupportedValue = void 0; function defaultFallbackInView(inView) { unsupportedValue = inView; } function getRootId(root) { if (!root) return "0"; if (RootIds.has(root)) return RootIds.get(root); rootId += 1; RootIds.set(root, rootId.toString()); return RootIds.get(root); } function optionsToId(options) { return Object.keys(options).sort().filter( (key) => options[key] !== void 0 ).map((key) => { return `${key}_${key === "root" ? getRootId(options.root) : options[key]}`; }).toString(); } function createObserver(options) { const id = optionsToId(options); let instance = observerMap.get(id); if (!instance) { const elements = /* @__PURE__ */ new Map(); let thresholds; const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { var _a; const inView = entry.isIntersecting && thresholds.some((threshold) => entry.intersectionRatio >= threshold); if (options.trackVisibility && typeof entry.isVisible === "undefined") { entry.isVisible = inView; } (_a = elements.get(entry.target)) == null ? void 0 : _a.forEach((callback) => { callback(inView, entry); }); }); }, options); thresholds = observer.thresholds || (Array.isArray(options.threshold) ? options.threshold : [options.threshold || 0]); instance = { id, observer, elements }; observerMap.set(id, instance); } return instance; } function observe(element, callback, options = {}, fallbackInView = unsupportedValue) { if (typeof window.IntersectionObserver === "undefined" && fallbackInView !== void 0) { const bounds = element.getBoundingClientRect(); callback(fallbackInView, { isIntersecting: fallbackInView, target: element, intersectionRatio: typeof options.threshold === "number" ? options.threshold : 0, time: 0, boundingClientRect: bounds, intersectionRect: bounds, rootBounds: bounds }); return () => { }; } const { id, observer, elements } = createObserver(options); const callbacks = elements.get(element) || []; if (!elements.has(element)) { elements.set(element, callbacks); } callbacks.push(callback); observer.observe(element); return function unobserve() { callbacks.splice(callbacks.indexOf(callback), 1); if (callbacks.length === 0) { elements.delete(element); observer.unobserve(element); } if (elements.size === 0) { observer.disconnect(); observerMap.delete(id); } }; } // src/InView.tsx function isPlainChildren(props) { return typeof props.children !== "function"; } var InView = class extends React.Component { constructor(props) { super(props); __publicField(this, "node", null); __publicField(this, "_unobserveCb", null); __publicField(this, "handleNode", (node) => { if (this.node) { this.unobserve(); if (!node && !this.props.triggerOnce && !this.props.skip) { this.setState({ inView: !!this.props.initialInView, entry: void 0 }); } } this.node = node ? node : null; this.observeNode(); }); __publicField(this, "handleChange", (inView, entry) => { if (inView && this.props.triggerOnce) { this.unobserve(); } if (!isPlainChildren(this.props)) { this.setState({ inView, entry }); } if (this.props.onChange) { this.props.onChange(inView, entry); } }); this.state = { inView: !!props.initialInView, entry: void 0 }; } componentDidMount() { this.unobserve(); this.observeNode(); } componentDidUpdate(prevProps) { if (prevProps.rootMargin !== this.props.rootMargin || prevProps.root !== this.props.root || prevProps.threshold !== this.props.threshold || prevProps.skip !== this.props.skip || prevProps.trackVisibility !== this.props.trackVisibility || prevProps.delay !== this.props.delay) { this.unobserve(); this.observeNode(); } } componentWillUnmount() { this.unobserve(); } observeNode() { if (!this.node || this.props.skip) return; const { threshold, root, rootMargin, trackVisibility, delay, fallbackInView } = this.props; this._unobserveCb = observe( this.node, this.handleChange, { threshold, root, rootMargin, // @ts-ignore trackVisibility, // @ts-ignore delay }, fallbackInView ); } unobserve() { if (this._unobserveCb) { this._unobserveCb(); this._unobserveCb = null; } } render() { const { children } = this.props; if (typeof children === "function") { const { inView, entry } = this.state; return children({ inView, entry, ref: this.handleNode }); } const { as, triggerOnce, threshold, root, rootMargin, onChange, skip, trackVisibility, delay, initialInView, fallbackInView, ...props } = this.props; return React.createElement( as || "div", { ref: this.handleNode, ...props }, children ); } }; // src/useInView.tsx var React2 = __toESM(require("react")); function useInView({ threshold, delay, trackVisibility, rootMargin, root, triggerOnce, skip, initialInView, fallbackInView, onChange } = {}) { var _a; const [ref, setRef] = React2.useState(null); const callback = React2.useRef(onChange); const [state, setState] = React2.useState({ inView: !!initialInView, entry: void 0 }); callback.current = onChange; React2.useEffect( () => { if (skip || !ref) return; let unobserve; unobserve = observe( ref, (inView, entry) => { setState({ inView, entry }); if (callback.current) callback.current(inView, entry); if (entry.isIntersecting && triggerOnce && unobserve) { unobserve(); unobserve = void 0; } }, { root, rootMargin, threshold, // @ts-ignore trackVisibility, // @ts-ignore delay }, fallbackInView ); return () => { if (unobserve) { unobserve(); } }; }, // We break the rule here, because we aren't including the actual `threshold` variable // eslint-disable-next-line react-hooks/exhaustive-deps [ // If the threshold is an array, convert it to a string, so it won't change between renders. Array.isArray(threshold) ? threshold.toString() : threshold, ref, root, rootMargin, triggerOnce, skip, trackVisibility, fallbackInView, delay ] ); const entryTarget = (_a = state.entry) == null ? void 0 : _a.target; const previousEntryTarget = React2.useRef(void 0); if (!ref && entryTarget && !triggerOnce && !skip && previousEntryTarget.current !== entryTarget) { previousEntryTarget.current = entryTarget; setState({ inView: !!initialInView, entry: void 0 }); } const result = [setRef, state.inView, state.entry]; result.ref = result[0]; result.inView = result[1]; result.entry = result[2]; return result; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { InView, defaultFallbackInView, observe, useInView }); //# sourceMappingURL=index.js.map