react-component-transition
Version:
Easy animations between react component transitions.
124 lines (123 loc) • 6.76 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.Transition = void 0;
var tslib_1 = require("tslib");
var react_1 = tslib_1.__importStar(require("react"));
var react_dom_1 = require("react-dom");
var classnames_1 = tslib_1.__importDefault(require("classnames"));
var types_1 = require("./animation-hooks/types");
var animation_hooks_1 = require("./animation-hooks");
var defaults_1 = require("../animations/defaults");
var Transition = function (_a) {
var _b = _a.animateContainer, animateContainer = _b === void 0 ? false : _b, _c = _a.animateContainerDuration, animateContainerDuration = _c === void 0 ? defaults_1.defaultTransitionDuration : _c, _d = _a.animateContainerEasing, animateContainerEasing = _d === void 0 ? defaults_1.defaultTransitionEasing : _d, animateOnMount = _a.animateOnMount, children = _a.children, className = _a.className, classNameEnter = _a.classNameEnter, classNameExit = _a.classNameExit, disabled = _a.disabled, enterAnimation = _a.enterAnimation, exitAnimation = _a.exitAnimation, inViewRef = _a.inViewRef, inViewEnabled = _a.inViewEnabled, lazy = _a.lazy, onEnterFinished = _a.onEnterFinished, onExitFinished = _a.onExitFinished, style = _a.style;
var _e = react_1.useState(children && !lazy && animateOnMount && !animateContainer ? types_1.TransitionState.ContainerRect : null), transitionState = _e[0], setTransitionState = _e[1];
var prevChildren = react_1.useRef(animateOnMount && animateContainer ? null : children);
var containerRef = react_1.useRef(null);
var unmounted = react_1.useRef(false);
var hasChildrenChanged = didChildrenChanged(prevChildren.current, children);
if (!hasChildrenChanged && !transitionState) {
prevChildren.current = children;
}
var updatedState = function (state) {
if (!unmounted.current) {
setTransitionState(state);
}
};
react_1.useEffect(function () { return function () {
unmounted.current = true;
}; }, []);
react_1.useLayoutEffect(function () {
if (inViewEnabled && animateOnMount && !animateContainer) {
updatedState(types_1.TransitionState.ContainerRect);
}
}, [inViewEnabled]);
// start exit transition if children changed
react_1.useEffect(function () {
if (!hasChildrenChanged) {
return;
}
if (!transitionState) {
updatedState(types_1.TransitionState.Exit);
}
});
var animationHooks = {
children: children,
getElement: function () { return containerRef.current; },
prevChildren: prevChildren.current,
transitionState: transitionState,
disabled: disabled,
onFinish: null,
};
var _f = animation_hooks_1.useContainerRectangle(tslib_1.__assign(tslib_1.__assign({}, animationHooks), { onFinish: function () { return updatedState(types_1.TransitionState.Container); } })), nextClientRect = _f.nextClientRect, prevClientRect = _f.prevClientRect;
var exitFinishedHandler = function () {
onExitFinished && onExitFinished();
};
animation_hooks_1.useExitAnimation(tslib_1.__assign(tslib_1.__assign({}, animationHooks), { prevClientRect: prevClientRect, settings: exitAnimation, onFinish: function () {
var hadPrevChildren = !!prevChildren.current;
prevChildren.current = children;
if (hadPrevChildren && !animateContainer) {
exitFinishedHandler();
}
// If flushSync is done when there are no children, then we get a warning because we are still in react lifecycle
// So, if there are no children it means that there was no exit animation therefore we are yet in useEffect of useExitAnimation
if (hadPrevChildren && !disabled) {
// need to rerender the component after setting state to avoid animation blinking on animation exit
react_dom_1.flushSync(function () {
updatedState(types_1.TransitionState.ContainerRect);
});
}
else {
updatedState(types_1.TransitionState.ContainerRect);
}
} }));
animation_hooks_1.useContainerAnimation(tslib_1.__assign(tslib_1.__assign({}, animationHooks), { prevClientRect: prevClientRect,
nextClientRect: nextClientRect,
animateContainer: animateContainer,
animateContainerDuration: animateContainerDuration,
animateContainerEasing: animateContainerEasing, onFinish: function () {
if (!prevChildren.current && animateContainer) {
exitFinishedHandler();
}
updatedState(types_1.TransitionState.Enter);
} }));
animation_hooks_1.useEnterAnimation(tslib_1.__assign(tslib_1.__assign({}, animationHooks), { nextClientRect: nextClientRect, settings: enterAnimation, onFinish: function () {
if (prevChildren.current) {
onEnterFinished && onEnterFinished();
}
updatedState(null);
} }));
var shouldRenderPrevChildren = hasChildrenChanged || transitionState === types_1.TransitionState.Exit;
var hideContent = (lazy && !inViewEnabled) ||
transitionState === types_1.TransitionState.ContainerRect ||
transitionState === types_1.TransitionState.Container;
var setRefs = react_1.useCallback(function (element) {
containerRef.current = element;
inViewRef && inViewRef(element);
}, [inViewRef]);
if (!lazy && !hasChildrenChanged && !transitionState && !children) {
return null;
}
return (react_1.default.createElement("div", { ref: setRefs, className: classnames_1.default(className, transitionState === types_1.TransitionState.Enter && classNameEnter, transitionState === types_1.TransitionState.Exit && classNameExit) || null, style: tslib_1.__assign(tslib_1.__assign({}, style), { opacity: hideContent ? 0 : null }) }, shouldRenderPrevChildren ?
prevChildren.current :
children));
};
exports.Transition = Transition;
var didChildrenChanged = function (prevChildren, children) {
var prevChildrenElement = prevChildren;
var childrenElement = children;
if (!prevChildren && !children) {
return false;
}
if (!prevChildren && children) {
return true;
}
if (prevChildren && !children) {
return true;
}
if (prevChildrenElement.key === childrenElement.key &&
prevChildrenElement.type === childrenElement.type) {
return false;
}
return true;
};
exports.Transition.displayName = "Transition";
;