import { Transition } from 'react-transition-group'; import { TimeoutProps } from 'react-transition-group/Transition'; import React, { CSSProperties, HTMLAttributes, ReactElement, RefCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react'; import { st, classes } from './Collapse.st.css.js'; import { TransitionState } from '@wix/bex-core'; import { observer } from 'mobx-react-lite'; import { action, runInAction } from 'mobx'; export interface CollapseProps extends Omit, 'in'> { children: ReactElement<{ className?: string; style?: CSSProperties }>; state: TransitionState; dataHook?: string; style?: HTMLAttributes['style']; rootRef?: RefCallback; } function _Collapse(props: CollapseProps) { const { children, timeout, state, onExit, onExiting, onExited, onEnter, onEntering, onEntered, dataHook, rootRef, style: styleProp, ...rest } = props; const ref = useRef(null); const [initialStyle, setInitialStyle] = useState(false); const enterTimeoutRef = useRef(); const exitTimeoutRef = useRef(); const { status, show } = state; useLayoutEffect(() => { setInitialStyle(true); runInAction(() => { if (state.show) { state.status = 'entered'; } }); }, []); useEffect(() => { return () => { window.clearTimeout(enterTimeoutRef.current); window.clearTimeout(exitTimeoutRef.current); }; }, []); const statusStyle = useMemo( () => initialStyle ? { ...(status !== 'afterEntered' && { overflow: 'hidden' }), ...(status !== 'afterEntered' && { height: status === 'exited' || status === 'exiting' ? 0 : ref.current?.scrollHeight, }), } : {}, [status, initialStyle], ); const style = useMemo( () => ({ transitionDuration: `${timeout}ms`, ...styleProp, ...statusStyle, }), [timeout, styleProp, statusStyle], ); return ( { state.setStatus('enter'); onEnter?.(node, isAppearing); }} onEntering={(node, isAppearing) => { state.setStatus('entering'); onEntering?.(node, isAppearing); }} onEntered={(node, isAppearing) => { state.setStatus('entered'); enterTimeoutRef.current = window.setTimeout( action(() => { if (state.status === 'entered') { state.setStatus('afterEntered'); } }), 0, ); onEntered?.(node, isAppearing); }} onExit={(node) => { state.setStatus('exit'); onExit?.(node); }} onExiting={(node) => { state.setStatus('beforeExit'); exitTimeoutRef.current = window.setTimeout( action(() => { if (state.status === 'beforeExit') { state.setStatus('exiting'); } }), 0, ); onExiting?.(node); }} onExited={(node) => { state.setStatus('exited'); onExited?.(node); }} timeout={timeout} {...rest} >
{ ref.current = el; rootRef?.(el); }} className={st(classes.root)} style={style} > {children}
); } export const Collapse = observer(_Collapse);