transition-mode

This commit is contained in:
Evan You 2016-06-03 19:32:45 -04:00
parent 8015100d77
commit 7a3261115f
8 changed files with 156 additions and 45 deletions

View File

@ -15,8 +15,9 @@ declare type CompilerOptions = {
declare type ModuleOptions = {
staticKeys?: Array<string>,
parse: Function,
genData: Function
parse: (el: ASTElement) => void,
genData: (el: ASTElement) => string,
transformElement?: (el: ASTElement, code: string) => string
}
declare type ASTElementHandler = {
@ -59,7 +60,9 @@ declare type ASTElement = {
ns?: string,
component?: string,
keepAlive?: boolean,
inlineTemplate?: true,
transitionMode?: string | null,
slotName?: ?string,
slotTarget?: ?string,

View File

@ -47,29 +47,39 @@ function genElement (el: ASTElement): string {
return genRender(el)
} else if (el.tag === 'slot') {
return genSlot(el)
} else if (el.component) {
return genComponent(el)
} else {
const data = genData(el)
// if the element is potentially a component,
// wrap its children as a thunk.
const children = !el.inlineTemplate
? genChildren(el, !el.ns && !isPlatformReservedTag(el.tag) /* asThunk */)
: null
const code = `_h(_e('${el.tag}'${
data ? `,${data}` : el.ns ? ',void 0' : '' // data
}${
el.ns ? `,'${el.ns}'` : '' // namespace
})${
children ? `,${children}` : '' // children
})`
if (el.staticRoot) {
// hoist static sub-trees out
staticRenderFns.push(`with(this){return ${code}}`)
return `_m(${staticRenderFns.length - 1})`
// component or element
let code
if (el.component) {
code = genComponent(el)
} else {
return code
const data = genData(el)
// if the element is potentially a component,
// wrap its children as a thunk.
const children = !el.inlineTemplate
? genChildren(el, !el.ns && !isPlatformReservedTag(el.tag) /* asThunk */)
: null
code = `_h(_e('${el.tag}'${
data ? `,${data}` : el.ns ? ',void 0' : '' // data
}${
el.ns ? `,'${el.ns}'` : '' // namespace
})${
children ? `,${children}` : '' // children
})`
if (el.staticRoot) {
// hoist static sub-trees out
staticRenderFns.push(`with(this){return ${code}}`)
code = `_m(${staticRenderFns.length - 1})`
}
}
// platform modules
for (let i = 0; i < platformModules.length; i++) {
const transform = platformModules[i].transformElement
if (transform) {
code = transform(el, code)
}
}
return code
}
}

View File

@ -313,9 +313,12 @@ function processSlot (el) {
}
function processComponent (el) {
const isBinding = getBindingAttr(el, 'is')
if (isBinding) {
el.component = isBinding
let binding
if ((binding = getBindingAttr(el, 'is'))) {
el.component = binding
}
if (getAndRemoveAttr(el, 'keep-alive') != null) {
el.keepAlive = true
}
if (getAndRemoveAttr(el, 'inline-template') != null) {
el.inlineTemplate = true

View File

@ -2,9 +2,10 @@
import Vue from 'core/index'
import config from 'core/config'
import { noop } from 'shared/util'
import { extend, noop } from 'shared/util'
import { patch } from 'web/runtime/patch'
import platformDirectives from 'web/runtime/directives/index'
import platformComponents from 'web/runtime/components/index'
import { query, isUnknownElement, isReservedTag, mustUseProp } from 'web/util/index'
// install platform specific utils
@ -12,8 +13,9 @@ Vue.config.isUnknownElement = isUnknownElement
Vue.config.isReservedTag = isReservedTag
Vue.config.mustUseProp = mustUseProp
// install platform runtime directives
Vue.options.directives = platformDirectives
// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)
// install platform patch function
Vue.prototype.__patch__ = config._isServer ? noop : patch

View File

@ -12,6 +12,10 @@ function parse (el: ASTElement) {
if (transition) {
el.transition = transition
}
const mode = getBindingAttr(el, 'transition-mode')
if (mode) {
el.transitionMode = mode
}
}
function genData (el: ASTElement): string {
@ -20,7 +24,16 @@ function genData (el: ASTElement): string {
: ''
}
function transformElement (el: ASTElement, code: string): string {
return el.transitionMode
? `_h(_e('transition-control',{props:{mode:${
el.transitionMode
}}}),function(){return [${code}]})`
: code
}
export default {
parse,
genData
genData,
transformElement
}

View File

@ -0,0 +1,5 @@
import transitionControl from './transition-control'
export default {
transitionControl
}

View File

@ -0,0 +1,66 @@
/* flow */
import { warn } from 'core/util/index'
export default {
props: {
mode: {
validator (val) {
if (val && val !== 'out-in' && val !== 'in-out') {
warn('transition-mode must be either "out-in" or "in-out".')
return false
}
return true
}
}
},
render () {
const oldChild = this._vnode
const newChild = this.$slots.default[0]
if (oldChild && oldChild.data && oldChild.tag !== newChild.tag) {
if (this.mode === 'out-in') {
// return empty node
// and queue an update when the leave finishes
addHook(oldChild, 'afterLeave', () => {
this.$forceUpdate()
})
return
} else {
if (this.mode === 'in-out') {
let delayedLeave
const performLeave = () => { delayedLeave() }
addHook(newChild, 'afterEnter', performLeave)
addHook(newChild, 'enterCancelled', performLeave)
addHook(oldChild, 'delayLeave', leave => {
delayedLeave = leave
})
}
return newChild
}
} else {
return newChild
}
}
}
function addHook (vnode: VNode, name: string, hook: Function) {
if (!vnode.data || !vnode.data.transition) {
return
}
let trans = vnode.data.transition
if (typeof trans === 'string') {
trans = vnode.data.transition = { name: trans }
} else if (typeof trans !== 'object') {
trans = vnode.data.transition = { name: 'v' }
}
if (trans[name]) {
const oldHook = trans[name]
trans[name] = function (el) {
const res = oldHook.apply(this, arguments)
hook()
return res
}
} else {
trans[name] = hook
}
}

View File

@ -129,7 +129,8 @@ export function leave (vnode: VNodeWithData, rm: Function) {
beforeLeave,
onLeave,
afterLeave,
leaveCancelled
leaveCancelled,
delayLeave
} = resolveTransition(data, vnode.context)
const expectsCSS = css !== false
@ -150,22 +151,30 @@ export function leave (vnode: VNodeWithData, rm: Function) {
el._leaveCb = null
})
beforeLeave && beforeLeave(el)
if (expectsCSS) {
addTransitionClass(el, leaveClass)
nextFrame(() => {
removeTransitionClass(el, leaveClass)
if (!cb.cancelled) {
addTransitionClass(el, leaveActiveClass)
if (!userWantsControl) {
whenTransitionEnds(el, cb)
}
}
})
if (delayLeave) {
delayLeave(performLeave)
} else {
performLeave()
}
onLeave && onLeave(el, cb)
if (!expectsCSS && !userWantsControl) {
cb()
function performLeave () {
beforeLeave && beforeLeave(el)
if (expectsCSS) {
addTransitionClass(el, leaveClass)
nextFrame(() => {
removeTransitionClass(el, leaveClass)
if (!cb.cancelled) {
addTransitionClass(el, leaveActiveClass)
if (!userWantsControl) {
whenTransitionEnds(el, cb)
}
}
})
}
onLeave && onLeave(el, cb)
if (!expectsCSS && !userWantsControl) {
cb()
}
}
}