UNPKG

reblend-ui

Version:

Utilities for creating robust overlay components

100 lines (98 loc) 3.83 kB
"use strict"; exports.__esModule = true; exports.default = exports.Position = void 0; var _reblendHooks = require("reblend-hooks"); var _useIntersectionObserver = require("reblend-hooks/useIntersectionObserver"); var _reblendjs = require("reblendjs"); var _scrollParent = require("dom-helpers/scrollParent"); /** Accepts all options an IntersectionObserver accepts */ let Position = exports.Position = /*#__PURE__*/function (Position) { Position[Position["UNKNOWN"] = 0] = "UNKNOWN"; Position[Position["BEFORE"] = 1] = "BEFORE"; Position[Position["INSIDE"] = 2] = "INSIDE"; Position[Position["AFTER"] = 3] = "AFTER"; return Position; }({}); function toCss(margin) { if (!margin || typeof margin === 'string') return margin; const { top = 0, right = 0, bottom = 0, left = 0 } = margin; return `${top}px ${right}px ${bottom}px ${left}px`; } const findRoot = el => (0, _scrollParent.default)(el, true); function useWaypoint(element, callback, options = {}) { const { rootMargin, threshold, scrollDirection = 'vertical' } = options; this.state.rootMargin = rootMargin; this.state.threshold = threshold; this.state.scrollDirection = scrollDirection; let { root } = options; this.state.root = root; const handler = _reblendHooks.useEventCallback.bind(this)(callback); this.state.handler = handler; const prevPositionRef = _reblendjs.useRef.bind(this)(null); this.state.prevPositionRef = prevPositionRef; if (this.state.root === 'scrollParent') { this.state.root = findRoot; } const scrollParent = _reblendjs.useMemo.bind(this)(() => element && typeof this.state.root === 'function' ? this.state.root(element) : null, "scrollParent", (() => [element, this.state.root]).bind(this)); this.state.scrollParent = scrollParent; let realRoot = typeof this.state.root === 'function' ? this.state.scrollParent : this.state.root; this.state.realRoot = realRoot; if (this.state.realRoot && this.state.realRoot.nodeType === document.DOCUMENT_NODE) { // explicit undefined means "use the viewport", instead of `null` // which means "no root yet". This works around a bug in safari // where document is not accepted in older versions, // or is accepted but doesn't work (as of v14) this.state.realRoot = undefined; } _useIntersectionObserver.default.bind(this)( // We change the meaning of explicit null to "not provided yet" // this is to allow easier synchronizing between element and roots derived // from it. Otherwise if the root updates later an observer will be created // for the document and then for the root element, ([entry], observer) => { if (!entry) return; const [start, end, point] = this.state.scrollDirection === 'vertical' ? ['top', 'bottom', 'y'] : ['left', 'right', 'x']; const { [point]: coord } = entry.boundingClientRect; const rootStart = entry.rootBounds?.[start] || 0; const rootEnd = entry.rootBounds?.[end] || 0; // The position may remain UNKNOWN if the root // is 0 width/height or everything is hidden. let position = Position.UNKNOWN; if (entry.isIntersecting) { position = Position.INSIDE; } else if (coord > rootEnd) { position = Position.AFTER; } else if (coord < rootStart) { position = Position.BEFORE; } const previousPosition = this.state.prevPositionRef.current; if (previousPosition === position) { return; } this.state.handler({ position, previousPosition }, entry, observer); this.state.prevPositionRef.current = position; }, { threshold: this.state.threshold, root: this.state.realRoot, rootMargin: toCss(this.state.rootMargin) }); return; } /* @Reblend: Transformed from function to class */ var _default = exports.default = useWaypoint;