Files
owl/src/app/app.ts
T

106 lines
3.0 KiB
TypeScript
Raw Normal View History

2021-11-13 09:02:34 +01:00
import { Component } from "../component/component";
import { ComponentNode } from "../component/component_node";
import { MountOptions } from "../component/fibers";
import { Scheduler } from "../component/scheduler";
2021-12-20 17:32:32 +01:00
import { TemplateSet, TemplateSetConfig } from "./template_set";
import { nodeErrorHandlers } from "../component/error_handling";
2020-11-26 16:45:25 +01:00
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
export interface Env {
[key: string]: any;
}
2021-12-20 17:32:32 +01:00
export interface AppConfig extends TemplateSetConfig {
env?: Env;
2021-12-20 17:32:32 +01:00
props?: any;
2021-10-20 16:08:31 +02:00
}
export const DEV_MSG = `Owl is running in 'dev' mode.
This is not suitable for production use.
See https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode for more information.`;
2020-11-26 16:45:25 +01:00
export class App<T extends typeof Component = any> extends TemplateSet {
Root: T;
props: any;
2021-12-20 17:32:32 +01:00
env: Env;
scheduler = new Scheduler();
2020-11-26 16:45:25 +01:00
root: ComponentNode | null = null;
2021-12-20 17:32:32 +01:00
constructor(Root: T, config: AppConfig = {}) {
super(config);
2020-11-26 16:45:25 +01:00
this.Root = Root;
2021-10-20 16:08:31 +02:00
if (config.dev) {
console.info(DEV_MSG);
}
2021-12-20 17:32:32 +01:00
const descrs = Object.getOwnPropertyDescriptors(config.env || {});
this.env = Object.freeze(Object.defineProperties({}, descrs));
this.props = config.props || {};
2020-11-26 16:45:25 +01:00
}
mount(target: HTMLElement, options?: MountOptions): Promise<InstanceType<T>> {
this.checkTarget(target);
const node = this.makeNode(this.Root, this.props);
const prom = this.mountNode(node, target, options);
this.root = node;
return prom;
}
checkTarget(target: HTMLElement) {
2020-11-26 16:45:25 +01:00
if (!(target instanceof HTMLElement)) {
throw new Error("Cannot mount component: the target is not a valid DOM element");
}
if (!document.body.contains(target)) {
throw new Error("Cannot mount a component on a detached dom node");
}
}
makeNode(Component: T, props: any): ComponentNode {
return new ComponentNode(Component, props, this);
}
mountNode(node: ComponentNode, target: HTMLElement, options?: MountOptions) {
const promise: any = new Promise((resolve, reject) => {
2021-12-15 15:55:13 +01:00
let isResolved = false;
// manually set a onMounted callback.
// that way, we are independant from the current node.
node.mounted.push(() => {
resolve(node.component);
2021-12-15 15:55:13 +01:00
isResolved = true;
});
// Manually add the last resort error handler on the node
let handlers = nodeErrorHandlers.get(node);
if (!handlers) {
handlers = [];
nodeErrorHandlers.set(node, handlers);
}
handlers.unshift((e) => {
2021-12-15 15:55:13 +01:00
if (isResolved) {
console.error(e);
} else {
reject(e);
}
throw e;
});
});
node.mountComponent(target, options);
return promise;
2020-11-26 16:45:25 +01:00
}
destroy() {
if (this.root) {
this.root.destroy();
}
}
}
2021-12-20 17:32:32 +01:00
export async function mount<T extends typeof Component>(
C: T,
target: HTMLElement,
config: AppConfig & MountOptions = {}
): Promise<InstanceType<T>> {
return new App(C, config).mount(target, config);
}