diff --git a/packages-private/vapor-e2e-test/__tests__/transition.spec.ts b/packages-private/vapor-e2e-test/__tests__/transition.spec.ts
index 0babdd8b8dc..180b0157aaa 100644
--- a/packages-private/vapor-e2e-test/__tests__/transition.spec.ts
+++ b/packages-private/vapor-e2e-test/__tests__/transition.spec.ts
@@ -923,7 +923,54 @@ describe('vapor transition', () => {
})
describe.todo('transition with Suspense', () => {})
- describe.todo('transition with Teleport', () => {})
+
+ describe('transition with Teleport', () => {
+ test(
+ 'apply transition to teleport child',
+ async () => {
+ const btnSelector = '.with-teleport > button'
+ const containerSelector = '.with-teleport > .container'
+ const targetSelector = `.with-teleport > .target`
+
+ await transitionFinish()
+ expect(await html(containerSelector)).toBe('')
+ expect(await html(targetSelector)).toBe('')
+
+ // enter
+ expect(
+ (await transitionStart(btnSelector, `${targetSelector} div`))
+ .classNames,
+ ).toStrictEqual(['test', 'v-enter-from', 'v-enter-active'])
+ await nextFrame()
+ expect(await classList(`${targetSelector} div`)).toStrictEqual([
+ 'test',
+ 'v-enter-active',
+ 'v-enter-to',
+ ])
+ await transitionFinish()
+ expect(await html(targetSelector)).toBe(
+ '
diff --git a/packages/compiler-vapor/src/transforms/vSlot.ts b/packages/compiler-vapor/src/transforms/vSlot.ts
index 05aac4aee3c..57bd1c29eb5 100644
--- a/packages/compiler-vapor/src/transforms/vSlot.ts
+++ b/packages/compiler-vapor/src/transforms/vSlot.ts
@@ -90,12 +90,17 @@ function transformComponentSlot(
let slotKey
if (isTransitionNode(node) && nonSlotTemplateChildren.length) {
- const keyProp = findProp(
- nonSlotTemplateChildren[0] as ElementNode,
- 'key',
- ) as VaporDirectiveNode
- if (keyProp) {
- slotKey = keyProp.exp
+ const nonCommentChild = nonSlotTemplateChildren.find(
+ n => n.type !== NodeTypes.COMMENT,
+ )
+ if (nonCommentChild) {
+ const keyProp = findProp(
+ nonCommentChild as ElementNode,
+ 'key',
+ ) as VaporDirectiveNode
+ if (keyProp) {
+ slotKey = keyProp.exp
+ }
}
}
diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts
index 5b18c465104..c2c2cd1f0c2 100644
--- a/packages/runtime-vapor/src/component.ts
+++ b/packages/runtime-vapor/src/component.ts
@@ -381,10 +381,7 @@ export function setupComponent(
component.inheritAttrs !== false &&
Object.keys(instance.attrs).length
) {
- const el = getRootElement(instance)
- if (el) {
- renderEffect(() => applyFallthroughProps(el, instance.attrs))
- }
+ renderEffect(() => applyFallthroughProps(instance.block, instance.attrs))
}
setActiveSub(prevSub)
@@ -402,9 +399,12 @@ export function applyFallthroughProps(
block: Block,
attrs: Record,
): void {
- isApplyingFallthroughProps = true
- setDynamicProps(block as Element, [attrs])
- isApplyingFallthroughProps = false
+ const el = getRootElement(block)
+ if (el) {
+ isApplyingFallthroughProps = true
+ setDynamicProps(el, [attrs])
+ isApplyingFallthroughProps = false
+ }
}
/**
@@ -761,9 +761,7 @@ export function getExposed(
}
}
-function getRootElement({
- block,
-}: VaporComponentInstance): Element | undefined {
+function getRootElement(block: Block): Element | undefined {
if (block instanceof Element) {
return block
}
diff --git a/packages/runtime-vapor/src/components/Teleport.ts b/packages/runtime-vapor/src/components/Teleport.ts
index ef3d4598c9b..756ff5a6b58 100644
--- a/packages/runtime-vapor/src/components/Teleport.ts
+++ b/packages/runtime-vapor/src/components/Teleport.ts
@@ -29,6 +29,7 @@ import {
runWithoutHydration,
setCurrentHydrationNode,
} from '../dom/hydration'
+import { applyTransitionHooks } from './Transition'
export const VaporTeleportImpl = {
name: 'VaporTeleport',
@@ -122,6 +123,9 @@ export class TeleportFragment extends VaporFragment {
if (!this.parent || isHydrating) return
const mount = (parent: ParentNode, anchor: Node | null) => {
+ if (this.$transition) {
+ applyTransitionHooks(this.nodes, this.$transition)
+ }
insert(
this.nodes,
(this.mountContainer = parent),
diff --git a/packages/runtime-vapor/src/components/Transition.ts b/packages/runtime-vapor/src/components/Transition.ts
index a1f420deae5..6b1580a9221 100644
--- a/packages/runtime-vapor/src/components/Transition.ts
+++ b/packages/runtime-vapor/src/components/Transition.ts
@@ -10,6 +10,7 @@ import {
baseResolveTransitionHooks,
checkTransitionMode,
currentInstance,
+ getComponentName,
isTemplateNode,
leaveCbKey,
queuePostFlushCb,
@@ -33,8 +34,10 @@ import {
setCurrentHydrationNode,
} from '../dom/hydration'
+const displayName = 'VaporTransition'
+
const decorate = (t: typeof VaporTransition) => {
- t.displayName = 'VaporTransition'
+ t.displayName = displayName
t.props = TransitionPropsValidators
t.__vapor = true
return t
@@ -208,8 +211,18 @@ export function applyTransitionHooks(
hooks: VaporTransitionHooks,
fallthroughAttrs: boolean = true,
): VaporTransitionHooks {
+ // filter out comment nodes
+ if (isArray(block)) {
+ block = block.filter(b => !(b instanceof Comment))
+ if (block.length === 1) {
+ block = block[0]
+ } else if (block.length === 0) {
+ return hooks
+ }
+ }
+
const isFrag = isFragment(block)
- const child = findTransitionBlock(block)
+ const child = findTransitionBlock(block, isFrag)
if (!child) {
// set transition hooks on fragment for reusing during it's updating
if (isFrag) setTransitionHooksOnFragment(block, hooks)
@@ -296,35 +309,38 @@ export function findTransitionBlock(
return transitionBlockCache.get(block)
}
- let isFrag = false
let child: TransitionBlock | undefined
if (block instanceof Node) {
// transition can only be applied on Element child
if (block instanceof Element) child = block
} else if (isVaporComponent(block)) {
- child = findTransitionBlock(block.block)
+ // stop searching if encountering nested Transition component
+ if (getComponentName(block.type) === displayName) return undefined
+ child = findTransitionBlock(block.block, inFragment)
// use component id as key
if (child && child.$key === undefined) child.$key = block.uid
} else if (isArray(block)) {
- child = block[0] as TransitionBlock
let hasFound = false
for (const c of block) {
- const item = findTransitionBlock(c)
- if (item instanceof Element) {
- if (__DEV__ && hasFound) {
- // warn more than one non-comment child
- warn(
- ' can only be used on a single element or component. ' +
- 'Use for lists.',
- )
- break
- }
- child = item
- hasFound = true
- if (!__DEV__) break
+ if (c instanceof Comment) continue
+ // check if the child is a fragment to suppress warnings
+ if (isFragment(c)) inFragment = true
+ const item = findTransitionBlock(c, inFragment)
+ if (__DEV__ && hasFound) {
+ // warn more than one non-comment child
+ warn(
+ ' can only be used on a single element or component. ' +
+ 'Use for lists.',
+ )
+ break
}
+ child = item
+ hasFound = true
+ if (!__DEV__) break
}
- } else if ((isFrag = isFragment(block))) {
+ } else if (isFragment(block)) {
+ // mark as in fragment to suppress warnings
+ inFragment = true
if (block.insert) {
child = block
} else {
@@ -332,7 +348,7 @@ export function findTransitionBlock(
}
}
- if (__DEV__ && !child && !inFragment && !isFrag) {
+ if (__DEV__ && !child && !inFragment) {
warn('Transition component has no valid child element')
}
@@ -344,7 +360,7 @@ export function setTransitionHooksOnFragment(
hooks: VaporTransitionHooks,
): void {
if (isFragment(block)) {
- setTransitionHooks(block, hooks)
+ block.$transition = hooks
} else if (isArray(block)) {
for (let i = 0; i < block.length; i++) {
setTransitionHooksOnFragment(block[i], hooks)