}
export interface TransitionHooksContext {
- setLeavingNodeCache: () => void
- unsetLeavingNodeCache: () => void
+ setLeavingNodeCache: (node: any) => void
+ unsetLeavingNodeCache: (node: any) => void
earlyRemove: () => void
cloneHooks: (node: any) => TransitionHooks
}
callHook(onAfterLeave, [el])
}
el[leaveCbKey] = undefined
- unsetLeavingNodeCache()
+ unsetLeavingNodeCache(el)
})
- setLeavingNodeCache()
+ setLeavingNodeCache(el)
if (onLeave) {
callAsyncHook(onLeave, [el, done])
} else {
/**
* @internal
*/
-export { applyTransitionEnter, applyTransitionLeave } from './renderer'
+export { performTransitionEnter, performTransitionLeave } from './renderer'
// #1583 For inside suspense + suspense not resolved case, enter hook should call when suspense resolved
// #1689 For inside suspense + suspense resolved case, just call it
if (transition) {
- applyTransitionEnter(
+ performTransitionEnter(
el,
transition,
() => hostInsert(el, container, anchor),
transition
if (needTransition) {
if (moveType === MoveType.ENTER) {
- applyTransitionEnter(
+ performTransitionEnter(
el!,
transition,
() => hostInsert(el!, container, anchor),
}
if (transition) {
- applyTransitionLeave(
+ performTransitionLeave(
el!,
transition,
() => hostRemove(el!),
}
}
-export function applyTransitionEnter(
+export function performTransitionEnter(
el: RendererElement,
transition: TransitionHooks,
insert: () => void,
}
}
-export function applyTransitionLeave(
+export function performTransitionLeave(
el: RendererElement,
transition: TransitionHooks,
remove: () => void,
import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
import {
type TransitionHooks,
- applyTransitionEnter,
- applyTransitionLeave,
+ type TransitionProps,
+ type TransitionState,
+ performTransitionEnter,
+ performTransitionLeave,
} from '@vue/runtime-dom'
+import {
+ applyTransitionEnterHooks,
+ applyTransitionLeaveHooks,
+} from './components/Transition'
export type Block = (
| Node
) &
TransitionBlock
+export interface VaporTransitionHooks extends TransitionHooks {
+ state?: TransitionState
+ props?: TransitionProps
+}
+
export type TransitionBlock = {
key?: any
- transition?: TransitionHooks
- applyTransitionLeavingHooks?: (
- block: Block,
- afterLeaveCb: () => void,
- ) => TransitionHooks | undefined
+ transition?: VaporTransitionHooks
}
export type BlockFn = (...args: any[]) => Block
anchor?: Node
insert?: (parent: ParentNode, anchor: Node | null) => void
remove?: (parent?: ParentNode) => void
- transition?: TransitionHooks
- applyTransitionLeavingHooks?: (
- block: Block,
- afterLeaveCb: () => void,
- ) => TransitionHooks | undefined
+ transitionChild?: TransitionBlock | undefined
constructor(nodes: Block) {
this.nodes = nodes
scope: EffectScope | undefined
current?: BlockFn
fallback?: BlockFn
+ transitionChild?: Block
constructor(anchorLabel?: string) {
super([])
const renderBranch = () => {
if (render) {
+ const transition = this.transition
this.scope = new EffectScope()
this.nodes = this.scope.run(render) || []
- if (parent) insert(this.nodes, parent, this.anchor, this.transition)
+ if (transition) {
+ this.transitionChild = applyTransitionEnterHooks(
+ this.nodes,
+ transition.state!,
+ transition.props!,
+ transition,
+ )
+ }
+ if (parent) insert(this.nodes, parent, this.anchor)
} else {
this.scope = undefined
this.nodes = []
this.scope.stop()
const mode = this.transition && this.transition.mode
if (mode) {
- const transition = this.applyTransitionLeavingHooks!(
+ applyTransitionLeaveHooks(
this.nodes,
+ this.transition!.state!,
+ this.transition!.props!,
renderBranch,
+ this.transition,
)
- parent && remove(this.nodes, parent, transition)
+ parent && remove(this.nodes, parent)
if (mode === 'out-in') {
resetTracking()
return
}
} else {
- parent && remove(this.nodes, parent, this.transition)
+ parent && remove(this.nodes, parent)
}
}
renderBranch()
if (this.fallback && !isValidBlock(this.nodes)) {
- parent && remove(this.nodes, parent, this.transition)
+ parent && remove(this.nodes, parent)
this.nodes =
(this.scope || (this.scope = new EffectScope())).run(this.fallback) ||
[]
- parent && insert(this.nodes, parent, this.anchor, this.transition)
+ parent && insert(this.nodes, parent, this.anchor)
}
resetTracking()
}
+
+ get transition(): VaporTransitionHooks | undefined {
+ return this.transitionChild && this.transitionChild.transition
+ }
}
export function isFragment(val: NonNullable<unknown>): val is VaporFragment {
block: Block,
parent: ParentNode,
anchor: Node | null | 0 = null, // 0 means prepend
- transition: TransitionHooks | undefined = block.transition,
parentSuspense?: any, // TODO Suspense
): void {
anchor = anchor === 0 ? parent.firstChild : anchor
if (block instanceof Node) {
// don't apply transition on text or comment nodes
- if (transition && block instanceof Element) {
- applyTransitionEnter(
+ if (block.transition && block instanceof Element) {
+ performTransitionEnter(
block,
- transition,
+ // @ts-expect-error
+ block.transition,
() => parent.insertBefore(block, anchor),
parentSuspense,
)
parent.insertBefore(block, anchor)
}
} else if (isVaporComponent(block)) {
- mountComponent(block, parent, anchor, transition)
+ mountComponent(block, parent, anchor)
} else if (isArray(block)) {
for (let i = 0; i < block.length; i++) {
insert(block[i], parent, anchor)
if (block.insert) {
block.insert(parent, anchor)
} else {
- insert(block.nodes, parent, anchor, block.transition)
+ insert(block.nodes, parent, anchor, parentSuspense)
}
if (block.anchor) insert(block.anchor, parent, anchor)
}
while (i--) insert(blocks[i], parent, 0)
}
-export function remove(
- block: Block,
- parent?: ParentNode,
- transition: TransitionHooks | undefined = block.transition,
-): void {
+export function remove(block: Block, parent?: ParentNode): void {
if (block instanceof Node) {
- if (transition && block instanceof Element) {
- applyTransitionLeave(
+ if (block.transition && block instanceof Element) {
+ performTransitionLeave(
block,
- transition,
+ // @ts-expect-error
+ block.transition,
() => parent && parent.removeChild(block),
)
} else {
parent && parent.removeChild(block)
}
} else if (isVaporComponent(block)) {
- unmountComponent(block, parent, transition)
+ unmountComponent(block, parent)
} else if (isArray(block)) {
for (let i = 0; i < block.length; i++) {
remove(block[i], parent)
if (block.remove) {
block.remove(parent)
} else {
- remove(block.nodes, parent, block.transition)
+ remove(block.nodes, parent)
}
if (block.anchor) remove(block.anchor, parent)
if ((block as DynamicFragment).scope) {
type NormalizedPropsOptions,
type ObjectEmitsOptions,
type SuspenseBoundary,
- type TransitionHooks,
callWithErrorHandling,
currentInstance,
endMeasure,
instance: VaporComponentInstance,
parent: ParentNode,
anchor?: Node | null | 0,
- transition?: TransitionHooks,
): void {
if (__DEV__) {
startMeasure(instance, `mount`)
}
if (!instance.isMounted) {
if (instance.bm) invokeArrayFns(instance.bm)
- insert(instance.block, parent, anchor, transition)
+ insert(instance.block, parent, anchor)
if (instance.m) queuePostFlushCb(() => invokeArrayFns(instance.m!))
instance.isMounted = true
} else {
- insert(instance.block, parent, anchor, transition)
+ insert(instance.block, parent, anchor)
}
if (__DEV__) {
endMeasure(instance, `mount`)
export function unmountComponent(
instance: VaporComponentInstance,
parentNode?: ParentNode,
- transition?: TransitionHooks,
): void {
if (instance.isMounted && !instance.isUnmounted) {
if (__DEV__ && instance.type.__hmrId) {
}
if (parentNode) {
- remove(instance.block, parentNode, transition)
+ remove(instance.block, parentNode)
}
}
useTransitionState,
warn,
} from '@vue/runtime-dom'
-import type { Block } from '../block'
+import { type Block, type VaporTransitionHooks, isFragment } from '../block'
import { isVaporComponent } from '../component'
export const vaporTransitionImpl: VaporTransitionInterface = {
const children = slots.default && slots.default()
if (!children) return
- const child = findElementChild(children)
- if (!child) return
-
- const state = useTransitionState()
- let enterHooks = resolveTransitionHooks(
- child as any,
- props,
- state,
- currentInstance!,
- hooks => (enterHooks = hooks),
- )
- setTransitionHooks(child, enterHooks)
-
const { mode } = props
if (
__DEV__ &&
warn(`invalid <transition> mode: ${mode}`)
}
- child.applyTransitionLeavingHooks = (
- block: Block,
- afterLeaveCb: () => void,
- ) => {
- const leavingBlock = findElementChild(block)
- if (!leavingBlock) return undefined
-
- let leavingHooks = resolveTransitionHooks(
- leavingBlock as any,
- props,
- state,
- currentInstance!,
- )
- setTransitionHooks(leavingBlock, leavingHooks)
-
- if (mode === 'out-in') {
- state.isLeaving = true
- leavingHooks.afterLeave = () => {
- state.isLeaving = false
- afterLeaveCb()
- delete leavingHooks.afterLeave
- }
- } else if (mode === 'in-out') {
- leavingHooks.delayLeave = (
- block: TransitionElement,
- earlyRemove,
- delayedLeave,
- ) => {
- const leavingNodeCache = getLeavingNodesForBlock(state, leavingBlock)
- leavingNodeCache[String(leavingBlock.key)] = leavingBlock
- // early removal callback
- block[leaveCbKey] = () => {
- earlyRemove()
- block[leaveCbKey] = undefined
- delete enterHooks.delayedLeave
- }
- enterHooks.delayedLeave = () => {
- delayedLeave()
- delete enterHooks.delayedLeave
- }
- }
- }
- return leavingHooks
- }
+ applyTransitionEnterHooks(
+ children,
+ useTransitionState(),
+ props,
+ undefined,
+ false,
+ )
return children
},
}
const getTransitionHooksContext = (
- leavingNodeCache: Record<string, Block>,
key: string,
block: Block,
props: TransitionProps,
postClone: ((hooks: TransitionHooks) => void) | undefined,
) => {
const context: TransitionHooksContext = {
- setLeavingNodeCache: () => {
- leavingNodeCache[key] = block
+ setLeavingNodeCache: el => {
+ const leavingNodeCache = getLeavingNodesForBlock(state, block)
+ leavingNodeCache[key] = el
},
- unsetLeavingNodeCache: () => {
- if (leavingNodeCache[key] === block) {
+ unsetLeavingNodeCache: el => {
+ const leavingNodeCache = getLeavingNodesForBlock(state, block)
+ if (leavingNodeCache[key] === el) {
delete leavingNodeCache[key]
}
},
earlyRemove: () => {
+ const leavingNodeCache = getLeavingNodesForBlock(state, block)
const leavingNode = leavingNodeCache[key]
if (leavingNode && (leavingNode as TransitionElement)[leaveCbKey]) {
// force early removal (not cancelled)
state: TransitionState,
instance: GenericComponentInstance,
postClone?: (hooks: TransitionHooks) => void,
-): TransitionHooks {
+): VaporTransitionHooks {
const key = String(block.key)
- const leavingNodeCache = getLeavingNodesForBlock(state, block)
const context = getTransitionHooksContext(
- leavingNodeCache,
key,
block,
props,
instance,
postClone,
)
- return baseResolveTransitionHooks(context, props, state, instance)
+ const hooks: VaporTransitionHooks = baseResolveTransitionHooks(
+ context,
+ props,
+ state,
+ instance,
+ )
+ hooks.state = state
+ hooks.props = props
+ return hooks
}
function getLeavingNodesForBlock(
return leavingNodesCache
}
-function setTransitionHooks(block: Block, hooks: TransitionHooks) {
- block.transition = hooks
+function setTransitionHooks(block: Block, hooks: VaporTransitionHooks) {
+ if (!isFragment(block)) {
+ block.transition = hooks
+ }
+}
+
+export function applyTransitionEnterHooks(
+ block: Block,
+ state: TransitionState,
+ props: TransitionProps,
+ enterHooks?: VaporTransitionHooks,
+ clone: boolean = true,
+): Block | undefined {
+ const child = findElementChild(block)
+ if (child) {
+ if (!enterHooks) {
+ enterHooks = resolveTransitionHooks(
+ child,
+ props,
+ state,
+ currentInstance!,
+ hooks => (enterHooks = hooks),
+ )
+ }
+
+ setTransitionHooks(
+ child,
+ clone ? enterHooks.clone(child as any) : enterHooks,
+ )
+
+ if (isFragment(block)) {
+ block.transitionChild = child
+ }
+ }
+ return child
+}
+
+export function applyTransitionLeaveHooks(
+ block: Block,
+ state: TransitionState,
+ props: TransitionProps,
+ afterLeaveCb: () => void,
+ enterHooks: TransitionHooks,
+): void {
+ const leavingBlock = findElementChild(block)
+ if (!leavingBlock) return undefined
+
+ let leavingHooks = resolveTransitionHooks(
+ leavingBlock,
+ props,
+ state,
+ currentInstance!,
+ )
+ setTransitionHooks(leavingBlock, leavingHooks)
+
+ const { mode } = props
+ if (mode === 'out-in') {
+ state.isLeaving = true
+ leavingHooks.afterLeave = () => {
+ state.isLeaving = false
+ afterLeaveCb()
+ delete leavingHooks.afterLeave
+ }
+ } else if (mode === 'in-out') {
+ leavingHooks.delayLeave = (
+ block: TransitionElement,
+ earlyRemove,
+ delayedLeave,
+ ) => {
+ const leavingNodeCache = getLeavingNodesForBlock(state, leavingBlock)
+ leavingNodeCache[String(leavingBlock.key)] = leavingBlock
+ // early removal callback
+ block[leaveCbKey] = () => {
+ earlyRemove()
+ block[leaveCbKey] = undefined
+ delete enterHooks.delayedLeave
+ }
+ enterHooks.delayedLeave = () => {
+ delayedLeave()
+ delete enterHooks.delayedLeave
+ }
+ }
+ }
}
-function findElementChild(block: Block): Block | undefined {
+export function findElementChild(block: Block): Block | undefined {
let child: Block | undefined
- // transition can only be applied on Element child
- if (block instanceof Element) {
- child = block
+ if (block instanceof Node) {
+ // transition can only be applied on Element child
+ if (block instanceof Element) child = block
} else if (isVaporComponent(block)) {
child = findElementChild(block.block)
} else if (Array.isArray(block)) {
}
} else {
// fragment
- // store transition hooks on fragment itself, so it can apply to both
- // previous and new branch during updates.
- child = block
+ child = findElementChild(block.nodes)
}
if (__DEV__ && !child) {