diff options
Diffstat (limited to 'node_modules/vue/src/platforms/web/runtime/components')
3 files changed, 381 insertions, 0 deletions
diff --git a/node_modules/vue/src/platforms/web/runtime/components/index.js b/node_modules/vue/src/platforms/web/runtime/components/index.js new file mode 100644 index 00000000..6bfe5780 --- /dev/null +++ b/node_modules/vue/src/platforms/web/runtime/components/index.js @@ -0,0 +1,7 @@ +import Transition from './transition' +import TransitionGroup from './transition-group' + +export default { + Transition, + TransitionGroup +} diff --git a/node_modules/vue/src/platforms/web/runtime/components/transition-group.js b/node_modules/vue/src/platforms/web/runtime/components/transition-group.js new file mode 100644 index 00000000..219ff01c --- /dev/null +++ b/node_modules/vue/src/platforms/web/runtime/components/transition-group.js @@ -0,0 +1,180 @@ +/* @flow */ + +// Provides transition support for list items. +// supports move transitions using the FLIP technique. + +// Because the vdom's children update algorithm is "unstable" - i.e. +// it doesn't guarantee the relative positioning of removed elements, +// we force transition-group to update its children into two passes: +// in the first pass, we remove all nodes that need to be removed, +// triggering their leaving transition; in the second pass, we insert/move +// into the final desired state. This way in the second pass removed +// nodes will remain where they should be. + +import { warn, extend } from 'core/util/index' +import { addClass, removeClass } from '../class-util' +import { transitionProps, extractTransitionData } from './transition' + +import { + hasTransition, + getTransitionInfo, + transitionEndEvent, + addTransitionClass, + removeTransitionClass +} from '../transition-util' + +const props = extend({ + tag: String, + moveClass: String +}, transitionProps) + +delete props.mode + +export default { + props, + + render (h: Function) { + const tag: string = this.tag || this.$vnode.data.tag || 'span' + const map: Object = Object.create(null) + const prevChildren: Array<VNode> = this.prevChildren = this.children + const rawChildren: Array<VNode> = this.$slots.default || [] + const children: Array<VNode> = this.children = [] + const transitionData: Object = extractTransitionData(this) + + for (let i = 0; i < rawChildren.length; i++) { + const c: VNode = rawChildren[i] + if (c.tag) { + if (c.key != null && String(c.key).indexOf('__vlist') !== 0) { + children.push(c) + map[c.key] = c + ;(c.data || (c.data = {})).transition = transitionData + } else if (process.env.NODE_ENV !== 'production') { + const opts: ?VNodeComponentOptions = c.componentOptions + const name: string = opts ? (opts.Ctor.options.name || opts.tag || '') : c.tag + warn(`<transition-group> children must be keyed: <${name}>`) + } + } + } + + if (prevChildren) { + const kept: Array<VNode> = [] + const removed: Array<VNode> = [] + for (let i = 0; i < prevChildren.length; i++) { + const c: VNode = prevChildren[i] + c.data.transition = transitionData + c.data.pos = c.elm.getBoundingClientRect() + if (map[c.key]) { + kept.push(c) + } else { + removed.push(c) + } + } + this.kept = h(tag, null, kept) + this.removed = removed + } + + return h(tag, null, children) + }, + + beforeUpdate () { + // force removing pass + this.__patch__( + this._vnode, + this.kept, + false, // hydrating + true // removeOnly (!important, avoids unnecessary moves) + ) + this._vnode = this.kept + }, + + updated () { + const children: Array<VNode> = this.prevChildren + const moveClass: string = this.moveClass || ((this.name || 'v') + '-move') + if (!children.length || !this.hasMove(children[0].elm, moveClass)) { + return + } + + // we divide the work into three loops to avoid mixing DOM reads and writes + // in each iteration - which helps prevent layout thrashing. + children.forEach(callPendingCbs) + children.forEach(recordPosition) + children.forEach(applyTranslation) + + // force reflow to put everything in position + // assign to this to avoid being removed in tree-shaking + // $flow-disable-line + this._reflow = document.body.offsetHeight + + children.forEach((c: VNode) => { + if (c.data.moved) { + var el: any = c.elm + var s: any = el.style + addTransitionClass(el, moveClass) + s.transform = s.WebkitTransform = s.transitionDuration = '' + el.addEventListener(transitionEndEvent, el._moveCb = function cb (e) { + if (!e || /transform$/.test(e.propertyName)) { + el.removeEventListener(transitionEndEvent, cb) + el._moveCb = null + removeTransitionClass(el, moveClass) + } + }) + } + }) + }, + + methods: { + hasMove (el: any, moveClass: string): boolean { + /* istanbul ignore if */ + if (!hasTransition) { + return false + } + /* istanbul ignore if */ + if (this._hasMove) { + return this._hasMove + } + // Detect whether an element with the move class applied has + // CSS transitions. Since the element may be inside an entering + // transition at this very moment, we make a clone of it and remove + // all other transition classes applied to ensure only the move class + // is applied. + const clone: HTMLElement = el.cloneNode() + if (el._transitionClasses) { + el._transitionClasses.forEach((cls: string) => { removeClass(clone, cls) }) + } + addClass(clone, moveClass) + clone.style.display = 'none' + this.$el.appendChild(clone) + const info: Object = getTransitionInfo(clone) + this.$el.removeChild(clone) + return (this._hasMove = info.hasTransform) + } + } +} + +function callPendingCbs (c: VNode) { + /* istanbul ignore if */ + if (c.elm._moveCb) { + c.elm._moveCb() + } + /* istanbul ignore if */ + if (c.elm._enterCb) { + c.elm._enterCb() + } +} + +function recordPosition (c: VNode) { + c.data.newPos = c.elm.getBoundingClientRect() +} + +function applyTranslation (c: VNode) { + const oldPos = c.data.pos + const newPos = c.data.newPos + const dx = oldPos.left - newPos.left + const dy = oldPos.top - newPos.top + if (dx || dy) { + c.data.moved = true + const s = c.elm.style + s.transform = s.WebkitTransform = `translate(${dx}px,${dy}px)` + s.transitionDuration = '0s' + } +} 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 + } +} |
