})
describe.todo('transition with Suspense', () => {})
- describe.todo('transition with Teleport', () => {})
+
+ describe('transition with Teleport', () => {
+ test(
+ 'apply transition to teleport child',
+ async () => {
+ const btnSelector = '.with-teleport > button'
+ const containerSelector = '.with-teleport > .container'
+ const targetSelector = `.with-teleport > .target`
+
+ await transitionFinish()
+ expect(await html(containerSelector)).toBe('')
+ expect(await html(targetSelector)).toBe('')
+
+ // enter
+ expect(
+ (await transitionStart(btnSelector, `${targetSelector} div`))
+ .classNames,
+ ).toStrictEqual(['test', 'v-enter-from', 'v-enter-active'])
+ await nextFrame()
+ expect(await classList(`${targetSelector} div`)).toStrictEqual([
+ 'test',
+ 'v-enter-active',
+ 'v-enter-to',
+ ])
+ await transitionFinish()
+ expect(await html(targetSelector)).toBe(
+ '<div class="test">vapor compB</div>',
+ )
+ expect(await html(containerSelector)).toBe('')
+
+ // leave
+ expect(
+ (await transitionStart(btnSelector, `${targetSelector} div`))
+ .classNames,
+ ).toStrictEqual(['test', 'v-leave-from', 'v-leave-active'])
+ await nextFrame()
+ expect(await classList(`${targetSelector} div`)).toStrictEqual([
+ 'test',
+ 'v-leave-active',
+ 'v-leave-to',
+ ])
+ await transitionFinish()
+ expect(await html(targetSelector)).toBe('')
+ expect(await html(containerSelector)).toBe('')
+ },
+ E2E_TIMEOUT,
+ )
+ })
describe('transition with v-show', () => {
test(
</div>
<!-- mode end -->
+ <!-- with teleport -->
+ <div class="with-teleport">
+ <div class="target"></div>
+ <div class="container">
+ <Transition>
+ <Teleport to=".target" defer>
+ <!-- comment -->
+ <VaporCompB v-if="!toggle" class="test"></VaporCompB>
+ </Teleport>
+ </Transition>
+ </div>
+ <button @click="toggle = !toggle">button</button>
+ </div>
+ <!-- with teleport end -->
+
<!-- with keep-alive -->
<div class="keep-alive">
<div>
let slotKey
if (isTransitionNode(node) && nonSlotTemplateChildren.length) {
- const keyProp = findProp(
- nonSlotTemplateChildren[0] as ElementNode,
- 'key',
- ) as VaporDirectiveNode
- if (keyProp) {
- slotKey = keyProp.exp
+ const nonCommentChild = nonSlotTemplateChildren.find(
+ n => n.type !== NodeTypes.COMMENT,
+ )
+ if (nonCommentChild) {
+ const keyProp = findProp(
+ nonCommentChild as ElementNode,
+ 'key',
+ ) as VaporDirectiveNode
+ if (keyProp) {
+ slotKey = keyProp.exp
+ }
}
}
component.inheritAttrs !== false &&
Object.keys(instance.attrs).length
) {
- const el = getRootElement(instance)
- if (el) {
- renderEffect(() => applyFallthroughProps(el, instance.attrs))
- }
+ renderEffect(() => applyFallthroughProps(instance.block, instance.attrs))
}
setActiveSub(prevSub)
block: Block,
attrs: Record<string, any>,
): void {
- isApplyingFallthroughProps = true
- setDynamicProps(block as Element, [attrs])
- isApplyingFallthroughProps = false
+ const el = getRootElement(block)
+ if (el) {
+ isApplyingFallthroughProps = true
+ setDynamicProps(el, [attrs])
+ isApplyingFallthroughProps = false
+ }
}
/**
}
}
-function getRootElement({
- block,
-}: VaporComponentInstance): Element | undefined {
+function getRootElement(block: Block): Element | undefined {
if (block instanceof Element) {
return block
}
runWithoutHydration,
setCurrentHydrationNode,
} from '../dom/hydration'
+import { applyTransitionHooks } from './Transition'
export const VaporTeleportImpl = {
name: 'VaporTeleport',
if (!this.parent || isHydrating) return
const mount = (parent: ParentNode, anchor: Node | null) => {
+ if (this.$transition) {
+ applyTransitionHooks(this.nodes, this.$transition)
+ }
insert(
this.nodes,
(this.mountContainer = parent),
baseResolveTransitionHooks,
checkTransitionMode,
currentInstance,
+ getComponentName,
isTemplateNode,
leaveCbKey,
queuePostFlushCb,
setCurrentHydrationNode,
} from '../dom/hydration'
+const displayName = 'VaporTransition'
+
const decorate = (t: typeof VaporTransition) => {
- t.displayName = 'VaporTransition'
+ t.displayName = displayName
t.props = TransitionPropsValidators
t.__vapor = true
return t
hooks: VaporTransitionHooks,
fallthroughAttrs: boolean = true,
): VaporTransitionHooks {
+ // filter out comment nodes
+ if (isArray(block)) {
+ block = block.filter(b => !(b instanceof Comment))
+ if (block.length === 1) {
+ block = block[0]
+ } else if (block.length === 0) {
+ return hooks
+ }
+ }
+
const isFrag = isFragment(block)
- const child = findTransitionBlock(block)
+ const child = findTransitionBlock(block, isFrag)
if (!child) {
// set transition hooks on fragment for reusing during it's updating
if (isFrag) setTransitionHooksOnFragment(block, hooks)
return transitionBlockCache.get(block)
}
- let isFrag = false
let child: TransitionBlock | undefined
if (block instanceof Node) {
// transition can only be applied on Element child
if (block instanceof Element) child = block
} else if (isVaporComponent(block)) {
- child = findTransitionBlock(block.block)
+ // stop searching if encountering nested Transition component
+ if (getComponentName(block.type) === displayName) return undefined
+ child = findTransitionBlock(block.block, inFragment)
// use component id as key
if (child && child.$key === undefined) child.$key = block.uid
} else if (isArray(block)) {
- child = block[0] as TransitionBlock
let hasFound = false
for (const c of block) {
- const item = findTransitionBlock(c)
- if (item instanceof Element) {
- if (__DEV__ && hasFound) {
- // warn more than one non-comment child
- warn(
- '<transition> can only be used on a single element or component. ' +
- 'Use <transition-group> for lists.',
- )
- break
- }
- child = item
- hasFound = true
- if (!__DEV__) break
+ if (c instanceof Comment) continue
+ // check if the child is a fragment to suppress warnings
+ if (isFragment(c)) inFragment = true
+ const item = findTransitionBlock(c, inFragment)
+ if (__DEV__ && hasFound) {
+ // warn more than one non-comment child
+ warn(
+ '<transition> can only be used on a single element or component. ' +
+ 'Use <transition-group> for lists.',
+ )
+ break
}
+ child = item
+ hasFound = true
+ if (!__DEV__) break
}
- } else if ((isFrag = isFragment(block))) {
+ } else if (isFragment(block)) {
+ // mark as in fragment to suppress warnings
+ inFragment = true
if (block.insert) {
child = block
} else {
}
}
- if (__DEV__ && !child && !inFragment && !isFrag) {
+ if (__DEV__ && !child && !inFragment) {
warn('Transition component has no valid child element')
}
hooks: VaporTransitionHooks,
): void {
if (isFragment(block)) {
- setTransitionHooks(block, hooks)
+ block.$transition = hooks
} else if (isArray(block)) {
for (let i = 0; i < block.length; i++) {
setTransitionHooksOnFragment(block[i], hooks)