react-pose-15
Version:
A declarative animation library for React 15+
517 lines (507 loc) • 20.1 kB
JavaScript
import { __rest, __assign, __extends } from 'tslib';
import { createElement, Component, PureComponent, cloneElement, Children } from 'react';
import createContext from 'create-react-context';
import poseFactory from 'popmotion-pose';
import isValidProp from '@emotion/is-prop-valid';
import { invariant, warning } from 'hey-listen';
var hasChanged = function (prev, next) {
if (prev === next)
return false;
var prevIsArray = Array.isArray(prev);
var nextIsArray = Array.isArray(next);
if (prevIsArray !== nextIsArray || (!prevIsArray && !nextIsArray)) {
return true;
}
else if (prevIsArray && nextIsArray) {
var numPrev = prev.length;
var numNext = next.length;
if (numPrev !== numNext)
return true;
for (var i = numPrev; i < numPrev; i++) {
if (prev[i] !== next[i])
return true;
}
}
return false;
};
var pickAssign = function (shouldPick, sources) {
return sources.reduce(function (picked, source) {
for (var key in source) {
if (shouldPick(key)) {
picked[key] = source[key];
}
}
return picked;
}, {});
};
var PoseParentContext = createContext({});
var calcPopFromFlowStyle = function (el) {
var offsetTop = el.offsetTop, offsetLeft = el.offsetLeft, offsetWidth = el.offsetWidth, offsetHeight = el.offsetHeight;
return {
position: 'absolute',
top: offsetTop,
left: offsetLeft,
width: offsetWidth,
height: offsetHeight
};
};
var hasPose = function (pose, key) {
return Array.isArray(pose) ? pose.indexOf(key) !== -1 : pose === key;
};
var objectToMap = function (obj) {
return Object.keys(obj).reduce(function (map, key) {
map.set(key, { raw: obj[key] });
return map;
}, new Map());
};
var testAlwaysTrue = function () { return true; };
var filterProps = function (_a) {
var elementType = _a.elementType, poseConfig = _a.poseConfig, onValueChange = _a.onValueChange, innerRef = _a.innerRef, _pose = _a._pose, pose = _a.pose, initialPose = _a.initialPose, poseKey = _a.poseKey, onPoseComplete = _a.onPoseComplete, getParentPoseConfig = _a.getParentPoseConfig, registerChild = _a.registerChild, onUnmount = _a.onUnmount, getInitialPoseFromParent = _a.getInitialPoseFromParent, popFromFlow = _a.popFromFlow, values = _a.values, parentValues = _a.parentValues, onDragStart = _a.onDragStart, onDragEnd = _a.onDragEnd, onPressStart = _a.onPressStart, onPressEnd = _a.onPressEnd, props = __rest(_a, ["elementType", "poseConfig", "onValueChange", "innerRef", "_pose", "pose", "initialPose", "poseKey", "onPoseComplete", "getParentPoseConfig", "registerChild", "onUnmount", "getInitialPoseFromParent", "popFromFlow", "values", "parentValues", "onDragStart", "onDragEnd", "onPressStart", "onPressEnd"]);
return props;
};
var PoseElement = (function (_super) {
__extends(PoseElement, _super);
function PoseElement(props) {
var _this = _super.call(this, props) || this;
_this.children = new Set();
_this.childrenHandlers = {
registerChild: function (props) {
_this.children.add(props);
if (_this.poser)
_this.flushChildren();
},
onUnmount: function (child) { return _this.poser.removeChild(child); },
getParentPoseConfig: function () { return _this.poseConfig; },
getInitialPoseFromParent: function () { return _this.getInitialPose(); }
};
_this.getRefs = function () {
var refs = {};
var elementType = _this.props.elementType;
if (typeof elementType === 'string') {
refs.ref = _this.setRef;
}
else {
refs.innerRef = _this.setRef;
refs.hostRef = _this.setRef;
}
return refs;
};
_this.setRef = function (ref) {
if (ref instanceof Element || (_this.ref && ref === null)) {
var innerRef = _this.props.innerRef;
if (innerRef) {
if (typeof innerRef === 'function') {
innerRef(ref);
}
else {
innerRef.current = ref;
}
}
_this.ref = ref;
}
};
_this.shouldForwardProp =
typeof _this.props.elementType === 'string' ? isValidProp : testAlwaysTrue;
var poseConfig = _this.props.poseConfig;
_this.poseConfig =
typeof poseConfig === 'function'
? poseConfig(filterProps(props))
: poseConfig;
return _this;
}
PoseElement.prototype.getInitialPose = function () {
var _a = this.props, getInitialPoseFromParent = _a.getInitialPoseFromParent, pose = _a.pose, _pose = _a._pose, initialPose = _a.initialPose;
if (initialPose) {
return initialPose;
}
else {
var parentPose = getInitialPoseFromParent && getInitialPoseFromParent();
var initialPoses = (Array.isArray(parentPose)
? parentPose
: [parentPose])
.concat(pose, _pose)
.filter(Boolean);
return initialPoses.length > 0 ? initialPoses : undefined;
}
};
PoseElement.prototype.getFirstPose = function () {
var _a = this.props, initialPose = _a.initialPose, pose = _a.pose, _pose = _a._pose;
if (!initialPose)
return;
var firstPose = (Array.isArray(pose) ? pose : [pose])
.concat(_pose)
.filter(Boolean);
return firstPose.length === 1 ? firstPose[0] : firstPose;
};
PoseElement.prototype.getSetProps = function () {
var props = filterProps(this.props);
if (this.props.popFromFlow && this.ref && this.ref instanceof HTMLElement) {
if (!this.popStyle) {
props.style = __assign({}, props.style, calcPopFromFlowStyle(this.ref));
this.popStyle = props.style;
}
else {
props.style = this.popStyle;
}
}
else {
this.popStyle = null;
}
return props;
};
PoseElement.prototype.componentDidMount = function () {
var _this = this;
invariant(typeof this.ref !== 'undefined', "No DOM ref found. If you're converting an existing component via posed(Component), you must ensure you're passing the hostRef prop to your underlying DOM element.");
var _a = this.props, onValueChange = _a.onValueChange, registerChild = _a.registerChild, values = _a.values, parentValues = _a.parentValues, onDragStart = _a.onDragStart, onDragEnd = _a.onDragEnd, onPressStart = _a.onPressStart, onPressEnd = _a.onPressEnd;
var config = __assign({}, this.poseConfig, { initialPose: this.getInitialPose(), values: values || this.poseConfig.values, parentValues: parentValues ? objectToMap(parentValues) : undefined, props: this.getSetProps(), onDragStart: onDragStart,
onDragEnd: onDragEnd,
onPressStart: onPressStart,
onPressEnd: onPressEnd, onChange: onValueChange });
if (!registerChild) {
this.initPoser(poseFactory(this.ref, config));
}
else {
registerChild({
element: this.ref,
poseConfig: config,
onRegistered: function (poser) { return _this.initPoser(poser); }
});
}
};
PoseElement.prototype.componentWillUpdate = function (_a) {
var pose = _a.pose, _pose = _a._pose;
if (hasPose(pose, 'flip') || hasPose(_pose, 'flip'))
this.poser.measure();
};
PoseElement.prototype.componentDidUpdate = function (prevProps) {
var _a = this.props, pose = _a.pose, _pose = _a._pose, poseKey = _a.poseKey;
this.poser.setProps(this.getSetProps());
if (poseKey !== prevProps.poseKey ||
hasChanged(prevProps.pose, pose) ||
pose === 'flip') {
this.setPose(pose);
}
if (_pose !== prevProps._pose || _pose === 'flip')
this.setPose(_pose);
};
PoseElement.prototype.componentWillUnmount = function () {
if (!this.poser)
return;
var onUnmount = this.props.onUnmount;
if (onUnmount)
onUnmount(this.poser);
this.poser.destroy();
};
PoseElement.prototype.initPoser = function (poser) {
this.poser = poser;
this.flushChildren();
var firstPose = this.getFirstPose();
if (firstPose)
this.setPose(firstPose);
};
PoseElement.prototype.setPose = function (pose) {
var _this = this;
var onPoseComplete = this.props.onPoseComplete;
var poseList = Array.isArray(pose) ? pose : [pose];
Promise.all(poseList.map(function (key) { return key && _this.poser.set(key); })).then(function () { return onPoseComplete && onPoseComplete(pose); });
};
PoseElement.prototype.flushChildren = function () {
var _this = this;
this.children.forEach(function (_a) {
var element = _a.element, poseConfig = _a.poseConfig, onRegistered = _a.onRegistered;
return onRegistered(_this.poser.addChild(element, poseConfig));
});
this.children.clear();
};
PoseElement.prototype.render = function () {
var elementType = this.props.elementType;
return (createElement(PoseParentContext.Provider, { value: this.childrenHandlers }, createElement(elementType, pickAssign(this.shouldForwardProp, [
this.getSetProps(),
this.getRefs()
]))));
};
return PoseElement;
}(PureComponent));
var supportedElements = [
'a',
'article',
'aside',
'audio',
'b',
'blockquote',
'body',
'br',
'button',
'canvas',
'caption',
'cite',
'code',
'col',
'colgroup',
'data',
'datalist',
'dialog',
'div',
'em',
'embed',
'fieldset',
'figcaption',
'figure',
'footer',
'form',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'head',
'header',
'hgroup',
'hr',
'i',
'iframe',
'img',
'input',
'label',
'legend',
'li',
'nav',
'object',
'ol',
'option',
'p',
'param',
'picture',
'pre',
'progress',
'q',
'section',
'select',
'span',
'strong',
'table',
'tbody',
'td',
'textarea',
'tfoot',
'th',
'thead',
'time',
'title',
'tr',
'ul',
'video',
'circle',
'clipPath',
'defs',
'ellipse',
'g',
'image',
'line',
'linearGradient',
'mask',
'path',
'pattern',
'polygon',
'polyline',
'radialGradient',
'rect',
'stop',
'svg',
'text',
'tspan'
];
var componentCache = new Map();
var createComponentFactory = function (key) {
var componentFactory = function (poseConfig) {
if (poseConfig === void 0) { poseConfig = {}; }
return function (_a) {
var _b = _a.withParent, withParent = _b === void 0 ? true : _b, props = __rest(_a, ["withParent"]);
return !withParent || props.parentValues ? (createElement(PoseElement, __assign({ poseConfig: poseConfig, elementType: key }, props))) : (createElement(PoseParentContext.Consumer, null, function (parentCtx) { return (createElement(PoseElement, __assign({ poseConfig: poseConfig, elementType: key }, props, parentCtx))); }));
};
};
componentCache.set(key, componentFactory);
return componentFactory;
};
var getComponentFactory = function (key) {
return componentCache.has(key)
? componentCache.get(key)
: createComponentFactory(key);
};
var posed = (function (component) {
return getComponentFactory(component);
});
supportedElements.reduce(function (acc, key) {
acc[key] = getComponentFactory(key);
return acc;
}, posed);
var getKey = function (child) {
invariant(child && child.key !== null, 'Every child of Transition must be given a unique key');
var childKey = typeof child.key === 'number' ? child.key.toString() : child.key;
return childKey.replace('.$', '');
};
var prependProps = function (element, props) {
return createElement(element.type, __assign({ key: element.key, ref: element.ref }, props, element.props));
};
var handleTransition = function (_a, _b) {
var displayedChildren = _b.displayedChildren, finishedLeaving = _b.finishedLeaving, hasInitialized = _b.hasInitialized, prevChildren = _b.indexedChildren, scheduleChildRemoval = _b.scheduleChildRemoval;
var incomingChildren = _a.children, preEnterPose = _a.preEnterPose, enterPose = _a.enterPose, exitPose = _a.exitPose, animateOnMount = _a.animateOnMount, enterAfterExit = _a.enterAfterExit, flipMove = _a.flipMove, onRest = _a.onRest, propsForChildren = __rest(_a, ["children", "preEnterPose", "enterPose", "exitPose", "animateOnMount", "enterAfterExit", "flipMove", "onRest"]);
var targetChildren = makeChildList(incomingChildren);
var nextState = {
displayedChildren: [],
indexedChildren: {},
};
if (process.env.NODE_ENV !== 'production') {
warning(!propsForChildren.onPoseComplete, "<Transition/> (or <PoseGroup/>) doesn't accept onPoseComplete prop.");
}
var prevKeys = displayedChildren.map(getKey);
var nextKeys = targetChildren.map(getKey);
var hasPropsForChildren = Object.keys(propsForChildren).length !== 0;
var entering = new Set(nextKeys.filter(function (key) { return finishedLeaving.hasOwnProperty(key) || prevKeys.indexOf(key) === -1; }));
entering.forEach(function (key) { return delete finishedLeaving[key]; });
var leaving = [];
var newlyLeaving = {};
prevKeys.forEach(function (key) {
if (entering.has(key)) {
return;
}
var isLeaving = finishedLeaving.hasOwnProperty(key);
if (!isLeaving && nextKeys.indexOf(key) !== -1) {
return;
}
leaving.push(key);
if (!isLeaving) {
finishedLeaving[key] = false;
newlyLeaving[key] = true;
}
});
var moving = new Set(prevKeys.filter(function (key, i) {
if (entering.has(key)) {
return false;
}
var nextIndex = nextKeys.indexOf(key);
return nextIndex !== -1 && i !== nextIndex;
}));
targetChildren.forEach(function (child) {
var newChildProps = {};
if (entering.has(child.key)) {
if (hasInitialized || animateOnMount) {
newChildProps.initialPose = preEnterPose;
}
newChildProps._pose = enterPose;
}
else if (moving.has(child.key) && flipMove) {
newChildProps._pose = [enterPose, 'flip'];
}
else {
newChildProps._pose = enterPose;
}
var newChild = cloneElement(child, newChildProps);
nextState.indexedChildren[child.key] = newChild;
nextState.displayedChildren.push(hasPropsForChildren
? prependProps(newChild, propsForChildren)
: newChild);
});
leaving.forEach(function (key) {
var child = prevChildren[key];
var newChild = newlyLeaving[key]
? cloneElement(child, {
_pose: exitPose,
onPoseComplete: function (pose) {
scheduleChildRemoval(key);
var onPoseComplete = child.props.onPoseComplete;
onPoseComplete && onPoseComplete(pose);
},
popFromFlow: flipMove,
})
: child;
var insertionIndex = prevKeys.indexOf(key);
nextState.indexedChildren[child.key] = newChild;
nextState.displayedChildren.splice(insertionIndex, 0, hasPropsForChildren
? prependProps(newChild, propsForChildren)
: newChild);
});
return nextState;
};
var handleChildrenTransitions = (function (props, state) { return (__assign({ hasInitialized: true }, handleTransition(props, state))); });
var makeChildList = function (children) {
var list = [];
Children.forEach(children, function (child) { return child && list.push(child); });
return list;
};
var Transition = (function (_super) {
__extends(Transition, _super);
function Transition() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.state = {
displayedChildren: [],
finishedLeaving: {},
hasInitialized: false,
indexedChildren: {},
scheduleChildRemoval: function (key) { return _this.removeChild(key); }
};
_this.setDerivedState = function (props) {
if (props === void 0) { props = _this.props; }
var derivedState = handleChildrenTransitions(props, _this.state);
if (derivedState !== _this.state) {
_this.setState(derivedState);
}
};
return _this;
}
Transition.prototype.componentWillReceiveProps = function (nextProps) {
this.setDerivedState(nextProps);
};
Transition.prototype.componentWillMount = function () {
this.setDerivedState(this.props);
};
Transition.prototype.removeChild = function (key) {
var _a = this.state, displayedChildren = _a.displayedChildren, finishedLeaving = _a.finishedLeaving;
var _b = this.props, enterAfterExit = _b.enterAfterExit, onRest = _b.onRest;
if (!finishedLeaving.hasOwnProperty(key))
return;
finishedLeaving[key] = true;
if (!Object.keys(finishedLeaving).every(function (leavingKey) { return finishedLeaving[leavingKey]; })) {
return;
}
var targetChildren = displayedChildren.filter(function (child) { return !finishedLeaving.hasOwnProperty(child.key); });
var newState = enterAfterExit
? __assign({ finishedLeaving: {} }, handleChildrenTransitions(__assign({}, this.props, { enterAfterExit: false }), __assign({}, this.state, { displayedChildren: targetChildren }))) : {
finishedLeaving: {},
displayedChildren: targetChildren
};
this.setState(newState, onRest);
};
Transition.prototype.shouldComponentUpdate = function (nextProps, nextState) {
return this.state !== nextState;
};
Transition.prototype.render = function () {
var displayedChildren = this.state.displayedChildren;
var hasChildren = Array.isArray(displayedChildren) && displayedChildren.length;
return hasChildren ? (createElement("div", { className: "ReactPoseTransitionGroup" }, displayedChildren)) : null;
};
Transition.defaultProps = {
flipMove: false,
enterAfterExit: false,
preEnterPose: 'exit',
enterPose: 'enter',
exitPose: 'exit'
};
return Transition;
}(Component));
var PoseGroup = (function (_super) {
__extends(PoseGroup, _super);
function PoseGroup() {
return _super !== null && _super.apply(this, arguments) || this;
}
PoseGroup.prototype.render = function () {
return createElement(Transition, __assign({}, this.props));
};
PoseGroup.defaultProps = {
flipMove: true
};
return PoseGroup;
}(Component));
export default posed;
export { Transition, PoseGroup };