UNPKG

react-pose-15

Version:

A declarative animation library for React 15+

517 lines (507 loc) 20.1 kB
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 };