diff --git a/README.md b/README.md index f622f6be..526352d5 100644 --- a/README.md +++ b/README.md @@ -185,3 +185,5 @@ We have 3 main folders and 3 main files: - **Rendering** think about batching all patching updates in a nextanimationframe + +- when t-widget/t-on directives are compiled, the widget/eval context is actually available. Should we use that info to determine if bound methods exists on the widget? diff --git a/web/static/src/scss/app.scss b/web/static/src/scss/app.scss index ec5514ee..5dcd58b0 100644 --- a/web/static/src/scss/app.scss +++ b/web/static/src/scss/app.scss @@ -1,5 +1,7 @@ $navbar-height: 40px; $main-color: #875a7b; +$o-notification-info-bg-color: #fcfbea; +$o-main-text-color: #666666; html { height: 100%; @@ -53,6 +55,41 @@ body { } } +/* Notifications */ +.o_notification_container { + position: absolute; + width: 300px; + right: 5px; + top: $navbar-height; + bottom: 0; + + .o_notification { + padding: 0; + margin: 5px 0 0 0; + background-color: $o-notification-info-bg-color; + box-shadow: 0px 0px 5px 1px $o-main-text-color; + + .o_notification_title { + display: flex; + align-items: center; + + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + padding: 10px 10px 10px 20px; + + font-weight: bold; + + .o_icon { + display: inline-block; + margin-right: 20px; + color: rgba(0, 0, 0, 0.3); + } + } + .o_notification_content { + padding: 10px; + } + } +} + /* Discuss */ .o_discuss { padding: 20px; diff --git a/web/static/src/ts/core/qweb_vdom.ts b/web/static/src/ts/core/qweb_vdom.ts index 917de500..1d45729b 100644 --- a/web/static/src/ts/core/qweb_vdom.ts +++ b/web/static/src/ts/core/qweb_vdom.ts @@ -29,6 +29,7 @@ export class Context { caller: Element | undefined; shouldDefineOwner: boolean = false; shouldProtectContext: boolean = false; + inLoop: boolean = false; constructor() { this.rootContext = this; @@ -134,7 +135,7 @@ export class QWeb { if (!doc.firstChild) { throw new Error("Invalid template (should not be empty)"); } - if (doc.firstChild.nodeName === "parsererror") { + if (doc.getElementsByTagName("parsererror").length) { throw new Error("Invalid XML in template"); } let tbranch = doc.querySelectorAll("[t-elif], [t-else]"); @@ -331,13 +332,17 @@ export class QWeb { const attrs: string[] = []; const tattrs: number[] = []; for (let i = 0; i < attributes.length; i++) { - const name = attributes[i].name; + let name = attributes[i].name; const value = attributes[i].textContent!; // regular attributes if (!name.startsWith("t-")) { const attID = ctx.generateID(); ctx.addLine(`let _${attID} = '${value}';`); + if (!name.match(/^[a-zA-Z]+$/)) { + // attribute contains 'non letters' => we want to quote it + name = '"' + name + '"'; + } attrs.push(`${name}: _${attID}`); } @@ -624,6 +629,7 @@ const forEachDirective: Directive = { priority: 10, atNodeEncounter({ node, qweb, ctx }): boolean { ctx.rootContext.shouldProtectContext = true; + ctx.inLoop = true; const elems = node.getAttribute("t-foreach")!; const name = node.getAttribute("t-as")!; let arrayID = ctx.generateID(); @@ -715,8 +721,9 @@ const widgetDirective: Directive = { ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`); ctx.addLine(`c${ctx.parentNode}.push(_${dummyID});`); ctx.addLine(`let def${defID};`); + let templateID = ctx.inLoop ? `(${widgetID} + i)` : String(widgetID); ctx.addLine( - `let w${widgetID} = ${widgetID} in context.__widget__.cmap ? context.__widget__.children[context.__widget__.cmap[${widgetID}]] : false;` + `let w${widgetID} = ${templateID} in context.__widget__.cmap ? context.__widget__.children[context.__widget__.cmap[${templateID}]] : false;` ); ctx.addLine(`if (w${widgetID}) {`); @@ -734,7 +741,7 @@ const widgetDirective: Directive = { `let _${widgetID} = new context.widgets['${value}'](owner, ${props});` ); ctx.addLine( - `context.__widget__.cmap[${widgetID}] = _${widgetID}.__widget__.id;` + `context.__widget__.cmap[${templateID}] = _${widgetID}.__widget__.id;` ); ctx.addLine( `def${defID} = _${widgetID}._start().then(() => _${widgetID}._render()).then(vnode=>{c${ diff --git a/web/static/src/ts/env.ts b/web/static/src/ts/env.ts index e952c00b..e8d3a7d3 100644 --- a/web/static/src/ts/env.ts +++ b/web/static/src/ts/env.ts @@ -4,6 +4,10 @@ import { WEnv } from "./core/widget"; import { registry } from "./registry"; import { ActionManager, IActionManager } from "./services/action_manager"; import { Ajax, IAjax } from "./services/ajax"; +import { + INotificationManager, + NotificationManager +} from "./services/notifications"; import { IRouter, Router } from "./services/router"; //------------------------------------------------------------------------------ @@ -21,6 +25,7 @@ export interface Env extends WEnv { ajax: IAjax; router: IRouter; menus: Menu[]; + notifications: INotificationManager; // helpers rpc: IAjax["rpc"]; @@ -48,6 +53,8 @@ export const makeEnvironment = memoize(function(): Env { const router = new Router(); const ajax = new Ajax(); const actionManager = new ActionManager(router, registry); + const notifications = new NotificationManager(); + const menus = [ { title: "Discuss", actionID: 1 }, { title: "CRM", actionID: 2 } @@ -62,6 +69,7 @@ export const makeEnvironment = memoize(function(): Env { router, actionManager, menus, + notifications, rpc: ajax.rpc, debug: false diff --git a/web/static/src/ts/root.ts b/web/static/src/ts/root.ts index c75fe253..d5154057 100644 --- a/web/static/src/ts/root.ts +++ b/web/static/src/ts/root.ts @@ -1,12 +1,18 @@ import { Widget } from "./core/widget"; import { Navbar } from "./widgets/navbar"; import { ActionWidget } from "./services/action_manager"; +import { INotification } from "./services/notifications"; +import { Notification } from "./widgets/notification"; import { Env } from "./env"; const template = `
`);
const result = renderToString(qweb, "test");
diff --git a/web/static/tests/core/widget.test.ts b/web/static/tests/core/widget.test.ts
index 7c016f19..c932966f 100644
--- a/web/static/tests/core/widget.test.ts
+++ b/web/static/tests/core/widget.test.ts
@@ -1,5 +1,6 @@
import { WEnv, Widget } from "../../src/ts/core/widget";
import { makeTestWEnv, makeTestFixture } from "../helpers";
+import { normalize } from "../helpers";
//------------------------------------------------------------------------------
// Setup and helpers
@@ -573,6 +574,37 @@ describe("composition", () => {
"