Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task #11059: Fork react-sticky #1

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions lib/Container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});

var _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; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _propTypes = require('prop-types');

var _propTypes2 = _interopRequireDefault(_propTypes);

var _raf = require('raf');

var _raf2 = _interopRequireDefault(_raf);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var Container = function (_PureComponent) {
_inherits(Container, _PureComponent);

function Container() {
var _ref;

var _temp, _this, _ret;

_classCallCheck(this, Container);

for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}

return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Container.__proto__ || Object.getPrototypeOf(Container)).call.apply(_ref, [this].concat(args))), _this), _this.events = ['resize', 'scroll', 'touchstart', 'touchmove', 'touchend', 'pageshow', 'load'], _this.subscribers = [], _this.subscribe = function (handler) {
_this.subscribers = _this.subscribers.concat(handler);
}, _this.unsubscribe = function (handler) {
_this.subscribers = _this.subscribers.filter(function (current) {
return current !== handler;
});
}, _this.notifySubscribers = function (evt) {
_this.doNotifySubscribers(evt.currentTarget);
}, _this.onScroll = function (evt) {
_this.props.onScroll(evt);
_this.notifySubscribers(evt);
}, _this.getParent = function () {
return _this.node;
}, _temp), _possibleConstructorReturn(_this, _ret);
}

_createClass(Container, [{
key: 'getChildContext',
value: function getChildContext() {
return {
subscribe: this.subscribe,
unsubscribe: this.unsubscribe,
getParent: this.getParent
};
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
var _this2 = this;

this.events.forEach(function (event) {
return window.addEventListener(event, _this2.notifySubscribers);
});
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
var _this3 = this;

this.events.forEach(function (event) {
return window.removeEventListener(event, _this3.notifySubscribers);
});
}

/**
* Notifies the current node of a layout update.
*/

}, {
key: 'notifyLayoutUpdate',
value: function notifyLayoutUpdate() {
this.doNotifySubscribers(this.node);
}
}, {
key: 'render',
value: function render() {
var _this4 = this;

return _react2.default.createElement('div', _extends({}, this.props, {
ref: function ref(node) {
return _this4.node = node;
},
onScroll: this.onScroll,
onTouchStart: this.notifySubscribers,
onTouchMove: this.notifySubscribers,
onTouchEnd: this.notifySubscribers
}));
}

/**
* Notifies the subscribers of this container.
*
* @param {HTMLElement} updateSource The source of the update.
*/

}, {
key: 'doNotifySubscribers',
value: function doNotifySubscribers(updateSource) {
var _this5 = this;

if (!this.framePending) {

(0, _raf2.default)(function () {
_this5.framePending = false;

var _node$getBoundingClie = _this5.node.getBoundingClientRect(),
top = _node$getBoundingClie.top,
bottom = _node$getBoundingClie.bottom;

_this5.subscribers.forEach(function (handler) {
return handler({
distanceFromTop: top,
distanceFromBottom: bottom,
eventSource: updateSource === window ? document.body : _this5.node
});
});
});
this.framePending = true;
}
}
}]);

return Container;
}(_react.PureComponent);

Container.childContextTypes = {
subscribe: _propTypes2.default.func,
unsubscribe: _propTypes2.default.func,
getParent: _propTypes2.default.func
};
exports.default = Container;
165 changes: 165 additions & 0 deletions lib/Sticky.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _reactDom = require('react-dom');

var _reactDom2 = _interopRequireDefault(_reactDom);

var _propTypes = require('prop-types');

