mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[REF] move component function to app, improve some code
This commit is contained in:
committed by
Sam Degueldre
parent
51538c2fea
commit
0e6059467f
@@ -264,9 +264,7 @@ export class CodeGenerator {
|
||||
tKeyExpr: null,
|
||||
});
|
||||
// define blocks and utility functions
|
||||
let mainCode = [
|
||||
` let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;`,
|
||||
];
|
||||
let mainCode = [` let { text, createBlock, list, multi, html, toggler, comment } = bdom;`];
|
||||
if (this.helpers.size) {
|
||||
mainCode.push(`let { ${[...this.helpers].join(", ")} } = helpers;`);
|
||||
}
|
||||
@@ -1178,8 +1176,15 @@ export class CodeGenerator {
|
||||
if (ctx.tKeyExpr) {
|
||||
keyArg = `${ctx.tKeyExpr} + ${keyArg}`;
|
||||
}
|
||||
const blockArgs = `${expr}, ${propString}, ${keyArg}, node, ctx`;
|
||||
let blockExpr = `component(${blockArgs})`;
|
||||
let id = generateId("comp");
|
||||
this.staticDefs.push({
|
||||
id,
|
||||
expr: `app.createComponent(${
|
||||
ast.isDynamic ? null : expr
|
||||
}, ${!ast.isDynamic}, ${!!ast.slots}, ${!!ast.dynamicProps})`,
|
||||
});
|
||||
|
||||
let blockExpr = `${id}(${propString}, ${keyArg}, node, ctx, ${ast.isDynamic ? expr : null})`;
|
||||
if (ast.isDynamic) {
|
||||
blockExpr = `toggler(${expr}, ${blockExpr})`;
|
||||
}
|
||||
@@ -1270,8 +1275,14 @@ export class CodeGenerator {
|
||||
this.helpers.add("capture");
|
||||
this.define(ctxStr, `capture(ctx)`);
|
||||
}
|
||||
let id = generateId("comp");
|
||||
this.staticDefs.push({
|
||||
id,
|
||||
expr: `app.createComponent(null, false, true, false)`,
|
||||
});
|
||||
|
||||
const target = compileExpr(ast.target);
|
||||
const blockString = `component(Portal, {target: ${target},slots: {'default': {__render: ${name}, __ctx: ${ctxStr}}}}, key + \`${key}\`, node, ctx)`;
|
||||
const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}, __ctx: ${ctxStr}}}}, key + \`${key}\`, node, ctx, Portal)`;
|
||||
if (block) {
|
||||
this.insertAnchor(block);
|
||||
}
|
||||
|
||||
+67
-5
@@ -1,11 +1,12 @@
|
||||
import { Component, ComponentConstructor } from "./component";
|
||||
import { Component, ComponentConstructor, Props } from "./component";
|
||||
import { ComponentNode } from "./component_node";
|
||||
import { MountOptions } from "./fibers";
|
||||
import { Scheduler } from "./scheduler";
|
||||
import { TemplateSet, TemplateSetConfig } from "./template_set";
|
||||
import { nodeErrorHandlers } from "./error_handling";
|
||||
import { validateTarget } from "./utils";
|
||||
import { Fiber, MountOptions } from "./fibers";
|
||||
import { Scheduler } from "./scheduler";
|
||||
import { STATUS } from "./status";
|
||||
import { validateProps } from "./template_helpers";
|
||||
import { TemplateSet, TemplateSetConfig } from "./template_set";
|
||||
import { validateTarget } from "./utils";
|
||||
|
||||
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
|
||||
|
||||
@@ -112,6 +113,67 @@ export class App<
|
||||
this.root.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
createComponent<P extends Props>(
|
||||
name: string | null,
|
||||
isStatic: boolean,
|
||||
hasSlotsProp: boolean,
|
||||
hasDynamicPropList: boolean
|
||||
) {
|
||||
const isDynamic = !isStatic;
|
||||
function _arePropsDifferent(props1: Props, props2: Props): boolean {
|
||||
for (let k in props1) {
|
||||
if (props1[k] !== props2[k]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return hasDynamicPropList && Object.keys(props1).length !== Object.keys(props2).length;
|
||||
}
|
||||
const arePropsDifferent = hasSlotsProp ? () => true : _arePropsDifferent;
|
||||
|
||||
return (props: P, key: string, ctx: ComponentNode, parent: any, C: any) => {
|
||||
let children = ctx.children;
|
||||
let node: any = children[key];
|
||||
|
||||
if (node && node.status === STATUS.DESTROYED) {
|
||||
node = undefined;
|
||||
}
|
||||
if (isDynamic && node && node.component.constructor !== C) {
|
||||
node = undefined;
|
||||
}
|
||||
|
||||
const parentFiber = ctx.fiber!;
|
||||
if (node) {
|
||||
let shouldRender = node.forceNextRender;
|
||||
if (shouldRender) {
|
||||
node.forceNextRender = false;
|
||||
} else {
|
||||
const currentProps = node.props;
|
||||
shouldRender = parentFiber.deep || arePropsDifferent(currentProps, props);
|
||||
}
|
||||
if (shouldRender) {
|
||||
node.updateAndRender(props, parentFiber);
|
||||
}
|
||||
} else {
|
||||
// new component
|
||||
if (isStatic) {
|
||||
C = parent.constructor.components[name as any];
|
||||
if (!C) {
|
||||
throw new Error(`Cannot find the definition of component "${name}"`);
|
||||
} else if (!(C.prototype instanceof Component)) {
|
||||
throw new Error(
|
||||
`"${name}" is not a Component. It must inherit from the Component class`
|
||||
);
|
||||
}
|
||||
}
|
||||
node = new ComponentNode(C, props, this, ctx, key);
|
||||
children[key] = node;
|
||||
node.initiateRender(new Fiber(node, parentFiber));
|
||||
}
|
||||
parentFiber.childrenMap[key] = node;
|
||||
return node;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function mount<
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { ComponentNode } from "./component_node";
|
||||
// Component Class
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Props = { [key: string]: any };
|
||||
export type Props = { [key: string]: any };
|
||||
|
||||
interface StaticComponentProperties {
|
||||
template: string;
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import type { App, Env } from "./app";
|
||||
import { BDom, VNode } from "./blockdom";
|
||||
import { Component, ComponentConstructor, Props } from "./component";
|
||||
import { fibersInError, handleError } from "./error_handling";
|
||||
import { Fiber, makeChildFiber, makeRootFiber, MountFiber, MountOptions } from "./fibers";
|
||||
import {
|
||||
clearReactivesForCallback,
|
||||
getSubscriptions,
|
||||
NonReactive,
|
||||
Reactive,
|
||||
reactive,
|
||||
toRaw,
|
||||
TARGET,
|
||||
} from "./reactivity";
|
||||
import { batched, Callback } from "./utils";
|
||||
import { Component, ComponentConstructor } from "./component";
|
||||
import { fibersInError, handleError } from "./error_handling";
|
||||
import { Fiber, makeChildFiber, makeRootFiber, MountFiber, MountOptions } from "./fibers";
|
||||
import { STATUS } from "./status";
|
||||
import { batched, Callback } from "./utils";
|
||||
|
||||
let currentNode: ComponentNode | null = null;
|
||||
|
||||
@@ -31,14 +30,12 @@ export function useComponent(): Component {
|
||||
/**
|
||||
* Apply default props (only top level).
|
||||
*/
|
||||
function applyDefaultProps<P extends object>(props: P, defaultProps: Partial<P>): P {
|
||||
const result = Object.assign({} as any, props);
|
||||
function applyDefaultProps<P extends object>(props: P, defaultProps: Partial<P>) {
|
||||
for (let propName in defaultProps) {
|
||||
if (props[propName] === undefined) {
|
||||
result[propName] = defaultProps[propName];
|
||||
(props as any)[propName] = defaultProps[propName];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// -----------------------------------------------------------------------------
|
||||
// Integration with reactivity system (useState)
|
||||
@@ -67,72 +64,6 @@ export function useState<T extends object>(state: T): Reactive<T> | NonReactive<
|
||||
return reactive(state, render);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// component function (used in compiled template code)
|
||||
// -----------------------------------------------------------------------------
|
||||
type Props = { [key: string]: any };
|
||||
|
||||
function arePropsDifferent(props1: Props, props2: Props): boolean {
|
||||
for (let k in props1) {
|
||||
const prop1 = props1[k] && typeof props1[k] === "object" ? toRaw(props1[k]) : props1[k];
|
||||
const prop2 = props2[k] && typeof props2[k] === "object" ? toRaw(props2[k]) : props2[k];
|
||||
if (prop1 !== prop2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return Object.keys(props1).length !== Object.keys(props2).length;
|
||||
}
|
||||
|
||||
export function component<P extends Props>(
|
||||
name: string | ComponentConstructor<P>,
|
||||
props: P,
|
||||
key: string,
|
||||
ctx: ComponentNode,
|
||||
parent: any
|
||||
): ComponentNode<P> {
|
||||
let node: any = ctx.children[key];
|
||||
let isDynamic = typeof name !== "string";
|
||||
|
||||
if (node && node.status === STATUS.DESTROYED) {
|
||||
node = undefined;
|
||||
}
|
||||
if (isDynamic && node && node.component.constructor !== name) {
|
||||
node = undefined;
|
||||
}
|
||||
|
||||
const parentFiber = ctx.fiber!;
|
||||
if (node) {
|
||||
let shouldRender = node.forceNextRender;
|
||||
if (shouldRender) {
|
||||
node.forceNextRender = false;
|
||||
} else {
|
||||
const currentProps = node.props;
|
||||
shouldRender = parentFiber.deep || arePropsDifferent(currentProps, props);
|
||||
}
|
||||
if (shouldRender) {
|
||||
node.updateAndRender(props, parentFiber);
|
||||
}
|
||||
} else {
|
||||
// new component
|
||||
let C;
|
||||
if (isDynamic) {
|
||||
C = name;
|
||||
} else {
|
||||
C = parent.constructor.components[name as any];
|
||||
if (!C) {
|
||||
throw new Error(`Cannot find the definition of component "${name}"`);
|
||||
} else if (!(C.prototype instanceof Component)) {
|
||||
throw new Error(`"${name}" is not a Component. It must inherit from the Component class`);
|
||||
}
|
||||
}
|
||||
node = new ComponentNode(C, props, ctx.app, ctx, key);
|
||||
ctx.children[key] = node;
|
||||
node.initiateRender(new Fiber(node, parentFiber));
|
||||
}
|
||||
parentFiber.childrenMap[key] = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Component VNode class
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -179,8 +110,9 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
|
||||
this.parentKey = parentKey;
|
||||
this.level = parent ? parent.level + 1 : 0;
|
||||
const defaultProps = C.defaultProps;
|
||||
props = Object.assign({}, props);
|
||||
if (defaultProps) {
|
||||
props = applyDefaultProps(props, defaultProps);
|
||||
applyDefaultProps(props, defaultProps);
|
||||
}
|
||||
const env = (parent && parent.childEnv) || app.env;
|
||||
this.childEnv = env;
|
||||
@@ -297,13 +229,14 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
|
||||
|
||||
async updateAndRender(props: P, parentFiber: Fiber) {
|
||||
const rawProps = props;
|
||||
props = Object.assign({}, props);
|
||||
// update
|
||||
const fiber = makeChildFiber(this, parentFiber);
|
||||
this.fiber = fiber;
|
||||
const component = this.component;
|
||||
const defaultProps = (component.constructor as any).defaultProps;
|
||||
if (defaultProps) {
|
||||
props = applyDefaultProps(props, defaultProps);
|
||||
applyDefaultProps(props, defaultProps);
|
||||
}
|
||||
|
||||
currentNode = this;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { createBlock, html, list, multi, text, toggler, comment } from "./blockdom";
|
||||
import { compile, Template, TemplateFunction } from "../compiler";
|
||||
import { comment, createBlock, html, list, multi, text, toggler } from "./blockdom";
|
||||
import { getCurrent } from "./component_node";
|
||||
import { Portal, portalTemplate } from "./portal";
|
||||
import { component, getCurrent } from "./component_node";
|
||||
import { helpers } from "./template_helpers";
|
||||
|
||||
const bdom = { text, createBlock, list, multi, html, toggler, component, comment };
|
||||
const bdom = { text, createBlock, list, multi, html, toggler, comment };
|
||||
|
||||
function parseXML(xml: string): Document {
|
||||
const parser = new DOMParser();
|
||||
|
||||
Reference in New Issue
Block a user