]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(transition): handle persisted mode
authorEvan You <yyx990803@gmail.com>
Sat, 23 Nov 2019 04:21:39 +0000 (23:21 -0500)
committerEvan You <yyx990803@gmail.com>
Sat, 23 Nov 2019 04:21:53 +0000 (23:21 -0500)
packages/runtime-core/src/components/Transition.ts
packages/runtime-core/src/renderer.ts
packages/runtime-dom/src/components/CSSTransition.ts
packages/runtime-dom/src/index.ts

index 1d4d3febfca37a83fde853e3b26650f20c97f4f6..e620aaf6d8740f9a8dbf42376e7231a5d43e1af0 100644 (file)
@@ -17,6 +17,12 @@ import { ShapeFlags } from '../shapeFlags'
 export interface TransitionProps {
   mode?: 'in-out' | 'out-in' | 'default'
   appear?: boolean
+  // If true, indicates this is a transition that doesn't actually insert/remove
+  // the element, but toggles the show / hidden status instead.
+  // The transition hooks are injected, but will be skipped by the renderer.
+  // Instead, a custom directive can control the transition by calling the
+  // injected hooks (e.g. v-show).
+  persisted?: boolean
   // enter
   onBeforeEnter?: (el: any) => void
   onEnter?: (el: any, done: () => void) => void
@@ -139,6 +145,7 @@ if (__DEV__) {
   ;(TransitionImpl as ComponentOptions).props = {
     mode: String,
     appear: Boolean,
+    persisted: Boolean,
     // enter
     onBeforeEnter: Function,
     onEnter: Function,
@@ -161,6 +168,7 @@ export const Transition = (TransitionImpl as any) as {
 }
 
 export interface TransitionHooks {
+  persisted: boolean
   beforeEnter(el: object): void
   enter(el: object): void
   leave(el: object, remove: () => void): void
@@ -173,6 +181,7 @@ export interface TransitionHooks {
 function resolveTransitionHooks(
   {
     appear,
+    persisted = false,
     onBeforeEnter,
     onEnter,
     onAfterEnter,
@@ -188,6 +197,7 @@ function resolveTransitionHooks(
   performDelayedLeave: () => void
 ): TransitionHooks {
   return {
+    persisted,
     beforeEnter(el) {
       if (!isMounted && !appear) {
         return
@@ -202,7 +212,10 @@ function resolveTransitionHooks(
       if (!isMounted && !appear) {
         return
       }
+      let called = false
       const afterEnter = (pendingCallbacks.enter = (cancelled?) => {
+        if (called) return
+        called = true
         if (cancelled) {
           callHook(onEnterCancelled, [el])
         } else {
@@ -223,7 +236,10 @@ function resolveTransitionHooks(
         pendingCallbacks.enter(true /* cancelled */)
       }
       callHook(onBeforeLeave, [el])
+      let called = false
       const afterLeave = (pendingCallbacks.leave = (cancelled?) => {
+        if (called) return
+        called = true
         remove()
         if (cancelled) {
           callHook(onLeaveCancelled, [el])
index da3e777cec603a30c2865719f87e74342b6e8ce5..258c70ac2c94909aaf70573c2c3865e82819b474 100644 (file)
@@ -367,7 +367,7 @@ export function createRenderer<
         invokeDirectiveHook(props.onVnodeBeforeMount, parentComponent, vnode)
       }
     }
-    if (transition != null) {
+    if (transition != null && !transition.persisted) {
       transition.beforeEnter(el)
     }
     if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
@@ -385,11 +385,14 @@ export function createRenderer<
     }
     hostInsert(el, container, anchor)
     const vnodeMountedHook = props && props.onVnodeMounted
-    if (vnodeMountedHook != null || transition != null) {
+    if (
+      vnodeMountedHook != null ||
+      (transition != null && !transition.persisted)
+    ) {
       queuePostRenderEffect(() => {
         vnodeMountedHook &&
           invokeDirectiveHook(vnodeMountedHook, parentComponent, vnode)
-        transition && transition.enter(el)
+        transition && !transition.persisted && transition.enter(el)
       }, parentSuspense)
     }
   }
@@ -1468,11 +1471,19 @@ export function createRenderer<
       const remove = () => {
         hostRemove(vnode.el!)
         if (anchor != null) hostRemove(anchor)
-        if (transition != null && transition.afterLeave) {
+        if (
+          transition != null &&
+          !transition.persisted &&
+          transition.afterLeave
+        ) {
           transition.afterLeave()
         }
       }
-      if (vnode.shapeFlag & ShapeFlags.ELEMENT && transition != null) {
+      if (
+        vnode.shapeFlag & ShapeFlags.ELEMENT &&
+        transition != null &&
+        !transition.persisted
+      ) {
         const { leave, delayLeave } = transition
         const performLeave = () => leave(el!, remove)
         if (delayLeave) {
index c3a858f826321f767963bb5c17efd384896e7212..b5436c159b38c50f00d655e3cca3ba90d68250ef 100644 (file)
@@ -6,6 +6,7 @@ import {
   FunctionalComponent
 } from '@vue/runtime-core'
 import { isObject } from '@vue/shared'
+import { currentRenderingInstance } from 'packages/runtime-core/src/componentRenderUtils'
 
 const TRANSITION = 'transition'
 const ANIMATION = 'animation'
@@ -18,12 +19,12 @@ export interface CSSTransitionProps extends TransitionProps {
   enterFromClass?: string
   enterActiveClass?: string
   enterToClass?: string
+  appearFromClass?: string
+  appearActiveClass?: string
+  appearToClass?: string
   leaveFromClass?: string
   leaveActiveClass?: string
   leaveToClass?: string
-  // if present, indicates this is a v-show transition by toggling the
-  // CSS display property instead of actually removing the element.
-  show?: boolean
 }
 
 export const CSSTransition: FunctionalComponent = (
@@ -36,10 +37,13 @@ if (__DEV__) {
     ...(BaseTransition as any).props,
     name: String,
     type: String,
-    enterClass: String,
+    enterFromClass: String,
     enterActiveClass: String,
     enterToClass: String,
-    leaveClass: String,
+    appearFromClass: String,
+    appearActiveClass: String,
+    appearToClass: String,
+    leaveFromClass: String,
     leaveActiveClass: String,
     leaveToClass: String,
     duration: Object
@@ -53,6 +57,9 @@ function resolveCSSTransitionProps({
   enterFromClass = `${name}-enter-from`,
   enterActiveClass = `${name}-enter-active`,
   enterToClass = `${name}-enter-to`,
+  appearFromClass = enterFromClass,
+  appearActiveClass = enterActiveClass,
+  appearToClass = enterToClass,
   leaveFromClass = `${name}-leave-from`,
   leaveActiveClass = `${name}-leave-active`,
   leaveToClass = `${name}-leave-to`,
@@ -61,7 +68,26 @@ function resolveCSSTransitionProps({
   const durations = normalizeDuration(duration)
   const enterDuration = durations && durations[0]
   const leaveDuration = durations && durations[1]
-  const { onBeforeEnter, onEnter, onLeave } = baseProps
+  const { appear, onBeforeEnter, onEnter, onLeave } = baseProps
+
+  // is appearing
+  if (appear && !currentRenderingInstance!.subTree) {
+    enterFromClass = appearFromClass
+    enterActiveClass = appearActiveClass
+    enterToClass = appearToClass
+  }
+
+  function finishEnter(el: Element, done?: () => void) {
+    removeTransitionClass(el, enterToClass)
+    removeTransitionClass(el, enterActiveClass)
+    done && done()
+  }
+
+  function finishLeave(el: Element, done?: () => void) {
+    removeTransitionClass(el, leaveToClass)
+    removeTransitionClass(el, leaveActiveClass)
+    done && done()
+  }
 
   return {
     ...baseProps,
@@ -72,11 +98,7 @@ function resolveCSSTransitionProps({
     },
     onEnter(el, done) {
       nextFrame(() => {
-        const resolve = () => {
-          removeTransitionClass(el, enterToClass)
-          removeTransitionClass(el, enterActiveClass)
-          done()
-        }
+        const resolve = () => finishEnter(el, done)
         onEnter && onEnter(el, resolve)
         removeTransitionClass(el, enterFromClass)
         addTransitionClass(el, enterToClass)
@@ -93,11 +115,7 @@ function resolveCSSTransitionProps({
       addTransitionClass(el, leaveActiveClass)
       addTransitionClass(el, leaveFromClass)
       nextFrame(() => {
-        const resolve = () => {
-          removeTransitionClass(el, leaveToClass)
-          removeTransitionClass(el, leaveActiveClass)
-          done()
-        }
+        const resolve = () => finishLeave(el, done)
         onLeave && onLeave(el, resolve)
         removeTransitionClass(el, leaveFromClass)
         addTransitionClass(el, leaveToClass)
@@ -109,7 +127,9 @@ function resolveCSSTransitionProps({
           }
         }
       })
-    }
+    },
+    onEnterCancelled: finishEnter,
+    onLeaveCancelled: finishLeave
   }
 }
 
@@ -161,9 +181,11 @@ function addTransitionClass(el: ElementWithTransition, cls: string) {
 
 function removeTransitionClass(el: ElementWithTransition, cls: string) {
   el.classList.remove(cls)
-  el._vtc!.delete(cls)
-  if (!el._vtc!.size) {
-    el._vtc = undefined
+  if (el._vtc) {
+    el._vtc.delete(cls)
+    if (!el._vtc!.size) {
+      el._vtc = undefined
+    }
   }
 }
 
index ea736067f6140e6569bce0d13e249b3d1db2a553..bec21093777709e8adb92cdb10f1eeb8811aa9f0 100644 (file)
@@ -66,7 +66,7 @@ export {
 export { withModifiers, withKeys } from './directives/vOn'
 
 // DOM-only components
-export { CSSTransition } from './components/CSSTransition'
+export { CSSTransition, CSSTransitionProps } from './components/CSSTransition'
 
 // re-export everything from core
 // h, Component, reactivity API, nextTick, flags & types