mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 21b07afb60 | |||
| 13c3178760 | |||
| 2b565ce1b4 | |||
| cfcf2c6714 | |||
| 92174559a1 | |||
| 840348892f | |||
| b988a68b6f | |||
| 7869a1f2c6 | |||
| 9e81d14d50 | |||
| 0e4a55ba09 | |||
| 6e9b68dafa | |||
| ba7c9063c0 | |||
| 3a361876ad | |||
| 58b8572f0a | |||
| c23637e6d8 | |||
| 4a4b1fbba5 | |||
| 536d9e1762 | |||
| 6ed68372a6 |
+4
-2
@@ -447,8 +447,10 @@ bus.addEventListener('event-name', callback);
|
|||||||
Rationale: it makes it easier to have just one interface to remember, it makes
|
Rationale: it makes it easier to have just one interface to remember, it makes
|
||||||
the code simpler
|
the code simpler
|
||||||
|
|
||||||
Migration: most bus methods need to be adapted. So, `bus.on(...)` has to be
|
Migration: most bus methods need to be adapted. So, `bus.on("event-type", owner, (info) => {...})` has to be
|
||||||
rewritten like this: `bus.addEventListener(...)`.
|
rewritten like this: `bus.addEventListener("event-type", (({detail: info}) => {...}).bind(owner))`.
|
||||||
|
|
||||||
|
Do not forget to similarly replace `bus.off(...)` by `bus.removeEventListener(...)`
|
||||||
|
|
||||||
### 22. `Store` is removed
|
### 22. `Store` is removed
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
# 🦉 How to debug Owl applications 🦉
|
|
||||||
|
|
||||||
Non trivial applications become quickly more difficult to understand. It is then
|
|
||||||
useful to have a solid understanding of what is going on. To help with that,
|
|
||||||
logging useful information is extremely valuable. There is a [javascript file](../../tools/debug.js) which can be evaluated in an application.
|
|
||||||
|
|
||||||
Once it is executed, it will log a lot of information on each component main hooks. The following code is a minified version to make it easier to copy/paste:
|
|
||||||
|
|
||||||
```
|
|
||||||
function debugOwl(t,e){let n,o="[OWL_DEBUG]";function r(t){let e;try{e=JSON.stringify(t||{})}catch(t){e="<JSON error>"}return e.length>200&&(e=e.slice(0,200)+"..."),e}if(Object.defineProperty(t.Component,"current",{get:()=>n,set(s){n=s;const i=s.constructor.name;if(e.componentBlackList&&e.componentBlackList.test(i))return;if(e.componentWhiteList&&!e.componentWhiteList.test(i))return;let l;Object.defineProperty(n,"__owl__",{get:()=>l,set(n){!function(n,s,i){let l=`${s}<id=${i}>`,c=t=>console.log(`${o} ${l} ${t}`),u=t=>(!e.methodBlackList||!e.methodBlackList.includes(t))&&!(e.methodWhiteList&&!e.methodWhiteList.includes(t));u("constructor")&&c(`constructor, props=${r(n.props)}`);u("willStart")&&t.hooks.onWillStart(()=>{c("willStart")});u("mounted")&&t.hooks.onMounted(()=>{c("mounted")});u("willUpdateProps")&&t.hooks.onWillUpdateProps(t=>{c(`willUpdateProps, nextprops=${r(t)}`)});u("willPatch")&&t.hooks.onWillPatch(()=>{c("willPatch")});u("patched")&&t.hooks.onPatched(()=>{c("patched")});u("willUnmount")&&t.hooks.onWillUnmount(()=>{c("willUnmount")});const d=n.__render.bind(n);n.__render=function(...t){c("rendering template"),d(...t)};const h=n.render.bind(n);n.render=function(...t){const e=n.__owl__;let o="render";return e.isMounted||e.currentFiber||(o+=" (warning: component is not mounted, this render has no effect)"),c(o),h(...t)};const p=n.mount.bind(n);n.mount=function(...t){return c("mount"),p(...t)}}(s,i,(l=n).id)}})}}),e.logScheduler){let e=t.Component.scheduler.start,n=t.Component.scheduler.stop;t.Component.scheduler.start=function(){this.isRunning||console.log(`${o} scheduler: start running tasks queue`),e.call(this)},t.Component.scheduler.stop=function(){this.isRunning&&console.log(`${o} scheduler: stop running tasks queue`),n.call(this)}}if(e.logStore){let e=t.Store.prototype.dispatch;t.Store.prototype.dispatch=function(t,...n){return console.log(`${o} store: action '${t}' dispatched. Payload: '${r(n)}'`),e.call(this,t,...n)}}}
|
|
||||||
debugOwl(owl, {
|
|
||||||
// componentBlackList: /App/, // regexp
|
|
||||||
// componentWhiteList: /SomeComponent/, // regexp
|
|
||||||
// methodBlackList: ["mounted"], // list of method names
|
|
||||||
// methodWhiteList: ["willStart"], // list of method names
|
|
||||||
logScheduler: false, // display/mute scheduler logs
|
|
||||||
logStore: true, // display/mute store logs
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
The above code, once pasted somewhere in the main javascript file of an owl
|
|
||||||
application, will log information looking like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
[OWL_DEBUG] TodoApp<id=1> constructor, props={}
|
|
||||||
[OWL_DEBUG] TodoApp<id=1> mount
|
|
||||||
[OWL_DEBUG] TodoApp<id=1> willStart
|
|
||||||
[OWL_DEBUG] TodoApp<id=1> rendering template
|
|
||||||
[OWL_DEBUG] TodoItem<id=2> constructor, props={"id":2,"completed":false,"title":"hey"}
|
|
||||||
[OWL_DEBUG] TodoItem<id=2> willStart
|
|
||||||
[OWL_DEBUG] TodoItem<id=3> constructor, props={"id":4,"completed":false,"title":"aaa"}
|
|
||||||
[OWL_DEBUG] TodoItem<id=3> willStart
|
|
||||||
[OWL_DEBUG] TodoItem<id=2> rendering template
|
|
||||||
[OWL_DEBUG] TodoItem<id=3> rendering template
|
|
||||||
[OWL_DEBUG] TodoItem<id=3> mounted
|
|
||||||
[OWL_DEBUG] TodoItem<id=2> mounted
|
|
||||||
[OWL_DEBUG] TodoApp<id=1> mounted
|
|
||||||
```
|
|
||||||
|
|
||||||
Each component has an internal `id`, which is very useful when debugging.
|
|
||||||
|
|
||||||
Note that it is certainly useful to run this code at some point in an application,
|
|
||||||
just to get a feel of what each user action implies, for the framework.
|
|
||||||
@@ -9,7 +9,6 @@ Are you new to Owl? This is the place to start!
|
|||||||
- [How to start an Owl project](learning/quick_start.md)
|
- [How to start an Owl project](learning/quick_start.md)
|
||||||
- [How to test Components](learning/how_to_test.md)
|
- [How to test Components](learning/how_to_test.md)
|
||||||
- [How to write Single File Components](learning/how_to_write_sfc.md)
|
- [How to write Single File Components](learning/how_to_write_sfc.md)
|
||||||
- [How to write debug Owl applications](learning/how_to_debug.md)
|
|
||||||
|
|
||||||
## Reference
|
## Reference
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@odoo/owl",
|
"name": "@odoo/owl",
|
||||||
"version": "1.4.7",
|
"version": "2.0.0-alpha1",
|
||||||
"description": "Odoo Web Library (OWL)",
|
"description": "Odoo Web Library (OWL)",
|
||||||
"main": "dist/owl.cjs.js",
|
"main": "dist/owl.cjs.js",
|
||||||
"browser": "dist/owl.iife.js",
|
"browser": "dist/owl.iife.js",
|
||||||
@@ -18,10 +18,10 @@
|
|||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --watch",
|
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --watch",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"tools:serve": "python3 tools/server.py || python tools/server.py",
|
"playground:serve": "python3 tools/server.py || python tools/server.py",
|
||||||
"tools": "npm run build && npm run tools:serve",
|
"playground": "npm run build && npm run playground:serve",
|
||||||
"pretools:watch": "npm run build",
|
"preplayground:watch": "npm run build",
|
||||||
"tools:watch": "npm-run-all --parallel tools:serve \"build:* -- --watch\"",
|
"playground:watch": "npm-run-all --parallel playground:serve \"build:* -- --watch\"",
|
||||||
"prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --write",
|
"prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --write",
|
||||||
"check-formatting": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --check",
|
"check-formatting": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --check",
|
||||||
"publish": "npm run build && npm publish",
|
"publish": "npm run build && npm publish",
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"prettier": "2.4.1",
|
"prettier": "2.4.1",
|
||||||
"rollup": "^2.56.3",
|
"rollup": "^2.56.3",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"rollup-plugin-typescript2": "^0.30.0",
|
"rollup-plugin-typescript2": "^0.31.1",
|
||||||
"sass": "^1.16.1",
|
"sass": "^1.16.1",
|
||||||
"source-map-support": "^0.5.10",
|
"source-map-support": "^0.5.10",
|
||||||
"ts-jest": "^27.0.5",
|
"ts-jest": "^27.0.5",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export interface AppConfig {
|
|||||||
env?: Env;
|
env?: Env;
|
||||||
translatableAttributes?: string[];
|
translatableAttributes?: string[];
|
||||||
translateFn?: (s: string) => string;
|
translateFn?: (s: string) => string;
|
||||||
|
templates?: string | Document;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEV_MSG = `Owl is running in 'dev' mode.
|
export const DEV_MSG = `Owl is running in 'dev' mode.
|
||||||
@@ -49,6 +50,9 @@ export class App<T extends typeof Component = any> extends TemplateSet {
|
|||||||
if (config.translatableAttributes) {
|
if (config.translatableAttributes) {
|
||||||
this.translatableAttributes = config.translatableAttributes;
|
this.translatableAttributes = config.translatableAttributes;
|
||||||
}
|
}
|
||||||
|
if (config.templates) {
|
||||||
|
this.addTemplates(config.templates);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,19 +17,24 @@ function callSlot(
|
|||||||
parent: any,
|
parent: any,
|
||||||
key: string,
|
key: string,
|
||||||
name: string,
|
name: string,
|
||||||
defaultSlot?: (ctx: any, key: string) => BDom,
|
dynamic: boolean,
|
||||||
dynamic?: boolean
|
extra: any,
|
||||||
|
defaultContent?: (ctx: any, node: any, key: string) => BDom
|
||||||
): BDom {
|
): BDom {
|
||||||
const slots = ctx.__owl__.slots;
|
const slots = (ctx.props && ctx.props.slots) || {};
|
||||||
const slotFn = slots[name];
|
const { __render, __ctx, __scope } = slots[name] || {};
|
||||||
const slotBDom = slotFn ? slotFn(parent, key) : null;
|
const slotScope = Object.create(__ctx || {});
|
||||||
if (defaultSlot) {
|
if (__scope) {
|
||||||
|
slotScope[__scope] = extra || {};
|
||||||
|
}
|
||||||
|
const slotBDom = __render ? __render.call(__ctx.__owl__.component, slotScope, parent, key) : null;
|
||||||
|
if (defaultContent) {
|
||||||
let child1: BDom | undefined = undefined;
|
let child1: BDom | undefined = undefined;
|
||||||
let child2: BDom | undefined = undefined;
|
let child2: BDom | undefined = undefined;
|
||||||
if (slotBDom) {
|
if (slotBDom) {
|
||||||
child1 = dynamic ? toggler(name, slotBDom) : slotBDom;
|
child1 = dynamic ? toggler(name, slotBDom) : slotBDom;
|
||||||
} else {
|
} else {
|
||||||
child2 = defaultSlot(parent, key);
|
child2 = defaultContent.call(ctx.__owl__.component, ctx, parent, key);
|
||||||
}
|
}
|
||||||
return multi([child1, child2]);
|
return multi([child1, child2]);
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-1
@@ -7,6 +7,36 @@ const bdom = { text, createBlock, list, multi, html, toggler, component };
|
|||||||
|
|
||||||
export const globalTemplates: { [key: string]: string | Node } = {};
|
export const globalTemplates: { [key: string]: string | Node } = {};
|
||||||
|
|
||||||
|
function parseXML(xml: string): Document {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
|
||||||
|
const doc = parser.parseFromString(xml, "text/xml");
|
||||||
|
if (doc.getElementsByTagName("parsererror").length) {
|
||||||
|
let msg = "Invalid XML in template.";
|
||||||
|
const parsererrorText = doc.getElementsByTagName("parsererror")[0].textContent;
|
||||||
|
if (parsererrorText) {
|
||||||
|
msg += "\nThe parser has produced the following error message:\n" + parsererrorText;
|
||||||
|
const re = /\d+/g;
|
||||||
|
const firstMatch = re.exec(parsererrorText);
|
||||||
|
if (firstMatch) {
|
||||||
|
const lineNumber = Number(firstMatch[0]);
|
||||||
|
const line = xml.split("\n")[lineNumber - 1];
|
||||||
|
const secondMatch = re.exec(parsererrorText);
|
||||||
|
if (line && secondMatch) {
|
||||||
|
const columnIndex = Number(secondMatch[0]) - 1;
|
||||||
|
if (line[columnIndex]) {
|
||||||
|
msg +=
|
||||||
|
`\nThe error might be located at xml line ${lineNumber} column ${columnIndex}\n` +
|
||||||
|
`${line}\n${"-".repeat(columnIndex - 1)}^`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
export class TemplateSet {
|
export class TemplateSet {
|
||||||
rawTemplates: typeof globalTemplates = Object.create(globalTemplates);
|
rawTemplates: typeof globalTemplates = Object.create(globalTemplates);
|
||||||
templates: { [name: string]: Template } = {};
|
templates: { [name: string]: Template } = {};
|
||||||
@@ -33,7 +63,11 @@ export class TemplateSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addTemplates(xml: string | Document, options: { allowDuplicate?: boolean } = {}) {
|
addTemplates(xml: string | Document, options: { allowDuplicate?: boolean } = {}) {
|
||||||
xml = xml instanceof Document ? xml : new DOMParser().parseFromString(xml, "text/xml");
|
if (!xml) {
|
||||||
|
// empty string
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
xml = xml instanceof Document ? xml : parseXML(xml);
|
||||||
for (const template of xml.querySelectorAll("[t-name]")) {
|
for (const template of xml.querySelectorAll("[t-name]")) {
|
||||||
const name = template.getAttribute("t-name")!;
|
const name = template.getAttribute("t-name")!;
|
||||||
template.removeAttribute("t-name");
|
template.removeAttribute("t-name");
|
||||||
|
|||||||
+109
-54
@@ -137,7 +137,6 @@ function createContext(parentCtx: Context, params?: Partial<Context>) {
|
|||||||
|
|
||||||
class CodeTarget {
|
class CodeTarget {
|
||||||
name: string;
|
name: string;
|
||||||
signature: string = "";
|
|
||||||
indentLevel = 0;
|
indentLevel = 0;
|
||||||
loopLevel = 0;
|
loopLevel = 0;
|
||||||
code: string[] = [];
|
code: string[] = [];
|
||||||
@@ -334,7 +333,7 @@ export class CodeGenerator {
|
|||||||
|
|
||||||
generateFunctions(fn: CodeTarget) {
|
generateFunctions(fn: CodeTarget) {
|
||||||
this.addLine("");
|
this.addLine("");
|
||||||
this.addLine(`const ${fn.name} = ${fn.signature}`);
|
this.addLine(`function ${fn.name}(ctx, node, key) {`);
|
||||||
if (fn.hasCache) {
|
if (fn.hasCache) {
|
||||||
this.addLine(`let cache = ctx.cache || {};`);
|
this.addLine(`let cache = ctx.cache || {};`);
|
||||||
this.addLine(`let nextCache = ctx.cache = {};`);
|
this.addLine(`let nextCache = ctx.cache = {};`);
|
||||||
@@ -344,8 +343,23 @@ export class CodeGenerator {
|
|||||||
}
|
}
|
||||||
this.addLine(`}`);
|
this.addLine(`}`);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
captureExpression(expr: string): string {
|
* Captures variables that are used inside of an expression. This is useful
|
||||||
|
* because in compiled code, almost all variables are accessed through the ctx
|
||||||
|
* object. In the case of functions, that lookup in the context can be delayed
|
||||||
|
* which can cause issues if the value has changed since the function was
|
||||||
|
* defined.
|
||||||
|
*
|
||||||
|
* @param expr the expression to capture
|
||||||
|
* @param forceCapture whether the expression should capture its scope even if
|
||||||
|
* it doesn't contain a function. Useful when the expression will be used as
|
||||||
|
* a function body.
|
||||||
|
* @returns a new expression that uses the captured values
|
||||||
|
*/
|
||||||
|
captureExpression(expr: string, forceCapture: boolean = false): string {
|
||||||
|
if (!forceCapture && !expr.includes("=>")) {
|
||||||
|
return compileExpr(expr);
|
||||||
|
}
|
||||||
const tokens = compileExprToArray(expr);
|
const tokens = compileExprToArray(expr);
|
||||||
const mapping = new Map<string, string>();
|
const mapping = new Map<string, string>();
|
||||||
return tokens
|
return tokens
|
||||||
@@ -535,7 +549,7 @@ export class CodeGenerator {
|
|||||||
if (isDynamic) {
|
if (isDynamic) {
|
||||||
const str = ast.ref.replace(
|
const str = ast.ref.replace(
|
||||||
INTERP_REGEXP,
|
INTERP_REGEXP,
|
||||||
(expr) => "${" + this.captureExpression(expr.slice(2, -2)) + "}"
|
(expr) => "${" + this.captureExpression(expr.slice(2, -2), true) + "}"
|
||||||
);
|
);
|
||||||
const idx = block!.insertData(`(el) => refs[\`${str}\`] = el`);
|
const idx = block!.insertData(`(el) => refs[\`${str}\`] = el`);
|
||||||
attrs["block-ref"] = String(idx);
|
attrs["block-ref"] = String(idx);
|
||||||
@@ -734,6 +748,10 @@ export class CodeGenerator {
|
|||||||
this.addLine(
|
this.addLine(
|
||||||
`const [${keys}, ${vals}, ${l}, ${c}] = prepareList(${compileExpr(ast.collection)});`
|
`const [${keys}, ${vals}, ${l}, ${c}] = prepareList(${compileExpr(ast.collection)});`
|
||||||
);
|
);
|
||||||
|
// Throw errors on duplicate keys in dev mode
|
||||||
|
if (this.dev) {
|
||||||
|
this.addLine(`const keys${block.id} = new Set();`);
|
||||||
|
}
|
||||||
this.addLine(`for (let ${loopVar} = 0; ${loopVar} < ${l}; ${loopVar}++) {`);
|
this.addLine(`for (let ${loopVar} = 0; ${loopVar} < ${l}; ${loopVar}++) {`);
|
||||||
this.target.indentLevel++;
|
this.target.indentLevel++;
|
||||||
this.addLine(`ctx[\`${ast.elem}\`] = ${vals}[${loopVar}];`);
|
this.addLine(`ctx[\`${ast.elem}\`] = ${vals}[${loopVar}];`);
|
||||||
@@ -750,6 +768,13 @@ export class CodeGenerator {
|
|||||||
this.addLine(`ctx[\`${ast.elem}_value\`] = ${keys}[${loopVar}];`);
|
this.addLine(`ctx[\`${ast.elem}_value\`] = ${keys}[${loopVar}];`);
|
||||||
}
|
}
|
||||||
this.addLine(`let key${this.target.loopLevel} = ${ast.key ? compileExpr(ast.key) : loopVar};`);
|
this.addLine(`let key${this.target.loopLevel} = ${ast.key ? compileExpr(ast.key) : loopVar};`);
|
||||||
|
if (this.dev) {
|
||||||
|
// Throw error on duplicate keys in dev mode
|
||||||
|
this.addLine(
|
||||||
|
`if (keys${block.id}.has(key${this.target.loopLevel})) { throw new Error(\`Got duplicate key in t-foreach: \${key${this.target.loopLevel}}\`)}`
|
||||||
|
);
|
||||||
|
this.addLine(`keys${block.id}.add(key${this.target.loopLevel});`);
|
||||||
|
}
|
||||||
let id: string;
|
let id: string;
|
||||||
if (ast.memo) {
|
if (ast.memo) {
|
||||||
this.target.hasCache = true;
|
this.target.hasCache = true;
|
||||||
@@ -936,24 +961,84 @@ export class CodeGenerator {
|
|||||||
|
|
||||||
compileComponent(ast: ASTComponent, ctx: Context) {
|
compileComponent(ast: ASTComponent, ctx: Context) {
|
||||||
let { block } = ctx;
|
let { block } = ctx;
|
||||||
let extraArgs: { [key: string]: string } = {};
|
|
||||||
|
|
||||||
// props
|
// props
|
||||||
const props: string[] = [];
|
const props: string[] = [];
|
||||||
|
let hasSlotsProp = false;
|
||||||
for (let p in ast.props) {
|
for (let p in ast.props) {
|
||||||
props.push(`${p}: ${compileExpr(ast.props[p]) || undefined}`);
|
const propName = /^[a-z_]+$/i.test(p) ? p : `'${p}'`;
|
||||||
|
props.push(`${propName}: ${this.captureExpression(ast.props[p]) || undefined}`);
|
||||||
|
if (p === "slots") {
|
||||||
|
hasSlotsProp = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// slots
|
||||||
|
const hasSlot = !!Object.keys(ast.slots).length;
|
||||||
|
let slotDef: string = "";
|
||||||
|
if (hasSlot) {
|
||||||
|
let ctxStr = "ctx";
|
||||||
|
if (this.target.loopLevel || !this.hasSafeContext) {
|
||||||
|
ctxStr = this.generateId("ctx");
|
||||||
|
this.addLine(`const ${ctxStr} = capture(ctx);`);
|
||||||
|
}
|
||||||
|
let slotStr: string[] = [];
|
||||||
|
const initialTarget = this.target;
|
||||||
|
for (let slotName in ast.slots) {
|
||||||
|
let name = this.generateId("slot");
|
||||||
|
const slot = new CodeTarget(name);
|
||||||
|
this.functions.push(slot);
|
||||||
|
this.target = slot;
|
||||||
|
const subCtx: Context = createContext(ctx);
|
||||||
|
this.compileAST(ast.slots[slotName].content, subCtx);
|
||||||
|
const params = [`__render: ${name}, __ctx: ${ctxStr}`];
|
||||||
|
const scope = ast.slots[slotName].scope;
|
||||||
|
if (scope) {
|
||||||
|
params.push(`__scope: "${scope}"`);
|
||||||
|
}
|
||||||
|
if (ast.slots[slotName].attrs) {
|
||||||
|
for (const [n, v] of Object.entries(ast.slots[slotName].attrs!)) {
|
||||||
|
params.push(`${n}: ${compileExpr(v) || undefined}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const slotInfo = `{${params.join(", ")}}`;
|
||||||
|
if (this.hasRef) {
|
||||||
|
slot.code.unshift(` const refs = ctx.__owl__.refs`);
|
||||||
|
slotStr.push(`'${slotName}': ${slotInfo}`);
|
||||||
|
} else {
|
||||||
|
slotStr.push(`'${slotName}': ${slotInfo}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.target = initialTarget;
|
||||||
|
slotDef = `{${slotStr.join(", ")}}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slotDef && !(ast.dynamicProps || hasSlotsProp)) {
|
||||||
|
props.push(`slots: ${slotDef}`);
|
||||||
|
}
|
||||||
|
|
||||||
const propStr = `{${props.join(",")}}`;
|
const propStr = `{${props.join(",")}}`;
|
||||||
|
|
||||||
let propString = propStr;
|
let propString = propStr;
|
||||||
if (ast.dynamicProps) {
|
if (ast.dynamicProps) {
|
||||||
if (!props.length) {
|
if (!props.length) {
|
||||||
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)})`;
|
propString = `${compileExpr(ast.dynamicProps)}`;
|
||||||
} else {
|
} else {
|
||||||
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)}, ${propStr})`;
|
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)}, ${propStr})`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let propVar: string;
|
||||||
|
if ((slotDef && (ast.dynamicProps || hasSlotsProp)) || this.dev) {
|
||||||
|
propVar = this.generateId("props");
|
||||||
|
this.addLine(`const ${propVar!} = ${propString}`);
|
||||||
|
propString = propVar!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slotDef && (ast.dynamicProps || hasSlotsProp)) {
|
||||||
|
this.addLine(`${propVar!}.slots = Object.assign(${slotDef}, ${propVar!}.slots)`);
|
||||||
|
}
|
||||||
|
|
||||||
// cmap key
|
// cmap key
|
||||||
const key = this.generateComponentKey();
|
const key = this.generateComponentKey();
|
||||||
let expr: string;
|
let expr: string;
|
||||||
@@ -965,41 +1050,7 @@ export class CodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.dev) {
|
if (this.dev) {
|
||||||
const propVar = this.generateId("props");
|
this.addLine(`helpers.validateProps(${expr}, ${propVar!}, ctx)`);
|
||||||
this.addLine(`const ${propVar} = ${propString}`);
|
|
||||||
this.addLine(`helpers.validateProps(${expr}, ${propVar}, ctx)`);
|
|
||||||
propString = propVar;
|
|
||||||
}
|
|
||||||
|
|
||||||
// slots
|
|
||||||
const hasSlot = !!Object.keys(ast.slots).length;
|
|
||||||
let slotDef: string;
|
|
||||||
if (hasSlot) {
|
|
||||||
let ctxStr = "ctx";
|
|
||||||
if (this.target.loopLevel || !this.hasSafeContext) {
|
|
||||||
ctxStr = this.generateId("ctx");
|
|
||||||
this.addLine(`const ${ctxStr} = capture(ctx);`);
|
|
||||||
}
|
|
||||||
let slotStr: string[] = [];
|
|
||||||
const initialTarget = this.target;
|
|
||||||
for (let slotName in ast.slots) {
|
|
||||||
let name = this.generateId("slot");
|
|
||||||
const slot = new CodeTarget(name);
|
|
||||||
slot.signature = "ctx => (node, key) => {";
|
|
||||||
this.functions.push(slot);
|
|
||||||
this.target = slot;
|
|
||||||
const subCtx: Context = createContext(ctx);
|
|
||||||
this.compileAST(ast.slots[slotName], subCtx);
|
|
||||||
if (this.hasRef) {
|
|
||||||
slot.code.unshift(` const refs = ctx.__owl__.refs`);
|
|
||||||
slotStr.push(`'${slotName}': ${name}(${ctxStr})`);
|
|
||||||
} else {
|
|
||||||
slotStr.push(`'${slotName}': ${name}(${ctxStr})`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.target = initialTarget;
|
|
||||||
slotDef = `{${slotStr.join(", ")}}`;
|
|
||||||
extraArgs.slots = slotDef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block && (ctx.forceNewBlock === false || ctx.tKeyExpr)) {
|
if (block && (ctx.forceNewBlock === false || ctx.tKeyExpr)) {
|
||||||
@@ -1012,12 +1063,7 @@ export class CodeGenerator {
|
|||||||
keyArg = `${ctx.tKeyExpr} + ${keyArg}`;
|
keyArg = `${ctx.tKeyExpr} + ${keyArg}`;
|
||||||
}
|
}
|
||||||
const blockArgs = `${expr}, ${propString}, ${keyArg}, node, ctx`;
|
const blockArgs = `${expr}, ${propString}, ${keyArg}, node, ctx`;
|
||||||
let blockExpr = `component(${blockArgs}${hasSlot ? ", true" : ""})`;
|
let blockExpr = `component(${blockArgs})`;
|
||||||
if (Object.keys(extraArgs).length) {
|
|
||||||
this.shouldDefineAssign = true;
|
|
||||||
const content = Object.keys(extraArgs).map((k) => `${k}: ${extraArgs[k]}`);
|
|
||||||
blockExpr = `assign(${blockExpr}, {${content.join(", ")}})`;
|
|
||||||
}
|
|
||||||
if (ast.isDynamic) {
|
if (ast.isDynamic) {
|
||||||
blockExpr = `toggler(${expr}, ${blockExpr})`;
|
blockExpr = `toggler(${expr}, ${blockExpr})`;
|
||||||
}
|
}
|
||||||
@@ -1036,24 +1082,33 @@ export class CodeGenerator {
|
|||||||
} else {
|
} else {
|
||||||
slotName = "'" + ast.name + "'";
|
slotName = "'" + ast.name + "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let scope = null;
|
||||||
|
if (ast.attrs) {
|
||||||
|
const params = [];
|
||||||
|
for (const [n, v] of Object.entries(ast.attrs!)) {
|
||||||
|
params.push(`${n}: ${compileExpr(v) || undefined}`);
|
||||||
|
}
|
||||||
|
scope = `{${params.join(", ")}}`;
|
||||||
|
}
|
||||||
|
|
||||||
if (ast.defaultContent) {
|
if (ast.defaultContent) {
|
||||||
let name = this.generateId("defaultSlot");
|
let name = this.generateId("defaultContent");
|
||||||
const slot = new CodeTarget(name);
|
const slot = new CodeTarget(name);
|
||||||
slot.signature = "ctx => {";
|
|
||||||
this.functions.push(slot);
|
this.functions.push(slot);
|
||||||
const initialTarget = this.target;
|
const initialTarget = this.target;
|
||||||
const subCtx: Context = createContext(ctx);
|
const subCtx: Context = createContext(ctx);
|
||||||
this.target = slot;
|
this.target = slot;
|
||||||
this.compileAST(ast.defaultContent, subCtx);
|
this.compileAST(ast.defaultContent, subCtx);
|
||||||
this.target = initialTarget;
|
this.target = initialTarget;
|
||||||
blockString = `callSlot(ctx, node, key, ${slotName}, ${name}, ${dynamic})`;
|
blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope}, ${name})`;
|
||||||
} else {
|
} else {
|
||||||
if (dynamic) {
|
if (dynamic) {
|
||||||
let name = this.generateId("slot");
|
let name = this.generateId("slot");
|
||||||
this.addLine(`const ${name} = ${slotName};`);
|
this.addLine(`const ${name} = ${slotName};`);
|
||||||
blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}))`;
|
blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}), ${dynamic}, ${scope})`;
|
||||||
} else {
|
} else {
|
||||||
blockString = `callSlot(ctx, node, key, ${slotName})`;
|
blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope})`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (block) {
|
if (block) {
|
||||||
|
|||||||
+133
-37
@@ -118,12 +118,13 @@ export interface ASTComponent {
|
|||||||
isDynamic: boolean;
|
isDynamic: boolean;
|
||||||
dynamicProps: string | null;
|
dynamicProps: string | null;
|
||||||
props: { [name: string]: string };
|
props: { [name: string]: string };
|
||||||
slots: { [name: string]: AST };
|
slots: { [name: string]: { content: AST; attrs?: { [key: string]: string }; scope?: string } };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTSlot {
|
export interface ASTSlot {
|
||||||
type: ASTType.TSlot;
|
type: ASTType.TSlot;
|
||||||
name: string;
|
name: string;
|
||||||
|
attrs: { [key: string]: string };
|
||||||
defaultContent: AST | null;
|
defaultContent: AST | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +177,8 @@ interface ParsingContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function parse(xml: string | Node): AST {
|
export function parse(xml: string | Node): AST {
|
||||||
const node = xml instanceof Element ? xml : parseXML(`<t>${xml}</t>`).firstChild!;
|
const node = xml instanceof Element ? xml : (parseXML(`<t>${xml}</t>`).firstChild! as Element);
|
||||||
|
normalizeXML(node);
|
||||||
const ctx = { inPreTag: false, inSVG: false };
|
const ctx = { inPreTag: false, inSVG: false };
|
||||||
const ast = parseNode(node, ctx);
|
const ast = parseNode(node, ctx);
|
||||||
if (!ast) {
|
if (!ast) {
|
||||||
@@ -593,7 +595,7 @@ function parseTCall(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
if (ast && ast.type === ASTType.TComponent) {
|
if (ast && ast.type === ASTType.TComponent) {
|
||||||
return {
|
return {
|
||||||
...ast,
|
...ast,
|
||||||
slots: { default: tcall },
|
slots: { default: { content: tcall } },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -702,6 +704,20 @@ function parseTSetNode(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
// Components
|
// Components
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Error messages when trying to use an unsupported directive on a component
|
||||||
|
const directiveErrorMap = new Map([
|
||||||
|
["t-on", "t-on is no longer supported on components. Consider passing a callback in props."],
|
||||||
|
[
|
||||||
|
"t-ref",
|
||||||
|
"t-ref is no longer supported on components. Consider exposing only the public part of the component's API through a callback prop.",
|
||||||
|
],
|
||||||
|
["t-att", "t-att makes no sense on component: props are already treated as expressions"],
|
||||||
|
[
|
||||||
|
"t-attf",
|
||||||
|
"t-attf is not supported on components: use template strings for string interpolation in props",
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
function parseComponent(node: Element, ctx: ParsingContext): AST | null {
|
function parseComponent(node: Element, ctx: ParsingContext): AST | null {
|
||||||
let name = node.tagName;
|
let name = node.tagName;
|
||||||
const firstLetter = name[0];
|
const firstLetter = name[0];
|
||||||
@@ -725,10 +741,9 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
const props: ASTComponent["props"] = {};
|
const props: ASTComponent["props"] = {};
|
||||||
for (let name of node.getAttributeNames()) {
|
for (let name of node.getAttributeNames()) {
|
||||||
const value = node.getAttribute(name)!;
|
const value = node.getAttribute(name)!;
|
||||||
if (name.startsWith("t-on-")) {
|
if (name.startsWith("t-")) {
|
||||||
throw new Error(
|
const message = directiveErrorMap.get(name.split("-").slice(0, 2).join("-"));
|
||||||
"t-on is no longer supported on Component node. Consider passing a callback in props."
|
throw new Error(message || `unsupported directive on Component: ${name}`);
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
props[name] = value;
|
props[name] = value;
|
||||||
}
|
}
|
||||||
@@ -741,6 +756,11 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
// named slots
|
// named slots
|
||||||
const slotNodes = Array.from(clone.querySelectorAll("[t-set-slot]"));
|
const slotNodes = Array.from(clone.querySelectorAll("[t-set-slot]"));
|
||||||
for (let slotNode of slotNodes) {
|
for (let slotNode of slotNodes) {
|
||||||
|
if (slotNode.tagName !== "t") {
|
||||||
|
throw new Error(
|
||||||
|
`Directive 't-set-slot' can only be used on <t> nodes (used on a <${slotNode.tagName}>)`
|
||||||
|
);
|
||||||
|
}
|
||||||
const name = slotNode.getAttribute("t-set-slot")!;
|
const name = slotNode.getAttribute("t-set-slot")!;
|
||||||
|
|
||||||
// check if this is defined in a sub component (in which case it should
|
// check if this is defined in a sub component (in which case it should
|
||||||
@@ -762,14 +782,27 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
slotNode.remove();
|
slotNode.remove();
|
||||||
const slotAst = parseNode(slotNode, ctx);
|
const slotAst = parseNode(slotNode, ctx);
|
||||||
if (slotAst) {
|
if (slotAst) {
|
||||||
slots[name] = slotAst;
|
const slotInfo: any = { content: slotAst };
|
||||||
|
const attrs: { [key: string]: string } = {};
|
||||||
|
for (let attributeName of slotNode.getAttributeNames()) {
|
||||||
|
const value = slotNode.getAttribute(attributeName)!;
|
||||||
|
if (attributeName === "t-slot-scope") {
|
||||||
|
slotInfo.scope = value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
attrs[attributeName] = value;
|
||||||
|
}
|
||||||
|
if (Object.keys(attrs).length) {
|
||||||
|
slotInfo.attrs = attrs;
|
||||||
|
}
|
||||||
|
slots[name] = slotInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// default slot
|
// default slot
|
||||||
const defaultContent = parseChildNodes(clone, ctx);
|
const defaultContent = parseChildNodes(clone, ctx);
|
||||||
if (defaultContent) {
|
if (defaultContent) {
|
||||||
slots.default = defaultContent;
|
slots.default = { content: defaultContent };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, slots };
|
return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, slots };
|
||||||
@@ -783,9 +816,17 @@ function parseTSlot(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
if (!node.hasAttribute("t-slot")) {
|
if (!node.hasAttribute("t-slot")) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const name = node.getAttribute("t-slot")!;
|
||||||
|
node.removeAttribute("t-slot");
|
||||||
|
const attrs: { [key: string]: string } = {};
|
||||||
|
for (let attributeName of node.getAttributeNames()) {
|
||||||
|
const value = node.getAttribute(attributeName)!;
|
||||||
|
attrs[attributeName] = value;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
type: ASTType.TSlot,
|
type: ASTType.TSlot,
|
||||||
name: node.getAttribute("t-slot")!,
|
name,
|
||||||
|
attrs,
|
||||||
defaultContent: parseChildNodes(node, ctx),
|
defaultContent: parseChildNodes(node, ctx),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -822,34 +863,17 @@ function parseChildNodes(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
return { type: ASTType.Multi, content: children };
|
return { type: ASTType.Multi, content: children };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function parseXML(xml: string): Document {
|
|
||||||
const parser = new DOMParser();
|
|
||||||
|
|
||||||
const doc = parser.parseFromString(xml, "text/xml");
|
/**
|
||||||
if (doc.getElementsByTagName("parsererror").length) {
|
* Normalizes the content of an Element so that t-if/t-elif/t-else directives
|
||||||
let msg = "Invalid XML in template.";
|
* immediately follow one another (by removing empty text nodes or comments).
|
||||||
const parsererrorText = doc.getElementsByTagName("parsererror")[0].textContent;
|
* Throws an error when a conditional branching statement is malformed. This
|
||||||
if (parsererrorText) {
|
* function modifies the Element in place.
|
||||||
msg += "\nThe parser has produced the following error message:\n" + parsererrorText;
|
*
|
||||||
const re = /\d+/g;
|
* @param el the element containing the tree that should be normalized
|
||||||
const firstMatch = re.exec(parsererrorText);
|
*/
|
||||||
if (firstMatch) {
|
function normalizeTIf(el: Element) {
|
||||||
const lineNumber = Number(firstMatch[0]);
|
let tbranch = el.querySelectorAll("[t-elif], [t-else]");
|
||||||
const line = xml.split("\n")[lineNumber - 1];
|
|
||||||
const secondMatch = re.exec(parsererrorText);
|
|
||||||
if (line && secondMatch) {
|
|
||||||
const columnIndex = Number(secondMatch[0]) - 1;
|
|
||||||
if (line[columnIndex]) {
|
|
||||||
msg +=
|
|
||||||
`\nThe error might be located at xml line ${lineNumber} column ${columnIndex}\n` +
|
|
||||||
`${line}\n${"-".repeat(columnIndex - 1)}^`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error(msg);
|
|
||||||
}
|
|
||||||
let tbranch = doc.querySelectorAll("[t-elif], [t-else]");
|
|
||||||
for (let i = 0, ilen = tbranch.length; i < ilen; i++) {
|
for (let i = 0, ilen = tbranch.length; i < ilen; i++) {
|
||||||
let node = tbranch[i];
|
let node = tbranch[i];
|
||||||
let prevElem = node.previousElementSibling!;
|
let prevElem = node.previousElementSibling!;
|
||||||
@@ -883,6 +907,78 @@ function parseXML(xml: string): Document {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes the content of an Element so that t-esc directives on components
|
||||||
|
* are removed and instead places a <t t-esc=""> as the default slot of the
|
||||||
|
* component. Also throws if the component already has content. This function
|
||||||
|
* modifies the Element in place.
|
||||||
|
*
|
||||||
|
* @param el the element containing the tree that should be normalized
|
||||||
|
*/
|
||||||
|
function normalizeTEsc(el: Element) {
|
||||||
|
const elements = [...el.querySelectorAll("[t-esc]")].filter(
|
||||||
|
(el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component")
|
||||||
|
);
|
||||||
|
for (const el of elements) {
|
||||||
|
if (el.childNodes.length) {
|
||||||
|
throw new Error("Cannot have t-esc on a component that already has content");
|
||||||
|
}
|
||||||
|
const value = el.getAttribute("t-esc");
|
||||||
|
el.removeAttribute("t-esc");
|
||||||
|
const t = el.ownerDocument.createElement("t");
|
||||||
|
if (value != null) {
|
||||||
|
t.setAttribute("t-esc", value);
|
||||||
|
}
|
||||||
|
el.appendChild(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes the tree inside a given element and do some preliminary validation
|
||||||
|
* on it. This function modifies the Element in place.
|
||||||
|
*
|
||||||
|
* @param el the element containing the tree that should be normalized
|
||||||
|
*/
|
||||||
|
function normalizeXML(el: Element) {
|
||||||
|
normalizeTIf(el);
|
||||||
|
normalizeTEsc(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an XML string into an XML document, throwing errors on parser errors
|
||||||
|
* instead of returning an XML document containing the parseerror.
|
||||||
|
*
|
||||||
|
* @param xml the string to parse
|
||||||
|
* @returns an XML document corresponding to the content of the string
|
||||||
|
*/
|
||||||
|
function parseXML(xml: string): XMLDocument {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(xml, "text/xml");
|
||||||
|
if (doc.getElementsByTagName("parsererror").length) {
|
||||||
|
let msg = "Invalid XML in template.";
|
||||||
|
const parsererrorText = doc.getElementsByTagName("parsererror")[0].textContent;
|
||||||
|
if (parsererrorText) {
|
||||||
|
msg += "\nThe parser has produced the following error message:\n" + parsererrorText;
|
||||||
|
const re = /\d+/g;
|
||||||
|
const firstMatch = re.exec(parsererrorText);
|
||||||
|
if (firstMatch) {
|
||||||
|
const lineNumber = Number(firstMatch[0]);
|
||||||
|
const line = xml.split("\n")[lineNumber - 1];
|
||||||
|
const secondMatch = re.exec(parsererrorText);
|
||||||
|
if (line && secondMatch) {
|
||||||
|
const columnIndex = Number(secondMatch[0]) - 1;
|
||||||
|
if (line[columnIndex]) {
|
||||||
|
msg +=
|
||||||
|
`\nThe error might be located at xml line ${lineNumber} column ${columnIndex}\n` +
|
||||||
|
`${line}\n${"-".repeat(columnIndex - 1)}^`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import type { Env } from "../app/app";
|
import type { Env } from "../app/app";
|
||||||
import type { ComponentNode } from "./component_node";
|
import type { ComponentNode } from "./component_node";
|
||||||
|
|
||||||
export type Props = { [key: string]: any };
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Component Class
|
// Component Class
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -28,7 +26,7 @@ export class Component {
|
|||||||
|
|
||||||
setup() {}
|
setup() {}
|
||||||
|
|
||||||
render(deep: boolean = false): Promise<void> {
|
render(): Promise<void> {
|
||||||
return this.__owl__.render(deep);
|
return this.__owl__.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import type { App, Env } from "../app/app";
|
import type { App, Env } from "../app/app";
|
||||||
import { BDom, VNode } from "../blockdom";
|
import { BDom, VNode } from "../blockdom";
|
||||||
import { clearReactivesForCallback, Reactive, reactive } from "../reactivity";
|
import { Component } from "./component";
|
||||||
import { batched, Callback } from "../utils";
|
|
||||||
import { Component, Props } from "./component";
|
|
||||||
import { fibersInError, handleError } from "./error_handling";
|
|
||||||
import {
|
import {
|
||||||
Fiber,
|
Fiber,
|
||||||
makeChildFiber,
|
makeChildFiber,
|
||||||
@@ -11,68 +8,20 @@ import {
|
|||||||
MountFiber,
|
MountFiber,
|
||||||
MountOptions,
|
MountOptions,
|
||||||
RootFiber,
|
RootFiber,
|
||||||
__internal__destroyed
|
__internal__destroyed,
|
||||||
} from "./fibers";
|
} from "./fibers";
|
||||||
|
import { handleError, fibersInError } from "./error_handling";
|
||||||
import { applyDefaultProps } from "./props_validation";
|
import { applyDefaultProps } from "./props_validation";
|
||||||
import { STATUS } from "./status";
|
import { STATUS } from "./status";
|
||||||
import { applyStyles } from "./style";
|
import { applyStyles } from "./style";
|
||||||
|
|
||||||
let currentNode: ComponentNode | null = null;
|
|
||||||
|
|
||||||
export function getCurrent(): ComponentNode | null {
|
|
||||||
return currentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Integration with reactivity system (useState)
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const batchedRenderFunctions = new WeakMap<ComponentNode, Callback>();
|
|
||||||
/**
|
|
||||||
* Creates a reactive object that will be observed by the current component.
|
|
||||||
* Reading data from the returned object (eg during rendering) will cause the
|
|
||||||
* component to subscribe to that data and be rerendered when it changes.
|
|
||||||
*
|
|
||||||
* @param state the state to observe
|
|
||||||
* @returns a reactive object that will cause the component to re-render on
|
|
||||||
* relevant changes
|
|
||||||
* @see reactive
|
|
||||||
*/
|
|
||||||
export function useState<T extends object>(state: T): Reactive<T> {
|
|
||||||
if (!batchedRenderFunctions.has(currentNode!)) {
|
|
||||||
batchedRenderFunctions.set(
|
|
||||||
currentNode!,
|
|
||||||
batched(() => currentNode!.render())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const render = batchedRenderFunctions.get(currentNode!)!;
|
|
||||||
const reactiveState = reactive(state, render);
|
|
||||||
|
|
||||||
// manual implementation of onWillUnmount to break cyclic dependency
|
|
||||||
currentNode!.willUnmount.unshift( clearReactivesForCallback.bind(null, render))
|
|
||||||
return reactiveState;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// component function (used in compiled template code)
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
function arePropsDifferent(props1: Props, props2: Props): boolean {
|
|
||||||
for (let k in props1) {
|
|
||||||
if (props1[k] !== props2[k]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
export function component(
|
export function component(
|
||||||
name: string | typeof Component,
|
name: string | typeof Component,
|
||||||
props: any,
|
props: any,
|
||||||
key: string,
|
key: string,
|
||||||
ctx: ComponentNode,
|
ctx: ComponentNode,
|
||||||
parent: any,
|
parent: any
|
||||||
hasSlots: boolean = false
|
|
||||||
): ComponentNode {
|
): ComponentNode {
|
||||||
console.warn('asdf')
|
|
||||||
let node: any = ctx.children[key];
|
let node: any = ctx.children[key];
|
||||||
let isDynamic = typeof name !== "string";
|
let isDynamic = typeof name !== "string";
|
||||||
|
|
||||||
@@ -90,11 +39,7 @@ export function component(
|
|||||||
|
|
||||||
const parentFiber = ctx.fiber!;
|
const parentFiber = ctx.fiber!;
|
||||||
if (node) {
|
if (node) {
|
||||||
console.warn('coucou');
|
node.updateAndRender(props, parentFiber);
|
||||||
if (hasSlots || parentFiber.deep || arePropsDifferent(node.component.props, props)) {
|
|
||||||
console.warn('coucou3');
|
|
||||||
node.updateAndRender(props, parentFiber);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// new component
|
// new component
|
||||||
let C;
|
let C;
|
||||||
@@ -116,9 +61,15 @@ export function component(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Component VNode class
|
// Component VNode
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
let currentNode: ComponentNode | null = null;
|
||||||
|
|
||||||
|
export function getCurrent(): ComponentNode | null {
|
||||||
|
return currentNode;
|
||||||
|
}
|
||||||
|
|
||||||
type LifecycleHook = Function;
|
type LifecycleHook = Function;
|
||||||
|
|
||||||
export class ComponentNode<T extends typeof Component = typeof Component>
|
export class ComponentNode<T extends typeof Component = typeof Component>
|
||||||
@@ -136,7 +87,6 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
|||||||
level: number;
|
level: number;
|
||||||
childEnv: Env;
|
childEnv: Env;
|
||||||
children: { [key: string]: ComponentNode } = Object.create(null);
|
children: { [key: string]: ComponentNode } = Object.create(null);
|
||||||
slots: any = {};
|
|
||||||
refs: any = {};
|
refs: any = {};
|
||||||
|
|
||||||
willStart: LifecycleHook[] = [];
|
willStart: LifecycleHook[] = [];
|
||||||
@@ -155,9 +105,6 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
|||||||
applyDefaultProps(props, C);
|
applyDefaultProps(props, C);
|
||||||
const env = (parent && parent.childEnv) || app.env;
|
const env = (parent && parent.childEnv) || app.env;
|
||||||
this.childEnv = env;
|
this.childEnv = env;
|
||||||
// if (props) {
|
|
||||||
// props = useState(props);
|
|
||||||
// }
|
|
||||||
this.component = new C(props, env, this) as any;
|
this.component = new C(props, env, this) as any;
|
||||||
this.renderFn = app.getTemplate(C.template).bind(this.component, this.component, this);
|
this.renderFn = app.getTemplate(C.template).bind(this.component, this.component, this);
|
||||||
if (C.style) {
|
if (C.style) {
|
||||||
@@ -174,6 +121,7 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initiateRender(fiber: Fiber | MountFiber) {
|
async initiateRender(fiber: Fiber | MountFiber) {
|
||||||
|
this.fiber = fiber;
|
||||||
if (this.mounted.length) {
|
if (this.mounted.length) {
|
||||||
fiber.root.mounted.push(fiber);
|
fiber.root.mounted.push(fiber);
|
||||||
}
|
}
|
||||||
@@ -189,23 +137,34 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async render(deep: boolean = false) {
|
async render() {
|
||||||
let fiber = this.fiber;
|
const current = this.fiber;
|
||||||
if (fiber && !fiber.bdom && !fibersInError.has(fiber)) {
|
if (current && !current.bdom && !fibersInError.has(current)) {
|
||||||
return fiber.root.promise;
|
return current.root.promise;
|
||||||
}
|
}
|
||||||
if (!this.bdom && !fiber) {
|
if (!this.bdom && !current) {
|
||||||
// should find a way to return the future mounting promise
|
// should find a way to return the future mounting promise
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fiber = makeRootFiber(this);
|
const fiber = makeRootFiber(this);
|
||||||
fiber.deep = deep;
|
this.fiber = fiber;
|
||||||
this.app.scheduler.addFiber(fiber);
|
this.app.scheduler.addFiber(fiber);
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
if (this.status === STATUS.DESTROYED) {
|
if (this.status === STATUS.DESTROYED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.fiber === fiber) {
|
// We only want to actually render the component if the following two
|
||||||
|
// conditions are true:
|
||||||
|
// * this.fiber: it could be null, in which case the render has been cancelled
|
||||||
|
// * (current || !fiber.parent): if current is not null, this means that the
|
||||||
|
// render function was called when a render was already occurring. In this
|
||||||
|
// case, the pending rendering was cancelled, and the fiber needs to be
|
||||||
|
// rendered to complete the work. If current is null, we check that the
|
||||||
|
// fiber has no parent. If that is the case, the fiber was downgraded from
|
||||||
|
// a root fiber to a child fiber in the previous microtick, because it was
|
||||||
|
// embedded in a rendering coming from above, so the fiber will be rendered
|
||||||
|
// in the next microtick anyway, so we should not render it again.
|
||||||
|
if (this.fiber && (current || !fiber.parent)) {
|
||||||
this._render(fiber);
|
this._render(fiber);
|
||||||
}
|
}
|
||||||
return fiber.root.promise;
|
return fiber.root.promise;
|
||||||
@@ -254,6 +213,7 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
|||||||
async updateAndRender(props: any, parentFiber: Fiber) {
|
async updateAndRender(props: any, parentFiber: Fiber) {
|
||||||
// update
|
// update
|
||||||
const fiber = makeChildFiber(this, parentFiber);
|
const fiber = makeChildFiber(this, parentFiber);
|
||||||
|
this.fiber = fiber;
|
||||||
if (this.willPatch.length) {
|
if (this.willPatch.length) {
|
||||||
parentFiber.root.willPatch.push(fiber);
|
parentFiber.root.willPatch.push(fiber);
|
||||||
}
|
}
|
||||||
@@ -294,10 +254,6 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
|||||||
}
|
}
|
||||||
|
|
||||||
patch() {
|
patch() {
|
||||||
if (!this.fiber) {
|
|
||||||
// component was not rendered => no need to do anything
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.bdom!.patch(this!.fiber!.bdom!, false);
|
this.bdom!.patch(this!.fiber!.bdom!, false);
|
||||||
this.fiber!.appliedToDom = true;
|
this.fiber!.appliedToDom = true;
|
||||||
this.fiber = null;
|
this.fiber = null;
|
||||||
|
|||||||
@@ -67,11 +67,9 @@ export class Fiber {
|
|||||||
parent: Fiber | null;
|
parent: Fiber | null;
|
||||||
children: Fiber[] = [];
|
children: Fiber[] = [];
|
||||||
appliedToDom = false;
|
appliedToDom = false;
|
||||||
deep: boolean = false;
|
|
||||||
|
|
||||||
constructor(node: ComponentNode, parent: Fiber | null) {
|
constructor(node: ComponentNode, parent: Fiber | null) {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
node.fiber = this;
|
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
if (parent) {
|
if (parent) {
|
||||||
const root = parent.root;
|
const root = parent.root;
|
||||||
|
|||||||
+1
-2
@@ -34,7 +34,6 @@ import type { AppConfig } from "./app/app";
|
|||||||
import { App } from "./app/app";
|
import { App } from "./app/app";
|
||||||
import { Component } from "./component/component";
|
import { Component } from "./component/component";
|
||||||
import { getCurrent } from "./component/component_node";
|
import { getCurrent } from "./component/component_node";
|
||||||
export { useState } from "./component/component_node";
|
|
||||||
|
|
||||||
export { App, Component };
|
export { App, Component };
|
||||||
|
|
||||||
@@ -56,7 +55,7 @@ export { status } from "./component/status";
|
|||||||
export { Portal } from "./portal";
|
export { Portal } from "./portal";
|
||||||
export { Memo } from "./memo";
|
export { Memo } from "./memo";
|
||||||
export { css, xml } from "./tags";
|
export { css, xml } from "./tags";
|
||||||
export { reactive } from "./reactivity";
|
export { useState, reactive } from "./reactivity";
|
||||||
export { useEffect, useEnv, useExternalListener, useRef, useSubEnv } from "./hooks";
|
export { useEffect, useEnv, useExternalListener, useRef, useSubEnv } from "./hooks";
|
||||||
export { EventBus, whenReady, loadFile, markup } from "./utils";
|
export { EventBus, whenReady, loadFile, markup } from "./utils";
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -39,7 +39,7 @@ export class Memo extends Component {
|
|||||||
*/
|
*/
|
||||||
function shallowEqual(p1: any, p2: any): boolean {
|
function shallowEqual(p1: any, p2: any): boolean {
|
||||||
for (let k in p1) {
|
for (let k in p1) {
|
||||||
if (p1[k] !== p2[k]) {
|
if (k !== "slots" && p1[k] !== p2[k]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+53
-3
@@ -1,4 +1,5 @@
|
|||||||
import { Callback } from "./utils";
|
import { onWillUnmount } from "./component/lifecycle_hooks";
|
||||||
|
import { ComponentNode, getCurrent } from "./component/component_node";
|
||||||
|
|
||||||
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
|
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
|
||||||
const TARGET = Symbol("Target");
|
const TARGET = Symbol("Target");
|
||||||
@@ -7,7 +8,8 @@ const KEYCHANGES = Symbol("Key changes");
|
|||||||
|
|
||||||
type ObjectKey = string | number | symbol;
|
type ObjectKey = string | number | symbol;
|
||||||
type Target = object;
|
type Target = object;
|
||||||
export type Reactive<T extends Target = Target> = T & {
|
type Callback = () => void;
|
||||||
|
type Reactive<T extends Target = Target> = T & {
|
||||||
[TARGET]: any;
|
[TARGET]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ const callbacksToTargets = new WeakMap<Callback, Set<Target>>();
|
|||||||
*
|
*
|
||||||
* @param callback the callback for which the reactives need to be cleared
|
* @param callback the callback for which the reactives need to be cleared
|
||||||
*/
|
*/
|
||||||
export function clearReactivesForCallback(callback: Callback): void {
|
function clearReactivesForCallback(callback: Callback): void {
|
||||||
const targetsToClear = callbacksToTargets.get(callback);
|
const targetsToClear = callbacksToTargets.get(callback);
|
||||||
if (!targetsToClear) {
|
if (!targetsToClear) {
|
||||||
return;
|
return;
|
||||||
@@ -188,3 +190,51 @@ export function reactive<T extends Target>(target: T, callback: Callback): React
|
|||||||
return reactivesForTarget.get(callback) as Reactive<T>;
|
return reactivesForTarget.get(callback) as Reactive<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a batched version of a callback so that all calls to it in the same
|
||||||
|
* microtick will only call the original callback once.
|
||||||
|
*
|
||||||
|
* @param callback the callback to batch
|
||||||
|
* @returns a batched version of the original callback
|
||||||
|
*/
|
||||||
|
export function batched(callback: Callback): Callback {
|
||||||
|
let called = false;
|
||||||
|
return async () => {
|
||||||
|
// This await blocks all calls to the callback here, then releases them sequentially
|
||||||
|
// in the next microtick. This line decides the granularity of the batch.
|
||||||
|
await Promise.resolve();
|
||||||
|
if (!called) {
|
||||||
|
called = true;
|
||||||
|
callback();
|
||||||
|
// wait for all calls in this microtick to fall through before resetting "called"
|
||||||
|
// so that only the first call to the batched function calls the original callback
|
||||||
|
await Promise.resolve();
|
||||||
|
called = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const batchedRenderFunctions = new WeakMap<ComponentNode, Callback>();
|
||||||
|
/**
|
||||||
|
* Creates a reactive object that will be observed by the current component.
|
||||||
|
* Reading data from the returned object (eg during rendering) will cause the
|
||||||
|
* component to subscribe to that data and be rerendered when it changes.
|
||||||
|
*
|
||||||
|
* @param state the state to observe
|
||||||
|
* @returns a reactive object that will cause the component to re-render on
|
||||||
|
* relevant changes
|
||||||
|
* @see reactive
|
||||||
|
*/
|
||||||
|
export function useState<T extends object>(state: T): Reactive<T> {
|
||||||
|
const node = getCurrent()!;
|
||||||
|
if (!batchedRenderFunctions.has(node)) {
|
||||||
|
batchedRenderFunctions.set(
|
||||||
|
node,
|
||||||
|
batched(() => node.render())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const render = batchedRenderFunctions.get(node)!;
|
||||||
|
const reactiveState = reactive(state, render);
|
||||||
|
onWillUnmount(() => clearReactivesForCallback(render));
|
||||||
|
return reactiveState;
|
||||||
|
}
|
||||||
|
|||||||
+2
-2
@@ -5,9 +5,9 @@ import { globalTemplates } from "./app/template_set";
|
|||||||
// Global templates
|
// Global templates
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
export function xml(strings: TemplateStringsArray, ...args: any[]) {
|
export function xml(...args: Parameters<typeof String.raw>) {
|
||||||
const name = `__template__${xml.nextId++}`;
|
const name = `__template__${xml.nextId++}`;
|
||||||
const value = String.raw(strings, ...args);
|
const value = String.raw(...args);
|
||||||
globalTemplates[name] = value;
|
globalTemplates[name] = value;
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,3 @@
|
|||||||
export type Callback = () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a batched version of a callback so that all calls to it in the same
|
|
||||||
* microtick will only call the original callback once.
|
|
||||||
*
|
|
||||||
* @param callback the callback to batch
|
|
||||||
* @returns a batched version of the original callback
|
|
||||||
*/
|
|
||||||
export function batched(callback: Callback): Callback {
|
|
||||||
let called = false;
|
|
||||||
return async () => {
|
|
||||||
// This await blocks all calls to the callback here, then releases them sequentially
|
|
||||||
// in the next microtick. This line decides the granularity of the batch.
|
|
||||||
await Promise.resolve();
|
|
||||||
if (!called) {
|
|
||||||
called = true;
|
|
||||||
callback();
|
|
||||||
// wait for all calls in this microtick to fall through before resetting "called"
|
|
||||||
// so that only the first call to the batched function calls the original callback
|
|
||||||
await Promise.resolve();
|
|
||||||
called = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EventBus extends EventTarget {
|
export class EventBus extends EventTarget {
|
||||||
trigger(name: string, payload?: any) {
|
trigger(name: string, payload?: any) {
|
||||||
this.dispatchEvent(new CustomEvent(name, { detail: payload }));
|
this.dispatchEvent(new CustomEvent(name, { detail: payload }));
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ exports[`t-on can bind event handler 1`] = `
|
|||||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['add'];
|
let d1 = [ctx['add'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
return block1([d1]);
|
return block1([d1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -116,10 +115,8 @@ exports[`t-on can bind two event handlers 1`] = `
|
|||||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\" block-handler-1=\\"dblclick\\">Click</button>\`);
|
let block1 = createBlock(\`<button block-handler-0=\\"click\\" block-handler-1=\\"dblclick\\">Click</button>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['handleClick'];
|
let d1 = [ctx['handleClick'], ctx];
|
||||||
let d1 = [v1, ctx];
|
let d2 = [ctx['handleDblClick'], ctx];
|
||||||
const v2 = ctx['handleDblClick'];
|
|
||||||
let d2 = [v2, ctx];
|
|
||||||
return block1([d1, d2]);
|
return block1([d1, d2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -134,8 +131,7 @@ exports[`t-on handler is bound to proper owner 1`] = `
|
|||||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['add'];
|
let d1 = [ctx['add'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
return block1([d1]);
|
return block1([d1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -155,8 +151,7 @@ exports[`t-on handler is bound to proper owner, part 2 1`] = `
|
|||||||
for (let i1 = 0; i1 < l_block1; i1++) {
|
for (let i1 = 0; i1 < l_block1; i1++) {
|
||||||
ctx[\`value\`] = v_block1[i1];
|
ctx[\`value\`] = v_block1[i1];
|
||||||
let key1 = ctx['value'];
|
let key1 = ctx['value'];
|
||||||
const v1 = ctx['add'];
|
let d1 = [ctx['add'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
c_block1[i1] = withKey(block2([d1]), key1);
|
c_block1[i1] = withKey(block2([d1]), key1);
|
||||||
}
|
}
|
||||||
return list(c_block1);
|
return list(c_block1);
|
||||||
@@ -173,8 +168,7 @@ exports[`t-on handler is bound to proper owner, part 3 1`] = `
|
|||||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['add'];
|
let d1 = [ctx['add'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
return block1([d1]);
|
return block1([d1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -202,8 +196,7 @@ exports[`t-on handler is bound to proper owner, part 4 1`] = `
|
|||||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['add'];
|
let d1 = [ctx['add'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
return block1([d1]);
|
return block1([d1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -242,8 +235,7 @@ exports[`t-on receive event in first argument 1`] = `
|
|||||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['add'];
|
let d1 = [ctx['add'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
return block1([d1]);
|
return block1([d1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -258,10 +250,8 @@ exports[`t-on t-on modifiers (native listener) basic support for native listener
|
|||||||
let block1 = createBlock(\`<div class=\\"myClass\\" block-handler-0=\\"click\\"><button block-handler-1=\\"click\\">Button</button></div>\`);
|
let block1 = createBlock(\`<div class=\\"myClass\\" block-handler-0=\\"click\\"><button block-handler-1=\\"click\\">Button</button></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['divClicked'];
|
let d1 = [ctx['divClicked'], ctx];
|
||||||
let d1 = [v1, ctx];
|
let d2 = [ctx['btnClicked'], ctx];
|
||||||
const v2 = ctx['btnClicked'];
|
|
||||||
let d2 = [v2, ctx];
|
|
||||||
return block1([d1, d2]);
|
return block1([d1, d2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -276,8 +266,7 @@ exports[`t-on t-on modifiers (native listener) t-on combined with t-esc 1`] = `
|
|||||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><block-text-1/></button></div>\`);
|
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><block-text-1/></button></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['onClick'];
|
let d1 = [ctx['onClick'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
let d2 = ctx['text'];
|
let d2 = ctx['text'];
|
||||||
return block1([d1, d2]);
|
return block1([d1, d2]);
|
||||||
}
|
}
|
||||||
@@ -293,8 +282,7 @@ exports[`t-on t-on modifiers (native listener) t-on combined with t-out 1`] = `
|
|||||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><block-child-0/></button></div>\`);
|
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><block-child-0/></button></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['onClick'];
|
let d1 = [ctx['onClick'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
let b2 = safeOutput(ctx['html']);
|
let b2 = safeOutput(ctx['html']);
|
||||||
return block1([d1], [b2]);
|
return block1([d1], [b2]);
|
||||||
}
|
}
|
||||||
@@ -310,10 +298,8 @@ exports[`t-on t-on modifiers (native listener) t-on with .capture modifier 1`] =
|
|||||||
let block1 = createBlock(\`<div block-handler-0=\\"click.capture\\"><button block-handler-1=\\"click\\">Button</button></div>\`);
|
let block1 = createBlock(\`<div block-handler-0=\\"click.capture\\"><button block-handler-1=\\"click\\">Button</button></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['onCapture'];
|
let d1 = [\\"capture\\", ctx['onCapture'], ctx];
|
||||||
let d1 = [\\"capture\\", v1, ctx];
|
let d2 = [ctx['doSomething'], ctx];
|
||||||
const v2 = ctx['doSomething'];
|
|
||||||
let d2 = [v2, ctx];
|
|
||||||
return block1([d1, d2]);
|
return block1([d1, d2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -343,8 +329,7 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent and self modifi
|
|||||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click.prevent.self\\"><span>Button</span></button></div>\`);
|
let block1 = createBlock(\`<div><button block-handler-0=\\"click.prevent.self\\"><span>Button</span></button></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['onClick'];
|
let d1 = [\\"prevent\\",\\"self\\", ctx['onClick'], ctx];
|
||||||
let d1 = [\\"prevent\\",\\"self\\", v1, ctx];
|
|
||||||
return block1([d1]);
|
return block1([d1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -359,12 +344,9 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent and/or stop mod
|
|||||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click.prevent\\">Button 1</button><button block-handler-1=\\"click.stop\\">Button 2</button><button block-handler-2=\\"click.prevent.stop\\">Button 3</button></div>\`);
|
let block1 = createBlock(\`<div><button block-handler-0=\\"click.prevent\\">Button 1</button><button block-handler-1=\\"click.stop\\">Button 2</button><button block-handler-2=\\"click.prevent.stop\\">Button 3</button></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['onClickPrevented'];
|
let d1 = [\\"prevent\\", ctx['onClickPrevented'], ctx];
|
||||||
let d1 = [\\"prevent\\", v1, ctx];
|
let d2 = [\\"stop\\", ctx['onClickStopped'], ctx];
|
||||||
const v2 = ctx['onClickStopped'];
|
let d3 = [\\"prevent\\",\\"stop\\", ctx['onClickPreventedAndStopped'], ctx];
|
||||||
let d2 = [\\"stop\\", v2, ctx];
|
|
||||||
const v3 = ctx['onClickPreventedAndStopped'];
|
|
||||||
let d3 = [\\"prevent\\",\\"stop\\", v3, ctx];
|
|
||||||
return block1([d1, d2, d3]);
|
return block1([d1, d2, d3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -406,8 +388,7 @@ exports[`t-on t-on modifiers (native listener) t-on with self and prevent modifi
|
|||||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click.self.prevent\\"><span>Button</span></button></div>\`);
|
let block1 = createBlock(\`<div><button block-handler-0=\\"click.self.prevent\\"><span>Button</span></button></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['onClick'];
|
let d1 = [\\"self\\",\\"prevent\\", ctx['onClick'], ctx];
|
||||||
let d1 = [\\"self\\",\\"prevent\\", v1, ctx];
|
|
||||||
return block1([d1]);
|
return block1([d1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -422,10 +403,8 @@ exports[`t-on t-on modifiers (native listener) t-on with self modifier 1`] = `
|
|||||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><span>Button</span></button><button block-handler-1=\\"click.self\\"><span>Button</span></button></div>\`);
|
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><span>Button</span></button><button block-handler-1=\\"click.self\\"><span>Button</span></button></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['onClick'];
|
let d1 = [ctx['onClick'], ctx];
|
||||||
let d1 = [v1, ctx];
|
let d2 = [\\"self\\", ctx['onClickSelf'], ctx];
|
||||||
const v2 = ctx['onClickSelf'];
|
|
||||||
let d2 = [\\"self\\", v2, ctx];
|
|
||||||
return block1([d1, d2]);
|
return block1([d1, d2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -440,10 +419,8 @@ exports[`t-on t-on modifiers (synthetic listener) basic support for synthetic 1`
|
|||||||
let block1 = createBlock(\`<div block-handler-0=\\"click.synthetic\\"><button block-handler-1=\\"click.synthetic\\">Button</button></div>\`);
|
let block1 = createBlock(\`<div block-handler-0=\\"click.synthetic\\"><button block-handler-1=\\"click.synthetic\\">Button</button></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['divClicked'];
|
let d1 = [\\"synthetic\\", ctx['divClicked'], ctx];
|
||||||
let d1 = [\\"synthetic\\", v1, ctx];
|
let d2 = [\\"synthetic\\", ctx['btnClicked'], ctx];
|
||||||
const v2 = ctx['btnClicked'];
|
|
||||||
let d2 = [\\"synthetic\\", v2, ctx];
|
|
||||||
return block1([d1, d2]);
|
return block1([d1, d2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -523,8 +500,7 @@ exports[`t-on t-on with t-call 1`] = `
|
|||||||
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
|
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['update'];
|
let d1 = [ctx['update'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
return block1([d1]);
|
return block1([d1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ exports[`misc other complex template 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
const callTemplate_14 = getTemplate(\`LOAD_INFOS_TEMPLATE\`);
|
const callTemplate_2 = getTemplate(\`LOAD_INFOS_TEMPLATE\`);
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><header><nav class=\\"navbar navbar-expand-md navbar-light bg-light\\"><a block-attribute-0=\\"href\\"><b style=\\"color:#777;\\"><block-text-1/></b></a><button type=\\"button\\" class=\\"navbar-toggler\\" data-toggle=\\"collapse\\" data-target=\\"#top_menu_collapse\\"><span class=\\"navbar-toggler-icon\\"/></button><div class=\\"collapse navbar-collapse\\" id=\\"top_menu_collapse\\" aria-expanded=\\"false\\"><ul class=\\"nav navbar-nav ml-auto text-right\\" id=\\"top_menu\\"><block-child-0/><li class=\\"nav-item divider\\"/><block-child-1/></ul><div><div class=\\"input-group input-group-sm\\"><div class=\\"input-group-prepend input-group-sm\\"><button class=\\"btn btn-default fa fa-cog\\" title=\\"Settings\\" block-handler-2=\\"click\\"/><button class=\\"btn btn-default\\" block-handler-3=\\"click\\"> More </button><block-child-2/></div><input class=\\"form-control\\" type=\\"text\\" placeholder=\\"Search\\" aria-label=\\"Search\\" name=\\"search\\" block-attribute-4=\\"value\\" block-handler-5=\\"keyup\\" block-handler-6=\\"change\\" block-ref=\\"7\\"/><div class=\\"input-group-append\\"><button class=\\"btn btn-default fa fa-eraser\\" block-handler-8=\\"click\\"/></div></div></div></div></nav></header><div class=\\"container-fluid\\" block-ref=\\"9\\"><div class=\\"row\\"><!--div class=\\"form-group col-md-6\\">
|
let block1 = createBlock(\`<div><header><nav class=\\"navbar navbar-expand-md navbar-light bg-light\\"><a block-attribute-0=\\"href\\"><b style=\\"color:#777;\\"><block-text-1/></b></a><button type=\\"button\\" class=\\"navbar-toggler\\" data-toggle=\\"collapse\\" data-target=\\"#top_menu_collapse\\"><span class=\\"navbar-toggler-icon\\"/></button><div class=\\"collapse navbar-collapse\\" id=\\"top_menu_collapse\\" aria-expanded=\\"false\\"><ul class=\\"nav navbar-nav ml-auto text-right\\" id=\\"top_menu\\"><block-child-0/><li class=\\"nav-item divider\\"/><block-child-1/></ul><div><div class=\\"input-group input-group-sm\\"><div class=\\"input-group-prepend input-group-sm\\"><button class=\\"btn btn-default fa fa-cog\\" title=\\"Settings\\" block-handler-2=\\"click\\"/><button class=\\"btn btn-default\\" block-handler-3=\\"click\\"> More </button><block-child-2/></div><input class=\\"form-control\\" type=\\"text\\" placeholder=\\"Search\\" aria-label=\\"Search\\" name=\\"search\\" block-attribute-4=\\"value\\" block-handler-5=\\"keyup\\" block-handler-6=\\"change\\" block-ref=\\"7\\"/><div class=\\"input-group-append\\"><button class=\\"btn btn-default fa fa-eraser\\" block-handler-8=\\"click\\"/></div></div></div></div></nav></header><div class=\\"container-fluid\\" block-ref=\\"9\\"><div class=\\"row\\"><!--div class=\\"form-group col-md-6\\">
|
||||||
<h5>Search options</h5>
|
<h5>Search options</h5>
|
||||||
@@ -223,9 +223,7 @@ exports[`misc other complex template 1`] = `
|
|||||||
for (let i1 = 0; i1 < l_block2; i1++) {
|
for (let i1 = 0; i1 < l_block2; i1++) {
|
||||||
ctx[\`project\`] = v_block2[i1];
|
ctx[\`project\`] = v_block2[i1];
|
||||||
let key1 = ctx['project'].id;
|
let key1 = ctx['project'].id;
|
||||||
const v1 = ctx['selectProject'];
|
let d3 = [ctx['selectProject'](ctx['project']), ctx];
|
||||||
const v2 = ctx['project'];
|
|
||||||
let d3 = [v1(v2), ctx];
|
|
||||||
let d4 = ctx['project'].name;
|
let d4 = ctx['project'].name;
|
||||||
c_block2[i1] = withKey(block3([d3, d4]), key1);
|
c_block2[i1] = withKey(block3([d3, d4]), key1);
|
||||||
}
|
}
|
||||||
@@ -257,10 +255,8 @@ exports[`misc other complex template 1`] = `
|
|||||||
}
|
}
|
||||||
b4 = multi([b5, b6]);
|
b4 = multi([b5, b6]);
|
||||||
}
|
}
|
||||||
const v3 = ctx['toggleSettingsMenu'];
|
let d11 = [ctx['toggleSettingsMenu'], ctx];
|
||||||
let d11 = [v3, ctx];
|
let d12 = [ctx['toggleMore'], ctx];
|
||||||
const v4 = ctx['toggleMore'];
|
|
||||||
let d12 = [v4, ctx];
|
|
||||||
if (ctx['categories']&&ctx['categories'].length>1) {
|
if (ctx['categories']&&ctx['categories'].length>1) {
|
||||||
ctx = Object.create(ctx);
|
ctx = Object.create(ctx);
|
||||||
const [k_block15, v_block15, l_block15, c_block15] = prepareList(ctx['categories']);
|
const [k_block15, v_block15, l_block15, c_block15] = prepareList(ctx['categories']);
|
||||||
@@ -277,13 +273,10 @@ exports[`misc other complex template 1`] = `
|
|||||||
b14 = block14([], [b15]);
|
b14 = block14([], [b15]);
|
||||||
}
|
}
|
||||||
let d16 = ctx['search'].value;
|
let d16 = ctx['search'].value;
|
||||||
const v5 = ctx['updateFilter'];
|
let d17 = [ctx['updateFilter'], ctx];
|
||||||
let d17 = [v5, ctx];
|
let d18 = [ctx['updateFilter'], ctx];
|
||||||
const v6 = ctx['updateFilter'];
|
|
||||||
let d18 = [v6, ctx];
|
|
||||||
let d19 = (el) => refs[\`search_input\`] = el;
|
let d19 = (el) => refs[\`search_input\`] = el;
|
||||||
const v7 = ctx['clearSearch'];
|
let d20 = [ctx['clearSearch'], ctx];
|
||||||
let d20 = [v7, ctx];
|
|
||||||
let d21 = (el) => refs[\`settings_menu\`] = el;
|
let d21 = (el) => refs[\`settings_menu\`] = el;
|
||||||
if (ctx['triggers']) {
|
if (ctx['triggers']) {
|
||||||
ctx = Object.create(ctx);
|
ctx = Object.create(ctx);
|
||||||
@@ -297,8 +290,7 @@ exports[`misc other complex template 1`] = `
|
|||||||
let d23 = \`trigger_\${ctx['trigger'].id}\`;
|
let d23 = \`trigger_\${ctx['trigger'].id}\`;
|
||||||
let d24 = ctx['options'].trigger_display[ctx['trigger'].id];
|
let d24 = ctx['options'].trigger_display[ctx['trigger'].id];
|
||||||
let d25 = ctx['trigger'].id;
|
let d25 = ctx['trigger'].id;
|
||||||
const v8 = ctx['updateTriggerDisplay'];
|
let d26 = [ctx['updateTriggerDisplay'], ctx];
|
||||||
let d26 = [v8, ctx];
|
|
||||||
let d27 = \`trigger_\${ctx['trigger'].id}\`;
|
let d27 = \`trigger_\${ctx['trigger'].id}\`;
|
||||||
let d28 = ctx['trigger'].name;
|
let d28 = ctx['trigger'].name;
|
||||||
b20 = block20([d22, d23, d24, d25, d26, d27, d28]);
|
b20 = block20([d22, d23, d24, d25, d26, d27, d28]);
|
||||||
@@ -307,19 +299,15 @@ exports[`misc other complex template 1`] = `
|
|||||||
}
|
}
|
||||||
ctx = ctx.__proto__;
|
ctx = ctx.__proto__;
|
||||||
let b18 = list(c_block18);
|
let b18 = list(c_block18);
|
||||||
const v9 = ctx['triggerAll'];
|
let d29 = [ctx['triggerAll'], ctx];
|
||||||
let d29 = [v9, ctx];
|
let d30 = [ctx['triggerNone'], ctx];
|
||||||
const v10 = ctx['triggerNone'];
|
let d31 = [ctx['triggerDefault'], ctx];
|
||||||
let d30 = [v10, ctx];
|
let d32 = [ctx['toggleSettingsMenu'], ctx];
|
||||||
const v11 = ctx['triggerDefault'];
|
|
||||||
let d31 = [v11, ctx];
|
|
||||||
const v12 = ctx['toggleSettingsMenu'];
|
|
||||||
let d32 = [v12, ctx];
|
|
||||||
let b21 = block21([d29, d30, d31, d32]);
|
let b21 = block21([d29, d30, d31, d32]);
|
||||||
b17 = multi([b18, b21]);
|
b17 = multi([b18, b21]);
|
||||||
}
|
}
|
||||||
if (ctx['load_infos']) {
|
if (ctx['load_infos']) {
|
||||||
b22 = callTemplate_14.call(this, ctx, node, key + \`__13\`);
|
b22 = callTemplate_2.call(this, ctx, node, key + \`__1\`);
|
||||||
}
|
}
|
||||||
if (ctx['message']) {
|
if (ctx['message']) {
|
||||||
let d33 = ctx['message'];
|
let d33 = ctx['message'];
|
||||||
@@ -328,8 +316,8 @@ exports[`misc other complex template 1`] = `
|
|||||||
if (!ctx['project']) {
|
if (!ctx['project']) {
|
||||||
b24 = block24();
|
b24 = block24();
|
||||||
} else {
|
} else {
|
||||||
let b26 = component(\`BundlesList\`, {bundles: ctx['bundles'].sticky,category_custom_views: ctx['category_custom_views'],search: ctx['search']}, key + \`__15\`, node, ctx);
|
let b26 = component(\`BundlesList\`, {bundles: ctx['bundles'].sticky,category_custom_views: ctx['category_custom_views'],search: ctx['search']}, key + \`__3\`, node, ctx);
|
||||||
let b27 = component(\`BundlesList\`, {bundles: ctx['bundles'].dev,search: ctx['search']}, key + \`__16\`, node, ctx);
|
let b27 = component(\`BundlesList\`, {bundles: ctx['bundles'].dev,search: ctx['search']}, key + \`__4\`, node, ctx);
|
||||||
b25 = block25([], [b26, b27]);
|
b25 = block25([], [b26, b27]);
|
||||||
}
|
}
|
||||||
return block1([d1, d2, d11, d12, d16, d17, d18, d19, d20, d21], [b2, b4, b14, b17, b22, b23, b24, b25]);
|
return block1([d1, d2, d11, d12, d16, d17, d18, d19, d20, d21], [b2, b4, b14, b17, b22, b23, b24, b25]);
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ describe("error handling", () => {
|
|||||||
expect(() => context.addTemplate("test", "<div/>")).toThrow("already defined");
|
expect(() => context.addTemplate("test", "<div/>")).toThrow("already defined");
|
||||||
});
|
});
|
||||||
|
|
||||||
// test("addTemplates throw if parser error", () => {
|
test("addTemplates throw if parser error", () => {
|
||||||
// const context = new TestContext();
|
const context = new TestContext();
|
||||||
// expect(() => {
|
expect(() => {
|
||||||
// context.addTemplates("<templates><abc>></templates>");
|
context.addTemplates("<templates><abc>></templates>");
|
||||||
// }).toThrow("Invalid XML in template");
|
}).toThrow("Invalid XML in template");
|
||||||
// });
|
});
|
||||||
|
|
||||||
test("nice error when t-on is evaluated with a missing event", () => {
|
test("nice error when t-on is evaluated with a missing event", () => {
|
||||||
expect(() => renderToString(`<div t-on="somemethod"></div>`)).toThrow(
|
expect(() => renderToString(`<div t-on="somemethod"></div>`)).toThrow(
|
||||||
|
|||||||
+118
-48
@@ -1069,7 +1069,31 @@ describe("qweb parser", () => {
|
|||||||
|
|
||||||
test("component with event handler", async () => {
|
test("component with event handler", async () => {
|
||||||
expect(() => parse(`<MyComponent t-on-click="someMethod"/>`)).toThrow(
|
expect(() => parse(`<MyComponent t-on-click="someMethod"/>`)).toThrow(
|
||||||
"t-on is no longer supported on Component node. Consider passing a callback in props."
|
"t-on is no longer supported on components. Consider passing a callback in props."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("component with t-ref", async () => {
|
||||||
|
expect(() => parse(`<MyComponent t-ref="something"/>`)).toThrow(
|
||||||
|
"t-ref is no longer supported on components. Consider exposing only the public part of the component's API through a callback prop."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("component with t-att", async () => {
|
||||||
|
expect(() => parse(`<MyComponent t-att="something"/>`)).toThrow(
|
||||||
|
"t-att makes no sense on component: props are already treated as expressions"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("component with t-attf", async () => {
|
||||||
|
expect(() => parse(`<MyComponent t-attf="something"/>`)).toThrow(
|
||||||
|
"t-attf is not supported on components: use template strings for string interpolation in props"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("component with other unsupported directive", async () => {
|
||||||
|
expect(() => parse(`<MyComponent t-something="5"/>`)).toThrow(
|
||||||
|
"unsupported directive on Component: t-something"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1080,7 +1104,22 @@ describe("qweb parser", () => {
|
|||||||
dynamicProps: null,
|
dynamicProps: null,
|
||||||
props: {},
|
props: {},
|
||||||
isDynamic: false,
|
isDynamic: false,
|
||||||
slots: { default: { type: ASTType.Text, value: "foo" } },
|
slots: { default: { content: { type: ASTType.Text, value: "foo" } } },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("a component with a default slot with attributes", async () => {
|
||||||
|
expect(
|
||||||
|
parse(`<MyComponent><t t-set-slot="default" param="param">foo</t></MyComponent>`)
|
||||||
|
).toEqual({
|
||||||
|
type: ASTType.TComponent,
|
||||||
|
name: "MyComponent",
|
||||||
|
dynamicProps: null,
|
||||||
|
props: {},
|
||||||
|
isDynamic: false,
|
||||||
|
slots: {
|
||||||
|
default: { content: { type: ASTType.Text, value: "foo" }, attrs: { param: "param" } },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1093,31 +1132,33 @@ describe("qweb parser", () => {
|
|||||||
props: {},
|
props: {},
|
||||||
slots: {
|
slots: {
|
||||||
default: {
|
default: {
|
||||||
type: ASTType.Multi,
|
content: {
|
||||||
content: [
|
type: ASTType.Multi,
|
||||||
{
|
content: [
|
||||||
type: ASTType.DomNode,
|
{
|
||||||
tag: "span",
|
type: ASTType.DomNode,
|
||||||
dynamicTag: null,
|
tag: "span",
|
||||||
attrs: {},
|
dynamicTag: null,
|
||||||
content: [],
|
attrs: {},
|
||||||
ref: null,
|
content: [],
|
||||||
model: null,
|
ref: null,
|
||||||
on: {},
|
model: null,
|
||||||
ns: null,
|
on: {},
|
||||||
},
|
ns: null,
|
||||||
{
|
},
|
||||||
type: ASTType.DomNode,
|
{
|
||||||
tag: "div",
|
type: ASTType.DomNode,
|
||||||
dynamicTag: null,
|
tag: "div",
|
||||||
attrs: {},
|
dynamicTag: null,
|
||||||
content: [],
|
attrs: {},
|
||||||
ref: null,
|
content: [],
|
||||||
model: null,
|
ref: null,
|
||||||
on: {},
|
model: null,
|
||||||
ns: null,
|
on: {},
|
||||||
},
|
ns: null,
|
||||||
],
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1130,10 +1171,27 @@ describe("qweb parser", () => {
|
|||||||
isDynamic: false,
|
isDynamic: false,
|
||||||
dynamicProps: null,
|
dynamicProps: null,
|
||||||
props: {},
|
props: {},
|
||||||
slots: { name: { type: ASTType.Text, value: "foo" } },
|
slots: { name: { content: { type: ASTType.Text, value: "foo" } } },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("a component with a named slot with attributes", async () => {
|
||||||
|
expect(parse(`<MyComponent><t t-set-slot="name" param="param">foo</t></MyComponent>`)).toEqual({
|
||||||
|
type: ASTType.TComponent,
|
||||||
|
name: "MyComponent",
|
||||||
|
isDynamic: false,
|
||||||
|
dynamicProps: null,
|
||||||
|
props: {},
|
||||||
|
slots: { name: { content: { type: ASTType.Text, value: "foo" }, attrs: { param: "param" } } },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("a component with a named slot with div tag", async () => {
|
||||||
|
expect(() =>
|
||||||
|
parse(`<MyComponent><div t-set-slot="name">foo</div></MyComponent>`)
|
||||||
|
).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
test("a component with a named slot and some white space", async () => {
|
test("a component with a named slot and some white space", async () => {
|
||||||
expect(parse(`<MyComponent><t t-set-slot="name">foo</t> </MyComponent>`)).toEqual({
|
expect(parse(`<MyComponent><t t-set-slot="name">foo</t> </MyComponent>`)).toEqual({
|
||||||
type: ASTType.TComponent,
|
type: ASTType.TComponent,
|
||||||
@@ -1142,8 +1200,8 @@ describe("qweb parser", () => {
|
|||||||
props: {},
|
props: {},
|
||||||
isDynamic: false,
|
isDynamic: false,
|
||||||
slots: {
|
slots: {
|
||||||
default: { type: ASTType.Text, value: " " },
|
default: { content: { type: ASTType.Text, value: " " } },
|
||||||
name: { type: ASTType.Text, value: "foo" },
|
name: { content: { type: ASTType.Text, value: "foo" } },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1161,8 +1219,8 @@ describe("qweb parser", () => {
|
|||||||
props: {},
|
props: {},
|
||||||
isDynamic: false,
|
isDynamic: false,
|
||||||
slots: {
|
slots: {
|
||||||
a: { type: ASTType.Text, value: "foo" },
|
a: { content: { type: ASTType.Text, value: "foo" } },
|
||||||
b: { type: ASTType.Text, value: "bar" },
|
b: { content: { type: ASTType.Text, value: "bar" } },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1201,8 +1259,14 @@ describe("qweb parser", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("component with t-esc", async () => {
|
test("component with t-esc", async () => {
|
||||||
expect(() => parse(`<MyComponent t-esc="someValue"/>`)).toThrow(
|
expect(parse(`<MyComponent t-esc="someValue"/>`)).toEqual(
|
||||||
"t-esc is not supported on Component nodes"
|
parse(`<MyComponent><t t-esc="someValue"/></MyComponent>`)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("component with t-esc and content", async () => {
|
||||||
|
expect(() => parse(`<MyComponent t-esc="someValue">Some content</MyComponent>`)).toThrow(
|
||||||
|
"Cannot have t-esc on a component that already has content"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1213,7 +1277,7 @@ describe("qweb parser", () => {
|
|||||||
dynamicProps: null,
|
dynamicProps: null,
|
||||||
props: {},
|
props: {},
|
||||||
isDynamic: false,
|
isDynamic: false,
|
||||||
slots: { default: { body: null, name: "subTemplate", type: ASTType.TCall } },
|
slots: { default: { content: { body: null, name: "subTemplate", type: ASTType.TCall } } },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1233,12 +1297,14 @@ describe("qweb parser", () => {
|
|||||||
isDynamic: false,
|
isDynamic: false,
|
||||||
slots: {
|
slots: {
|
||||||
default: {
|
default: {
|
||||||
type: ASTType.TComponent,
|
content: {
|
||||||
isDynamic: false,
|
type: ASTType.TComponent,
|
||||||
name: "Child",
|
isDynamic: false,
|
||||||
dynamicProps: null,
|
name: "Child",
|
||||||
props: {},
|
dynamicProps: null,
|
||||||
slots: { brol: { type: ASTType.Text, value: "coucou" } },
|
props: {},
|
||||||
|
slots: { brol: { content: { type: ASTType.Text, value: "coucou" } } },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1248,7 +1314,7 @@ describe("qweb parser", () => {
|
|||||||
const template = `
|
const template = `
|
||||||
<MyComponent>
|
<MyComponent>
|
||||||
<Child>
|
<Child>
|
||||||
<t><t t-set-slot="brol">coucou</t></t>
|
<t t-set-slot="brol">coucou</t>
|
||||||
</Child>
|
</Child>
|
||||||
</MyComponent>
|
</MyComponent>
|
||||||
`;
|
`;
|
||||||
@@ -1260,12 +1326,14 @@ describe("qweb parser", () => {
|
|||||||
isDynamic: false,
|
isDynamic: false,
|
||||||
slots: {
|
slots: {
|
||||||
default: {
|
default: {
|
||||||
type: ASTType.TComponent,
|
content: {
|
||||||
isDynamic: false,
|
type: ASTType.TComponent,
|
||||||
name: "Child",
|
isDynamic: false,
|
||||||
dynamicProps: null,
|
name: "Child",
|
||||||
props: {},
|
dynamicProps: null,
|
||||||
slots: { brol: { type: ASTType.Text, value: "coucou" } },
|
props: {},
|
||||||
|
slots: { brol: { content: { type: ASTType.Text, value: "coucou" } } },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1279,6 +1347,7 @@ describe("qweb parser", () => {
|
|||||||
expect(parse(`<t t-slot="default"/>`)).toEqual({
|
expect(parse(`<t t-slot="default"/>`)).toEqual({
|
||||||
type: ASTType.TSlot,
|
type: ASTType.TSlot,
|
||||||
name: "default",
|
name: "default",
|
||||||
|
attrs: {},
|
||||||
defaultContent: null,
|
defaultContent: null,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1287,6 +1356,7 @@ describe("qweb parser", () => {
|
|||||||
expect(parse(`<t t-slot="header">default content</t>`)).toEqual({
|
expect(parse(`<t t-slot="header">default content</t>`)).toEqual({
|
||||||
type: ASTType.TSlot,
|
type: ASTType.TSlot,
|
||||||
name: "header",
|
name: "header",
|
||||||
|
attrs: {},
|
||||||
defaultContent: { type: ASTType.Text, value: "default content" },
|
defaultContent: { type: ASTType.Text, value: "default content" },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1020,7 +1020,7 @@ exports[`basics update props of component without concrete own node 3`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const tKey_1 = ctx['childProps'].key;
|
const tKey_1 = ctx['childProps'].key;
|
||||||
let b2 = toggler(tKey_1, component(\`Child\`, Object.assign({}, ctx['childProps']), tKey_1 + key + \`__2\`, node, ctx));
|
let b2 = toggler(tKey_1, component(\`Child\`, ctx['childProps'], tKey_1 + key + \`__2\`, node, ctx));
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -1044,6 +1044,30 @@ exports[`destroying/recreating a subwidget with different props (if start is not
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`parent and child rendered at exact same time 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return text(ctx['props'].value);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`parent and child rendered at exact same time 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return component(\`Child\`, {value: ctx['state'].value}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`properly behave when destroyed/unmounted while rendering 1`] = `
|
exports[`properly behave when destroyed/unmounted while rendering 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -1067,7 +1091,7 @@ exports[`properly behave when destroyed/unmounted while rendering 2`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`SubChild\`, {val: ctx['props'].val}, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubChild\`, {}, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -1130,10 +1154,9 @@ exports[`rendering component again in next microtick 2`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2;
|
let b2;
|
||||||
const v1 = ctx['onClick'];
|
let d1 = [ctx['onClick'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
if (ctx['env'].config.flag) {
|
if (ctx['env'].config.flag) {
|
||||||
b2 = component(\`Child\`, {}, key + \`__2\`, node, ctx);
|
b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||||
}
|
}
|
||||||
return block1([d1], [b2]);
|
return block1([d1], [b2]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ exports[`can catch errors can catch an error in a component render function 2`]
|
|||||||
if (ctx['state'].error) {
|
if (ctx['state'].error) {
|
||||||
b2 = text(\`Error handled\`);
|
b2 = text(\`Error handled\`);
|
||||||
} else {
|
} else {
|
||||||
b3 = callSlot(ctx, node, key, 'default');
|
b3 = callSlot(ctx, node, key, 'default', false, {});
|
||||||
}
|
}
|
||||||
return block1([], [b2, b3]);
|
return block1([], [b2, b3]);
|
||||||
}
|
}
|
||||||
@@ -117,16 +117,15 @@ exports[`can catch errors can catch an error in a component render function 3`]
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`ErrorComponent\`, {flag: ctx['state'].flag}, key + \`__3\`, node, ctx);
|
return component(\`ErrorComponent\`, {flag: ctx['state'].flag}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -159,7 +158,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon
|
|||||||
if (ctx['state'].error) {
|
if (ctx['state'].error) {
|
||||||
b2 = text(\`Error handled\`);
|
b2 = text(\`Error handled\`);
|
||||||
} else {
|
} else {
|
||||||
b3 = callSlot(ctx, node, key, 'default');
|
b3 = callSlot(ctx, node, key, 'default', false, {});
|
||||||
}
|
}
|
||||||
return block1([], [b2, b3]);
|
return block1([], [b2, b3]);
|
||||||
}
|
}
|
||||||
@@ -207,7 +206,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon
|
|||||||
if (ctx['state'].error) {
|
if (ctx['state'].error) {
|
||||||
b2 = text(\`Error handled\`);
|
b2 = text(\`Error handled\`);
|
||||||
} else {
|
} else {
|
||||||
b3 = callSlot(ctx, node, key, 'default');
|
b3 = callSlot(ctx, node, key, 'default', false, {});
|
||||||
}
|
}
|
||||||
return block1([], [b2, b3]);
|
return block1([], [b2, b3]);
|
||||||
}
|
}
|
||||||
@@ -219,18 +218,17 @@ exports[`can catch errors can catch an error in the constructor call of a compon
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
let b3 = component(\`ClassicCompoent\`, {}, key + \`__3\`, node, ctx);
|
let b3 = component(\`ClassicCompoent\`, {}, key + \`__2\`, node, ctx);
|
||||||
let b4 = component(\`ErrorComponent\`, {}, key + \`__4\`, node, ctx);
|
let b4 = component(\`ErrorComponent\`, {}, key + \`__3\`, node, ctx);
|
||||||
return multi([b3, b4]);
|
return multi([b3, b4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b5 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b5 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__4\`, node, ctx);
|
||||||
return block1([], [b5]);
|
return block1([], [b5]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -241,16 +239,15 @@ exports[`can catch errors can catch an error in the constructor call of a compon
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`ErrorComponent\`, {}, key + \`__3\`, node, ctx);
|
return component(\`ErrorComponent\`, {}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -284,7 +281,7 @@ exports[`can catch errors can catch an error in the initial call of a component
|
|||||||
if (ctx['state'].error) {
|
if (ctx['state'].error) {
|
||||||
b2 = text(\`Error handled\`);
|
b2 = text(\`Error handled\`);
|
||||||
} else {
|
} else {
|
||||||
b3 = callSlot(ctx, node, key, 'default');
|
b3 = callSlot(ctx, node, key, 'default', false, {});
|
||||||
}
|
}
|
||||||
return block1([], [b2, b3]);
|
return block1([], [b2, b3]);
|
||||||
}
|
}
|
||||||
@@ -296,16 +293,15 @@ exports[`can catch errors can catch an error in the initial call of a component
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`ErrorComponent\`, {}, key + \`__3\`, node, ctx);
|
return component(\`ErrorComponent\`, {}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -339,7 +335,7 @@ exports[`can catch errors can catch an error in the initial call of a component
|
|||||||
if (ctx['state'].error) {
|
if (ctx['state'].error) {
|
||||||
b2 = text(\`Error handled\`);
|
b2 = text(\`Error handled\`);
|
||||||
} else {
|
} else {
|
||||||
b3 = callSlot(ctx, node, key, 'default');
|
b3 = callSlot(ctx, node, key, 'default', false, {});
|
||||||
}
|
}
|
||||||
return block1([], [b2, b3]);
|
return block1([], [b2, b3]);
|
||||||
}
|
}
|
||||||
@@ -351,18 +347,17 @@ exports[`can catch errors can catch an error in the initial call of a component
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`ErrorComponent\`, {}, key + \`__3\`, node, ctx);
|
return component(\`ErrorComponent\`, {}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3;
|
let b3;
|
||||||
if (ctx['state'].flag) {
|
if (ctx['state'].flag) {
|
||||||
b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
}
|
}
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
@@ -396,7 +391,7 @@ exports[`can catch errors can catch an error in the mounted call 2`] = `
|
|||||||
if (ctx['state'].error) {
|
if (ctx['state'].error) {
|
||||||
b2 = text(\`Error handled\`);
|
b2 = text(\`Error handled\`);
|
||||||
} else {
|
} else {
|
||||||
b3 = callSlot(ctx, node, key, 'default');
|
b3 = callSlot(ctx, node, key, 'default', false, {});
|
||||||
}
|
}
|
||||||
return block1([], [b2, b3]);
|
return block1([], [b2, b3]);
|
||||||
}
|
}
|
||||||
@@ -408,16 +403,15 @@ exports[`can catch errors can catch an error in the mounted call 3`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`ErrorComponent\`, {}, key + \`__3\`, node, ctx);
|
return component(\`ErrorComponent\`, {}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -451,7 +445,7 @@ exports[`can catch errors can catch an error in the willPatch call 2`] = `
|
|||||||
if (ctx['state'].error) {
|
if (ctx['state'].error) {
|
||||||
b2 = text(\`Error handled\`);
|
b2 = text(\`Error handled\`);
|
||||||
} else {
|
} else {
|
||||||
b3 = callSlot(ctx, node, key, 'default');
|
b3 = callSlot(ctx, node, key, 'default', false, {});
|
||||||
}
|
}
|
||||||
return block1([], [b2, b3]);
|
return block1([], [b2, b3]);
|
||||||
}
|
}
|
||||||
@@ -463,17 +457,16 @@ exports[`can catch errors can catch an error in the willPatch call 3`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><span><block-text-0/></span><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><span><block-text-0/></span><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`ErrorComponent\`, {message: ctx['state'].message}, key + \`__3\`, node, ctx);
|
return component(\`ErrorComponent\`, {message: ctx['state'].message}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let d1 = ctx['state'].message;
|
let d1 = ctx['state'].message;
|
||||||
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([d1], [b3]);
|
return block1([d1], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -506,7 +499,7 @@ exports[`can catch errors can catch an error in the willStart call 2`] = `
|
|||||||
if (ctx['state'].error) {
|
if (ctx['state'].error) {
|
||||||
b2 = text(\`Error handled\`);
|
b2 = text(\`Error handled\`);
|
||||||
} else {
|
} else {
|
||||||
b3 = callSlot(ctx, node, key, 'default');
|
b3 = callSlot(ctx, node, key, 'default', false, {});
|
||||||
}
|
}
|
||||||
return block1([], [b2, b3]);
|
return block1([], [b2, b3]);
|
||||||
}
|
}
|
||||||
@@ -518,16 +511,15 @@ exports[`can catch errors can catch an error in the willStart call 3`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`ErrorComponent\`, {}, key + \`__3\`, node, ctx);
|
return component(\`ErrorComponent\`, {}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -574,7 +566,7 @@ exports[`can catch errors can catch an error origination from a child's willStar
|
|||||||
if (ctx['state'].error) {
|
if (ctx['state'].error) {
|
||||||
b2 = text(\`Error handled\`);
|
b2 = text(\`Error handled\`);
|
||||||
} else {
|
} else {
|
||||||
b3 = callSlot(ctx, node, key, 'default');
|
b3 = callSlot(ctx, node, key, 'default', false, {});
|
||||||
}
|
}
|
||||||
return block1([], [b2, b3]);
|
return block1([], [b2, b3]);
|
||||||
}
|
}
|
||||||
@@ -586,18 +578,17 @@ exports[`can catch errors can catch an error origination from a child's willStar
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
let b3 = component(\`ClassicCompoent\`, {}, key + \`__3\`, node, ctx);
|
let b3 = component(\`ClassicCompoent\`, {}, key + \`__2\`, node, ctx);
|
||||||
let b4 = component(\`ErrorComponent\`, {}, key + \`__4\`, node, ctx);
|
let b4 = component(\`ErrorComponent\`, {}, key + \`__3\`, node, ctx);
|
||||||
return multi([b3, b4]);
|
return multi([b3, b4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b5 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b5 = component(\`ErrorBoundary\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__4\`, node, ctx);
|
||||||
return block1([], [b5]);
|
return block1([], [b5]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -23,15 +23,40 @@ exports[`event handling handler receive the event as argument 2`] = `
|
|||||||
let block1 = createBlock(\`<span block-handler-0=\\"click\\"><block-child-0/><block-text-1/></span>\`);
|
let block1 = createBlock(\`<span block-handler-0=\\"click\\"><block-child-0/><block-text-1/></span>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['inc'];
|
let d1 = [ctx['inc'], ctx];
|
||||||
let d1 = [v1, ctx];
|
let b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||||
let b2 = component(\`Child\`, {}, key + \`__2\`, node, ctx);
|
|
||||||
let d2 = ctx['state'].value;
|
let d2 = ctx['state'].value;
|
||||||
return block1([d1, d2], [b2]);
|
return block1([d1, d2], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`event handling objects from scope are properly captured by t-on 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
let block3 = createBlock(\`<div class=\\"item\\" block-handler-0=\\"click\\"/>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['items']);
|
||||||
|
for (let i1 = 0; i1 < l_block2; i1++) {
|
||||||
|
ctx[\`item\`] = v_block2[i1];
|
||||||
|
let key1 = ctx['item'];
|
||||||
|
const v1 = ctx['onClick'];
|
||||||
|
const v2 = ctx['item'];
|
||||||
|
let d1 = [ev=>v1(v2.val,ev), ctx];
|
||||||
|
c_block2[i1] = withKey(block3([d1]), key1);
|
||||||
|
}
|
||||||
|
let b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`event handling support for callable expression in event handler 1`] = `
|
exports[`event handling support for callable expression in event handler 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -42,8 +67,7 @@ exports[`event handling support for callable expression in event handler 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let d1 = ctx['state'].value;
|
let d1 = ctx['state'].value;
|
||||||
const v1 = ctx['obj'];
|
let d2 = [ctx['obj'].onInput, ctx];
|
||||||
let d2 = [v1.onInput, ctx];
|
|
||||||
return block1([d1, d2]);
|
return block1([d1, d2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -130,8 +130,7 @@ exports[`basics sub widget is interactive 1`] = `
|
|||||||
let block1 = createBlock(\`<span><button block-handler-0=\\"click\\">click</button>child<block-text-1/></span>\`);
|
let block1 = createBlock(\`<span><button block-handler-0=\\"click\\">click</button>child<block-text-1/></span>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['inc'];
|
let d1 = [ctx['inc'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
let d2 = ctx['state'].val;
|
let d2 = ctx['state'].val;
|
||||||
return block1([d1, d2]);
|
return block1([d1, d2]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -540,8 +540,7 @@ exports[`lifecycle hooks onWillRender 1`] = `
|
|||||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\"><block-text-1/></button>\`);
|
let block1 = createBlock(\`<button block-handler-0=\\"click\\"><block-text-1/></button>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['increment'];
|
let d1 = [ctx['increment'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
let d2 = ctx['state'].value;
|
let d2 = ctx['state'].value;
|
||||||
return block1([d1, d2]);
|
return block1([d1, d2]);
|
||||||
}
|
}
|
||||||
@@ -555,7 +554,7 @@ exports[`lifecycle hooks onWillRender 2`] = `
|
|||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
return component(\`Child\`, {someValue: ctx['state'].value}, key + \`__1\`, node, ctx);
|
return component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -30,6 +30,42 @@ exports[`basics accept ES6-like syntax for props (with getters) 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`basics arrow functions as prop correctly capture their scope 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<button block-handler-0=\\"click\\"/>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let d1 = [ctx['props'].onClick, ctx];
|
||||||
|
return block1([d1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`basics arrow functions as prop correctly capture their scope 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const [k_block1, v_block1, l_block1, c_block1] = prepareList(ctx['items']);
|
||||||
|
for (let i1 = 0; i1 < l_block1; i1++) {
|
||||||
|
ctx[\`item\`] = v_block1[i1];
|
||||||
|
let key1 = ctx['item'].val;
|
||||||
|
const v1 = ctx['onClick'];
|
||||||
|
const v2 = ctx['item'];
|
||||||
|
c_block1[i1] = withKey(component(\`Child\`, {onClick: ev=>v1(v2.val,ev)}, key + \`__3__\${key1}\`, node, ctx), key1);
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`basics explicit object prop 1`] = `
|
exports[`basics explicit object prop 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -60,6 +96,60 @@ exports[`basics explicit object prop 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`basics prop names can contain - 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let d1 = ctx['props']['prop-name'];
|
||||||
|
return block1([d1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`basics prop names can contain - 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return component(\`Child\`, {'prop-name': 7}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`basics support prop names that aren't valid bare object property names 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<button block-handler-0=\\"click\\"/>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let d1 = [ctx['props'].onClick, ctx];
|
||||||
|
return block1([d1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`basics support prop names that aren't valid bare object property names 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return component(\`Child\`, {'some-dashed-prop': 5,'a.b': 'keyword prop'}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`basics t-set with a body expression can be passed in props, and then t-out 1`] = `
|
exports[`basics t-set with a body expression can be passed in props, and then t-out 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -161,3 +251,27 @@ exports[`basics t-set works 2`] = `
|
|||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`basics template string in prop 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return text(\`\`);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`basics template string in prop 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return component(\`Child\`, {propName: \`1\${ctx['someVal']}3\`}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ exports[`default props can set default required boolean values 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {}
|
const props1 = {}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -26,9 +26,9 @@ exports[`default props can set default values 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {}
|
const props1 = {}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -43,9 +43,9 @@ exports[`default props default values are also set whenever component is updated
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['state'].p}
|
const props1 = {p: ctx['state'].p}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -60,9 +60,9 @@ exports[`props validation can validate a prop with multiple types 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -77,9 +77,9 @@ exports[`props validation can validate a prop with multiple types 2`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -94,9 +94,9 @@ exports[`props validation can validate an array with given primitive type 1`] =
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -111,9 +111,9 @@ exports[`props validation can validate an array with given primitive type 2`] =
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -128,9 +128,9 @@ exports[`props validation can validate an array with multiple sub element types
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -145,9 +145,9 @@ exports[`props validation can validate an array with multiple sub element types
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -162,9 +162,9 @@ exports[`props validation can validate an array with multiple sub element types
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -179,9 +179,9 @@ exports[`props validation can validate an object with simple shape 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -196,9 +196,9 @@ exports[`props validation can validate an optional props 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -213,9 +213,9 @@ exports[`props validation can validate an optional props 2`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -230,9 +230,9 @@ exports[`props validation can validate recursively complicated prop def 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -247,9 +247,9 @@ exports[`props validation can validate recursively complicated prop def 2`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -264,9 +264,9 @@ exports[`props validation default values are applied before validating props at
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['state'].p}
|
const props1 = {p: ctx['state'].p}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -281,9 +281,9 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {message: 1}
|
const props1 = {message: 1}
|
||||||
helpers.validateProps(\`Child\`, props2, ctx)
|
helpers.validateProps(\`Child\`, props1, ctx)
|
||||||
let b2 = component(\`Child\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`Child\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -298,9 +298,9 @@ exports[`props validation props are validated whenever component is updated 1`]
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['state'].p}
|
const props1 = {p: ctx['state'].p}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -315,9 +315,9 @@ exports[`props validation validate simple types 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -332,9 +332,9 @@ exports[`props validation validate simple types 2`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -349,9 +349,9 @@ exports[`props validation validate simple types 3`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -366,9 +366,9 @@ exports[`props validation validate simple types 4`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -383,9 +383,9 @@ exports[`props validation validate simple types 5`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -400,9 +400,9 @@ exports[`props validation validate simple types 6`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -417,9 +417,9 @@ exports[`props validation validate simple types, alternate form 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -434,9 +434,9 @@ exports[`props validation validate simple types, alternate form 2`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -451,9 +451,9 @@ exports[`props validation validate simple types, alternate form 3`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -468,9 +468,9 @@ exports[`props validation validate simple types, alternate form 4`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -485,9 +485,9 @@ exports[`props validation validate simple types, alternate form 5`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -502,9 +502,9 @@ exports[`props validation validate simple types, alternate form 6`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props2 = {p: ctx['p']}
|
const props1 = {p: ctx['p']}
|
||||||
helpers.validateProps(\`SubComp\`, props2, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx)
|
||||||
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ exports[`refs refs are properly bound in slots 1`] = `
|
|||||||
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = callSlot(ctx, node, key, 'footer');
|
let b2 = callSlot(ctx, node, key, 'footer', false, {});
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -36,15 +36,13 @@ exports[`refs refs are properly bound in slots 2`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><span class=\\"counter\\"><block-text-0/></span><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><span class=\\"counter\\"><block-text-0/></span><block-child-0/></div>\`);
|
||||||
let block2 = createBlock(\`<button block-handler-0=\\"click\\" block-ref=\\"1\\">do something</button>\`);
|
let block2 = createBlock(\`<button block-handler-0=\\"click\\" block-ref=\\"1\\">do something</button>\`);
|
||||||
|
|
||||||
const slot3 = ctx => (node, key) => {
|
function slot2(ctx, node, key) {
|
||||||
const refs = ctx.__owl__.refs
|
const refs = ctx.__owl__.refs
|
||||||
const v4 = ctx['doSomething'];
|
let d2 = [ctx['doSomething'], ctx];
|
||||||
let d2 = [v4, ctx];
|
|
||||||
let d3 = (el) => refs[\`myButton\`] = el;
|
let d3 = (el) => refs[\`myButton\`] = el;
|
||||||
return block2([d2, d3]);
|
return block2([d2, d3]);
|
||||||
}
|
}
|
||||||
@@ -52,8 +50,8 @@ exports[`refs refs are properly bound in slots 2`] = `
|
|||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const refs = ctx.__owl__.refs;
|
const refs = ctx.__owl__.refs;
|
||||||
let d1 = ctx['state'].val;
|
let d1 = ctx['state'].val;
|
||||||
const ctx2 = capture(ctx);
|
const ctx1 = capture(ctx);
|
||||||
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'footer': slot3(ctx2)}});
|
let b3 = component(\`Dialog\`, {slots: {'footer': {__render: slot2, __ctx: ctx1}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([d1], [b3]);
|
return block1([d1], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`rendering semantics blabla 1`] = `
|
|
||||||
"function anonymous(bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return text(ctx['props'].a.b);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`rendering semantics blabla 2`] = `
|
|
||||||
"function anonymous(bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return component(\`Child\`, {a: ctx['state']}, key + \`__1\`, node, ctx);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`rendering semantics can force a render to update sub tree 1`] = `
|
|
||||||
"function anonymous(bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return text(\`child\`);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`rendering semantics can force a render to update sub tree 2`] = `
|
|
||||||
"function anonymous(bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let b2 = text(ctx['state'].value);
|
|
||||||
let b3 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
|
||||||
return multi([b2, b3]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`rendering semantics can render a parent without rendering child 1`] = `
|
|
||||||
"function anonymous(bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return text(\`child\`);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`rendering semantics can render a parent without rendering child 2`] = `
|
|
||||||
"function anonymous(bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let b2 = text(ctx['state'].value);
|
|
||||||
let b3 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
|
||||||
return multi([b2, b3]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -52,8 +52,7 @@ exports[`t-call handlers are properly bound through a t-call 1`] = `
|
|||||||
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
|
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['update'];
|
let d1 = [ctx['update'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
return block1([d1]);
|
return block1([d1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -39,6 +39,61 @@ exports[`list of components components in a node in a t-foreach 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`list of components crash on duplicate key in dev mode 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const [k_block1, v_block1, l_block1, c_block1] = prepareList([1,2]);
|
||||||
|
const keys1 = new Set();
|
||||||
|
for (let i1 = 0; i1 < l_block1; i1++) {
|
||||||
|
ctx[\`item\`] = v_block1[i1];
|
||||||
|
let key1 = 'child';
|
||||||
|
if (keys1.has(key1)) { throw new Error(\`Got duplicate key in t-foreach: \${key1}\`)}
|
||||||
|
keys1.add(key1);
|
||||||
|
const props1 = {}
|
||||||
|
helpers.validateProps(\`Child\`, props1, ctx)
|
||||||
|
c_block1[i1] = withKey(component(\`Child\`, props1, key + \`__2__\${key1}\`, node, ctx), key1);
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components crash on duplicate key in dev mode 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return text(\`\`);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components crash on duplicate key in dev mode 3`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const [k_block1, v_block1, l_block1, c_block1] = prepareList([1,2]);
|
||||||
|
for (let i1 = 0; i1 < l_block1; i1++) {
|
||||||
|
ctx[\`item\`] = v_block1[i1];
|
||||||
|
let key1 = 'child';
|
||||||
|
c_block1[i1] = withKey(component(\`Child\`, {}, key + \`__1__\${key1}\`, node, ctx), key1);
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`list of components list of sub components inside other nodes 1`] = `
|
exports[`list of components list of sub components inside other nodes 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -117,11 +117,10 @@ exports[`t-model directive can also define t-on directive on same event, part 1
|
|||||||
let block1 = createBlock(\`<div><input block-handler-0=\\"input\\" block-attribute-1=\\"value\\" block-handler-2=\\"input\\"/></div>\`);
|
let block1 = createBlock(\`<div><input block-handler-0=\\"input\\" block-attribute-1=\\"value\\" block-handler-2=\\"input\\"/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['onInput'];
|
let d1 = [ctx['onInput'], ctx];
|
||||||
let d1 = [v1, ctx];
|
const bExpr1 = ctx['state'];
|
||||||
const bExpr2 = ctx['state'];
|
|
||||||
let d2 = ctx['state']['text'];
|
let d2 = ctx['state']['text'];
|
||||||
let d3 = [(ev) => { bExpr2['text'] = ev.target.value; }];
|
let d3 = [(ev) => { bExpr1['text'] = ev.target.value; }];
|
||||||
return block1([d1, d2, d3]);
|
return block1([d1, d2, d3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -136,21 +135,18 @@ exports[`t-model directive can also define t-on directive on same event, part 2
|
|||||||
let block1 = createBlock(\`<div><input type=\\"radio\\" id=\\"one\\" value=\\"One\\" block-handler-0=\\"click\\" block-attribute-1=\\"checked\\" block-handler-2=\\"click\\"/><input type=\\"radio\\" id=\\"two\\" value=\\"Two\\" block-handler-3=\\"click\\" block-attribute-4=\\"checked\\" block-handler-5=\\"click\\"/><input type=\\"radio\\" id=\\"three\\" value=\\"Three\\" block-handler-6=\\"click\\" block-attribute-7=\\"checked\\" block-handler-8=\\"click\\"/></div>\`);
|
let block1 = createBlock(\`<div><input type=\\"radio\\" id=\\"one\\" value=\\"One\\" block-handler-0=\\"click\\" block-attribute-1=\\"checked\\" block-handler-2=\\"click\\"/><input type=\\"radio\\" id=\\"two\\" value=\\"Two\\" block-handler-3=\\"click\\" block-attribute-4=\\"checked\\" block-handler-5=\\"click\\"/><input type=\\"radio\\" id=\\"three\\" value=\\"Three\\" block-handler-6=\\"click\\" block-attribute-7=\\"checked\\" block-handler-8=\\"click\\"/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['onClick'];
|
let d1 = [ctx['onClick'], ctx];
|
||||||
let d1 = [v1, ctx];
|
const bExpr1 = ctx['state'];
|
||||||
const bExpr2 = ctx['state'];
|
|
||||||
let d2 = ctx['state']['choice'] === 'One';
|
let d2 = ctx['state']['choice'] === 'One';
|
||||||
let d3 = [(ev) => { bExpr2['choice'] = ev.target.value; }];
|
let d3 = [(ev) => { bExpr1['choice'] = ev.target.value; }];
|
||||||
const v3 = ctx['onClick'];
|
let d4 = [ctx['onClick'], ctx];
|
||||||
let d4 = [v3, ctx];
|
const bExpr2 = ctx['state'];
|
||||||
const bExpr4 = ctx['state'];
|
|
||||||
let d5 = ctx['state']['choice'] === 'Two';
|
let d5 = ctx['state']['choice'] === 'Two';
|
||||||
let d6 = [(ev) => { bExpr4['choice'] = ev.target.value; }];
|
let d6 = [(ev) => { bExpr2['choice'] = ev.target.value; }];
|
||||||
const v5 = ctx['onClick'];
|
let d7 = [ctx['onClick'], ctx];
|
||||||
let d7 = [v5, ctx];
|
const bExpr3 = ctx['state'];
|
||||||
const bExpr6 = ctx['state'];
|
|
||||||
let d8 = ctx['state']['choice'] === 'Three';
|
let d8 = ctx['state']['choice'] === 'Three';
|
||||||
let d9 = [(ev) => { bExpr6['choice'] = ev.target.value; }];
|
let d9 = [(ev) => { bExpr3['choice'] = ev.target.value; }];
|
||||||
return block1([d1, d2, d3, d4, d5, d6, d7, d8, d9]);
|
return block1([d1, d2, d3, d4, d5, d6, d7, d8, d9]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -130,8 +130,7 @@ exports[`t-on t-on on destroyed components 1`] = `
|
|||||||
let block1 = createBlock(\`<div block-handler-0=\\"click\\"/>\`);
|
let block1 = createBlock(\`<div block-handler-0=\\"click\\"/>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['onClick'];
|
let d1 = [ctx['onClick'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
return block1([d1]);
|
return block1([d1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ exports[`t-props basic use 2`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`Child\`, Object.assign({}, ctx['some'].obj), key + \`__1\`, node, ctx);
|
let b2 = component(\`Child\`, ctx['some'].obj, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -85,7 +85,7 @@ exports[`t-props t-props only 2`] = `
|
|||||||
let block1 = createBlock(\`<div><div><block-child-0/></div></div>\`);
|
let block1 = createBlock(\`<div><div><block-child-0/></div></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`Comp\`, Object.assign({}, ctx['state']), key + \`__1\`, node, ctx);
|
let b2 = component(\`Comp\`, ctx['state'], key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -24,11 +24,10 @@ exports[`t-set slot setted value (with t-set) not accessible with t-esc 2`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><p><block-text-0/></p><block-child-0/><p><block-text-1/></p></div>\`);
|
let block1 = createBlock(\`<div><p><block-text-0/></p><block-child-0/><p><block-text-1/></p></div>\`);
|
||||||
|
|
||||||
const slot3 = ctx => (node, key) => {
|
function slot2(ctx, node, key) {
|
||||||
setContextValue(ctx, \\"iter\\", 'inCall');
|
setContextValue(ctx, \\"iter\\", 'inCall');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,8 +36,8 @@ exports[`t-set slot setted value (with t-set) not accessible with t-esc 2`] = `
|
|||||||
ctx[isBoundary] = 1
|
ctx[isBoundary] = 1
|
||||||
setContextValue(ctx, \\"iter\\", 'source');
|
setContextValue(ctx, \\"iter\\", 'source');
|
||||||
let d1 = ctx['iter'];
|
let d1 = ctx['iter'];
|
||||||
const ctx2 = capture(ctx);
|
const ctx1 = capture(ctx);
|
||||||
let b2 = assign(component(\`Childcomp\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
|
let b2 = component(\`Childcomp\`, {slots: {'default': {__render: slot2, __ctx: ctx1}}}, key + \`__3\`, node, ctx);
|
||||||
let d2 = ctx['iter'];
|
let d2 = ctx['iter'];
|
||||||
return block1([d1, d2], [b2]);
|
return block1([d1, d2], [b2]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ describe("basics", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><span></span></div>");
|
expect(fixture.innerHTML).toBe("<div><span></span></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
test.only("child can be updated", async () => {
|
test("child can be updated", async () => {
|
||||||
class Child extends Component {
|
class Child extends Component {
|
||||||
static template = xml`<t t-esc="props.value"/>`;
|
static template = xml`<t t-esc="props.value"/>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -543,7 +543,7 @@ test("properly behave when destroyed/unmounted while rendering ", async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Child extends Component {
|
class Child extends Component {
|
||||||
static template = xml`<div><SubChild val="props.val"/></div>`;
|
static template = xml`<div><SubChild /></div>`;
|
||||||
static components = { SubChild };
|
static components = { SubChild };
|
||||||
setup() {
|
setup() {
|
||||||
useLogLifecycle(steps);
|
useLogLifecycle(steps);
|
||||||
@@ -1900,13 +1900,18 @@ test("concurrent renderings scenario 13", async () => {
|
|||||||
"Child:willPatch",
|
"Child:willPatch",
|
||||||
"Child:patched",
|
"Child:patched",
|
||||||
"Parent:willRender",
|
"Parent:willRender",
|
||||||
|
"Child:willUpdateProps",
|
||||||
"Child:setup",
|
"Child:setup",
|
||||||
"Child:willStart",
|
"Child:willStart",
|
||||||
"Parent:rendered",
|
"Parent:rendered",
|
||||||
"Child:willRender",
|
"Child:willRender",
|
||||||
"Child:rendered",
|
"Child:rendered",
|
||||||
|
"Child:willRender",
|
||||||
|
"Child:rendered",
|
||||||
"Parent:willPatch",
|
"Parent:willPatch",
|
||||||
|
"Child:willPatch",
|
||||||
"Child:mounted",
|
"Child:mounted",
|
||||||
|
"Child:patched",
|
||||||
"Parent:patched",
|
"Parent:patched",
|
||||||
"Child:willRender",
|
"Child:willRender",
|
||||||
"Child:rendered",
|
"Child:rendered",
|
||||||
@@ -2557,6 +2562,59 @@ test("two renderings initiated between willPatch and patched", async () => {
|
|||||||
Object.freeze(steps);
|
Object.freeze(steps);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("parent and child rendered at exact same time", async () => {
|
||||||
|
let child: any = null;
|
||||||
|
let steps: any[] = [];
|
||||||
|
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<t t-esc="props.value"/>`;
|
||||||
|
setup() {
|
||||||
|
child = this;
|
||||||
|
useLogLifecycle(steps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`<Child value="state.value"/>`;
|
||||||
|
static components = { Child };
|
||||||
|
state = { value: 0 };
|
||||||
|
setup() {
|
||||||
|
useLogLifecycle(steps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = await mount(Parent, fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("0");
|
||||||
|
|
||||||
|
parent.state.value = 1;
|
||||||
|
parent.render();
|
||||||
|
child.render();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("1");
|
||||||
|
expect(steps).toEqual([
|
||||||
|
"Parent:setup",
|
||||||
|
"Parent:willStart",
|
||||||
|
"Parent:willRender",
|
||||||
|
"Child:setup",
|
||||||
|
"Child:willStart",
|
||||||
|
"Parent:rendered",
|
||||||
|
"Child:willRender",
|
||||||
|
"Child:rendered",
|
||||||
|
"Child:mounted",
|
||||||
|
"Parent:mounted",
|
||||||
|
"Parent:willRender",
|
||||||
|
"Child:willUpdateProps",
|
||||||
|
"Parent:rendered",
|
||||||
|
"Child:willRender",
|
||||||
|
"Child:rendered",
|
||||||
|
"Parent:willPatch",
|
||||||
|
"Child:willPatch",
|
||||||
|
"Child:patched",
|
||||||
|
"Parent:patched",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
// test.skip("components with shouldUpdate=false", async () => {
|
// test.skip("components with shouldUpdate=false", async () => {
|
||||||
// const state = { p: 1, cc: 10 };
|
// const state = { p: 1, cc: 10 };
|
||||||
|
|
||||||
|
|||||||
@@ -72,4 +72,25 @@ describe("event handling", () => {
|
|||||||
expect(onClickArgs![0]).toBe(1);
|
expect(onClickArgs![0]).toBe(1);
|
||||||
expect(onClickArgs![1]).toBeInstanceOf(MouseEvent);
|
expect(onClickArgs![1]).toBeInstanceOf(MouseEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("objects from scope are properly captured by t-on", async () => {
|
||||||
|
let onClickArgs: [number, MouseEvent] | null = null;
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-foreach="items" t-as="item" t-key="item">
|
||||||
|
<div class="item" t-on-click="ev => onClick(item.val, ev)"/>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
items = [{ val: 1 }, { val: 2 }, { val: 3 }, { val: 4 }];
|
||||||
|
onClick(n: number, ev: MouseEvent) {
|
||||||
|
onClickArgs = [n, ev];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
expect(onClickArgs).toBeNull();
|
||||||
|
(<HTMLElement>fixture.querySelector(".item")).click();
|
||||||
|
expect(onClickArgs![0]).toBe(1);
|
||||||
|
expect(onClickArgs![1]).toBeInstanceOf(MouseEvent);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -899,9 +899,8 @@ describe("lifecycle hooks", () => {
|
|||||||
|
|
||||||
class Parent extends Component {
|
class Parent extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
<Child someValue="state.value" />`;
|
<Child />`;
|
||||||
static components = { Child };
|
static components = { Child };
|
||||||
state = useState({ value: 1 });
|
|
||||||
setup() {
|
setup() {
|
||||||
useLogLifecycle(steps);
|
useLogLifecycle(steps);
|
||||||
}
|
}
|
||||||
@@ -911,7 +910,7 @@ describe("lifecycle hooks", () => {
|
|||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<button>1</button>");
|
expect(fixture.innerHTML).toBe("<button>1</button>");
|
||||||
|
|
||||||
parent.state.value++; // to block child render
|
parent.render(); // to block child render
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
fixture.querySelector("button")!.click();
|
fixture.querySelector("button")!.click();
|
||||||
@@ -1064,18 +1063,22 @@ describe("lifecycle hooks", () => {
|
|||||||
steps.splice(0);
|
steps.splice(0);
|
||||||
c!.state.flag = false;
|
c!.state.flag = false;
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe(`<div>A<div>B</div><div>C<div>D</div><div>F</div></div></div>`);
|
|
||||||
expect(steps).toEqual([
|
expect(steps).toEqual([
|
||||||
"C:willRender",
|
"C:willRender",
|
||||||
|
"D:willUpdateProps",
|
||||||
"F:setup",
|
"F:setup",
|
||||||
"F:willStart",
|
"F:willStart",
|
||||||
"C:rendered",
|
"C:rendered",
|
||||||
|
"D:willRender",
|
||||||
|
"D:rendered",
|
||||||
"F:willRender",
|
"F:willRender",
|
||||||
"F:rendered",
|
"F:rendered",
|
||||||
"C:willPatch",
|
"C:willPatch",
|
||||||
|
"D:willPatch",
|
||||||
"E:willUnmount",
|
"E:willUnmount",
|
||||||
"E:destroyed",
|
"E:destroyed",
|
||||||
"F:mounted",
|
"F:mounted",
|
||||||
|
"D:patched",
|
||||||
"C:patched",
|
"C:patched",
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,6 +30,20 @@ describe("basics", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><span>42</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>42</span></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("prop names can contain -", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<div><t t-esc="props['prop-name']"/></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`<Child prop-name="7"/>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div>7</div>");
|
||||||
|
});
|
||||||
|
|
||||||
test("accept ES6-like syntax for props (with getters)", async () => {
|
test("accept ES6-like syntax for props (with getters)", async () => {
|
||||||
class Child extends Component {
|
class Child extends Component {
|
||||||
static template = xml`<span><t t-esc="props.greetings"/></span>`;
|
static template = xml`<span><t t-esc="props.greetings"/></span>`;
|
||||||
@@ -102,4 +116,63 @@ describe("basics", () => {
|
|||||||
await mount(Parent, fixture);
|
await mount(Parent, fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><span><p>43</p><p>43</p></span></div>");
|
expect(fixture.innerHTML).toBe("<div><span><p>43</p><p>43</p></span></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("arrow functions as prop correctly capture their scope", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<button t-on-click="props.onClick"/>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let onClickArgs: [number, MouseEvent] | null = null;
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<t t-foreach="items" t-as="item" t-key="item.val">
|
||||||
|
<Child onClick="ev => onClick(item.val, ev)"/>
|
||||||
|
</t>
|
||||||
|
`;
|
||||||
|
static components = { Child };
|
||||||
|
items = [{ val: 1 }, { val: 2 }, { val: 3 }, { val: 4 }];
|
||||||
|
onClick(n: number, ev: MouseEvent) {
|
||||||
|
onClickArgs = [n, ev];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
expect(onClickArgs).toBeNull();
|
||||||
|
(<HTMLElement>fixture.querySelector("button")).click();
|
||||||
|
expect(onClickArgs![0]).toBe(1);
|
||||||
|
expect(onClickArgs![1]).toBeInstanceOf(MouseEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("support prop names that aren't valid bare object property names", async () => {
|
||||||
|
expect.assertions(4);
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<button t-on-click="props.onClick"/>`;
|
||||||
|
setup() {
|
||||||
|
expect(this.props["some-dashed-prop"]).toBe(5);
|
||||||
|
expect(this.props["a.b"]).toBe("keyword prop");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`<Child some-dashed-prop="5" a.b="'keyword prop'"/>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("template string in prop", async () => {
|
||||||
|
expect.assertions(3);
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml``;
|
||||||
|
setup() {
|
||||||
|
expect(this.props.propName).toBe("123");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml({ raw: ['<Child propName="`1${someVal}3`"/>'] });
|
||||||
|
static components = { Child };
|
||||||
|
someVal = 2;
|
||||||
|
}
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
import { Component, mount, onRendered, useState } from "../../src";
|
|
||||||
import { xml } from "../../src/tags";
|
|
||||||
import { makeTestFixture, snapshotEverything, nextTick } from "../helpers";
|
|
||||||
|
|
||||||
let fixture: HTMLElement;
|
|
||||||
|
|
||||||
snapshotEverything();
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = makeTestFixture();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("rendering semantics", () => {
|
|
||||||
test("can render a parent without rendering child", async () => {
|
|
||||||
let childN = 0;
|
|
||||||
let parentN = 0;
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`child`;
|
|
||||||
setup() {
|
|
||||||
onRendered(() => childN++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<t t-esc="state.value"/>
|
|
||||||
<Child/>
|
|
||||||
`;
|
|
||||||
static components = { Child };
|
|
||||||
|
|
||||||
state = useState({ value: "A" });
|
|
||||||
setup() {
|
|
||||||
onRendered(() => parentN++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = await mount(Parent, fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("Achild");
|
|
||||||
expect(parentN).toBe(1);
|
|
||||||
expect(childN).toBe(1);
|
|
||||||
|
|
||||||
parent.state.value = "B";
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe("Bchild");
|
|
||||||
expect(parentN).toBe(2);
|
|
||||||
expect(childN).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("can force a render to update sub tree", async () => {
|
|
||||||
let childN = 0;
|
|
||||||
let parentN = 0;
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`child`;
|
|
||||||
setup() {
|
|
||||||
onRendered(() => childN++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<t t-esc="state.value"/>
|
|
||||||
<Child/>
|
|
||||||
`;
|
|
||||||
static components = { Child };
|
|
||||||
|
|
||||||
state = { value: "A" };
|
|
||||||
setup() {
|
|
||||||
onRendered(() => parentN++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = await mount(Parent, fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("Achild");
|
|
||||||
expect(parentN).toBe(1);
|
|
||||||
expect(childN).toBe(1);
|
|
||||||
|
|
||||||
parent.state.value = "B";
|
|
||||||
parent.render(true);
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe("Bchild");
|
|
||||||
expect(parentN).toBe(2);
|
|
||||||
expect(childN).toBe(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("blabla", async () => {
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`<t t-esc="props.a.b"/>`;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<Child a="state"/>
|
|
||||||
`;
|
|
||||||
static components = { Child };
|
|
||||||
|
|
||||||
state = useState({ b: 1 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = await mount(Parent, fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("1");
|
|
||||||
|
|
||||||
parent.state.b = 3;
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe("3");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -37,6 +37,59 @@ describe("slots", () => {
|
|||||||
expect(fixture.innerHTML).toBe("some text");
|
expect(fixture.innerHTML).toBe("some text");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("simple default slot with params", async () => {
|
||||||
|
let child: any;
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<span><t t-slot="default" bool="state.bool"/></span>`;
|
||||||
|
state = useState({ bool: true });
|
||||||
|
setup() {
|
||||||
|
child = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<Child>
|
||||||
|
<t t-set-slot="default" t-slot-scope="slotScope">
|
||||||
|
<t t-if="slotScope.bool">some text</t>
|
||||||
|
<t t-else="slotScope.bool">other text</t>
|
||||||
|
</t>
|
||||||
|
</Child>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<span>some text</span>");
|
||||||
|
|
||||||
|
child.state.bool = false;
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<span>other text</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("simple default slot with params", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<span><t t-slot="default" bool="state.bool"/></span>`;
|
||||||
|
state = useState({ bool: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<Child>
|
||||||
|
<t t-if="slotScope.bool">some text</t>
|
||||||
|
<t t-else="slotScope.bool">other text</t>
|
||||||
|
</Child>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
|
||||||
|
let error = null;
|
||||||
|
try {
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
test("fun: two calls to the same slot", async () => {
|
test("fun: two calls to the same slot", async () => {
|
||||||
class Child extends Component {
|
class Child extends Component {
|
||||||
static template = xml`<t t-slot="default"/><t t-slot="default"/>`;
|
static template = xml`<t t-slot="default"/><t t-slot="default"/>`;
|
||||||
@@ -71,6 +124,33 @@ describe("slots", () => {
|
|||||||
expect(parent.state.value).toBe(1);
|
expect(parent.state.value).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("slot content is bound to caller (variation)", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<span><t t-slot="default"/></span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
// t-set t-value in template is to force compiler to protect the scope
|
||||||
|
// which in turns means that the ctx propagated to the slot is a sub object
|
||||||
|
static template = xml`
|
||||||
|
<Child>
|
||||||
|
<t t-set="var" t-value="1"/>
|
||||||
|
<button t-on-click="() => this.inc()">some text</button>
|
||||||
|
</Child>`;
|
||||||
|
static components = { Child };
|
||||||
|
state = useState({ value: 0 });
|
||||||
|
inc() {
|
||||||
|
expect(this).toBe(parent);
|
||||||
|
this.state.value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const parent = await mount(Parent, fixture);
|
||||||
|
|
||||||
|
expect(parent.state.value).toBe(0);
|
||||||
|
fixture.querySelector("button")!.click();
|
||||||
|
expect(parent.state.value).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
test("can define and call slots", async () => {
|
test("can define and call slots", async () => {
|
||||||
class Dialog extends Component {
|
class Dialog extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
@@ -97,6 +177,35 @@ describe("slots", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("can define and call slots with params", async () => {
|
||||||
|
class Dialog extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-esc="props.slots['header'].param"/>
|
||||||
|
<div><t t-slot="header"/></div>
|
||||||
|
<t t-esc="props.slots['footer'].param"/>
|
||||||
|
<div><t t-slot="footer"/></div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static components = { Dialog };
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<Dialog>
|
||||||
|
<t t-set-slot="header" param="var"><span>header</span></t>
|
||||||
|
<t t-set-slot="footer" param="'5'"><span>footer</span></t>
|
||||||
|
</Dialog>
|
||||||
|
</div>`;
|
||||||
|
var = 3;
|
||||||
|
}
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
"<div><div>3<div><span>header</span></div>5<div><span>footer</span></div></div></div>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("no named slot content => just no children", async () => {
|
test("no named slot content => just no children", async () => {
|
||||||
class Dialog extends Component {
|
class Dialog extends Component {
|
||||||
static template = xml`<span><t t-slot="header"/></span>`;
|
static template = xml`<span><t t-slot="header"/></span>`;
|
||||||
@@ -126,7 +235,7 @@ describe("slots", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><span>default content</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>default content</span></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("dafault slots can define a default content", async () => {
|
test("can define a default content", async () => {
|
||||||
class Dialog extends Component {
|
class Dialog extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
<span>
|
<span>
|
||||||
@@ -174,6 +283,42 @@ describe("slots", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><span>hey</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>hey</span></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("slots are properly bound to correct component", async () => {
|
||||||
|
let child: any = null;
|
||||||
|
class Child extends Component {
|
||||||
|
// t-set t-value in template is to force compiler to protect the scope
|
||||||
|
// which in turns means that the ctx propagated to the slot is a sub object
|
||||||
|
static template = xml`
|
||||||
|
<t t-slot="default">
|
||||||
|
<t t-set="var" t-value="1"/>
|
||||||
|
<button t-on-click="() => this.increment()">
|
||||||
|
<t t-esc="state.value"/>
|
||||||
|
</button>
|
||||||
|
</t>`;
|
||||||
|
|
||||||
|
state = useState({ value: 1 });
|
||||||
|
setup() {
|
||||||
|
child = this;
|
||||||
|
}
|
||||||
|
increment() {
|
||||||
|
expect(this).toBe(child);
|
||||||
|
this.state.value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`<Child/>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<button>1</button>");
|
||||||
|
|
||||||
|
fixture.querySelector("button")!.click();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<button>2</button>");
|
||||||
|
});
|
||||||
|
|
||||||
test("slots are rendered with proper context", async () => {
|
test("slots are rendered with proper context", async () => {
|
||||||
class Dialog extends Component {
|
class Dialog extends Component {
|
||||||
static template = xml`<span><t t-slot="footer"/></span>`;
|
static template = xml`<span><t t-slot="footer"/></span>`;
|
||||||
@@ -1352,4 +1497,33 @@ describe("slots", () => {
|
|||||||
}
|
}
|
||||||
expect(error).toBeNull();
|
expect(error).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("can use t-call in default-content of t-slot", async () => {
|
||||||
|
const template = xml``;
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<t t-slot="default"><t t-call="${template}"/></t>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`<Child/>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("can use component in default-content of t-slot", async () => {
|
||||||
|
class GrandChild extends Component {
|
||||||
|
static template = xml``;
|
||||||
|
}
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<t t-slot="default"><GrandChild/></t>`;
|
||||||
|
static components = { GrandChild };
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`<Child/>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { Component, mount, onMounted, useState, xml } from "../../src/index";
|
import { App, Component, mount, onMounted, useState, xml } from "../../src/index";
|
||||||
import { makeTestFixture, nextTick, snapshotEverything, useLogLifecycle } from "../helpers";
|
import {
|
||||||
|
makeTestFixture,
|
||||||
|
nextTick,
|
||||||
|
snapshotApp,
|
||||||
|
snapshotEverything,
|
||||||
|
useLogLifecycle,
|
||||||
|
} from "../helpers";
|
||||||
|
|
||||||
snapshotEverything();
|
snapshotEverything();
|
||||||
|
|
||||||
@@ -291,4 +297,28 @@ describe("list of components", () => {
|
|||||||
expect((parent.el as HTMLElement).innerHTML).toBe("<div>2</div><div>1</div>");
|
expect((parent.el as HTMLElement).innerHTML).toBe("<div>2</div><div>1</div>");
|
||||||
expect(childInstances.length).toBe(2);
|
expect(childInstances.length).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("crash on duplicate key in dev mode", async () => {
|
||||||
|
const consoleInfo = console.info;
|
||||||
|
console.info = jest.fn();
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml``;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<t t-foreach="[1, 2]" t-as="item" t-key="'child'">
|
||||||
|
<Child/>
|
||||||
|
</t>
|
||||||
|
`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
const app = new App(Parent);
|
||||||
|
app.configure({ dev: true });
|
||||||
|
await expect(async () => {
|
||||||
|
await app.mount(fixture);
|
||||||
|
}).rejects.toThrowError("Got duplicate key in t-foreach: child");
|
||||||
|
snapshotApp(app);
|
||||||
|
console.info = consoleInfo;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ describe("t-props", () => {
|
|||||||
`;
|
`;
|
||||||
setup() {
|
setup() {
|
||||||
expect(this.props).toEqual({ a: 1, b: 2 });
|
expect(this.props).toEqual({ a: 1, b: 2 });
|
||||||
expect(this.props).not.toBe(props);
|
expect(this.props).toBe(props);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class Parent extends Component {
|
class Parent extends Component {
|
||||||
|
|||||||
@@ -17,9 +17,8 @@ exports[`Memo if no prop change, prevent renderings from above 2`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
let b6 = text(ctx['state'].a);
|
let b6 = text(ctx['state'].a);
|
||||||
let b7 = text(ctx['state'].b);
|
let b7 = text(ctx['state'].b);
|
||||||
let b8 = text(ctx['state'].c);
|
let b8 = text(ctx['state'].c);
|
||||||
@@ -30,7 +29,7 @@ exports[`Memo if no prop change, prevent renderings from above 2`] = `
|
|||||||
let b2 = text(ctx['state'].a);
|
let b2 = text(ctx['state'].a);
|
||||||
let b3 = text(ctx['state'].b);
|
let b3 = text(ctx['state'].b);
|
||||||
let b4 = text(ctx['state'].c);
|
let b4 = text(ctx['state'].c);
|
||||||
let b9 = assign(component(\`Memo\`, {a: ctx['state'].a,b: ctx['state'].b}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b9 = component(\`Memo\`, {a: ctx['state'].a,b: ctx['state'].b,slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return multi([b2, b3, b4, b9]);
|
return multi([b2, b3, b4, b9]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -53,15 +52,14 @@ exports[`Memo if no props, prevent renderings from above 2`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
const slot3 = ctx => (node, key) => {
|
function slot2(ctx, node, key) {
|
||||||
return component(\`Child\`, {value: ctx['state'].value}, key + \`__4\`, node, ctx);
|
return component(\`Child\`, {value: ctx['state'].value}, key + \`__3\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`Child\`, {value: ctx['state'].value}, key + \`__1\`, node, ctx);
|
let b2 = component(\`Child\`, {value: ctx['state'].value}, key + \`__1\`, node, ctx);
|
||||||
let b4 = assign(component(\`Memo\`, {}, key + \`__2\`, node, ctx, true), {slots: {'default': slot3(ctx)}});
|
let b4 = component(\`Memo\`, {slots: {'default': {__render: slot2, __ctx: ctx}}}, key + \`__4\`, node, ctx);
|
||||||
return multi([b2, b4]);
|
return multi([b2, b4]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -72,15 +70,14 @@ exports[`Memo if no props, prevent renderings from above (work with simple html)
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return text(ctx['state'].value);
|
return text(ctx['state'].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = text(ctx['state'].value);
|
let b2 = text(ctx['state'].value);
|
||||||
let b4 = assign(component(\`Memo\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b4 = component(\`Memo\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return multi([b2, b4]);
|
return multi([b2, b4]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ exports[`Portal Portal composed with t-slot 1`] = `
|
|||||||
let block1 = createBlock(\`<div block-handler-0=\\"custom\\"><span id=\\"childSpan\\">child2</span></div>\`);
|
let block1 = createBlock(\`<div block-handler-0=\\"custom\\"><span id=\\"childSpan\\">child2</span></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const v1 = ctx['onCustom'];
|
let d1 = [ctx['onCustom'], ctx];
|
||||||
let d1 = [v1, ctx];
|
|
||||||
return block1([d1]);
|
return block1([d1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -21,14 +20,13 @@ exports[`Portal Portal composed with t-slot 2`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return callSlot(ctx, node, key, 'default');
|
return callSlot(ctx, node, key, 'default', false, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
return assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
return component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
@@ -38,16 +36,15 @@ exports[`Portal Portal composed with t-slot 3`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`Child2\`, {customHandler: ctx['_handled']}, key + \`__3\`, node, ctx);
|
return component(\`Child2\`, {customHandler: ctx['_handled']}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -58,17 +55,16 @@ exports[`Portal basic use of portal 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><span>1</span><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><span>1</span><block-child-0/></div>\`);
|
||||||
let block2 = createBlock(\`<p>2</p>\`);
|
let block2 = createBlock(\`<p>2</p>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return block2();
|
return block2();
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -94,19 +90,18 @@ exports[`Portal conditional use of Portal (with sub Component) 2`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block2 = createBlock(\`<span>1</span>\`);
|
let block2 = createBlock(\`<span>1</span>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`Child\`, {val: ctx['state'].val}, key + \`__3\`, node, ctx);
|
return component(\`Child\`, {val: ctx['state'].val}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2,b4;
|
let b2,b4;
|
||||||
b2 = block2();
|
b2 = block2();
|
||||||
if (ctx['state'].hasPortal) {
|
if (ctx['state'].hasPortal) {
|
||||||
b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
b4 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
}
|
}
|
||||||
return multi([b2, b4]);
|
return multi([b2, b4]);
|
||||||
}
|
}
|
||||||
@@ -118,12 +113,11 @@ exports[`Portal conditional use of Portal 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block2 = createBlock(\`<span>1</span>\`);
|
let block2 = createBlock(\`<span>1</span>\`);
|
||||||
let block3 = createBlock(\`<p>2</p>\`);
|
let block3 = createBlock(\`<p>2</p>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return block3();
|
return block3();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +125,7 @@ exports[`Portal conditional use of Portal 1`] = `
|
|||||||
let b2,b4;
|
let b2,b4;
|
||||||
b2 = block2();
|
b2 = block2();
|
||||||
if (ctx['state'].hasPortal) {
|
if (ctx['state'].hasPortal) {
|
||||||
b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
b4 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
return multi([b2, b4]);
|
return multi([b2, b4]);
|
||||||
}
|
}
|
||||||
@@ -158,18 +152,17 @@ exports[`Portal lifecycle hooks of portal sub component are properly called 2`]
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`Child\`, {val: ctx['state'].val}, key + \`__3\`, node, ctx);
|
return component(\`Child\`, {val: ctx['state'].val}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3;
|
let b3;
|
||||||
if (ctx['state'].hasChild) {
|
if (ctx['state'].hasChild) {
|
||||||
b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
b3 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
}
|
}
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
@@ -181,12 +174,11 @@ exports[`Portal portal could have dynamically no content 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
let block3 = createBlock(\`<span><block-text-0/></span>\`);
|
let block3 = createBlock(\`<span><block-text-0/></span>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
let b3;
|
let b3;
|
||||||
if (ctx['state'].val) {
|
if (ctx['state'].val) {
|
||||||
let d1 = ctx['state'].val;
|
let d1 = ctx['state'].val;
|
||||||
@@ -196,7 +188,7 @@ exports[`Portal portal could have dynamically no content 1`] = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b4 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b4]);
|
return block1([], [b4]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -222,16 +214,15 @@ exports[`Portal portal destroys on crash 2`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`Child\`, {error: ctx['state'].error}, key + \`__3\`, node, ctx);
|
return component(\`Child\`, {error: ctx['state'].error}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -257,16 +248,15 @@ exports[`Portal portal with child and props 2`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`Child\`, {val: ctx['state'].val}, key + \`__3\`, node, ctx);
|
return component(\`Child\`, {val: ctx['state'].val}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -277,13 +267,12 @@ exports[`Portal portal with dynamic body 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
let block3 = createBlock(\`<span><block-text-0/></span>\`);
|
let block3 = createBlock(\`<span><block-text-0/></span>\`);
|
||||||
let block4 = createBlock(\`<div/>\`);
|
let block4 = createBlock(\`<div/>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
let b3,b4;
|
let b3,b4;
|
||||||
if (ctx['state'].val) {
|
if (ctx['state'].val) {
|
||||||
let d1 = ctx['state'].val;
|
let d1 = ctx['state'].val;
|
||||||
@@ -295,7 +284,7 @@ exports[`Portal portal with dynamic body 1`] = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b5 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b5 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b5]);
|
return block1([], [b5]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -306,20 +295,19 @@ exports[`Portal portal with many children 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
let block3 = createBlock(\`<div>1</div>\`);
|
let block3 = createBlock(\`<div>1</div>\`);
|
||||||
let block4 = createBlock(\`<p>2</p>\`);
|
let block4 = createBlock(\`<p>2</p>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
let b3 = block3();
|
let b3 = block3();
|
||||||
let b4 = block4();
|
let b4 = block4();
|
||||||
return multi([b3, b4]);
|
return multi([b3, b4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b5 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b5 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b5]);
|
return block1([], [b5]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -330,11 +318,10 @@ exports[`Portal portal with no content 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
let b3;
|
let b3;
|
||||||
if (false) {
|
if (false) {
|
||||||
b3 = text('ABC');
|
b3 = text('ABC');
|
||||||
@@ -343,7 +330,7 @@ exports[`Portal portal with no content 1`] = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b4 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b4]);
|
return block1([], [b4]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -354,16 +341,15 @@ exports[`Portal portal with only text as content 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return text('only text');
|
return text('only text');
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -374,17 +360,16 @@ exports[`Portal portal with target not in dom 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
let block2 = createBlock(\`<div>2</div>\`);
|
let block2 = createBlock(\`<div>2</div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return block2();
|
return block2();
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Portal\`, {target: '#does-not-exist'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Portal\`, {target: '#does-not-exist',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -409,16 +394,15 @@ exports[`Portal portal's parent's env is not polluted 2`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`Child\`, {}, key + \`__3\`, node, ctx);
|
return component(\`Child\`, {}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -429,17 +413,16 @@ exports[`Portal with target in template (after portal) 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><span>1</span><block-child-0/><div id=\\"local-target\\"/></div>\`);
|
let block1 = createBlock(\`<div><span>1</span><block-child-0/><div id=\\"local-target\\"/></div>\`);
|
||||||
let block2 = createBlock(\`<p>2</p>\`);
|
let block2 = createBlock(\`<p>2</p>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return block2();
|
return block2();
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Portal\`, {target: '#local-target'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Portal\`, {target: '#local-target',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -450,17 +433,16 @@ exports[`Portal with target in template (before portal) 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><div id=\\"local-target\\"/><span>1</span><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><div id=\\"local-target\\"/><span>1</span><block-child-0/></div>\`);
|
||||||
let block2 = createBlock(\`<p>2</p>\`);
|
let block2 = createBlock(\`<p>2</p>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return block2();
|
return block2();
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Portal\`, {target: '#local-target'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Portal\`, {target: '#local-target',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -471,17 +453,16 @@ exports[`Portal: Props validation target is mandatory 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
let block2 = createBlock(\`<div>2</div>\`);
|
let block2 = createBlock(\`<div>2</div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return block2();
|
return block2();
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Portal\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Portal\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -492,17 +473,16 @@ exports[`Portal: Props validation target is not list 1`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
let block2 = createBlock(\`<div>2</div>\`);
|
let block2 = createBlock(\`<div>2</div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return block2();
|
return block2();
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Portal\`, {target: ['body']}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Portal\`, {target: ['body'],slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -528,16 +508,15 @@ exports[`Portal: UI/UX focus is kept across re-renders 2`] = `
|
|||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||||
let assign = Object.assign;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
const slot2 = ctx => (node, key) => {
|
function slot1(ctx, node, key) {
|
||||||
return component(\`Child\`, {val: ctx['state'].val}, key + \`__3\`, node, ctx);
|
return component(\`Child\`, {val: ctx['state'].val}, key + \`__2\`, node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
|
let b3 = component(\`Portal\`, {target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__3\`, node, ctx);
|
||||||
return block1([], [b3]);
|
return block1([], [b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Component, mount, onWillRender, onWillStart, onWillUpdateProps, useState } from "../src";
|
import { Component, mount, onWillRender, onWillStart, onWillUpdateProps, useState } from "../src";
|
||||||
import { reactive } from "../src/reactivity";
|
import { batched, reactive } from "../src/reactivity";
|
||||||
import { xml } from "../src/tags";
|
import { xml } from "../src/tags";
|
||||||
import { batched } from "../src/utils";
|
|
||||||
import {
|
import {
|
||||||
makeDeferred,
|
makeDeferred,
|
||||||
makeTestFixture,
|
makeTestFixture,
|
||||||
@@ -1510,7 +1509,7 @@ describe("Reactivity: useState", () => {
|
|||||||
expect([...steps]).toEqual(["list"]);
|
expect([...steps]).toEqual(["list"]);
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe("<div><div>3</div> Total: 3 Count: 1</div>");
|
expect(fixture.innerHTML).toBe("<div><div>3</div> Total: 3 Count: 1</div>");
|
||||||
expect([...steps]).toEqual(["list"]);
|
expect([...steps]).toEqual(["list", "quantity1"]);
|
||||||
steps.clear();
|
steps.clear();
|
||||||
|
|
||||||
secondQuantity.quantity = 2;
|
secondQuantity.quantity = 2;
|
||||||
|
|||||||
+1
-19
@@ -1,5 +1,4 @@
|
|||||||
import { batched, EventBus } from "../src/utils";
|
import { EventBus } from "../src/utils";
|
||||||
import { nextMicroTick } from "./helpers";
|
|
||||||
|
|
||||||
describe("event bus behaviour", () => {
|
describe("event bus behaviour", () => {
|
||||||
test("can subscribe and be notified", () => {
|
test("can subscribe and be notified", () => {
|
||||||
@@ -34,20 +33,3 @@ describe("event bus behaviour", () => {
|
|||||||
bus.trigger("event", "hello world");
|
bus.trigger("event", "hello world");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("batched", () => {
|
|
||||||
test("callback is called only once after operations", async () => {
|
|
||||||
let n = 0;
|
|
||||||
let fn = batched(() => n++);
|
|
||||||
|
|
||||||
expect(n).toBe(0);
|
|
||||||
fn();
|
|
||||||
fn();
|
|
||||||
expect(n).toBe(0);
|
|
||||||
|
|
||||||
await nextMicroTick();
|
|
||||||
expect(n).toBe(1);
|
|
||||||
await nextMicroTick();
|
|
||||||
expect(n).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,301 +0,0 @@
|
|||||||
import {
|
|
||||||
buildData,
|
|
||||||
startMeasure,
|
|
||||||
stopMeasure,
|
|
||||||
formatNumber
|
|
||||||
} from "../shared/utils.js";
|
|
||||||
|
|
||||||
odoo.define("app", function(require) {
|
|
||||||
const Widget = require("web.Widget");
|
|
||||||
var core = require("web.core");
|
|
||||||
var dom = require("web.dom");
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
var Counter = Widget.extend({
|
|
||||||
template: "counter",
|
|
||||||
events: {
|
|
||||||
"click .o_increment": "_onIncrement"
|
|
||||||
},
|
|
||||||
init: function(parent) {
|
|
||||||
this._super(parent);
|
|
||||||
this.value = 0;
|
|
||||||
},
|
|
||||||
start: function() {
|
|
||||||
this.updateCounter();
|
|
||||||
},
|
|
||||||
on_attach_callback: function() {},
|
|
||||||
on_detach_callback: function() {},
|
|
||||||
updateCounter: function() {
|
|
||||||
this.$(".o_increment").html("Value: " + this.value);
|
|
||||||
},
|
|
||||||
_onIncrement: function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.value += 1;
|
|
||||||
this.updateCounter();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
var Message = Widget.extend({
|
|
||||||
template: "message",
|
|
||||||
events: {
|
|
||||||
"click .remove": "_onRemove"
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function(parent, message) {
|
|
||||||
this._super(parent);
|
|
||||||
this.author = message.author;
|
|
||||||
this.msg = message.msg;
|
|
||||||
this.id = message.id;
|
|
||||||
},
|
|
||||||
willStart: function() {
|
|
||||||
this.counter = new Counter(this);
|
|
||||||
return this.counter.appendTo($("<div>"));
|
|
||||||
},
|
|
||||||
|
|
||||||
start: function() {
|
|
||||||
this.$msg = this.$(".msg");
|
|
||||||
dom.append(this.$el, this.counter.$el, {
|
|
||||||
in_DOM: this.isInDom,
|
|
||||||
callbacks: [{ widget: this.counter }]
|
|
||||||
});
|
|
||||||
},
|
|
||||||
on_attach_callback: function() {
|
|
||||||
this.isInDom = true;
|
|
||||||
if (this.counter) {
|
|
||||||
this.counter.on_attach_callback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_detach_callback: function() {
|
|
||||||
this.isInDom = true;
|
|
||||||
if (this.counter) {
|
|
||||||
this.counter.on_detach_callback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onRemove: function() {
|
|
||||||
this.trigger_up("remove_message", { id: this.id });
|
|
||||||
},
|
|
||||||
|
|
||||||
update: function() {
|
|
||||||
this.msg += "!!!";
|
|
||||||
this.$msg.text(this.msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
var App = Widget.extend({
|
|
||||||
template: "root",
|
|
||||||
events: {
|
|
||||||
"click .o_btn_msg.100": function() {
|
|
||||||
this.addMessages(100);
|
|
||||||
},
|
|
||||||
"click .o_btn_msg.1000": function() {
|
|
||||||
this.addMessages(1000);
|
|
||||||
},
|
|
||||||
"click .o_btn_msg.10000": function() {
|
|
||||||
this.addMessages(10000);
|
|
||||||
},
|
|
||||||
"click .o_btn_msg.30000": function() {
|
|
||||||
this.addMessages(30000);
|
|
||||||
},
|
|
||||||
"click .updateSomeMessages": function() {
|
|
||||||
this.updateSomeMessages();
|
|
||||||
},
|
|
||||||
"click .clear": function() {
|
|
||||||
this.clear();
|
|
||||||
},
|
|
||||||
"click .o_multiple": function() {
|
|
||||||
this.multipleFlag = !this.multipleFlag;
|
|
||||||
},
|
|
||||||
"click .o_clear": function() {
|
|
||||||
this.clearFlag = !this.clearFlag;
|
|
||||||
},
|
|
||||||
"click .clear-log": function() {
|
|
||||||
this.$log[0].innerHTML = "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
custom_events: {
|
|
||||||
remove_message: "_onRemoveMessage"
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function(parent) {
|
|
||||||
this._super(parent);
|
|
||||||
this.widgets = {};
|
|
||||||
this.isInDom = false;
|
|
||||||
this.messageCount = 0;
|
|
||||||
this.multipleFlag = false;
|
|
||||||
this.clearFlag = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
start: function() {
|
|
||||||
this.$content = this.$(".content");
|
|
||||||
this.$msgCount = this.$(".message_count");
|
|
||||||
this.$log = this.$(".log-content");
|
|
||||||
this.log("Benchmarking odoo widgets, 12.0");
|
|
||||||
},
|
|
||||||
on_attach_callback: function() {
|
|
||||||
this.isInDom = true;
|
|
||||||
for (let widget of Object.values(this.widgets)) {
|
|
||||||
if (widget.on_attach_callback) {
|
|
||||||
widget.on_attach_callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_detach_callback: function() {
|
|
||||||
this.isInDom = true;
|
|
||||||
for (let widget of Object.values(this.widgets)) {
|
|
||||||
if (widget.on_detach_callback) {
|
|
||||||
widget.on_detach_callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateMessageCount() {
|
|
||||||
this.$msgCount.text("Number of msg: " + this.messageCount);
|
|
||||||
},
|
|
||||||
|
|
||||||
addMessages: function(n) {
|
|
||||||
const self = this;
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const defs = [];
|
|
||||||
const messages = buildData(n);
|
|
||||||
for (let message of messages) {
|
|
||||||
const widget = new Message(this, message);
|
|
||||||
this.widgets[message.id] = widget;
|
|
||||||
defs.push(widget.appendTo("<div>"));
|
|
||||||
}
|
|
||||||
return $.when
|
|
||||||
.apply($, defs)
|
|
||||||
.then(function() {
|
|
||||||
for (let message of messages) {
|
|
||||||
let widget = self.widgets[message.id];
|
|
||||||
dom.append(self.$content, widget.$el, {
|
|
||||||
in_DOM: this.isInDom,
|
|
||||||
callbacks: [{ widget: widget }]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(function() {
|
|
||||||
self.messageCount += n;
|
|
||||||
self.updateMessageCount();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
clear: function() {
|
|
||||||
startMeasure("clear");
|
|
||||||
this._clear();
|
|
||||||
stopMeasure(info => {
|
|
||||||
this.log(info.msg);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_clear: function() {
|
|
||||||
this.$content.empty();
|
|
||||||
for (let key in this.widgets) {
|
|
||||||
this.widgets[key].destroy();
|
|
||||||
delete this.widgets[key];
|
|
||||||
}
|
|
||||||
this.messageCount = 0;
|
|
||||||
this.updateMessageCount();
|
|
||||||
},
|
|
||||||
updateSomeMessages: function() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const widgets = Object.values(this.widgets);
|
|
||||||
for (let i = 0; i < widgets.length; i += 10) {
|
|
||||||
widgets[i].update();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_onRemoveMessage: function(ev) {
|
|
||||||
startMeasure("remove message");
|
|
||||||
ev.target.destroy();
|
|
||||||
delete this.widgets[ev.data.id];
|
|
||||||
this.messageCount--;
|
|
||||||
this.updateMessageCount();
|
|
||||||
stopMeasure();
|
|
||||||
},
|
|
||||||
|
|
||||||
benchmark: function(message, fn, callback) {
|
|
||||||
if (this.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.clearFlag) {
|
|
||||||
this._benchmark("clear", this._clear.bind(this), finalize, false);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_benchmark: function(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
const benchmark = fn();
|
|
||||||
(benchmark && benchmark.then ? benchmark : $.when()).then(() => {
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
log: function(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.$log[0].appendChild(div);
|
|
||||||
this.$log[0].scrollTop = this.$log[0].scrollHeight;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
// prepare QWeb
|
|
||||||
const templates = await fetch("templates.xml");
|
|
||||||
let strTemplates = await templates.text();
|
|
||||||
strTemplates = strTemplates.replace(/<!--[\s\S]*?-->/g, "");
|
|
||||||
core.qweb.add_template(strTemplates);
|
|
||||||
|
|
||||||
// prepare app
|
|
||||||
const app = new App();
|
|
||||||
await app.appendTo(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
});
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Odoo Widget Benchmark (12.0)</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='web.assets_common.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='main'></div>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div class="main" t-name="root">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button class="o_btn_msg 100">Add 100 messages</button>
|
|
||||||
<button class="o_btn_msg 1000">Add 1k messages</button>
|
|
||||||
<button class="o_btn_msg 10000">Add 10k messages</button>
|
|
||||||
<button class="o_btn_msg 30000">Add 30k messages</button>
|
|
||||||
<button class="updateSomeMessages">Update every 10th message</button>
|
|
||||||
<button class="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" class="o_multiple" id="multipleflag" />
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" class="o_clear" id="clearFlag" />
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info message_count">
|
|
||||||
Number of messages: 0
|
|
||||||
</div>
|
|
||||||
<div class="title">Log <span class="clear-log">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="message" class="message">
|
|
||||||
<span class="author"><t t-esc="widget.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="widget.msg"/></span>
|
|
||||||
<button class="remove">Remove</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="counter">
|
|
||||||
<button class="o_increment"></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,301 +0,0 @@
|
|||||||
import {
|
|
||||||
buildData,
|
|
||||||
startMeasure,
|
|
||||||
stopMeasure,
|
|
||||||
formatNumber
|
|
||||||
} from "../shared/utils.js";
|
|
||||||
|
|
||||||
odoo.define("app", function(require) {
|
|
||||||
const Widget = require("web.Widget");
|
|
||||||
var core = require("web.core");
|
|
||||||
var dom = require("web.dom");
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
var Counter = Widget.extend({
|
|
||||||
template: "counter",
|
|
||||||
events: {
|
|
||||||
"click .o_increment": "_onIncrement"
|
|
||||||
},
|
|
||||||
init: function(parent) {
|
|
||||||
this._super(parent);
|
|
||||||
this.value = 0;
|
|
||||||
},
|
|
||||||
start: function() {
|
|
||||||
this.updateCounter();
|
|
||||||
},
|
|
||||||
on_attach_callback: function() {},
|
|
||||||
on_detach_callback: function() {},
|
|
||||||
updateCounter: function() {
|
|
||||||
this.$(".o_increment").html("Value: " + this.value);
|
|
||||||
},
|
|
||||||
_onIncrement: function(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.value += 1;
|
|
||||||
this.updateCounter();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
var Message = Widget.extend({
|
|
||||||
template: "message",
|
|
||||||
events: {
|
|
||||||
"click .remove": "_onRemove"
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function(parent, message) {
|
|
||||||
this._super(parent);
|
|
||||||
this.author = message.author;
|
|
||||||
this.msg = message.msg;
|
|
||||||
this.id = message.id;
|
|
||||||
},
|
|
||||||
willStart: function() {
|
|
||||||
this.counter = new Counter(this);
|
|
||||||
return this.counter.appendTo($("<div>"));
|
|
||||||
},
|
|
||||||
|
|
||||||
start: function() {
|
|
||||||
this.$msg = this.$(".msg");
|
|
||||||
dom.append(this.$el, this.counter.$el, {
|
|
||||||
in_DOM: this.isInDom,
|
|
||||||
callbacks: [{ widget: this.counter }]
|
|
||||||
});
|
|
||||||
},
|
|
||||||
on_attach_callback: function() {
|
|
||||||
this.isInDom = true;
|
|
||||||
if (this.counter) {
|
|
||||||
this.counter.on_attach_callback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_detach_callback: function() {
|
|
||||||
this.isInDom = true;
|
|
||||||
if (this.counter) {
|
|
||||||
this.counter.on_detach_callback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onRemove: function() {
|
|
||||||
this.trigger_up("remove_message", { id: this.id });
|
|
||||||
},
|
|
||||||
|
|
||||||
update: function() {
|
|
||||||
this.msg += "!!!";
|
|
||||||
this.$msg.text(this.msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
var App = Widget.extend({
|
|
||||||
template: "root",
|
|
||||||
events: {
|
|
||||||
"click .o_btn_msg.100": function() {
|
|
||||||
this.addMessages(100);
|
|
||||||
},
|
|
||||||
"click .o_btn_msg.1000": function() {
|
|
||||||
this.addMessages(1000);
|
|
||||||
},
|
|
||||||
"click .o_btn_msg.10000": function() {
|
|
||||||
this.addMessages(10000);
|
|
||||||
},
|
|
||||||
"click .o_btn_msg.30000": function() {
|
|
||||||
this.addMessages(30000);
|
|
||||||
},
|
|
||||||
"click .updateSomeMessages": function() {
|
|
||||||
this.updateSomeMessages();
|
|
||||||
},
|
|
||||||
"click .clear": function() {
|
|
||||||
this.clear();
|
|
||||||
},
|
|
||||||
"click .o_multiple": function() {
|
|
||||||
this.multipleFlag = !this.multipleFlag;
|
|
||||||
},
|
|
||||||
"click .o_clear": function() {
|
|
||||||
this.clearFlag = !this.clearFlag;
|
|
||||||
},
|
|
||||||
"click .clear-log": function() {
|
|
||||||
this.$log[0].innerHTML = "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
custom_events: {
|
|
||||||
remove_message: "_onRemoveMessage"
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function(parent) {
|
|
||||||
this._super(parent);
|
|
||||||
this.widgets = {};
|
|
||||||
this.isInDom = false;
|
|
||||||
this.messageCount = 0;
|
|
||||||
this.multipleFlag = false;
|
|
||||||
this.clearFlag = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
start: function() {
|
|
||||||
this.$content = this.$(".content");
|
|
||||||
this.$msgCount = this.$(".message_count");
|
|
||||||
this.$log = this.$(".log-content");
|
|
||||||
this.log("Benchmarking odoo widgets, 12.3");
|
|
||||||
},
|
|
||||||
on_attach_callback: function() {
|
|
||||||
this.isInDom = true;
|
|
||||||
for (let widget of Object.values(this.widgets)) {
|
|
||||||
if (widget.on_attach_callback) {
|
|
||||||
widget.on_attach_callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_detach_callback: function() {
|
|
||||||
this.isInDom = true;
|
|
||||||
for (let widget of Object.values(this.widgets)) {
|
|
||||||
if (widget.on_detach_callback) {
|
|
||||||
widget.on_detach_callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateMessageCount() {
|
|
||||||
this.$msgCount.text("Number of msg: " + this.messageCount);
|
|
||||||
},
|
|
||||||
|
|
||||||
addMessages: function(n) {
|
|
||||||
const self = this;
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const defs = [];
|
|
||||||
const messages = buildData(n);
|
|
||||||
for (let message of messages) {
|
|
||||||
const widget = new Message(this, message);
|
|
||||||
this.widgets[message.id] = widget;
|
|
||||||
defs.push(widget.appendTo("<div>"));
|
|
||||||
}
|
|
||||||
return $.when
|
|
||||||
.apply($, defs)
|
|
||||||
.then(function() {
|
|
||||||
for (let message of messages) {
|
|
||||||
let widget = self.widgets[message.id];
|
|
||||||
dom.append(self.$content, widget.$el, {
|
|
||||||
in_DOM: this.isInDom,
|
|
||||||
callbacks: [{ widget: widget }]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(function() {
|
|
||||||
self.messageCount += n;
|
|
||||||
self.updateMessageCount();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
clear: function() {
|
|
||||||
startMeasure("clear");
|
|
||||||
this._clear();
|
|
||||||
stopMeasure(info => {
|
|
||||||
this.log(info.msg);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_clear: function() {
|
|
||||||
this.$content.empty();
|
|
||||||
for (let key in this.widgets) {
|
|
||||||
this.widgets[key].destroy();
|
|
||||||
delete this.widgets[key];
|
|
||||||
}
|
|
||||||
this.messageCount = 0;
|
|
||||||
this.updateMessageCount();
|
|
||||||
},
|
|
||||||
updateSomeMessages: function() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const widgets = Object.values(this.widgets);
|
|
||||||
for (let i = 0; i < widgets.length; i += 10) {
|
|
||||||
widgets[i].update();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_onRemoveMessage: function(ev) {
|
|
||||||
startMeasure("remove message");
|
|
||||||
ev.target.destroy();
|
|
||||||
delete this.widgets[ev.data.id];
|
|
||||||
this.messageCount--;
|
|
||||||
this.updateMessageCount();
|
|
||||||
stopMeasure();
|
|
||||||
},
|
|
||||||
|
|
||||||
benchmark: function(message, fn, callback) {
|
|
||||||
if (this.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.clearFlag) {
|
|
||||||
this._benchmark("clear", this._clear.bind(this), finalize, false);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_benchmark: function(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
const benchmark = fn();
|
|
||||||
(benchmark && benchmark.then ? benchmark : $.when()).then(() => {
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
log: function(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.$log[0].appendChild(div);
|
|
||||||
this.$log[0].scrollTop = this.$log[0].scrollHeight;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
// prepare QWeb
|
|
||||||
const templates = await fetch("templates.xml");
|
|
||||||
let strTemplates = await templates.text();
|
|
||||||
strTemplates = strTemplates.replace(/<!--[\s\S]*?-->/g, "");
|
|
||||||
core.qweb.add_template(strTemplates);
|
|
||||||
|
|
||||||
// prepare app
|
|
||||||
const app = new App();
|
|
||||||
await app.appendTo(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
});
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Odoo Widget Benchmark (12.3)</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='web.assets_common.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='main'></div>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div class="main" t-name="root">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button class="o_btn_msg 100">Add 100 messages</button>
|
|
||||||
<button class="o_btn_msg 1000">Add 1k messages</button>
|
|
||||||
<button class="o_btn_msg 10000">Add 10k messages</button>
|
|
||||||
<button class="o_btn_msg 30000">Add 30k messages</button>
|
|
||||||
<button class="updateSomeMessages">Update every 10th message</button>
|
|
||||||
<button class="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" class="o_multiple" id="multipleflag" />
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" class="o_clear" id="clearFlag" />
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info message_count">
|
|
||||||
Number of messages: 0
|
|
||||||
</div>
|
|
||||||
<div class="title">Log <span class="clear-log">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="message" class="message">
|
|
||||||
<span class="author"><t t-esc="widget.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="widget.msg"/></span>
|
|
||||||
<button class="remove">Remove</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="counter">
|
|
||||||
<button class="o_increment"></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,170 +0,0 @@
|
|||||||
import {
|
|
||||||
buildData,
|
|
||||||
startMeasure,
|
|
||||||
stopMeasure,
|
|
||||||
formatNumber
|
|
||||||
} from "../shared/utils.js";
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Counter extends owl.Component {
|
|
||||||
state = { counter: 0 };
|
|
||||||
|
|
||||||
increment() {
|
|
||||||
this.state.counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Message extends owl.Component {
|
|
||||||
widgets = { Counter };
|
|
||||||
|
|
||||||
shouldUpdate(nextProps) {
|
|
||||||
return nextProps !== this.props;
|
|
||||||
}
|
|
||||||
removeMessage() {
|
|
||||||
this.trigger("remove_message", {
|
|
||||||
id: this.props.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class App extends owl.Component {
|
|
||||||
widgets = { Message };
|
|
||||||
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.log(
|
|
||||||
`Benchmarking Owl v${owl._version} (build date: ${
|
|
||||||
owl._date
|
|
||||||
})`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(message, fn, callback) {
|
|
||||||
if (this.state.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.clearAfterFlag) {
|
|
||||||
this._benchmark(
|
|
||||||
"clear",
|
|
||||||
() => {
|
|
||||||
this.state.messages = [];
|
|
||||||
},
|
|
||||||
finalize,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_benchmark(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
fn();
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessages(n) {
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const newMessages = buildData(n);
|
|
||||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._benchmark("clear", () => {
|
|
||||||
this.state.messages = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSomeMessages() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const messages = this.state.messages;
|
|
||||||
for (let i = 0; i < this.state.messages.length; i += 10) {
|
|
||||||
const msg = Object.assign({}, messages[i]);
|
|
||||||
msg.author += "!!!";
|
|
||||||
this.set(messages, i, msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessage(data) {
|
|
||||||
this.benchmark("remove message", () => {
|
|
||||||
const index = this.state.messages.findIndex(m => m.id === data.id);
|
|
||||||
this.state.messages.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.refs.log.appendChild(div);
|
|
||||||
this.refs.log.scrollTop = this.refs.log.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLog() {
|
|
||||||
this.refs.log.innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleMultiple() {
|
|
||||||
this.state.multipleFlag = !this.state.multipleFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleClear() {
|
|
||||||
this.state.clearAfterFlag = !this.state.clearAfterFlag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
const templates = await owl.utils.loadTemplates("templates.xml");
|
|
||||||
const env = {
|
|
||||||
qweb: new owl.QWeb(templates)
|
|
||||||
};
|
|
||||||
const app = new App(env);
|
|
||||||
app.mount(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OWL 0.11.0 Benchmark</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='owl.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='main'></div>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div t-name="App" class="main">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
|
||||||
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
|
||||||
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
|
||||||
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
|
||||||
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
|
||||||
<button t-on-click="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
|
||||||
<hr/>
|
|
||||||
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content" t-ref="'log'"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content" t-on-remove-message="removeMessage">
|
|
||||||
<t t-foreach="state.messages" t-as="message">
|
|
||||||
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Message" class="message">
|
|
||||||
<span class="author"><t t-esc="props.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="props.msg"/></span>
|
|
||||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
|
||||||
<t t-widget="Counter"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Counter">
|
|
||||||
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Counter extends owl.Component {
|
|
||||||
state = { counter: 0 };
|
|
||||||
|
|
||||||
increment() {
|
|
||||||
this.state.counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Message extends owl.Component {
|
|
||||||
widgets = { Counter };
|
|
||||||
|
|
||||||
shouldUpdate(nextProps) {
|
|
||||||
return nextProps.message !== this.props.message;
|
|
||||||
}
|
|
||||||
removeMessage() {
|
|
||||||
this.trigger("remove-message", {
|
|
||||||
id: this.props.message.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class App extends owl.Component {
|
|
||||||
widgets = { Message };
|
|
||||||
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(message, fn, callback) {
|
|
||||||
if (this.state.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.clearAfterFlag) {
|
|
||||||
this._benchmark(
|
|
||||||
"clear",
|
|
||||||
() => {
|
|
||||||
this.state.messages = [];
|
|
||||||
},
|
|
||||||
finalize,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_benchmark(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
fn();
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessages(n) {
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const newMessages = buildData(n);
|
|
||||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._benchmark("clear", () => {
|
|
||||||
this.state.messages = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSomeMessages() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const messages = this.state.messages;
|
|
||||||
for (let i = 0; i < this.state.messages.length; i += 10) {
|
|
||||||
const msg = Object.assign({}, messages[i]);
|
|
||||||
msg.author += "!!!";
|
|
||||||
this.set(messages, i, msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessage(event) {
|
|
||||||
this.benchmark("remove message", () => {
|
|
||||||
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
|
|
||||||
this.state.messages.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.refs.log.appendChild(div);
|
|
||||||
this.refs.log.scrollTop = this.refs.log.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLog() {
|
|
||||||
this.refs.log.innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleMultiple() {
|
|
||||||
this.state.multipleFlag = !this.state.multipleFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleClear() {
|
|
||||||
this.state.clearAfterFlag = !this.state.clearAfterFlag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
const templates = await owl.utils.loadTemplates("templates.xml");
|
|
||||||
const env = {
|
|
||||||
qweb: new owl.QWeb(templates)
|
|
||||||
};
|
|
||||||
const app = new App(env);
|
|
||||||
app.mount(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OWL 0.13.0 Benchmark</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='owl.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='main'></div>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div t-name="App" class="main">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
|
||||||
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
|
||||||
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
|
||||||
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
|
||||||
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
|
||||||
<button t-on-click="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
|
||||||
<hr/>
|
|
||||||
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content" t-ref="log"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content" t-on-remove-message="removeMessage">
|
|
||||||
<t t-foreach="state.messages" t-as="message">
|
|
||||||
<t t-widget="Message" t-key="message.id" message="message"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Message" class="message">
|
|
||||||
<span class="author"><t t-esc="props.message.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="props.message.msg"/></span>
|
|
||||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
|
||||||
<t t-widget="Counter"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Counter">
|
|
||||||
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Counter extends owl.Component {
|
|
||||||
state = { counter: 0 };
|
|
||||||
|
|
||||||
increment() {
|
|
||||||
this.state.counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Message extends owl.Component {
|
|
||||||
widgets = { Counter };
|
|
||||||
|
|
||||||
shouldUpdate(nextProps) {
|
|
||||||
return nextProps.message !== this.props.message;
|
|
||||||
}
|
|
||||||
removeMessage() {
|
|
||||||
this.trigger("remove-message", {
|
|
||||||
id: this.props.message.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class App extends owl.Component {
|
|
||||||
widgets = { Message };
|
|
||||||
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(message, fn, callback) {
|
|
||||||
if (this.state.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.clearAfterFlag) {
|
|
||||||
this._benchmark(
|
|
||||||
"clear",
|
|
||||||
() => {
|
|
||||||
this.state.messages = [];
|
|
||||||
},
|
|
||||||
finalize,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_benchmark(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
fn();
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessages(n) {
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const newMessages = buildData(n);
|
|
||||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._benchmark("clear", () => {
|
|
||||||
this.state.messages = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSomeMessages() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const messages = this.state.messages;
|
|
||||||
for (let i = 0; i < this.state.messages.length; i += 10) {
|
|
||||||
const msg = Object.assign({}, messages[i]);
|
|
||||||
msg.author += "!!!";
|
|
||||||
this.set(messages, i, msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessage(event) {
|
|
||||||
this.benchmark("remove message", () => {
|
|
||||||
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
|
|
||||||
this.state.messages.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.refs.log.appendChild(div);
|
|
||||||
this.refs.log.scrollTop = this.refs.log.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLog() {
|
|
||||||
this.refs.log.innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleMultiple() {
|
|
||||||
this.state.multipleFlag = !this.state.multipleFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleClear() {
|
|
||||||
this.state.clearAfterFlag = !this.state.clearAfterFlag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
const templates = await owl.utils.loadTemplates("templates.xml");
|
|
||||||
const env = {
|
|
||||||
qweb: new owl.QWeb(templates)
|
|
||||||
};
|
|
||||||
const app = new App(env);
|
|
||||||
app.mount(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OWL 0.15.0 Benchmark</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='owl.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='main'></div>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div t-name="App" class="main">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
|
||||||
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
|
||||||
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
|
||||||
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
|
||||||
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
|
||||||
<button t-on-click="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
|
||||||
<hr/>
|
|
||||||
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content" t-ref="log"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content" t-on-remove-message="removeMessage">
|
|
||||||
<t t-foreach="state.messages" t-as="message">
|
|
||||||
<t t-widget="Message" t-key="message.id" message="message"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Message" class="message">
|
|
||||||
<span class="author"><t t-esc="props.message.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="props.message.msg"/></span>
|
|
||||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
|
||||||
<t t-widget="Counter"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Counter">
|
|
||||||
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Counter extends owl.Component {
|
|
||||||
state = { counter: 0 };
|
|
||||||
|
|
||||||
increment() {
|
|
||||||
this.state.counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Message extends owl.Component {
|
|
||||||
components = { Counter };
|
|
||||||
|
|
||||||
shouldUpdate(nextProps) {
|
|
||||||
return nextProps.message !== this.props.message;
|
|
||||||
}
|
|
||||||
removeMessage() {
|
|
||||||
this.trigger("remove-message", {
|
|
||||||
id: this.props.message.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class App extends owl.Component {
|
|
||||||
components = { Message };
|
|
||||||
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(message, fn, callback) {
|
|
||||||
if (this.state.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.clearAfterFlag) {
|
|
||||||
this._benchmark(
|
|
||||||
"clear",
|
|
||||||
() => {
|
|
||||||
this.state.messages = [];
|
|
||||||
},
|
|
||||||
finalize,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_benchmark(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
fn();
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessages(n) {
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const newMessages = buildData(n);
|
|
||||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._benchmark("clear", () => {
|
|
||||||
this.state.messages = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSomeMessages() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const messages = this.state.messages;
|
|
||||||
for (let i = 0; i < this.state.messages.length; i += 10) {
|
|
||||||
const msg = Object.assign({}, messages[i]);
|
|
||||||
msg.author += "!!!";
|
|
||||||
owl.Observer.set(messages, i, msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessage(event) {
|
|
||||||
this.benchmark("remove message", () => {
|
|
||||||
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
|
|
||||||
this.state.messages.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.refs.log.appendChild(div);
|
|
||||||
this.refs.log.scrollTop = this.refs.log.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLog() {
|
|
||||||
this.refs.log.innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleMultiple() {
|
|
||||||
this.state.multipleFlag = !this.state.multipleFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleClear() {
|
|
||||||
this.state.clearAfterFlag = !this.state.clearAfterFlag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
const templates = await owl.utils.loadTemplates("templates.xml");
|
|
||||||
const env = {
|
|
||||||
qweb: new owl.QWeb(templates)
|
|
||||||
};
|
|
||||||
const app = new App(env);
|
|
||||||
app.mount(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OWL 0.17.0 Benchmark</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='owl.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='main'></div>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div t-name="App" class="main">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
|
||||||
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
|
||||||
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
|
||||||
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
|
||||||
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
|
||||||
<button t-on-click="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
|
||||||
<hr/>
|
|
||||||
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content" t-ref="log"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content" t-on-remove-message="removeMessage">
|
|
||||||
<t t-foreach="state.messages" t-as="message">
|
|
||||||
<Message t-key="message.id" message="message"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Message" class="message">
|
|
||||||
<span class="author"><t t-esc="props.message.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="props.message.msg"/></span>
|
|
||||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
|
||||||
<Counter/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Counter">
|
|
||||||
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Counter extends owl.Component {
|
|
||||||
state = { counter: 0 };
|
|
||||||
|
|
||||||
increment() {
|
|
||||||
this.state.counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Message extends owl.Component {
|
|
||||||
components = { Counter };
|
|
||||||
|
|
||||||
shouldUpdate(nextProps) {
|
|
||||||
return nextProps.message !== this.props.message;
|
|
||||||
}
|
|
||||||
removeMessage() {
|
|
||||||
this.trigger("remove-message", {
|
|
||||||
id: this.props.message.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class App extends owl.Component {
|
|
||||||
components = { Message };
|
|
||||||
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(message, fn, callback) {
|
|
||||||
if (this.state.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.clearAfterFlag) {
|
|
||||||
this._benchmark(
|
|
||||||
"clear",
|
|
||||||
() => {
|
|
||||||
this.state.messages = [];
|
|
||||||
},
|
|
||||||
finalize,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_benchmark(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
fn();
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessages(n) {
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const newMessages = buildData(n);
|
|
||||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._benchmark("clear", () => {
|
|
||||||
this.state.messages = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSomeMessages() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const messages = this.state.messages;
|
|
||||||
for (let i = 0; i < this.state.messages.length; i += 10) {
|
|
||||||
const msg = Object.assign({}, messages[i]);
|
|
||||||
msg.author += "!!!";
|
|
||||||
messages[i] = msg;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessage(event) {
|
|
||||||
this.benchmark("remove message", () => {
|
|
||||||
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
|
|
||||||
this.state.messages.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.refs.log.appendChild(div);
|
|
||||||
this.refs.log.scrollTop = this.refs.log.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLog() {
|
|
||||||
this.refs.log.innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleMultiple() {
|
|
||||||
this.state.multipleFlag = !this.state.multipleFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleClear() {
|
|
||||||
this.state.clearAfterFlag = !this.state.clearAfterFlag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
const templates = await owl.utils.loadTemplates("templates.xml");
|
|
||||||
const env = {
|
|
||||||
qweb: new owl.QWeb(templates)
|
|
||||||
};
|
|
||||||
const app = new App(env);
|
|
||||||
app.mount(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OWL 0.18.0 Benchmark</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='owl.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='main'></div>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div t-name="App" class="main">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
|
||||||
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
|
||||||
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
|
||||||
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
|
||||||
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
|
||||||
<button t-on-click="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
|
||||||
<hr/>
|
|
||||||
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content" t-ref="log"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content" t-on-remove-message="removeMessage">
|
|
||||||
<t t-foreach="state.messages" t-as="message">
|
|
||||||
<Message t-key="message.id" message="message"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Message" class="message">
|
|
||||||
<span class="author"><t t-esc="props.message.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="props.message.msg"/></span>
|
|
||||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
|
||||||
<Counter/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Counter">
|
|
||||||
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Counter extends owl.Component {
|
|
||||||
state = { counter: 0 };
|
|
||||||
|
|
||||||
increment() {
|
|
||||||
this.state.counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Message extends owl.Component {
|
|
||||||
shouldUpdate(nextProps) {
|
|
||||||
return nextProps.message !== this.props.message;
|
|
||||||
}
|
|
||||||
removeMessage() {
|
|
||||||
this.trigger("remove-message", {
|
|
||||||
id: this.props.message.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message.components = { Counter };
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class App extends owl.Component {
|
|
||||||
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(message, fn, callback) {
|
|
||||||
if (this.state.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.clearAfterFlag) {
|
|
||||||
this._benchmark(
|
|
||||||
"clear",
|
|
||||||
() => {
|
|
||||||
this.state.messages = [];
|
|
||||||
},
|
|
||||||
finalize,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_benchmark(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
fn();
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessages(n) {
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const newMessages = buildData(n);
|
|
||||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._benchmark("clear", () => {
|
|
||||||
this.state.messages = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSomeMessages() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const messages = this.state.messages;
|
|
||||||
for (let i = 0; i < messages.length; i += 10) {
|
|
||||||
const msg = Object.assign({}, messages[i]);
|
|
||||||
msg.author += "!!!";
|
|
||||||
messages[i] = msg;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessage(event) {
|
|
||||||
this.benchmark("remove message", () => {
|
|
||||||
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
|
|
||||||
this.state.messages.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.refs.log.appendChild(div);
|
|
||||||
this.refs.log.scrollTop = this.refs.log.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLog() {
|
|
||||||
this.refs.log.innerHTML = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
App.components = { Message };
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
const templates = await owl.utils.loadTemplates("templates.xml");
|
|
||||||
const env = {
|
|
||||||
qweb: new owl.QWeb(templates)
|
|
||||||
};
|
|
||||||
const app = new App(env);
|
|
||||||
app.mount(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OWL 0.21.0 Benchmark</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='owl.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div t-name="App" class="main">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
|
||||||
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
|
||||||
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
|
||||||
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
|
||||||
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
|
||||||
<button t-on-click="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="multipleflag" t-model="multipleFlag"/>
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="clearFlag" t-model="clearAfterFlag" />
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
|
||||||
<hr/>
|
|
||||||
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content" t-ref="log"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content" t-on-remove-message="removeMessage">
|
|
||||||
<t t-foreach="state.messages" t-as="message">
|
|
||||||
<Message t-key="message.id" message="message"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Message" class="message">
|
|
||||||
<span class="author"><t t-esc="props.message.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="props.message.msg"/></span>
|
|
||||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
|
||||||
<Counter/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Counter">
|
|
||||||
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
|
|
||||||
|
|
||||||
const { useState, useRef } = owl.hooks;
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Counter extends owl.Component {
|
|
||||||
state = useState({ counter: 0 });
|
|
||||||
|
|
||||||
increment() {
|
|
||||||
this.state.counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Message extends owl.Component {
|
|
||||||
static components = { Counter };
|
|
||||||
|
|
||||||
shouldUpdate(nextProps) {
|
|
||||||
return nextProps.message !== this.props.message;
|
|
||||||
}
|
|
||||||
removeMessage() {
|
|
||||||
this.trigger("remove-message", {
|
|
||||||
id: this.props.message.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class App extends owl.Component {
|
|
||||||
static components = { Message };
|
|
||||||
state = useState({ messages: [], multipleFlag: false, clearAfterFlag: false });
|
|
||||||
logRef = useRef("log");
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(message, fn, callback) {
|
|
||||||
if (this.state.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.clearAfterFlag) {
|
|
||||||
this._benchmark(
|
|
||||||
"clear",
|
|
||||||
() => {
|
|
||||||
this.state.messages = [];
|
|
||||||
},
|
|
||||||
finalize,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_benchmark(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
fn();
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessages(n) {
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const newMessages = buildData(n);
|
|
||||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._benchmark("clear", () => {
|
|
||||||
this.state.messages = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSomeMessages() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const messages = this.state.messages;
|
|
||||||
for (let i = 0; i < messages.length; i += 10) {
|
|
||||||
const msg = Object.assign({}, messages[i]);
|
|
||||||
msg.author += "!!!";
|
|
||||||
messages[i] = msg;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessage(event) {
|
|
||||||
this.benchmark("remove message", () => {
|
|
||||||
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
|
|
||||||
this.state.messages.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.logRef.el.appendChild(div);
|
|
||||||
this.logRef.el.scrollTop = this.logRef.el.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLog() {
|
|
||||||
this.logRef.el.innerHTML = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
const templates = await owl.utils.loadFile("templates.xml");
|
|
||||||
const env = {
|
|
||||||
qweb: new owl.QWeb(templates)
|
|
||||||
};
|
|
||||||
const app = new App(env);
|
|
||||||
app.mount(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OWL v0.24.0 Benchmark</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='owl.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div t-name="App" class="main">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
|
||||||
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
|
||||||
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
|
||||||
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
|
||||||
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
|
||||||
<button t-on-click="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="multipleflag" t-model="state.multipleFlag"/>
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="clearFlag" t-model="state.clearAfterFlag" />
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
|
||||||
<hr/>
|
|
||||||
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content" t-ref="log"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content" t-on-remove-message="removeMessage">
|
|
||||||
<t t-foreach="state.messages" t-as="message">
|
|
||||||
<Message t-key="message.id" message="message"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Message" class="message">
|
|
||||||
<span class="author"><t t-esc="props.message.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="props.message.msg"/></span>
|
|
||||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
|
||||||
<Counter/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Counter">
|
|
||||||
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
import {
|
|
||||||
buildData,
|
|
||||||
startMeasure,
|
|
||||||
stopMeasure,
|
|
||||||
formatNumber
|
|
||||||
} from "../shared/utils.js";
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Counter extends owl.Component {
|
|
||||||
state = { counter: 0 };
|
|
||||||
template = "Counter";
|
|
||||||
|
|
||||||
increment() {
|
|
||||||
this.state.counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Message extends owl.Component {
|
|
||||||
widgets = { Counter };
|
|
||||||
template = "Message";
|
|
||||||
|
|
||||||
shouldUpdate(nextProps) {
|
|
||||||
return nextProps !== this.props;
|
|
||||||
}
|
|
||||||
removeMessage() {
|
|
||||||
this.trigger("remove_message", {
|
|
||||||
id: this.props.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class App extends owl.Component {
|
|
||||||
widgets = { Message };
|
|
||||||
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
|
|
||||||
template = "App";
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.log(
|
|
||||||
`Benchmarking Owl v${owl._version} (build date: ${
|
|
||||||
owl._date
|
|
||||||
})`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(message, fn, callback) {
|
|
||||||
if (this.state.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.clearAfterFlag) {
|
|
||||||
this._benchmark(
|
|
||||||
"clear",
|
|
||||||
() => {
|
|
||||||
this.state.messages = [];
|
|
||||||
},
|
|
||||||
finalize,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_benchmark(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
fn();
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessages(n) {
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const newMessages = buildData(n);
|
|
||||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._benchmark("clear", () => {
|
|
||||||
this.state.messages = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSomeMessages() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const messages = this.state.messages;
|
|
||||||
for (let i = 0; i < this.state.messages.length; i += 10) {
|
|
||||||
const msg = Object.assign({}, messages[i]);
|
|
||||||
msg.author += "!!!";
|
|
||||||
this.set(messages, i, msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessage(data) {
|
|
||||||
this.benchmark("remove message", () => {
|
|
||||||
const index = this.state.messages.findIndex(m => m.id === data.id);
|
|
||||||
this.state.messages.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.refs.log.appendChild(div);
|
|
||||||
this.refs.log.scrollTop = this.refs.log.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLog() {
|
|
||||||
this.refs.log.innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleMultiple() {
|
|
||||||
this.state.multipleFlag = !this.state.multipleFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleClear() {
|
|
||||||
this.state.clearAfterFlag = !this.state.clearAfterFlag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
const templates = await owl.utils.loadTemplates("templates.xml");
|
|
||||||
const env = {
|
|
||||||
qweb: new owl.QWeb(templates)
|
|
||||||
};
|
|
||||||
const app = new App(env);
|
|
||||||
app.mount(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OWL 0.7.0 Benchmark</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='owl.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='main'></div>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div t-name="App" class="main">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
|
||||||
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
|
||||||
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
|
||||||
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
|
||||||
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
|
||||||
<button t-on-click="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
|
||||||
<hr/>
|
|
||||||
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content" t-ref="'log'"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content" t-on-remove-message="removeMessage">
|
|
||||||
<t t-foreach="state.messages" t-as="message">
|
|
||||||
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Message" class="message">
|
|
||||||
<span class="author"><t t-esc="props.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="props.msg"/></span>
|
|
||||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
|
||||||
<t t-widget="Counter"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Counter">
|
|
||||||
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
import {
|
|
||||||
buildData,
|
|
||||||
startMeasure,
|
|
||||||
stopMeasure,
|
|
||||||
formatNumber
|
|
||||||
} from "../shared/utils.js";
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Counter extends owl.Component {
|
|
||||||
state = { counter: 0 };
|
|
||||||
template = "Counter";
|
|
||||||
|
|
||||||
increment() {
|
|
||||||
this.state.counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Message extends owl.Component {
|
|
||||||
widgets = { Counter };
|
|
||||||
template = "Message";
|
|
||||||
|
|
||||||
shouldUpdate(nextProps) {
|
|
||||||
return nextProps !== this.props;
|
|
||||||
}
|
|
||||||
removeMessage() {
|
|
||||||
this.trigger("remove_message", {
|
|
||||||
id: this.props.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class App extends owl.Component {
|
|
||||||
widgets = { Message };
|
|
||||||
state = { messages: [], multipleFlag: false, clearAfterFlag: false };
|
|
||||||
template = "App";
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.log(
|
|
||||||
`Benchmarking Owl v${owl._version} (build date: ${
|
|
||||||
owl._date
|
|
||||||
})`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(message, fn, callback) {
|
|
||||||
if (this.state.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.clearAfterFlag) {
|
|
||||||
this._benchmark(
|
|
||||||
"clear",
|
|
||||||
() => {
|
|
||||||
this.state.messages = [];
|
|
||||||
},
|
|
||||||
finalize,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_benchmark(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
fn();
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessages(n) {
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const newMessages = buildData(n);
|
|
||||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._benchmark("clear", () => {
|
|
||||||
this.state.messages = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSomeMessages() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const messages = this.state.messages;
|
|
||||||
for (let i = 0; i < this.state.messages.length; i += 10) {
|
|
||||||
const msg = Object.assign({}, messages[i]);
|
|
||||||
msg.author += "!!!";
|
|
||||||
this.set(messages, i, msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessage(data) {
|
|
||||||
this.benchmark("remove message", () => {
|
|
||||||
const index = this.state.messages.findIndex(m => m.id === data.id);
|
|
||||||
this.state.messages.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.refs.log.appendChild(div);
|
|
||||||
this.refs.log.scrollTop = this.refs.log.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLog() {
|
|
||||||
this.refs.log.innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleMultiple() {
|
|
||||||
this.state.multipleFlag = !this.state.multipleFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleClear() {
|
|
||||||
this.state.clearAfterFlag = !this.state.clearAfterFlag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
const templates = await owl.utils.loadTemplates("templates.xml");
|
|
||||||
const env = {
|
|
||||||
qweb: new owl.QWeb(templates)
|
|
||||||
};
|
|
||||||
const app = new App(env);
|
|
||||||
app.mount(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OWL 0.9.0 Benchmark</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='owl.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='main'></div>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div t-name="App" class="main">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
|
||||||
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
|
||||||
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
|
||||||
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
|
||||||
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
|
||||||
<button t-on-click="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="multipleflag" t-on-change="toggleMultiple" t-att-checked="state.multipleFlag"/>
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="clearFlag" t-on-change="toggleClear" t-att-checked="state.clearAfterFlag"/>
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
|
||||||
<hr/>
|
|
||||||
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content" t-ref="'log'"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content" t-on-remove-message="removeMessage">
|
|
||||||
<t t-foreach="state.messages" t-as="message">
|
|
||||||
<t t-widget="Message" t-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Message" class="message">
|
|
||||||
<span class="author"><t t-esc="props.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="props.msg"/></span>
|
|
||||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
|
||||||
<t t-widget="Counter"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Counter">
|
|
||||||
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
|
|
||||||
|
|
||||||
const { useState, useRef } = owl.hooks;
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Counter extends owl.Component {
|
|
||||||
state = useState({ counter: 0 });
|
|
||||||
|
|
||||||
increment() {
|
|
||||||
this.state.counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Message extends owl.Component {
|
|
||||||
shouldUpdate(nextProps) {
|
|
||||||
return nextProps.message !== this.props.message;
|
|
||||||
}
|
|
||||||
removeMessage() {
|
|
||||||
this.trigger("remove-message", {
|
|
||||||
id: this.props.message.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message.components = { Counter };
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class App extends owl.Component {
|
|
||||||
state = useState({ messages: [], multipleFlag: false, clearAfterFlag: false });
|
|
||||||
logRef = useRef("log");
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(message, fn, callback) {
|
|
||||||
if (this.state.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.clearAfterFlag) {
|
|
||||||
this._benchmark(
|
|
||||||
"clear",
|
|
||||||
() => {
|
|
||||||
this.state.messages = [];
|
|
||||||
},
|
|
||||||
finalize,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_benchmark(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
fn();
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessages(n) {
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const newMessages = buildData(n);
|
|
||||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._benchmark("clear", () => {
|
|
||||||
this.state.messages = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSomeMessages() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const messages = this.state.messages;
|
|
||||||
for (let i = 0; i < messages.length; i += 10) {
|
|
||||||
const msg = Object.assign({}, messages[i]);
|
|
||||||
msg.author += "!!!";
|
|
||||||
messages[i] = msg;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessage(event) {
|
|
||||||
this.benchmark("remove message", () => {
|
|
||||||
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
|
|
||||||
this.state.messages.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.logRef.el.appendChild(div);
|
|
||||||
this.logRef.el.scrollTop = this.logRef.el.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLog() {
|
|
||||||
this.logRef.el.innerHTML = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
App.components = { Message };
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
const templates = await owl.utils.loadFile("templates.xml");
|
|
||||||
App.env = {
|
|
||||||
qweb: new owl.QWeb({ templates })
|
|
||||||
};
|
|
||||||
const app = new App();
|
|
||||||
app.mount(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OWL v1.0.0-beta1 Benchmark</title>
|
|
||||||
<link href="../shared/main.css" rel="stylesheet"/>
|
|
||||||
<script src='owl.js'></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script src='app.js' type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
|||||||
<templates>
|
|
||||||
<div t-name="App" class="main">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="title">Actions</div>
|
|
||||||
<div class="panel">
|
|
||||||
<button t-on-click="addMessages(100)">Add 100 messages</button>
|
|
||||||
<button t-on-click="addMessages(1000)">Add 1k messages</button>
|
|
||||||
<button t-on-click="addMessages(10000)">Add 10k messages</button>
|
|
||||||
<button t-on-click="addMessages(30000)">Add 30k messages</button>
|
|
||||||
<button t-on-click="updateSomeMessages">Update every 10th messages</button>
|
|
||||||
<button t-on-click="clear">Clear</button>
|
|
||||||
</div>
|
|
||||||
<div class="flags">
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="multipleflag" t-model="state.multipleFlag"/>
|
|
||||||
<label for="multipleflag">Do it 20x</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" id="clearFlag" t-model="state.clearAfterFlag" />
|
|
||||||
<label for="clearFlag">Clear after</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info">Number of messages: <t t-esc="state.messages.length"/></div>
|
|
||||||
<hr/>
|
|
||||||
<div class="title">Log <span class="clear-log" t-on-click="clearLog">(clear)</span></div>
|
|
||||||
<div class="log">
|
|
||||||
<div class="log-content" t-ref="log"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content" t-on-remove-message="removeMessage">
|
|
||||||
<t t-foreach="state.messages" t-as="message">
|
|
||||||
<Message t-key="message.id" message="message"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Message" class="message">
|
|
||||||
<span class="author"><t t-esc="props.message.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="props.message.msg"/></span>
|
|
||||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
|
||||||
<Counter/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="Counter">
|
|
||||||
<button t-on-click="increment">Value: <t t-esc="state.counter"/></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</templates>
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
|
|
||||||
|
|
||||||
const { useState, useRef } = owl.hooks;
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Likes Counter Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Counter extends owl.Component {
|
|
||||||
state = useState({ counter: 0 });
|
|
||||||
|
|
||||||
increment() {
|
|
||||||
this.state.counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Message Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class Message extends owl.Component {
|
|
||||||
shouldUpdate(nextProps) {
|
|
||||||
return nextProps.message !== this.props.message;
|
|
||||||
}
|
|
||||||
removeMessage() {
|
|
||||||
this.trigger("remove-message", {
|
|
||||||
id: this.props.message.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message.components = { Counter };
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Root Widget
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class App extends owl.Component {
|
|
||||||
state = useState({ messages: [], multipleFlag: false, clearAfterFlag: false });
|
|
||||||
logRef = useRef("log");
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark(message, fn, callback) {
|
|
||||||
if (this.state.multipleFlag) {
|
|
||||||
const N = 20;
|
|
||||||
let n = N;
|
|
||||||
let total = 0;
|
|
||||||
let cb = info => {
|
|
||||||
let finalize = () => {
|
|
||||||
n--;
|
|
||||||
total += info.delta;
|
|
||||||
if (n === 0) {
|
|
||||||
const avg = total / N;
|
|
||||||
this.log(`Average: ${formatNumber(avg)}ms`, true);
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.clearAfterFlag) {
|
|
||||||
this._benchmark(
|
|
||||||
"clear",
|
|
||||||
() => {
|
|
||||||
this.state.messages = [];
|
|
||||||
},
|
|
||||||
finalize,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
finalize();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._benchmark(message, fn, cb);
|
|
||||||
} else {
|
|
||||||
this._benchmark(message, fn, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_benchmark(message, fn, cb, log = true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
startMeasure(message);
|
|
||||||
fn();
|
|
||||||
stopMeasure(info => {
|
|
||||||
if (log) {
|
|
||||||
this.log(info.msg);
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessages(n) {
|
|
||||||
this.benchmark("add " + n, () => {
|
|
||||||
const newMessages = buildData(n);
|
|
||||||
this.state.messages.push.apply(this.state.messages, newMessages);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._benchmark("clear", () => {
|
|
||||||
this.state.messages = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSomeMessages() {
|
|
||||||
this.benchmark("update every 10th", () => {
|
|
||||||
const messages = this.state.messages;
|
|
||||||
for (let i = 0; i < messages.length; i += 10) {
|
|
||||||
const msg = Object.assign({}, messages[i]);
|
|
||||||
msg.author += "!!!";
|
|
||||||
messages[i] = msg;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessage(event) {
|
|
||||||
this.benchmark("remove message", () => {
|
|
||||||
const index = this.state.messages.findIndex(m => m.id === event.detail.id);
|
|
||||||
this.state.messages.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log(str, isBold) {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
if (isBold) {
|
|
||||||
div.classList.add("bold");
|
|
||||||
}
|
|
||||||
div.textContent = `> ${str}`;
|
|
||||||
this.logRef.el.appendChild(div);
|
|
||||||
this.logRef.el.scrollTop = this.logRef.el.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLog() {
|
|
||||||
this.logRef.el.innerHTML = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
App.components = { Message };
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Application initialization
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
async function start() {
|
|
||||||
const templates = await owl.utils.loadFile("templates.xml");
|
|
||||||
App.env = {
|
|
||||||
qweb: new owl.QWeb({ templates })
|
|
||||||
};
|
|
||||||
const app = new App();
|
|
||||||
app.mount(document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user