Skip to content

Commit 47e4e13

Browse files
committed
feat(vapor): vapor transition
1 parent e1d26b1 commit 47e4e13

File tree

13 files changed

+214
-55
lines changed

13 files changed

+214
-55
lines changed

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

Lines changed: 1 addition & 2 deletions
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/compiler-vapor/src/transforms/transformElement.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ function transformComponentElement(
124124
}
125125

126126
context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT
127-
context.registerOperation({
127+
// context.registerOperation()
128+
// TODO revert wait for https://github.com/vuejs/core/pull/12951 get merged
129+
context.block.operation.unshift({
128130
type: IRNodeTypes.CREATE_COMPONENT_NODE,
129131
id: context.reference(),
130132
tag,

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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 55 additions & 33 deletions
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,10 @@ 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 (vnode.shapeFlag & ShapeFlags.ELEMENT && transition) {
2300+
applyTransitionLeave(el!, transition, () => hostRemove(el!))
23142301
} else {
2315-
performRemove()
2302+
hostRemove(el!)
23162303
}
23172304
}
23182305

@@ -2630,6 +2617,41 @@ export function invalidateMount(hooks: LifecycleHook | undefined): void {
26302617
}
26312618
}
26322619

2620+
export function applyTransitionEnter(
2621+
el: RendererElement,
2622+
transition: TransitionHooks,
2623+
insert: () => void,
2624+
parentSuspense: SuspenseBoundary | null,
2625+
): void {
2626+
if (needTransition(parentSuspense, transition)) {
2627+
transition.beforeEnter(el)
2628+
insert()
2629+
queuePostRenderEffect(() => transition.enter(el), parentSuspense)
2630+
} else {
2631+
insert()
2632+
}
2633+
}
2634+
2635+
export function applyTransitionLeave(
2636+
el: RendererElement,
2637+
transition: TransitionHooks,
2638+
remove: () => void,
2639+
): void {
2640+
const performRemove = () => {
2641+
remove()
2642+
if (transition && !transition.persisted && transition.afterLeave) {
2643+
transition.afterLeave()
2644+
}
2645+
}
2646+
const { leave, delayLeave } = transition
2647+
const performLeave = () => leave(el, performRemove)
2648+
if (delayLeave) {
2649+
delayLeave(el, performRemove, performLeave)
2650+
} else {
2651+
performLeave()
2652+
}
2653+
}
2654+
26332655
function getVaporInterface(
26342656
instance: ComponentInternalInstance | null,
26352657
vnode: VNode,

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

Lines changed: 21 additions & 3 deletions
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 VaporTransitionInferface {
36+
applyTransition: (
37+
props: TransitionProps,
38+
slots: { default: () => any },
39+
) => void
40+
}
41+
42+
let vaporTransitionImpl: VaporTransitionInferface | null = null
43+
export const registerVaporTransition = (
44+
impl: VaporTransitionInferface,
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

Lines changed: 12 additions & 0 deletions
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 { VaporTransitionInferface } from './components/Transition'

packages/runtime-vapor/src/apiCreateApp.ts

Lines changed: 2 additions & 0 deletions
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

Lines changed: 41 additions & 11 deletions
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)