This commit is contained in:
568
node_modules/react-waypoint/es/index.js
generated
vendored
Normal file
568
node_modules/react-waypoint/es/index.js
generated
vendored
Normal file
@@ -0,0 +1,568 @@
|
||||
import _inheritsLoose from '@babel/runtime/helpers/esm/inheritsLoose';
|
||||
import { addEventListener } from 'consolidated-events';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { isForwardRef } from 'react-is';
|
||||
|
||||
/**
|
||||
* Attempts to parse the offset provided as a prop as a percentage. For
|
||||
* instance, if the component has been provided with the string "20%" as
|
||||
* a value of one of the offset props. If the value matches, then it returns
|
||||
* a numeric version of the prop. For instance, "20%" would become `0.2`.
|
||||
* If `str` isn't a percentage, then `undefined` will be returned.
|
||||
*
|
||||
* @param {string} str The value of an offset prop to be converted to a
|
||||
* number.
|
||||
* @return {number|undefined} The numeric version of `str`. Undefined if `str`
|
||||
* was not a percentage.
|
||||
*/
|
||||
function parseOffsetAsPercentage(str) {
|
||||
if (str.slice(-1) === '%') {
|
||||
return parseFloat(str.slice(0, -1)) / 100;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to parse the offset provided as a prop as a pixel value. If
|
||||
* parsing fails, then `undefined` is returned. Three examples of values that
|
||||
* will be successfully parsed are:
|
||||
* `20`
|
||||
* "20px"
|
||||
* "20"
|
||||
*
|
||||
* @param {string|number} str A string of the form "{number}" or "{number}px",
|
||||
* or just a number.
|
||||
* @return {number|undefined} The numeric version of `str`. Undefined if `str`
|
||||
* was neither a number nor string ending in "px".
|
||||
*/
|
||||
function parseOffsetAsPixels(str) {
|
||||
if (!isNaN(parseFloat(str)) && isFinite(str)) {
|
||||
return parseFloat(str);
|
||||
}
|
||||
|
||||
if (str.slice(-2) === 'px') {
|
||||
return parseFloat(str.slice(0, -2));
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string|number} offset
|
||||
* @param {number} contextHeight
|
||||
* @return {number} A number representing `offset` converted into pixels.
|
||||
*/
|
||||
|
||||
function computeOffsetPixels(offset, contextHeight) {
|
||||
var pixelOffset = parseOffsetAsPixels(offset);
|
||||
|
||||
if (typeof pixelOffset === 'number') {
|
||||
return pixelOffset;
|
||||
}
|
||||
|
||||
var percentOffset = parseOffsetAsPercentage(offset);
|
||||
|
||||
if (typeof percentOffset === 'number') {
|
||||
return percentOffset * contextHeight;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var ABOVE = 'above';
|
||||
var INSIDE = 'inside';
|
||||
var BELOW = 'below';
|
||||
var INVISIBLE = 'invisible';
|
||||
|
||||
function debugLog() {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
var _console;
|
||||
|
||||
(_console = console).log.apply(_console, arguments); // eslint-disable-line no-console
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When an element's type is a string, it represents a DOM node with that tag name
|
||||
* https://facebook.github.io/react/blog/2015/12/18/react-components-elements-and-instances.html#dom-elements
|
||||
*
|
||||
* @param {React.element} Component
|
||||
* @return {bool} Whether the component is a DOM Element
|
||||
*/
|
||||
function isDOMElement(Component) {
|
||||
return typeof Component.type === 'string';
|
||||
}
|
||||
|
||||
var errorMessage = '<Waypoint> needs a DOM element to compute boundaries. The child you passed is neither a ' + 'DOM element (e.g. <div>) nor does it use the innerRef prop.\n\n' + 'See https://goo.gl/LrBNgw for more info.';
|
||||
/**
|
||||
* Raise an error if "children" is not a DOM Element and there is no ref provided to Waypoint
|
||||
*
|
||||
* @param {?React.element} children
|
||||
* @param {?HTMLElement} ref
|
||||
* @return {undefined}
|
||||
*/
|
||||
|
||||
function ensureRefIsProvidedByChild(children, ref) {
|
||||
if (children && !isDOMElement(children) && !ref) {
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} bounds An object with bounds data for the waypoint and
|
||||
* scrollable parent
|
||||
* @return {string} The current position of the waypoint in relation to the
|
||||
* visible portion of the scrollable parent. One of the constants `ABOVE`,
|
||||
* `BELOW`, `INSIDE` or `INVISIBLE`.
|
||||
*/
|
||||
|
||||
function getCurrentPosition(bounds) {
|
||||
if (bounds.viewportBottom - bounds.viewportTop === 0) {
|
||||
return INVISIBLE;
|
||||
} // top is within the viewport
|
||||
|
||||
|
||||
if (bounds.viewportTop <= bounds.waypointTop && bounds.waypointTop <= bounds.viewportBottom) {
|
||||
return INSIDE;
|
||||
} // bottom is within the viewport
|
||||
|
||||
|
||||
if (bounds.viewportTop <= bounds.waypointBottom && bounds.waypointBottom <= bounds.viewportBottom) {
|
||||
return INSIDE;
|
||||
} // top is above the viewport and bottom is below the viewport
|
||||
|
||||
|
||||
if (bounds.waypointTop <= bounds.viewportTop && bounds.viewportBottom <= bounds.waypointBottom) {
|
||||
return INSIDE;
|
||||
}
|
||||
|
||||
if (bounds.viewportBottom < bounds.waypointTop) {
|
||||
return BELOW;
|
||||
}
|
||||
|
||||
if (bounds.waypointTop < bounds.viewportTop) {
|
||||
return ABOVE;
|
||||
}
|
||||
|
||||
return INVISIBLE;
|
||||
}
|
||||
|
||||
var timeout;
|
||||
var timeoutQueue = [];
|
||||
function onNextTick(cb) {
|
||||
timeoutQueue.push(cb);
|
||||
|
||||
if (!timeout) {
|
||||
timeout = setTimeout(function () {
|
||||
timeout = null; // Drain the timeoutQueue
|
||||
|
||||
var item; // eslint-disable-next-line no-cond-assign
|
||||
|
||||
while (item = timeoutQueue.shift()) {
|
||||
item();
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
var isSubscribed = true;
|
||||
return function unsubscribe() {
|
||||
if (!isSubscribed) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSubscribed = false;
|
||||
var index = timeoutQueue.indexOf(cb);
|
||||
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
timeoutQueue.splice(index, 1);
|
||||
|
||||
if (!timeoutQueue.length && timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function resolveScrollableAncestorProp(scrollableAncestor) {
|
||||
// When Waypoint is rendered on the server, `window` is not available.
|
||||
// To make Waypoint easier to work with, we allow this to be specified in
|
||||
// string form and safely convert to `window` here.
|
||||
if (scrollableAncestor === 'window') {
|
||||
return global.window;
|
||||
}
|
||||
|
||||
return scrollableAncestor;
|
||||
}
|
||||
|
||||
var hasWindow = typeof window !== 'undefined';
|
||||
var defaultProps = {
|
||||
debug: false,
|
||||
scrollableAncestor: undefined,
|
||||
children: undefined,
|
||||
topOffset: '0px',
|
||||
bottomOffset: '0px',
|
||||
horizontal: false,
|
||||
onEnter: function onEnter() {},
|
||||
onLeave: function onLeave() {},
|
||||
onPositionChange: function onPositionChange() {},
|
||||
fireOnRapidScroll: true
|
||||
}; // Calls a function when you scroll to the element.
|
||||
|
||||
var Waypoint = /*#__PURE__*/function (_React$PureComponent) {
|
||||
_inheritsLoose(Waypoint, _React$PureComponent);
|
||||
|
||||
function Waypoint(props) {
|
||||
var _this;
|
||||
|
||||
_this = _React$PureComponent.call(this, props) || this;
|
||||
|
||||
_this.refElement = function (e) {
|
||||
_this._ref = e;
|
||||
};
|
||||
|
||||
return _this;
|
||||
}
|
||||
|
||||
var _proto = Waypoint.prototype;
|
||||
|
||||
_proto.componentDidMount = function componentDidMount() {
|
||||
var _this2 = this;
|
||||
|
||||
if (!hasWindow) {
|
||||
return;
|
||||
} // this._ref may occasionally not be set at this time. To help ensure that
|
||||
// this works smoothly and to avoid layout thrashing, we want to delay the
|
||||
// initial execution until the next tick.
|
||||
|
||||
|
||||
this.cancelOnNextTick = onNextTick(function () {
|
||||
_this2.cancelOnNextTick = null;
|
||||
var _this2$props = _this2.props,
|
||||
children = _this2$props.children,
|
||||
debug = _this2$props.debug; // Berofe doing anything, we want to check that this._ref is avaliable in Waypoint
|
||||
|
||||
ensureRefIsProvidedByChild(children, _this2._ref);
|
||||
_this2._handleScroll = _this2._handleScroll.bind(_this2);
|
||||
_this2.scrollableAncestor = _this2._findScrollableAncestor();
|
||||
|
||||
if (process.env.NODE_ENV !== 'production' && debug) {
|
||||
debugLog('scrollableAncestor', _this2.scrollableAncestor);
|
||||
}
|
||||
|
||||
_this2.scrollEventListenerUnsubscribe = addEventListener(_this2.scrollableAncestor, 'scroll', _this2._handleScroll, {
|
||||
passive: true
|
||||
});
|
||||
_this2.resizeEventListenerUnsubscribe = addEventListener(window, 'resize', _this2._handleScroll, {
|
||||
passive: true
|
||||
});
|
||||
|
||||
_this2._handleScroll(null);
|
||||
});
|
||||
};
|
||||
|
||||
_proto.componentDidUpdate = function componentDidUpdate() {
|
||||
var _this3 = this;
|
||||
|
||||
if (!hasWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.scrollableAncestor) {
|
||||
// The Waypoint has not yet initialized.
|
||||
return;
|
||||
} // The element may have moved, so we need to recompute its position on the
|
||||
// page. This happens via handleScroll in a way that forces layout to be
|
||||
// computed.
|
||||
//
|
||||
// We want this to be deferred to avoid forcing layout during render, which
|
||||
// causes layout thrashing. And, if we already have this work enqueued, we
|
||||
// can just wait for that to happen instead of enqueueing again.
|
||||
|
||||
|
||||
if (this.cancelOnNextTick) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cancelOnNextTick = onNextTick(function () {
|
||||
_this3.cancelOnNextTick = null;
|
||||
|
||||
_this3._handleScroll(null);
|
||||
});
|
||||
};
|
||||
|
||||
_proto.componentWillUnmount = function componentWillUnmount() {
|
||||
if (!hasWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.scrollEventListenerUnsubscribe) {
|
||||
this.scrollEventListenerUnsubscribe();
|
||||
}
|
||||
|
||||
if (this.resizeEventListenerUnsubscribe) {
|
||||
this.resizeEventListenerUnsubscribe();
|
||||
}
|
||||
|
||||
if (this.cancelOnNextTick) {
|
||||
this.cancelOnNextTick();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Traverses up the DOM to find an ancestor container which has an overflow
|
||||
* style that allows for scrolling.
|
||||
*
|
||||
* @return {Object} the closest ancestor element with an overflow style that
|
||||
* allows for scrolling. If none is found, the `window` object is returned
|
||||
* as a fallback.
|
||||
*/
|
||||
;
|
||||
|
||||
_proto._findScrollableAncestor = function _findScrollableAncestor() {
|
||||
var _this$props = this.props,
|
||||
horizontal = _this$props.horizontal,
|
||||
scrollableAncestor = _this$props.scrollableAncestor;
|
||||
|
||||
if (scrollableAncestor) {
|
||||
return resolveScrollableAncestorProp(scrollableAncestor);
|
||||
}
|
||||
|
||||
var node = this._ref;
|
||||
|
||||
while (node.parentNode) {
|
||||
node = node.parentNode;
|
||||
|
||||
if (node === document.body) {
|
||||
// We've reached all the way to the root node.
|
||||
return window;
|
||||
}
|
||||
|
||||
var style = window.getComputedStyle(node);
|
||||
var overflowDirec = horizontal ? style.getPropertyValue('overflow-x') : style.getPropertyValue('overflow-y');
|
||||
var overflow = overflowDirec || style.getPropertyValue('overflow');
|
||||
|
||||
if (overflow === 'auto' || overflow === 'scroll' || overflow === 'overlay') {
|
||||
return node;
|
||||
}
|
||||
} // A scrollable ancestor element was not found, which means that we need to
|
||||
// do stuff on window.
|
||||
|
||||
|
||||
return window;
|
||||
}
|
||||
/**
|
||||
* @param {Object} event the native scroll event coming from the scrollable
|
||||
* ancestor, or resize event coming from the window. Will be undefined if
|
||||
* called by a React lifecyle method
|
||||
*/
|
||||
;
|
||||
|
||||
_proto._handleScroll = function _handleScroll(event) {
|
||||
if (!this._ref) {
|
||||
// There's a chance we end up here after the component has been unmounted.
|
||||
return;
|
||||
}
|
||||
|
||||
var bounds = this._getBounds();
|
||||
|
||||
var currentPosition = getCurrentPosition(bounds);
|
||||
var previousPosition = this._previousPosition;
|
||||
var _this$props2 = this.props,
|
||||
debug = _this$props2.debug,
|
||||
onPositionChange = _this$props2.onPositionChange,
|
||||
onEnter = _this$props2.onEnter,
|
||||
onLeave = _this$props2.onLeave,
|
||||
fireOnRapidScroll = _this$props2.fireOnRapidScroll;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production' && debug) {
|
||||
debugLog('currentPosition', currentPosition);
|
||||
debugLog('previousPosition', previousPosition);
|
||||
} // Save previous position as early as possible to prevent cycles
|
||||
|
||||
|
||||
this._previousPosition = currentPosition;
|
||||
|
||||
if (previousPosition === currentPosition) {
|
||||
// No change since last trigger
|
||||
return;
|
||||
}
|
||||
|
||||
var callbackArg = {
|
||||
currentPosition: currentPosition,
|
||||
previousPosition: previousPosition,
|
||||
event: event,
|
||||
waypointTop: bounds.waypointTop,
|
||||
waypointBottom: bounds.waypointBottom,
|
||||
viewportTop: bounds.viewportTop,
|
||||
viewportBottom: bounds.viewportBottom
|
||||
};
|
||||
onPositionChange.call(this, callbackArg);
|
||||
|
||||
if (currentPosition === INSIDE) {
|
||||
onEnter.call(this, callbackArg);
|
||||
} else if (previousPosition === INSIDE) {
|
||||
onLeave.call(this, callbackArg);
|
||||
}
|
||||
|
||||
var isRapidScrollDown = previousPosition === BELOW && currentPosition === ABOVE;
|
||||
var isRapidScrollUp = previousPosition === ABOVE && currentPosition === BELOW;
|
||||
|
||||
if (fireOnRapidScroll && (isRapidScrollDown || isRapidScrollUp)) {
|
||||
// If the scroll event isn't fired often enough to occur while the
|
||||
// waypoint was visible, we trigger both callbacks anyway.
|
||||
onEnter.call(this, {
|
||||
currentPosition: INSIDE,
|
||||
previousPosition: previousPosition,
|
||||
event: event,
|
||||
waypointTop: bounds.waypointTop,
|
||||
waypointBottom: bounds.waypointBottom,
|
||||
viewportTop: bounds.viewportTop,
|
||||
viewportBottom: bounds.viewportBottom
|
||||
});
|
||||
onLeave.call(this, {
|
||||
currentPosition: currentPosition,
|
||||
previousPosition: INSIDE,
|
||||
event: event,
|
||||
waypointTop: bounds.waypointTop,
|
||||
waypointBottom: bounds.waypointBottom,
|
||||
viewportTop: bounds.viewportTop,
|
||||
viewportBottom: bounds.viewportBottom
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_proto._getBounds = function _getBounds() {
|
||||
var _this$props3 = this.props,
|
||||
horizontal = _this$props3.horizontal,
|
||||
debug = _this$props3.debug;
|
||||
|
||||
var _this$_ref$getBoundin = this._ref.getBoundingClientRect(),
|
||||
left = _this$_ref$getBoundin.left,
|
||||
top = _this$_ref$getBoundin.top,
|
||||
right = _this$_ref$getBoundin.right,
|
||||
bottom = _this$_ref$getBoundin.bottom;
|
||||
|
||||
var waypointTop = horizontal ? left : top;
|
||||
var waypointBottom = horizontal ? right : bottom;
|
||||
var contextHeight;
|
||||
var contextScrollTop;
|
||||
|
||||
if (this.scrollableAncestor === window) {
|
||||
contextHeight = horizontal ? window.innerWidth : window.innerHeight;
|
||||
contextScrollTop = 0;
|
||||
} else {
|
||||
contextHeight = horizontal ? this.scrollableAncestor.offsetWidth : this.scrollableAncestor.offsetHeight;
|
||||
contextScrollTop = horizontal ? this.scrollableAncestor.getBoundingClientRect().left : this.scrollableAncestor.getBoundingClientRect().top;
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production' && debug) {
|
||||
debugLog('waypoint top', waypointTop);
|
||||
debugLog('waypoint bottom', waypointBottom);
|
||||
debugLog('scrollableAncestor height', contextHeight);
|
||||
debugLog('scrollableAncestor scrollTop', contextScrollTop);
|
||||
}
|
||||
|
||||
var _this$props4 = this.props,
|
||||
bottomOffset = _this$props4.bottomOffset,
|
||||
topOffset = _this$props4.topOffset;
|
||||
var topOffsetPx = computeOffsetPixels(topOffset, contextHeight);
|
||||
var bottomOffsetPx = computeOffsetPixels(bottomOffset, contextHeight);
|
||||
var contextBottom = contextScrollTop + contextHeight;
|
||||
return {
|
||||
waypointTop: waypointTop,
|
||||
waypointBottom: waypointBottom,
|
||||
viewportTop: contextScrollTop + topOffsetPx,
|
||||
viewportBottom: contextBottom - bottomOffsetPx
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @return {Object}
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.render = function render() {
|
||||
var _this4 = this;
|
||||
|
||||
var children = this.props.children;
|
||||
|
||||
if (!children) {
|
||||
// We need an element that we can locate in the DOM to determine where it is
|
||||
// rendered relative to the top of its context.
|
||||
return /*#__PURE__*/React.createElement("span", {
|
||||
ref: this.refElement,
|
||||
style: {
|
||||
fontSize: 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isDOMElement(children) || isForwardRef(children)) {
|
||||
var ref = function ref(node) {
|
||||
_this4.refElement(node);
|
||||
|
||||
if (children.ref) {
|
||||
if (typeof children.ref === 'function') {
|
||||
children.ref(node);
|
||||
} else {
|
||||
children.ref.current = node;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return /*#__PURE__*/React.cloneElement(children, {
|
||||
ref: ref
|
||||
});
|
||||
}
|
||||
|
||||
return /*#__PURE__*/React.cloneElement(children, {
|
||||
innerRef: this.refElement
|
||||
});
|
||||
};
|
||||
|
||||
return Waypoint;
|
||||
}(React.PureComponent);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Waypoint.propTypes = {
|
||||
children: PropTypes.element,
|
||||
debug: PropTypes.bool,
|
||||
onEnter: PropTypes.func,
|
||||
onLeave: PropTypes.func,
|
||||
onPositionChange: PropTypes.func,
|
||||
fireOnRapidScroll: PropTypes.bool,
|
||||
// eslint-disable-next-line react/forbid-prop-types
|
||||
scrollableAncestor: PropTypes.any,
|
||||
horizontal: PropTypes.bool,
|
||||
// `topOffset` can either be a number, in which case its a distance from the
|
||||
// top of the container in pixels, or a string value. Valid string values are
|
||||
// of the form "20px", which is parsed as pixels, or "20%", which is parsed
|
||||
// as a percentage of the height of the containing element.
|
||||
// For instance, if you pass "-20%", and the containing element is 100px tall,
|
||||
// then the waypoint will be triggered when it has been scrolled 20px beyond
|
||||
// the top of the containing element.
|
||||
topOffset: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
// `bottomOffset` can either be a number, in which case its a distance from the
|
||||
// bottom of the container in pixels, or a string value. Valid string values are
|
||||
// of the form "20px", which is parsed as pixels, or "20%", which is parsed
|
||||
// as a percentage of the height of the containing element.
|
||||
// For instance, if you pass "20%", and the containing element is 100px tall,
|
||||
// then the waypoint will be triggered when it has been scrolled 20px beyond
|
||||
// the bottom of the containing element.
|
||||
// Similar to `topOffset`, but for the bottom of the container.
|
||||
bottomOffset: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
||||
};
|
||||
}
|
||||
|
||||
Waypoint.above = ABOVE;
|
||||
Waypoint.below = BELOW;
|
||||
Waypoint.inside = INSIDE;
|
||||
Waypoint.invisible = INVISIBLE;
|
||||
Waypoint.defaultProps = defaultProps;
|
||||
Waypoint.displayName = 'Waypoint';
|
||||
|
||||
export { Waypoint };
|
||||
Reference in New Issue
Block a user