diff options
Diffstat (limited to 'node_modules/vue/src/platforms/web/runtime/components/transition.js')
| -rw-r--r-- | node_modules/vue/src/platforms/web/runtime/components/transition.js | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/node_modules/vue/src/platforms/web/runtime/components/transition.js b/node_modules/vue/src/platforms/web/runtime/components/transition.js new file mode 100644 index 00000000..6111d8cb --- /dev/null +++ b/node_modules/vue/src/platforms/web/runtime/components/transition.js @@ -0,0 +1,194 @@ +/* @flow */ + +// Provides transition support for a single element/component. +// supports transition mode (out-in / in-out) + +import { warn } from 'core/util/index' +import { camelize, extend, isPrimitive } from 'shared/util' +import { + mergeVNodeHook, + isAsyncPlaceholder, + getFirstComponentChild +} from 'core/vdom/helpers/index' + +export const transitionProps = { + name: String, + appear: Boolean, + css: Boolean, + mode: String, + type: String, + enterClass: String, + leaveClass: String, + enterToClass: String, + leaveToClass: String, + enterActiveClass: String, + leaveActiveClass: String, + appearClass: String, + appearActiveClass: String, + appearToClass: String, + duration: [Number, String, Object] +} + +// in case the child is also an abstract component, e.g. <keep-alive> +// we want to recursively retrieve the real component to be rendered +function getRealChild (vnode: ?VNode): ?VNode { + const compOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions + if (compOptions && compOptions.Ctor.options.abstract) { + return getRealChild(getFirstComponentChild(compOptions.children)) + } else { + return vnode + } +} + +export function extractTransitionData (comp: Component): Object { + const data = {} + const options: ComponentOptions = comp.$options + // props + for (const key in options.propsData) { + data[key] = comp[key] + } + // events. + // extract listeners and pass them directly to the transition methods + const listeners: ?Object = options._parentListeners + for (const key in listeners) { + data[camelize(key)] = listeners[key] + } + return data +} + +function placeholder (h: Function, rawChild: VNode): ?VNode { + if (/\d-keep-alive$/.test(rawChild.tag)) { + return h('keep-alive', { + props: rawChild.componentOptions.propsData + }) + } +} + +function hasParentTransition (vnode: VNode): ?boolean { + while ((vnode = vnode.parent)) { + if (vnode.data.transition) { + return true + } + } +} + +function isSameChild (child: VNode, oldChild: VNode): boolean { + return oldChild.key === child.key && oldChild.tag === child.tag +} + +export default { + name: 'transition', + props: transitionProps, + abstract: true, + + render (h: Function) { + let children: any = this.$slots.default + if (!children) { + return + } + + // filter out text nodes (possible whitespaces) + children = children.filter((c: VNode) => c.tag || isAsyncPlaceholder(c)) + /* istanbul ignore if */ + if (!children.length) { + return + } + + // warn multiple elements + if (process.env.NODE_ENV !== 'production' && children.length > 1) { + warn( + '<transition> can only be used on a single element. Use ' + + '<transition-group> for lists.', + this.$parent + ) + } + + const mode: string = this.mode + + // warn invalid mode + if (process.env.NODE_ENV !== 'production' && + mode && mode !== 'in-out' && mode !== 'out-in' + ) { + warn( + 'invalid <transition> mode: ' + mode, + this.$parent + ) + } + + const rawChild: VNode = children[0] + + // if this is a component root node and the component's + // parent container node also has transition, skip. + if (hasParentTransition(this.$vnode)) { + return rawChild + } + + // apply transition data to child + // use getRealChild() to ignore abstract components e.g. keep-alive + const child: ?VNode = getRealChild(rawChild) + /* istanbul ignore if */ + if (!child) { + return rawChild + } + + if (this._leaving) { + return placeholder(h, rawChild) + } + + // ensure a key that is unique to the vnode type and to this transition + // component instance. This key will be used to remove pending leaving nodes + // during entering. + const id: string = `__transition-${this._uid}-` + child.key = child.key == null + ? child.isComment + ? id + 'comment' + : id + child.tag + : isPrimitive(child.key) + ? (String(child.key).indexOf(id) === 0 ? child.key : id + child.key) + : child.key + + const data: Object = (child.data || (child.data = {})).transition = extractTransitionData(this) + const oldRawChild: VNode = this._vnode + const oldChild: VNode = getRealChild(oldRawChild) + + // mark v-show + // so that the transition module can hand over the control to the directive + if (child.data.directives && child.data.directives.some(d => d.name === 'show')) { + child.data.show = true + } + + if ( + oldChild && + oldChild.data && + !isSameChild(child, oldChild) && + !isAsyncPlaceholder(oldChild) && + // #6687 component root is a comment node + !(oldChild.componentInstance && oldChild.componentInstance._vnode.isComment) + ) { + // replace old child transition data with fresh one + // important for dynamic transitions! + const oldData: Object = oldChild.data.transition = extend({}, data) + // handle transition mode + if (mode === 'out-in') { + // return placeholder node and queue update when leave finishes + this._leaving = true + mergeVNodeHook(oldData, 'afterLeave', () => { + this._leaving = false + this.$forceUpdate() + }) + return placeholder(h, rawChild) + } else if (mode === 'in-out') { + if (isAsyncPlaceholder(child)) { + return oldRawChild + } + let delayedLeave + const performLeave = () => { delayedLeave() } + mergeVNodeHook(data, 'afterEnter', performLeave) + mergeVNodeHook(data, 'enterCancelled', performLeave) + mergeVNodeHook(oldData, 'delayLeave', leave => { delayedLeave = leave }) + } + } + + return rawChild + } +} |