var _propTypes2 = _interopRequireDefault(_propTypes);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var Sticky = function (_Component) {
_inherits(Sticky, _Component);

function Sticky() {
var _ref;

var _temp, _this, _ret;

_classCallCheck(this, Sticky);

for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}

return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Sticky.__proto__ || Object.getPrototypeOf(Sticky)).call.apply(_ref, [this].concat(args))), _this), _this.state = {
isSticky: false,
wasSticky: false,
style: {}
}, _this.handleContainerEvent = function (_ref2) {
var distanceFromTop = _ref2.distanceFromTop,
distanceFromBottom = _ref2.distanceFromBottom,
eventSource = _ref2.eventSource;

var parent = _this.context.getParent();

var preventingStickyStateChanges = false;

if (_this.props.relative) {
preventingStickyStateChanges = eventSource !== parent;
var accumulatedDistanceFromTop = 0;

var currentOffsetParent = _this.placeholder;

while (currentOffsetParent && currentOffsetParent !== parent) {
accumulatedDistanceFromTop += currentOffsetParent.offsetTop;
currentOffsetParent = currentOffsetParent.offsetParent;
}

distanceFromTop = accumulatedDistanceFromTop - parent.scrollTop;
}

var placeholderClientRect = _this.placeholder.getBoundingClientRect();
var contentClientRect = _this.content.getBoundingClientRect();
var calculatedHeight = contentClientRect.height;

var bottomDifference = distanceFromBottom - _this.props.bottomOffset - calculatedHeight;

var wasSticky = !!_this.state.isSticky;
var isSticky = preventingStickyStateChanges ? wasSticky : distanceFromTop <= -_this.props.topOffset && distanceFromBottom > -_this.props.bottomOffset;

distanceFromBottom = (_this.props.relative ? parent.scrollHeight - parent.scrollTop : distanceFromBottom) - calculatedHeight;

var style = !isSticky ? {} : {
position: 'fixed',
top: bottomDifference > 0 ? _this.props.relative ? parent.getBoundingClientRect().top - parent.offsetParent.scrollTop : 0 : bottomDifference,
left: placeholderClientRect.left,
width: placeholderClientRect.width
};

_this.setState({
isSticky: isSticky,
wasSticky: wasSticky,
distanceFromTop: distanceFromTop,
distanceFromBottom: distanceFromBottom,
calculatedHeight: calculatedHeight,
style: style
});
}, _temp), _possibleConstructorReturn(_this, _ret);
}

_createClass(Sticky, [{
key: 'componentWillMount',
value: function componentWillMount() {
if (!this.context.subscribe) throw new TypeError("Expected Sticky to be mounted within StickyContainer");

this.context.subscribe(this.handleContainerEvent);
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
this.context.unsubscribe(this.handleContainerEvent);
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate() {
this.placeholder.style.paddingBottom = this.props.disableCompensation ? 0 : (this.state.isSticky ? this.state.calculatedHeight : 0) + 'px';
}
}, {
key: 'render',
value: function render() {
var _this2 = this;

var element = _react2.default.cloneElement(this.props.children({
isSticky: this.state.isSticky,
wasSticky: this.state.wasSticky,
distanceFromTop: this.state.distanceFromTop,
distanceFromBottom: this.state.distanceFromBottom,
calculatedHeight: this.state.calculatedHeight,
style: this.state.style
}), { ref: function ref(content) {
_this2.content = _reactDom2.default.findDOMNode(content);
} });

return _react2.default.createElement(
'div',
null,
_react2.default.createElement('div', { ref: function ref(placeholder) {
return _this2.placeholder = placeholder;
}, onScroll: this.props.onScroll }),
element
);
}
}]);

return Sticky;
}(_react.Component);

Sticky.propTypes = {
topOffset: _propTypes2.default.number,
bottomOffset: _propTypes2.default.number,
relative: _propTypes2.default.bool,
children: _propTypes2.default.func.isRequired,
onScroll: _propTypes2.default.func
};
Sticky.defaultProps = {
relative: false,
topOffset: 0,
bottomOffset: 0,
disableCompensation: false,
disableHardwareAcceleration: false
};
Sticky.contextTypes = {
subscribe: _propTypes2.default.func,
unsubscribe: _propTypes2.default.func,
getParent: _propTypes2.default.func
};
exports.default = Sticky;
20 changes: 20 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.StickyContainer = exports.Sticky = undefined;

var _Sticky = require('./Sticky');

var _Sticky2 = _interopRequireDefault(_Sticky);

var _Container = require('./Container');

var _Container2 = _interopRequireDefault(_Container);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

exports.Sticky = _Sticky2.default;
exports.StickyContainer = _Container2.default;
exports.default = _Sticky2.default;
Loading