Skip to content

Commit ec70485

Browse files
committed
fix(runtime-vapor): track root slot updates in TransitionGroup
1 parent edca540 commit ec70485

4 files changed

Lines changed: 82 additions & 3 deletions

File tree

packages-private/vapor-e2e-test/__tests__/transition-group.spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,44 @@ describe('vapor transition-group', () => {
984984
E2E_TIMEOUT,
985985
)
986986

987+
test(
988+
'root slot component move',
989+
async () => {
990+
const btnSelector = '.root-slot-component-move > button'
991+
const containerSelector = '.root-slot-component-move > div'
992+
993+
await expect
994+
.element(css(containerSelector))
995+
.toContainHTML(
996+
`<div class="test">a</div>` +
997+
`<div class="test">b</div>` +
998+
`<div class="test">c</div>` +
999+
`<!--for--><!--slot--><!--transition-group-->`,
1000+
)
1001+
1002+
click(btnSelector)
1003+
await nextTick()
1004+
await nextFrame()
1005+
expect(html(containerSelector)).toContain(
1006+
`<div class="test group-enter-from group-enter-active">d</div>` +
1007+
`<div class="test">b</div>` +
1008+
`<div class="test group-move" style="">a</div>` +
1009+
`<div class="test group-leave-from group-leave-active group-move" style="">c</div>` +
1010+
`<!--for--><!--slot--><!--transition-group-->`,
1011+
)
1012+
1013+
await transitionFinish()
1014+
await expect
1015+
.element(css(containerSelector))
1016+
.toContainHTML(
1017+
`<div class="test">d</div>` +
1018+
`<div class="test">b</div>` +
1019+
`<div class="test" style="">a</div>`,
1020+
)
1021+
},
1022+
E2E_TIMEOUT,
1023+
)
1024+
9871025
describe('interop', () => {
9881026
test(
9891027
'avoid set transition hooks for comment node',
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script setup vapor>
2+
import { ref } from 'vue'
3+
import RootSlot from '../../components/RootSlot.vue'
4+
5+
const items = ref(['a', 'b', 'c'])
6+
const moveClick = () => (items.value = ['d', 'b', 'a'])
7+
</script>
8+
9+
<template>
10+
<div class="root-slot-component-move">
11+
<button @click="moveClick">root slot button</button>
12+
<div>
13+
<transition-group name="group">
14+
<RootSlot>
15+
<div v-for="item in items" :key="item" class="test">{{ item }}</div>
16+
</RootSlot>
17+
</transition-group>
18+
</div>
19+
</div>
20+
</template>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script setup vapor></script>
2+
3+
<template>
4+
<slot />
5+
</template>

packages/runtime-vapor/src/components/TransitionGroup.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ import {
4242
import { resolveDynamicProps } from '../componentProps'
4343
import { isForBlock, setForHydrationAnchorResolver } from '../apiCreateFor'
4444
import { createComment, createElement, createTextNode } from '../dom/node'
45-
import { DynamicFragment, type VaporFragment, isFragment } from '../fragment'
45+
import {
46+
DynamicFragment,
47+
SlotFragment,
48+
type VaporFragment,
49+
isFragment,
50+
} from '../fragment'
4651
import {
4752
type DefineVaporComponent,
4853
defineVaporComponent,
@@ -394,8 +399,19 @@ function getTransitionBlocks(
394399
if (block instanceof Element) {
395400
children.push(block)
396401
} else if (isVaporComponent(block)) {
397-
if (onUpdateOwner) onUpdateOwner(block)
398-
const blocks = getTransitionBlocks(block.block, onFragment, onUpdateOwner)
402+
// A normal component child can move when parent-driven props update its
403+
// root layout without re-running the surrounding v-for fragment.
404+
// When the component root is a slot, the TransitionGroup children are the
405+
// slotted blocks, so track the SlotFragment instead of the component.
406+
const isRootSlot = block.block instanceof SlotFragment
407+
if (onUpdateOwner && !isRootSlot) onUpdateOwner(block)
408+
const blocks = getTransitionBlocks(
409+
block.block,
410+
onFragment,
411+
// Only a root slot exposes nested blocks as TransitionGroup children.
412+
// Other component internals should not trigger group move bookkeeping.
413+
isRootSlot ? onUpdateOwner : undefined,
414+
)
399415
inheritKey(blocks, block.$key)
400416
children.push(...blocks)
401417
} else if (isArray(block)) {

0 commit comments

Comments
 (0)