import React, { HTMLAttributes, ReactNode, RefCallback, useEffect, useMemo, useRef, } from 'react'; import { st, classes } from './SlidingModal.st.css.js'; import { scopeTab } from './scopeTab'; export interface SlidingModalProps { dataHook?: string; width: string | number; isOpen?: boolean; style?: HTMLAttributes['style']; className?: HTMLAttributes['className']; children?: ReactNode; contentRef?: RefCallback; onExited?: () => void; onEntered?: () => void; onEntering?: () => void; onExiting?: () => void; transitionStatus?: string; onRequestClose?: () => void; shadowWidth?: number; appendTo?: 'parent'; overlay?: boolean; } export function SlidingModal(props: SlidingModalProps) { const { dataHook, className, style: styleProp, shadowWidth = 20, width, isOpen, children, contentRef, onExited, onEntered, onEntering, onExiting, onRequestClose, transitionStatus, appendTo, overlay, } = props; const style = useMemo( () => ({ ...styleProp, transform: `translate3d(${ isOpen ? 0 : typeof width === 'number' ? `${width + shadowWidth}px` : `${width}px` }, 0, 0)`, }), [styleProp, width, isOpen], ); const isOpenRef = useRef(isOpen); const divRef = useRef(); useEffect(() => { if (process.env.NODE_ENV === 'test' && isOpen) { onEntered?.(); } else { if (process.env.NODE_ENV === 'test' && isOpenRef.current) { onExited?.(); } } isOpenRef.current = isOpen; }, [isOpen]); useEffect(() => { const rootElement = divRef.current; if (!rootElement) { return; } const onTransitionEnd = (e: TransitionEvent) => { if (e.propertyName === 'transform') { isOpenRef.current ? onEntered?.() : onExited?.(); } }; const onTransitionStart = (e: TransitionEvent) => { if (e.propertyName === 'transform') { isOpenRef.current ? onEntering?.() : onExiting?.(); } }; rootElement.addEventListener('transitionend', onTransitionEnd); rootElement.addEventListener('transitionstart', onTransitionStart); return () => { rootElement.removeEventListener('transitionend', onTransitionEnd); rootElement.removeEventListener('transitionstart', onTransitionStart); }; }, []); return (
{ if (e.nativeEvent.code === 'Escape') { onRequestClose?.(); return; } if (divRef.current && e.nativeEvent.code === 'Tab') { scopeTab(divRef.current, e); return; } }} ref={(el) => { if (el) { divRef.current = el; } contentRef?.(el); }} data-hook={dataHook} data-transition-status={transitionStatus} className={st( classes.root, { exited: transitionStatus === 'exited', overlay, appendTo, }, ...[className, isOpen ? 'cairo-sliding-modal-is-open' : undefined], )} role="presentation" style={style} aria-modal={undefined} > {children}
); }