Skip to content

Commit 41822e3

Browse files
committed
feat(vapor): vapor transition
1 parent e1d26b1 commit 41822e3

File tree

12 files changed

+224
-54
lines changed

12 files changed

+224
-54
lines changed

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,12 @@ export function genCreateComponent(
5252
const [ids, handlers] = processInlineHandlers(props, context)
5353
const rawProps = context.withId(() => genRawProps(props, context), ids)
5454
const inlineHandlers: CodeFragment[] = handlers.reduce<CodeFragment[]>(
55-
(acc, { name, value }) => {
55+
(acc, { name, value }: InlineHandler) => {
5656
const handler = genEventHandler(context, value, undefined, false)
5757
return [...acc, `const ${name} = `, ...handler, NEWLINE]
5858
},
5959
[],
6060
)
61-
6261
return [
6362
NEWLINE,
6463
...inlineHandlers,

packages/runtime-core/src/components/BaseTransition.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
type ComponentInternalInstance,
33
type ComponentOptions,
4+
type GenericComponentInstance,
45
type SetupContext,
56
getCurrentInstance,
67
} from '../component'
@@ -324,7 +325,7 @@ export function resolveTransitionHooks(
324325
vnode: VNode,
325326
props: BaseTransitionProps<any>,
326327
state: TransitionState,
327-
instance: ComponentInternalInstance,
328+
instance: GenericComponentInstance,
328329
postClone?: (hooks: TransitionHooks) => void,
329330
): TransitionHooks {
330331
const {

packages/runtime-core/src/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -557,3 +557,7 @@ export { startMeasure, endMeasure } from './profiling'
557557
* @internal
558558
*/
559559
export { initFeatureFlags } from './featureFlags'
560+
/**
561+
* @internal
562+
*/
563+
export { applyTransitionEnter, applyTransitionLeave } from './renderer'

packages/runtime-core/src/renderer.ts

+66-33
Original file line numberDiff line numberDiff line change
@@ -731,19 +731,20 @@ function baseCreateRenderer(
731731
}
732732
// #1583 For inside suspense + suspense not resolved case, enter hook should call when suspense resolved
733733
// #1689 For inside suspense + suspense resolved case, just call it
734-
const needCallTransitionHooks = needTransition(parentSuspense, transition)
735-
if (needCallTransitionHooks) {
736-
transition!.beforeEnter(el)
734+
if (transition) {
735+
applyTransitionEnter(
736+
el,
737+
transition,
738+
() => hostInsert(el, container, anchor),
739+
parentSuspense,
740+
)
741+
} else {
742+
hostInsert(el, container, anchor)
737743
}
738-
hostInsert(el, container, anchor)
739-
if (
740-
(vnodeHook = props && props.onVnodeMounted) ||
741-
needCallTransitionHooks ||
742-
dirs
743-
) {
744+
745+
if ((vnodeHook = props && props.onVnodeMounted) || dirs) {
744746
queuePostRenderEffect(() => {
745747
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
746-
needCallTransitionHooks && transition!.enter(el)
747748
dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted')
748749
}, parentSuspense)
749750
}
@@ -2115,9 +2116,12 @@ function baseCreateRenderer(
21152116
transition
21162117
if (needTransition) {
21172118
if (moveType === MoveType.ENTER) {
2118-
transition!.beforeEnter(el!)
2119-
hostInsert(el!, container, anchor)
2120-
queuePostRenderEffect(() => transition!.enter(el!), parentSuspense)
2119+
applyTransitionEnter(
2120+
el!,
2121+
transition,
2122+
() => hostInsert(el!, container, anchor),
2123+
parentSuspense,
2124+
)
21212125
} else {
21222126
const { leave, delayLeave, afterLeave } = transition!
21232127
const remove = () => hostInsert(el!, container, anchor)
@@ -2292,27 +2296,15 @@ function baseCreateRenderer(
22922296
return
22932297
}
22942298

2295-
const performRemove = () => {
2296-
hostRemove(el!)
2297-
if (transition && !transition.persisted && transition.afterLeave) {
2298-
transition.afterLeave()
2299-
}
2300-
}
2301-
2302-
if (
2303-
vnode.shapeFlag & ShapeFlags.ELEMENT &&
2304-
transition &&
2305-
!transition.persisted
2306-
) {
2307-
const { leave, delayLeave } = transition
2308-
const performLeave = () => leave(el!, performRemove)
2309-
if (delayLeave) {
2310-
delayLeave(vnode.el!, performRemove, performLeave)
2311-
} else {
2312-
performLeave()
2313-
}
2299+
if (transition) {
2300+
applyTransitionLeave(
2301+
el!,
2302+
transition,
2303+
() => hostRemove(el!),
2304+
!!(vnode.shapeFlag & ShapeFlags.ELEMENT),
2305+
)
23142306
} else {
2315-
performRemove()
2307+
hostRemove(el!)
23162308
}
23172309
}
23182310

@@ -2630,6 +2622,47 @@ export function invalidateMount(hooks: LifecycleHook | undefined): void {
26302622
}
26312623
}
26322624

2625+
export function applyTransitionEnter(
2626+
el: RendererElement,
2627+
transition: TransitionHooks,
2628+
insert: () => void,
2629+
parentSuspense: SuspenseBoundary | null,
2630+
): void {
2631+
if (needTransition(parentSuspense, transition)) {
2632+
transition.beforeEnter(el)
2633+
insert()
2634+
queuePostRenderEffect(() => transition.enter(el), parentSuspense)
2635+
} else {
2636+
insert()
2637+
}
2638+
}
2639+
2640+
export function applyTransitionLeave(
2641+
el: RendererElement,
2642+
transition: TransitionHooks,
2643+
remove: () => void,
2644+
isElement: boolean = true,
2645+
): void {
2646+
const performRemove = () => {
2647+
remove()
2648+
if (transition && !transition.persisted && transition.afterLeave) {
2649+
transition.afterLeave()
2650+
}
2651+
}
2652+
2653+
if (isElement && transition && !transition.persisted) {
2654+
const { leave, delayLeave } = transition
2655+
const performLeave = () => leave(el, performRemove)
2656+
if (delayLeave) {
2657+
delayLeave(el, performRemove, performLeave)
2658+
} else {
2659+
performLeave()
2660+
}
2661+
} else {
2662+
performRemove()
2663+
}
2664+
}
2665+
26332666
function getVaporInterface(
26342667
instance: ComponentInternalInstance | null,
26352668
vnode: VNode,

packages/runtime-dom/src/components/Transition.ts

+21-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ export interface TransitionProps extends BaseTransitionProps<Element> {
3232
leaveToClass?: string
3333
}
3434

35+
export interface VaporTransitionInterface {
36+
applyTransition: (
37+
props: TransitionProps,
38+
slots: { default: () => any },
39+
) => void
40+
}
41+
42+
let vaporTransitionImpl: VaporTransitionInterface | null = null
43+
export const registerVaporTransition = (
44+
impl: VaporTransitionInterface,
45+
): void => {
46+
vaporTransitionImpl = impl
47+
}
48+
3549
export const vtcKey: unique symbol = Symbol('_vtc')
3650

3751
export interface ElementWithTransition extends HTMLElement {
@@ -85,9 +99,13 @@ const decorate = (t: typeof Transition) => {
8599
* base Transition component, with DOM-specific logic.
86100
*/
87101
export const Transition: FunctionalComponent<TransitionProps> =
88-
/*@__PURE__*/ decorate((props, { slots }) =>
89-
h(BaseTransition, resolveTransitionProps(props), slots),
90-
)
102+
/*@__PURE__*/ decorate((props, { slots, vapor }: any) => {
103+
const resolvedProps = resolveTransitionProps(props)
104+
if (vapor) {
105+
return vaporTransitionImpl!.applyTransition(resolvedProps, slots)
106+
}
107+
return h(BaseTransition, resolvedProps, slots)
108+
})
91109

92110
/**
93111
* #3227 Incoming hooks may be merged into arrays when wrapping Transition

packages/runtime-dom/src/index.ts

+12
Original file line numberDiff line numberDiff line change
@@ -348,3 +348,15 @@ export {
348348
vModelSelectInit,
349349
vModelSetSelected,
350350
} from './directives/vModel'
351+
/**
352+
* @internal
353+
*/
354+
export {
355+
resolveTransitionProps,
356+
TransitionPropsValidators,
357+
registerVaporTransition,
358+
} from './components/Transition'
359+
/**
360+
* @internal
361+
*/
362+
export type { VaporTransitionInterface } from './components/Transition'

packages/runtime-vapor/src/apiCreateApp.ts

+2
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ import {
2020
import type { RawProps } from './componentProps'
2121
import { getGlobalThis } from '@vue/shared'
2222
import { optimizePropertyLookup } from './dom/prop'
23+
import { ensureVaporTransition } from './components/Transition'
2324

2425
let _createApp: CreateAppFunction<ParentNode, VaporComponent>
2526

2627
const mountApp: AppMountFn<ParentNode> = (app, container) => {
2728
optimizePropertyLookup()
29+
ensureVaporTransition()
2830

2931
// clear content before mounting
3032
if (container.nodeType === 1 /* Node.ELEMENT_NODE */) {

packages/runtime-vapor/src/block.ts

+41-11
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@ import {
77
} from './component'
88
import { createComment, createTextNode } from './dom/node'
99
import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
10+
import {
11+
type TransitionHooks,
12+
applyTransitionEnter,
13+
applyTransitionLeave,
14+
} from '@vue/runtime-dom'
1015

11-
export type Block =
16+
export type Block = (
1217
| Node
1318
| VaporFragment
1419
| DynamicFragment
1520
| VaporComponentInstance
1621
| Block[]
22+
) & { transition?: TransitionHooks }
1723

1824
export type BlockFn = (...args: any[]) => Block
1925

@@ -22,6 +28,7 @@ export class VaporFragment {
2228
anchor?: Node
2329
insert?: (parent: ParentNode, anchor: Node | null) => void
2430
remove?: (parent?: ParentNode) => void
31+
transition?: TransitionHooks
2532

2633
constructor(nodes: Block) {
2734
this.nodes = nodes
@@ -52,13 +59,13 @@ export class DynamicFragment extends VaporFragment {
5259
// teardown previous branch
5360
if (this.scope) {
5461
this.scope.stop()
55-
parent && remove(this.nodes, parent)
62+
parent && remove(this.nodes, parent, this.transition)
5663
}
5764

5865
if (render) {
5966
this.scope = new EffectScope()
6067
this.nodes = this.scope.run(render) || []
61-
if (parent) insert(this.nodes, parent, this.anchor)
68+
if (parent) insert(this.nodes, parent, this.anchor, this.transition)
6269
} else {
6370
this.scope = undefined
6471
this.nodes = []
@@ -69,7 +76,7 @@ export class DynamicFragment extends VaporFragment {
6976
this.nodes =
7077
(this.scope || (this.scope = new EffectScope())).run(this.fallback) ||
7178
[]
72-
parent && insert(this.nodes, parent, this.anchor)
79+
parent && insert(this.nodes, parent, this.anchor, this.transition)
7380
}
7481

7582
resetTracking()
@@ -106,12 +113,23 @@ export function insert(
106113
block: Block,
107114
parent: ParentNode,
108115
anchor: Node | null | 0 = null, // 0 means prepend
116+
transition: TransitionHooks | undefined = block.transition,
117+
parentSuspense?: any, // TODO Suspense
109118
): void {
110119
anchor = anchor === 0 ? parent.firstChild : anchor
111120
if (block instanceof Node) {
112-
parent.insertBefore(block, anchor)
121+
if (transition) {
122+
applyTransitionEnter(
123+
block,
124+
transition,
125+
() => parent.insertBefore(block, anchor),
126+
parentSuspense,
127+
)
128+
} else {
129+
parent.insertBefore(block, anchor)
130+
}
113131
} else if (isVaporComponent(block)) {
114-
mountComponent(block, parent, anchor)
132+
mountComponent(block, parent, anchor, transition)
115133
} else if (isArray(block)) {
116134
for (let i = 0; i < block.length; i++) {
117135
insert(block[i], parent, anchor)
@@ -121,7 +139,7 @@ export function insert(
121139
if (block.insert) {
122140
block.insert(parent, anchor)
123141
} else {
124-
insert(block.nodes, parent, anchor)
142+
insert(block.nodes, parent, anchor, block.transition)
125143
}
126144
if (block.anchor) insert(block.anchor, parent, anchor)
127145
}
@@ -132,11 +150,23 @@ export function prepend(parent: ParentNode, ...blocks: Block[]): void {
132150
while (i--) insert(blocks[i], parent, 0)
133151
}
134152

135-
export function remove(block: Block, parent?: ParentNode): void {
153+
export function remove(
154+
block: Block,
155+
parent?: ParentNode,
156+
transition: TransitionHooks | undefined = block.transition,
157+
): void {
136158
if (block instanceof Node) {
137-
parent && parent.removeChild(block)
159+
if (transition) {
160+
applyTransitionLeave(
161+
block,
162+
transition,
163+
() => parent && parent.removeChild(block),
164+
)
165+
} else {
166+
parent && parent.removeChild(block)
167+
}
138168
} else if (isVaporComponent(block)) {
139-
unmountComponent(block, parent)
169+
unmountComponent(block, parent, transition)
140170
} else if (isArray(block)) {
141171
for (let i = 0; i < block.length; i++) {
142172
remove(block[i], parent)
@@ -146,7 +176,7 @@ export function remove(block: Block, parent?: ParentNode): void {
146176
if (block.remove) {
147177
block.remove(parent)
148178
} else {
149-
remove(block.nodes, parent)
179+
remove(block.nodes, parent, block.transition)
150180
}
151181
if (block.anchor) remove(block.anchor, parent)
152182
if ((block as DynamicFragment).scope) {

0 commit comments

Comments
 (0)