import { getStyleValue } from './utils' ; export const MODE = { SHOW : 'show' , HIDE : 'hide' , } ; export type TransitionStylesFn = ( domElement : HTMLElement ) => TransitionStyles ; export type TransitionStyles = { default ? : Partial < CSSStyleDeclaration > ; showStart ? : Partial < CSSStyleDeclaration > ; showEnd ? : Partial < CSSStyleDeclaration > ; hideStart ? : Partial < CSSStyleDeclaration > ; hideEnd ? : Partial < CSSStyleDeclaration > ; } ; type TransitionProps = { domElement ? : HTMLElement ; className ? : string ; styles ? : TransitionStyles | TransitionStylesFn ; __transitionTimeoutId__ ? : number ; } ; type TransitionClassNames = { [ key ] : string [ ] ; showStart : string [ ] ; showEnd : string [ ] ; hideStart : string [ ] ; hideEnd : string [ ] ; } ; type TransitionStep = 'showStart' | 'showEnd' | 'hideStart' | 'hideEnd' ; type TransitionStyleState = 'default' | TransitionStep ; type KeyValue = { [ key ] : string ; } ; const removeTransitionClassNames = ( domElement , transitionClassNames ) => domElement . classList . remove ( ... transitionClassNames . showStart , ... transitionClassNames . showEnd , ... transitionClassNames . hideStart , ... transitionClassNames . hideEnd ) ; const applyTransitionStyles = ( domElement , step , styles ) => { const transitionStyle = styles [ step ] as CSSStyleDeclaration ; if ( transitionStyle ) { Object . keys ( transitionStyle ) . forEach ( ( key ) => { const value = ( ( transitionStyle as unknown ) as KeyValue ) [ key ] ; ( ( domElement . style as unknown ) as KeyValue ) [ key ] = value ; } ) ; } } ; const applyNoDurationTransitionStyle = ( domElement ) => { domElement . style . transitionDuration = '0ms' ; } ; const getTransitionStyles = ( domElement , styles ) => ( typeof styles === 'function' ? styles ( domElement ) : styles ) || { } ; const createClassList = ( className , step ) => className . split ( / / ) . map ( ( n ) => `${ n }-${ step }` ) ; const applyStylesForState = ( domElement , props , step , isEnterStep ? ) => { if ( props . styles ) { const styles = getTransitionStyles ( domElement , props . styles ) ; applyTransitionStyles ( domElement , 'default' , styles ) ; if ( isEnterStep ) { applyNoDurationTransitionStyle ( domElement ) ; } applyTransitionStyles ( domElement , step , styles ) ; } if ( props . className ) { const transitionClassNames = { showStart : createClassList ( props . className , 'show-start' ) , showEnd : createClassList ( props . className , 'show-end' ) , hideStart : createClassList ( props . className , 'hide-start' ) , hideEnd : createClassList ( props . className , 'hide-end' ) , } ; removeTransitionClassNames ( domElement , transitionClassNames ) ; if ( transitionClassNames ) { domElement . classList . add ( ... transitionClassNames [ step ] ) ; } } domElement . scrollTop ; } ; const styleDurationToMs = ( durationStr ) => { const parsed = parseFloat ( durationStr ) * ( durationStr . indexOf ( 'ms' ) === - 1 ? 1000 : 1 ) ; return Number . isNaN ( parsed ) ? 0 : parsed ; } ; const getDuration = ( domElement ) => { const durationStyleValue = getStyleValue ( { domElement , prop : 'transition-duration' , } ) ; const durationValue = durationStyleValue !== undefined ? styleDurationToMs ( durationStyleValue ) : 0 ; const delayStyleValue = getStyleValue ( { domElement , prop : 'transition-delay' , } ) ; const delayValue = delayStyleValue !== undefined ? styleDurationToMs ( delayStyleValue ) : 0 ; return durationValue + delayValue ; } ; type Step = { nextStep ? : TransitionStep ; } ; type Steps = { showStart : Step ; showEnd : Step ; hideStart : Step ; hideEnd : Step ; } ; const steps = { showStart : { nextStep : 'showEnd' , } , showEnd : { nextStep : undefined , } , hideStart : { nextStep : 'hideEnd' , } , hideEnd : { nextStep : undefined , } , } ; export const transition = ( props , mode ? ) => { const { domElement } = props ; if ( ! domElement ) { return Promise . resolve ( 'no domElement' ) ; } clearTimeout ( props . __transitionTimeoutId__ ) ; let currentStep = mode === MODE . SHOW ? 'showStart' : 'hideStart' ; return new Promise ( resolve => { applyStylesForState ( domElement , props , currentStep , currentStep === 'showStart' ) ; setTimeout ( ( ) => { const { nextStep } = steps [ currentStep ] ; if ( nextStep ) { currentStep = nextStep ; applyStylesForState ( domElement , props , currentStep ) ; const duration = getDuration ( domElement ) ; props . __transitionTimeoutId__ = window . setTimeout ( resolve , duration ) ; } } , 0 ) ; } ) ; } ;