remeasure
Version:
Get position and size of the DOM element for any React Component
511 lines (411 loc) • 18.2 kB
JavaScript
;
exports.__esModule = true;
exports.default = exports.createSetResizeObserver = exports.createSetRenderMethod = exports.createSetRef = exports.createGetPassedValues = exports.createDisconnectObserver = exports.createConnectObserver = exports.createComponentWillUnmount = exports.createComponentDidUpdate = exports.createSetValues = exports.createComponentWillReceiveProps = exports.createComponentDidMount = exports.createComponentWillMount = exports.getInitialState = void 0;
var _debounce = _interopRequireDefault(require("debounce"));
var _fastEquals = require("fast-equals");
var _microMemoize = _interopRequireDefault(require("micro-memoize"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _react = _interopRequireWildcard(require("react"));
var _reactDom = require("react-dom");
var _raf = _interopRequireDefault(require("raf"));
var _resizeObserverPolyfill = _interopRequireDefault(require("resize-observer-polyfill"));
var _constants = require("./constants");
var _utils = require("./utils");
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _extends() { _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; }; return _extends.apply(this, arguments); }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/**
* @private
*
* @function getInitialState
*
* @description
* get the initial state of the component instance
*
* @returns {Object} the initial state
*/
var getInitialState = function getInitialState() {
return _constants.KEY_NAMES.reduce(function (state, key) {
state[key] = null;
return state;
}, {});
};
exports.getInitialState = getInitialState;
var createComponentWillMount = function createComponentWillMount(instance) {
return (
/**
* @private
*
* @function componentWillMount
*
* @description
* prior to mount, set the keys to watch for and the render method
*/
function () {
instance.keys = (0, _utils.getStateKeys)(instance.props);
instance.setRenderMethod(instance.props);
}
);
};
exports.createComponentWillMount = createComponentWillMount;
var createComponentDidMount = function createComponentDidMount(instance) {
return (
/**
* @private
*
* @function componentDidMount
*
* @description
* on mount, get the element and set it's resize observer
*/
function () {
instance._isMounted = true;
instance.element = (0, _reactDom.findDOMNode)(instance);
instance.setResizeObserver();
}
);
};
exports.createComponentDidMount = createComponentDidMount;
var createComponentWillReceiveProps = function createComponentWillReceiveProps(instance) {
return (
/**
* @private
*
* @function componentWillReceiveProps
*
* @description
* when the component receives new props, set the render method for future renders
*
* @param {Object} nextProps the next render's props
*/
function (nextProps) {
return instance.setRenderMethod(nextProps);
}
);
};
exports.createComponentWillReceiveProps = createComponentWillReceiveProps;
var createSetValues = function createSetValues(instance, isDebounce) {
var debounce = instance.props.debounce;
/**
* @private
*
* @function delayedMethod
*
* @description
* on a delay (either requestAnimationFrame or debounce), determine the calculated measurements and assign
* them to state if changed
*/
var delayedMethod = function delayedMethod(event) {
var clientRect = instance.element ? instance.element.getBoundingClientRect() : {};
var isResize = event && event.type === 'resize';
var newValues = _constants.KEYS.reduce(function (values, key) {
values[key.key] = ~instance.keys.indexOf(key) ? instance.element ? (0, _utils.getNaturalDimensionValue)(key.source === _constants.SOURCES.CLIENT_RECT ? clientRect : instance.element, key.key) : 0 : null;
return values;
}, {});
if (!instance._isMounted) {
return;
}
if (isResize || !(0, _fastEquals.deepEqual)(instance.state, newValues)) {
instance.setState(function () {
return newValues;
});
}
};
return isDebounce && typeof debounce === 'number' ? (0, _debounce.default)(delayedMethod, debounce) : function (event) {
return (0, _raf.default)(function () {
return delayedMethod(event);
});
};
};
exports.createSetValues = createSetValues;
var createComponentDidUpdate = function createComponentDidUpdate(instance) {
return (
/**
* @private
*
* @function componentDidUpdate
*
* @description
* on update, assign the new properties if they have changed
* * element
* * debounce (assign new debounced render method)
* * keys
*
* @param {number} [previousDebounce] the previous props' debounce value
*/
function (_ref) {
var previousDebounce = _ref.debounce;
var debounce = instance.props.debounce;
var element = (0, _reactDom.findDOMNode)(instance);
var hasElementChanged = element !== instance.element;
var hasDebounceChanged = debounce !== previousDebounce;
var shouldSetResizeObserver = hasElementChanged || hasDebounceChanged;
if (hasElementChanged) {
instance.element = element;
}
if (hasDebounceChanged) {
instance.setValuesViaDebounce = createSetValues(instance, true);
}
if (shouldSetResizeObserver) {
instance.setResizeObserver();
}
var newKeys = (0, _utils.getStateKeys)(instance.props);
if (shouldSetResizeObserver || !(0, _fastEquals.deepEqual)(instance.keys, newKeys)) {
instance.keys = newKeys;
instance.resizeMethod();
}
}
);
};
exports.createComponentDidUpdate = createComponentDidUpdate;
var createComponentWillUnmount = function createComponentWillUnmount(instance) {
return (
/**
* @private
*
* @function componentWillUnmount
*
* @description
* prior to unmount, disconnect the resize observer and reset the instance properties
*/
function () {
instance._isMounted = false;
instance.disconnectObserver();
instance.element = null;
instance.keys = [];
instance.resizeMethod = null;
}
);
};
exports.createComponentWillUnmount = createComponentWillUnmount;
var createConnectObserver = function createConnectObserver(instance) {
return (
/**
* @private
*
* @function connectObserver
*
* @description
* if render on resize is requested, assign a resize observer to the element with the correct resize method
*/
function () {
var _instance$props = instance.props,
renderOnResize = _instance$props.renderOnResize,
renderOnWindowResize = _instance$props.renderOnWindowResize;
if (renderOnWindowResize) {
window.addEventListener('resize', instance.resizeMethod);
}
if (renderOnResize) {
if (!_constants.IS_PRODUCTION && (0, _utils.isElementVoidTag)(instance.element)) {
/* eslint-disable no-console */
console.warn('WARNING: You are attempting to listen to resizes on a void element, which is not supported. You should wrap this element in an element that supports children, such as a <div>, to ensure correct behavior.');
/* eslint-enable */
}
instance.resizeObserver = new _resizeObserverPolyfill.default(instance.resizeMethod);
instance.resizeObserver.observe(instance.element);
}
}
);
};
exports.createConnectObserver = createConnectObserver;
var createDisconnectObserver = function createDisconnectObserver(instance) {
return (
/**
* @private
*
* @function disconnectObserver
*
* @description
* if a resize observer exists, disconnect it from the element
*/
function () {
if (instance.resizeObserver) {
instance.resizeObserver.disconnect(instance.element);
instance.resizeObserver = null;
}
window.removeEventListener('resize', instance.resizeMethod);
}
);
};
exports.createDisconnectObserver = createDisconnectObserver;
var createGetPassedValues = function createGetPassedValues(instance) {
return (
/**
* @private
*
* @function getPassedValues
*
* @description
* get the passed values as an object, namespaced if requested
*
* @param {Object} state the current state values
* @param {string} [namespace] the possible namespace to assign the values to
* @returns {Object} the values to pass
*/
(0, _microMemoize.default)(function (state, namespace) {
var _ref3;
var populatedState = instance.keys.reduce(function (values, _ref2) {
var key = _ref2.key;
values[key] = state[key] || 0;
return values;
}, {});
return namespace ? (_ref3 = {}, _ref3[namespace] = populatedState, _ref3) : populatedState;
})
);
};
exports.createGetPassedValues = createGetPassedValues;
var createSetRef = function createSetRef(instance, ref) {
return (
/**
* @private
*
* @function setRef
*
* @description
* set the DOM node to the ref passed
*
* @param {HTMLElement|ReactComponent} element the element to find the DOM node of
*/
function (element) {
instance[ref] = (0, _reactDom.findDOMNode)(element);
}
);
};
exports.createSetRef = createSetRef;
var createSetRenderMethod = function createSetRenderMethod(instance) {
return (
/**
* @private
*
* @function setRenderMethod
*
* @description
* set the render method based on the possible props passed
*
* @param {function} [children] the child render function
* @param {function} [component] the component prop function
* @param {function} [render] the render prop function
*/
function (_ref4) {
var children = _ref4.children,
component = _ref4.component,
render = _ref4.render;
var RenderComponent = children || component || render || null;
if (!_constants.IS_PRODUCTION && typeof RenderComponent !== 'function') {
/* eslint-disable no-console */
console.error('ERROR: You must provide a render function, or either a "render" or "component" prop that passes a functional component.');
/* eslint-enable */
}
if (RenderComponent !== instance.RenderComponent) {
instance.RenderComponent = RenderComponent;
}
}
);
};
exports.createSetRenderMethod = createSetRenderMethod;
var createSetResizeObserver = function createSetResizeObserver(instance) {
return (
/**
* @private
*
* @function setResizeObserver
*
* @description
* set the resize observer on the instance, based on the existence of a currrent resizeObserver and the new element
*/
function () {
var debounce = instance.props.debounce;
var resizeMethod = typeof debounce === 'number' ? instance.setValuesViaDebounce : instance.setValuesViaRaf;
if (instance.resizeObserver) {
instance.disconnectObserver();
}
if (resizeMethod !== instance.resizeMethod) {
instance.resizeMethod = resizeMethod;
resizeMethod();
}
if (instance.element) {
instance.connectObserver();
}
}
);
}; // eslint-disable-next-line react/no-deprecated
exports.createSetResizeObserver = createSetResizeObserver;
var Measured =
/*#__PURE__*/
function (_Component) {
_inheritsLoose(Measured, _Component);
function Measured() {
var _this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _Component.call.apply(_Component, [this].concat(args)) || this;
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "state", getInitialState());
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "componentDidMount", createComponentDidMount(_assertThisInitialized(_assertThisInitialized(_this))));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "componentDidUpdate", createComponentDidUpdate(_assertThisInitialized(_assertThisInitialized(_this))));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "componentWillUnmount", createComponentWillUnmount(_assertThisInitialized(_assertThisInitialized(_this))));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), _constants.COMPONENT_WILL_MOUNT, createComponentWillMount(_assertThisInitialized(_assertThisInitialized(_this))));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), _constants.COMPONENT_WILL_RECEIVE_PROPS, createComponentWillReceiveProps(_assertThisInitialized(_assertThisInitialized(_this))));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "_isMounted", false);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "element", null);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "keys", []);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "RenderComponent", null);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "resizeMethod", null);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "resizeObserver", null);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "connectObserver", createConnectObserver(_assertThisInitialized(_assertThisInitialized(_this))));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "disconnectObserver", createDisconnectObserver(_assertThisInitialized(_assertThisInitialized(_this))));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "getPassedValues", createGetPassedValues(_assertThisInitialized(_assertThisInitialized(_this))));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "setElementRef", createSetRef(_assertThisInitialized(_assertThisInitialized(_this)), 'element'));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "setRenderMethod", createSetRenderMethod(_assertThisInitialized(_assertThisInitialized(_this))));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "setResizeObserver", createSetResizeObserver(_assertThisInitialized(_assertThisInitialized(_this))));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "setValuesViaDebounce", createSetValues(_assertThisInitialized(_assertThisInitialized(_this)), true));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "setValuesViaRaf", createSetValues(_assertThisInitialized(_assertThisInitialized(_this)), false));
return _this;
}
var _proto = Measured.prototype;
_proto.render = function render() {
if (!this.RenderComponent) {
return null;
}
var _this$props = this.props,
childrenIgnored = _this$props.children,
componentIgnored = _this$props.component,
debounceIgnored = _this$props.debounce,
keysIgnored = _this$props.keys,
namespace = _this$props.namespace,
renderIgnored = _this$props.render,
renderOnResizeIgnored = _this$props.renderOnResize,
renderOnWindowResizeIgnored = _this$props.renderOnWindowResize,
passThroughProps = _objectWithoutPropertiesLoose(_this$props, ["children", "component", "debounce", "keys", "namespace", "render", "renderOnResize", "renderOnWindowResize"]);
var RenderComponent = this.RenderComponent;
return (
/* eslint-disable prettier */
_react.default.createElement(RenderComponent, _extends({}, passThroughProps, this.getPassedValues(this.state, namespace)))
/* eslint-enable */
);
};
return Measured;
}(_react.Component);
_defineProperty(Measured, "displayName", 'Measured');
_defineProperty(Measured, "propTypes", _extends({
children: _propTypes.default.func,
component: _propTypes.default.func,
debounce: _propTypes.default.number,
namespace: _propTypes.default.string,
render: _propTypes.default.func,
renderOnResize: _propTypes.default.bool.isRequired,
renderOnWindowResize: _propTypes.default.bool.isRequired
}, _constants.KEY_NAMES.reduce(function (keyPropTypes, key) {
keyPropTypes[key] = _propTypes.default.bool;
return keyPropTypes;
}, {})));
_defineProperty(Measured, "defaultProps", {
renderOnResize: true,
renderOnWindowResize: false
});
var _default = Measured;
exports.default = _default;