UNPKG

@re-flex/transition-group

Version:

Re-flex UI Transtion Controller Package

71 lines (70 loc) 2.81 kB
import { cloneElement, useRef } from "react"; const getChildProp = (child, propName, defaultValue) => { const { [propName]: prop = defaultValue } = child.props; return prop; }; export default (props) => { const { children, appear = false, enter = true, exit = true, duration = 500, } = props; const derivedChildren = Array.isArray(children) ? children : [children]; const firstRenderRef = useRef(true); const prevVisibleChildrenRef = useRef([]); const nextVisibleChildren = []; const nextChildrenKeys = {}; const nextChildren = []; const addVisibleChild = (child, removeTimeout) => { if (!child) { return; } const visibleChild = cloneElement(child, { enter, exit, duration, ...child.props, in: !removeTimeout, appear: firstRenderRef.current ? getChildProp(child, "appear", appear) : getChildProp(child, "enter", enter), }); nextVisibleChildren.push({ visibleChild, removeTimeout }); nextChildrenKeys[child.key] = true; nextChildren.push(visibleChild); }; const makeRemoveTimeout = (child) => setTimeout(() => { const { current: prevVisibleChildren } = prevVisibleChildrenRef; const indexToDelete = prevVisibleChildren.findIndex(({ visibleChild }) => visibleChild.key === child.key); if (indexToDelete > -1) { prevVisibleChildren.splice(indexToDelete, 1); } }, getChildProp(child, "duration", duration)); let lastAddedChildIndex = 0; prevVisibleChildrenRef.current.forEach(({ visibleChild, removeTimeout }) => { const { key } = visibleChild; const foundIndex = derivedChildren.findIndex((child) => child.key === key); if (foundIndex < 0) { if (removeTimeout) { addVisibleChild(visibleChild, removeTimeout); } else { const shouldAddTimeout = exit && visibleChild.props.exit !== false; if (shouldAddTimeout) { addVisibleChild(visibleChild, makeRemoveTimeout(visibleChild)); } } } else { if (removeTimeout) { clearTimeout(removeTimeout); } for (let i = lastAddedChildIndex; i <= foundIndex; i++) { addVisibleChild(derivedChildren[i], null); } } lastAddedChildIndex = Math.max(lastAddedChildIndex, foundIndex + 1); }); for (let i = lastAddedChildIndex; i < derivedChildren.length; i++) { addVisibleChild(derivedChildren[i], null); } prevVisibleChildrenRef.current = nextVisibleChildren; firstRenderRef.current = false; return nextChildren; };