From be9b4b252723bac5582630b72188eb00d97606cc Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 22 Nov 2019 15:31:55 -0500 Subject: [PATCH] feat(transition): handle transition classes when patching classes --- .../src/components/CSSTransition.ts | 45 ++++++++++++++----- packages/runtime-dom/src/modules/class.ts | 14 +++++- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/packages/runtime-dom/src/components/CSSTransition.ts b/packages/runtime-dom/src/components/CSSTransition.ts index ae4107b5..e7a24e88 100644 --- a/packages/runtime-dom/src/components/CSSTransition.ts +++ b/packages/runtime-dom/src/components/CSSTransition.ts @@ -64,19 +64,19 @@ function resolveCSSTransitionData({ ...baseProps, onBeforeEnter(el) { onBeforeEnter && onBeforeEnter(el) - el.classList.add(enterActiveClass) - el.classList.add(enterFromClass) + addTransitionClass(el, enterActiveClass) + addTransitionClass(el, enterFromClass) }, onEnter(el, done) { nextFrame(() => { const resolve = () => { - el.classList.remove(enterToClass) - el.classList.remove(enterActiveClass) + removeTransitionClass(el, enterToClass) + removeTransitionClass(el, enterActiveClass) done() } onEnter && onEnter(el, resolve) - el.classList.remove(enterFromClass) - el.classList.add(enterToClass) + removeTransitionClass(el, enterFromClass) + addTransitionClass(el, enterToClass) if (!(onEnter && onEnter.length > 1)) { if (enterDuration) { setTimeout(resolve, enterDuration) @@ -87,17 +87,17 @@ function resolveCSSTransitionData({ }) }, onLeave(el, done) { - el.classList.add(leaveActiveClass) - el.classList.add(leaveFromClass) + addTransitionClass(el, leaveActiveClass) + addTransitionClass(el, leaveFromClass) nextFrame(() => { const resolve = () => { - el.classList.remove(leaveToClass) - el.classList.remove(leaveActiveClass) + removeTransitionClass(el, leaveToClass) + removeTransitionClass(el, leaveActiveClass) done() } onLeave && onLeave(el, resolve) - el.classList.remove(leaveFromClass) - el.classList.add(leaveToClass) + removeTransitionClass(el, leaveFromClass) + addTransitionClass(el, leaveToClass) if (!(onLeave && onLeave.length > 1)) { if (leaveDuration) { setTimeout(resolve, leaveDuration) @@ -143,6 +143,27 @@ function validateDuration(val: unknown) { } } +export interface ElementWithTransition extends Element { + // _vtc = Vue Transition Classes. + // Store the temporarily-added transition classes on the element + // so that we can avoid overwriting them if the element's class is patched + // during the transition. + _vtc?: Set +} + +function addTransitionClass(el: ElementWithTransition, cls: string) { + el.classList.add(cls) + ;(el._vtc || (el._vtc = new Set())).add(cls) +} + +function removeTransitionClass(el: ElementWithTransition, cls: string) { + el.classList.remove(cls) + el._vtc!.delete(cls) + if (!el._vtc!.size) { + el._vtc = undefined + } +} + function nextFrame(cb: () => void) { requestAnimationFrame(() => { requestAnimationFrame(cb) diff --git a/packages/runtime-dom/src/modules/class.ts b/packages/runtime-dom/src/modules/class.ts index fb9349f2..9641a952 100644 --- a/packages/runtime-dom/src/modules/class.ts +++ b/packages/runtime-dom/src/modules/class.ts @@ -1,7 +1,17 @@ +import { ElementWithTransition } from '../components/CSSTransition' + // compiler should normalize class + :class bindings on the same element // into a single binding ['staticClass', dynamic] - -export function patchClass(el: Element, value: string, isSVG: boolean) { +export function patchClass( + el: ElementWithTransition, + value: string, + isSVG: boolean +) { + // if this is an element during a transition, take the temporary transition + // classes into account. + if (el._vtc) { + value = [value, ...el._vtc].join(' ') + } // directly setting className should be faster than setAttribute in theory if (isSVG) { el.setAttribute('class', value)