Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-stickynode

Package Overview
Dependencies
Maintainers
0
Versions
54
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-stickynode - npm Package Compare versions

Comparing version 4.2.0 to 5.0.0

737

dist/cjs/Sticky.js

@@ -8,16 +8,2 @@ /**

require("core-js/modules/es.symbol.js");
require("core-js/modules/es.symbol.description.js");
require("core-js/modules/es.symbol.iterator.js");
require("core-js/modules/es.symbol.to-primitive.js");
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/es.date.to-primitive.js");
require("core-js/modules/es.number.constructor.js");
require("core-js/modules/es.object.get-own-property-descriptor.js");
require("core-js/modules/es.object.get-prototype-of.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.reflect.construct.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/es.weak-map.js");
require("core-js/modules/web.dom-collections.iterator.js");
Object.defineProperty(exports, "__esModule", {

@@ -27,3 +13,2 @@ value: true

exports.default = void 0;
require("core-js/modules/es.object.set-prototype-of.js");
var _react = _interopRequireWildcard(require("react"));

@@ -35,52 +20,36 @@ var _propTypes = _interopRequireDefault(require("prop-types"));

function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
// constants
var STATUS_ORIGINAL = 0; // The default status, locating at the original position.
var STATUS_RELEASED = 1; // The released status, locating at somewhere on document but not default one.
var STATUS_FIXED = 2; // The sticky status, locating fixed to the top or the bottom of screen.
const STATUS_ORIGINAL = 0; // The default status, locating at the original position.
const STATUS_RELEASED = 1; // The released status, locating at somewhere on document but not default one.
const STATUS_FIXED = 2; // The sticky status, locating fixed to the top or the bottom of screen.
var TRANSFORM_PROP = 'transform';
let TRANSFORM_PROP = 'transform';
// global variable for all instances
var doc;
var docBody;
var docEl;
var canEnableTransforms = true; // Use transform by default, so no Sticky on lower-end browser when no Modernizr
var M;
var scrollDelta = 0;
var win;
var winHeight = -1;
var Sticky = /*#__PURE__*/function (_Component) {
function Sticky(props, context) {
var _this;
_classCallCheck(this, Sticky);
_this = _callSuper(this, Sticky, [props, context]);
_this.handleResize = _this.handleResize.bind(_this);
_this.handleScroll = _this.handleScroll.bind(_this);
_this.handleScrollStart = _this.handleScrollStart.bind(_this);
_this.delta = 0;
_this.stickyTop = 0;
_this.stickyBottom = 0;
_this.frozen = false;
_this.skipNextScrollEvent = false;
_this.scrollTop = -1;
_this.bottomBoundaryTarget;
_this.topTarget;
_this.subscribers;
_this.state = {
let doc;
let docBody;
let docEl;
let canEnableTransforms = true; // Use transform by default, so no Sticky on lower-end browser when no Modernizr
let M;
let scrollDelta = 0;
let win;
let winHeight = -1;
class Sticky extends _react.Component {
constructor(props, context) {
super(props, context);
this.handleResize = this.handleResize.bind(this);
this.handleScroll = this.handleScroll.bind(this);
this.handleScrollStart = this.handleScrollStart.bind(this);
this.delta = 0;
this.stickyTop = 0;
this.stickyBottom = 0;
this.frozen = false;
this.skipNextScrollEvent = false;
this.scrollTop = -1;
this.bottomBoundaryTarget;
this.topTarget;
this.subscribers;
this.state = {
top: 0,

@@ -108,378 +77,342 @@ // A top offset from viewport top where Sticky sticks to when scrolling up

};
return _this;
}
_inherits(Sticky, _Component);
return _createClass(Sticky, [{
key: "getTargetHeight",
value: function getTargetHeight(target) {
return target && target.offsetHeight || 0;
}
}, {
key: "getTopPosition",
value: function getTopPosition(top) {
// a top argument can be provided to override reading from the props
top = top || this.props.top || 0;
if (typeof top === 'string') {
if (!this.topTarget) {
this.topTarget = doc.querySelector(top);
}
top = this.getTargetHeight(this.topTarget);
getTargetHeight(target) {
return target && target.offsetHeight || 0;
}
getTopPosition(top) {
// a top argument can be provided to override reading from the props
top = top || this.props.top || 0;
if (typeof top === 'string') {
if (!this.topTarget) {
this.topTarget = doc.querySelector(top);
}
return top;
top = this.getTargetHeight(this.topTarget);
}
}, {
key: "getTargetBottom",
value: function getTargetBottom(target) {
if (!target) {
return -1;
}
var rect = target.getBoundingClientRect();
return this.scrollTop + rect.bottom;
return top;
}
getTargetBottom(target) {
if (!target) {
return -1;
}
}, {
key: "getBottomBoundary",
value: function getBottomBoundary(bottomBoundary) {
// a bottomBoundary can be provided to avoid reading from the props
var boundary = bottomBoundary || this.props.bottomBoundary;
const rect = target.getBoundingClientRect();
return this.scrollTop + rect.bottom;
}
getBottomBoundary(bottomBoundary) {
// a bottomBoundary can be provided to avoid reading from the props
let boundary = bottomBoundary || this.props.bottomBoundary;
// TODO, bottomBoundary was an object, depricate it later.
if (_typeof(boundary) === 'object') {
boundary = boundary.value || boundary.target || 0;
// TODO, bottomBoundary was an object, depricate it later.
if (typeof boundary === 'object') {
boundary = boundary.value || boundary.target || 0;
}
if (typeof boundary === 'string') {
if (!this.bottomBoundaryTarget) {
this.bottomBoundaryTarget = doc.querySelector(boundary);
}
if (typeof boundary === 'string') {
if (!this.bottomBoundaryTarget) {
this.bottomBoundaryTarget = doc.querySelector(boundary);
}
boundary = this.getTargetBottom(this.bottomBoundaryTarget);
}
return boundary && boundary > 0 ? boundary : Infinity;
boundary = this.getTargetBottom(this.bottomBoundaryTarget);
}
}, {
key: "reset",
value: function reset() {
this.setState({
status: STATUS_ORIGINAL,
pos: 0
});
return boundary && boundary > 0 ? boundary : Infinity;
}
reset() {
this.setState({
status: STATUS_ORIGINAL,
pos: 0
});
}
release(pos) {
this.setState({
status: STATUS_RELEASED,
pos: pos - this.state.y
});
}
fix(pos) {
this.setState({
status: STATUS_FIXED,
pos: pos
});
}
/**
* Update the initial position, width, and height. It should update whenever children change.
* @param {Object} options optional top and bottomBoundary new values
*/
updateInitialDimension(options) {
options = options || {};
if (!this.outerElement || !this.innerElement) {
return;
}
}, {
key: "release",
value: function release(pos) {
this.setState({
status: STATUS_RELEASED,
pos: pos - this.state.y
});
const outerRect = this.outerElement.getBoundingClientRect();
const innerRect = this.innerElement.getBoundingClientRect();
const width = outerRect.width || outerRect.right - outerRect.left;
const height = innerRect.height || innerRect.bottom - innerRect.top;
const outerY = outerRect.top + this.scrollTop;
this.setState({
top: this.getTopPosition(options.top),
bottom: Math.min(this.state.top + height, winHeight),
width,
height,
x: outerRect.left,
y: outerY,
bottomBoundary: this.getBottomBoundary(options.bottomBoundary),
topBoundary: outerY
});
}
handleResize(e, ae) {
if (this.props.shouldFreeze()) {
return;
}
}, {
key: "fix",
value: function fix(pos) {
this.setState({
status: STATUS_FIXED,
pos: pos
});
winHeight = ae.resize.height;
this.updateInitialDimension();
this.update();
}
handleScrollStart(e, ae) {
this.frozen = this.props.shouldFreeze();
if (this.frozen) {
return;
}
/**
* Update the initial position, width, and height. It should update whenever children change.
* @param {Object} options optional top and bottomBoundary new values
*/
}, {
key: "updateInitialDimension",
value: function updateInitialDimension(options) {
options = options || {};
if (!this.outerElement || !this.innerElement) {
return;
}
var outerRect = this.outerElement.getBoundingClientRect();
var innerRect = this.innerElement.getBoundingClientRect();
var width = outerRect.width || outerRect.right - outerRect.left;
var height = innerRect.height || innerRect.bottom - innerRect.top;
var outerY = outerRect.top + this.scrollTop;
this.setState({
top: this.getTopPosition(options.top),
bottom: Math.min(this.state.top + height, winHeight),
width: width,
height: height,
x: outerRect.left,
y: outerY,
bottomBoundary: this.getBottomBoundary(options.bottomBoundary),
topBoundary: outerY
});
}
}, {
key: "handleResize",
value: function handleResize(e, ae) {
if (this.props.shouldFreeze()) {
return;
}
winHeight = ae.resize.height;
if (this.scrollTop === ae.scroll.top) {
// Scroll position hasn't changed,
// do nothing
this.skipNextScrollEvent = true;
} else {
this.scrollTop = ae.scroll.top;
this.updateInitialDimension();
this.update();
}
}, {
key: "handleScrollStart",
value: function handleScrollStart(e, ae) {
this.frozen = this.props.shouldFreeze();
if (this.frozen) {
return;
}
if (this.scrollTop === ae.scroll.top) {
// Scroll position hasn't changed,
// do nothing
this.skipNextScrollEvent = true;
} else {
this.scrollTop = ae.scroll.top;
this.updateInitialDimension();
}
}
handleScroll(e, ae) {
// Scroll doesn't need to be handled
if (this.skipNextScrollEvent) {
this.skipNextScrollEvent = false;
return;
}
}, {
key: "handleScroll",
value: function handleScroll(e, ae) {
// Scroll doesn't need to be handled
if (this.skipNextScrollEvent) {
this.skipNextScrollEvent = false;
return;
scrollDelta = ae.scroll.delta;
this.scrollTop = ae.scroll.top;
this.update();
}
/**
* Update Sticky position.
*/
update() {
var disabled = !this.props.enabled || this.state.bottomBoundary - this.state.topBoundary <= this.state.height || this.state.width === 0 && this.state.height === 0;
if (disabled) {
if (this.state.status !== STATUS_ORIGINAL) {
this.reset();
}
scrollDelta = ae.scroll.delta;
this.scrollTop = ae.scroll.top;
this.update();
return;
}
var delta = scrollDelta;
// "top" and "bottom" are the positions that this.state.top and this.state.bottom project
// on document from viewport.
var top = this.scrollTop + this.state.top;
var bottom = this.scrollTop + this.state.bottom;
/**
* Update Sticky position.
*/
}, {
key: "update",
value: function update() {
var disabled = !this.props.enabled || this.state.bottomBoundary - this.state.topBoundary <= this.state.height || this.state.width === 0 && this.state.height === 0;
if (disabled) {
if (this.state.status !== STATUS_ORIGINAL) {
this.reset();
// There are 2 principles to make sure Sticky won't get wrong so much:
// 1. Reset Sticky to the original postion when "top" <= topBoundary
// 2. Release Sticky to the bottom boundary when "bottom" >= bottomBoundary
if (top <= this.state.topBoundary) {
// #1
this.reset();
} else if (bottom >= this.state.bottomBoundary) {
// #2
this.stickyBottom = this.state.bottomBoundary;
this.stickyTop = this.stickyBottom - this.state.height;
this.release(this.stickyTop);
} else {
if (this.state.height > winHeight - this.state.top) {
// In this case, Sticky is higher then viewport minus top offset
switch (this.state.status) {
case STATUS_ORIGINAL:
this.release(this.state.y);
this.stickyTop = this.state.y;
this.stickyBottom = this.stickyTop + this.state.height;
// Commentting out "break" is on purpose, because there is a chance to transit to FIXED
// from ORIGINAL when calling window.scrollTo().
// break;
/* eslint-disable-next-line no-fallthrough */
case STATUS_RELEASED:
// If "top" and "bottom" are inbetween stickyTop and stickyBottom, then Sticky is in
// RELEASE status. Otherwise, it changes to FIXED status, and its bottom sticks to
// viewport bottom when scrolling down, or its top sticks to viewport top when scrolling up.
this.stickyBottom = this.stickyTop + this.state.height;
if (delta > 0 && bottom > this.stickyBottom) {
this.fix(this.state.bottom - this.state.height);
} else if (delta < 0 && top < this.stickyTop) {
this.fix(this.state.top);
}
break;
case STATUS_FIXED:
var toRelease = true;
var pos = this.state.pos;
var height = this.state.height;
// In regular cases, when Sticky is in FIXED status,
// 1. it's top will stick to the screen top,
// 2. it's bottom will stick to the screen bottom,
// 3. if not the cases above, then it's height gets changed
if (delta > 0 && pos === this.state.top) {
// case 1, and scrolling down
this.stickyTop = top - delta;
this.stickyBottom = this.stickyTop + height;
} else if (delta < 0 && pos === this.state.bottom - height) {
// case 2, and scrolling up
this.stickyBottom = bottom - delta;
this.stickyTop = this.stickyBottom - height;
} else if (pos !== this.state.bottom - height && pos !== this.state.top) {
// case 3
// This case only happens when Sticky's bottom sticks to the screen bottom and
// its height gets changed. Sticky should be in RELEASE status and update its
// sticky bottom by calculating how much height it changed.
const deltaHeight = pos + height - this.state.bottom;
this.stickyBottom = bottom - delta + deltaHeight;
this.stickyTop = this.stickyBottom - height;
} else {
toRelease = false;
}
if (toRelease) {
this.release(this.stickyTop);
}
break;
}
return;
} else {
// In this case, Sticky is shorter then viewport minus top offset
// and will always fix to the top offset of viewport
this.fix(this.state.top);
}
var delta = scrollDelta;
// "top" and "bottom" are the positions that this.state.top and this.state.bottom project
// on document from viewport.
var top = this.scrollTop + this.state.top;
var bottom = this.scrollTop + this.state.bottom;
}
this.delta = delta;
}
componentDidUpdate(prevProps, prevState) {
if (prevState.status !== this.state.status && this.props.onStateChange) {
this.props.onStateChange({
status: this.state.status
});
}
// There are 2 principles to make sure Sticky won't get wrong so much:
// 1. Reset Sticky to the original postion when "top" <= topBoundary
// 2. Release Sticky to the bottom boundary when "bottom" >= bottomBoundary
if (top <= this.state.topBoundary) {
// #1
this.reset();
} else if (bottom >= this.state.bottomBoundary) {
// #2
this.stickyBottom = this.state.bottomBoundary;
this.stickyTop = this.stickyBottom - this.state.height;
this.release(this.stickyTop);
} else {
if (this.state.height > winHeight - this.state.top) {
// In this case, Sticky is higher then viewport minus top offset
switch (this.state.status) {
case STATUS_ORIGINAL:
this.release(this.state.y);
this.stickyTop = this.state.y;
this.stickyBottom = this.stickyTop + this.state.height;
// Commentting out "break" is on purpose, because there is a chance to transit to FIXED
// from ORIGINAL when calling window.scrollTo().
// break;
/* eslint-disable-next-line no-fallthrough */
case STATUS_RELEASED:
// If "top" and "bottom" are inbetween stickyTop and stickyBottom, then Sticky is in
// RELEASE status. Otherwise, it changes to FIXED status, and its bottom sticks to
// viewport bottom when scrolling down, or its top sticks to viewport top when scrolling up.
this.stickyBottom = this.stickyTop + this.state.height;
if (delta > 0 && bottom > this.stickyBottom) {
this.fix(this.state.bottom - this.state.height);
} else if (delta < 0 && top < this.stickyTop) {
this.fix(this.state.top);
}
break;
case STATUS_FIXED:
var toRelease = true;
var pos = this.state.pos;
var height = this.state.height;
// In regular cases, when Sticky is in FIXED status,
// 1. it's top will stick to the screen top,
// 2. it's bottom will stick to the screen bottom,
// 3. if not the cases above, then it's height gets changed
if (delta > 0 && pos === this.state.top) {
// case 1, and scrolling down
this.stickyTop = top - delta;
this.stickyBottom = this.stickyTop + height;
} else if (delta < 0 && pos === this.state.bottom - height) {
// case 2, and scrolling up
this.stickyBottom = bottom - delta;
this.stickyTop = this.stickyBottom - height;
} else if (pos !== this.state.bottom - height && pos !== this.state.top) {
// case 3
// This case only happens when Sticky's bottom sticks to the screen bottom and
// its height gets changed. Sticky should be in RELEASE status and update its
// sticky bottom by calculating how much height it changed.
var deltaHeight = pos + height - this.state.bottom;
this.stickyBottom = bottom - delta + deltaHeight;
this.stickyTop = this.stickyBottom - height;
} else {
toRelease = false;
}
if (toRelease) {
this.release(this.stickyTop);
}
break;
}
// check if we are up-to-date, is triggered in case of scroll restoration
if (this.state.top !== prevState.top) {
this.updateInitialDimension();
this.update();
}
const arePropsChanged = !(0, _shallowequal.default)(this.props, prevProps);
if (arePropsChanged) {
// if the props for enabling are toggled, then trigger the update or reset depending on the current props
if (prevProps.enabled !== this.props.enabled) {
if (this.props.enabled) {
this.setState({
activated: true
}, () => {
this.updateInitialDimension();
this.update();
});
} else {
// In this case, Sticky is shorter then viewport minus top offset
// and will always fix to the top offset of viewport
this.fix(this.state.top);
this.setState({
activated: false
}, () => {
this.reset();
});
}
}
this.delta = delta;
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
var _this2 = this;
if (prevState.status !== this.state.status && this.props.onStateChange) {
this.props.onStateChange({
status: this.state.status
});
}
// check if we are up-to-date, is triggered in case of scroll restoration
if (this.state.top !== prevState.top) {
// if the top or bottomBoundary props were changed, then trigger the update
else if (prevProps.top !== this.props.top || prevProps.bottomBoundary !== this.props.bottomBoundary) {
this.updateInitialDimension();
this.update();
}
var arePropsChanged = !(0, _shallowequal.default)(this.props, prevProps);
if (arePropsChanged) {
// if the props for enabling are toggled, then trigger the update or reset depending on the current props
if (prevProps.enabled !== this.props.enabled) {
if (this.props.enabled) {
this.setState({
activated: true
}, function () {
_this2.updateInitialDimension();
_this2.update();
});
} else {
this.setState({
activated: false
}, function () {
_this2.reset();
});
}
}
// if the top or bottomBoundary props were changed, then trigger the update
else if (prevProps.top !== this.props.top || prevProps.bottomBoundary !== this.props.bottomBoundary) {
this.updateInitialDimension();
this.update();
}
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
var subscribers = this.subscribers || [];
for (var i = subscribers.length - 1; i >= 0; i--) {
this.subscribers[i].unsubscribe();
}
componentWillUnmount() {
const subscribers = this.subscribers || [];
for (var i = subscribers.length - 1; i >= 0; i--) {
this.subscribers[i].unsubscribe();
}
}
componentDidMount() {
// Only initialize the globals if this is the first
// time this component type has been mounted
if (!win) {
win = window;
doc = document;
docEl = doc.documentElement;
docBody = doc.body;
winHeight = win.innerHeight || docEl.clientHeight;
M = window.Modernizr;
// No Sticky on lower-end browser when no Modernizr
if (M && M.prefixed) {
canEnableTransforms = M.csstransforms3d;
TRANSFORM_PROP = M.prefixed('transform');
}
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
// Only initialize the globals if this is the first
// time this component type has been mounted
if (!win) {
win = window;
doc = document;
docEl = doc.documentElement;
docBody = doc.body;
winHeight = win.innerHeight || docEl.clientHeight;
M = window.Modernizr;
// No Sticky on lower-end browser when no Modernizr
if (M && M.prefixed) {
canEnableTransforms = M.csstransforms3d;
TRANSFORM_PROP = M.prefixed('transform');
}
}
// when mount, the scrollTop is not necessary on the top
this.scrollTop = docBody.scrollTop + docEl.scrollTop;
if (this.props.enabled) {
this.setState({
activated: true
});
this.updateInitialDimension();
this.update();
}
// bind the listeners regardless if initially enabled - allows the component to toggle sticky functionality
this.subscribers = [(0, _subscribeUiEvent.subscribe)('scrollStart', this.handleScrollStart.bind(this), {
useRAF: true
}), (0, _subscribeUiEvent.subscribe)('scroll', this.handleScroll.bind(this), {
useRAF: true,
enableScrollInfo: true
}), (0, _subscribeUiEvent.subscribe)('resize', this.handleResize.bind(this), {
enableResizeInfo: true
})];
// when mount, the scrollTop is not necessary on the top
this.scrollTop = docBody.scrollTop + docEl.scrollTop;
if (this.props.enabled) {
this.setState({
activated: true
});
this.updateInitialDimension();
this.update();
}
}, {
key: "translate",
value: function translate(style, pos) {
var enableTransforms = canEnableTransforms && this.props.enableTransforms;
if (enableTransforms && this.state.activated) {
style[TRANSFORM_PROP] = 'translate3d(0,' + Math.round(pos) + 'px,0)';
} else {
style.top = pos + 'px';
}
// bind the listeners regardless if initially enabled - allows the component to toggle sticky functionality
this.subscribers = [(0, _subscribeUiEvent.subscribe)('scrollStart', this.handleScrollStart.bind(this), {
useRAF: true
}), (0, _subscribeUiEvent.subscribe)('scroll', this.handleScroll.bind(this), {
useRAF: true,
enableScrollInfo: true
}), (0, _subscribeUiEvent.subscribe)('resize', this.handleResize.bind(this), {
enableResizeInfo: true
})];
}
translate(style, pos) {
const enableTransforms = canEnableTransforms && this.props.enableTransforms;
if (enableTransforms && this.state.activated) {
style[TRANSFORM_PROP] = 'translate3d(0,' + Math.round(pos) + 'px,0)';
} else {
style.top = pos + 'px';
}
}, {
key: "shouldComponentUpdate",
value: function shouldComponentUpdate(nextProps, nextState) {
return !this.props.shouldFreeze() && !((0, _shallowequal.default)(this.props, nextProps) && (0, _shallowequal.default)(this.state, nextState));
}
}, {
key: "render",
value: function render() {
var _this3 = this;
// TODO, "overflow: auto" prevents collapse, need a good way to get children height
var innerStyle = {
position: this.state.status === STATUS_FIXED ? 'fixed' : 'relative',
top: this.state.status === STATUS_FIXED ? '0px' : '',
zIndex: this.props.innerZ
};
var outerStyle = {};
}
shouldComponentUpdate(nextProps, nextState) {
return !this.props.shouldFreeze() && !((0, _shallowequal.default)(this.props, nextProps) && (0, _shallowequal.default)(this.state, nextState));
}
render() {
// TODO, "overflow: auto" prevents collapse, need a good way to get children height
const innerStyle = {
position: this.state.status === STATUS_FIXED ? 'fixed' : 'relative',
top: this.state.status === STATUS_FIXED ? '0px' : '',
zIndex: this.props.innerZ
};
const outerStyle = {};
// always use translate3d to enhance the performance
this.translate(innerStyle, this.state.pos);
if (this.state.status !== STATUS_ORIGINAL) {
innerStyle.width = this.state.width + 'px';
outerStyle.height = this.state.height + 'px';
}
var outerClasses = (0, _classnames.default)('sticky-outer-wrapper', this.props.className, _defineProperty(_defineProperty({}, this.props.activeClass, this.state.status === STATUS_FIXED), this.props.releasedClass, this.state.status === STATUS_RELEASED));
var innerClasses = (0, _classnames.default)('sticky-inner-wrapper', this.props.innerClass, _defineProperty({}, this.props.innerActiveClass, this.state.status === STATUS_FIXED));
var children = this.props.children;
return /*#__PURE__*/_react.default.createElement("div", {
ref: function ref(outer) {
_this3.outerElement = outer;
},
className: outerClasses,
style: outerStyle
}, /*#__PURE__*/_react.default.createElement("div", {
ref: function ref(inner) {
_this3.innerElement = inner;
},
className: innerClasses,
style: innerStyle
}, typeof children === 'function' ? children({
status: this.state.status
}) : children));
// always use translate3d to enhance the performance
this.translate(innerStyle, this.state.pos);
if (this.state.status !== STATUS_ORIGINAL) {
innerStyle.width = this.state.width + 'px';
outerStyle.height = this.state.height + 'px';
}
}]);
}(_react.Component);
const outerClasses = (0, _classnames.default)('sticky-outer-wrapper', this.props.className, {
[this.props.activeClass]: this.state.status === STATUS_FIXED,
[this.props.releasedClass]: this.state.status === STATUS_RELEASED
});
const innerClasses = (0, _classnames.default)('sticky-inner-wrapper', this.props.innerClass, {
[this.props.innerActiveClass]: this.state.status === STATUS_FIXED
});
const children = this.props.children;
return /*#__PURE__*/_react.default.createElement("div", {
ref: outer => {
this.outerElement = outer;
},
className: outerClasses,
style: outerStyle
}, /*#__PURE__*/_react.default.createElement("div", {
ref: inner => {
this.innerElement = inner;
},
className: innerClasses,
style: innerStyle
}, typeof children === 'function' ? children({
status: this.state.status
}) : children));
}
}
Sticky.displayName = 'Sticky';
Sticky.defaultProps = {
shouldFreeze: function shouldFreeze() {
shouldFreeze: function () {
return false;

@@ -486,0 +419,0 @@ },

@@ -8,29 +8,2 @@ /**

import "core-js/modules/es.symbol.js";
import "core-js/modules/es.symbol.description.js";
import "core-js/modules/es.symbol.iterator.js";
import "core-js/modules/es.symbol.to-primitive.js";
import "core-js/modules/es.array.iterator.js";
import "core-js/modules/es.date.to-primitive.js";
import "core-js/modules/es.number.constructor.js";
import "core-js/modules/es.object.get-prototype-of.js";
import "core-js/modules/es.object.set-prototype-of.js";
import "core-js/modules/es.object.to-string.js";
import "core-js/modules/es.reflect.construct.js";
import "core-js/modules/es.string.iterator.js";
import "core-js/modules/web.dom-collections.iterator.js";
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
import React, { Component } from 'react';

@@ -43,35 +16,33 @@ import PropTypes from 'prop-types';

// constants
var STATUS_ORIGINAL = 0; // The default status, locating at the original position.
var STATUS_RELEASED = 1; // The released status, locating at somewhere on document but not default one.
var STATUS_FIXED = 2; // The sticky status, locating fixed to the top or the bottom of screen.
const STATUS_ORIGINAL = 0; // The default status, locating at the original position.
const STATUS_RELEASED = 1; // The released status, locating at somewhere on document but not default one.
const STATUS_FIXED = 2; // The sticky status, locating fixed to the top or the bottom of screen.
var TRANSFORM_PROP = 'transform';
let TRANSFORM_PROP = 'transform';
// global variable for all instances
var doc;
var docBody;
var docEl;
var canEnableTransforms = true; // Use transform by default, so no Sticky on lower-end browser when no Modernizr
var M;
var scrollDelta = 0;
var win;
var winHeight = -1;
var Sticky = /*#__PURE__*/function (_Component) {
function Sticky(props, context) {
var _this;
_classCallCheck(this, Sticky);
_this = _callSuper(this, Sticky, [props, context]);
_this.handleResize = _this.handleResize.bind(_this);
_this.handleScroll = _this.handleScroll.bind(_this);
_this.handleScrollStart = _this.handleScrollStart.bind(_this);
_this.delta = 0;
_this.stickyTop = 0;
_this.stickyBottom = 0;
_this.frozen = false;
_this.skipNextScrollEvent = false;
_this.scrollTop = -1;
_this.bottomBoundaryTarget;
_this.topTarget;
_this.subscribers;
_this.state = {
let doc;
let docBody;
let docEl;
let canEnableTransforms = true; // Use transform by default, so no Sticky on lower-end browser when no Modernizr
let M;
let scrollDelta = 0;
let win;
let winHeight = -1;
class Sticky extends Component {
constructor(props, context) {
super(props, context);
this.handleResize = this.handleResize.bind(this);
this.handleScroll = this.handleScroll.bind(this);
this.handleScrollStart = this.handleScrollStart.bind(this);
this.delta = 0;
this.stickyTop = 0;
this.stickyBottom = 0;
this.frozen = false;
this.skipNextScrollEvent = false;
this.scrollTop = -1;
this.bottomBoundaryTarget;
this.topTarget;
this.subscribers;
this.state = {
top: 0,

@@ -99,378 +70,342 @@ // A top offset from viewport top where Sticky sticks to when scrolling up

};
return _this;
}
_inherits(Sticky, _Component);
return _createClass(Sticky, [{
key: "getTargetHeight",
value: function getTargetHeight(target) {
return target && target.offsetHeight || 0;
}
}, {
key: "getTopPosition",
value: function getTopPosition(top) {
// a top argument can be provided to override reading from the props
top = top || this.props.top || 0;
if (typeof top === 'string') {
if (!this.topTarget) {
this.topTarget = doc.querySelector(top);
}
top = this.getTargetHeight(this.topTarget);
getTargetHeight(target) {
return target && target.offsetHeight || 0;
}
getTopPosition(top) {
// a top argument can be provided to override reading from the props
top = top || this.props.top || 0;
if (typeof top === 'string') {
if (!this.topTarget) {
this.topTarget = doc.querySelector(top);
}
return top;
top = this.getTargetHeight(this.topTarget);
}
}, {
key: "getTargetBottom",
value: function getTargetBottom(target) {
if (!target) {
return -1;
}
var rect = target.getBoundingClientRect();
return this.scrollTop + rect.bottom;
return top;
}
getTargetBottom(target) {
if (!target) {
return -1;
}
}, {
key: "getBottomBoundary",
value: function getBottomBoundary(bottomBoundary) {
// a bottomBoundary can be provided to avoid reading from the props
var boundary = bottomBoundary || this.props.bottomBoundary;
const rect = target.getBoundingClientRect();
return this.scrollTop + rect.bottom;
}
getBottomBoundary(bottomBoundary) {
// a bottomBoundary can be provided to avoid reading from the props
let boundary = bottomBoundary || this.props.bottomBoundary;
// TODO, bottomBoundary was an object, depricate it later.
if (_typeof(boundary) === 'object') {
boundary = boundary.value || boundary.target || 0;
// TODO, bottomBoundary was an object, depricate it later.
if (typeof boundary === 'object') {
boundary = boundary.value || boundary.target || 0;
}
if (typeof boundary === 'string') {
if (!this.bottomBoundaryTarget) {
this.bottomBoundaryTarget = doc.querySelector(boundary);
}
if (typeof boundary === 'string') {
if (!this.bottomBoundaryTarget) {
this.bottomBoundaryTarget = doc.querySelector(boundary);
}
boundary = this.getTargetBottom(this.bottomBoundaryTarget);
}
return boundary && boundary > 0 ? boundary : Infinity;
boundary = this.getTargetBottom(this.bottomBoundaryTarget);
}
}, {
key: "reset",
value: function reset() {
this.setState({
status: STATUS_ORIGINAL,
pos: 0
});
return boundary && boundary > 0 ? boundary : Infinity;
}
reset() {
this.setState({
status: STATUS_ORIGINAL,
pos: 0
});
}
release(pos) {
this.setState({
status: STATUS_RELEASED,
pos: pos - this.state.y
});
}
fix(pos) {
this.setState({
status: STATUS_FIXED,
pos: pos
});
}
/**
* Update the initial position, width, and height. It should update whenever children change.
* @param {Object} options optional top and bottomBoundary new values
*/
updateInitialDimension(options) {
options = options || {};
if (!this.outerElement || !this.innerElement) {
return;
}
}, {
key: "release",
value: function release(pos) {
this.setState({
status: STATUS_RELEASED,
pos: pos - this.state.y
});
const outerRect = this.outerElement.getBoundingClientRect();
const innerRect = this.innerElement.getBoundingClientRect();
const width = outerRect.width || outerRect.right - outerRect.left;
const height = innerRect.height || innerRect.bottom - innerRect.top;
const outerY = outerRect.top + this.scrollTop;
this.setState({
top: this.getTopPosition(options.top),
bottom: Math.min(this.state.top + height, winHeight),
width,
height,
x: outerRect.left,
y: outerY,
bottomBoundary: this.getBottomBoundary(options.bottomBoundary),
topBoundary: outerY
});
}
handleResize(e, ae) {
if (this.props.shouldFreeze()) {
return;
}
}, {
key: "fix",
value: function fix(pos) {
this.setState({
status: STATUS_FIXED,
pos: pos
});
winHeight = ae.resize.height;
this.updateInitialDimension();
this.update();
}
handleScrollStart(e, ae) {
this.frozen = this.props.shouldFreeze();
if (this.frozen) {
return;
}
/**
* Update the initial position, width, and height. It should update whenever children change.
* @param {Object} options optional top and bottomBoundary new values
*/
}, {
key: "updateInitialDimension",
value: function updateInitialDimension(options) {
options = options || {};
if (!this.outerElement || !this.innerElement) {
return;
}
var outerRect = this.outerElement.getBoundingClientRect();
var innerRect = this.innerElement.getBoundingClientRect();
var width = outerRect.width || outerRect.right - outerRect.left;
var height = innerRect.height || innerRect.bottom - innerRect.top;
var outerY = outerRect.top + this.scrollTop;
this.setState({
top: this.getTopPosition(options.top),
bottom: Math.min(this.state.top + height, winHeight),
width: width,
height: height,
x: outerRect.left,
y: outerY,
bottomBoundary: this.getBottomBoundary(options.bottomBoundary),
topBoundary: outerY
});
}
}, {
key: "handleResize",
value: function handleResize(e, ae) {
if (this.props.shouldFreeze()) {
return;
}
winHeight = ae.resize.height;
if (this.scrollTop === ae.scroll.top) {
// Scroll position hasn't changed,
// do nothing
this.skipNextScrollEvent = true;
} else {
this.scrollTop = ae.scroll.top;
this.updateInitialDimension();
this.update();
}
}, {
key: "handleScrollStart",
value: function handleScrollStart(e, ae) {
this.frozen = this.props.shouldFreeze();
if (this.frozen) {
return;
}
if (this.scrollTop === ae.scroll.top) {
// Scroll position hasn't changed,
// do nothing
this.skipNextScrollEvent = true;
} else {
this.scrollTop = ae.scroll.top;
this.updateInitialDimension();
}
}
handleScroll(e, ae) {
// Scroll doesn't need to be handled
if (this.skipNextScrollEvent) {
this.skipNextScrollEvent = false;
return;
}
}, {
key: "handleScroll",
value: function handleScroll(e, ae) {
// Scroll doesn't need to be handled
if (this.skipNextScrollEvent) {
this.skipNextScrollEvent = false;
return;
scrollDelta = ae.scroll.delta;
this.scrollTop = ae.scroll.top;
this.update();
}
/**
* Update Sticky position.
*/
update() {
var disabled = !this.props.enabled || this.state.bottomBoundary - this.state.topBoundary <= this.state.height || this.state.width === 0 && this.state.height === 0;
if (disabled) {
if (this.state.status !== STATUS_ORIGINAL) {
this.reset();
}
scrollDelta = ae.scroll.delta;
this.scrollTop = ae.scroll.top;
this.update();
return;
}
var delta = scrollDelta;
// "top" and "bottom" are the positions that this.state.top and this.state.bottom project
// on document from viewport.
var top = this.scrollTop + this.state.top;
var bottom = this.scrollTop + this.state.bottom;
/**
* Update Sticky position.
*/
}, {
key: "update",
value: function update() {
var disabled = !this.props.enabled || this.state.bottomBoundary - this.state.topBoundary <= this.state.height || this.state.width === 0 && this.state.height === 0;
if (disabled) {
if (this.state.status !== STATUS_ORIGINAL) {
this.reset();
// There are 2 principles to make sure Sticky won't get wrong so much:
// 1. Reset Sticky to the original postion when "top" <= topBoundary
// 2. Release Sticky to the bottom boundary when "bottom" >= bottomBoundary
if (top <= this.state.topBoundary) {
// #1
this.reset();
} else if (bottom >= this.state.bottomBoundary) {
// #2
this.stickyBottom = this.state.bottomBoundary;
this.stickyTop = this.stickyBottom - this.state.height;
this.release(this.stickyTop);
} else {
if (this.state.height > winHeight - this.state.top) {
// In this case, Sticky is higher then viewport minus top offset
switch (this.state.status) {
case STATUS_ORIGINAL:
this.release(this.state.y);
this.stickyTop = this.state.y;
this.stickyBottom = this.stickyTop + this.state.height;
// Commentting out "break" is on purpose, because there is a chance to transit to FIXED
// from ORIGINAL when calling window.scrollTo().
// break;
/* eslint-disable-next-line no-fallthrough */
case STATUS_RELEASED:
// If "top" and "bottom" are inbetween stickyTop and stickyBottom, then Sticky is in
// RELEASE status. Otherwise, it changes to FIXED status, and its bottom sticks to
// viewport bottom when scrolling down, or its top sticks to viewport top when scrolling up.
this.stickyBottom = this.stickyTop + this.state.height;
if (delta > 0 && bottom > this.stickyBottom) {
this.fix(this.state.bottom - this.state.height);
} else if (delta < 0 && top < this.stickyTop) {
this.fix(this.state.top);
}
break;
case STATUS_FIXED:
var toRelease = true;
var pos = this.state.pos;
var height = this.state.height;
// In regular cases, when Sticky is in FIXED status,
// 1. it's top will stick to the screen top,
// 2. it's bottom will stick to the screen bottom,
// 3. if not the cases above, then it's height gets changed
if (delta > 0 && pos === this.state.top) {
// case 1, and scrolling down
this.stickyTop = top - delta;
this.stickyBottom = this.stickyTop + height;
} else if (delta < 0 && pos === this.state.bottom - height) {
// case 2, and scrolling up
this.stickyBottom = bottom - delta;
this.stickyTop = this.stickyBottom - height;
} else if (pos !== this.state.bottom - height && pos !== this.state.top) {
// case 3
// This case only happens when Sticky's bottom sticks to the screen bottom and
// its height gets changed. Sticky should be in RELEASE status and update its
// sticky bottom by calculating how much height it changed.
const deltaHeight = pos + height - this.state.bottom;
this.stickyBottom = bottom - delta + deltaHeight;
this.stickyTop = this.stickyBottom - height;
} else {
toRelease = false;
}
if (toRelease) {
this.release(this.stickyTop);
}
break;
}
return;
} else {
// In this case, Sticky is shorter then viewport minus top offset
// and will always fix to the top offset of viewport
this.fix(this.state.top);
}
var delta = scrollDelta;
// "top" and "bottom" are the positions that this.state.top and this.state.bottom project
// on document from viewport.
var top = this.scrollTop + this.state.top;
var bottom = this.scrollTop + this.state.bottom;
}
this.delta = delta;
}
componentDidUpdate(prevProps, prevState) {
if (prevState.status !== this.state.status && this.props.onStateChange) {
this.props.onStateChange({
status: this.state.status
});
}
// There are 2 principles to make sure Sticky won't get wrong so much:
// 1. Reset Sticky to the original postion when "top" <= topBoundary
// 2. Release Sticky to the bottom boundary when "bottom" >= bottomBoundary
if (top <= this.state.topBoundary) {
// #1
this.reset();
} else if (bottom >= this.state.bottomBoundary) {
// #2
this.stickyBottom = this.state.bottomBoundary;
this.stickyTop = this.stickyBottom - this.state.height;
this.release(this.stickyTop);
} else {
if (this.state.height > winHeight - this.state.top) {
// In this case, Sticky is higher then viewport minus top offset
switch (this.state.status) {
case STATUS_ORIGINAL:
this.release(this.state.y);
this.stickyTop = this.state.y;
this.stickyBottom = this.stickyTop + this.state.height;
// Commentting out "break" is on purpose, because there is a chance to transit to FIXED
// from ORIGINAL when calling window.scrollTo().
// break;
/* eslint-disable-next-line no-fallthrough */
case STATUS_RELEASED:
// If "top" and "bottom" are inbetween stickyTop and stickyBottom, then Sticky is in
// RELEASE status. Otherwise, it changes to FIXED status, and its bottom sticks to
// viewport bottom when scrolling down, or its top sticks to viewport top when scrolling up.
this.stickyBottom = this.stickyTop + this.state.height;
if (delta > 0 && bottom > this.stickyBottom) {
this.fix(this.state.bottom - this.state.height);
} else if (delta < 0 && top < this.stickyTop) {
this.fix(this.state.top);
}
break;
case STATUS_FIXED:
var toRelease = true;
var pos = this.state.pos;
var height = this.state.height;
// In regular cases, when Sticky is in FIXED status,
// 1. it's top will stick to the screen top,
// 2. it's bottom will stick to the screen bottom,
// 3. if not the cases above, then it's height gets changed
if (delta > 0 && pos === this.state.top) {
// case 1, and scrolling down
this.stickyTop = top - delta;
this.stickyBottom = this.stickyTop + height;
} else if (delta < 0 && pos === this.state.bottom - height) {
// case 2, and scrolling up
this.stickyBottom = bottom - delta;
this.stickyTop = this.stickyBottom - height;
} else if (pos !== this.state.bottom - height && pos !== this.state.top) {
// case 3
// This case only happens when Sticky's bottom sticks to the screen bottom and
// its height gets changed. Sticky should be in RELEASE status and update its
// sticky bottom by calculating how much height it changed.
var deltaHeight = pos + height - this.state.bottom;
this.stickyBottom = bottom - delta + deltaHeight;
this.stickyTop = this.stickyBottom - height;
} else {
toRelease = false;
}
if (toRelease) {
this.release(this.stickyTop);
}
break;
}
// check if we are up-to-date, is triggered in case of scroll restoration
if (this.state.top !== prevState.top) {
this.updateInitialDimension();
this.update();
}
const arePropsChanged = !shallowEqual(this.props, prevProps);
if (arePropsChanged) {
// if the props for enabling are toggled, then trigger the update or reset depending on the current props
if (prevProps.enabled !== this.props.enabled) {
if (this.props.enabled) {
this.setState({
activated: true
}, () => {
this.updateInitialDimension();
this.update();
});
} else {
// In this case, Sticky is shorter then viewport minus top offset
// and will always fix to the top offset of viewport
this.fix(this.state.top);
this.setState({
activated: false
}, () => {
this.reset();
});
}
}
this.delta = delta;
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
var _this2 = this;
if (prevState.status !== this.state.status && this.props.onStateChange) {
this.props.onStateChange({
status: this.state.status
});
}
// check if we are up-to-date, is triggered in case of scroll restoration
if (this.state.top !== prevState.top) {
// if the top or bottomBoundary props were changed, then trigger the update
else if (prevProps.top !== this.props.top || prevProps.bottomBoundary !== this.props.bottomBoundary) {
this.updateInitialDimension();
this.update();
}
var arePropsChanged = !shallowEqual(this.props, prevProps);
if (arePropsChanged) {
// if the props for enabling are toggled, then trigger the update or reset depending on the current props
if (prevProps.enabled !== this.props.enabled) {
if (this.props.enabled) {
this.setState({
activated: true
}, function () {
_this2.updateInitialDimension();
_this2.update();
});
} else {
this.setState({
activated: false
}, function () {
_this2.reset();
});
}
}
// if the top or bottomBoundary props were changed, then trigger the update
else if (prevProps.top !== this.props.top || prevProps.bottomBoundary !== this.props.bottomBoundary) {
this.updateInitialDimension();
this.update();
}
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
var subscribers = this.subscribers || [];
for (var i = subscribers.length - 1; i >= 0; i--) {
this.subscribers[i].unsubscribe();
}
componentWillUnmount() {
const subscribers = this.subscribers || [];
for (var i = subscribers.length - 1; i >= 0; i--) {
this.subscribers[i].unsubscribe();
}
}
componentDidMount() {
// Only initialize the globals if this is the first
// time this component type has been mounted
if (!win) {
win = window;
doc = document;
docEl = doc.documentElement;
docBody = doc.body;
winHeight = win.innerHeight || docEl.clientHeight;
M = window.Modernizr;
// No Sticky on lower-end browser when no Modernizr
if (M && M.prefixed) {
canEnableTransforms = M.csstransforms3d;
TRANSFORM_PROP = M.prefixed('transform');
}
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
// Only initialize the globals if this is the first
// time this component type has been mounted
if (!win) {
win = window;
doc = document;
docEl = doc.documentElement;
docBody = doc.body;
winHeight = win.innerHeight || docEl.clientHeight;
M = window.Modernizr;
// No Sticky on lower-end browser when no Modernizr
if (M && M.prefixed) {
canEnableTransforms = M.csstransforms3d;
TRANSFORM_PROP = M.prefixed('transform');
}
}
// when mount, the scrollTop is not necessary on the top
this.scrollTop = docBody.scrollTop + docEl.scrollTop;
if (this.props.enabled) {
this.setState({
activated: true
});
this.updateInitialDimension();
this.update();
}
// bind the listeners regardless if initially enabled - allows the component to toggle sticky functionality
this.subscribers = [subscribe('scrollStart', this.handleScrollStart.bind(this), {
useRAF: true
}), subscribe('scroll', this.handleScroll.bind(this), {
useRAF: true,
enableScrollInfo: true
}), subscribe('resize', this.handleResize.bind(this), {
enableResizeInfo: true
})];
// when mount, the scrollTop is not necessary on the top
this.scrollTop = docBody.scrollTop + docEl.scrollTop;
if (this.props.enabled) {
this.setState({
activated: true
});
this.updateInitialDimension();
this.update();
}
}, {
key: "translate",
value: function translate(style, pos) {
var enableTransforms = canEnableTransforms && this.props.enableTransforms;
if (enableTransforms && this.state.activated) {
style[TRANSFORM_PROP] = 'translate3d(0,' + Math.round(pos) + 'px,0)';
} else {
style.top = pos + 'px';
}
// bind the listeners regardless if initially enabled - allows the component to toggle sticky functionality
this.subscribers = [subscribe('scrollStart', this.handleScrollStart.bind(this), {
useRAF: true
}), subscribe('scroll', this.handleScroll.bind(this), {
useRAF: true,
enableScrollInfo: true
}), subscribe('resize', this.handleResize.bind(this), {
enableResizeInfo: true
})];
}
translate(style, pos) {
const enableTransforms = canEnableTransforms && this.props.enableTransforms;
if (enableTransforms && this.state.activated) {
style[TRANSFORM_PROP] = 'translate3d(0,' + Math.round(pos) + 'px,0)';
} else {
style.top = pos + 'px';
}
}, {
key: "shouldComponentUpdate",
value: function shouldComponentUpdate(nextProps, nextState) {
return !this.props.shouldFreeze() && !(shallowEqual(this.props, nextProps) && shallowEqual(this.state, nextState));
}
}, {
key: "render",
value: function render() {
var _this3 = this;
// TODO, "overflow: auto" prevents collapse, need a good way to get children height
var innerStyle = {
position: this.state.status === STATUS_FIXED ? 'fixed' : 'relative',
top: this.state.status === STATUS_FIXED ? '0px' : '',
zIndex: this.props.innerZ
};
var outerStyle = {};
}
shouldComponentUpdate(nextProps, nextState) {
return !this.props.shouldFreeze() && !(shallowEqual(this.props, nextProps) && shallowEqual(this.state, nextState));
}
render() {
// TODO, "overflow: auto" prevents collapse, need a good way to get children height
const innerStyle = {
position: this.state.status === STATUS_FIXED ? 'fixed' : 'relative',
top: this.state.status === STATUS_FIXED ? '0px' : '',
zIndex: this.props.innerZ
};
const outerStyle = {};
// always use translate3d to enhance the performance
this.translate(innerStyle, this.state.pos);
if (this.state.status !== STATUS_ORIGINAL) {
innerStyle.width = this.state.width + 'px';
outerStyle.height = this.state.height + 'px';
}
var outerClasses = classNames('sticky-outer-wrapper', this.props.className, _defineProperty(_defineProperty({}, this.props.activeClass, this.state.status === STATUS_FIXED), this.props.releasedClass, this.state.status === STATUS_RELEASED));
var innerClasses = classNames('sticky-inner-wrapper', this.props.innerClass, _defineProperty({}, this.props.innerActiveClass, this.state.status === STATUS_FIXED));
var children = this.props.children;
return /*#__PURE__*/React.createElement("div", {
ref: function ref(outer) {
_this3.outerElement = outer;
},
className: outerClasses,
style: outerStyle
}, /*#__PURE__*/React.createElement("div", {
ref: function ref(inner) {
_this3.innerElement = inner;
},
className: innerClasses,
style: innerStyle
}, typeof children === 'function' ? children({
status: this.state.status
}) : children));
// always use translate3d to enhance the performance
this.translate(innerStyle, this.state.pos);
if (this.state.status !== STATUS_ORIGINAL) {
innerStyle.width = this.state.width + 'px';
outerStyle.height = this.state.height + 'px';
}
}]);
}(Component);
const outerClasses = classNames('sticky-outer-wrapper', this.props.className, {
[this.props.activeClass]: this.state.status === STATUS_FIXED,
[this.props.releasedClass]: this.state.status === STATUS_RELEASED
});
const innerClasses = classNames('sticky-inner-wrapper', this.props.innerClass, {
[this.props.innerActiveClass]: this.state.status === STATUS_FIXED
});
const children = this.props.children;
return /*#__PURE__*/React.createElement("div", {
ref: outer => {
this.outerElement = outer;
},
className: outerClasses,
style: outerStyle
}, /*#__PURE__*/React.createElement("div", {
ref: inner => {
this.innerElement = inner;
},
className: innerClasses,
style: innerStyle
}, typeof children === 'function' ? children({
status: this.state.status
}) : children));
}
}
Sticky.displayName = 'Sticky';
Sticky.defaultProps = {
shouldFreeze: function shouldFreeze() {
shouldFreeze: function () {
return false;

@@ -477,0 +412,0 @@ },

{
"name": "react-stickynode",
"version": "4.2.0",
"version": "5.0.0",
"description": "A performant and comprehensive React sticky component",

@@ -50,3 +50,2 @@ "main": "./dist/cjs/Sticky.js",

"classnames": "^2.0.0",
"core-js": "^3.6.5",
"prop-types": "^15.6.0",

@@ -74,2 +73,3 @@ "shallowequal": "^1.0.0",

"chromedriver": "^131.0.2",
"core-js": "^3.39.0",
"eslint": "^9.0.0",

@@ -101,8 +101,3 @@ "eslint-plugin-react": "^7.37.1",

"license": "BSD-3-Clause",
"browserslist": [
"last 2 versions",
"ie >= 11",
"iOS >= 12",
"Android >= 11"
],
"browserslist": "> 0.25%, not dead",
"prettier": {

@@ -109,0 +104,0 @@ "singleQuote": true,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc