[IMP] app: mount app in "first-child" position

We reintroduce the possibility to mount the app in first position in
a target. The option "self" has been dropped since it is now possible
for a component to have several top level nodes.
This commit is contained in:
Mathieu Duckerts-Antoine
2021-10-22 17:30:20 +02:00
committed by Aaron Bohy
parent aa3148eddf
commit ebd2e4324f
7 changed files with 92 additions and 76 deletions
+3 -2
View File
@@ -1,5 +1,6 @@
import { Component } from "./component/component";
import { ComponentNode } from "./component/component_node";
import { MountOptions } from "./component/fibers";
import { Scheduler } from "./component/scheduler";
import { TemplateSet } from "./qweb/template_helpers";
@@ -47,7 +48,7 @@ export class App<T extends typeof Component = any> extends TemplateSet {
}
}
mount(target: HTMLElement): Promise<InstanceType<T>> {
mount(target: HTMLElement, options?: MountOptions): Promise<InstanceType<T>> {
if (!(target instanceof HTMLElement)) {
throw new Error("Cannot mount component: the target is not a valid DOM element");
}
@@ -56,7 +57,7 @@ export class App<T extends typeof Component = any> extends TemplateSet {
}
const node = new ComponentNode(this.Root, this.props, this);
this.root = node;
return node.mountComponent(target);
return node.mountComponent(target, options);
}
destroy() {
+2 -2
View File
@@ -23,8 +23,8 @@ export interface VNode<T = any> {
export type BDom = VNode<any>;
export function mount(vnode: VNode, fixture: HTMLElement) {
vnode.mount(fixture, null);
export function mount(vnode: VNode, fixture: HTMLElement, afterNode: Node | null = null) {
vnode.mount(fixture, afterNode);
}
export function patch(vnode1: VNode, vnode2: VNode, withBeforeRemove: boolean = false) {
+3 -2
View File
@@ -6,6 +6,7 @@ import {
makeChildFiber,
makeRootFiber,
MountFiber,
MountOptions,
RootFiber,
__internal__destroyed,
} from "./fibers";
@@ -92,8 +93,8 @@ export class ComponentNode<T extends typeof Component = any> implements VNode<Co
this.component.setup();
}
mountComponent(target: any): Promise<InstanceType<T>> {
const fiber = new MountFiber(this, target);
mountComponent(target: any, options?: MountOptions): Promise<InstanceType<T>> {
const fiber = new MountFiber(this, target, options);
this.app.scheduler.addFiber(fiber);
this.initiateRender(fiber);
return fiber.promise.then(() => this.component);
+15 -2
View File
@@ -158,17 +158,30 @@ export class RootFiber extends Fiber {
export let __internal__destroyed: ComponentNode[] = [];
type Position = "first-child" | "last-child";
export interface MountOptions {
position?: Position;
}
export class MountFiber extends RootFiber {
target: HTMLElement;
position: Position;
constructor(node: ComponentNode, target: HTMLElement) {
constructor(node: ComponentNode, target: HTMLElement, options: MountOptions = {}) {
super(node);
this.target = target;
this.position = options.position || "last-child";
}
complete() {
const node = this.node;
node.bdom = this.bdom;
mount(node.bdom!, this.target);
if (this.position === "last-child" || this.target.childNodes.length === 0) {
mount(node.bdom!, this.target);
} else {
const firstChild = this.target.childNodes[0];
mount(node.bdom!, this.target, firstChild);
}
node.status = STATUS.MOUNTED;
this.appliedToDom = true;
let current;
+1 -2
View File
@@ -6,7 +6,7 @@ import { Component } from "./component";
* Note that this method does modify in place the props
*/
export function applyDefaultProps(props: { [key: string]: any }, ComponentClass: typeof Component) {
const defaultProps = (ComponentClass as any).defaultProps
const defaultProps = (ComponentClass as any).defaultProps;
if (defaultProps) {
for (let propName in defaultProps) {
if (props![propName] === undefined) {
@@ -16,7 +16,6 @@ export function applyDefaultProps(props: { [key: string]: any }, ComponentClass:
}
}
//------------------------------------------------------------------------------
// Prop validation helper
//------------------------------------------------------------------------------