// Teleport *always* has Array children. This is enforced in both the
// compiler and vnode children normalization.
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
- if (parentComponent && parentComponent.isCE) {
- parentComponent.ce!._teleportTarget = container
- }
mountChildren(
children as VNodeArrayChildren,
container,
} else if (namespace !== 'mathml' && isTargetMathML(target)) {
namespace = 'mathml'
}
+
+ // track CE teleport targets
+ if (parentComponent && parentComponent.isCE) {
+ ;(
+ parentComponent.ce!._teleportTargets ||
+ (parentComponent.ce!._teleportTargets = new Set())
+ ).add(target)
+ }
+
if (!disabled) {
mount(target, targetAnchor)
updateCssVars(n2, false)
app.unmount()
})
+ test('render two Teleports w/ shadowRoot false', async () => {
+ const target1 = document.createElement('div')
+ const target2 = document.createElement('span')
+ const Child = defineCustomElement(
+ {
+ render() {
+ return [
+ h(Teleport, { to: target1 }, [renderSlot(this.$slots, 'header')]),
+ h(Teleport, { to: target2 }, [renderSlot(this.$slots, 'body')]),
+ ]
+ },
+ },
+ { shadowRoot: false },
+ )
+ customElements.define('my-el-two-teleport-child', Child)
+
+ const App = {
+ render() {
+ return h('my-el-two-teleport-child', null, {
+ default: () => [
+ h('div', { slot: 'header' }, 'header'),
+ h('span', { slot: 'body' }, 'body'),
+ ],
+ })
+ },
+ }
+ const app = createApp(App)
+ app.mount(container)
+ await nextTick()
+ expect(target1.outerHTML).toBe(
+ `<div><div slot="header">header</div></div>`,
+ )
+ expect(target2.outerHTML).toBe(
+ `<span><span slot="body">body</span></span>`,
+ )
+ app.unmount()
+ })
+
+ test('render two Teleports w/ shadowRoot false (with disabled)', async () => {
+ const target1 = document.createElement('div')
+ const target2 = document.createElement('span')
+ const Child = defineCustomElement(
+ {
+ render() {
+ return [
+ // with disabled: true
+ h(Teleport, { to: target1, disabled: true }, [
+ renderSlot(this.$slots, 'header'),
+ ]),
+ h(Teleport, { to: target2 }, [renderSlot(this.$slots, 'body')]),
+ ]
+ },
+ },
+ { shadowRoot: false },
+ )
+ customElements.define('my-el-two-teleport-child-0', Child)
+
+ const App = {
+ render() {
+ return h('my-el-two-teleport-child-0', null, {
+ default: () => [
+ h('div', { slot: 'header' }, 'header'),
+ h('span', { slot: 'body' }, 'body'),
+ ],
+ })
+ },
+ }
+ const app = createApp(App)
+ app.mount(container)
+ await nextTick()
+ expect(target1.outerHTML).toBe(`<div></div>`)
+ expect(target2.outerHTML).toBe(
+ `<span><span slot="body">body</span></span>`,
+ )
+ app.unmount()
+ })
+
test('toggle nested custom element with shadowRoot: false', async () => {
customElements.define(
'my-el-child-shadow-false',
/**
* @internal
*/
- _teleportTarget?: HTMLElement
+ _teleportTargets?: Set<Element>
private _connected = false
private _resolved = false
this._app && this._app.unmount()
if (this._instance) this._instance.ce = undefined
this._app = this._instance = null
+ if (this._teleportTargets) {
+ this._teleportTargets.clear()
+ this._teleportTargets = undefined
+ }
}
})
}
* Only called when shadowRoot is false
*/
private _renderSlots() {
- const outlets = (this._teleportTarget || this).querySelectorAll('slot')
+ const outlets = this._getSlots()
const scopeId = this._instance!.type.__scopeId
for (let i = 0; i < outlets.length; i++) {
const o = outlets[i] as HTMLSlotElement
}
}
+ /**
+ * @internal
+ */
+ private _getSlots(): HTMLSlotElement[] {
+ const roots: Element[] = [this]
+ if (this._teleportTargets) {
+ roots.push(...this._teleportTargets)
+ }
+ return roots.reduce<HTMLSlotElement[]>((res, i) => {
+ res.push(...Array.from(i.querySelectorAll('slot')))
+ return res
+ }, [])
+ }
/**
* @internal
*/