@@ -16,6 +16,11 @@ import type {
1616import { STATUS_NONE , STEP_PREPARE , STEP_START } from './interface' ;
1717import { getTransitionName , supportTransition } from './util/motion' ;
1818
19+ export interface CSSMotionRef {
20+ nativeElement : HTMLElement ;
21+ inMotion : ( ) => boolean ;
22+ }
23+
1924export type CSSMotionConfig =
2025 | boolean
2126 | {
@@ -117,116 +122,121 @@ export function genCSSMotion(config: CSSMotionConfig) {
117122 return ! ! ( props . motionName && transitionSupport && contextMotion !== false ) ;
118123 }
119124
120- const CSSMotion = React . forwardRef < any , CSSMotionProps > ( ( props , ref ) => {
121- const {
122- // Default config
123- visible = true ,
124- removeOnLeave = true ,
125-
126- forceRender,
127- children,
128- motionName,
129- leavedClassName,
130- eventProps,
131- } = props ;
132-
133- const { motion : contextMotion } = React . useContext ( Context ) ;
134-
135- const supportMotion = isSupportTransition ( props , contextMotion ) ;
136-
137- // Ref to the react node, it may be a HTMLElement
138- const nodeRef = useRef < any > ( ) ;
139-
140- function getDomElement ( ) {
141- return getDOM ( nodeRef . current ) as HTMLElement ;
142- }
143-
144- const [ status , statusStep , statusStyle , mergedVisible ] = useStatus (
145- supportMotion ,
146- visible ,
147- getDomElement ,
148- props ,
149- ) ;
150-
151- // Record whether content has rendered
152- // Will return null for un-rendered even when `removeOnLeave={false}`
153- const renderedRef = React . useRef ( mergedVisible ) ;
154- if ( mergedVisible ) {
155- renderedRef . current = true ;
156- }
157-
158- // ====================== Refs ======================
159- React . useImperativeHandle ( ref , ( ) => getDomElement ( ) ) ;
160-
161- // ===================== Render =====================
162- let motionChildren : React . ReactNode ;
163- const mergedProps = { ...eventProps , visible } ;
164-
165- if ( ! children ) {
166- // No children
167- motionChildren = null ;
168- } else if ( status === STATUS_NONE ) {
169- // Stable children
170- if ( mergedVisible ) {
171- motionChildren = children ( { ...mergedProps } , nodeRef ) ;
172- } else if ( ! removeOnLeave && renderedRef . current && leavedClassName ) {
173- motionChildren = children (
174- { ...mergedProps , className : leavedClassName } ,
175- nodeRef ,
176- ) ;
177- } else if ( forceRender || ( ! removeOnLeave && ! leavedClassName ) ) {
178- motionChildren = children (
179- { ...mergedProps , style : { display : 'none' } } ,
180- nodeRef ,
181- ) ;
182- } else {
183- motionChildren = null ;
184- }
185- } else {
186- // In motion
187- let statusSuffix : string ;
188- if ( statusStep === STEP_PREPARE ) {
189- statusSuffix = 'prepare' ;
190- } else if ( isActive ( statusStep ) ) {
191- statusSuffix = 'active' ;
192- } else if ( statusStep === STEP_START ) {
193- statusSuffix = 'start' ;
194- }
125+ const CSSMotion = React . forwardRef < CSSMotionRef , CSSMotionProps > (
126+ ( props , ref ) => {
127+ const {
128+ // Default config
129+ visible = true ,
130+ removeOnLeave = true ,
195131
196- const motionCls = getTransitionName (
132+ forceRender,
133+ children,
197134 motionName,
198- `${ status } -${ statusSuffix } ` ,
199- ) ;
135+ leavedClassName,
136+ eventProps,
137+ } = props ;
138+
139+ const { motion : contextMotion } = React . useContext ( Context ) ;
140+
141+ const supportMotion = isSupportTransition ( props , contextMotion ) ;
200142
201- motionChildren = children (
202- {
203- ...mergedProps ,
204- className : classNames ( getTransitionName ( motionName , status ) , {
205- [ motionCls ] : motionCls && statusSuffix ,
206- [ motionName as string ] : typeof motionName === 'string' ,
207- } ) ,
208- style : statusStyle ,
209- } ,
210- nodeRef ,
143+ // Ref to the react node, it may be a HTMLElement
144+ const nodeRef = useRef < any > ( ) ;
145+
146+ function getDomElement ( ) {
147+ return getDOM ( nodeRef . current ) as HTMLElement ;
148+ }
149+
150+ const [ status , statusStep , statusStyle , mergedVisible ] = useStatus (
151+ supportMotion ,
152+ visible ,
153+ getDomElement ,
154+ props ,
211155 ) ;
212- }
213156
214- // Auto inject ref if child node not have `ref` props
215- if ( React . isValidElement ( motionChildren ) && supportRef ( motionChildren ) ) {
216- const originNodeRef = getNodeRef ( motionChildren ) ;
157+ // Record whether content has rendered
158+ // Will return null for un-rendered even when `removeOnLeave={false}`
159+ const renderedRef = React . useRef ( mergedVisible ) ;
160+ if ( mergedVisible ) {
161+ renderedRef . current = true ;
162+ }
163+
164+ // ====================== Refs ======================
165+ React . useImperativeHandle ( ref , ( ) => ( {
166+ nativeElement : getDomElement ( ) ,
167+ inMotion : ( ) => status !== STATUS_NONE ,
168+ } ) ) ;
169+
170+ // ===================== Render =====================
171+ let motionChildren : React . ReactNode ;
172+ const mergedProps = { ...eventProps , visible } ;
173+
174+ if ( ! children ) {
175+ // No children
176+ motionChildren = null ;
177+ } else if ( status === STATUS_NONE ) {
178+ // Stable children
179+ if ( mergedVisible ) {
180+ motionChildren = children ( { ...mergedProps } , nodeRef ) ;
181+ } else if ( ! removeOnLeave && renderedRef . current && leavedClassName ) {
182+ motionChildren = children (
183+ { ...mergedProps , className : leavedClassName } ,
184+ nodeRef ,
185+ ) ;
186+ } else if ( forceRender || ( ! removeOnLeave && ! leavedClassName ) ) {
187+ motionChildren = children (
188+ { ...mergedProps , style : { display : 'none' } } ,
189+ nodeRef ,
190+ ) ;
191+ } else {
192+ motionChildren = null ;
193+ }
194+ } else {
195+ // In motion
196+ let statusSuffix : string ;
197+ if ( statusStep === STEP_PREPARE ) {
198+ statusSuffix = 'prepare' ;
199+ } else if ( isActive ( statusStep ) ) {
200+ statusSuffix = 'active' ;
201+ } else if ( statusStep === STEP_START ) {
202+ statusSuffix = 'start' ;
203+ }
204+
205+ const motionCls = getTransitionName (
206+ motionName ,
207+ `${ status } -${ statusSuffix } ` ,
208+ ) ;
217209
218- if ( ! originNodeRef ) {
219- motionChildren = React . cloneElement (
220- motionChildren as React . ReactElement ,
210+ motionChildren = children (
221211 {
222- ref : nodeRef ,
212+ ...mergedProps ,
213+ className : classNames ( getTransitionName ( motionName , status ) , {
214+ [ motionCls ] : motionCls && statusSuffix ,
215+ [ motionName as string ] : typeof motionName === 'string' ,
216+ } ) ,
217+ style : statusStyle ,
223218 } ,
219+ nodeRef ,
224220 ) ;
225221 }
226- }
227222
228- return motionChildren as React . ReactElement ;
229- } ) ;
223+ // Auto inject ref if child node not have `ref` props
224+ if ( React . isValidElement ( motionChildren ) && supportRef ( motionChildren ) ) {
225+ const originNodeRef = getNodeRef ( motionChildren ) ;
226+
227+ if ( ! originNodeRef ) {
228+ motionChildren = React . cloneElement (
229+ motionChildren as React . ReactElement ,
230+ {
231+ ref : nodeRef ,
232+ } ,
233+ ) ;
234+ }
235+ }
236+
237+ return motionChildren as React . ReactElement ;
238+ } ,
239+ ) ;
230240
231241 CSSMotion . displayName = 'CSSMotion' ;
232242
0 commit comments