@re-flex/transition-group
Version:
Re-flex UI Transtion Controller Package
71 lines (70 loc) • 2.81 kB
JavaScript
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;
};