react-transition-preset
Version:
Lightweight, zero-dependency transition component for React with common preset transition
110 lines (107 loc) • 3.75 kB
JavaScript
import { useState, useRef } from 'react';
import { useDidUpdate } from './hooks/use-did-update.js';
import { useIsomorphicLayoutEffect } from './hooks/use-isomorphic-layout-effect.js';
import { useMemoizedFn } from './hooks/use-memoized-fn.js';
import { secToMs } from './utils.js';
// src/use-transition.ts
var TransitionStatus = /* @__PURE__ */ ((TransitionStatus2) => {
TransitionStatus2["entered"] = "entered";
TransitionStatus2["exited"] = "exited";
TransitionStatus2["entering"] = "entering";
TransitionStatus2["exiting"] = "exiting";
TransitionStatus2["preExiting"] = "pre-exiting";
TransitionStatus2["preEntering"] = "pre-entering";
return TransitionStatus2;
})(TransitionStatus || {});
function useTransition(props) {
const {
duration: durationInSec,
initial,
exitDuration: exitDurationInSec,
timingFunction,
mounted,
reduceMotion,
onEnter,
onExit,
onEntered,
onExited,
enterDelay: enterDelayInSec,
exitDelay: exitDelayInSec
} = props;
const [duration, exitDuration, enterDelay, exitDelay] = [
durationInSec,
exitDurationInSec,
enterDelayInSec,
exitDelayInSec
].map(secToMs);
const [transitionDuration, setTransitionDuration] = useState(reduceMotion ? 0 : duration);
const [transitionStatus, setStatus] = useState(() => {
if (mounted) {
return initial ? "pre-entering" /* preEntering */ : "entered" /* entered */;
}
return "exited" /* exited */;
});
const transitionTimeoutRef = useRef(-1);
const delayTimeoutRef = useRef(-1);
const rafRef = useRef(-1);
const handleStateChange = useMemoizedFn((shouldMount) => {
const preHandler = shouldMount ? onEnter : onExit;
const handler = shouldMount ? onEntered : onExited;
window.clearTimeout(transitionTimeoutRef.current);
const newTransitionDuration = reduceMotion ? 0 : shouldMount ? duration : exitDuration;
setTransitionDuration(newTransitionDuration);
if (newTransitionDuration === 0) {
typeof preHandler === "function" && preHandler();
typeof handler === "function" && handler();
setStatus(shouldMount ? "entered" /* entered */ : "exited" /* exited */);
} else {
rafRef.current = requestAnimationFrame(() => {
setStatus(shouldMount ? "pre-entering" /* preEntering */ : "pre-exiting" /* preExiting */);
rafRef.current = requestAnimationFrame(() => {
typeof preHandler === "function" && preHandler();
setStatus(shouldMount ? "entering" /* entering */ : "exiting" /* exiting */);
transitionTimeoutRef.current = window.setTimeout(() => {
typeof handler === "function" && handler();
setStatus(shouldMount ? "entered" /* entered */ : "exited" /* exited */);
}, newTransitionDuration);
});
});
}
});
const handleTransitionWithDelay = useMemoizedFn((shouldMount) => {
window.clearTimeout(delayTimeoutRef.current);
const delay = shouldMount ? enterDelay : exitDelay;
if (typeof delay !== "number") {
handleStateChange(shouldMount);
return;
}
delayTimeoutRef.current = window.setTimeout(
() => {
handleStateChange(shouldMount);
},
shouldMount ? enterDelay : exitDelay
);
});
useDidUpdate(
() => {
handleTransitionWithDelay(mounted);
},
{
deps: [mounted],
initialMounted: initial && mounted
}
);
useIsomorphicLayoutEffect(
() => () => {
window.clearTimeout(transitionTimeoutRef.current);
cancelAnimationFrame(rafRef.current);
},
[]
);
return {
transitionDuration,
transitionStatus,
transitionTimingFunction: timingFunction || "ease"
};
}
export { TransitionStatus, useTransition };