mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
refactoring (action management is now applied in root widget)
This commit is contained in:
@@ -30,16 +30,3 @@ document.addEventListener("DOMContentLoaded", async function() {
|
||||
// For debugging purpose, we keep a reference to the root widget in odoo
|
||||
(<any>window).odoo.rootWidget = rootWidget;
|
||||
});
|
||||
|
||||
let testMixin = Base =>
|
||||
class extends Base {
|
||||
calc() {
|
||||
return 32;
|
||||
}
|
||||
};
|
||||
|
||||
class Foo {}
|
||||
|
||||
class Bar extends testMixin(Foo) {}
|
||||
|
||||
console.log(new Bar().calc());
|
||||
|
||||
@@ -49,7 +49,13 @@ export type ActionDescription =
|
||||
| ActWindowActionDescription;
|
||||
|
||||
export type ActionInfo = ClientActionInfo | ActWindowInfo;
|
||||
export type ActionStack = ActionInfo[];
|
||||
|
||||
export interface Action {
|
||||
id: number;
|
||||
widget?: Widget<any, any>;
|
||||
executor(parent: Widget<any, any>): Promise<Widget<any, any> | null>;
|
||||
activate(): void;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Action Manager Mixin
|
||||
@@ -60,55 +66,61 @@ export function actionManagerMixin<T extends ReturnType<typeof rpcMixin>>(
|
||||
) {
|
||||
return class extends Base {
|
||||
actionCache: { [key: number]: Promise<ActionDescription> } = {};
|
||||
currentAction?: Action;
|
||||
lastAction?: Action;
|
||||
|
||||
async doAction(request: ActionRequest) {
|
||||
const self = this;
|
||||
const descr = await this.loadAction(request);
|
||||
let executor;
|
||||
switch (descr.type) {
|
||||
case "ir.actions.client":
|
||||
return this.doClientAction(descr);
|
||||
executor = this.doClientAction(descr);
|
||||
break;
|
||||
case "ir.actions.act_window":
|
||||
return this.doActWindowAction(descr);
|
||||
executor = this.doActWindowAction(descr);
|
||||
break;
|
||||
default:
|
||||
throw new Error("unhandled action");
|
||||
}
|
||||
const action: Action = {
|
||||
id: this.generateID(),
|
||||
executor,
|
||||
activate() {
|
||||
if (self.currentAction && self.currentAction.widget) {
|
||||
self.currentAction.widget.destroy();
|
||||
}
|
||||
self.currentAction = action;
|
||||
self.update({
|
||||
inHome: false
|
||||
});
|
||||
document.title = descr.name + " - Odoo";
|
||||
}
|
||||
};
|
||||
self.lastAction = action;
|
||||
this.trigger("update_action", action);
|
||||
}
|
||||
|
||||
doActWindowAction(descr: ActWindowActionDescription) {
|
||||
let title = descr.name;
|
||||
this.update({
|
||||
inHome: false,
|
||||
stack: [
|
||||
{
|
||||
id: 1,
|
||||
context: {},
|
||||
target: "current",
|
||||
type: "act_window",
|
||||
title,
|
||||
Widget: View
|
||||
}
|
||||
]
|
||||
});
|
||||
document.title = descr.name + " - Odoo";
|
||||
return async function executor(this: Action, parent: Widget<any, any>) {
|
||||
const widget = new View(parent, {});
|
||||
const div = document.createElement("div");
|
||||
await widget.mount(div);
|
||||
this.widget = widget;
|
||||
return widget;
|
||||
};
|
||||
}
|
||||
|
||||
doClientAction(descr: ClientActionDescription) {
|
||||
let key = descr.tag;
|
||||
let title = descr.name;
|
||||
let Widget = this.actionRegistry.get(key);
|
||||
this.update({
|
||||
inHome: false,
|
||||
stack: [
|
||||
{
|
||||
id: 1,
|
||||
context: {},
|
||||
target: "current",
|
||||
type: "client",
|
||||
title,
|
||||
Widget: Widget
|
||||
}
|
||||
]
|
||||
});
|
||||
document.title = descr.name + " - Odoo";
|
||||
return async function executor(this: Action, parent: Widget<any, any>) {
|
||||
const widget = new Widget(parent, {});
|
||||
const div = document.createElement("div");
|
||||
await widget.mount(div);
|
||||
this.widget = widget;
|
||||
return widget;
|
||||
};
|
||||
}
|
||||
|
||||
loadAction(id: number): Promise<ActionDescription> {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Type } from "../core/component";
|
||||
import { BaseStore } from "./store";
|
||||
import { idGenerator } from "../core/utils";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Notifications Mixin
|
||||
@@ -15,8 +14,6 @@ export interface INotification {
|
||||
|
||||
export function notificationMixin<T extends Type<BaseStore>>(Base: T) {
|
||||
return class extends Base {
|
||||
generateID = idGenerator();
|
||||
|
||||
addNotification(notif: Partial<INotification>): number {
|
||||
const id = this.generateID();
|
||||
const defaultVals = {
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { EventBus } from "../core/event_bus";
|
||||
import { Registry } from "../core/registry";
|
||||
import { idGenerator } from "../core/utils";
|
||||
import { RPC } from "../services/ajax";
|
||||
import { IRouter, Query } from "../services/router";
|
||||
import {
|
||||
actionManagerMixin,
|
||||
ActionStack,
|
||||
ActionWidget
|
||||
} from "./action_manager_mixin";
|
||||
import { actionManagerMixin, ActionWidget } from "./action_manager_mixin";
|
||||
import { notificationMixin } from "./notification_mixin";
|
||||
import { rpcMixin } from "./rpc_mixin";
|
||||
import { MenuItem } from "./store";
|
||||
@@ -15,7 +12,7 @@ import { MenuItem } from "./store";
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export { ActionStack, ActionWidget } from "./action_manager_mixin";
|
||||
export { ActionWidget } from "./action_manager_mixin";
|
||||
export { INotification } from "./notification_mixin";
|
||||
export { RPC } from "./rpc_mixin";
|
||||
|
||||
@@ -40,7 +37,6 @@ export interface MenuInfo {
|
||||
}
|
||||
|
||||
export interface State {
|
||||
stack: ActionStack;
|
||||
inHome: boolean;
|
||||
currentApp: MenuItem | null;
|
||||
}
|
||||
@@ -55,7 +51,6 @@ export interface Services {
|
||||
//------------------------------------------------------------------------------
|
||||
export class BaseStore extends EventBus {
|
||||
state: State = {
|
||||
stack: [],
|
||||
inHome: false,
|
||||
currentApp: null
|
||||
};
|
||||
@@ -63,6 +58,7 @@ export class BaseStore extends EventBus {
|
||||
services: Services;
|
||||
actionRegistry: Registry<ActionWidget>;
|
||||
currentQuery: Query;
|
||||
generateID = idGenerator();
|
||||
|
||||
constructor(
|
||||
services: Services,
|
||||
@@ -115,7 +111,6 @@ export class Store extends actionManagerMixin(
|
||||
this.state.inHome = true;
|
||||
this.services.router.navigate({ home: true });
|
||||
}
|
||||
|
||||
this.services.router.on("query_changed", this, this.updateAction);
|
||||
this.updateAction(this.services.router.getQuery());
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { ActionStack } from "../store/store";
|
||||
import { Widget } from "./widget";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export interface Props {
|
||||
stack: ActionStack;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Action Container
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export class ActionContainer extends Widget<Props, {}> {
|
||||
template = "web.action_container";
|
||||
currentWidget: any;
|
||||
|
||||
willStart() {
|
||||
return this.setContentWidget();
|
||||
}
|
||||
|
||||
mounted() {
|
||||
if (this.currentWidget && this.currentWidget.el) {
|
||||
this.el!.appendChild(this.currentWidget.el);
|
||||
this.currentWidget.__mount();
|
||||
}
|
||||
}
|
||||
|
||||
shouldUpdate(nextProps: Props) {
|
||||
if (nextProps.stack !== this.props.stack) {
|
||||
this.props = nextProps;
|
||||
this.setContentWidget();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async setContentWidget() {
|
||||
const info = this.props.stack[this.props.stack.length - 1];
|
||||
if (info) {
|
||||
const Widget = info.Widget;
|
||||
let widget = new Widget(this, {});
|
||||
await widget.mount(this.el || document.createElement("div"));
|
||||
if (this.currentWidget) {
|
||||
this.currentWidget.destroy();
|
||||
}
|
||||
this.currentWidget = widget;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { debounce } from "../core/utils";
|
||||
import { Env } from "../env";
|
||||
import { State, Store } from "../store/store";
|
||||
import { ActionContainer } from "./action_container";
|
||||
import { HomeMenu } from "./home_menu";
|
||||
import { Navbar } from "./navbar";
|
||||
import { Notification } from "./notification";
|
||||
import { Widget } from "./widget";
|
||||
import { Action } from "../store/action_manager_mixin";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Root Widget
|
||||
@@ -13,7 +13,7 @@ import { Widget } from "./widget";
|
||||
|
||||
export class Root extends Widget<Store, State> {
|
||||
template = "web.web_client";
|
||||
widgets = { Navbar, HomeMenu, ActionContainer };
|
||||
widgets = { Navbar, HomeMenu };
|
||||
|
||||
notifications: { [id: number]: Notification } = {};
|
||||
store: Store;
|
||||
@@ -53,5 +53,21 @@ export class Root extends Widget<Store, State> {
|
||||
this.render();
|
||||
}
|
||||
}, 50));
|
||||
|
||||
// actions
|
||||
this.store.on("update_action", this, this.applyAction);
|
||||
if (this.store.lastAction) {
|
||||
this.applyAction(this.store.lastAction);
|
||||
}
|
||||
}
|
||||
|
||||
async applyAction(action: Action) {
|
||||
const widget = await action.executor(this);
|
||||
if (widget) {
|
||||
// to do: call some public method of widget instead...
|
||||
(<HTMLElement>this.refs.content).appendChild(widget.el!);
|
||||
widget.__mount();
|
||||
action.activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
<div t-name="web.web_client" class="o_web_client">
|
||||
<t t-widget="Navbar" t-props="{inHome:state.inHome,app:state.currentApp}" />
|
||||
<t t-widget="HomeMenu" t-if="state.inHome" t-keep-alive="1" t-props="{menuInfo:props.menuInfo}" />
|
||||
<t t-else="1">
|
||||
<t t-widget="ActionContainer" t-props="{stack:state.stack}" t-keep-alive="1"/>
|
||||
</t>
|
||||
<div class="o_content" t-att-class="state.inHome ? 'o_hidden' : ''" t-ref="content"/>
|
||||
<div class="o_notification_container" t-ref="notification_container"/>
|
||||
<div class="o_loading d-none" t-ref="loading_indicator">Loading</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user