diff --git a/packages/runtime-vapor/__tests__/components/Teleport.spec.ts b/packages/runtime-vapor/__tests__/components/Teleport.spec.ts index bad520872..d4282ab8a 100644 --- a/packages/runtime-vapor/__tests__/components/Teleport.spec.ts +++ b/packages/runtime-vapor/__tests__/components/Teleport.spec.ts @@ -2,6 +2,7 @@ import { type LooseRawProps, type VaporComponent, createComponent as createComp, + createComponent, } from '../../src/component' import { type VaporDirective, @@ -9,15 +10,18 @@ import { child, createIf, createTemplateRefSetter, + createVaporApp, defineVaporComponent, renderEffect, setInsertionState, setText, template, + vaporInteropPlugin, withVaporDirectives, } from '@vue/runtime-vapor' import { makeRender } from '../_utils' import { + h, nextTick, onBeforeUnmount, onMounted, @@ -63,7 +67,7 @@ describe('renderer: VaporTeleport', () => { mount(root) expect(root.innerHTML).toBe( - '
teleported
', + '
teleported
', ) }) @@ -116,7 +120,7 @@ describe('renderer: VaporTeleport', () => { show.value = true await nextTick() expect(root.innerHTML).toBe( - `
Footer
bar
`, + `
Footer
bar
`, ) }) }) @@ -155,7 +159,9 @@ describe('renderer: VaporTeleport', () => { createRecord(parentId, Parent as any) mount(root) - expect(root.innerHTML).toBe('
root
') + expect(root.innerHTML).toBe( + '
root
', + ) expect(target.innerHTML).toBe('
teleported
') // rerender child @@ -163,7 +169,9 @@ describe('renderer: VaporTeleport', () => { return template('
teleported 2
')() }) - expect(root.innerHTML).toBe('
root
') + expect(root.innerHTML).toBe( + '
root
', + ) expect(target.innerHTML).toBe('
teleported 2
') // rerender parent @@ -181,7 +189,9 @@ describe('renderer: VaporTeleport', () => { return [n0, n1] }) - expect(root.innerHTML).toBe('
root 2
') + expect(root.innerHTML).toBe( + '
root 2
', + ) expect(target.innerHTML).toBe('
teleported 2
') }) @@ -219,7 +229,7 @@ describe('renderer: VaporTeleport', () => { mount(root) expect(root.innerHTML).toBe( - '
teleported
root
', + '
teleported
root
', ) expect(target.innerHTML).toBe('') @@ -241,14 +251,16 @@ describe('renderer: VaporTeleport', () => { }) expect(root.innerHTML).toBe( - '
teleported
root 2
', + '
teleported
root 2
', ) expect(target.innerHTML).toBe('') // toggle disabled disabled.value = false await nextTick() - expect(root.innerHTML).toBe('
root 2
') + expect(root.innerHTML).toBe( + '
root 2
', + ) expect(target.innerHTML).toBe('
teleported
') }) @@ -300,7 +312,9 @@ describe('renderer: VaporTeleport', () => { createRecord(parentId, Parent as any) mount(root) - expect(root.innerHTML).toBe('
root
') + expect(root.innerHTML).toBe( + '
root
', + ) expect(target.innerHTML).toBe('
teleported
') // reload child by changing msg @@ -318,7 +332,9 @@ describe('renderer: VaporTeleport', () => { return [n0] }, }) - expect(root.innerHTML).toBe('
root
') + expect(root.innerHTML).toBe( + '
root
', + ) expect(target.innerHTML).toBe('
teleported 2
') // reload parent by changing msg @@ -348,7 +364,9 @@ describe('renderer: VaporTeleport', () => { }, }) - expect(root.innerHTML).toBe('
root 2
') + expect(root.innerHTML).toBe( + '
root 2
', + ) expect(target.innerHTML).toBe('
teleported 2
') // reload parent again by changing disabled @@ -379,7 +397,7 @@ describe('renderer: VaporTeleport', () => { }) expect(root.innerHTML).toBe( - '
teleported 2
root 2
', + '
teleported 2
root 2
', ) expect(target.innerHTML).toBe('') }) @@ -434,7 +452,7 @@ describe('renderer: VaporTeleport', () => { mount(root) expect(root.innerHTML).toBe( - '
teleported
root
', + '
teleported
root
', ) expect(target.innerHTML).toBe('') @@ -454,7 +472,7 @@ describe('renderer: VaporTeleport', () => { }, }) expect(root.innerHTML).toBe( - '
teleported 2
root
', + '
teleported 2
root
', ) expect(target.innerHTML).toBe('') @@ -474,14 +492,16 @@ describe('renderer: VaporTeleport', () => { }, }) expect(root.innerHTML).toBe( - '
teleported 3
root
', + '
teleported 3
root
', ) expect(target.innerHTML).toBe('') // toggle disabled disabled.value = false await nextTick() - expect(root.innerHTML).toBe('
root
') + expect(root.innerHTML).toBe( + '
root
', + ) expect(target.innerHTML).toBe('
teleported 3
') }) @@ -537,7 +557,7 @@ describe('renderer: VaporTeleport', () => { mount(root) expect(root.innerHTML).toBe( - '
teleported
child
root
', + '
teleported
child
root
', ) expect(target.innerHTML).toBe('') @@ -557,7 +577,7 @@ describe('renderer: VaporTeleport', () => { }, }) expect(root.innerHTML).toBe( - '
teleported 2
child
root
', + '
teleported 2
child
root
', ) expect(target.innerHTML).toBe('') @@ -577,17 +597,68 @@ describe('renderer: VaporTeleport', () => { }, }) expect(root.innerHTML).toBe( - '
teleported 3
child
root
', + '
teleported 3
child
root
', ) expect(target.innerHTML).toBe('') // toggle disabled disabled.value = false await nextTick() - expect(root.innerHTML).toBe('
root
') + expect(root.innerHTML).toBe( + '
root
', + ) expect(target.innerHTML).toBe('
teleported 3
child') }) }) + + describe('VDOM interop', () => { + test('render vdom component', async () => { + const target = document.createElement('div') + const root = document.createElement('div') + + const VDOMComp = { + setup() { + return () => h('h1', null, 'vdom comp') + }, + } + + const disabled = ref(true) + const App = defineVaporComponent({ + setup() { + const n1 = createComponent( + VaporTeleport, + { + to: () => target, + defer: () => '', + disabled: () => disabled.value, + }, + { + default: () => { + const n0 = createComponent(VDOMComp) + return n0 + }, + }, + true, + ) + return n1 + }, + }) + + const app = createVaporApp(App) + app.use(vaporInteropPlugin) + app.mount(root) + + expect(target.innerHTML).toBe('') + expect(root.innerHTML).toBe( + '

vdom comp

', + ) + + disabled.value = false + await nextTick() + expect(root.innerHTML).toBe('') + expect(target.innerHTML).toBe('

vdom comp

') + }) + }) }) function runSharedTests(deferMode: boolean): void { @@ -625,7 +696,9 @@ function runSharedTests(deferMode: boolean): void { }).create() mount(root) - expect(root.innerHTML).toBe('
root
') + expect(root.innerHTML).toBe( + '
root
', + ) expect(target.innerHTML).toBe('
teleported
') }) @@ -654,14 +727,18 @@ function runSharedTests(deferMode: boolean): void { }).create() mount(root) - expect(root.innerHTML).toBe('
root
') + expect(root.innerHTML).toBe( + '
root
', + ) expect(targetA.innerHTML).toBe('
teleported
') expect(targetB.innerHTML).toBe('') target.value = targetB await nextTick() - expect(root.innerHTML).toBe('
root
') + expect(root.innerHTML).toBe( + '
root
', + ) expect(targetA.innerHTML).toBe('') expect(targetB.innerHTML).toBe('
teleported
') }) @@ -834,7 +911,9 @@ function runSharedTests(deferMode: boolean): void { }, }).create() mount(root) - expect(root.innerHTML).toBe('
') + expect(root.innerHTML).toBe( + '
', + ) expect(target.innerHTML).toBe('
one
two') // update existing content @@ -849,7 +928,9 @@ function runSharedTests(deferMode: boolean): void { // toggling child1.value = [] as any await nextTick() - expect(root.innerHTML).toBe('
') + expect(root.innerHTML).toBe( + '
', + ) expect(target.innerHTML).toBe('three') // toggle back @@ -859,14 +940,18 @@ function runSharedTests(deferMode: boolean): void { ] as any child2.value = [template('three')()] as any await nextTick() - expect(root.innerHTML).toBe('
') + expect(root.innerHTML).toBe( + '
', + ) // should append expect(target.innerHTML).toBe('
one
two
three') // toggle the other teleport child2.value = [] as any await nextTick() - expect(root.innerHTML).toBe('
') + expect(root.innerHTML).toBe( + '
', + ) expect(target.innerHTML).toBe('
one
two
') }) @@ -897,12 +982,12 @@ function runSharedTests(deferMode: boolean): void { mount(root) expect(root.innerHTML).toBe( - '
teleported
', + '
teleported
', ) disabled.value = false await nextTick() expect(root.innerHTML).toBe( - '
teleported
', + '
teleported
', ) }) @@ -929,13 +1014,15 @@ function runSharedTests(deferMode: boolean): void { }).create() mount(root) - expect(root.innerHTML).toBe('
root
') + expect(root.innerHTML).toBe( + '
root
', + ) expect(target.innerHTML).toBe('
teleported
') disabled.value = true await nextTick() expect(root.innerHTML).toBe( - '
teleported
root
', + '
teleported
root
', ) expect(target.innerHTML).toBe('') @@ -943,7 +1030,7 @@ function runSharedTests(deferMode: boolean): void { disabled.value = false await nextTick() expect(root.innerHTML).toBe( - '
root
', + '
root
', ) expect(target.innerHTML).toBe('
teleported
') }) @@ -984,14 +1071,14 @@ function runSharedTests(deferMode: boolean): void { }).create() mount(root) - expect(root.innerHTML).toBe('') + expect(root.innerHTML).toBe('') expect(target.innerHTML).toBe('
foo
') expect(spy).toHaveBeenCalledTimes(1) expect(teardown).not.toHaveBeenCalled() toggle.value = false await nextTick() - expect(root.innerHTML).toBe('') + expect(root.innerHTML).toBe('') expect(target.innerHTML).toBe('') expect(spy).toHaveBeenCalledTimes(1) expect(teardown).toHaveBeenCalledTimes(1) @@ -1078,7 +1165,9 @@ function runSharedTests(deferMode: boolean): void { show.value = true await nextTick() - expect(root.innerHTML).toBe('
teleported
') + expect(root.innerHTML).toBe( + '
teleported
', + ) show.value = false await nextTick() @@ -1125,7 +1214,7 @@ function runSharedTests(deferMode: boolean): void { parentShow.value = true await nextTick() expect(root.innerHTML).toBe( - '
foo
', + '
foo
', ) parentShow.value = false diff --git a/packages/runtime-vapor/src/block.ts b/packages/runtime-vapor/src/block.ts index b980b3999..aeac078ae 100644 --- a/packages/runtime-vapor/src/block.ts +++ b/packages/runtime-vapor/src/block.ts @@ -110,12 +110,7 @@ export function insert( if (block.insert) { block.insert(parent, anchor, (block as TransitionBlock).$transition) } else { - insert( - block.nodes, - block.target || parent, - block.targetAnchor || anchor, - parentSuspense, - ) + insert(block.nodes, parent, anchor, parentSuspense) } } } diff --git a/packages/runtime-vapor/src/components/Teleport.ts b/packages/runtime-vapor/src/components/Teleport.ts index d80399373..637985ddc 100644 --- a/packages/runtime-vapor/src/components/Teleport.ts +++ b/packages/runtime-vapor/src/components/Teleport.ts @@ -35,13 +35,12 @@ export const VaporTeleportImpl = { ) const updateEffect = renderEffect(() => { - frag.update( - // access the props to trigger tracking - extend( - {}, - new Proxy(props, rawPropsProxyHandlers) as any as TeleportProps, - ), + // access the props to trigger tracking + frag.props = extend( + {}, + new Proxy(props, rawPropsProxyHandlers) as any as TeleportProps, ) + frag.update() }) if (__DEV__) { @@ -82,7 +81,10 @@ export const VaporTeleportImpl = { } export class TeleportFragment extends VaporFragment { + target?: ParentNode | null + targetAnchor?: Node | null anchor: Node + props?: TeleportProps private targetStart?: Node private mainAnchor?: Node @@ -92,7 +94,7 @@ export class TeleportFragment extends VaporFragment { constructor() { super([]) - this.anchor = __DEV__ ? createComment('teleport') : createTextNode() + this.anchor = createTextNode() } get currentParent(): ParentNode { @@ -104,7 +106,7 @@ export class TeleportFragment extends VaporFragment { } get parent(): ParentNode | null { - return this.anchor.parentNode + return this.anchor && this.anchor.parentNode } updateChildren(children: Block): void { @@ -120,7 +122,10 @@ export class TeleportFragment extends VaporFragment { insert((this.nodes = children), this.currentParent, this.currentAnchor) } - update(props: TeleportProps): void { + update(): void { + // not mounted yet + if (!this.parent) return + const mount = (parent: ParentNode, anchor: Node | null) => { insert( this.nodes, @@ -130,7 +135,10 @@ export class TeleportFragment extends VaporFragment { } const mountToTarget = () => { - const target = (this.target = resolveTeleportTarget(props, querySelector)) + const target = (this.target = resolveTeleportTarget( + this.props!, + querySelector, + )) if (target) { if ( // initial mount into target @@ -153,29 +161,12 @@ export class TeleportFragment extends VaporFragment { } // mount into main container - if (isTeleportDisabled(props)) { - if (this.parent) { - if (!this.mainAnchor) { - this.mainAnchor = __DEV__ - ? createComment('teleport end') - : createTextNode() - } - if (!this.placeholder) { - this.placeholder = __DEV__ - ? createComment('teleport start') - : createTextNode() - } - if (!this.mainAnchor.isConnected) { - insert(this.placeholder, this.parent, this.anchor) - insert(this.mainAnchor, this.parent, this.anchor) - } - - mount(this.parent, this.mainAnchor) - } + if (isTeleportDisabled(this.props!)) { + mount(this.parent, this.mainAnchor!) } // mount into target container else { - if (isTeleportDeferred(props)) { + if (isTeleportDeferred(this.props!)) { queuePostFlushCb(mountToTarget) } else { mountToTarget() @@ -183,6 +174,17 @@ export class TeleportFragment extends VaporFragment { } } + insert = (container: ParentNode, anchor: Node | null): void => { + // insert anchors in the main view + this.placeholder = __DEV__ + ? createComment('teleport start') + : createTextNode() + this.mainAnchor = __DEV__ ? createComment('teleport end') : createTextNode() + insert(this.placeholder, container, anchor) + insert(this.mainAnchor, container, anchor) + this.update() + } + remove = (parent: ParentNode | undefined = this.parent!): void => { // remove nodes if (this.nodes) { diff --git a/packages/runtime-vapor/src/fragment.ts b/packages/runtime-vapor/src/fragment.ts index f5b70b08b..58f9d9379 100644 --- a/packages/runtime-vapor/src/fragment.ts +++ b/packages/runtime-vapor/src/fragment.ts @@ -41,8 +41,6 @@ export class VaporFragment remove?: (parent?: ParentNode, transitionHooks?: TransitionHooks) => void fallback?: BlockFn - target?: ParentNode | null - targetAnchor?: Node | null getNodes?: () => Block setRef?: (comp: VaporComponentInstance) => void