type LooseRawProps,
type VaporComponent,
createComponent as createComp,
+ createComponent,
} from '../../src/component'
import {
type VaporDirective,
child,
createIf,
createTemplateRefSetter,
+ createVaporApp,
defineVaporComponent,
renderEffect,
setInsertionState,
setText,
template,
+ vaporInteropPlugin,
withVaporDirectives,
} from '@vue/runtime-vapor'
import { makeRender } from '../_utils'
import {
+ h,
nextTick,
onBeforeUnmount,
onMounted,
mount(root)
expect(root.innerHTML).toBe(
- '<!--teleport--><div id="target"><div>teleported</div></div>',
+ '<!--teleport start--><!--teleport end--><div id="target"><div>teleported</div></div>',
)
})
show.value = true
await nextTick()
expect(root.innerHTML).toBe(
- `<!--teleport--><div>Footer</div><div id="targetId"><div>bar</div></div><!--if-->`,
+ `<!--teleport start--><!--teleport end--><div>Footer</div><div id="targetId"><div>bar</div></div><!--if-->`,
)
})
})
createRecord(parentId, Parent as any)
mount(root)
- expect(root.innerHTML).toBe('<!--teleport--><div>root</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root</div>',
+ )
expect(target.innerHTML).toBe('<div>teleported</div>')
// rerender child
return template('<div>teleported 2</div>')()
})
- expect(root.innerHTML).toBe('<!--teleport--><div>root</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root</div>',
+ )
expect(target.innerHTML).toBe('<div>teleported 2</div>')
// rerender parent
return [n0, n1]
})
- expect(root.innerHTML).toBe('<!--teleport--><div>root 2</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root 2</div>',
+ )
expect(target.innerHTML).toBe('<div>teleported 2</div>')
})
mount(root)
expect(root.innerHTML).toBe(
- '<div><div>teleported</div><!--teleport--><div>root</div></div>',
+ '<div><!--teleport start--><div>teleported</div><!--teleport end--><div>root</div></div>',
)
expect(target.innerHTML).toBe('')
})
expect(root.innerHTML).toBe(
- '<div><div>teleported</div><!--teleport--><div>root 2</div></div>',
+ '<div><!--teleport start--><div>teleported</div><!--teleport end--><div>root 2</div></div>',
)
expect(target.innerHTML).toBe('')
// toggle disabled
disabled.value = false
await nextTick()
- expect(root.innerHTML).toBe('<div><!--teleport--><div>root 2</div></div>')
+ expect(root.innerHTML).toBe(
+ '<div><!--teleport start--><!--teleport end--><div>root 2</div></div>',
+ )
expect(target.innerHTML).toBe('<div>teleported</div>')
})
createRecord(parentId, Parent as any)
mount(root)
- expect(root.innerHTML).toBe('<!--teleport--><div>root</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root</div>',
+ )
expect(target.innerHTML).toBe('<div>teleported</div>')
// reload child by changing msg
return [n0]
},
})
- expect(root.innerHTML).toBe('<!--teleport--><div>root</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root</div>',
+ )
expect(target.innerHTML).toBe('<div>teleported 2</div>')
// reload parent by changing msg
},
})
- expect(root.innerHTML).toBe('<!--teleport--><div>root 2</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root 2</div>',
+ )
expect(target.innerHTML).toBe('<div>teleported 2</div>')
// reload parent again by changing disabled
})
expect(root.innerHTML).toBe(
- '<div>teleported 2</div><!--teleport--><div>root 2</div>',
+ '<!--teleport start--><div>teleported 2</div><!--teleport end--><div>root 2</div>',
)
expect(target.innerHTML).toBe('')
})
mount(root)
expect(root.innerHTML).toBe(
- '<div>teleported</div><!--teleport--><div>root</div>',
+ '<!--teleport start--><div>teleported</div><!--teleport end--><div>root</div>',
)
expect(target.innerHTML).toBe('')
},
})
expect(root.innerHTML).toBe(
- '<div>teleported 2</div><!--teleport--><div>root</div>',
+ '<!--teleport start--><div>teleported 2</div><!--teleport end--><div>root</div>',
)
expect(target.innerHTML).toBe('')
},
})
expect(root.innerHTML).toBe(
- '<div>teleported 3</div><!--teleport--><div>root</div>',
+ '<!--teleport start--><div>teleported 3</div><!--teleport end--><div>root</div>',
)
expect(target.innerHTML).toBe('')
// toggle disabled
disabled.value = false
await nextTick()
- expect(root.innerHTML).toBe('<!--teleport--><div>root</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root</div>',
+ )
expect(target.innerHTML).toBe('<div>teleported 3</div>')
})
mount(root)
expect(root.innerHTML).toBe(
- '<div>teleported</div><span>child</span><!--teleport--><div>root</div>',
+ '<!--teleport start--><div>teleported</div><span>child</span><!--teleport end--><div>root</div>',
)
expect(target.innerHTML).toBe('')
},
})
expect(root.innerHTML).toBe(
- '<div>teleported 2</div><span>child</span><!--teleport--><div>root</div>',
+ '<!--teleport start--><div>teleported 2</div><span>child</span><!--teleport end--><div>root</div>',
)
expect(target.innerHTML).toBe('')
},
})
expect(root.innerHTML).toBe(
- '<div>teleported 3</div><span>child</span><!--teleport--><div>root</div>',
+ '<!--teleport start--><div>teleported 3</div><span>child</span><!--teleport end--><div>root</div>',
)
expect(target.innerHTML).toBe('')
// toggle disabled
disabled.value = false
await nextTick()
- expect(root.innerHTML).toBe('<!--teleport--><div>root</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root</div>',
+ )
expect(target.innerHTML).toBe('<div>teleported 3</div><span>child</span>')
})
})
+
+ 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(
+ '<!--teleport start--><h1>vdom comp</h1><!--teleport end-->',
+ )
+
+ disabled.value = false
+ await nextTick()
+ expect(root.innerHTML).toBe('<!--teleport start--><!--teleport end-->')
+ expect(target.innerHTML).toBe('<h1>vdom comp</h1>')
+ })
+ })
})
function runSharedTests(deferMode: boolean): void {
}).create()
mount(root)
- expect(root.innerHTML).toBe('<!--teleport--><div>root</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root</div>',
+ )
expect(target.innerHTML).toBe('<div>teleported</div>')
})
}).create()
mount(root)
- expect(root.innerHTML).toBe('<!--teleport--><div>root</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root</div>',
+ )
expect(targetA.innerHTML).toBe('<div>teleported</div>')
expect(targetB.innerHTML).toBe('')
target.value = targetB
await nextTick()
- expect(root.innerHTML).toBe('<!--teleport--><div>root</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root</div>',
+ )
expect(targetA.innerHTML).toBe('')
expect(targetB.innerHTML).toBe('<div>teleported</div>')
})
},
}).create()
mount(root)
- expect(root.innerHTML).toBe('<div><!--teleport--><!--teleport--></div>')
+ expect(root.innerHTML).toBe(
+ '<div><!--teleport start--><!--teleport end--><!--teleport start--><!--teleport end--></div>',
+ )
expect(target.innerHTML).toBe('<div>one</div>two')
// update existing content
// toggling
child1.value = [] as any
await nextTick()
- expect(root.innerHTML).toBe('<div><!--teleport--><!--teleport--></div>')
+ expect(root.innerHTML).toBe(
+ '<div><!--teleport start--><!--teleport end--><!--teleport start--><!--teleport end--></div>',
+ )
expect(target.innerHTML).toBe('three')
// toggle back
] as any
child2.value = [template('three')()] as any
await nextTick()
- expect(root.innerHTML).toBe('<div><!--teleport--><!--teleport--></div>')
+ expect(root.innerHTML).toBe(
+ '<div><!--teleport start--><!--teleport end--><!--teleport start--><!--teleport end--></div>',
+ )
// should append
expect(target.innerHTML).toBe('<div>one</div><div>two</div>three')
// toggle the other teleport
child2.value = [] as any
await nextTick()
- expect(root.innerHTML).toBe('<div><!--teleport--><!--teleport--></div>')
+ expect(root.innerHTML).toBe(
+ '<div><!--teleport start--><!--teleport end--><!--teleport start--><!--teleport end--></div>',
+ )
expect(target.innerHTML).toBe('<div>one</div><div>two</div>')
})
mount(root)
expect(root.innerHTML).toBe(
- '<div></div><div>teleported</div><!--teleport-->',
+ '<div></div><!--teleport start--><div>teleported</div><!--teleport end-->',
)
disabled.value = false
await nextTick()
expect(root.innerHTML).toBe(
- '<div><div>teleported</div></div><!--teleport-->',
+ '<div><div>teleported</div></div><!--teleport start--><!--teleport end-->',
)
})
}).create()
mount(root)
- expect(root.innerHTML).toBe('<!--teleport--><div>root</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><div>root</div>',
+ )
expect(target.innerHTML).toBe('<div>teleported</div>')
disabled.value = true
await nextTick()
expect(root.innerHTML).toBe(
- '<!--teleport start--><div>teleported</div><!--teleport end--><!--teleport--><div>root</div>',
+ '<!--teleport start--><div>teleported</div><!--teleport end--><div>root</div>',
)
expect(target.innerHTML).toBe('')
disabled.value = false
await nextTick()
expect(root.innerHTML).toBe(
- '<!--teleport start--><!--teleport end--><!--teleport--><div>root</div>',
+ '<!--teleport start--><!--teleport end--><div>root</div>',
)
expect(target.innerHTML).toBe('<div>teleported</div>')
})
}).create()
mount(root)
- expect(root.innerHTML).toBe('<!--teleport-->')
+ expect(root.innerHTML).toBe('<!--teleport start--><!--teleport end-->')
expect(target.innerHTML).toBe('<div>foo</div><!--if-->')
expect(spy).toHaveBeenCalledTimes(1)
expect(teardown).not.toHaveBeenCalled()
toggle.value = false
await nextTick()
- expect(root.innerHTML).toBe('<!--teleport-->')
+ expect(root.innerHTML).toBe('<!--teleport start--><!--teleport end-->')
expect(target.innerHTML).toBe('<!--if-->')
expect(spy).toHaveBeenCalledTimes(1)
expect(teardown).toHaveBeenCalledTimes(1)
show.value = true
await nextTick()
- expect(root.innerHTML).toBe('<!--teleport--><!--if--><div>teleported</div>')
+ expect(root.innerHTML).toBe(
+ '<!--teleport start--><!--teleport end--><!--if--><div>teleported</div>',
+ )
show.value = false
await nextTick()
parentShow.value = true
await nextTick()
expect(root.innerHTML).toBe(
- '<!--teleport--><!--if--><!--if--><div>foo</div>',
+ '<!--teleport start--><!--teleport end--><!--if--><!--if--><div>foo</div>',
)
parentShow.value = false
)
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__) {
}
export class TeleportFragment extends VaporFragment {
+ target?: ParentNode | null
+ targetAnchor?: Node | null
anchor: Node
+ props?: TeleportProps
private targetStart?: Node
private mainAnchor?: Node
constructor() {
super([])
- this.anchor = __DEV__ ? createComment('teleport') : createTextNode()
+ this.anchor = createTextNode()
}
get currentParent(): ParentNode {
}
get parent(): ParentNode | null {
- return this.anchor.parentNode
+ return this.anchor && this.anchor.parentNode
}
updateChildren(children: Block): void {
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,
}
const mountToTarget = () => {
- const target = (this.target = resolveTeleportTarget(props, querySelector))
+ const target = (this.target = resolveTeleportTarget(
+ this.props!,
+ querySelector,
+ ))
if (target) {
if (
// initial mount into target
}
// 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()
}
}
+ 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) {