mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3fe8d9966 | |||
| f6634df72c |
@@ -102,16 +102,10 @@ Submit a PR!
|
|||||||
|
|
||||||
## Installing/Building
|
## Installing/Building
|
||||||
|
|
||||||
Owl can be installed with the following command:
|
|
||||||
|
|
||||||
```
|
|
||||||
npm install @odoo/owl
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
||||||
|
|
||||||
- [owl-1.0.0-beta5.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta5/owl.js)
|
- [owl-1.0.0-beta2.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta2/owl.js)
|
||||||
- [owl-1.0.0-beta5.min.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta5/owl.min.js)
|
- [owl-1.0.0-beta2.min.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta2/owl.min.js)
|
||||||
|
|
||||||
Some npm scripts are available:
|
Some npm scripts are available:
|
||||||
|
|
||||||
|
|||||||
+9
-9
@@ -56,15 +56,15 @@ useState Link
|
|||||||
config RouteComponent
|
config RouteComponent
|
||||||
mode Router
|
mode Router
|
||||||
core tags
|
core tags
|
||||||
EventBus css
|
EventBus xml
|
||||||
Observer xml
|
Observer utils
|
||||||
hooks utils
|
hooks debounce
|
||||||
onWillStart debounce
|
onWillStart escape
|
||||||
onMounted escape
|
onMounted loadJS
|
||||||
onWillUpdateProps loadJS
|
onWillUpdateProps loadFile
|
||||||
onWillPatch loadFile
|
onWillPatch shallowEqual
|
||||||
onPatched shallowEqual
|
onPatched whenReady
|
||||||
onWillUnmount whenReady
|
onWillUnmount
|
||||||
useContext
|
useContext
|
||||||
useState
|
useState
|
||||||
useRef
|
useRef
|
||||||
|
|||||||
@@ -225,10 +225,6 @@ to be called in the constructor.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- **`style`** (string, optional): it should be the return value of the [`css tag](tags.md#css-tag),
|
|
||||||
which is used to inject stylesheet whenever the component is visible on the
|
|
||||||
screen.
|
|
||||||
|
|
||||||
There is another static property defined on the `Component` class: `current`.
|
There is another static property defined on the `Component` class: `current`.
|
||||||
This property is set to the currently being defined component (in the constructor).
|
This property is set to the currently being defined component (in the constructor).
|
||||||
This is the way [hooks](hooks.md) are able to get a reference to the target
|
This is the way [hooks](hooks.md) are able to get a reference to the target
|
||||||
|
|||||||
+4
-88
@@ -4,19 +4,16 @@
|
|||||||
|
|
||||||
- [Overview](#overview)
|
- [Overview](#overview)
|
||||||
- [`xml` tag](#xml-tag)
|
- [`xml` tag](#xml-tag)
|
||||||
- [`css` tag](#css-tag)
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Tags are very small helpers intended to make it easy to write inline templates
|
Tags are very small helpers to make it easy to write inline templates. There is
|
||||||
or styles. There are currently two tags: `css` and `xml`. With these functions,
|
only one currently available tag: `xml`, but we plan to add other tags later,
|
||||||
it is possible to write [single file components](../tooling.md#single-file-component).
|
such as a `css` tag, which will be used to write [single file components](../tooling.md#single-file-component).
|
||||||
|
|
||||||
## XML tag
|
## XML tag
|
||||||
|
|
||||||
The `xml` tag is certainly the most useful tag. It is used to define an inline
|
Without tags, creating a standalone component would look like this:
|
||||||
QWeb template for a component. Without tags, creating a standalone component
|
|
||||||
would look like this:
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { Component } from 'owl'
|
import { Component } from 'owl'
|
||||||
@@ -55,84 +52,3 @@ class MyComponent extends Component {
|
|||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## CSS tag
|
|
||||||
|
|
||||||
The CSS tag is useful to define a css stylesheet in the javascript file:
|
|
||||||
|
|
||||||
```js
|
|
||||||
class MyComponent extends Component {
|
|
||||||
static template = xml`
|
|
||||||
<div class="my-component">some template</div>
|
|
||||||
`;
|
|
||||||
static css`
|
|
||||||
.my-component {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `css` tag registers internally the css information. Then, whenever the first
|
|
||||||
instance of the component is created, will add a `<style>` tag to the document
|
|
||||||
`<head>`.
|
|
||||||
|
|
||||||
Note that to make it more useful, like other css preprocessors, the `css` tag
|
|
||||||
accepts a small extension of the css specification: css scopes can be nested,
|
|
||||||
and the rules will then be expanded by the `css` helper:
|
|
||||||
|
|
||||||
```scss
|
|
||||||
.my-component {
|
|
||||||
display: block;
|
|
||||||
.sub-component h {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
will be formatted as:
|
|
||||||
|
|
||||||
```css
|
|
||||||
.my-component {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.my-component .sub-component h {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, there is no additional processing done by the `css` tag. However, since it
|
|
||||||
is done in javascript at runtime, we actually have more power. For example:
|
|
||||||
|
|
||||||
1. sharing values between javascript and css:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { theme } from "./theme";
|
|
||||||
|
|
||||||
class MyComponent extends Component {
|
|
||||||
static template = xml`<div class="my-component">...</div>`;
|
|
||||||
static style = css`
|
|
||||||
.my-component {
|
|
||||||
color: ${theme.MAIN_COLOR};
|
|
||||||
background-color: ${theme.SECONDARY_color};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. scoping rules to the current component:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { generateUUID } from "./utils";
|
|
||||||
|
|
||||||
const uuid = generateUUID();
|
|
||||||
|
|
||||||
class MyComponent extends Component {
|
|
||||||
static template = xml`<div data-o-${uuid}="">...</div>`;
|
|
||||||
static style = css`
|
|
||||||
[data-o-${uuid}] {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|||||||
+9
-20
@@ -43,43 +43,32 @@ useful to compare various performance metrics on some tasks.
|
|||||||
It is very useful to group code by feature instead of by type of file. It makes
|
It is very useful to group code by feature instead of by type of file. It makes
|
||||||
it easier to scale application to larger size.
|
it easier to scale application to larger size.
|
||||||
|
|
||||||
To do so, Owl has two small helpers that make it easy to define a
|
To do so, Owl currently has a small helper that makes it easy to define a
|
||||||
template or a stylesheet inside a javascript (or typescript) file: the
|
template inside a javascript (or typescript) file: the [`xml`](reference/tags.md#xml-tag)
|
||||||
[`xml`](reference/tags.md#xml-tag) and [`css`](reference/tags.md#css-tag)
|
helper. With this, a template is automatically registered to [QWeb](reference/qweb_engine.md).
|
||||||
helper.
|
|
||||||
|
|
||||||
This means that the template, the style and the javascript code can be defined in
|
This means that the template and the javascript code can be defined in the same
|
||||||
the same file. For example:
|
file. It is not currently possible to add css to the same file, but Owl may
|
||||||
|
get a `css` tag helper later.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { Component } = owl;
|
const { Component } = owl;
|
||||||
const { xml, css } = owl.tags;
|
const { xml } = owl.tags;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// TEMPLATE
|
// TEMPLATE
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
const TEMPLATE = xml/* xml */ `
|
const TEMPLATE = xml/* xml */ `
|
||||||
<div class="main">
|
<div class="main two-columns">
|
||||||
<Sidebar/>
|
<Sidebar/>
|
||||||
<Content />
|
<Content />
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// STYLE
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
const STYLE = css/* css */ `
|
|
||||||
.main {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 200px auto;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// CODE
|
// CODE
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
class Main extends Component {
|
class MyComponent extends Component {
|
||||||
static template = TEMPLATE;
|
static template = TEMPLATE;
|
||||||
static style = STYLE;
|
|
||||||
static components = { Sidebar, Content };
|
static components = { Sidebar, Content };
|
||||||
|
|
||||||
// rest of component...
|
// rest of component...
|
||||||
|
|||||||
+3
-6
@@ -1,11 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "@odoo/owl",
|
"name": "owl-framework",
|
||||||
"version": "1.0.0-beta5",
|
"version": "1.0.0-beta2",
|
||||||
"description": "Odoo Web Library (OWL)",
|
"description": "Odoo Web Library (OWL)",
|
||||||
"main": "dist/owl/index.js",
|
"main": "src/index.ts",
|
||||||
"types": "dist/owl/index.d.ts",
|
|
||||||
"prepublish": "npm run build",
|
|
||||||
"files": ["dist/owl/"],
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.15.3"
|
"node": ">=10.15.3"
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# 🦉 OWL Roadmap 🦉
|
# 🦉 OWL Roadmap 🦉
|
||||||
|
|
||||||
- Current version: 1.0.0-beta5
|
- Current version: 1.0.0-beta2
|
||||||
- Status: mostly stable
|
- Status: mostly stable
|
||||||
|
|
||||||
This roadmap is only an attempt at predicting Owl's future. Everything may
|
This roadmap is only an attempt at predicting Owl's future. Everything may
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import "./directive";
|
|||||||
import { Fiber } from "./fiber";
|
import { Fiber } from "./fiber";
|
||||||
import "./props_validation";
|
import "./props_validation";
|
||||||
import { Scheduler, scheduler } from "./scheduler";
|
import { Scheduler, scheduler } from "./scheduler";
|
||||||
import { activateSheet } from "./styles";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Owl Component System
|
* Owl Component System
|
||||||
@@ -200,9 +199,6 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
refs: null,
|
refs: null,
|
||||||
scope: null
|
scope: null
|
||||||
};
|
};
|
||||||
if (constr.style) {
|
|
||||||
this.__applyStyles(constr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -584,20 +580,6 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
return fiber;
|
return fiber;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply the stylesheets defined by the component. Note that we need to make
|
|
||||||
* sure all inherited stylesheets are applied as well. We then delete the
|
|
||||||
* `style` key from the constructor to make sure we do not apply it again.
|
|
||||||
*/
|
|
||||||
private __applyStyles(constr) {
|
|
||||||
while (constr && constr.style) {
|
|
||||||
if (constr.hasOwnProperty("style")) {
|
|
||||||
activateSheet(constr.style, constr.name);
|
|
||||||
delete constr.style;
|
|
||||||
}
|
|
||||||
constr = constr.__proto__;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
__getTemplate(qweb: QWeb): string {
|
__getTemplate(qweb: QWeb): string {
|
||||||
let p = (<any>this).constructor;
|
let p = (<any>this).constructor;
|
||||||
if (!p.hasOwnProperty("_template")) {
|
if (!p.hasOwnProperty("_template")) {
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ QWeb.addDirective({
|
|||||||
} else if (!name.startsWith("t-")) {
|
} else if (!name.startsWith("t-")) {
|
||||||
if (name !== "class" && name !== "style") {
|
if (name !== "class" && name !== "style") {
|
||||||
// this is a prop!
|
// this is a prop!
|
||||||
props[name] = ctx.formatExpression(value) || "undefined";
|
props[name] = ctx.formatExpression(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
/**
|
|
||||||
* Owl Style System
|
|
||||||
*
|
|
||||||
* This files contains the Owl code related to processing (extended) css strings
|
|
||||||
* and creating/adding <style> tags to the document head.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const STYLESHEETS: { [id: string]: HTMLStyleElement } = {};
|
|
||||||
|
|
||||||
function processSheet(str: string): string {
|
|
||||||
const tokens = str.split(/(\{|\}|;)/).map(s => s.trim());
|
|
||||||
const selectorStack: string[] = [];
|
|
||||||
const parts: string[] = [];
|
|
||||||
let rules: string[] = [];
|
|
||||||
function generateRules() {
|
|
||||||
if (rules.length) {
|
|
||||||
parts.push(selectorStack.join(" ") + " {");
|
|
||||||
parts.push(...rules);
|
|
||||||
parts.push("}");
|
|
||||||
rules = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (tokens.length) {
|
|
||||||
let token = tokens.shift()!;
|
|
||||||
if (token === "}") {
|
|
||||||
generateRules();
|
|
||||||
selectorStack.pop();
|
|
||||||
} else {
|
|
||||||
if (tokens[0] === "{") {
|
|
||||||
generateRules();
|
|
||||||
selectorStack.push(token);
|
|
||||||
tokens.shift();
|
|
||||||
}
|
|
||||||
if (tokens[0] === ";") {
|
|
||||||
rules.push(" " + token + ";");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parts.join("\n");
|
|
||||||
}
|
|
||||||
export function registerSheet(id: string, css: string) {
|
|
||||||
const sheet = document.createElement("style");
|
|
||||||
sheet.innerHTML = processSheet(css);
|
|
||||||
STYLESHEETS[id] = sheet;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function activateSheet(id, name) {
|
|
||||||
const sheet = STYLESHEETS[id];
|
|
||||||
if (!sheet) {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid css stylesheet for component '${name}'. Did you forget to use the 'css' tag helper?`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
sheet.setAttribute("component", name);
|
|
||||||
document.head.appendChild(sheet);
|
|
||||||
}
|
|
||||||
@@ -29,6 +29,14 @@ export class Observer {
|
|||||||
// fun fact: typeof null === 'object'
|
// fun fact: typeof null === 'object'
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
value instanceof Map ||
|
||||||
|
value instanceof WeakMap ||
|
||||||
|
value instanceof Set ||
|
||||||
|
value instanceof WeakSet
|
||||||
|
) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
let metadata = this.weakMap.get(value) || this._observe(value, parent);
|
let metadata = this.weakMap.get(value) || this._observe(value, parent);
|
||||||
return metadata.proxy;
|
return metadata.proxy;
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-24
@@ -1,7 +1,6 @@
|
|||||||
import { CompilationContext } from "./compilation_context";
|
import { CompilationContext } from "./compilation_context";
|
||||||
import { QWeb } from "./qweb";
|
import { QWeb } from "./qweb";
|
||||||
import { htmlToVDOM } from "../vdom/html_to_vdom";
|
import { htmlToVDOM } from "../vdom/html_to_vdom";
|
||||||
import { QWebVar } from "./expression_parser";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Owl QWeb Directives
|
* Owl QWeb Directives
|
||||||
@@ -40,11 +39,11 @@ function compileValueNode(value: any, node: Element, qweb: QWeb, ctx: Compilatio
|
|||||||
let exprID: string;
|
let exprID: string;
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
exprID = `_${ctx.generateID()}`;
|
exprID = `_${ctx.generateID()}`;
|
||||||
ctx.addLine(`let ${exprID} = ${ctx.formatExpression(value)};`);
|
ctx.addLine(`var ${exprID} = ${ctx.formatExpression(value)};`);
|
||||||
} else {
|
} else {
|
||||||
exprID = `scope.${value.id}`;
|
exprID = `scope.${value.id}`;
|
||||||
}
|
}
|
||||||
ctx.addIf(`${exprID} != null`);
|
ctx.addIf(`${exprID} || ${exprID} === 0`);
|
||||||
|
|
||||||
if (ctx.escaping) {
|
if (ctx.escaping) {
|
||||||
let protectID;
|
let protectID;
|
||||||
@@ -62,7 +61,7 @@ function compileValueNode(value: any, node: Element, qweb: QWeb, ctx: Compilatio
|
|||||||
let nodeID = ctx.generateID();
|
let nodeID = ctx.generateID();
|
||||||
ctx.rootContext.rootNode = nodeID;
|
ctx.rootContext.rootNode = nodeID;
|
||||||
ctx.rootContext.parentTextNode = nodeID;
|
ctx.rootContext.parentTextNode = nodeID;
|
||||||
ctx.addLine(`let vn${nodeID} = {text: ${exprID}};`);
|
ctx.addLine(`var vn${nodeID} = {text: ${exprID}};`);
|
||||||
if (ctx.rootContext.shouldDefineResult) {
|
if (ctx.rootContext.shouldDefineResult) {
|
||||||
ctx.addLine(`result = vn${nodeID}`);
|
ctx.addLine(`result = vn${nodeID}`);
|
||||||
}
|
}
|
||||||
@@ -120,7 +119,7 @@ QWeb.addDirective({
|
|||||||
ctx.rootContext.shouldDefineScope = true;
|
ctx.rootContext.shouldDefineScope = true;
|
||||||
const variable = node.getAttribute("t-set")!;
|
const variable = node.getAttribute("t-set")!;
|
||||||
let value = node.getAttribute("t-value")!;
|
let value = node.getAttribute("t-value")!;
|
||||||
ctx.variables[variable] = ctx.variables[variable] || ({} as QWebVar);
|
ctx.variables[variable] = ctx.variables[variable] || {};
|
||||||
let qwebvar = ctx.variables[variable];
|
let qwebvar = ctx.variables[variable];
|
||||||
const hasBody = node.hasChildNodes();
|
const hasBody = node.hasChildNodes();
|
||||||
|
|
||||||
@@ -141,7 +140,7 @@ QWeb.addDirective({
|
|||||||
const _parentNode = ctx.parentNode;
|
const _parentNode = ctx.parentNode;
|
||||||
ctx.parentNode = tempParentNodeID;
|
ctx.parentNode = tempParentNodeID;
|
||||||
|
|
||||||
ctx.addLine(`let c${tempParentNodeID} = new utils.VDomArray();`);
|
ctx.addLine(`const c${tempParentNodeID} = new utils.VDomArray();`);
|
||||||
const nodeCopy = node.cloneNode(true) as Element;
|
const nodeCopy = node.cloneNode(true) as Element;
|
||||||
for (let attr of ["t-set", "t-value", "t-if", "t-else", "t-elif"]) {
|
for (let attr of ["t-set", "t-value", "t-if", "t-else", "t-elif"]) {
|
||||||
nodeCopy.removeAttribute(attr);
|
nodeCopy.removeAttribute(attr);
|
||||||
@@ -216,7 +215,6 @@ QWeb.addDirective({
|
|||||||
// Step 1: sanity checks
|
// Step 1: sanity checks
|
||||||
// ------------------------------------------------
|
// ------------------------------------------------
|
||||||
ctx.rootContext.shouldDefineScope = true;
|
ctx.rootContext.shouldDefineScope = true;
|
||||||
ctx.rootContext.shouldDefineUtils = true;
|
|
||||||
if (node.nodeName !== "t") {
|
if (node.nodeName !== "t") {
|
||||||
throw new Error("Invalid tag for t-call directive (should be 't')");
|
throw new Error("Invalid tag for t-call directive (should be 't')");
|
||||||
}
|
}
|
||||||
@@ -230,7 +228,7 @@ QWeb.addDirective({
|
|||||||
// ------------------------------------------------
|
// ------------------------------------------------
|
||||||
if (!qweb.subTemplates[subTemplate]) {
|
if (!qweb.subTemplates[subTemplate]) {
|
||||||
qweb.subTemplates[subTemplate] = true;
|
qweb.subTemplates[subTemplate] = true;
|
||||||
const subTemplateFn = qweb._compile(subTemplate, nodeTemplate.elem, ctx, true);
|
const subTemplateFn = qweb._compile(subTemplate, nodeTemplate.elem, ctx);
|
||||||
qweb.subTemplates[subTemplate] = subTemplateFn;
|
qweb.subTemplates[subTemplate] = subTemplateFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,17 +262,17 @@ QWeb.addDirective({
|
|||||||
// Step 4: add the appropriate function call to current component
|
// Step 4: add the appropriate function call to current component
|
||||||
// ------------------------------------------------
|
// ------------------------------------------------
|
||||||
const callingScope = hasBody ? "scope" : "Object.assign(Object.create(context), scope)";
|
const callingScope = hasBody ? "scope" : "Object.assign(Object.create(context), scope)";
|
||||||
const parentComponent = `utils.getComponent(context)`;
|
|
||||||
const keyCode = ctx.loopNumber || ctx.hasKey0 ? `, key: ${ctx.generateTemplateKey()}` : "";
|
|
||||||
const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result";
|
|
||||||
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}${keyCode}})`;
|
|
||||||
if (ctx.parentNode) {
|
if (ctx.parentNode) {
|
||||||
ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`);
|
ctx.addLine(
|
||||||
|
`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, Object.assign({}, extra, {parentNode: c${ctx.parentNode}}));`
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// this is a t-call with no parentnode, we need to extract the result
|
// this is a t-call with no parentnode, we need to extract the result
|
||||||
ctx.rootContext.shouldDefineResult = true;
|
ctx.rootContext.shouldDefineResult = true;
|
||||||
ctx.addLine(`result = []`);
|
ctx.addLine(`result = []`);
|
||||||
ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`);
|
ctx.addLine(
|
||||||
|
`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, Object.assign({}, extra, {parentNode: result}));`
|
||||||
|
);
|
||||||
ctx.addLine(`result = result[0]`);
|
ctx.addLine(`result = result[0]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,16 +301,16 @@ QWeb.addDirective({
|
|||||||
const elems = node.getAttribute("t-foreach")!;
|
const elems = node.getAttribute("t-foreach")!;
|
||||||
const name = node.getAttribute("t-as")!;
|
const name = node.getAttribute("t-as")!;
|
||||||
let arrayID = ctx.generateID();
|
let arrayID = ctx.generateID();
|
||||||
ctx.addLine(`let _${arrayID} = ${ctx.formatExpression(elems)};`);
|
ctx.addLine(`var _${arrayID} = ${ctx.formatExpression(elems)};`);
|
||||||
ctx.addLine(`if (!_${arrayID}) { throw new Error('QWeb error: Invalid loop expression')}`);
|
ctx.addLine(`if (!_${arrayID}) { throw new Error('QWeb error: Invalid loop expression')}`);
|
||||||
let keysID = ctx.generateID();
|
let keysID = ctx.generateID();
|
||||||
let valuesID = ctx.generateID();
|
let valuesID = ctx.generateID();
|
||||||
ctx.addLine(`let _${keysID} = _${valuesID} = _${arrayID};`);
|
ctx.addLine(`var _${keysID} = _${valuesID} = _${arrayID};`);
|
||||||
ctx.addIf(`!(_${arrayID} instanceof Array)`);
|
ctx.addIf(`!(_${arrayID} instanceof Array)`);
|
||||||
ctx.addLine(`_${keysID} = Object.keys(_${arrayID});`);
|
ctx.addLine(`_${keysID} = Object.keys(_${arrayID});`);
|
||||||
ctx.addLine(`_${valuesID} = Object.values(_${arrayID});`);
|
ctx.addLine(`_${valuesID} = Object.values(_${arrayID});`);
|
||||||
ctx.closeIf();
|
ctx.closeIf();
|
||||||
ctx.addLine(`let _length${keysID} = _${keysID}.length;`);
|
ctx.addLine(`var _length${keysID} = _${keysID}.length;`);
|
||||||
let varsID = ctx.startProtectScope();
|
let varsID = ctx.startProtectScope();
|
||||||
const loopVar = `i${ctx.loopNumber}`;
|
const loopVar = `i${ctx.loopNumber}`;
|
||||||
ctx.addLine(`for (let ${loopVar} = 0; ${loopVar} < _length${keysID}; ${loopVar}++) {`);
|
ctx.addLine(`for (let ${loopVar} = 0; ${loopVar} < _length${keysID}; ${loopVar}++) {`);
|
||||||
@@ -334,13 +332,6 @@ QWeb.addDirective({
|
|||||||
`Directive t-foreach should always be used with a t-key! (in template: '${ctx.templateName}')`
|
`Directive t-foreach should always be used with a t-key! (in template: '${ctx.templateName}')`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (nodeCopy.hasAttribute("t-key")) {
|
|
||||||
const expr = ctx.formatExpression(nodeCopy.getAttribute("t-key")!);
|
|
||||||
ctx.addLine(`let key${ctx.loopNumber} = ${expr};`);
|
|
||||||
nodeCopy.removeAttribute("t-key");
|
|
||||||
} else {
|
|
||||||
ctx.addLine(`let key${ctx.loopNumber} = i${ctx.loopNumber};`);
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeCopy.removeAttribute("t-foreach");
|
nodeCopy.removeAttribute("t-foreach");
|
||||||
qweb._compileNode(nodeCopy, ctx);
|
qweb._compileNode(nodeCopy, ctx);
|
||||||
|
|||||||
@@ -26,13 +26,12 @@ export class CompilationContext {
|
|||||||
templateName: string;
|
templateName: string;
|
||||||
allowMultipleRoots: boolean = false;
|
allowMultipleRoots: boolean = false;
|
||||||
hasParentWidget: boolean = false;
|
hasParentWidget: boolean = false;
|
||||||
hasKey0: boolean = false;
|
currentKey: string = "";
|
||||||
keyStack: boolean[] = [];
|
|
||||||
|
|
||||||
constructor(name?: string) {
|
constructor(name?: string) {
|
||||||
this.rootContext = this;
|
this.rootContext = this;
|
||||||
this.templateName = name || "noname";
|
this.templateName = name || "noname";
|
||||||
this.addLine("let h = this.h;");
|
this.addLine("var h = this.h;");
|
||||||
}
|
}
|
||||||
|
|
||||||
generateID(): number {
|
generateID(): number {
|
||||||
@@ -49,15 +48,20 @@ export class CompilationContext {
|
|||||||
*/
|
*/
|
||||||
generateTemplateKey(prefix: string = ""): string {
|
generateTemplateKey(prefix: string = ""): string {
|
||||||
const id = this.generateID();
|
const id = this.generateID();
|
||||||
if (this.loopNumber === 0 && !this.hasKey0) {
|
if (this.loopNumber === 0 && !this.currentKey) {
|
||||||
return `'${prefix}__${id}__'`;
|
return `'${prefix}__${id}__'`;
|
||||||
}
|
}
|
||||||
let key = `\`${prefix}__${id}__`;
|
let locationExpr = `\`${prefix}__${id}__`;
|
||||||
let start = this.hasKey0 ? 0 : 1;
|
for (let i = 0; i < this.loopNumber - 1; i++) {
|
||||||
for (let i = start; i < this.loopNumber + 1; i++) {
|
locationExpr += `\${i${i + 1}}__`;
|
||||||
key += `\${key${i}}__`;
|
}
|
||||||
|
if (this.currentKey) {
|
||||||
|
const k = this.currentKey;
|
||||||
|
this.addLine(`let k${id} = ${locationExpr}\` + ${k};`);
|
||||||
|
} else {
|
||||||
|
locationExpr += this.loopNumber ? `\${i${this.loopNumber}}__\`` : "`";
|
||||||
|
this.addLine(`let k${id} = ${locationExpr};`);
|
||||||
}
|
}
|
||||||
this.addLine(`let k${id} = ${key}\`;`);
|
|
||||||
return `k${id}`;
|
return `k${id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,11 +116,11 @@ export class CompilationContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
indent() {
|
indent() {
|
||||||
this.rootContext.indentLevel++;
|
this.indentLevel++;
|
||||||
}
|
}
|
||||||
|
|
||||||
dedent() {
|
dedent() {
|
||||||
this.rootContext.indentLevel--;
|
this.indentLevel--;
|
||||||
}
|
}
|
||||||
|
|
||||||
addLine(line: string): number {
|
addLine(line: string): number {
|
||||||
@@ -176,7 +180,7 @@ export class CompilationContext {
|
|||||||
startProtectScope(): number {
|
startProtectScope(): number {
|
||||||
const protectID = this.generateID();
|
const protectID = this.generateID();
|
||||||
this.rootContext.shouldDefineScope = true;
|
this.rootContext.shouldDefineScope = true;
|
||||||
this.addLine(`let _origScope${protectID} = scope;`);
|
this.addLine(`const _origScope${protectID} = scope;`);
|
||||||
this.addLine(`scope = Object.assign(Object.create(context), scope);`);
|
this.addLine(`scope = Object.assign(Object.create(context), scope);`);
|
||||||
return protectID;
|
return protectID;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ const STATIC_TOKEN_MAP: { [key: string]: TKind } = {
|
|||||||
|
|
||||||
// note that the space after typeof is relevant. It makes sure that the formatted
|
// note that the space after typeof is relevant. It makes sure that the formatted
|
||||||
// expression has a space after typeof
|
// expression has a space after typeof
|
||||||
const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;".split(",");
|
const OPERATORS = ".,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;".split(",");
|
||||||
|
|
||||||
type Tokenizer = (expr: string) => Token | false;
|
type Tokenizer = (expr: string) => Token | false;
|
||||||
|
|
||||||
|
|||||||
+4
-15
@@ -303,20 +303,9 @@ QWeb.addDirective({
|
|||||||
QWeb.addDirective({
|
QWeb.addDirective({
|
||||||
name: "key",
|
name: "key",
|
||||||
priority: 45,
|
priority: 45,
|
||||||
atNodeEncounter({ ctx, value, node }) {
|
atNodeEncounter({ ctx, value }) {
|
||||||
if (ctx.loopNumber === 0) {
|
let id = ctx.generateID();
|
||||||
ctx.keyStack.push(ctx.rootContext.hasKey0);
|
ctx.addLine(`const nodeKey${id} = ${ctx.formatExpression(value)};`);
|
||||||
ctx.rootContext.hasKey0 = true;
|
ctx.currentKey = `nodeKey${id}`;
|
||||||
}
|
|
||||||
ctx.addLine("{");
|
|
||||||
ctx.indent();
|
|
||||||
ctx.addLine(`let key${ctx.loopNumber} = ${ctx.formatExpression(value)};`);
|
|
||||||
},
|
|
||||||
finalize({ ctx }) {
|
|
||||||
ctx.dedent();
|
|
||||||
ctx.addLine("}");
|
|
||||||
if (ctx.loopNumber === 0) {
|
|
||||||
ctx.rootContext.hasKey0 = ctx.keyStack.pop() as boolean;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+15
-24
@@ -119,7 +119,7 @@ const UTILS: Utils = {
|
|||||||
return vnode.text;
|
return vnode.text;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join("");
|
.join();
|
||||||
},
|
},
|
||||||
getComponent(obj) {
|
getComponent(obj) {
|
||||||
while (obj && !obj.hasOwnProperty("__owl__")) {
|
while (obj && !obj.hasOwnProperty("__owl__")) {
|
||||||
@@ -383,12 +383,7 @@ export class QWeb extends EventBus {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_compile(
|
_compile(name: string, elem: Element, parentContext?: CompilationContext): CompiledTemplate {
|
||||||
name: string,
|
|
||||||
elem: Element,
|
|
||||||
parentContext?: CompilationContext,
|
|
||||||
defineKey?: boolean
|
|
||||||
): CompiledTemplate {
|
|
||||||
const isDebug = elem.attributes.hasOwnProperty("t-debug");
|
const isDebug = elem.attributes.hasOwnProperty("t-debug");
|
||||||
const ctx = new CompilationContext(name);
|
const ctx = new CompilationContext(name);
|
||||||
if (elem.tagName !== "t") {
|
if (elem.tagName !== "t") {
|
||||||
@@ -401,10 +396,6 @@ export class QWeb extends EventBus {
|
|||||||
ctx.hasParentWidget = true;
|
ctx.hasParentWidget = true;
|
||||||
ctx.shouldDefineResult = false;
|
ctx.shouldDefineResult = false;
|
||||||
ctx.addLine(`let c${ctx.parentNode} = extra.parentNode;`);
|
ctx.addLine(`let c${ctx.parentNode} = extra.parentNode;`);
|
||||||
if (defineKey) {
|
|
||||||
ctx.addLine(`let key0 = extra.key || "";`);
|
|
||||||
ctx.hasKey0 = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this._compileNode(elem, ctx);
|
this._compileNode(elem, ctx);
|
||||||
|
|
||||||
@@ -475,13 +466,16 @@ export class QWeb extends EventBus {
|
|||||||
// this is an unusual situation: this text node is the result of the
|
// this is an unusual situation: this text node is the result of the
|
||||||
// template rendering.
|
// template rendering.
|
||||||
let nodeID = ctx.generateID();
|
let nodeID = ctx.generateID();
|
||||||
ctx.addLine(`let vn${nodeID} = {text: \`${text}\`};`);
|
ctx.addLine(`var vn${nodeID} = {text: \`${text}\`};`);
|
||||||
ctx.addLine(`result = vn${nodeID};`);
|
ctx.addLine(`result = vn${nodeID};`);
|
||||||
ctx.rootContext.rootNode = nodeID;
|
ctx.rootContext.rootNode = nodeID;
|
||||||
ctx.rootContext.parentTextNode = nodeID;
|
ctx.rootContext.parentTextNode = nodeID;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (ctx !== ctx.rootContext) {
|
||||||
|
ctx = ctx.subContext("currentKey", ctx.currentKey);
|
||||||
|
}
|
||||||
|
|
||||||
const firstLetter = node.tagName[0];
|
const firstLetter = node.tagName[0];
|
||||||
if (firstLetter === firstLetter.toUpperCase()) {
|
if (firstLetter === firstLetter.toUpperCase()) {
|
||||||
@@ -684,7 +678,7 @@ export class QWeb extends EventBus {
|
|||||||
ctx.addLine(`let ${classObj} = {${classDef}};`);
|
ctx.addLine(`let ${classObj} = {${classDef}};`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.addLine(`let _${attID} = '${value}';`);
|
ctx.addLine(`var _${attID} = '${value}';`);
|
||||||
if (!name.match(/^[a-zA-Z]+$/)) {
|
if (!name.match(/^[a-zA-Z]+$/)) {
|
||||||
// attribute contains 'non letters' => we want to quote it
|
// attribute contains 'non letters' => we want to quote it
|
||||||
name = '"' + name + '"';
|
name = '"' + name + '"';
|
||||||
@@ -720,12 +714,12 @@ export class QWeb extends EventBus {
|
|||||||
const attValue = (<Element>node).getAttribute(attName);
|
const attValue = (<Element>node).getAttribute(attName);
|
||||||
if (attValue) {
|
if (attValue) {
|
||||||
const attValueID = ctx.generateID();
|
const attValueID = ctx.generateID();
|
||||||
ctx.addLine(`let _${attValueID} = ${formattedValue};`);
|
ctx.addLine(`var _${attValueID} = ${formattedValue};`);
|
||||||
formattedValue = `'${attValue}' + (_${attValueID} ? ' ' + _${attValueID} : '')`;
|
formattedValue = `'${attValue}' + (_${attValueID} ? ' ' + _${attValueID} : '')`;
|
||||||
const attrIndex = attrs.findIndex(att => att.startsWith(attName + ":"));
|
const attrIndex = attrs.findIndex(att => att.startsWith(attName + ":"));
|
||||||
attrs.splice(attrIndex, 1);
|
attrs.splice(attrIndex, 1);
|
||||||
}
|
}
|
||||||
ctx.addLine(`let _${attID} = ${formattedValue};`);
|
ctx.addLine(`var _${attID} = ${formattedValue};`);
|
||||||
attrs.push(`${attName}: _${attID}`);
|
attrs.push(`${attName}: _${attID}`);
|
||||||
handleBooleanProps(attName, attID);
|
handleBooleanProps(attName, attID);
|
||||||
}
|
}
|
||||||
@@ -741,9 +735,9 @@ export class QWeb extends EventBus {
|
|||||||
const attID = ctx.generateID();
|
const attID = ctx.generateID();
|
||||||
let staticVal = (<Element>node).getAttribute(attName);
|
let staticVal = (<Element>node).getAttribute(attName);
|
||||||
if (staticVal) {
|
if (staticVal) {
|
||||||
ctx.addLine(`let _${attID} = '${staticVal} ' + ${formattedExpr};`);
|
ctx.addLine(`var _${attID} = '${staticVal} ' + ${formattedExpr};`);
|
||||||
} else {
|
} else {
|
||||||
ctx.addLine(`let _${attID} = ${formattedExpr};`);
|
ctx.addLine(`var _${attID} = ${formattedExpr};`);
|
||||||
}
|
}
|
||||||
attrs.push(`${attName}: _${attID}`);
|
attrs.push(`${attName}: _${attID}`);
|
||||||
}
|
}
|
||||||
@@ -751,13 +745,13 @@ export class QWeb extends EventBus {
|
|||||||
// t-att= attributes
|
// t-att= attributes
|
||||||
if (name === "t-att") {
|
if (name === "t-att") {
|
||||||
let id = ctx.generateID();
|
let id = ctx.generateID();
|
||||||
ctx.addLine(`let _${id} = ${ctx.formatExpression(value!)};`);
|
ctx.addLine(`var _${id} = ${ctx.formatExpression(value!)};`);
|
||||||
tattrs.push(id);
|
tattrs.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let nodeID = ctx.generateID();
|
let nodeID = ctx.generateID();
|
||||||
let key = ctx.loopNumber || ctx.hasKey0 ? `\`\${key${ctx.loopNumber}}_${nodeID}\`` : nodeID;
|
let nodeKey = ctx.currentKey || nodeID;
|
||||||
const parts = [`key:${key}`];
|
const parts = [`key:${nodeKey}`];
|
||||||
if (attrs.length + tattrs.length > 0) {
|
if (attrs.length + tattrs.length > 0) {
|
||||||
parts.push(`attrs:{${attrs.join(",")}}`);
|
parts.push(`attrs:{${attrs.join(",")}}`);
|
||||||
}
|
}
|
||||||
@@ -783,12 +777,9 @@ export class QWeb extends EventBus {
|
|||||||
ctx.addLine(`}`);
|
ctx.addLine(`}`);
|
||||||
ctx.closeIf();
|
ctx.closeIf();
|
||||||
}
|
}
|
||||||
ctx.addLine(`let vn${nodeID} = h('${node.nodeName}', p${nodeID}, c${nodeID});`);
|
ctx.addLine(`var vn${nodeID} = h('${node.nodeName}', p${nodeID}, c${nodeID});`);
|
||||||
if (ctx.parentNode) {
|
if (ctx.parentNode) {
|
||||||
ctx.addLine(`c${ctx.parentNode}.push(vn${nodeID});`);
|
ctx.addLine(`c${ctx.parentNode}.push(vn${nodeID});`);
|
||||||
} else if (ctx.loopNumber || ctx.hasKey0) {
|
|
||||||
ctx.rootContext.shouldDefineResult = true;
|
|
||||||
ctx.addLine(`result = vn${nodeID};`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeID;
|
return nodeID;
|
||||||
|
|||||||
+1
-4
@@ -132,7 +132,7 @@ export function useStore(selector, options: SelectorOptions = {}): any {
|
|||||||
__destroy.call(component, parent);
|
__destroy.call(component, parent);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof result !== "object" || result === null) {
|
if (typeof result !== "object") {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return new Proxy(result, {
|
return new Proxy(result, {
|
||||||
@@ -141,9 +141,6 @@ export function useStore(selector, options: SelectorOptions = {}): any {
|
|||||||
},
|
},
|
||||||
set(target, k, v) {
|
set(target, k, v) {
|
||||||
throw new Error("Store state should only be modified through actions");
|
throw new Error("Store state should only be modified through actions");
|
||||||
},
|
|
||||||
has(target, k) {
|
|
||||||
return k in result;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
-17
@@ -1,5 +1,4 @@
|
|||||||
import { QWeb } from "./qweb/index";
|
import { QWeb } from "./qweb/index";
|
||||||
import { registerSheet } from "./component/styles";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Owl Tags
|
* Owl Tags
|
||||||
@@ -26,19 +25,3 @@ export function xml(strings, ...args) {
|
|||||||
QWeb.registerTemplate(name, value);
|
QWeb.registerTemplate(name, value);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* CSS tag helper for defining inline stylesheets. With this, one can simply define
|
|
||||||
* an inline stylesheet with just the following code:
|
|
||||||
* ```js
|
|
||||||
* class A extends Component {
|
|
||||||
* static style = css`.component-a { color: red; }`;
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export function css(strings, ...args) {
|
|
||||||
const name = `__sheet__${QWeb.nextId++}`;
|
|
||||||
const value = String.raw(strings, ...args);
|
|
||||||
registerSheet(name, value);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ exports[`animations t-transition combined with component 1`] = `
|
|||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
let parent = context;
|
let parent = context;
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
let vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
// Component 'Child'
|
// Component 'Child'
|
||||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||||
let props2 = {};
|
let props2 = {};
|
||||||
@@ -52,9 +52,9 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
|
|||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
let parent = context;
|
let parent = context;
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
let vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
if (scope['state'].display) {
|
if (scope['state'].display) {
|
||||||
// Component 'Child'
|
// Component 'Child'
|
||||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||||
@@ -98,9 +98,9 @@ exports[`animations t-transition combined with t-component, remove and re-add be
|
|||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
let parent = context;
|
let parent = context;
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
let vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
if (scope['state'].flag) {
|
if (scope['state'].flag) {
|
||||||
// Component 'Child'
|
// Component 'Child'
|
||||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||||
@@ -141,9 +141,9 @@ exports[`animations t-transition with no delay/duration 1`] = `
|
|||||||
) {
|
) {
|
||||||
// Template name: \\"test\\"
|
// Template name: \\"test\\"
|
||||||
let utils = this.constructor.utils;
|
let utils = this.constructor.utils;
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
let vn1 = h('span', p1, c1);
|
var vn1 = h('span', p1, c1);
|
||||||
p1.hook = {
|
p1.hook = {
|
||||||
insert: vn => {
|
insert: vn => {
|
||||||
utils.transitionInsert(vn, 'jupiler');
|
utils.transitionInsert(vn, 'jupiler');
|
||||||
@@ -162,9 +162,9 @@ exports[`animations t-transition, on a simple node (insert) 1`] = `
|
|||||||
) {
|
) {
|
||||||
// Template name: \\"test\\"
|
// Template name: \\"test\\"
|
||||||
let utils = this.constructor.utils;
|
let utils = this.constructor.utils;
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
let vn1 = h('span', p1, c1);
|
var vn1 = h('span', p1, c1);
|
||||||
p1.hook = {
|
p1.hook = {
|
||||||
insert: vn => {
|
insert: vn => {
|
||||||
utils.transitionInsert(vn, 'chimay');
|
utils.transitionInsert(vn, 'chimay');
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8,9 +8,9 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
|
|||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
let parent = context;
|
let parent = context;
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
let vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
// Component 'Child'
|
// Component 'Child'
|
||||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||||
let props2 = {message:1};
|
let props2 = {message:1};
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ exports[`t-slot directive can define and call slots 1`] = `
|
|||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
let parent = context;
|
let parent = context;
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
let vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
// Component 'Dialog'
|
// Component 'Dialog'
|
||||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||||
let props2 = {};
|
let props2 = {};
|
||||||
@@ -43,18 +43,18 @@ exports[`t-slot directive can define and call slots 2`] = `
|
|||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
// Template name: \\"Dialog\\"
|
// Template name: \\"Dialog\\"
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c6 = [], p6 = {key:6};
|
let c6 = [], p6 = {key:6};
|
||||||
let vn6 = h('div', p6, c6);
|
var vn6 = h('div', p6, c6);
|
||||||
let c7 = [], p7 = {key:7};
|
let c7 = [], p7 = {key:7};
|
||||||
let vn7 = h('div', p7, c7);
|
var vn7 = h('div', p7, c7);
|
||||||
c6.push(vn7);
|
c6.push(vn7);
|
||||||
const slot8 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
const slot8 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
||||||
if (slot8) {
|
if (slot8) {
|
||||||
slot8.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c7, parent: extra.parent || context}));
|
slot8.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c7, parent: extra.parent || context}));
|
||||||
}
|
}
|
||||||
let c9 = [], p9 = {key:9};
|
let c9 = [], p9 = {key:9};
|
||||||
let vn9 = h('div', p9, c9);
|
var vn9 = h('div', p9, c9);
|
||||||
c6.push(vn9);
|
c6.push(vn9);
|
||||||
const slot10 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
|
const slot10 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
|
||||||
if (slot10) {
|
if (slot10) {
|
||||||
@@ -68,10 +68,10 @@ exports[`t-slot directive can define and call slots 3`] = `
|
|||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
// Template name: \\"slot_header_template\\"
|
// Template name: \\"slot_header_template\\"
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = extra.parentNode;
|
let c1 = extra.parentNode;
|
||||||
let c4 = [], p4 = {key:4};
|
let c4 = [], p4 = {key:4};
|
||||||
let vn4 = h('span', p4, c4);
|
var vn4 = h('span', p4, c4);
|
||||||
c1.push(vn4);
|
c1.push(vn4);
|
||||||
c4.push({text: \`header\`});
|
c4.push({text: \`header\`});
|
||||||
}"
|
}"
|
||||||
@@ -81,10 +81,10 @@ exports[`t-slot directive can define and call slots 4`] = `
|
|||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
// Template name: \\"slot_footer_template\\"
|
// Template name: \\"slot_footer_template\\"
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = extra.parentNode;
|
let c1 = extra.parentNode;
|
||||||
let c5 = [], p5 = {key:5};
|
let c5 = [], p5 = {key:5};
|
||||||
let vn5 = h('span', p5, c5);
|
var vn5 = h('span', p5, c5);
|
||||||
c1.push(vn5);
|
c1.push(vn5);
|
||||||
c5.push({text: \`footer\`});
|
c5.push({text: \`footer\`});
|
||||||
}"
|
}"
|
||||||
@@ -94,10 +94,10 @@ exports[`t-slot directive content is the default slot 1`] = `
|
|||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
// Template name: \\"slot_default_template\\"
|
// Template name: \\"slot_default_template\\"
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = extra.parentNode;
|
let c1 = extra.parentNode;
|
||||||
let c4 = [], p4 = {key:4};
|
let c4 = [], p4 = {key:4};
|
||||||
let vn4 = h('span', p4, c4);
|
var vn4 = h('span', p4, c4);
|
||||||
c1.push(vn4);
|
c1.push(vn4);
|
||||||
c4.push({text: \`sts rocks\`});
|
c4.push({text: \`sts rocks\`});
|
||||||
}"
|
}"
|
||||||
@@ -107,9 +107,9 @@ exports[`t-slot directive dafault slots can define a default content 1`] = `
|
|||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
// Template name: \\"__template__1\\"
|
// Template name: \\"__template__1\\"
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c4 = [], p4 = {key:4};
|
let c4 = [], p4 = {key:4};
|
||||||
let vn4 = h('span', p4, c4);
|
var vn4 = h('span', p4, c4);
|
||||||
const slot5 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
const slot5 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||||
if (slot5) {
|
if (slot5) {
|
||||||
slot5.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c4, parent: extra.parent || context}));
|
slot5.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c4, parent: extra.parent || context}));
|
||||||
@@ -124,7 +124,7 @@ exports[`t-slot directive default slot work with text nodes 1`] = `
|
|||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
// Template name: \\"slot_default_template\\"
|
// Template name: \\"slot_default_template\\"
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = extra.parentNode;
|
let c1 = extra.parentNode;
|
||||||
c1.push({text: \`sts rocks\`});
|
c1.push({text: \`sts rocks\`});
|
||||||
}"
|
}"
|
||||||
@@ -134,14 +134,14 @@ exports[`t-slot directive multiple roots are allowed in a default slot 1`] = `
|
|||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
// Template name: \\"slot_default_template\\"
|
// Template name: \\"slot_default_template\\"
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = extra.parentNode;
|
let c1 = extra.parentNode;
|
||||||
let c4 = [], p4 = {key:4};
|
let c4 = [], p4 = {key:4};
|
||||||
let vn4 = h('span', p4, c4);
|
var vn4 = h('span', p4, c4);
|
||||||
c1.push(vn4);
|
c1.push(vn4);
|
||||||
c4.push({text: \`sts\`});
|
c4.push({text: \`sts\`});
|
||||||
let c5 = [], p5 = {key:5};
|
let c5 = [], p5 = {key:5};
|
||||||
let vn5 = h('span', p5, c5);
|
var vn5 = h('span', p5, c5);
|
||||||
c1.push(vn5);
|
c1.push(vn5);
|
||||||
c5.push({text: \`rocks\`});
|
c5.push({text: \`rocks\`});
|
||||||
}"
|
}"
|
||||||
@@ -151,14 +151,14 @@ exports[`t-slot directive multiple roots are allowed in a named slot 1`] = `
|
|||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
// Template name: \\"slot_content_template\\"
|
// Template name: \\"slot_content_template\\"
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = extra.parentNode;
|
let c1 = extra.parentNode;
|
||||||
let c4 = [], p4 = {key:4};
|
let c4 = [], p4 = {key:4};
|
||||||
let vn4 = h('span', p4, c4);
|
var vn4 = h('span', p4, c4);
|
||||||
c1.push(vn4);
|
c1.push(vn4);
|
||||||
c4.push({text: \`sts\`});
|
c4.push({text: \`sts\`});
|
||||||
let c5 = [], p5 = {key:5};
|
let c5 = [], p5 = {key:5};
|
||||||
let vn5 = h('span', p5, c5);
|
var vn5 = h('span', p5, c5);
|
||||||
c1.push(vn5);
|
c1.push(vn5);
|
||||||
c5.push({text: \`rocks\`});
|
c5.push({text: \`rocks\`});
|
||||||
}"
|
}"
|
||||||
@@ -168,9 +168,9 @@ exports[`t-slot directive named slots can define a default content 1`] = `
|
|||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
// Template name: \\"__template__1\\"
|
// Template name: \\"__template__1\\"
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c4 = [], p4 = {key:4};
|
let c4 = [], p4 = {key:4};
|
||||||
let vn4 = h('span', p4, c4);
|
var vn4 = h('span', p4, c4);
|
||||||
const slot5 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
const slot5 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
||||||
if (slot5) {
|
if (slot5) {
|
||||||
slot5.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c4, parent: extra.parent || context}));
|
slot5.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c4, parent: extra.parent || context}));
|
||||||
@@ -187,10 +187,10 @@ exports[`t-slot directive refs are properly bound in slots 1`] = `
|
|||||||
// Template name: \\"slot_footer_template\\"
|
// Template name: \\"slot_footer_template\\"
|
||||||
let utils = this.constructor.utils;
|
let utils = this.constructor.utils;
|
||||||
context.__owl__.refs = context.__owl__.refs || {};
|
context.__owl__.refs = context.__owl__.refs || {};
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = extra.parentNode;
|
let c1 = extra.parentNode;
|
||||||
let c8 = [], p8 = {key:8,on:{}};
|
let c8 = [], p8 = {key:8,on:{}};
|
||||||
let vn8 = h('button', p8, c8);
|
var vn8 = h('button', p8, c8);
|
||||||
c1.push(vn8);
|
c1.push(vn8);
|
||||||
extra.handlers['click__9__'] = extra.handlers['click__9__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['doSomething'](e);};
|
extra.handlers['click__9__'] = extra.handlers['click__9__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['doSomething'](e);};
|
||||||
p8.on['click'] = extra.handlers['click__9__'];
|
p8.on['click'] = extra.handlers['click__9__'];
|
||||||
@@ -212,10 +212,10 @@ exports[`t-slot directive slots are rendered with proper context 1`] = `
|
|||||||
) {
|
) {
|
||||||
// Template name: \\"slot_footer_template\\"
|
// Template name: \\"slot_footer_template\\"
|
||||||
let utils = this.constructor.utils;
|
let utils = this.constructor.utils;
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = extra.parentNode;
|
let c1 = extra.parentNode;
|
||||||
let c8 = [], p8 = {key:8,on:{}};
|
let c8 = [], p8 = {key:8,on:{}};
|
||||||
let vn8 = h('button', p8, c8);
|
var vn8 = h('button', p8, c8);
|
||||||
c1.push(vn8);
|
c1.push(vn8);
|
||||||
extra.handlers['click__9__'] = extra.handlers['click__9__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['doSomething'](e);};
|
extra.handlers['click__9__'] = extra.handlers['click__9__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['doSomething'](e);};
|
||||||
p8.on['click'] = extra.handlers['click__9__'];
|
p8.on['click'] = extra.handlers['click__9__'];
|
||||||
@@ -228,15 +228,15 @@ exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
|
|||||||
) {
|
) {
|
||||||
// Template name: \\"Link\\"
|
// Template name: \\"Link\\"
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let _11 = scope['props'].to;
|
var _12 = scope['props'].to;
|
||||||
let c12 = [], p12 = {key:12,attrs:{href: _11}};
|
let c13 = [], p13 = {key:13,attrs:{href: _12}};
|
||||||
let vn12 = h('a', p12, c12);
|
var vn13 = h('a', p13, c13);
|
||||||
const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
const slot14 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||||
if (slot13) {
|
if (slot14) {
|
||||||
slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context}));
|
slot14.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c13, parent: extra.parent || context}));
|
||||||
}
|
}
|
||||||
return vn12;
|
return vn13;
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -248,21 +248,21 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
|||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
let parent = context;
|
let parent = context;
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
let vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
let c2 = [], p2 = {key:2};
|
let c2 = [], p2 = {key:2};
|
||||||
let vn2 = h('u', p2, c2);
|
var vn2 = h('u', p2, c2);
|
||||||
c1.push(vn2);
|
c1.push(vn2);
|
||||||
let _3 = scope['state'].users;
|
var _3 = scope['state'].users;
|
||||||
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
||||||
let _4 = _5 = _3;
|
var _4 = _5 = _3;
|
||||||
if (!(_3 instanceof Array)) {
|
if (!(_3 instanceof Array)) {
|
||||||
_4 = Object.keys(_3);
|
_4 = Object.keys(_3);
|
||||||
_5 = Object.values(_3);
|
_5 = Object.values(_3);
|
||||||
}
|
}
|
||||||
let _length4 = _4.length;
|
var _length4 = _4.length;
|
||||||
let _origScope6 = scope;
|
const _origScope6 = scope;
|
||||||
scope = Object.assign(Object.create(context), scope);
|
scope = Object.assign(Object.create(context), scope);
|
||||||
for (let i1 = 0; i1 < _length4; i1++) {
|
for (let i1 = 0; i1 < _length4; i1++) {
|
||||||
scope.user_first = i1 === 0
|
scope.user_first = i1 === 0
|
||||||
@@ -270,35 +270,35 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
|||||||
scope.user_index = i1
|
scope.user_index = i1
|
||||||
scope.user = _4[i1]
|
scope.user = _4[i1]
|
||||||
scope.user_value = _5[i1]
|
scope.user_value = _5[i1]
|
||||||
let key1 = scope['user'].id;
|
const nodeKey7 = scope['user'].id;
|
||||||
let c7 = [], p7 = {key:\`\${key1}_7\`};
|
let c8 = [], p8 = {key:nodeKey7};
|
||||||
let vn7 = h('li', p7, c7);
|
var vn8 = h('li', p8, c8);
|
||||||
c2.push(vn7);
|
c2.push(vn8);
|
||||||
// Component 'Link'
|
// Component 'Link'
|
||||||
let k9 = \`__9__\${key1}__\`;
|
let k10 = \`__10__\` + nodeKey7;
|
||||||
let w8 = k9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k9]] : false;
|
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false;
|
||||||
let props8 = {to:'/user/'+scope['user'].id};
|
let props9 = {to:'/user/'+scope['user'].id};
|
||||||
if (w8 && w8.__owl__.currentFiber && !w8.__owl__.vnode) {
|
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) {
|
||||||
w8.destroy();
|
w9.destroy();
|
||||||
w8 = false;
|
w9 = false;
|
||||||
}
|
}
|
||||||
if (w8) {
|
if (w9) {
|
||||||
w8.__updateProps(props8, extra.fiber, Object.assign(Object.create(context), scope));
|
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope));
|
||||||
let pvnode = w8.__owl__.pvnode;
|
let pvnode = w9.__owl__.pvnode;
|
||||||
c7.push(pvnode);
|
c8.push(pvnode);
|
||||||
} else {
|
} else {
|
||||||
let componentKey8 = \`Link\`;
|
let componentKey9 = \`Link\`;
|
||||||
let W8 = context.constructor.components[componentKey8] || QWeb.components[componentKey8]|| scope['Link'];
|
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link'];
|
||||||
if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
|
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')}
|
||||||
w8 = new W8(parent, props8);
|
w9 = new W9(parent, props9);
|
||||||
parent.__owl__.cmap[k9] = w8.__owl__.id;
|
parent.__owl__.cmap[k10] = w9.__owl__.id;
|
||||||
w8.__owl__.slotId = 1;
|
w9.__owl__.slotId = 1;
|
||||||
let fiber = w8.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||||
let pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
|
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}});
|
||||||
c7.push(pvnode);
|
c8.push(pvnode);
|
||||||
w8.__owl__.pvnode = pvnode;
|
w9.__owl__.pvnode = pvnode;
|
||||||
}
|
}
|
||||||
w8.__owl__.parentLastFiberId = extra.fiber.id;
|
w9.__owl__.parentLastFiberId = extra.fiber.id;
|
||||||
}
|
}
|
||||||
scope = _origScope6;
|
scope = _origScope6;
|
||||||
return vn1;
|
return vn1;
|
||||||
@@ -310,12 +310,12 @@ exports[`t-slot directive slots are rendered with proper context, part 2 3`] = `
|
|||||||
) {
|
) {
|
||||||
// Template name: \\"slot_default_template\\"
|
// Template name: \\"slot_default_template\\"
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c7 = extra.parentNode;
|
let c8 = extra.parentNode;
|
||||||
c7.push({text: \`User \`});
|
c8.push({text: \`User \`});
|
||||||
let _10 = scope['user'].name;
|
var _11 = scope['user'].name;
|
||||||
if (_10 != null) {
|
if (_11 || _11 === 0) {
|
||||||
c7.push({text: _10});
|
c8.push({text: _11});
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
@@ -325,15 +325,15 @@ exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
|
|||||||
) {
|
) {
|
||||||
// Template name: \\"Link\\"
|
// Template name: \\"Link\\"
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let _10 = scope['props'].to;
|
var _11 = scope['props'].to;
|
||||||
let c11 = [], p11 = {key:11,attrs:{href: _10}};
|
let c12 = [], p12 = {key:12,attrs:{href: _11}};
|
||||||
let vn11 = h('a', p11, c11);
|
var vn12 = h('a', p12, c12);
|
||||||
const slot12 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||||
if (slot12) {
|
if (slot13) {
|
||||||
slot12.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c11, parent: extra.parent || context}));
|
slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context}));
|
||||||
}
|
}
|
||||||
return vn11;
|
return vn12;
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -345,21 +345,21 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
|||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
let parent = context;
|
let parent = context;
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
let vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
let c2 = [], p2 = {key:2};
|
let c2 = [], p2 = {key:2};
|
||||||
let vn2 = h('u', p2, c2);
|
var vn2 = h('u', p2, c2);
|
||||||
c1.push(vn2);
|
c1.push(vn2);
|
||||||
let _3 = scope['state'].users;
|
var _3 = scope['state'].users;
|
||||||
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
||||||
let _4 = _5 = _3;
|
var _4 = _5 = _3;
|
||||||
if (!(_3 instanceof Array)) {
|
if (!(_3 instanceof Array)) {
|
||||||
_4 = Object.keys(_3);
|
_4 = Object.keys(_3);
|
||||||
_5 = Object.values(_3);
|
_5 = Object.values(_3);
|
||||||
}
|
}
|
||||||
let _length4 = _4.length;
|
var _length4 = _4.length;
|
||||||
let _origScope6 = scope;
|
const _origScope6 = scope;
|
||||||
scope = Object.assign(Object.create(context), scope);
|
scope = Object.assign(Object.create(context), scope);
|
||||||
for (let i1 = 0; i1 < _length4; i1++) {
|
for (let i1 = 0; i1 < _length4; i1++) {
|
||||||
scope.user_first = i1 === 0
|
scope.user_first = i1 === 0
|
||||||
@@ -367,36 +367,36 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
|||||||
scope.user_index = i1
|
scope.user_index = i1
|
||||||
scope.user = _4[i1]
|
scope.user = _4[i1]
|
||||||
scope.user_value = _5[i1]
|
scope.user_value = _5[i1]
|
||||||
let key1 = scope['user'].id;
|
const nodeKey7 = scope['user'].id;
|
||||||
let c7 = [], p7 = {key:\`\${key1}_7\`};
|
let c8 = [], p8 = {key:nodeKey7};
|
||||||
let vn7 = h('li', p7, c7);
|
var vn8 = h('li', p8, c8);
|
||||||
c2.push(vn7);
|
c2.push(vn8);
|
||||||
scope.userdescr = 'User '+scope['user'].name;
|
scope.userdescr = 'User '+scope['user'].name;
|
||||||
// Component 'Link'
|
// Component 'Link'
|
||||||
let k9 = \`__9__\${key1}__\`;
|
let k10 = \`__10__\` + nodeKey7;
|
||||||
let w8 = k9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k9]] : false;
|
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false;
|
||||||
let props8 = {to:'/user/'+scope['user'].id};
|
let props9 = {to:'/user/'+scope['user'].id};
|
||||||
if (w8 && w8.__owl__.currentFiber && !w8.__owl__.vnode) {
|
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) {
|
||||||
w8.destroy();
|
w9.destroy();
|
||||||
w8 = false;
|
w9 = false;
|
||||||
}
|
}
|
||||||
if (w8) {
|
if (w9) {
|
||||||
w8.__updateProps(props8, extra.fiber, Object.assign(Object.create(context), scope));
|
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope));
|
||||||
let pvnode = w8.__owl__.pvnode;
|
let pvnode = w9.__owl__.pvnode;
|
||||||
c7.push(pvnode);
|
c8.push(pvnode);
|
||||||
} else {
|
} else {
|
||||||
let componentKey8 = \`Link\`;
|
let componentKey9 = \`Link\`;
|
||||||
let W8 = context.constructor.components[componentKey8] || QWeb.components[componentKey8]|| scope['Link'];
|
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link'];
|
||||||
if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
|
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')}
|
||||||
w8 = new W8(parent, props8);
|
w9 = new W9(parent, props9);
|
||||||
parent.__owl__.cmap[k9] = w8.__owl__.id;
|
parent.__owl__.cmap[k10] = w9.__owl__.id;
|
||||||
w8.__owl__.slotId = 1;
|
w9.__owl__.slotId = 1;
|
||||||
let fiber = w8.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||||
let pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
|
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}});
|
||||||
c7.push(pvnode);
|
c8.push(pvnode);
|
||||||
w8.__owl__.pvnode = pvnode;
|
w9.__owl__.pvnode = pvnode;
|
||||||
}
|
}
|
||||||
w8.__owl__.parentLastFiberId = extra.fiber.id;
|
w9.__owl__.parentLastFiberId = extra.fiber.id;
|
||||||
}
|
}
|
||||||
scope = _origScope6;
|
scope = _origScope6;
|
||||||
return vn1;
|
return vn1;
|
||||||
@@ -408,10 +408,10 @@ exports[`t-slot directive slots are rendered with proper context, part 3 3`] = `
|
|||||||
) {
|
) {
|
||||||
// Template name: \\"slot_default_template\\"
|
// Template name: \\"slot_default_template\\"
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c7 = extra.parentNode;
|
let c8 = extra.parentNode;
|
||||||
if (scope.userdescr != null) {
|
if (scope.userdescr || scope.userdescr === 0) {
|
||||||
c7.push({text: scope.userdescr});
|
c8.push({text: scope.userdescr});
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
@@ -424,9 +424,9 @@ exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
|||||||
let QWeb = this.constructor;
|
let QWeb = this.constructor;
|
||||||
let parent = context;
|
let parent = context;
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
let vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
scope.userdescr = 'User '+scope['state'].user.name;
|
scope.userdescr = 'User '+scope['state'].user.name;
|
||||||
// Component 'Link'
|
// Component 'Link'
|
||||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||||
@@ -461,9 +461,9 @@ exports[`t-slot directive slots are rendered with proper context, part 4 2`] = `
|
|||||||
) {
|
) {
|
||||||
// Template name: \\"slot_default_template\\"
|
// Template name: \\"slot_default_template\\"
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let c1 = extra.parentNode;
|
let c1 = extra.parentNode;
|
||||||
if (scope.userdescr != null) {
|
if (scope.userdescr || scope.userdescr === 0) {
|
||||||
c1.push({text: scope.userdescr});
|
c1.push({text: scope.userdescr});
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -475,7 +475,7 @@ exports[`t-slot directive template can just return a slot 1`] = `
|
|||||||
// Template name: \\"__template__2\\"
|
// Template name: \\"__template__2\\"
|
||||||
let utils = this.constructor.utils;
|
let utils = this.constructor.utils;
|
||||||
let result;
|
let result;
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
const slot6 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
const slot6 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||||
if (slot6) {
|
if (slot6) {
|
||||||
let children7= []
|
let children7= []
|
||||||
|
|||||||
@@ -147,21 +147,6 @@ describe("basic widget properties", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div>1<button>Inc</button></div>");
|
expect(fixture.innerHTML).toBe("<div>1<button>Inc</button></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can handle empty props", async () => {
|
|
||||||
class Child extends Component<any, any> {
|
|
||||||
static template = xml`<span><t t-esc="props.val"/></span>`;
|
|
||||||
}
|
|
||||||
class Parent extends Component<any, any> {
|
|
||||||
static template = xml`<div><Child val=""/></div>`;
|
|
||||||
static components = { Child };
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span></span></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("cannot be clicked on and updated if not in DOM", async () => {
|
test("cannot be clicked on and updated if not in DOM", async () => {
|
||||||
class Counter extends Component<any, any> {
|
class Counter extends Component<any, any> {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
@@ -337,36 +322,6 @@ describe("basic widget properties", () => {
|
|||||||
console.warn = warn;
|
console.warn = warn;
|
||||||
});
|
});
|
||||||
|
|
||||||
test("reconciliation alg works for t-foreach in t-foreach, 2", async () => {
|
|
||||||
class Child extends Component<any, any> {
|
|
||||||
static template = xml`<div><t t-esc="props.row + '_' + props.col"/></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Component<any, any> {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<p t-foreach="state.rows" t-as="row" t-key="row">
|
|
||||||
<p t-foreach="state.cols" t-as="col" t-key="col">
|
|
||||||
<Child row="row" col="col"/>
|
|
||||||
</p>
|
|
||||||
</p>
|
|
||||||
</div>`;
|
|
||||||
static components = { Child };
|
|
||||||
state = useState({ rows: [1, 2], cols: ["a", "b"] });
|
|
||||||
}
|
|
||||||
|
|
||||||
const widget = new Parent();
|
|
||||||
await widget.mount(fixture);
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
"<div><p><p><div>1_a</div></p><p><div>1_b</div></p></p><p><p><div>2_a</div></p><p><div>2_b</div></p></p></div>"
|
|
||||||
);
|
|
||||||
widget.state.rows = [2, 1];
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
"<div><p><p><div>2_a</div></p><p><div>2_b</div></p></p><p><p><div>1_a</div></p><p><div>1_b</div></p></p></div>"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("same t-keys in two different places", async () => {
|
test("same t-keys in two different places", async () => {
|
||||||
class Child extends Component<any, any> {
|
class Child extends Component<any, any> {
|
||||||
static template = xml`<span><t t-esc="props.blip"/></span>`;
|
static template = xml`<span><t t-esc="props.blip"/></span>`;
|
||||||
@@ -384,7 +339,6 @@ describe("basic widget properties", () => {
|
|||||||
const widget = new Parent();
|
const widget = new Parent();
|
||||||
await widget.mount(fixture);
|
await widget.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><div><span>1</span></div><div><span>2</span></div></div>");
|
expect(fixture.innerHTML).toBe("<div><div><span>1</span></div><div><span>2</span></div></div>");
|
||||||
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-key on a component with t-if, and a sibling component", async () => {
|
test("t-key on a component with t-if, and a sibling component", async () => {
|
||||||
@@ -4464,28 +4418,6 @@ describe("t-model directive", () => {
|
|||||||
expect(comp.state[2].f).toBe(false);
|
expect(comp.state[2].f).toBe(false);
|
||||||
expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot();
|
expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("two inputs in a div with a t-key", async () => {
|
|
||||||
class SomeComponent extends Component<any, any> {
|
|
||||||
static template = xml`
|
|
||||||
<div t-key="'key'">
|
|
||||||
<input class="a" t-if="state.flag"/>
|
|
||||||
<input class="b" t-if="!state.flag"/>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
state = useState({ flag: true });
|
|
||||||
}
|
|
||||||
const comp = new SomeComponent();
|
|
||||||
await comp.mount(fixture);
|
|
||||||
expect(fixture.innerHTML).toBe('<div><input class="a"></div>');
|
|
||||||
expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot();
|
|
||||||
fixture.querySelector("input")!.value = "asdf";
|
|
||||||
expect(fixture.querySelector("input")!.value).toBe("asdf");
|
|
||||||
comp.state.flag = false;
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe('<div><input class="b"></div>');
|
|
||||||
expect(fixture.querySelector("input")!.value).toBe("");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("environment and plugins", () => {
|
describe("environment and plugins", () => {
|
||||||
@@ -4588,7 +4520,7 @@ describe("component error handling (catchError)", () => {
|
|||||||
}
|
}
|
||||||
const app = new App();
|
const app = new App();
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><div><div>heyfalse</div></div></div>");
|
expect(fixture.innerHTML).toBe("<div><div><div>hey</div></div></div>");
|
||||||
app.state.flag = true;
|
app.state.flag = true;
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||||
@@ -4623,7 +4555,7 @@ describe("component error handling (catchError)", () => {
|
|||||||
}
|
}
|
||||||
const app = new App();
|
const app = new App();
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><div>heyfalse</div></div>");
|
expect(fixture.innerHTML).toBe("<div><div>hey</div></div>");
|
||||||
app.state.flag = true;
|
app.state.flag = true;
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe("");
|
expect(fixture.innerHTML).toBe("");
|
||||||
@@ -5645,70 +5577,6 @@ describe("t-call", () => {
|
|||||||
expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot();
|
expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("parent is set within t-call", async () => {
|
|
||||||
env.qweb.addTemplate("sub", `<Child/>`);
|
|
||||||
let child;
|
|
||||||
class Child extends Component<any, any> {
|
|
||||||
static template = xml`<span>lucas</span>`;
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
child = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Parent extends Component<any, any> {
|
|
||||||
static components = { Child };
|
|
||||||
static template = xml`<div><t t-call="sub"/></div>`;
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>lucas</span></div>");
|
|
||||||
expect(child.__owl__.parent).toBe(parent);
|
|
||||||
expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-call in t-foreach and children component", async () => {
|
|
||||||
env.qweb.addTemplate("sub", `<Child val="val"/>`);
|
|
||||||
class Child extends Component<any, any> {
|
|
||||||
static template = xml`<span><t t-esc="props.val"/></span>`;
|
|
||||||
}
|
|
||||||
class Parent extends Component<any, any> {
|
|
||||||
static components = { Child };
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<t t-foreach="['a', 'b', 'c']" t-as="val" t-key="val">
|
|
||||||
<t t-call="sub"/>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>a</span><span>b</span><span>c</span></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("parent is set within t-call with no parentNode", async () => {
|
|
||||||
env.qweb.addTemplate("sub", `<Child/>`);
|
|
||||||
let child;
|
|
||||||
class Child extends Component<any, any> {
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
child = this;
|
|
||||||
}
|
|
||||||
static template = xml`<span>lucas</span>`;
|
|
||||||
}
|
|
||||||
class Parent extends Component<any, any> {
|
|
||||||
static components = { Child };
|
|
||||||
static template = xml`<t t-call="sub"/>`;
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<span>lucas</span>");
|
|
||||||
expect(child.__owl__.parent).toBe(parent);
|
|
||||||
expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("handlers with arguments are properly bound through a t-call", async () => {
|
test("handlers with arguments are properly bound through a t-call", async () => {
|
||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
env.qweb.addTemplate("sub", `<p t-on-click="update(a)">lucas</p>`);
|
env.qweb.addTemplate("sub", `<p t-on-click="update(a)">lucas</p>`);
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ let env: Env;
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = makeTestFixture();
|
fixture = makeTestFixture();
|
||||||
env = makeTestEnv();
|
env = makeTestEnv();
|
||||||
|
env.qweb.addTemplate("Component<any,any>", "<div></div>");
|
||||||
|
env.qweb.addTemplate(
|
||||||
|
"Counter",
|
||||||
|
`<div><t t-esc="state.counter"/><button t-on-click="inc">Inc</button></div>`
|
||||||
|
);
|
||||||
|
env.qweb.addTemplate("WidgetA", `<div>Hello<t t-component="b"/></div>`);
|
||||||
|
env.qweb.addTemplate("WidgetB", `<div>world</div>`);
|
||||||
Component.env = env;
|
Component.env = env;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
import { Component, Env } from "../../src/component/component";
|
|
||||||
import { xml, css } from "../../src/tags";
|
|
||||||
import { makeTestFixture, makeTestEnv } from "../helpers";
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Setup and helpers
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// We create before each test:
|
|
||||||
// - fixture: a div, appended to the DOM, intended to be the target of dom
|
|
||||||
// manipulations. Note that it is removed after each test.
|
|
||||||
// - env: an Env, necessary to create new components
|
|
||||||
|
|
||||||
let fixture: HTMLElement;
|
|
||||||
let env: Env;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = makeTestFixture();
|
|
||||||
env = makeTestEnv();
|
|
||||||
Component.env = env;
|
|
||||||
document.head.innerHTML = "";
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
fixture.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Tests
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
describe("styles and component", () => {
|
|
||||||
test("can define an inline stylesheet", async () => {
|
|
||||||
class App extends Component<any, any> {
|
|
||||||
static template = xml`<div class="app">text</div>`;
|
|
||||||
static style = css`
|
|
||||||
.app {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
expect(document.head.innerHTML).toBe("");
|
|
||||||
const app = new App();
|
|
||||||
|
|
||||||
expect(document.head.innerHTML).toBe(`<style component=\"App\">.app {
|
|
||||||
color: red;
|
|
||||||
}</style>`);
|
|
||||||
|
|
||||||
await app.mount(fixture);
|
|
||||||
const style = getComputedStyle(app.el!);
|
|
||||||
expect(style.color).toBe("red");
|
|
||||||
expect(fixture.innerHTML).toBe('<div class="app">text</div>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test("inherited components properly apply css", async () => {
|
|
||||||
class App extends Component<any, any> {
|
|
||||||
static template = xml`<div class="app">text</div>`;
|
|
||||||
static style = css`
|
|
||||||
.app {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
class SubApp extends App {
|
|
||||||
static style = css`
|
|
||||||
.app {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
expect(document.head.innerHTML).toBe("");
|
|
||||||
const app = new SubApp();
|
|
||||||
|
|
||||||
expect(document.head.innerHTML).toBe(`<style component=\"SubApp\">.app {
|
|
||||||
font-weight: bold;
|
|
||||||
}</style><style component=\"App\">.app {
|
|
||||||
color: red;
|
|
||||||
}</style>`);
|
|
||||||
|
|
||||||
await app.mount(fixture);
|
|
||||||
const style = getComputedStyle(app.el!);
|
|
||||||
expect(style.color).toBe("red");
|
|
||||||
expect(style.fontWeight).toBe("bold");
|
|
||||||
expect(fixture.innerHTML).toBe('<div class="app">text</div>');
|
|
||||||
});
|
|
||||||
|
|
||||||
test("get a meaningful error message if css helper is missing", async () => {
|
|
||||||
class App extends Component<any, any> {
|
|
||||||
static template = xml`<div class="app">text</div>`;
|
|
||||||
static style = `.app {color: red;}`;
|
|
||||||
}
|
|
||||||
let error;
|
|
||||||
try {
|
|
||||||
new App();
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
expect(error.message).toBe(
|
|
||||||
"Invalid css stylesheet for component 'App'. Did you forget to use the 'css' tag helper?"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("inline stylesheets are processed", async () => {
|
|
||||||
class App extends Component<any, any> {
|
|
||||||
static template = xml`<div class="app">text</div>`;
|
|
||||||
static style = css`
|
|
||||||
.app {
|
|
||||||
color: red;
|
|
||||||
.some-class {
|
|
||||||
font-weight: bold;
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
new App();
|
|
||||||
|
|
||||||
expect(document.head.querySelector("style")!.innerHTML).toBe(`.app {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.app .some-class {
|
|
||||||
font-weight: bold;
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
.app {
|
|
||||||
display: block;
|
|
||||||
}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -429,4 +429,44 @@ describe("observer", () => {
|
|||||||
obj.a.push(2);
|
obj.a.push(2);
|
||||||
}).toThrow('Observed state cannot be changed here! (key: "1", val: "2")');
|
}).toThrow('Observed state cannot be changed here! (key: "1", val: "2")');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("functions are not observed, but are properly bound", () => {
|
||||||
|
const observer = new Observer();
|
||||||
|
const obj: any = observer.observe({ m: new Map() });
|
||||||
|
expect(observer.rev).toBe(1);
|
||||||
|
obj.m.set("a", 1);
|
||||||
|
expect(obj.m.get("a")).toBe(1);
|
||||||
|
|
||||||
|
// should be 2, but we do not support reactivity on maps/weakmaps/set/weakset
|
||||||
|
expect(observer.rev).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("functions are correctly bound", async () => {
|
||||||
|
const observer = new Observer();
|
||||||
|
const obj: any = observer.observe({ set: new Set() });
|
||||||
|
expect(observer.revNumber(obj)).toBe(1);
|
||||||
|
obj.set.add('mojo');
|
||||||
|
expect(observer.revNumber(obj)).toBe(1);
|
||||||
|
|
||||||
|
const steps: string[] = [];
|
||||||
|
obj.set.forEach((elm)=>{steps.push(elm)});
|
||||||
|
|
||||||
|
expect(steps).toStrictEqual(['mojo']);
|
||||||
|
|
||||||
|
obj.set.clear();
|
||||||
|
expect(observer.revNumber(obj)).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("functions are correctly bound", async () => {
|
||||||
|
const observer = new Observer();
|
||||||
|
const obj: any = observer.observe({ set: [] });
|
||||||
|
expect(observer.revNumber(obj)).toBe(1);
|
||||||
|
obj.set.push('mojo');
|
||||||
|
expect(observer.revNumber(obj)).toBe(2);
|
||||||
|
|
||||||
|
const steps: string[] = [];
|
||||||
|
obj.set.forEach((elm)=>{steps.push(elm)});
|
||||||
|
|
||||||
|
expect(steps).toStrictEqual(['mojo']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -141,49 +141,17 @@ describe("t-esc", () => {
|
|||||||
qweb.addTemplate("test", `<span t-esc="var">nope</span>`);
|
qweb.addTemplate("test", `<span t-esc="var">nope</span>`);
|
||||||
expect(renderToString(qweb, "test")).toBe("<span>nope</span>");
|
expect(renderToString(qweb, "test")).toBe("<span>nope</span>");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-esc is escaped", () => {
|
test("t-esc is escaped", () => {
|
||||||
qweb.addTemplate("test", `<div><t t-set="var"><p>escaped</p></t><t t-esc="var"/></div>`);
|
qweb.addTemplate("test", `<div><t t-set="var"><p>escaped</p></t><t t-esc="var"/></div>`);
|
||||||
const domRendered = renderToDOM(qweb, "test");
|
const domRendered = renderToDOM(qweb, "test");
|
||||||
expect(domRendered.textContent).toBe("<p>escaped</p>");
|
expect(domRendered.textContent).toBe("<p>escaped</p>");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-esc=0 is escaped", () => {
|
test("t-esc=0 is escaped", () => {
|
||||||
qweb.addTemplate("test", `<span><t t-esc="0"/></span>`);
|
qweb.addTemplate("test", `<span><t t-esc="0"/></span>`);
|
||||||
qweb.addTemplate("testCaller", `<div><t t-call="test"><p>escaped</p></t></div>`);
|
qweb.addTemplate("testCaller", `<div><t t-call="test"><p>escaped</p></t></div>`);
|
||||||
const domRendered = renderToDOM(qweb, "testCaller") as HTMLElement;
|
const domRendered = renderToDOM(qweb, "testCaller") as HTMLElement;
|
||||||
expect(domRendered.querySelector("span")!.textContent).toBe("<p>escaped</p>");
|
expect(domRendered.querySelector("span")!.textContent).toBe("<p>escaped</p>");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("div with falsy values", () => {
|
|
||||||
qweb.addTemplate(
|
|
||||||
"test",
|
|
||||||
`
|
|
||||||
<div>
|
|
||||||
<p t-esc="v1"/>
|
|
||||||
<p t-esc="v2"/>
|
|
||||||
<p t-esc="v3"/>
|
|
||||||
<p t-esc="v4"/>
|
|
||||||
<p t-esc="v5"/>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
const vals = {
|
|
||||||
v1: false,
|
|
||||||
v2: undefined,
|
|
||||||
v3: null,
|
|
||||||
v4: 0,
|
|
||||||
v5: ""
|
|
||||||
};
|
|
||||||
expect(renderToString(qweb, "test", vals)).toBe(
|
|
||||||
"<div><p>false</p><p></p><p></p><p>0</p><p></p></div>"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-esc work with spread operator", () => {
|
|
||||||
qweb.addTemplate("test", `<span><t t-esc="[...state.list]"/></span>`);
|
|
||||||
const result = renderToString(qweb, "test", { state: { list: [1, 2] } });
|
|
||||||
expect(result).toBe("<span>1,2</span>");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("t-raw", () => {
|
describe("t-raw", () => {
|
||||||
@@ -290,18 +258,6 @@ describe("t-set", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-set with content and sub t-esc", () => {
|
|
||||||
qweb.addTemplate(
|
|
||||||
"test",
|
|
||||||
`<div>
|
|
||||||
<t t-set="setvar"><t t-esc="beep"/> boop</t>
|
|
||||||
<t t-esc="setvar"/>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(renderToString(qweb, "test", { beep: "beep" })).toBe("<div>beep boop</div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("evaluate value expression, part 2", () => {
|
test("evaluate value expression, part 2", () => {
|
||||||
qweb.addTemplate(
|
qweb.addTemplate(
|
||||||
"test",
|
"test",
|
||||||
|
|||||||
@@ -50,12 +50,6 @@ describe("tokenizer", () => {
|
|||||||
{ type: "OPERATOR", value: "typeof " },
|
{ type: "OPERATOR", value: "typeof " },
|
||||||
{ type: "SYMBOL", value: "a" }
|
{ type: "SYMBOL", value: "a" }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(tokenize("a...1")).toEqual([
|
|
||||||
{ type: "SYMBOL", value: "a" },
|
|
||||||
{ type: "OPERATOR", value: "..." },
|
|
||||||
{ type: "VALUE", value: "1" }
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("strings", () => {
|
test("strings", () => {
|
||||||
@@ -183,10 +177,4 @@ describe("expression evaluation", () => {
|
|||||||
expect(compileExpr("a -= b", {})).toBe("scope['a']-=scope['b']");
|
expect(compileExpr("a -= b", {})).toBe("scope['a']-=scope['b']");
|
||||||
expect(compileExpr("a.b = !a.b", {})).toBe("scope['a'].b=!scope['a'].b");
|
expect(compileExpr("a.b = !a.b", {})).toBe("scope['a'].b=!scope['a'].b");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("spread operator", () => {
|
|
||||||
expect(compileExpr("[...state.list]", {})).toBe("[...scope['state'].list]");
|
|
||||||
expect(compileExpr("f(...state.list)", {})).toBe("scope['f'](...scope['state'].list)");
|
|
||||||
expect(compileExpr("f([...list])", {})).toBe("scope['f']([...scope['list']])");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ exports[`Link component can render simple cases 1`] = `
|
|||||||
// Template name: \\"__template__1\\"
|
// Template name: \\"__template__1\\"
|
||||||
let utils = this.constructor.utils;
|
let utils = this.constructor.utils;
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
let _4 = utils.toObj({'router-link-active':scope['isActive']});
|
let _4 = utils.toObj({'router-link-active':scope['isActive']});
|
||||||
let _5 = scope['href'];
|
var _5 = scope['href'];
|
||||||
let c6 = [], p6 = {key:6,attrs:{href: _5},class:_4,on:{}};
|
let c6 = [], p6 = {key:6,attrs:{href: _5},class:_4,on:{}};
|
||||||
let vn6 = h('a', p6, c6);
|
var vn6 = h('a', p6, c6);
|
||||||
extra.handlers['click__7__'] = extra.handlers['click__7__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['navigate'](e);};
|
extra.handlers['click__7__'] = extra.handlers['click__7__'] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['navigate'](e);};
|
||||||
p6.on['click'] = extra.handlers['click__7__'];
|
p6.on['click'] = extra.handlers['click__7__'];
|
||||||
const slot8 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
const slot8 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||||
|
|||||||
@@ -9,37 +9,35 @@ exports[`RouteComponent can render simple cases 1`] = `
|
|||||||
let parent = context;
|
let parent = context;
|
||||||
let scope = Object.create(context);
|
let scope = Object.create(context);
|
||||||
let result;
|
let result;
|
||||||
let h = this.h;
|
var h = this.h;
|
||||||
if (scope['routeComponent']) {
|
if (scope['routeComponent']) {
|
||||||
{
|
const nodeKey4 = scope['env'].router.currentRouteName;
|
||||||
let key0 = scope['env'].router.currentRouteName;
|
// Component 'routeComponent'
|
||||||
// Component 'routeComponent'
|
let k6 = \`__6__\` + nodeKey4;
|
||||||
let k5 = \`__5__\${key0}__\`;
|
let w5 = k6 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k6]] : false;
|
||||||
let w4 = k5 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k5]] : false;
|
let vn7 = {};
|
||||||
let vn6 = {};
|
result = vn7;
|
||||||
result = vn6;
|
let props5 = Object.assign({}, scope['env'].router.currentParams);
|
||||||
let props4 = Object.assign({}, scope['env'].router.currentParams);
|
if (w5 && w5.__owl__.currentFiber && !w5.__owl__.vnode) {
|
||||||
if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) {
|
w5.destroy();
|
||||||
w4.destroy();
|
w5 = false;
|
||||||
w4 = false;
|
|
||||||
}
|
|
||||||
if (w4) {
|
|
||||||
w4.__updateProps(props4, extra.fiber, undefined);
|
|
||||||
let pvnode = w4.__owl__.pvnode;
|
|
||||||
utils.defineProxy(vn6, pvnode);
|
|
||||||
} else {
|
|
||||||
let componentKey4 = \`routeComponent\`;
|
|
||||||
let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| scope['routeComponent'];
|
|
||||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
|
||||||
w4 = new W4(parent, props4);
|
|
||||||
parent.__owl__.cmap[k5] = w4.__owl__.id;
|
|
||||||
let fiber = w4.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
|
||||||
let pvnode = h('dummy', {key: k5, hook: {remove() {},destroy(vn) {w4.destroy();}}});
|
|
||||||
utils.defineProxy(vn6, pvnode);
|
|
||||||
w4.__owl__.pvnode = pvnode;
|
|
||||||
}
|
|
||||||
w4.__owl__.parentLastFiberId = extra.fiber.id;
|
|
||||||
}
|
}
|
||||||
|
if (w5) {
|
||||||
|
w5.__updateProps(props5, extra.fiber, undefined);
|
||||||
|
let pvnode = w5.__owl__.pvnode;
|
||||||
|
utils.defineProxy(vn7, pvnode);
|
||||||
|
} else {
|
||||||
|
let componentKey5 = \`routeComponent\`;
|
||||||
|
let W5 = context.constructor.components[componentKey5] || QWeb.components[componentKey5]|| scope['routeComponent'];
|
||||||
|
if (!W5) {throw new Error('Cannot find the definition of component \\"' + componentKey5 + '\\"')}
|
||||||
|
w5 = new W5(parent, props5);
|
||||||
|
parent.__owl__.cmap[k6] = w5.__owl__.id;
|
||||||
|
let fiber = w5.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||||
|
let pvnode = h('dummy', {key: k6, hook: {remove() {},destroy(vn) {w5.destroy();}}});
|
||||||
|
utils.defineProxy(vn7, pvnode);
|
||||||
|
w5.__owl__.pvnode = pvnode;
|
||||||
|
}
|
||||||
|
w5.__owl__.parentLastFiberId = extra.fiber.id;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -49,13 +49,10 @@ describe("connecting a component to store", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("useStore can observe primitive types and call onUpdate", async () => {
|
test("useStore can observe primitive types and call onUpdate", async () => {
|
||||||
const state = { isBoolean: false, nullValue: null };
|
const state = { isBoolean: false };
|
||||||
const actions = {
|
const actions = {
|
||||||
setTrue({ state }) {
|
setTrue({ state }) {
|
||||||
state.isBoolean = true;
|
state.isBoolean = true;
|
||||||
},
|
|
||||||
setNotNull({ state }) {
|
|
||||||
state.nullValue = "ok";
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const store = new Store({ state, actions });
|
const store = new Store({ state, actions });
|
||||||
@@ -64,10 +61,8 @@ describe("connecting a component to store", () => {
|
|||||||
static template = xml`
|
static template = xml`
|
||||||
<div>
|
<div>
|
||||||
<span t-if="isBoolean">ok</span>
|
<span t-if="isBoolean">ok</span>
|
||||||
<span t-if="nullValue !== null">not null</span>
|
|
||||||
</div>`;
|
</div>`;
|
||||||
isBoolean: boolean;
|
isBoolean: boolean;
|
||||||
nullValue: string;
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.isBoolean = useStore(state => state.isBoolean, {
|
this.isBoolean = useStore(state => state.isBoolean, {
|
||||||
@@ -75,11 +70,6 @@ describe("connecting a component to store", () => {
|
|||||||
this.isBoolean = isBoolean;
|
this.isBoolean = isBoolean;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.nullValue = useStore(state => state.nullValue, {
|
|
||||||
onUpdate: nullValue => {
|
|
||||||
this.nullValue = nullValue;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,42 +82,6 @@ describe("connecting a component to store", () => {
|
|||||||
store.dispatch("setTrue");
|
store.dispatch("setTrue");
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe("<div><span>ok</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>ok</span></div>");
|
||||||
|
|
||||||
store.dispatch("setNotNull");
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>ok</span><span>not null</span></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("map works on the result of useStore when the resulting array changes for a bigger one", async () => {
|
|
||||||
const state = { smallerArray: [1], biggerArray: [2, 3], useSmallArray: true };
|
|
||||||
const store = new Store({ state });
|
|
||||||
|
|
||||||
class App extends Component<any, any> {
|
|
||||||
static template = xml`<div t-esc="mapAdd"/>`;
|
|
||||||
storeProps = {
|
|
||||||
array: useStore(state => {
|
|
||||||
if (state.useSmallArray) {
|
|
||||||
return state.smallerArray;
|
|
||||||
}
|
|
||||||
return state.biggerArray;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
get mapAdd() {
|
|
||||||
return this.storeProps.array.map(a => {
|
|
||||||
return a + 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(<any>env).store = store;
|
|
||||||
const app = new App();
|
|
||||||
|
|
||||||
await app.mount(fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("<div>2</div>");
|
|
||||||
|
|
||||||
store.state.useSmallArray = false;
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe("<div>3,4</div>");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("throw error if no store is found", async () => {
|
test("throw error if no store is found", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user