mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
6f23b18cab
Previously, when trying to mount a portal into a target that would be mounted in the same render as the portal itself, the portal content could not be mounted correctly. In a previous attempt to fix it, a mistake was made while writing the test causing us to incorrectly believe in now worked, when in fact, it would just move the portal content to the end of its parent but without changing it. This commit fixes the issue by making the `moveBeforeDOMNode` method of VNodes accept a second optional parameter which is the parent in which the element should be moved, defaulting to the current parent. This results in identical behaviour when a parent is not specified, but when it is, the VNode is "reparented" to the passed parent, which is what is now done by the Portal
92 lines
2.1 KiB
TypeScript
92 lines
2.1 KiB
TypeScript
import type { VNode } from "./index";
|
|
|
|
const getDescriptor = (o: any, p: any) => Object.getOwnPropertyDescriptor(o, p)!;
|
|
const nodeProto = Node.prototype;
|
|
const characterDataProto = CharacterData.prototype;
|
|
|
|
const nodeInsertBefore = nodeProto.insertBefore;
|
|
const characterDataSetData = getDescriptor(characterDataProto, "data").set!;
|
|
const nodeRemoveChild = nodeProto.removeChild;
|
|
|
|
abstract class VSimpleNode {
|
|
text: string | String;
|
|
parentEl?: HTMLElement | undefined;
|
|
el?: any;
|
|
|
|
constructor(text: string | String) {
|
|
this.text = text;
|
|
}
|
|
|
|
mountNode(node: Node, parent: HTMLElement, afterNode: Node | null) {
|
|
this.parentEl = parent;
|
|
nodeInsertBefore.call(parent, node, afterNode);
|
|
this.el = node;
|
|
}
|
|
|
|
moveBeforeDOMNode(node: Node | null, parent = this.parentEl) {
|
|
this.parentEl = parent;
|
|
nodeInsertBefore.call(parent, this.el!, node);
|
|
}
|
|
|
|
moveBeforeVNode(other: VText | null, afterNode: Node | null) {
|
|
nodeInsertBefore.call(this.parentEl, this.el!, other ? other.el! : afterNode);
|
|
}
|
|
|
|
beforeRemove() {}
|
|
|
|
remove() {
|
|
nodeRemoveChild.call(this.parentEl, this.el!);
|
|
}
|
|
|
|
firstNode(): Node {
|
|
return this.el!;
|
|
}
|
|
|
|
toString() {
|
|
return this.text;
|
|
}
|
|
}
|
|
|
|
class VText extends VSimpleNode {
|
|
mount(parent: HTMLElement, afterNode: Node | null) {
|
|
this.mountNode(document.createTextNode(toText(this.text)), parent, afterNode);
|
|
}
|
|
|
|
patch(other: VText) {
|
|
const text2 = other.text;
|
|
if (this.text !== text2) {
|
|
characterDataSetData.call(this.el!, toText(text2));
|
|
this.text = text2;
|
|
}
|
|
}
|
|
}
|
|
|
|
class VComment extends VSimpleNode {
|
|
mount(parent: HTMLElement, afterNode: Node | null) {
|
|
this.mountNode(document.createComment(toText(this.text)), parent, afterNode);
|
|
}
|
|
|
|
patch() {}
|
|
}
|
|
|
|
export function text(str: string | String): VNode<VText> {
|
|
return new VText(str);
|
|
}
|
|
|
|
export function comment(str: string): VNode<VComment> {
|
|
return new VComment(str);
|
|
}
|
|
|
|
export function toText(value: any): string {
|
|
switch (typeof value) {
|
|
case "string":
|
|
return value;
|
|
case "number":
|
|
return String(value);
|
|
case "boolean":
|
|
return value ? "true" : "false";
|
|
default:
|
|
return value || "";
|
|
}
|
|
}
|