mirror of https://github.com/vuejs/core.git
wip: work with v-show appear
This commit is contained in:
parent
9ee219ab7d
commit
2caeecd595
|
@ -1061,23 +1061,103 @@ describe('vapor transition', () => {
|
||||||
E2E_TIMEOUT,
|
E2E_TIMEOUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
test.todo(
|
test(
|
||||||
'transition on appear with v-show',
|
'transition on appear with v-show',
|
||||||
async () => {
|
async () => {
|
||||||
const btnSelector = '.show-appear > button'
|
const btnSelector = '.show-appear > button'
|
||||||
const containerSelector = '.show-appear > div'
|
const containerSelector = '.show-appear > div'
|
||||||
const childSelector = `${containerSelector} > div`
|
const childSelector = `${containerSelector} > div`
|
||||||
|
|
||||||
|
let calls = await page().evaluate(() => {
|
||||||
|
return (window as any).getCalls('showAppear')
|
||||||
|
})
|
||||||
|
expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
|
||||||
|
|
||||||
|
// appear
|
||||||
|
expect(await classList(childSelector)).contains('test-appear-active')
|
||||||
|
|
||||||
|
await transitionFinish()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
'<div class="test">content</div>',
|
||||||
|
)
|
||||||
|
calls = await page().evaluate(() => {
|
||||||
|
return (window as any).getCalls('showAppear')
|
||||||
|
})
|
||||||
|
expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
|
||||||
|
|
||||||
|
// leave
|
||||||
|
expect(
|
||||||
|
(await transitionStart(btnSelector, childSelector)).classNames,
|
||||||
|
).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
|
||||||
|
await nextFrame()
|
||||||
|
expect(await classList(childSelector)).toStrictEqual([
|
||||||
|
'test',
|
||||||
|
'test-leave-active',
|
||||||
|
'test-leave-to',
|
||||||
|
])
|
||||||
|
await transitionFinish()
|
||||||
|
expect(await isVisible(childSelector)).toBe(false)
|
||||||
|
|
||||||
|
// enter
|
||||||
|
expect(
|
||||||
|
(await transitionStart(btnSelector, childSelector)).classNames,
|
||||||
|
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
|
||||||
|
await nextFrame()
|
||||||
|
expect(await classList(childSelector)).toStrictEqual([
|
||||||
|
'test',
|
||||||
|
'test-enter-active',
|
||||||
|
'test-enter-to',
|
||||||
|
])
|
||||||
|
await transitionFinish()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
'<div class="test" style="">content</div>',
|
||||||
|
)
|
||||||
},
|
},
|
||||||
E2E_TIMEOUT,
|
E2E_TIMEOUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
test.todo(
|
test(
|
||||||
'transition events should not call onEnter with v-show false',
|
'transition events should not call onEnter with v-show false',
|
||||||
async () => {},
|
async () => {
|
||||||
|
const btnSelector = '.show-appear-not-enter > button'
|
||||||
|
const containerSelector = '.show-appear-not-enter > div'
|
||||||
|
const childSelector = `${containerSelector} > div`
|
||||||
|
|
||||||
|
expect(await isVisible(childSelector)).toBe(false)
|
||||||
|
let calls = await page().evaluate(() => {
|
||||||
|
return (window as any).getCalls('notEnter')
|
||||||
|
})
|
||||||
|
expect(calls).toStrictEqual([])
|
||||||
|
|
||||||
|
// enter
|
||||||
|
expect(
|
||||||
|
(await transitionStart(btnSelector, childSelector)).classNames,
|
||||||
|
).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
|
||||||
|
calls = await page().evaluate(() => {
|
||||||
|
return (window as any).getCalls('notEnter')
|
||||||
|
})
|
||||||
|
expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
|
||||||
|
await nextFrame()
|
||||||
|
expect(await classList(childSelector)).toStrictEqual([
|
||||||
|
'test',
|
||||||
|
'test-enter-active',
|
||||||
|
'test-enter-to',
|
||||||
|
])
|
||||||
|
calls = await page().evaluate(() => {
|
||||||
|
return (window as any).getCalls('notEnter')
|
||||||
|
})
|
||||||
|
expect(calls).not.contain('afterEnter')
|
||||||
|
await transitionFinish()
|
||||||
|
expect(await html(containerSelector)).toBe(
|
||||||
|
'<div class="test" style="">content</div>',
|
||||||
|
)
|
||||||
|
calls = await page().evaluate(() => {
|
||||||
|
return (window as any).getCalls('notEnter')
|
||||||
|
})
|
||||||
|
expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
|
||||||
|
},
|
||||||
E2E_TIMEOUT,
|
E2E_TIMEOUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
test.todo('transition on appear with v-show', async () => {}, E2E_TIMEOUT)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('explicit durations', () => {
|
describe('explicit durations', () => {
|
||||||
|
|
|
@ -27,6 +27,7 @@ let calls = {
|
||||||
show: [],
|
show: [],
|
||||||
showLeaveCancel: [],
|
showLeaveCancel: [],
|
||||||
showAppear: [],
|
showAppear: [],
|
||||||
|
notEnter: [],
|
||||||
}
|
}
|
||||||
window.getCalls = key => calls[key]
|
window.getCalls = key => calls[key]
|
||||||
window.resetCalls = key => (calls[key] = [])
|
window.resetCalls = key => (calls[key] = [])
|
||||||
|
@ -398,6 +399,20 @@ function changeViewInOut() {
|
||||||
</div>
|
</div>
|
||||||
<button @click="toggle = !toggle">button</button>
|
<button @click="toggle = !toggle">button</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="show-appear-not-enter">
|
||||||
|
<div>
|
||||||
|
<transition
|
||||||
|
name="test"
|
||||||
|
appear
|
||||||
|
@beforeEnter="() => calls.notEnter.push('beforeEnter')"
|
||||||
|
@enter="() => calls.notEnter.push('onEnter')"
|
||||||
|
@afterEnter="() => calls.notEnter.push('afterEnter')"
|
||||||
|
>
|
||||||
|
<div v-show="!toggle" class="test">content</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
<button @click="toggle = !toggle">button</button>
|
||||||
|
</div>
|
||||||
<!-- work with vshow end -->
|
<!-- work with vshow end -->
|
||||||
|
|
||||||
<!-- explicit durations -->
|
<!-- explicit durations -->
|
||||||
|
|
|
@ -28,6 +28,13 @@ const compileWithElementTransform = makeCompile({
|
||||||
|
|
||||||
describe('compiler: transition', () => {
|
describe('compiler: transition', () => {
|
||||||
test('basic', () => {
|
test('basic', () => {
|
||||||
|
const { code } = compileWithElementTransform(
|
||||||
|
`<Transition><h1 v-show="show">foo</h1></Transition>`,
|
||||||
|
)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('v-show + appear', () => {
|
||||||
const { code } = compileWithElementTransform(
|
const { code } = compileWithElementTransform(
|
||||||
`<Transition appear><h1 v-show="show">foo</h1></Transition>`,
|
`<Transition appear><h1 v-show="show">foo</h1></Transition>`,
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,10 +5,7 @@ exports[`compiler: transition > basic 1`] = `
|
||||||
const t0 = _template("<h1>foo</h1>")
|
const t0 = _template("<h1>foo</h1>")
|
||||||
|
|
||||||
export function render(_ctx) {
|
export function render(_ctx) {
|
||||||
const n1 = _createComponent(_VaporTransition, {
|
const n1 = _createComponent(_VaporTransition, { persisted: () => ("") }, {
|
||||||
appear: () => (""),
|
|
||||||
persisted: () => ("")
|
|
||||||
}, {
|
|
||||||
"default": () => {
|
"default": () => {
|
||||||
const n0 = t0()
|
const n0 = t0()
|
||||||
_applyVShow(n0, () => (_ctx.show))
|
_applyVShow(n0, () => (_ctx.show))
|
||||||
|
@ -72,6 +69,27 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transition > v-show + appear 1`] = `
|
||||||
|
"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, createComponent as _createComponent, template as _template } from 'vue';
|
||||||
|
const t0 = _template("<h1>foo</h1>")
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const lazyApplyVShowFn = []
|
||||||
|
const n1 = _createComponent(_VaporTransition, {
|
||||||
|
appear: () => (""),
|
||||||
|
persisted: () => ("")
|
||||||
|
}, {
|
||||||
|
"default": () => {
|
||||||
|
const n0 = t0()
|
||||||
|
lazyApplyVShowFn.push(() => _applyVShow(n0, () => (_ctx.show)))
|
||||||
|
return n0
|
||||||
|
}
|
||||||
|
}, true)
|
||||||
|
lazyApplyVShowFn.forEach(fn => fn())
|
||||||
|
return n1
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: transition > work with dynamic keyed children 1`] = `
|
exports[`compiler: transition > work with dynamic keyed children 1`] = `
|
||||||
"import { VaporTransition as _VaporTransition, createKeyedFragment as _createKeyedFragment, createComponent as _createComponent, template as _template } from 'vue';
|
"import { VaporTransition as _VaporTransition, createKeyedFragment as _createKeyedFragment, createComponent as _createComponent, template as _template } from 'vue';
|
||||||
const t0 = _template("<h1>foo</h1>")
|
const t0 = _template("<h1>foo</h1>")
|
||||||
|
|
|
@ -44,6 +44,10 @@ export function genBlockContent(
|
||||||
const { dynamic, effect, operation, returns, key } = block
|
const { dynamic, effect, operation, returns, key } = block
|
||||||
const resetBlock = context.enterBlock(block)
|
const resetBlock = context.enterBlock(block)
|
||||||
|
|
||||||
|
if (block.hasLazyApplyVShow) {
|
||||||
|
push(NEWLINE, `const lazyApplyVShowFn = []`)
|
||||||
|
}
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
genResolveAssets('component', 'resolveComponent')
|
genResolveAssets('component', 'resolveComponent')
|
||||||
genResolveAssets('directive', 'resolveDirective')
|
genResolveAssets('directive', 'resolveDirective')
|
||||||
|
@ -56,6 +60,10 @@ export function genBlockContent(
|
||||||
push(...genOperations(operation, context))
|
push(...genOperations(operation, context))
|
||||||
push(...genEffects(effect, context))
|
push(...genEffects(effect, context))
|
||||||
|
|
||||||
|
if (block.hasLazyApplyVShow) {
|
||||||
|
push(NEWLINE, `lazyApplyVShowFn.forEach(fn => fn())`)
|
||||||
|
}
|
||||||
|
|
||||||
if (dynamic.needsKey) {
|
if (dynamic.needsKey) {
|
||||||
for (const child of dynamic.children) {
|
for (const child of dynamic.children) {
|
||||||
const keyValue = key
|
const keyValue = key
|
||||||
|
|
|
@ -7,12 +7,15 @@ export function genVShow(
|
||||||
oper: DirectiveIRNode,
|
oper: DirectiveIRNode,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
): CodeFragment[] {
|
): CodeFragment[] {
|
||||||
|
const { lazy, element } = oper
|
||||||
return [
|
return [
|
||||||
NEWLINE,
|
NEWLINE,
|
||||||
...genCall(context.helper('applyVShow'), `n${oper.element}`, [
|
lazy ? `lazyApplyVShowFn.push(() => ` : undefined,
|
||||||
|
...genCall(context.helper('applyVShow'), `n${element}`, [
|
||||||
`() => (`,
|
`() => (`,
|
||||||
...genExpression(oper.dir.exp!, context),
|
...genExpression(oper.dir.exp!, context),
|
||||||
`)`,
|
`)`,
|
||||||
]),
|
]),
|
||||||
|
lazy ? `)` : undefined,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ export interface BlockIRNode extends BaseIRNode {
|
||||||
operation: OperationNode[]
|
operation: OperationNode[]
|
||||||
expressions: SimpleExpressionNode[]
|
expressions: SimpleExpressionNode[]
|
||||||
returns: number[]
|
returns: number[]
|
||||||
|
hasLazyApplyVShow: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RootIRNode {
|
export interface RootIRNode {
|
||||||
|
@ -187,6 +188,7 @@ export interface DirectiveIRNode extends BaseIRNode {
|
||||||
builtin?: boolean
|
builtin?: boolean
|
||||||
asset?: boolean
|
asset?: boolean
|
||||||
modelType?: 'text' | 'dynamic' | 'radio' | 'checkbox' | 'select'
|
modelType?: 'text' | 'dynamic' | 'radio' | 'checkbox' | 'select'
|
||||||
|
lazy?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateComponentIRNode extends BaseIRNode {
|
export interface CreateComponentIRNode extends BaseIRNode {
|
||||||
|
|
|
@ -31,6 +31,7 @@ export const newBlock = (node: BlockIRNode['node']): BlockIRNode => ({
|
||||||
returns: [],
|
returns: [],
|
||||||
expressions: [],
|
expressions: [],
|
||||||
tempId: 0,
|
tempId: 0,
|
||||||
|
hasLazyApplyVShow: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
export function wrapTemplate(node: ElementNode, dirs: string[]): TemplateNode {
|
export function wrapTemplate(node: ElementNode, dirs: string[]): TemplateNode {
|
||||||
|
|
|
@ -2,11 +2,13 @@ import {
|
||||||
DOMErrorCodes,
|
DOMErrorCodes,
|
||||||
ElementTypes,
|
ElementTypes,
|
||||||
ErrorCodes,
|
ErrorCodes,
|
||||||
|
NodeTypes,
|
||||||
createCompilerError,
|
createCompilerError,
|
||||||
createDOMCompilerError,
|
createDOMCompilerError,
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import type { DirectiveTransform } from '../transform'
|
import type { DirectiveTransform } from '../transform'
|
||||||
import { IRNodeTypes } from '../ir'
|
import { IRNodeTypes } from '../ir'
|
||||||
|
import { findProp, isTransitionTag } from '../utils'
|
||||||
|
|
||||||
export const transformVShow: DirectiveTransform = (dir, node, context) => {
|
export const transformVShow: DirectiveTransform = (dir, node, context) => {
|
||||||
const { exp, loc } = dir
|
const { exp, loc } = dir
|
||||||
|
@ -27,11 +29,26 @@ export const transformVShow: DirectiveTransform = (dir, node, context) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lazy apply vshow if the node is inside a transition with appear
|
||||||
|
let lazyApplyVShow = false
|
||||||
|
const parentNode = context.parent && context.parent.node
|
||||||
|
if (parentNode && parentNode.type === NodeTypes.ELEMENT) {
|
||||||
|
lazyApplyVShow = !!(
|
||||||
|
isTransitionTag(parentNode.tag) &&
|
||||||
|
findProp(parentNode, 'appear', false, true)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (lazyApplyVShow) {
|
||||||
|
context.parent!.parent!.block.hasLazyApplyVShow = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.registerOperation({
|
context.registerOperation({
|
||||||
type: IRNodeTypes.DIRECTIVE,
|
type: IRNodeTypes.DIRECTIVE,
|
||||||
element: context.reference(),
|
element: context.reference(),
|
||||||
dir,
|
dir,
|
||||||
name: 'show',
|
name: 'show',
|
||||||
builtin: true,
|
builtin: true,
|
||||||
|
lazy: lazyApplyVShow,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,9 +52,15 @@ function setDisplay(target: Block, value: unknown): void {
|
||||||
el.style.display = el[vShowOriginalDisplay]!
|
el.style.display = el[vShowOriginalDisplay]!
|
||||||
$transition.enter(target)
|
$transition.enter(target)
|
||||||
} else {
|
} else {
|
||||||
|
// during initial render, the element is not yet inserted into the
|
||||||
|
// DOM, and it is hidden, no need to trigger transition
|
||||||
|
if (target.isConnected) {
|
||||||
$transition.leave(target, () => {
|
$transition.leave(target, () => {
|
||||||
el.style.display = 'none'
|
el.style.display = 'none'
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
el.style.display = 'none'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
el.style.display = value ? el[vShowOriginalDisplay]! : 'none'
|
el.style.display = value ? el[vShowOriginalDisplay]! : 'none'
|
||||||
|
|
Loading…
Reference in New Issue