bloom-layout
Version:
layout components used in bloom packages
557 lines (462 loc) • 17.1 kB
JavaScript
exports.__esModule = true;
exports.EXITING = exports.ENTERED = exports.ENTERING = exports.EXITED = exports.UNMOUNTED = undefined;
var _propTypes = require('prop-types');
var PropTypes = _interopRequireWildcard(_propTypes);
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _reactDom = require('react-dom');
var _reactDom2 = _interopRequireDefault(_reactDom);
var _PropTypes = require('./utils/PropTypes');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
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)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
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 UNMOUNTED = exports.UNMOUNTED = 'unmounted';
var EXITED = exports.EXITED = 'exited';
var ENTERING = exports.ENTERING = 'entering';
var ENTERED = exports.ENTERED = 'entered';
var EXITING = exports.EXITING = 'exiting';
/**
* The Transition component lets you describe a transition from one component
* state to another _over time_ with a simple declarative API. Most commonly
* it's used to animate the mounting and unmounting of a component, but can also
* be used to describe in-place transition states as well.
*
* By default the `Transition` component does not alter the behavior of the
* component it renders, it only tracks "enter" and "exit" states for the components.
* It's up to you to give meaning and effect to those states. For example we can
* add styles to a component when it enters or exits:
*
* ```jsx
* import Transition from 'react-transition-group/Transition';
*
* const duration = 300;
*
* const defaultStyle = {
* transition: `opacity ${duration}ms ease-in-out`,
* opacity: 0,
* }
*
* const transitionStyles = {
* entering: { opacity: 0 },
* entered: { opacity: 1 },
* };
*
* const Fade = ({ in: inProp }) => (
* <Transition in={inProp} timeout={duration}>
* {(state) => (
* <div style={{
* ...defaultStyle,
* ...transitionStyles[state]
* }}>
* I'm A fade Transition!
* </div>
* )}
* </Transition>
* );
* ```
*
* As noted the `Transition` component doesn't _do_ anything by itself to its child component.
* What it does do is track transition states over time so you can update the
* component (such as by adding styles or classes) when it changes states.
*
* There are 4 main states a Transition can be in:
* - `ENTERING`
* - `ENTERED`
* - `EXITING`
* - `EXITED`
*
* Transition state is toggled via the `in` prop. When `true` the component begins the
* "Enter" stage. During this stage, the component will shift from its current transition state,
* to `'entering'` for the duration of the transition and then to the `'entered'` stage once
* it's complete. Let's take the following example:
*
* ```jsx
* state= { in: false };
*
* toggleEnterState = () => {
* this.setState({ in: true });
* }
*
* render() {
* return (
* <div>
* <Transition in={this.state.in} timeout={500} />
* <button onClick={this.toggleEnterState}>Click to Enter</button>
* </div>
* );
* }
* ```
*
* When the button is clicked the component will shift to the `'entering'` state and
* stay there for 500ms (the value of `timeout`) when finally switches to `'entered'`.
*
* When `in` is `false` the same thing happens except the state moves from `'exiting'` to `'exited'`.
*/
var Transition = function (_React$Component) {
_inherits(Transition, _React$Component);
function Transition(props, context) {
_classCallCheck(this, Transition);
var _this = _possibleConstructorReturn(this, _React$Component.call(this, props, context));
var parentGroup = context.transitionGroup;
// In the context of a TransitionGroup all enters are really appears
var appear = parentGroup && !parentGroup.isMounting ? props.enter : props.appear;
var initialStatus = void 0;
_this.nextStatus = null;
if (props.in) {
if (appear) {
initialStatus = EXITED;
_this.nextStatus = ENTERING;
} else {
initialStatus = ENTERED;
}
} else {
if (props.unmountOnExit || props.mountOnEnter) {
initialStatus = UNMOUNTED;
} else {
initialStatus = EXITED;
}
}
_this.state = { status: initialStatus };
_this.nextCallback = null;
return _this;
}
Transition.prototype.getChildContext = function getChildContext() {
return { transitionGroup: null }; // allows for nested Transitions
};
Transition.prototype.componentDidMount = function componentDidMount() {
this.updateStatus(true);
};
Transition.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
var _ref = this.pendingState || this.state,
status = _ref.status;
if (nextProps.in) {
if (status === UNMOUNTED) {
this.setState({ status: EXITED });
}
if (status !== ENTERING && status !== ENTERED) {
this.nextStatus = ENTERING;
}
} else {
if (status === ENTERING || status === ENTERED) {
this.nextStatus = EXITING;
}
}
};
Transition.prototype.componentDidUpdate = function componentDidUpdate() {
this.updateStatus();
};
Transition.prototype.componentWillUnmount = function componentWillUnmount() {
this.cancelNextCallback();
};
Transition.prototype.getTimeouts = function getTimeouts() {
var timeout = this.props.timeout;
var exit = void 0,
enter = void 0,
appear = void 0;
exit = enter = appear = timeout;
if (timeout != null && typeof timeout !== 'number') {
exit = timeout.exit;
enter = timeout.enter;
appear = timeout.appear;
}
return { exit: exit, enter: enter, appear: appear };
};
Transition.prototype.updateStatus = function updateStatus() {
var mounting = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
var nextStatus = this.nextStatus;
if (nextStatus !== null) {
this.nextStatus = null;
// nextStatus will always be ENTERING or EXITING.
this.cancelNextCallback();
var node = _reactDom2.default.findDOMNode(this);
if (nextStatus === ENTERING) {
this.performEnter(node, mounting);
} else {
this.performExit(node);
}
} else if (this.props.unmountOnExit && this.state.status === EXITED) {
this.setState({ status: UNMOUNTED });
}
};
Transition.prototype.performEnter = function performEnter(node, mounting) {
var _this2 = this;
var enter = this.props.enter;
var appearing = this.context.transitionGroup ? this.context.transitionGroup.isMounting : mounting;
var timeouts = this.getTimeouts();
// no enter animation skip right to ENTERED
// if we are mounting and running this it means appear _must_ be set
if (!mounting && !enter) {
this.safeSetState({ status: ENTERED }, function () {
_this2.props.onEntered(node);
});
return;
}
this.props.onEnter(node, appearing);
this.safeSetState({ status: ENTERING }, function () {
_this2.props.onEntering(node, appearing);
// FIXME: appear timeout?
_this2.onTransitionEnd(node, timeouts.enter, function () {
_this2.safeSetState({ status: ENTERED }, function () {
_this2.props.onEntered(node, appearing);
});
});
});
};
Transition.prototype.performExit = function performExit(node) {
var _this3 = this;
var exit = this.props.exit;
var timeouts = this.getTimeouts();
// no exit animation skip right to EXITED
if (!exit) {
this.safeSetState({ status: EXITED }, function () {
_this3.props.onExited(node);
});
return;
}
this.props.onExit(node);
this.safeSetState({ status: EXITING }, function () {
_this3.props.onExiting(node);
_this3.onTransitionEnd(node, timeouts.exit, function () {
_this3.safeSetState({ status: EXITED }, function () {
_this3.props.onExited(node);
});
});
});
};
Transition.prototype.cancelNextCallback = function cancelNextCallback() {
if (this.nextCallback !== null) {
this.nextCallback.cancel();
this.nextCallback = null;
}
};
Transition.prototype.safeSetState = function safeSetState(nextState, callback) {
var _this4 = this;
// We need to track pending updates for instances where a cWRP fires quickly
// after cDM and before the state flushes, which would double trigger a
// transition
this.pendingState = nextState;
// This shouldn't be necessary, but there are weird race conditions with
// setState callbacks and unmounting in testing, so always make sure that
// we can cancel any pending setState callbacks after we unmount.
callback = this.setNextCallback(callback);
this.setState(nextState, function () {
_this4.pendingState = null;
callback();
});
};
Transition.prototype.setNextCallback = function setNextCallback(callback) {
var _this5 = this;
var active = true;
this.nextCallback = function (event) {
if (active) {
active = false;
_this5.nextCallback = null;
callback(event);
}
};
this.nextCallback.cancel = function () {
active = false;
};
return this.nextCallback;
};
Transition.prototype.onTransitionEnd = function onTransitionEnd(node, timeout, handler) {
this.setNextCallback(handler);
if (node) {
if (this.props.addEndListener) {
this.props.addEndListener(node, this.nextCallback);
}
if (timeout != null) {
setTimeout(this.nextCallback, timeout);
}
} else {
setTimeout(this.nextCallback, 0);
}
};
Transition.prototype.render = function render() {
var status = this.state.status;
if (status === UNMOUNTED) {
return null;
}
var _props = this.props,
children = _props.children,
childProps = _objectWithoutProperties(_props, ['children']);
// filter props for Transtition
delete childProps.in;
delete childProps.mountOnEnter;
delete childProps.unmountOnExit;
delete childProps.appear;
delete childProps.enter;
delete childProps.exit;
delete childProps.timeout;
delete childProps.addEndListener;
delete childProps.onEnter;
delete childProps.onEntering;
delete childProps.onEntered;
delete childProps.onExit;
delete childProps.onExiting;
delete childProps.onExited;
if (typeof children === 'function') {
return children(status, childProps);
}
var child = _react2.default.Children.only(children);
return _react2.default.cloneElement(child, childProps);
};
return Transition;
}(_react2.default.Component);
Transition.contextTypes = {
transitionGroup: PropTypes.object
};
Transition.childContextTypes = {
transitionGroup: function transitionGroup() {}
};
Transition.propTypes = process.env.NODE_ENV !== "production" ? {
/**
* A `function` child can be used instead of a React element.
* This function is called with the current transition status
* ('entering', 'entered', 'exiting', 'exited', 'unmounted'), which can used
* to apply context specific props to a component.
*
* ```jsx
* <Transition timeout={150}>
* {(status) => (
* <MyComponent className={`fade fade-${status}`} />
* )}
* </Transition>
* ```
*/
children: PropTypes.oneOfType([PropTypes.func.isRequired, PropTypes.element.isRequired]).isRequired,
/**
* Show the component; triggers the enter or exit states
*/
in: PropTypes.bool,
/**
* By default the child component is mounted immediately along with
* the parent `Transition` component. If you want to "lazy mount" the component on the
* first `in={true}` you can set `mountOnEnter`. After the first enter transition the component will stay
* mounted, even on "exited", unless you also specify `unmountOnExit`.
*/
mountOnEnter: PropTypes.bool,
/**
* By default the child component stays mounted after it reaches the `'exited'` state.
* Set `unmountOnExit` if you'd prefer to unmount the component after it finishes exiting.
*/
unmountOnExit: PropTypes.bool,
/**
* Normally a component is not transitioned if it is shown when the `<Transition>` component mounts.
* If you want to transition on the first mount set `appear` to `true`, and the
* component will transition in as soon as the `<Transition>` mounts.
*
* > Note: there are no specific "appear" states. `appear` only adds an additional `enter` transition.
*/
appear: PropTypes.bool,
/**
* Enable or disable enter transitions.
*/
enter: PropTypes.bool,
/**
* Enable or disable exit transitions.
*/
exit: PropTypes.bool,
/**
* The duration of the transition, in milliseconds.
* Required unless `addEventListener` is provided
*
* You may specify a single timeout for all transitions like: `timeout={500}`,
* or individually like:
*
* ```jsx
* timeout={{
* enter: 300,
* exit: 500,
* }}
* ```
*
* @type {number | { enter?: number, exit?: number }}
*/
timeout: function timeout(props) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
var pt = _PropTypes.timeoutsShape;
if (!props.addEndListener) pt = pt.isRequired;
return pt.apply(undefined, [props].concat(args));
},
/**
* Add a custom transition end trigger. Called with the transitioning
* DOM node and a `done` callback. Allows for more fine grained transition end
* logic. **Note:** Timeouts are still used as a fallback if provided.
*
* ```jsx
* addEndListener={(node, done) => {
* // use the css transitionend event to mark the finish of a transition
* node.addEventListener('transitionend', done, false);
* }}
* ```
*/
addEndListener: PropTypes.func,
/**
* Callback fired before the "entering" status is applied. An extra parameter
* `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
*
* @type Function(node: HtmlElement, isAppearing: bool) -> void
*/
onEnter: PropTypes.func,
/**
* Callback fired after the "entering" status is applied. An extra parameter
* `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
*
* @type Function(node: HtmlElement, isAppearing: bool)
*/
onEntering: PropTypes.func,
/**
* Callback fired after the "entered" status is applied. An extra parameter
* `isAppearing` is supplied to indicate if the enter stage is occurring on the initial mount
*
* @type Function(node: HtmlElement, isAppearing: bool) -> void
*/
onEntered: PropTypes.func,
/**
* Callback fired before the "exiting" status is applied.
*
* @type Function(node: HtmlElement) -> void
*/
onExit: PropTypes.func,
/**
* Callback fired after the "exiting" status is applied.
*
* @type Function(node: HtmlElement) -> void
*/
onExiting: PropTypes.func,
/**
* Callback fired after the "exited" status is applied.
*
* @type Function(node: HtmlElement) -> void
*/
onExited: PropTypes.func
} : {};
// Name the function so it is clearer in the documentation
function noop() {}
Transition.defaultProps = {
in: false,
mountOnEnter: false,
unmountOnExit: false,
appear: false,
enter: true,
exit: true,
onEnter: noop,
onEntering: noop,
onEntered: noop,
onExit: noop,
onExiting: noop,
onExited: noop
};
Transition.UNMOUNTED = 0;
Transition.EXITED = 1;
Transition.ENTERING = 2;
Transition.ENTERED = 3;
Transition.EXITING = 4;
exports.default = Transition;
;