Files
owl/src/runtime/portal.ts
T

91 lines
2.2 KiB
TypeScript
Raw Normal View History

2022-09-23 11:22:42 +02:00
import { onMounted, onWillUnmount } from "./lifecycle_hooks";
2021-11-16 16:47:20 +01:00
import { BDom, text, VNode } from "./blockdom";
2022-05-26 23:45:40 +02:00
import { Component } from "./component";
import { OwlError } from "./error_handling";
2019-11-15 13:24:07 +01:00
2020-11-26 16:45:25 +01:00
const VText: any = text("").constructor;
2019-11-15 13:24:07 +01:00
class VPortal extends VText implements Partial<VNode<VPortal>> {
2022-09-23 11:22:42 +02:00
content: BDom | null;
selector: string;
2020-11-26 16:45:25 +01:00
target: HTMLElement | null = null;
2019-11-15 13:24:07 +01:00
2022-09-23 11:22:42 +02:00
constructor(selector: string, content: BDom) {
2020-11-26 16:45:25 +01:00
super("");
this.selector = selector;
2022-09-23 11:22:42 +02:00
this.content = content;
2019-11-15 13:24:07 +01:00
}
2022-09-23 11:22:42 +02:00
2020-11-26 16:45:25 +01:00
mount(parent: HTMLElement, anchor: ChildNode) {
super.mount(parent, anchor);
this.target = document.querySelector(this.selector) as any;
2022-09-23 11:22:42 +02:00
if (this.target) {
this.content!.mount(this.target!, null);
} else {
this.content!.mount(parent, anchor);
2019-11-15 13:24:07 +01:00
}
}
2020-11-26 16:45:25 +01:00
beforeRemove() {
2022-09-23 11:22:42 +02:00
this.content!.beforeRemove();
2019-11-15 13:24:07 +01:00
}
2020-11-26 16:45:25 +01:00
remove() {
2022-09-23 11:22:42 +02:00
if (this.content) {
super.remove();
2022-09-23 11:22:42 +02:00
this.content!.remove();
this.content = null;
}
2019-11-15 13:24:07 +01:00
}
2020-11-26 16:45:25 +01:00
patch(other: VPortal) {
super.patch(other);
2022-09-23 11:22:42 +02:00
if (this.content) {
this.content.patch(other.content!, true);
2020-11-26 16:45:25 +01:00
} else {
2022-09-23 11:22:42 +02:00
this.content = other.content;
this.content!.mount(this.target!, null);
2019-11-15 13:24:07 +01:00
}
2020-11-26 16:45:25 +01:00
}
}
/**
2022-09-23 11:22:42 +02:00
* kind of similar to <t t-slot="default"/>, but it wraps it around a VPortal
*/
export function portalTemplate(app: any, bdom: any, helpers: any) {
let { callSlot } = helpers;
2022-09-23 11:22:42 +02:00
return function template(ctx: any, node: any, key = ""): any {
return new VPortal(ctx.props.target, callSlot(ctx, node, key, "default", false, null));
};
}
export class Portal extends Component {
static template = "__portal__";
static props = {
target: {
type: String,
},
slots: true,
};
setup() {
2022-09-23 11:22:42 +02:00
const node: any = this.__owl__;
onMounted(() => {
const portal: VPortal = node.bdom;
if (!portal.target) {
const target: HTMLElement = document.querySelector(this.props.target);
if (target) {
portal.content!.moveBeforeDOMNode(target.firstChild, target);
2022-09-23 11:22:42 +02:00
} else {
throw new OwlError("invalid portal target");
}
}
});
2022-09-23 11:22:42 +02:00
onWillUnmount(() => {
const portal: VPortal = node.bdom;
portal.remove();
});
}
}