UNPKG

remeasure

Version:

Get position and size of the DOM element for any React Component

511 lines (411 loc) 18.2 kB
"use strict"; 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;