import * as anime from "animejs"; import * as React from "react"; import { Direction } from "../../utilities/units"; interface IStackProps { isOpen: boolean; align?: Direction; onOpenStart?: () => void; onOpenEnd?: () => void; onCloseStart?: () => void; onCloseEnd?: () => void; children?: any; } export class Stack extends React.Component { public ref: React.RefObject; constructor(props) { super(props) this.ref = React.createRef(); } public componentDidMount() { if (this.props.isOpen) { this.open(); } else { this.close(true); } } public componentDidUpdate(prevProps: IStackProps) { requestAnimationFrame(() => { if (!prevProps.isOpen && this.props.isOpen) { this.open(); } else if (prevProps.isOpen && !this.props.isOpen) { this.close(); } }); } public render() { return (
{React.Children.map(this.props.children, (child, index) => (
{child}
))}
); } private open = () => { anime.remove(".stack__item"); anime({ targets: ".stack__item", translateX: 0, delay: (el, i, l) => i * 100, begin: this.props.onOpenStart, complete: this.props.onOpenEnd, }); } private close = (immediate?: boolean) => { anime.remove(".stack__item"); anime({ targets: ".stack__item", translateX: (el) => ( this.withNode((node) => el.clientWidth + node.offsetLeft) ), delay: immediate ? 0 : (el, i, l) => (l - i - 1) * 100, ...(immediate && { duration: 0 }), elasticity: 0, begin: this.props.onCloseStart, complete: this.props.onCloseEnd, }); } private withNode = (found: (node: HTMLDivElement) => any): any => { const node = this.ref.current; if (node) { return found(node); } } }