Skip to content

Instantly share code, notes, and snippets.

@bluebeel
Forked from stevecastaneda/ModalExample.tsx
Last active August 4, 2021 01:23
Show Gist options
  • Save bluebeel/61fbf5fd00a49fb9449c6b6730551a58 to your computer and use it in GitHub Desktop.
Save bluebeel/61fbf5fd00a49fb9449c6b6730551a58 to your computer and use it in GitHub Desktop.
Modified Transition React Component to Support Nested Transitions - Typescript
// JSX Version by Adam Wathan: https://gist.github.com/adamwathan/e0a791aa0419098a7ece70028b2e641e
import React, { ReactNode } from "react";
import { CSSTransition as ReactCSSTransition } from "react-transition-group";
import { useRef, useEffect, useContext } from "react";
interface TransitionProps {
show?: boolean;
enter?: string;
enterFrom?: string;
enterTo?: string;
leave?: string;
leaveFrom?: string;
leaveTo?: string;
appear?: string | boolean;
children: ReactNode;
}
interface ParentContextProps {
parent: {
show?: boolean;
appear?: string | boolean;
isInitialRender?: boolean;
};
}
const TransitionContext = React.createContext<ParentContextProps>({
parent: {},
});
function useIsInitialRender() {
const isInitialRender = useRef(true);
useEffect(() => {
isInitialRender.current = false;
}, []);
return isInitialRender.current;
}
function CSSTransition({
show,
enter = "",
enterFrom = "",
enterTo = "",
leave = "",
leaveFrom = "",
leaveTo = "",
appear,
children,
}: TransitionProps) {
const enterClasses = enter.split(" ").filter((s) => s.length);
const enterFromClasses = enterFrom.split(" ").filter((s) => s.length);
const enterToClasses = enterTo.split(" ").filter((s) => s.length);
const leaveClasses = leave.split(" ").filter((s) => s.length);
const leaveFromClasses = leaveFrom.split(" ").filter((s) => s.length);
const leaveToClasses = leaveTo.split(" ").filter((s) => s.length);
function addClasses(node: HTMLElement, classes: Array<string>) {
classes.length && node.classList.add(...classes);
}
function removeClasses(node: HTMLElement, classes: Array<string>) {
classes.length && node.classList.remove(...classes);
}
return (
<ReactCSSTransition
appear={appear}
unmountOnExit
in={show}
addEndListener={(node, done) => {
node.addEventListener("transitionend", done, false);
}}
onEnter={(node) => {
addClasses(node, [...enterClasses, ...enterFromClasses]);
}}
onEntering={(node) => {
removeClasses(node, enterFromClasses);
addClasses(node, enterToClasses);
}}
onEntered={(node) => {
removeClasses(node, [...enterToClasses, ...enterClasses]);
}}
onExit={(node) => {
addClasses(node, [...leaveClasses, ...leaveFromClasses]);
}}
onExiting={(node) => {
removeClasses(node, leaveFromClasses);
addClasses(node, leaveToClasses);
}}
onExited={(node) => {
removeClasses(node, [...leaveToClasses, ...leaveClasses]);
}}
>
{children}
</ReactCSSTransition>
);
}
function Transition({ show, appear, ...rest }: TransitionProps) {
const { parent } = useContext(TransitionContext);
const isInitialRender = useIsInitialRender();
const isChild = show === undefined;
if (isChild) {
return <CSSTransition appear={parent.appear || !parent.isInitialRender} show={parent.show} {...rest} />;
}
return (
<TransitionContext.Provider
value={{
parent: {
show,
isInitialRender,
appear,
},
}}
>
<CSSTransition appear={appear} show={show} {...rest} />
</TransitionContext.Provider>
);
}
export default Transition;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment