Skip to content

Commit 2caeecd

Browse files
committed
wip: work with v-show appear
1 parent 9ee219a commit 2caeecd

File tree

10 files changed

+169
-12
lines changed

10 files changed

+169
-12
lines changed

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

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,23 +1061,103 @@ describe('vapor transition', () => {
10611061
E2E_TIMEOUT,
10621062
)
10631063

1064-
test.todo(
1064+
test(
10651065
'transition on appear with v-show',
10661066
async () => {
10671067
const btnSelector = '.show-appear > button'
10681068
const containerSelector = '.show-appear > div'
10691069
const childSelector = `${containerSelector} > div`
1070+
1071+
let calls = await page().evaluate(() => {
1072+
return (window as any).getCalls('showAppear')
1073+
})
1074+
expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
1075+
1076+
// appear
1077+
expect(await classList(childSelector)).contains('test-appear-active')
1078+
1079+
await transitionFinish()
1080+
expect(await html(containerSelector)).toBe(
1081+
'<div class="test">content</div>',
1082+
)
1083+
calls = await page().evaluate(() => {
1084+
return (window as any).getCalls('showAppear')
1085+
})
1086+
expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
1087+
1088+
// leave
1089+
expect(
1090+
(await transitionStart(btnSelector, childSelector)).classNames,
1091+
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
1092+
await nextFrame()
1093+
expect(await classList(childSelector)).toStrictEqual([
1094+
'test',
1095+
'test-leave-active',
1096+
'test-leave-to',
1097+
])
1098+
await transitionFinish()
1099+
expect(await isVisible(childSelector)).toBe(false)
1100+
1101+
// enter
1102+
expect(
1103+
(await transitionStart(btnSelector, childSelector)).classNames,
1104+
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
1105+
await nextFrame()
1106+
expect(await classList(childSelector)).toStrictEqual([
1107+
'test',
1108+
'test-enter-active',
1109+
'test-enter-to',
1110+
])
1111+
await transitionFinish()
1112+
expect(await html(containerSelector)).toBe(
1113+
'<div class="test" style="">content</div>',
1114+
)
10701115
},
10711116
E2E_TIMEOUT,
10721117
)
10731118

1074-
test.todo(
1119+
test(
10751120
'transition events should not call onEnter with v-show false',
1076-
async () => {},
1121+
async () => {
1122+
const btnSelector = '.show-appear-not-enter > button'
1123+
const containerSelector = '.show-appear-not-enter > div'
1124+
const childSelector = `${containerSelector} > div`
1125+
1126+
expect(await isVisible(childSelector)).toBe(false)
1127+
let calls = await page().evaluate(() => {
1128+
return (window as any).getCalls('notEnter')
1129+
})
1130+
expect(calls).toStrictEqual([])
1131+
1132+
// enter
1133+
expect(
1134+
(await transitionStart(btnSelector, childSelector)).classNames,
1135+
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
1136+
calls = await page().evaluate(() => {
1137+
return (window as any).getCalls('notEnter')
1138+
})
1139+
expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
1140+
await nextFrame()
1141+
expect(await classList(childSelector)).toStrictEqual([
1142+
'test',
1143+
'test-enter-active',
1144+
'test-enter-to',
1145+
])
1146+
calls = await page().evaluate(() => {
1147+
return (window as any).getCalls('notEnter')
1148+
})
1149+
expect(calls).not.contain('afterEnter')
1150+
await transitionFinish()
1151+
expect(await html(containerSelector)).toBe(
1152+
'<div class="test" style="">content</div>',
1153+
)
1154+
calls = await page().evaluate(() => {
1155+
return (window as any).getCalls('notEnter')
1156+
})
1157+
expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
1158+
},
10771159
E2E_TIMEOUT,
10781160
)
1079-
1080-
test.todo('transition on appear with v-show', async () => {}, E2E_TIMEOUT)
10811161
})
10821162

10831163
describe('explicit durations', () => {

packages-private/vapor-e2e-test/transition/App.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ let calls = {
2727
show: [],
2828
showLeaveCancel: [],
2929
showAppear: [],
30+
notEnter: [],
3031
}
3132
window.getCalls = key => calls[key]
3233
window.resetCalls = key => (calls[key] = [])
@@ -398,6 +399,20 @@ function changeViewInOut() {
398399
</div>
399400
<button @click="toggle = !toggle">button</button>
400401
</div>
402+
<div class="show-appear-not-enter">
403+
<div>
404+
<transition
405+
name="test"
406+
appear
407+
@beforeEnter="() => calls.notEnter.push('beforeEnter')"
408+
@enter="() => calls.notEnter.push('onEnter')"
409+
@afterEnter="() => calls.notEnter.push('afterEnter')"
410+
>
411+
<div v-show="!toggle" class="test">content</div>
412+
</transition>
413+
</div>
414+
<button @click="toggle = !toggle">button</button>
415+
</div>
401416
<!-- work with vshow end -->
402417

403418
<!-- explicit durations -->

packages/compiler-vapor/__tests__/transforms/TransformTransition.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ const compileWithElementTransform = makeCompile({
2828

2929
describe('compiler: transition', () => {
3030
test('basic', () => {
31+
const { code } = compileWithElementTransform(
32+
`<Transition><h1 v-show="show">foo</h1></Transition>`,
33+
)
34+
expect(code).toMatchSnapshot()
35+
})
36+
37+
test('v-show + appear', () => {
3138
const { code } = compileWithElementTransform(
3239
`<Transition appear><h1 v-show="show">foo</h1></Transition>`,
3340
)

packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ exports[`compiler: transition > basic 1`] = `
55
const t0 = _template("<h1>foo</h1>")
66
77
export function render(_ctx) {
8-
const n1 = _createComponent(_VaporTransition, {
9-
appear: () => (""),
10-
persisted: () => ("")
11-
}, {
8+
const n1 = _createComponent(_VaporTransition, { persisted: () => ("") }, {
129
"default": () => {
1310
const n0 = t0()
1411
_applyVShow(n0, () => (_ctx.show))
@@ -72,6 +69,27 @@ export function render(_ctx) {
7269
}"
7370
`;
7471

72+
exports[`compiler: transition > v-show + appear 1`] = `
73+
"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, createComponent as _createComponent, template as _template } from 'vue';
74+
const t0 = _template("<h1>foo</h1>")
75+
76+
export function render(_ctx) {
77+
const lazyApplyVShowFn = []
78+
const n1 = _createComponent(_VaporTransition, {
79+
appear: () => (""),
80+
persisted: () => ("")
81+
}, {
82+
"default": () => {
83+
const n0 = t0()
84+
lazyApplyVShowFn.push(() => _applyVShow(n0, () => (_ctx.show)))
85+
return n0
86+
}
87+
}, true)
88+
lazyApplyVShowFn.forEach(fn => fn())
89+
return n1
90+
}"
91+
`;
92+
7593
exports[`compiler: transition > work with dynamic keyed children 1`] = `
7694
"import { VaporTransition as _VaporTransition, createKeyedFragment as _createKeyedFragment, createComponent as _createComponent, template as _template } from 'vue';
7795
const t0 = _template("<h1>foo</h1>")

packages/compiler-vapor/src/generators/block.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ export function genBlockContent(
4444
const { dynamic, effect, operation, returns, key } = block
4545
const resetBlock = context.enterBlock(block)
4646

47+
if (block.hasLazyApplyVShow) {
48+
push(NEWLINE, `const lazyApplyVShowFn = []`)
49+
}
50+
4751
if (root) {
4852
genResolveAssets('component', 'resolveComponent')
4953
genResolveAssets('directive', 'resolveDirective')
@@ -56,6 +60,10 @@ export function genBlockContent(
5660
push(...genOperations(operation, context))
5761
push(...genEffects(effect, context))
5862

63+
if (block.hasLazyApplyVShow) {
64+
push(NEWLINE, `lazyApplyVShowFn.forEach(fn => fn())`)
65+
}
66+
5967
if (dynamic.needsKey) {
6068
for (const child of dynamic.children) {
6169
const keyValue = key

packages/compiler-vapor/src/generators/vShow.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ export function genVShow(
77
oper: DirectiveIRNode,
88
context: CodegenContext,
99
): CodeFragment[] {
10+
const { lazy, element } = oper
1011
return [
1112
NEWLINE,
12-
...genCall(context.helper('applyVShow'), `n${oper.element}`, [
13+
lazy ? `lazyApplyVShowFn.push(() => ` : undefined,
14+
...genCall(context.helper('applyVShow'), `n${element}`, [
1315
`() => (`,
1416
...genExpression(oper.dir.exp!, context),
1517
`)`,
1618
]),
19+
lazy ? `)` : undefined,
1720
]
1821
}

packages/compiler-vapor/src/ir/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export interface BlockIRNode extends BaseIRNode {
5656
operation: OperationNode[]
5757
expressions: SimpleExpressionNode[]
5858
returns: number[]
59+
hasLazyApplyVShow: boolean
5960
}
6061

6162
export interface RootIRNode {
@@ -187,6 +188,7 @@ export interface DirectiveIRNode extends BaseIRNode {
187188
builtin?: boolean
188189
asset?: boolean
189190
modelType?: 'text' | 'dynamic' | 'radio' | 'checkbox' | 'select'
191+
lazy?: boolean
190192
}
191193

192194
export interface CreateComponentIRNode extends BaseIRNode {

packages/compiler-vapor/src/transforms/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const newBlock = (node: BlockIRNode['node']): BlockIRNode => ({
3131
returns: [],
3232
expressions: [],
3333
tempId: 0,
34+
hasLazyApplyVShow: false,
3435
})
3536

3637
export function wrapTemplate(node: ElementNode, dirs: string[]): TemplateNode {

packages/compiler-vapor/src/transforms/vShow.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import {
22
DOMErrorCodes,
33
ElementTypes,
44
ErrorCodes,
5+
NodeTypes,
56
createCompilerError,
67
createDOMCompilerError,
78
} from '@vue/compiler-dom'
89
import type { DirectiveTransform } from '../transform'
910
import { IRNodeTypes } from '../ir'
11+
import { findProp, isTransitionTag } from '../utils'
1012

1113
export const transformVShow: DirectiveTransform = (dir, node, context) => {
1214
const { exp, loc } = dir
@@ -27,11 +29,26 @@ export const transformVShow: DirectiveTransform = (dir, node, context) => {
2729
return
2830
}
2931

32+
// lazy apply vshow if the node is inside a transition with appear
33+
let lazyApplyVShow = false
34+
const parentNode = context.parent && context.parent.node
35+
if (parentNode && parentNode.type === NodeTypes.ELEMENT) {
36+
lazyApplyVShow = !!(
37+
isTransitionTag(parentNode.tag) &&
38+
findProp(parentNode, 'appear', false, true)
39+
)
40+
41+
if (lazyApplyVShow) {
42+
context.parent!.parent!.block.hasLazyApplyVShow = true
43+
}
44+
}
45+
3046
context.registerOperation({
3147
type: IRNodeTypes.DIRECTIVE,
3248
element: context.reference(),
3349
dir,
3450
name: 'show',
3551
builtin: true,
52+
lazy: lazyApplyVShow,
3653
})
3754
}

packages/runtime-vapor/src/directives/vShow.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,15 @@ function setDisplay(target: Block, value: unknown): void {
5252
el.style.display = el[vShowOriginalDisplay]!
5353
$transition.enter(target)
5454
} else {
55-
$transition.leave(target, () => {
55+
// during initial render, the element is not yet inserted into the
56+
// DOM, and it is hidden, no need to trigger transition
57+
if (target.isConnected) {
58+
$transition.leave(target, () => {
59+
el.style.display = 'none'
60+
})
61+
} else {
5662
el.style.display = 'none'
57-
})
63+
}
5864
}
5965
} else {
6066
el.style.display = value ? el[vShowOriginalDisplay]! : 'none'

0 commit comments

Comments
 (0)