resize-sensor--react
Version:
Resize Sensor for React
260 lines (217 loc) • 11.9 kB
JavaScript
'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);
require('./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; } /**
* @license MIT
* @copyright Kirill Shestakov 2017
* @see https://github.com/guitarino/resize-sensor--react/
* ----
* Rework of https://github.com/procurios/ResizeSensor
*/
var
// this is for ie9
supportsAttachEvent,
// animation start events with varied prefixes
animStart = ['webkitAnimationStart', 'animationstart', 'oAnimationStart', 'MSAnimationStart'];
try {
supportsAttachEvent = 'attachEvent' in document;
} catch (probablyDoingSSR) {
supportsAttachEvent = false;
}
// essentially, this is the idea:
//
// we have contraction and expansion triggers,
// each of them have children
//
// for contraction:
// the child is 2x bigger than container,
// and it's always scrolled to the bottom right,
// so, when contracted, the bottom right scroll
// position changes, and the 'scroll' event gets called
//
// for expansion:
// the child is slightly bigger than container,
// and it's always scrolled to the bottom right,
// so, when the container expands, the scrollbar
// disappears and changes the child's scroll position
//
var ResizeSensor = function (_React$Component) {
_inherits(ResizeSensor, _React$Component);
function ResizeSensor() {
_classCallCheck(this, ResizeSensor);
// when invisible, <ResizeSensor/> size is 0x0
var _this = _possibleConstructorReturn(this, (ResizeSensor.__proto__ || Object.getPrototypeOf(ResizeSensor)).call(this));
_this.dimensions = {
width: 0,
height: 0
};
// binding (needed for requestAnimationFrame callback)
_this.onElementResize = _this.onElementResize.bind(_this);
return _this;
}
// as you can see, there's triggers that "listen" to expansion
// and triggers that "listen" to contraction
_createClass(ResizeSensor, [{
key: 'render',
value: function render() {
var _this2 = this;
return _react2.default.createElement(
'div',
{ className: 'resize-sensor-react', ref: function ref(e) {
_this2.self = e;
} },
_react2.default.createElement(
'div',
{ className: 'resize-sensor-react__expand', ref: function ref(e) {
_this2.expand = e;
} },
_react2.default.createElement('div', { className: 'resize-sensor-react__expand-child', ref: function ref(e) {
_this2.expandChild = e;
} })
),
_react2.default.createElement(
'div',
{ className: 'resize-sensor-react__contract', ref: function ref(e) {
_this2.contract = e;
} },
_react2.default.createElement('div', { className: 'resize-sensor-react__contract-child' })
)
);
}
// never update element, just render once
}, {
key: 'shouldComponentUpdate',
value: function shouldComponentUpdate() {
return false;
}
// overriding onResize if props are updated
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(props) {
this.setOnResize(props);
}
// when component is mounted, we just need to attach handlers
// scroll - needed for detecting resize
// animation start - needed detecting visibility (we need to
// trigger initial update once the element becomes visible
// because the size might have changed)
//
// Note: using addEventListener's ability to trigger `handleEvent`
// so that we don't have to deal with binding
}, {
key: 'componentDidMount',
value: function componentDidMount() {
this.setOnResize(this.props);
// ie9 only
if (supportsAttachEvent) {
this.self.attachEvent('onresize', this.onElementResize);
}
// other browsers
else {
this.self.addEventListener('scroll', this, true);
for (var i = 0; i < animStart.length; i++) {
this.self.addEventListener(animStart[i], this);
}
// Initial value reset of all triggers
this.resetTriggers();
}
}
// When element is unmounted, need to remove all
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
// ie9 only
if (supportsAttachEvent) {
this.self.detachEvent('onresize', this.onElementResize);
}
// other browsers
else {
for (var i = 0; i < animStart.length; i++) {
this.self.removeEventListener(animStart[i], this);
}
this.self.removeEventListener('scroll', this, true);
}
}
// if there's no 'onResize' prop, then we'll fall back
// to this onResize, which will do nothing
}, {
key: 'onResize',
value: function onResize() {}
}, {
key: 'setOnResize',
value: function setOnResize(props) {
if ('onResize' in props) {
this.onResize = props.onResize;
}
}
// using addEventListener's handleEvent ability
// so that we don't have to deal with binding
}, {
key: 'handleEvent',
value: function handleEvent(e) {
// on scroll, debounce-ish
if (e.type === 'scroll') {
this.resetTriggers();
if (this.resizeRAF) {
window.cancelAnimationFrame(this.resizeRAF);
}
this.resizeRAF = window.requestAnimationFrame(this.onElementResize);
}
// when element becomes visible, reset the trigger sizes;
// the scroll will be triggered if sizes changed
else {
if (e.animationName === 'resize-sensor-react-animation') {
this.resetTriggers();
}
}
}
// check if actually resized, call the callback
}, {
key: 'onElementResize',
value: function onElementResize() {
var currentDimensions = this.getDimensions();
if (this.isResized(currentDimensions)) {
this.dimensions.width = currentDimensions.width;
this.dimensions.height = currentDimensions.height;
this.onResize(this.dimensions.width, this.dimensions.height);
}
}
// just checking if either dimension changed
}, {
key: 'isResized',
value: function isResized(currentDimensions) {
return currentDimensions.width !== this.dimensions.width || currentDimensions.height !== this.dimensions.height;
}
// returning current dimensions of the resize sensor
}, {
key: 'getDimensions',
value: function getDimensions() {
return {
width: this.self.offsetWidth,
height: this.self.offsetHeight
};
}
// this implements the idea behind resize sensor
}, {
key: 'resetTriggers',
value: function resetTriggers() {
this.contract.scrollLeft = this.contract.scrollWidth;
this.contract.scrollTop = this.contract.scrollHeight;
this.expandChild.style.width = this.expand.offsetWidth + 1 + 'px';
this.expandChild.style.height = this.expand.offsetHeight + 1 + 'px';
this.expand.scrollLeft = this.expand.scrollWidth;
this.expand.scrollTop = this.expand.scrollHeight;
}
}]);
return ResizeSensor;
}(_react2.default.Component);
exports.default = ResizeSensor;
//# sourceMappingURL=resize-sensor.js.map