feat(transition): handle cancel hooks
This commit is contained in:
		
							parent
							
								
									231b940103
								
							
						
					
					
						commit
						5c691aebfd
					
				| @ -1,6 +1,5 @@ | |||||||
| import { | import { | ||||||
|   getCurrentInstance, |   getCurrentInstance, | ||||||
|   ComponentInternalInstance, |  | ||||||
|   SetupContext, |   SetupContext, | ||||||
|   ComponentOptions |   ComponentOptions | ||||||
| } from '../component' | } from '../component' | ||||||
| @ -30,17 +29,38 @@ export interface TransitionProps { | |||||||
|   onLeaveCancelled?: (el: any) => void |   onLeaveCancelled?: (el: any) => void | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type TransitionHookCaller = ( | ||||||
|  |   hook: ((el: any) => void) | undefined, | ||||||
|  |   args?: any[] | ||||||
|  | ) => void | ||||||
|  | 
 | ||||||
|  | interface PendingCallbacks { | ||||||
|  |   enter?: (cancelled?: boolean) => void | ||||||
|  |   leave?: (cancelled?: boolean) => void | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const TransitionImpl = { | const TransitionImpl = { | ||||||
|   name: `BaseTransition`, |   name: `BaseTransition`, | ||||||
|   setup(props: TransitionProps, { slots }: SetupContext) { |   setup(props: TransitionProps, { slots }: SetupContext) { | ||||||
|     const instance = getCurrentInstance()! |     const instance = getCurrentInstance()! | ||||||
|     let isLeaving = false |     let isLeaving = false | ||||||
|     let isMounted = false |     let isMounted = false | ||||||
|  |     const pendingCallbacks: PendingCallbacks = {} | ||||||
| 
 | 
 | ||||||
|     onMounted(() => { |     onMounted(() => { | ||||||
|       isMounted = true |       isMounted = true | ||||||
|     }) |     }) | ||||||
| 
 | 
 | ||||||
|  |     const callTransitionHook: TransitionHookCaller = (hook, args) => { | ||||||
|  |       hook && | ||||||
|  |         callWithAsyncErrorHandling( | ||||||
|  |           hook, | ||||||
|  |           instance, | ||||||
|  |           ErrorCodes.TRANSITION_HOOK, | ||||||
|  |           args | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return () => { |     return () => { | ||||||
|       const children = slots.default && slots.default() |       const children = slots.default && slots.default() | ||||||
|       if (!children || !children.length) { |       if (!children || !children.length) { | ||||||
| @ -72,10 +92,11 @@ const TransitionImpl = { | |||||||
| 
 | 
 | ||||||
|       let delayedLeave: (() => void) | undefined |       let delayedLeave: (() => void) | undefined | ||||||
|       const performDelayedLeave = () => delayedLeave && delayedLeave() |       const performDelayedLeave = () => delayedLeave && delayedLeave() | ||||||
|       const transitionData = (child.transition = resolveTransitionData( |       const transitionHooks = (child.transition = resolveTransitionHooks( | ||||||
|         instance, |  | ||||||
|         rawProps, |         rawProps, | ||||||
|  |         callTransitionHook, | ||||||
|         isMounted, |         isMounted, | ||||||
|  |         pendingCallbacks, | ||||||
|         performDelayedLeave |         performDelayedLeave | ||||||
|       )) |       )) | ||||||
| 
 | 
 | ||||||
| @ -92,18 +113,18 @@ const TransitionImpl = { | |||||||
|       ) { |       ) { | ||||||
|         // update old tree's hooks in case of dynamic transition
 |         // update old tree's hooks in case of dynamic transition
 | ||||||
|         // need to do this recursively in case of HOCs
 |         // need to do this recursively in case of HOCs
 | ||||||
|         updateHOCTransitionData(oldChild, transitionData) |         updateHOCTransitionData(oldChild, transitionHooks) | ||||||
|         // switching between different views
 |         // switching between different views
 | ||||||
|         if (mode === 'out-in') { |         if (mode === 'out-in') { | ||||||
|           isLeaving = true |           isLeaving = true | ||||||
|           // return placeholder node and queue update when leave finishes
 |           // return placeholder node and queue update when leave finishes
 | ||||||
|           transitionData.afterLeave = () => { |           transitionHooks.afterLeave = () => { | ||||||
|             isLeaving = false |             isLeaving = false | ||||||
|             instance.update() |             instance.update() | ||||||
|           } |           } | ||||||
|           return placeholder(child) |           return placeholder(child) | ||||||
|         } else if (mode === 'in-out') { |         } else if (mode === 'in-out') { | ||||||
|           transitionData.delayLeave = performLeave => { |           transitionHooks.delayLeave = performLeave => { | ||||||
|             delayedLeave = performLeave |             delayedLeave = performLeave | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
| @ -139,7 +160,7 @@ export const Transition = (TransitionImpl as any) as { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface TransitionData { | export interface TransitionHooks { | ||||||
|   beforeEnter(el: object): void |   beforeEnter(el: object): void | ||||||
|   enter(el: object): void |   enter(el: object): void | ||||||
|   leave(el: object, remove: () => void): void |   leave(el: object, remove: () => void): void | ||||||
| @ -147,8 +168,7 @@ export interface TransitionData { | |||||||
|   delayLeave?(performLeave: () => void): void |   delayLeave?(performLeave: () => void): void | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function resolveTransitionData( | function resolveTransitionHooks( | ||||||
|   instance: ComponentInternalInstance, |  | ||||||
|   { |   { | ||||||
|     appear, |     appear, | ||||||
|     onBeforeEnter, |     onBeforeEnter, | ||||||
| @ -160,66 +180,59 @@ function resolveTransitionData( | |||||||
|     onAfterLeave, |     onAfterLeave, | ||||||
|     onLeaveCancelled |     onLeaveCancelled | ||||||
|   }: TransitionProps, |   }: TransitionProps, | ||||||
|  |   callHook: TransitionHookCaller, | ||||||
|   isMounted: boolean, |   isMounted: boolean, | ||||||
|  |   pendingCallbacks: PendingCallbacks, | ||||||
|   performDelayedLeave: () => void |   performDelayedLeave: () => void | ||||||
| ): TransitionData { | ): TransitionHooks { | ||||||
|   // TODO handle cancel hooks
 |  | ||||||
|   return { |   return { | ||||||
|     beforeEnter(el) { |     beforeEnter(el) { | ||||||
|       if (!isMounted && !appear) { |       if (!isMounted && !appear) { | ||||||
|         return |         return | ||||||
|       } |       } | ||||||
|       onBeforeEnter && |       if (pendingCallbacks.leave) { | ||||||
|         callWithAsyncErrorHandling( |         pendingCallbacks.leave(true /* cancelled */) | ||||||
|           onBeforeEnter, |       } | ||||||
|           instance, |       callHook(onBeforeEnter, [el]) | ||||||
|           ErrorCodes.TRANSITION_HOOK, |  | ||||||
|           [el] |  | ||||||
|         ) |  | ||||||
|     }, |     }, | ||||||
|  | 
 | ||||||
|     enter(el) { |     enter(el) { | ||||||
|       if (!isMounted && !appear) { |       if (!isMounted && !appear) { | ||||||
|         return |         return | ||||||
|       } |       } | ||||||
|       const done = () => { |       const afterEnter = (pendingCallbacks.enter = (cancelled?) => { | ||||||
|         onAfterEnter && |         if (cancelled) { | ||||||
|           callWithAsyncErrorHandling( |           callHook(onEnterCancelled, [el]) | ||||||
|             onAfterEnter, |         } else { | ||||||
|             instance, |           callHook(onAfterEnter, [el]) | ||||||
|             ErrorCodes.TRANSITION_HOOK, |           performDelayedLeave() | ||||||
|             [el] |         } | ||||||
|           ) |         pendingCallbacks.enter = undefined | ||||||
|         performDelayedLeave() |       }) | ||||||
|       } |  | ||||||
|       if (onEnter) { |       if (onEnter) { | ||||||
|         onEnter(el, done) |         onEnter(el, afterEnter) | ||||||
|       } else { |       } else { | ||||||
|         done() |         afterEnter() | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  | 
 | ||||||
|     leave(el, remove) { |     leave(el, remove) { | ||||||
|       onBeforeLeave && |       if (pendingCallbacks.enter) { | ||||||
|         callWithAsyncErrorHandling( |         pendingCallbacks.enter(true /* cancelled */) | ||||||
|           onBeforeLeave, |       } | ||||||
|           instance, |       callHook(onBeforeLeave, [el]) | ||||||
|           ErrorCodes.TRANSITION_HOOK, |       const afterLeave = (pendingCallbacks.leave = (cancelled?) => { | ||||||
|           [el] |  | ||||||
|         ) |  | ||||||
|       const afterLeave = () => |  | ||||||
|         onAfterLeave && |  | ||||||
|         callWithAsyncErrorHandling( |  | ||||||
|           onAfterLeave, |  | ||||||
|           instance, |  | ||||||
|           ErrorCodes.TRANSITION_HOOK, |  | ||||||
|           [el] |  | ||||||
|         ) |  | ||||||
|       if (onLeave) { |  | ||||||
|         onLeave(el, () => { |  | ||||||
|           remove() |  | ||||||
|           afterLeave() |  | ||||||
|         }) |  | ||||||
|       } else { |  | ||||||
|         remove() |         remove() | ||||||
|  |         if (cancelled) { | ||||||
|  |           callHook(onLeaveCancelled, [el]) | ||||||
|  |         } else { | ||||||
|  |           callHook(onAfterLeave, [el]) | ||||||
|  |         } | ||||||
|  |         pendingCallbacks.leave = undefined | ||||||
|  |       }) | ||||||
|  |       if (onLeave) { | ||||||
|  |         onLeave(el, afterLeave) | ||||||
|  |       } else { | ||||||
|         afterLeave() |         afterLeave() | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| @ -238,7 +251,7 @@ function placeholder(vnode: VNode): VNode | undefined { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function updateHOCTransitionData(vnode: VNode, data: TransitionData) { | function updateHOCTransitionData(vnode: VNode, data: TransitionHooks) { | ||||||
|   if (vnode.shapeFlag & ShapeFlags.COMPONENT) { |   if (vnode.shapeFlag & ShapeFlags.COMPONENT) { | ||||||
|     updateHOCTransitionData(vnode.component!.subTree, data) |     updateHOCTransitionData(vnode.component!.subTree, data) | ||||||
|   } else { |   } else { | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ import { AppContext } from './apiApp' | |||||||
| import { SuspenseBoundary } from './components/Suspense' | import { SuspenseBoundary } from './components/Suspense' | ||||||
| import { DirectiveBinding } from './directives' | import { DirectiveBinding } from './directives' | ||||||
| import { SuspenseImpl } from './components/Suspense' | import { SuspenseImpl } from './components/Suspense' | ||||||
| import { TransitionData } from './components/Transition' | import { TransitionHooks } from './components/Transition' | ||||||
| 
 | 
 | ||||||
| export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as { | export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as { | ||||||
|   __isFragment: true |   __isFragment: true | ||||||
| @ -93,7 +93,7 @@ export interface VNode<HostNode = any, HostElement = any> { | |||||||
|   component: ComponentInternalInstance | null |   component: ComponentInternalInstance | null | ||||||
|   suspense: SuspenseBoundary<HostNode, HostElement> | null |   suspense: SuspenseBoundary<HostNode, HostElement> | null | ||||||
|   dirs: DirectiveBinding[] | null |   dirs: DirectiveBinding[] | null | ||||||
|   transition: TransitionData | null |   transition: TransitionHooks | null | ||||||
| 
 | 
 | ||||||
|   // DOM
 |   // DOM
 | ||||||
|   el: HostNode | null |   el: HostNode | null | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user