make widget generic on State

This commit is contained in:
Géry Debongnie
2019-02-04 21:09:17 +01:00
parent 70b25fc7b8
commit 05e9797f7a
16 changed files with 161 additions and 107 deletions
@@ -23,8 +23,8 @@ interface Meta<T extends WEnv> {
isStarted: boolean;
isMounted: boolean;
isDestroyed: boolean;
parent: Widget<T, {}> | null;
children: { [key: number]: Widget<T, {}> };
parent: BaseWidget<T, any, any> | null;
children: { [key: number]: BaseWidget<T, any, any> };
// children mapping: from templateID to widgetID
// should it be a map number => Widget?
cmap: { [key: number]: number };
@@ -40,7 +40,7 @@ export interface Type<T> extends Function {
// Widget
//------------------------------------------------------------------------------
export class Widget<T extends WEnv, Props> extends EventBus {
export class BaseWidget<T extends WEnv, Props, State> extends EventBus {
__widget__: Meta<WEnv>;
template: string = "default";
inlineTemplate: string | null = null;
@@ -52,13 +52,15 @@ export class Widget<T extends WEnv, Props> extends EventBus {
env: T;
state: Object = {};
props: Props;
refs: { [key: string]: Widget<T, {}> | HTMLElement | undefined } = {};
refs: {
[key: string]: BaseWidget<T, any, any> | HTMLElement | undefined;
} = {};
//--------------------------------------------------------------------------
// Lifecycle
//--------------------------------------------------------------------------
constructor(parent: Widget<T, any> | T, props?: Props) {
constructor(parent: BaseWidget<T, any, any> | T, props?: Props) {
super();
wl.push(this);
@@ -68,8 +70,8 @@ export class Widget<T extends WEnv, Props> extends EventBus {
// Pro: but creating widget (by a template) is always unsafe anyway
this.props = <Props>props;
let id: number;
let p: Widget<T, any> | null = null;
if (parent instanceof Widget) {
let p: BaseWidget<T, any, any> | null = null;
if (parent instanceof BaseWidget) {
p = parent;
this.env = parent.env;
id = this.env.getID();
@@ -173,17 +175,11 @@ export class Widget<T extends WEnv, Props> extends EventBus {
* - it is ok to call updateState before the widget is started. In that
* case, it will simply update the state and will not rerender
*/
async updateState(nextState: Object) {
async updateState(nextState: Partial<State>) {
if (Object.keys(nextState).length === 0) {
return;
}
for (let key in nextState) {
if (key in this.state) {
this.state[key] = nextState[key];
} else {
throw new Error(`Invalid key: '${key}' does not exist in widget state`);
}
}
Object.assign(this.state, nextState);
if (this.__widget__.isStarted) {
return this.render();
}
@@ -259,7 +255,7 @@ export class Widget<T extends WEnv, Props> extends EventBus {
}
}
private visitSubTree(callback: (w: Widget<T, any>) => boolean) {
private visitSubTree(callback: (w: BaseWidget<T, any, any>) => boolean) {
const shouldVisitChildren = callback(this);
if (shouldVisitChildren) {
const children = this.__widget__.children;
+11 -7
View File
@@ -1,18 +1,22 @@
import { QWeb } from "./core/qweb_vdom";
import { idGenerator, memoize } from "./core/utils";
import { WEnv } from "./core/widget";
import { ActionManager, IActionManager } from "./services/action_manager";
import { Ajax, IAjax } from "./core/ajax";
import { WEnv } from "./core/base_widget";
import {
INotificationManager,
NotificationManager
} from "./core/notifications";
import { actionRegistry } from "./registries";
import { QWeb } from "./core/qweb_vdom";
import { Registry } from "./core/registry";
import { IRouter, Router } from "./core/router";
import { idGenerator, memoize } from "./core/utils";
import { actionRegistry } from "./registries";
import {
ActionManager,
ActionWidget,
IActionManager
} from "./services/action_manager";
import { CRM } from "./widgets/crm";
import { Discuss } from "./widgets/discuss";
import { Widget, Type } from "./core/widget";
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
@@ -30,7 +34,7 @@ export interface Env extends WEnv {
router: IRouter;
// registries
actionRegistry: Registry<Type<Widget<Env, any>>>;
actionRegistry: Registry<ActionWidget>;
// data
menus: Menu[];
+2 -3
View File
@@ -1,5 +1,4 @@
import { Registry } from "./core/registry";
import { Widget, Type } from "./core/widget";
import { Env } from "./env";
import { ActionWidget } from "./services/action_manager";
export const actionRegistry: Registry<Type<Widget<Env, any>>> = new Registry();
export const actionRegistry: Registry<ActionWidget> = new Registry();
+7 -5
View File
@@ -1,7 +1,7 @@
import { Type } from "../core/base_widget";
import { EventBus } from "../core/event_bus";
import { Registry } from "../core/registry";
import { Type, Widget } from "../core/widget";
import { Env } from "../env";
import { Widget } from "../widgets/widget";
//------------------------------------------------------------------------------
// Types
@@ -18,10 +18,12 @@ export interface CommonActionInfo {
target: "current" | "new";
}
export type ActionWidget = Type<Widget<{}, {}>>;
export interface ClientActionInfo extends CommonActionInfo {
type: "client";
name: string;
Widget: Type<Widget<Env, {}>>;
Widget: ActionWidget;
}
export interface ActWindowInfo extends CommonActionInfo {
@@ -47,10 +49,10 @@ export interface IActionManager {
//------------------------------------------------------------------------------
export class ActionManager extends EventBus implements IActionManager {
registry: Registry<Type<Widget<Env, any>>>;
registry: Registry<ActionWidget>;
stack: ActionStack;
constructor(registry: Registry<Type<Widget<Env, any>>>) {
constructor(registry: Registry<ActionWidget>) {
super();
this.registry = registry;
this.stack = [];
+2 -3
View File
@@ -1,6 +1,5 @@
import { Widget } from "../core/widget";
import { Env } from "../env";
import { Widget } from "./widget";
export class CRM extends Widget<Env, {}> {
export class CRM extends Widget<{}, {}> {
template = "web.crm";
}
+16 -4
View File
@@ -1,22 +1,34 @@
import { Widget } from "../core/widget";
import { Env } from "../env";
import { Widget } from "./widget";
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
interface Props {
initialState?: number;
}
export class Counter extends Widget<Env, Props> {
interface State {
counter: number;
}
//------------------------------------------------------------------------------
// Counter
//------------------------------------------------------------------------------
export class Counter extends Widget<Props, State> {
inlineTemplate = `
<div t-name="counter">
<button t-on-click="increment(-1)">-</button>
<span style="font-weight:bold">Value: <t t-esc="state.counter"/></span>
<button t-on-click="increment(1)">+</button>
</div>`;
state = {
counter: 0
};
constructor(parent: Widget<Env, {}>, props: Props) {
constructor(parent: Widget<any, any>, props: Props) {
super(parent, props);
this.state.counter = props.initialState || 0;
}
+16 -4
View File
@@ -1,9 +1,21 @@
import { Widget } from "../core/widget";
import { Env } from "../env";
import { Clock } from "./clock";
import { Counter } from "./counter";
import { Widget } from "./widget";
export class Discuss extends Widget<Env, {}> {
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
interface State {
validcounter: boolean;
color: "red" | "blue";
}
//------------------------------------------------------------------------------
// Discuss
//------------------------------------------------------------------------------
export class Discuss extends Widget<{}, State> {
template = "web.discuss";
widgets = { Clock, Counter, ColorWidget };
state = { validcounter: true, color: "red" };
@@ -38,7 +50,7 @@ export class Discuss extends Widget<Env, {}> {
}
}
class ColorWidget extends Widget<Env, { color: "red" | "blue" }> {
class ColorWidget extends Widget<{ color: "red" | "blue" }, {}> {
inlineTemplate = `
<div t-name="colorwidget">
<span>Current Color: </span>
+11 -3
View File
@@ -1,13 +1,21 @@
import { Widget } from "../core/widget";
import { Env, Menu } from "../env";
import { Menu } from "../env";
import { MenuItem } from "../misc/menu_helpers";
import { Widget } from "./widget";
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
export interface Props {
inHome: boolean;
app: MenuItem | null;
}
export class Navbar extends Widget<Env, Props> {
//------------------------------------------------------------------------------
// Navbar
//------------------------------------------------------------------------------
export class Navbar extends Widget<Props, {}> {
template = "web.navbar";
getUrl(menu: Menu) {
+10 -3
View File
@@ -1,12 +1,19 @@
import { Widget } from "../core/widget";
import { Env } from "../env";
import { ActionStack } from "../services/action_manager";
import { Widget } from "./widget";
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
export interface Props {
stack: ActionStack;
}
export class ActionContainer extends Widget<Env, Props> {
//------------------------------------------------------------------------------
// Action Container
//------------------------------------------------------------------------------
export class ActionContainer extends Widget<Props, {}> {
template = "web.action_container";
currentWidget: any;
+14 -3
View File
@@ -1,7 +1,18 @@
import { Widget } from "../core/widget";
import { Env } from "../env";
import { Widget } from "./widget";
export class Clock extends Widget<Env, {}> {
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
interface State {
currentTime: string;
}
//------------------------------------------------------------------------------
// Clock
//------------------------------------------------------------------------------
export class Clock extends Widget<{}, State> {
inlineTemplate = `<div class="o_clock"><t t-esc="state.currentTime"/></div>`;
timeout: any | undefined;
+2 -3
View File
@@ -1,6 +1,5 @@
import { Widget } from "../core/widget";
import { Env } from "../env";
import { MenuInfo, MenuItem } from "../misc/menu_helpers";
import { Widget } from "./widget";
//------------------------------------------------------------------------------
// Types
@@ -14,7 +13,7 @@ export interface Props {
// Home Menu
//------------------------------------------------------------------------------
export class HomeMenu extends Widget<Env, Props> {
export class HomeMenu extends Widget<Props, {}> {
template = "web.home_menu";
get apps(): MenuItem[] {
+2 -3
View File
@@ -1,8 +1,7 @@
import { Widget } from "../core/widget";
import { Env } from "../env";
import { INotification } from "../core/notifications";
import { Widget } from "./widget";
export class Notification extends Widget<Env, INotification> {
export class Notification extends Widget<INotification, {}> {
template = "web.notification";
close() {
+2 -2
View File
@@ -1,5 +1,5 @@
import { INotification } from "../core/notifications";
import { Widget } from "../core/widget";
import { Widget } from "./widget";
import { Env } from "../env";
import { MenuInfo, MenuItem, getAppAndAction } from "../misc/menu_helpers";
import { ActionStack } from "../services/action_manager";
@@ -28,7 +28,7 @@ interface Props {
// Root Widget
//------------------------------------------------------------------------------
export class Root extends Widget<Env, Props> {
export class Root extends Widget<Props, State> {
template = "web.web_client";
widgets = { Navbar, Notification, HomeMenu, ActionContainer };
+4
View File
@@ -0,0 +1,4 @@
import { BaseWidget } from "../core/base_widget";
import { Env } from "../env";
export class Widget<Props, State> extends BaseWidget<Env, Props, State> {}