UNPKG

react-hold

Version:

Hold the empty presentational components in react.js

349 lines (292 loc) 12.6 kB
'use strict'; exports.__esModule = 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; }; }(); exports.default = function (targetComponent, condition) { var defaultHolder = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _Fill2.default; var holderDefaultProps = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; if (!(0, _utils.isFunction)(targetComponent) && typeof targetComponent !== 'string') { throw new TypeError('Expected the target component to be a string or class/function.'); } if (!(0, _utils.isFunction)(condition)) { throw new TypeError('Expected the hold condition to be a function.'); } if ((0, _utils.isObject)(defaultHolder)) { holderDefaultProps = defaultHolder; defaultHolder = _Fill2.default; } var targetComponentName = (0, _utils.getDisplayName)(targetComponent); var refiter = (0, _createRefiter2.default)(targetComponent); var Hold = function (_Component) { _inherits(Hold, _Component); function Hold() { var _ref; _classCallCheck(this, Hold); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var _this = _possibleConstructorReturn(this, (_ref = Hold.__proto__ || Object.getPrototypeOf(Hold)).call.apply(_ref, [this].concat(args))); _this.state = { hold: true, copy: true, holderAutoSize: { width: null, height: null } // The style of original node };_this.originNodeStyle = null; // window resize handler _this.resizeHandler = function () { if (_this.state.hold) { _this.updateHolderSizeIfNecessary(); } }; _this.cancelHold = _this.cancelHold.bind(_this); return _this; } _createClass(Hold, [{ key: 'componentWillMount', value: function componentWillMount() { if (condition.call(null, this.props, {})) { refiter.refit(); } else { this.cancelHold(); } } }, { key: 'componentDidMount', value: function componentDidMount() { if (this.state.hold) { this.originNodeStyle = this.computeOriginNodeStyle(); this.setState({ copy: false }); } (0, _utils.addHandler)(window, 'resize', this.resizeHandler); } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { if (condition.call(null, nextProps, this.props)) { this.setState({ hold: true, copy: true }); } else { this.cancelHold(); } } }, { key: 'componentDidUpdate', value: function componentDidUpdate() { if (this.state.hold) { if (this.state.copy) { refiter.refit(); this.originNodeStyle = this.computeOriginNodeStyle(); this.setState({ copy: false }); } else if (!(0, _utils.isNull)(this.originNodeStyle)) { this.setFakeNodeStyle(this.originNodeStyle); this.updateHolderSizeIfNecessary(); this.originNodeStyle = null; } } } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { (0, _utils.removeHandler)(window, 'resize', this.resizeHandler); } }, { key: 'setFakeNodeStyle', value: function setFakeNodeStyle(style) { if (!(0, _utils.isObject)(style)) return; var _refs = this.refs, fake = _refs.fake, env = _refs.env; var isInline = style.display && style.display.indexOf('inline') > -1; // hidden element fake.style.display = 'none'; // set style Object.keys(style).forEach(function (name) { if (name !== 'display') { fake.style[name] = style[name]; } }); // fix fake style fake.style.opacity = 1; fake.style.background = 'transparent'; fake.style.borderColor = 'transparent'; // fix env style if (isInline) { env.style.overflow = 'hidden'; } else { env.style.overflow = 'visible'; } // display fake fake.style.display = isInline ? 'inline-block' : style.display; } }, { key: 'computeOriginNodeStyle', value: function computeOriginNodeStyle() { var result = null; var originNode = (0, _reactDom.findDOMNode)(this); // store original display property var computedStyle = (0, _utils.getComputedStyle)(originNode, null); var originDisplay = computedStyle.getPropertyValue('display'); // set display to 'none' before recompute is very **important**, // don't remove or move this step! originNode.style.display = 'none'; // compute node style computedStyle = (0, _utils.getComputedStyle)(originNode, null); // copy style Object.keys(computedStyle).forEach(function (key) { if (/[0-9]+/.test(key)) { var name = computedStyle[key]; result = result || {}; if (name === 'display') { result[name] = originDisplay; } else { result[name] = computedStyle.getPropertyValue(name); } } }); return result; } }, { key: 'cancelHold', value: function cancelHold() { var _refs2 = this.refs, fake = _refs2.fake, env = _refs2.env; // manual restore fake and env node style, // because their style had been modified by method 'setFakeNodeStyle' if (fake) fake.removeAttribute('style'); if (env) env.style.overflow = 'visible'; // restore component lifecycle methods refiter.undo(); // clear origin node style this.originNodeStyle = null; // exit hold state this.setState({ hold: false, copy: false }); } }, { key: 'updateHolderSizeIfNecessary', value: function updateHolderSizeIfNecessary() { var env = this.refs.env; if (!env) return; var holderAutoSize = this.state.holderAutoSize; var holderProps = this.props.holderProps || this.props.props || {}; var customWidth = (0, _utils.isNull)(holderDefaultProps.width) ? holderProps.width : holderDefaultProps.width; var customHeight = (0, _utils.isNull)(holderDefaultProps.height) ? holderProps.height : holderDefaultProps.height; if (!(0, _utils.isNull)(customWidth) && !(0, _utils.isNull)(customHeight)) return; var size = (0, _utils.getNodeSize)(env); var width = (0, _utils.isNull)(customWidth) ? size.width : null; var height = (0, _utils.isNull)(customHeight) ? size.height : null; if (holderAutoSize.width !== width || holderAutoSize.height !== height) { this.setState({ holderAutoSize: { width: width, height: height } }); } } }, { key: 'render', value: function render() { var _state = this.state, hold = _state.hold, copy = _state.copy, holderAutoSize = _state.holderAutoSize; var _props = this.props, innerRef = _props.innerRef, holder = _props.holder, holderProps = _props.holderProps, props = _props.props, propsForElement = _objectWithoutProperties(_props, ['innerRef', 'holder', 'holderProps', 'props']); if (!hold || copy) { if (innerRef && !hold) propsForElement.ref = innerRef; return _react2.default.createElement(targetComponent, propsForElement); } var propsForHolder = _extends({ color: '#eee', width: null, height: null }, holderDefaultProps, props, holderProps, { cancelHold: this.cancelHold, targetProps: propsForElement }); (0, _utils.isNull)(propsForHolder.width) && (propsForHolder.width = holderAutoSize.width); (0, _utils.isNull)(propsForHolder.height) && (propsForHolder.height = holderAutoSize.height); if (typeof propsForHolder.children === 'string') { propsForHolder.children = propsForHolder.children.replace(/ /g, $nbsp); } propsForHolder.children = propsForHolder.children || $nbsp.repeat(blankLength); return _react2.default.createElement( 'div', { ref: 'fake' }, _react2.default.createElement( 'div', { ref: 'env', style: envDefaultStyle }, _react2.default.createElement(holder, propsForHolder) ) ); } }]); return Hold; }(_react.Component); (0, _hoistNonReactStatics2.default)(Hold, targetComponent); Hold.displayName = 'Hold(' + targetComponentName + ')'; Hold.propTypes = { holder: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.func]), holderProps: _propTypes2.default.object, props: _propTypes2.default.object, // The alias of 'holderProps' innerRef: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.func]) }; Hold.defaultProps = { holder: defaultHolder, holderProps: null, props: null, innerRef: null }; return Hold; }; var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _reactDom = require('react-dom'); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _hoistNonReactStatics = require('hoist-non-react-statics'); var _hoistNonReactStatics2 = _interopRequireDefault(_hoistNonReactStatics); var _utils = require('./utils'); var _Fill = require('./holders/Fill'); var _Fill2 = _interopRequireDefault(_Fill); var _createRefiter = require('./createRefiter'); var _createRefiter2 = _interopRequireDefault(_createRefiter); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } 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 $nbsp = '\xA0'; var blankLength = 10; var envDefaultStyle = { position: 'relative', padding: '0px', margin: '0px', width: '100%', height: '100%', border: 'none', overflow: 'visible' /** * Create a higher-order component to manage * original component and placeholder component. * * @param {Component|String} targetComponent * @param {Function} condition * @param {Component} defaultHolder * @param {Object} holderDefaultProps * @returns {Component} */ };