mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
small refactoring...
This commit is contained in:
@@ -7,6 +7,7 @@ import { QWeb } from "./qweb_vdom";
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Types/helpers
|
// Types/helpers
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export interface WEnv {
|
export interface WEnv {
|
||||||
qweb: QWeb;
|
qweb: QWeb;
|
||||||
getID(): number;
|
getID(): number;
|
||||||
@@ -35,6 +36,7 @@ const patch = init([sdListeners, sdAttrs]);
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Widget
|
// Widget
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export class Widget<T extends WEnv, Props> {
|
export class Widget<T extends WEnv, Props> {
|
||||||
__widget__: Meta<WEnv>;
|
__widget__: Meta<WEnv>;
|
||||||
name: string = "widget";
|
name: string = "widget";
|
||||||
@@ -53,7 +55,7 @@ export class Widget<T extends WEnv, Props> {
|
|||||||
// Lifecycle
|
// Lifecycle
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
constructor(parent: Widget<T, {}> | T, props?: Props) {
|
constructor(parent: Widget<T, any> | T, props?: Props) {
|
||||||
wl.push(this);
|
wl.push(this);
|
||||||
// is this a good idea?
|
// is this a good idea?
|
||||||
// Pro: if props is empty, we can create easily a widget
|
// Pro: if props is empty, we can create easily a widget
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export interface Subscription {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// EventBus
|
// EventBus
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export class EventBus {
|
export class EventBus {
|
||||||
private subscriptions: { [eventType: string]: Subscription[] } = {};
|
private subscriptions: { [eventType: string]: Subscription[] } = {};
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const RESERVED_WORDS = "true,false,NaN,null,undefined,debugger,console,window,in
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Compilation Context
|
// Compilation Context
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export class Context {
|
export class Context {
|
||||||
nextID: number = 1;
|
nextID: number = 1;
|
||||||
code: string[] = [];
|
code: string[] = [];
|
||||||
@@ -98,6 +99,7 @@ export class Context {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// QWeb rendering engine
|
// QWeb rendering engine
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export class QWeb {
|
export class QWeb {
|
||||||
rawTemplates: { [name: string]: RawTemplate } = {};
|
rawTemplates: { [name: string]: RawTemplate } = {};
|
||||||
parsedTemplates: { [name: string]: ParsedTemplate } = {};
|
parsedTemplates: { [name: string]: ParsedTemplate } = {};
|
||||||
@@ -461,6 +463,7 @@ export class QWeb {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// QWeb Directives
|
// QWeb Directives
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
interface CompilationInfo {
|
interface CompilationInfo {
|
||||||
nodeID?: number;
|
nodeID?: number;
|
||||||
node: Element;
|
node: Element;
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
///<amd-module name="main" />
|
///<amd-module name="main" />
|
||||||
|
|
||||||
|
import { debounce } from "./core/utils";
|
||||||
import { makeEnvironment } from "./env";
|
import { makeEnvironment } from "./env";
|
||||||
import { registry } from "./registry";
|
import { registry } from "./registry";
|
||||||
import { Discuss } from "./widgets/discuss";
|
|
||||||
import { Root } from "./root";
|
import { Root } from "./root";
|
||||||
import { debounce } from "./core/utils";
|
import { CRM } from "./widgets/crm";
|
||||||
|
import { Discuss } from "./widgets/discuss";
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Prepare application registry
|
// Prepare application registry
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
registry.add("action", "discuss", Discuss);
|
registry.add("action", "discuss", Discuss);
|
||||||
|
registry.add("action", "crm", CRM);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Application bootstrapping
|
// Application bootstrapping
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async function() {
|
document.addEventListener("DOMContentLoaded", async function() {
|
||||||
const env = makeEnvironment();
|
const env = makeEnvironment();
|
||||||
const rootWidget = new Root(env);
|
const rootWidget = new Root(env);
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ export class Registry {
|
|||||||
this.registries[type][name] = action;
|
this.registries[type][name] = action;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAction(name: string): ActionWidget {
|
||||||
|
return this.registries.action[name];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
+28
-38
@@ -1,19 +1,34 @@
|
|||||||
import { Widget } from "./core/widget";
|
import { Widget } from "./core/widget";
|
||||||
import { Env } from "./env";
|
import { Env } from "./env";
|
||||||
|
import { ActionStack } from "./services/action_manager";
|
||||||
import { INotification } from "./services/notifications";
|
import { INotification } from "./services/notifications";
|
||||||
import { Action } from "./widgets/action";
|
import { Action } from "./widgets/action";
|
||||||
import { HomeMenu } from "./widgets/home_menu";
|
import { HomeMenu } from "./widgets/home_menu";
|
||||||
import { Navbar } from "./widgets/navbar";
|
import { Navbar } from "./widgets/navbar";
|
||||||
import { Notification } from "./widgets/notification";
|
import { Notification } from "./widgets/notification";
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Types
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
notifications: INotification[];
|
||||||
|
stack: ActionStack;
|
||||||
|
inMenu: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Root Widget
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
<div class="o_web_client">
|
<div class="o_web_client">
|
||||||
<t t-widget="Navbar" t-props="{toggleHomeMenu:toggleHomeMenu,inMenu:state.inMenu}"/>
|
<t t-widget="Navbar" t-props="{toggleHome:toggleHome,inMenu:state.inMenu}"/>
|
||||||
<t t-if="state.inMenu">
|
<t t-if="state.inMenu">
|
||||||
<t t-widget="HomeMenu" t-props="{toggleHomeMenu:toggleHomeMenu}"/>
|
<t t-widget="HomeMenu" t-keep-alive="1"/>
|
||||||
</t>
|
</t>
|
||||||
<t t-else="1">
|
<t t-else="1">
|
||||||
<t t-widget="Action" t-keep-alive="1"/>
|
<t t-widget="Action" t-props="{stack:state.stack}" t-keep-alive="1"/>
|
||||||
</t>
|
</t>
|
||||||
<div class="o_notification_container">
|
<div class="o_notification_container">
|
||||||
<t t-foreach="state.notifications" t-as="notif">
|
<t t-foreach="state.notifications" t-as="notif">
|
||||||
@@ -23,58 +38,33 @@ const template = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface State {
|
|
||||||
notifications: INotification[];
|
|
||||||
inMenu: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Root extends Widget<Env, {}> {
|
export class Root extends Widget<Env, {}> {
|
||||||
name = "root";
|
name = "root";
|
||||||
template = template;
|
template = template;
|
||||||
widgets = { Navbar, Notification, HomeMenu, Action };
|
widgets = { Navbar, Notification, HomeMenu, Action };
|
||||||
content: Widget<Env, {}> | null = null;
|
|
||||||
|
|
||||||
state: State = {
|
state: State = {
|
||||||
notifications: [],
|
notifications: [],
|
||||||
|
stack: [],
|
||||||
inMenu: false
|
inMenu: false
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(env: Env) {
|
constructor(env: Env) {
|
||||||
super(env);
|
super(env);
|
||||||
this.toggleHomeMenu = this.toggleHomeMenu.bind(this);
|
this.toggleHome = this.toggleHome.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
// this.env.actionManager.on("action_ready", this, this.setContentWidget);
|
this.env.notifications.on("notifications_updated", this, notifs =>
|
||||||
this.env.notifications.on("notification_added", this, this.addNotif);
|
this.updateState({ notifications: notifs })
|
||||||
this.env.notifications.on("notification_removed", this, this.removeNotif);
|
);
|
||||||
|
this.env.actionManager.on("action_stack_updated", this, stack =>
|
||||||
// const actionWidget = this.env.actionManager.getCurrentAction();
|
this.updateState({ stack })
|
||||||
// if (actionWidget) {
|
);
|
||||||
// this.setContentWidget(actionWidget);
|
this.env.actionManager.activate();
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// async setContentWidget(actionWidget: ActionWidget) {
|
toggleHome() {
|
||||||
// const currentWidget = this.content;
|
|
||||||
// const newWidget = new actionWidget.Widget(this, actionWidget.props);
|
|
||||||
// await newWidget.mount(<HTMLElement>this.refs.content);
|
|
||||||
// if (currentWidget) {
|
|
||||||
// currentWidget.destroy();
|
|
||||||
// }
|
|
||||||
// this.content = newWidget;
|
|
||||||
// }
|
|
||||||
|
|
||||||
addNotif(notif: INotification) {
|
|
||||||
const notifications = this.state.notifications.concat(notif);
|
|
||||||
this.updateState({ notifications });
|
|
||||||
}
|
|
||||||
removeNotif(notif: INotification) {
|
|
||||||
const notifs = this.state.notifications.filter(f => f.id !== notif.id);
|
|
||||||
this.updateState({ notifications: notifs });
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleHomeMenu() {
|
|
||||||
this.updateState({ inMenu: !this.state.inMenu });
|
this.updateState({ inMenu: !this.state.inMenu });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,50 @@
|
|||||||
import { Callback, EventBus } from "../core/event_bus";
|
import { EventBus } from "../core/event_bus";
|
||||||
import { Widget } from "../core/widget";
|
import { Widget } from "../core/widget";
|
||||||
import { Env } from "../env";
|
import { Env } from "../env";
|
||||||
import { Registry } from "../registry";
|
import { Registry } from "../registry";
|
||||||
import { Type } from "../types";
|
import { Type } from "../types";
|
||||||
import { CRM } from "../widgets/crm";
|
|
||||||
import { Discuss } from "../widgets/discuss";
|
|
||||||
import { IRouter, Query } from "./router";
|
import { IRouter, Query } from "./router";
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Types
|
// Types
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export interface ClientAction {
|
export type ActionRequest = string | number;
|
||||||
|
|
||||||
|
export type Context = { [key: string]: any };
|
||||||
|
|
||||||
|
export interface CommonActionInfo {
|
||||||
|
id: number;
|
||||||
|
context: Context;
|
||||||
|
title: string;
|
||||||
|
target: "current" | "new";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ClientActionInfo extends CommonActionInfo {
|
||||||
type: "client";
|
type: "client";
|
||||||
name: string;
|
name: string;
|
||||||
|
Widget: Type<Widget<Env, {}>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ActWindowAction {
|
export interface ActWindowInfo extends CommonActionInfo {
|
||||||
type: "act_window";
|
type: "act_window";
|
||||||
views: string[];
|
view: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ActionEvent = "action_ready";
|
export type ActionInfo = ClientActionInfo | ActWindowInfo;
|
||||||
|
export type ActionStack = ActionInfo[];
|
||||||
|
|
||||||
|
export type ActionEvent = "action_stack_updated";
|
||||||
|
|
||||||
|
type Callback = (stack: ActionStack) => void;
|
||||||
|
|
||||||
export interface IActionManager {
|
export interface IActionManager {
|
||||||
doAction(actionID: number): void;
|
activate(): void;
|
||||||
|
doAction(request: ActionRequest): void;
|
||||||
on(event: ActionEvent, owner: any, callback: Callback): void;
|
on(event: ActionEvent, owner: any, callback: Callback): void;
|
||||||
getCurrentAction(): ActionWidget | null;
|
getStack(): ActionStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Action = ClientAction | ActWindowAction;
|
|
||||||
|
|
||||||
export interface ActionWidget {
|
|
||||||
id: number;
|
|
||||||
Widget: Type<Widget<Env, {}>>;
|
|
||||||
props: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
const actions: any[] = [
|
|
||||||
{ id: 1, title: "Discuss", Widget: Discuss, default: true },
|
|
||||||
{ id: 2, title: "CRM", Widget: CRM }
|
|
||||||
];
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Action Manager
|
// Action Manager
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -49,49 +52,47 @@ const actions: any[] = [
|
|||||||
export class ActionManager extends EventBus implements IActionManager {
|
export class ActionManager extends EventBus implements IActionManager {
|
||||||
router: IRouter;
|
router: IRouter;
|
||||||
registry: Registry;
|
registry: Registry;
|
||||||
currentAction: ActionWidget | null = null;
|
stack: ActionStack;
|
||||||
|
|
||||||
constructor(router: IRouter, registry: Registry) {
|
constructor(router: IRouter, registry: Registry) {
|
||||||
super();
|
super();
|
||||||
this.router = router;
|
this.router = router;
|
||||||
this.registry = registry;
|
this.registry = registry;
|
||||||
const query = this.router.getQuery();
|
this.stack = [];
|
||||||
this.update(query);
|
|
||||||
this.router.on("query_changed", this, this.update);
|
|
||||||
if (!this.currentAction) {
|
|
||||||
const action = actions.find(a => a.default);
|
|
||||||
if (action) {
|
|
||||||
this.doAction(action.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
activate() {
|
||||||
|
this.router.on("query_changed", this, this.update);
|
||||||
|
this.update(this.router.getQuery());
|
||||||
|
}
|
||||||
|
doAction(request: ActionRequest) {
|
||||||
|
if (typeof request === "number") {
|
||||||
|
// this is an action ID
|
||||||
|
let name = request === 1 ? 'discuss' : 'crm';
|
||||||
|
let title = request === 1 ? 'Discuss': 'CRM';
|
||||||
|
let Widget = this.registry.getAction(name);
|
||||||
|
this.stack = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
context: {},
|
||||||
|
target: "current",
|
||||||
|
type: "client",
|
||||||
|
name,
|
||||||
|
title,
|
||||||
|
Widget: Widget
|
||||||
|
}
|
||||||
|
];
|
||||||
|
this.trigger("action_stack_updated", this.stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getStack(): ActionStack {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
update(query: Query) {
|
update(query: Query) {
|
||||||
const initialAction = this.currentAction;
|
|
||||||
if ("action_id" in query) {
|
if ("action_id" in query) {
|
||||||
const actionID = parseInt(query.action_id);
|
const actionID = parseInt(query.action_id);
|
||||||
this.doAction(actionID);
|
this.doAction(actionID);
|
||||||
}
|
}
|
||||||
if (this.currentAction && initialAction !== this.currentAction) {
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
doAction(actionID: number) {
|
|
||||||
const action = actions.find(a => a.id === actionID);
|
|
||||||
if (action) {
|
|
||||||
this.currentAction = {
|
|
||||||
id: action.id,
|
|
||||||
Widget: action.Widget,
|
|
||||||
props: {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this.trigger("action_ready", this.currentAction);
|
|
||||||
this.router.navigate({ action_id: String(actionID) });
|
|
||||||
}
|
|
||||||
|
|
||||||
registerAction(action: Action) {}
|
|
||||||
|
|
||||||
getCurrentAction(): ActionWidget | null {
|
|
||||||
return this.currentAction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Types
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export interface RPCQuery {
|
export interface RPCQuery {
|
||||||
model: string;
|
model: string;
|
||||||
method: string;
|
method: string;
|
||||||
@@ -7,6 +11,10 @@ export interface IAjax {
|
|||||||
rpc(rpc: RPCQuery): Promise<any>;
|
rpc(rpc: RPCQuery): Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Ajax
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export class Ajax implements IAjax {
|
export class Ajax implements IAjax {
|
||||||
rpc(rpc: RPCQuery): Promise<any> {
|
rpc(rpc: RPCQuery): Promise<any> {
|
||||||
return Promise.resolve(1);
|
return Promise.resolve(1);
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ export interface INotification {
|
|||||||
sticky: boolean;
|
sticky: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NotificationEvent = "notification_added" | "notification_removed";
|
export type NotificationEvent = "notifications_updated";
|
||||||
|
|
||||||
export type Callback = (notif: INotification) => void;
|
export type Callback = (notifs: INotification[]) => void;
|
||||||
|
|
||||||
export interface INotificationManager {
|
export interface INotificationManager {
|
||||||
add(notif: Partial<INotification>): number;
|
add(notif: Partial<INotification>): number;
|
||||||
@@ -25,9 +25,10 @@ export interface INotificationManager {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Notification Manager
|
// Notification Manager
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export class NotificationManager extends Bus implements INotificationManager {
|
export class NotificationManager extends Bus implements INotificationManager {
|
||||||
nextID = 1;
|
nextID = 1;
|
||||||
notifications: { [key: number]: INotification } = {};
|
notifications: INotification[] = [];
|
||||||
|
|
||||||
add(notif: Partial<INotification>): number {
|
add(notif: Partial<INotification>): number {
|
||||||
const id = this.nextID++;
|
const id = this.nextID++;
|
||||||
@@ -38,18 +39,15 @@ export class NotificationManager extends Bus implements INotificationManager {
|
|||||||
sticky: false
|
sticky: false
|
||||||
};
|
};
|
||||||
const notification = Object.assign(defaultVals, notif, { id });
|
const notification = Object.assign(defaultVals, notif, { id });
|
||||||
this.notifications[id] = notification;
|
this.notifications.push(notification);
|
||||||
this.trigger("notification_added", notification);
|
this.trigger("notifications_updated", this.notifications);
|
||||||
if (!notification.sticky) {
|
if (!notification.sticky) {
|
||||||
setTimeout(() => this.close(id), 2500);
|
setTimeout(() => this.close(id), 2500);
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
close(id: number) {
|
close(id: number) {
|
||||||
let notification = this.notifications[id];
|
this.notifications = this.notifications.filter(n => n.id !== id);
|
||||||
if (notification) {
|
this.trigger("notifications_updated", this.notifications);
|
||||||
delete this.notifications[id];
|
|
||||||
this.trigger("notification_removed", notification);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { EventBus, Callback } from "../core/event_bus";
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Types and helpers
|
// Types and helpers
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export type Query = { [key: string]: string };
|
export type Query = { [key: string]: string };
|
||||||
|
|
||||||
function clearSlashes(s: string): string {
|
function clearSlashes(s: string): string {
|
||||||
@@ -21,6 +22,7 @@ export interface IRouter {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Router
|
// Router
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export class Router extends EventBus implements IRouter {
|
export class Router extends EventBus implements IRouter {
|
||||||
currentQuery: Query;
|
currentQuery: Query;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,36 @@
|
|||||||
import { Widget } from "../core/widget";
|
import { Widget } from "../core/widget";
|
||||||
import { Env } from "../env";
|
import { Env } from "../env";
|
||||||
|
import { ActionStack } from "../services/action_manager";
|
||||||
|
|
||||||
const template = `
|
export interface Props {
|
||||||
<div class="o_content">
|
stack: ActionStack;
|
||||||
<span>MAIN ACTION</span>
|
}
|
||||||
<input/>
|
|
||||||
</div>
|
export class Action extends Widget<Env, Props> {
|
||||||
`;
|
name = "action";
|
||||||
|
template = `<div class="o_content"/>`;
|
||||||
export class Action extends Widget<Env, {}> {
|
currentWidget: any;
|
||||||
name = "action";
|
|
||||||
template = template;
|
mounted() {
|
||||||
|
this.setContentWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldUpdate(nextProps: Props) {
|
||||||
|
this.props = nextProps;
|
||||||
|
this.setContentWidget();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setContentWidget() {
|
||||||
|
const info = this.props.stack[this.props.stack.length - 1];
|
||||||
|
if (info && info.type === "client") {
|
||||||
|
const Widget = info.Widget;
|
||||||
|
let widget = new Widget(this, {});
|
||||||
|
await widget.mount(this.el!);
|
||||||
|
if (this.currentWidget) {
|
||||||
|
this.currentWidget.destroy();
|
||||||
|
}
|
||||||
|
this.currentWidget = widget;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,20 @@ import { Env, Menu } from "../env";
|
|||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
<div class="o_navbar">
|
<div class="o_navbar">
|
||||||
<a aria-label="Applications" class="o_title fa fa-th" href="#" title="Applications" accesskey="h" t-on-click="toggleHomeMenu"/>
|
<a aria-label="Applications" class="o_title fa fa-th" href="#" title="Applications" accesskey="h" t-on-click="toggleHome"/>
|
||||||
<ul t-if="!props.inMenu">
|
<ul t-if="!props.inMenu">
|
||||||
<li t-foreach="env.menus" t-as="menu">
|
<li t-foreach="env.menus" t-as="menu">
|
||||||
<a t-att-href="getUrl(menu)">
|
<a t-att-href="getUrl(menu)">
|
||||||
<t t-esc="menu.title"/>
|
<t t-esc="menu.title"/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li t-if="env.isMobile">MOBILEMODE</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
toggleHomeMenu: () => void;
|
toggleHome: () => void;
|
||||||
inMenu: boolean;
|
inMenu: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,8 +29,8 @@ export class Navbar extends Widget<Env, Props> {
|
|||||||
return this.env.router.formatURL("", { action_id });
|
return this.env.router.formatURL("", { action_id });
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleHomeMenu(ev: MouseEvent) {
|
toggleHome(ev: MouseEvent) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.props.toggleHomeMenu();
|
this.props.toggleHome();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,7 @@ import { Env } from "../src/ts/env";
|
|||||||
import { IAjax, RPCQuery } from "../src/ts/services/ajax";
|
import { IAjax, RPCQuery } from "../src/ts/services/ajax";
|
||||||
import { NotificationManager } from "../src/ts/services/notifications";
|
import { NotificationManager } from "../src/ts/services/notifications";
|
||||||
|
|
||||||
import {
|
import { IActionManager, ActionEvent } from "../src/ts/services/action_manager";
|
||||||
IActionManager,
|
|
||||||
ActionEvent,
|
|
||||||
ActionWidget
|
|
||||||
} from "../src/ts/services/action_manager";
|
|
||||||
import { Callback } from "../src/ts/core/event_bus";
|
import { Callback } from "../src/ts/core/event_bus";
|
||||||
import { IRouter, Query, RouterEvent } from "../src/ts/services/router";
|
import { IRouter, Query, RouterEvent } from "../src/ts/services/router";
|
||||||
|
|
||||||
@@ -55,8 +51,9 @@ class MockAjax implements IAjax {
|
|||||||
class MockActionManager implements IActionManager {
|
class MockActionManager implements IActionManager {
|
||||||
doAction(actionID: number) {}
|
doAction(actionID: number) {}
|
||||||
on(event: ActionEvent, owner: any, callback: Callback) {}
|
on(event: ActionEvent, owner: any, callback: Callback) {}
|
||||||
getCurrentAction(): ActionWidget | null {
|
activate() {}
|
||||||
return null;
|
getStack() {
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,51 +1,54 @@
|
|||||||
import { NotificationManager } from "../../src/ts/services/notifications";
|
import {
|
||||||
|
NotificationManager,
|
||||||
|
INotification
|
||||||
|
} from "../../src/ts/services/notifications";
|
||||||
|
|
||||||
test("can subscribe and add notification", () => {
|
test("can subscribe and add notification", () => {
|
||||||
let notified = false;
|
let notifs: INotification[] = [];
|
||||||
const notifications = new NotificationManager();
|
const notifications = new NotificationManager();
|
||||||
notifications.on("notification_added", {}, () => (notified = true));
|
notifications.on("notifications_updated", {}, n => (notifs = n));
|
||||||
expect(notified).toBe(false);
|
expect(notifs.length).toBe(0);
|
||||||
const id = notifications.add({ title: "test", message: "message" });
|
const id = notifications.add({ title: "test", message: "message" });
|
||||||
expect(notified).toBe(true);
|
expect(notifs.length).toBe(1);
|
||||||
expect(id).toBeDefined();
|
expect(id).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can close a notification", () => {
|
test("can close a notification", () => {
|
||||||
|
let notifs: INotification[] = [];
|
||||||
const notifications = new NotificationManager();
|
const notifications = new NotificationManager();
|
||||||
let notified = false;
|
notifications.on("notifications_updated", {}, n => (notifs = n));
|
||||||
notifications.on("notification_removed", {}, () => (notified = true));
|
|
||||||
|
|
||||||
const id = notifications.add({ title: "test", message: "message" });
|
const id = notifications.add({ title: "test", message: "message" });
|
||||||
expect(notified).toBe(false);
|
expect(notifs.length).toBe(1);
|
||||||
|
|
||||||
notifications.close(id);
|
notifications.close(id);
|
||||||
expect(notified).toBe(true);
|
expect(notifs.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("notifications closes themselves after a while", () => {
|
test("notifications closes themselves after a while", () => {
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
let notifs: INotification[] = [];
|
||||||
const notifications = new NotificationManager();
|
const notifications = new NotificationManager();
|
||||||
let removed = false;
|
notifications.on("notifications_updated", {}, n => (notifs = n));
|
||||||
notifications.on("notification_removed", {}, () => (removed = true));
|
|
||||||
|
|
||||||
notifications.add({ title: "test", message: "message" });
|
notifications.add({ title: "test", message: "message" });
|
||||||
|
|
||||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||||
expect(removed).toBe(false);
|
expect(notifs.length).toBe(1);
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
expect(removed).toBe(true);
|
expect(notifs.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("sticky notifications do not close themselves after a while", () => {
|
test("sticky notifications do not close themselves after a while", () => {
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
let notifs: INotification[] = [];
|
||||||
const notifications = new NotificationManager();
|
const notifications = new NotificationManager();
|
||||||
let removed = false;
|
notifications.on("notifications_updated", {}, n => (notifs = n));
|
||||||
notifications.on("notification_removed", {}, () => (removed = true));
|
|
||||||
|
|
||||||
notifications.add({ title: "test", message: "message", sticky: true });
|
notifications.add({ title: "test", message: "message", sticky: true });
|
||||||
|
|
||||||
expect(setTimeout).toHaveBeenCalledTimes(0);
|
expect(setTimeout).toHaveBeenCalledTimes(0);
|
||||||
expect(removed).toBe(false);
|
expect(notifs.length).toBe(1);
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
expect(removed).toBe(false);
|
expect(notifs.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ exports[`can be rendered 1`] = `
|
|||||||
<a aria-label=\\"Applications\\" class=\\"o_title fa fa-th\\" href=\\"#\\" title=\\"Applications\\" accesskey=\\"h\\"></a>
|
<a aria-label=\\"Applications\\" class=\\"o_title fa fa-th\\" href=\\"#\\" title=\\"Applications\\" accesskey=\\"h\\"></a>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>"
|
</div>"
|
||||||
`;
|
`;
|
||||||
@@ -18,6 +19,7 @@ exports[`can render one menu item 1`] = `
|
|||||||
menu
|
menu
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>"
|
</div>"
|
||||||
`;
|
`;
|
||||||
@@ -27,6 +29,7 @@ exports[`mobile mode: navbar is different 1`] = `
|
|||||||
<a aria-label=\\"Applications\\" class=\\"o_title fa fa-th\\" href=\\"#\\" title=\\"Applications\\" accesskey=\\"h\\"></a>
|
<a aria-label=\\"Applications\\" class=\\"o_title fa fa-th\\" href=\\"#\\" title=\\"Applications\\" accesskey=\\"h\\"></a>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
||||||
|
<li>MOBILEMODE</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>"
|
</div>"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ let props: Props;
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = makeTestFixture();
|
fixture = makeTestFixture();
|
||||||
env = makeTestEnv();
|
env = makeTestEnv();
|
||||||
props = { inMenu: false, toggleHomeMenu: () => {} };
|
props = { inMenu: false, toggleHome: () => {} };
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -42,19 +42,21 @@ test("can be rendered", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("can be closed by clicking on it (if sticky)", async () => {
|
test("can be closed by clicking on it (if sticky)", async () => {
|
||||||
let notif: INotification;
|
let notifs: INotification[] = [];
|
||||||
let removed = false;
|
env.notifications.on(
|
||||||
env.notifications.on("notification_added", null, _notif => (notif = _notif));
|
"notifications_updated",
|
||||||
|
null,
|
||||||
|
_notifs => (notifs = _notifs)
|
||||||
|
);
|
||||||
env.notifications.add({
|
env.notifications.add({
|
||||||
title: "title",
|
title: "title",
|
||||||
message: "message",
|
message: "message",
|
||||||
sticky: true
|
sticky: true
|
||||||
});
|
});
|
||||||
env.notifications.on("notification_removed", null, () => (removed = true));
|
|
||||||
|
|
||||||
const navbar = new Notification(env, notif!);
|
const navbar = new Notification(env, notifs[0]);
|
||||||
await navbar.mount(fixture);
|
await navbar.mount(fixture);
|
||||||
expect(removed).toBe(false);
|
expect(notifs.length).toBe(1);
|
||||||
(<any>fixture.getElementsByClassName("o_close")[0]).click();
|
(<any>fixture.getElementsByClassName("o_close")[0]).click();
|
||||||
expect(removed).toBe(true);
|
expect(notifs.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user