? new TeleportFragment('teleport')
: new TeleportFragment()
- const resolvedProps = new Proxy(
- props,
- rawPropsProxyHandlers,
- ) as any as TeleportProps
-
let children: Block
-
renderEffect(() => {
frag.updateChildren(
(children = slots.default && (slots.default as BlockFn)()),
})
renderEffect(() => {
- // access the props to trigger tracking
- frag.update(extend({}, resolvedProps), children!)
+ frag.update(
+ // access the props to trigger tracking
+ extend(
+ {},
+ new Proxy(props, rawPropsProxyHandlers) as any as TeleportProps,
+ ),
+ children!,
+ )
})
return frag
},
}
-export class TeleportFragment extends VaporFragment {
+class TeleportFragment extends VaporFragment {
anchor: Node
- targetStart?: Node | null
- mainAnchor?: Node
- placeholder?: Node
- container?: ParentNode | null
- currentAnchor?: Node | null
+
+ private targetStart?: Node
+ private mainAnchor?: Node
+ private placeholder?: Node
+ private mountContainer?: ParentNode | null
+ private mountAnchor?: Node | null
constructor(anchorLabel?: string) {
super([])
__DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode()
}
- updateChildren(children: Block): void {
- const parent = this.anchor.parentNode
- if (!parent) return
+ get currentParent(): ParentNode {
+ return (this.mountContainer || this.parent)!
+ }
- const container = this.container || parent
+ get currentAnchor(): Node | null {
+ return this.mountAnchor || this.anchor
+ }
- // teardown previous
- remove(this.nodes, container)
+ get parent(): ParentNode | null {
+ return this.anchor.parentNode
+ }
+
+ updateChildren(children: Block): void {
+ // not mounted yet, early return
+ if (!this.parent) return
- insert(
- (this.nodes = children),
- container,
- this.currentAnchor || this.anchor,
- )
+ // teardown previous children
+ remove(this.nodes, this.currentParent)
+
+ // mount new
+ insert((this.nodes = children), this.currentParent, this.currentAnchor)
}
update(props: TeleportProps, children: Block): void {
- const parent = this.anchor.parentNode
this.nodes = children
- const disabled = isTeleportDisabled(props)
const mount = (parent: ParentNode, anchor: Node | null) => {
insert(
this.nodes,
- (this.container = parent),
- (this.currentAnchor = anchor),
+ (this.mountContainer = parent),
+ (this.mountAnchor = anchor),
)
}
const target = (this.target = resolveTarget(props, querySelector))
if (target) {
if (
- // initial mount
- !this.targetStart ||
+ // initial mount into target
+ !this.targetAnchor ||
// target changed
- this.targetStart.parentNode !== target
+ this.targetAnchor.parentNode !== target
) {
;[this.targetAnchor, this.targetStart] = prepareAnchor(target)
}
mount(target, this.targetAnchor!)
} else if (__DEV__) {
warn(
- `Invalid Teleport target on ${this.targetStart ? 'update' : 'mount'}:`,
+ `Invalid Teleport target on ${this.targetAnchor ? 'update' : 'mount'}:`,
target,
`(${typeof target})`,
)
}
}
- if (parent && disabled) {
- 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, parent, this.anchor)
- insert(this.mainAnchor, parent, this.anchor)
- }
+ // 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(parent, this.mainAnchor)
+ mount(this.parent, this.mainAnchor)
+ }
}
-
- if (!disabled) {
+ // mount into target container
+ else {
if (isTeleportDeferred(props)) {
queuePostFlushCb(mountToTarget)
} else {
remove = (parent: ParentNode | undefined): void => {
// remove nodes
- remove(this.nodes, this.container || parent)
+ remove(this.nodes, this.currentParent)
// remove anchors
if (this.targetStart) {
- let parentNode = this.targetStart.parentNode!
- remove(this.targetStart!, parentNode)
- remove(this.targetAnchor!, parentNode)
+ remove(this.targetStart!, this.target!)
+ remove(this.targetAnchor!, this.target!)
}
if (this.placeholder) {
remove(this.placeholder!, parent)
}
function prepareAnchor(target: ParentNode | null) {
- const targetStart = createTextNode('')
+ const targetStart = createTextNode('') as Text & { [TeleportEndKey]: Node }
const targetAnchor = createTextNode('')
// attach a special property, so we can skip teleported content in
// renderer's nextSibling search
- // @ts-expect-error
targetStart[TeleportEndKey] = targetAnchor
if (target) {