mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[REF] owl: reorganize src code in sub files
This commit is contained in:
@@ -1,5 +1,28 @@
|
||||
# 🦉 OWL Documentation 🦉
|
||||
|
||||
## Owl Content
|
||||
|
||||
Owl is a javascript library that contains some core classes and function to help
|
||||
build applications. Here is a complete representation of its content:
|
||||
|
||||
```
|
||||
owl
|
||||
Component
|
||||
QWeb
|
||||
core
|
||||
EventBus
|
||||
Observer
|
||||
store
|
||||
Store
|
||||
ConnectedComponent
|
||||
utils
|
||||
whenReady
|
||||
loadJS
|
||||
loadTemplates
|
||||
escape
|
||||
debounce
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
- [Animations](animations.md)
|
||||
|
||||
+3
-3
@@ -30,6 +30,7 @@
|
||||
"cpx": "^1.5.0",
|
||||
"git-rev-sync": "^1.12.0",
|
||||
"jest": "^23.6.0",
|
||||
"jest-environment-jsdom": "^24.7.1",
|
||||
"live-server": "^1.2.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rollup": "^1.6.0",
|
||||
@@ -38,8 +39,7 @@
|
||||
"source-map-support": "^0.5.10",
|
||||
"ts-jest": "^23.10.5",
|
||||
"typescript": "^3.2.2",
|
||||
"uglify-es": "^3.3.9",
|
||||
"jest-environment-jsdom": "^24.7.1"
|
||||
"uglify-es": "^3.3.9"
|
||||
},
|
||||
"dependencies": {},
|
||||
"jest": {
|
||||
@@ -62,6 +62,6 @@
|
||||
]
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 100
|
||||
"printWidth": 100
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Observer } from "./observer";
|
||||
import { QWeb, CompiledTemplate, UTILS } from "./qweb_core";
|
||||
import { h, patch, VNode } from "./vdom";
|
||||
import { Observer } from "../core/observer";
|
||||
import { CompiledTemplate, QWeb } from "../qweb/index";
|
||||
import { h, patch, VNode } from "../vdom/index";
|
||||
import "./directive";
|
||||
import "./props_validation";
|
||||
|
||||
/**
|
||||
* Owl Component System
|
||||
@@ -135,7 +137,7 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
|
||||
if (QWeb.dev) {
|
||||
// we only validate props for root widgets here. "Regular" widget
|
||||
// props are validated by the t-component directive
|
||||
UTILS.validateProps(this.constructor, this.props);
|
||||
QWeb.utils.validateProps(this.constructor, this.props);
|
||||
}
|
||||
this.env.qweb.on("update", this, () => {
|
||||
if (this.__owl__.isMounted) {
|
||||
@@ -1,186 +1,5 @@
|
||||
import { QWeb, UTILS } from "./qweb_core";
|
||||
import { VNode } from "./vdom";
|
||||
|
||||
/**
|
||||
* Owl QWeb Extensions
|
||||
*
|
||||
* This file contains the implementation of non standard QWeb directives, added
|
||||
* by Owl and that will only work on Owl projects:
|
||||
*
|
||||
* - t-on
|
||||
* - t-ref
|
||||
* - t-transition
|
||||
* - t-component/t-keepalive
|
||||
* - t-mounted
|
||||
* - t-slot
|
||||
* - t-model
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-on
|
||||
//------------------------------------------------------------------------------
|
||||
// these are pieces of code that will be injected into the event handler if
|
||||
// modifiers are specified
|
||||
const MODS_CODE = {
|
||||
prevent: "e.preventDefault();",
|
||||
self: "if (e.target !== this.elm) {return}",
|
||||
stop: "e.stopPropagation();"
|
||||
};
|
||||
|
||||
QWeb.addDirective({
|
||||
name: "on",
|
||||
priority: 90,
|
||||
atNodeCreation({ ctx, fullName, value, nodeID }) {
|
||||
ctx.rootContext.shouldDefineOwner = true;
|
||||
const [eventName, ...mods] = fullName.slice(5).split(".");
|
||||
if (!eventName) {
|
||||
throw new Error("Missing event name with t-on directive");
|
||||
}
|
||||
let extraArgs;
|
||||
let handlerName = value.replace(/\(.*\)/, function(args) {
|
||||
extraArgs = args.slice(1, -1);
|
||||
return "";
|
||||
});
|
||||
ctx.addIf(`!context['${handlerName}']`);
|
||||
ctx.addLine(
|
||||
`throw new Error('Missing handler \\'' + '${handlerName}' + \`\\' when evaluating template '${ctx.templateName.replace(
|
||||
/`/g,
|
||||
"'"
|
||||
)}'\`)`
|
||||
);
|
||||
ctx.closeIf();
|
||||
let params = extraArgs ? `owner, ${ctx.formatExpression(extraArgs)}` : "owner";
|
||||
let handler;
|
||||
if (mods.length > 0) {
|
||||
handler = `function (e) {`;
|
||||
handler += mods
|
||||
.map(function(mod) {
|
||||
return MODS_CODE[mod];
|
||||
})
|
||||
.join("");
|
||||
handler += `context['${handlerName}'].call(${params}, e);}`;
|
||||
} else {
|
||||
handler = `context['${handlerName}'].bind(${params})`;
|
||||
}
|
||||
if (extraArgs) {
|
||||
ctx.addLine(`p${nodeID}.on['${eventName}'] = ${handler};`);
|
||||
} else {
|
||||
ctx.addLine(
|
||||
`extra.handlers['${eventName}' + ${nodeID}] = extra.handlers['${eventName}' + ${nodeID}] || ${handler};`
|
||||
);
|
||||
ctx.addLine(`p${nodeID}.on['${eventName}'] = extra.handlers['${eventName}' + ${nodeID}];`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-ref
|
||||
//------------------------------------------------------------------------------
|
||||
QWeb.addDirective({
|
||||
name: "ref",
|
||||
priority: 95,
|
||||
atNodeCreation({ ctx, value, addNodeHook }) {
|
||||
const refKey = `ref${ctx.generateID()}`;
|
||||
ctx.addLine(`const ${refKey} = ${ctx.interpolate(value)};`);
|
||||
addNodeHook("create", `context.refs[${refKey}] = n.elm;`);
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-transition
|
||||
//------------------------------------------------------------------------------
|
||||
UTILS.nextFrame = function(cb: () => void) {
|
||||
requestAnimationFrame(() => requestAnimationFrame(cb));
|
||||
};
|
||||
|
||||
UTILS.transitionInsert = function(vn: VNode, name: string) {
|
||||
const elm = <HTMLElement>vn.elm;
|
||||
// remove potential duplicated vnode that is currently being removed, to
|
||||
// prevent from having twice the same node in the DOM during an animation
|
||||
const dup = elm.parentElement && elm.parentElement!.querySelector(`*[data-owl-key='${vn.key}']`);
|
||||
if (dup) {
|
||||
dup.remove();
|
||||
}
|
||||
|
||||
elm.classList.add(name + "-enter");
|
||||
elm.classList.add(name + "-enter-active");
|
||||
const finalize = () => {
|
||||
elm.classList.remove(name + "-enter-active");
|
||||
elm.classList.remove(name + "-enter-to");
|
||||
};
|
||||
this.nextFrame(() => {
|
||||
elm.classList.remove(name + "-enter");
|
||||
elm.classList.add(name + "-enter-to");
|
||||
whenTransitionEnd(elm, finalize);
|
||||
});
|
||||
};
|
||||
|
||||
UTILS.transitionRemove = function(vn: VNode, name: string, rm: () => void) {
|
||||
const elm = <HTMLElement>vn.elm;
|
||||
elm.setAttribute("data-owl-key", vn.key!);
|
||||
|
||||
elm.classList.add(name + "-leave");
|
||||
elm.classList.add(name + "-leave-active");
|
||||
const finalize = () => {
|
||||
elm.classList.remove(name + "-leave-active");
|
||||
elm.classList.remove(name + "-leave-to");
|
||||
rm();
|
||||
};
|
||||
this.nextFrame(() => {
|
||||
elm.classList.remove(name + "-leave");
|
||||
elm.classList.add(name + "-leave-to");
|
||||
whenTransitionEnd(elm, finalize);
|
||||
});
|
||||
};
|
||||
|
||||
function getTimeout(delays: Array<string>, durations: Array<string>): number {
|
||||
/* istanbul ignore next */
|
||||
while (delays.length < durations.length) {
|
||||
delays = delays.concat(delays);
|
||||
}
|
||||
|
||||
return Math.max.apply(
|
||||
null,
|
||||
durations.map((d, i) => {
|
||||
return toMs(d) + toMs(delays[i]);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Old versions of Chromium (below 61.0.3163.100) formats floating pointer numbers
|
||||
// in a locale-dependent way, using a comma instead of a dot.
|
||||
// If comma is not replaced with a dot, the input will be rounded down (i.e. acting
|
||||
// as a floor function) causing unexpected behaviors
|
||||
function toMs(s: string): number {
|
||||
return Number(s.slice(0, -1).replace(",", ".")) * 1000;
|
||||
}
|
||||
|
||||
function whenTransitionEnd(elm: HTMLElement, cb) {
|
||||
const styles = window.getComputedStyle(elm);
|
||||
const delays: Array<string> = (styles.transitionDelay || "").split(", ");
|
||||
const durations: Array<string> = (styles.transitionDuration || "").split(", ");
|
||||
const timeout: number = getTimeout(delays, durations);
|
||||
if (timeout > 0) {
|
||||
elm.addEventListener("transitionend", cb, { once: true });
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
QWeb.addDirective({
|
||||
name: "transition",
|
||||
priority: 96,
|
||||
atNodeCreation({ value, addNodeHook }) {
|
||||
let name = value;
|
||||
const hooks = {
|
||||
insert: `this.utils.transitionInsert(vn, '${name}');`,
|
||||
remove: `this.utils.transitionRemove(vn, '${name}', rm);`
|
||||
};
|
||||
for (let hookName in hooks) {
|
||||
addNodeHook(hookName, hooks[hookName]);
|
||||
}
|
||||
}
|
||||
});
|
||||
import { QWeb } from "../qweb/index";
|
||||
import { MODS_CODE } from "../qweb/extensions";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-component
|
||||
@@ -190,7 +9,7 @@ const T_COMPONENT_MODS_CODE = Object.assign({}, MODS_CODE, {
|
||||
self: "if (e.target !== vn.elm) {return}"
|
||||
});
|
||||
|
||||
UTILS.defineProxy = function defineProxy(target, source) {
|
||||
QWeb.utils.defineProxy = function defineProxy(target, source) {
|
||||
for (let k in source) {
|
||||
Object.defineProperty(target, k, {
|
||||
get() {
|
||||
@@ -673,198 +492,3 @@ QWeb.addDirective({
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Prop validation helper
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Validate the component props (or next props) against the (static) props
|
||||
* description. This is potentially an expensive operation: it may needs to
|
||||
* visit recursively the props and all the children to check if they are valid.
|
||||
* This is why it is only done in 'dev' mode.
|
||||
*/
|
||||
UTILS.validateProps = function(Widget, props: Object) {
|
||||
const propsDef = (<any>Widget).props;
|
||||
if (propsDef instanceof Array) {
|
||||
// list of strings (prop names)
|
||||
for (let i = 0, l = propsDef.length; i < l; i++) {
|
||||
const propName = propsDef[i];
|
||||
if (propName[propName.length - 1] === "?") {
|
||||
// optional prop
|
||||
break;
|
||||
}
|
||||
if (!props[propName]) {
|
||||
throw new Error(`Missing props '${propsDef[i]}' (component '${Widget.name}')`);
|
||||
}
|
||||
}
|
||||
for (let key in props) {
|
||||
if (!propsDef.includes(key) && !propsDef.includes(key + "?")) {
|
||||
throw new Error(`Unknown prop '${key}' given to component '${Widget.name}'`);
|
||||
}
|
||||
}
|
||||
} else if (propsDef) {
|
||||
// propsDef is an object now
|
||||
for (let propName in propsDef) {
|
||||
if (props[propName] === undefined) {
|
||||
if (propsDef[propName] && !propsDef[propName].optional) {
|
||||
throw new Error(`Missing props '${propName}' (component '${Widget.name}')`);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let isValid = isValidProp(props[propName], propsDef[propName]);
|
||||
if (!isValid) {
|
||||
throw new Error(`Props '${propName}' of invalid type in component '${Widget.name}'`);
|
||||
}
|
||||
}
|
||||
for (let propName in props) {
|
||||
if (!(propName in propsDef)) {
|
||||
throw new Error(`Unknown prop '${propName}' given to component '${Widget.name}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if an invidual prop value matches its (static) prop definition
|
||||
*/
|
||||
function isValidProp(prop, propDef): boolean {
|
||||
if (propDef === true) {
|
||||
return true;
|
||||
}
|
||||
if (typeof propDef === "function") {
|
||||
// Check if a value is constructed by some Constructor. Note that there is a
|
||||
// slight abuse of language: we want to consider primitive values as well.
|
||||
//
|
||||
// So, even though 1 is not an instance of Number, we want to consider that
|
||||
// it is valid.
|
||||
if (typeof prop === "object") {
|
||||
return prop instanceof propDef;
|
||||
}
|
||||
return typeof prop === propDef.name.toLowerCase();
|
||||
} else if (propDef instanceof Array) {
|
||||
// If this code is executed, this means that we want to check if a prop
|
||||
// matches at least one of its descriptor.
|
||||
let result = false;
|
||||
for (let i = 0, iLen = propDef.length; i < iLen; i++) {
|
||||
result = result || isValidProp(prop, propDef[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// propsDef is an object
|
||||
let result = isValidProp(prop, propDef.type);
|
||||
if (propDef.type === Array) {
|
||||
for (let i = 0, iLen = prop.length; i < iLen; i++) {
|
||||
result = result && isValidProp(prop[i], propDef.element);
|
||||
}
|
||||
}
|
||||
if (propDef.type === Object) {
|
||||
const shape = propDef.shape;
|
||||
for (let key in shape) {
|
||||
result = result && isValidProp(prop[key], shape[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-mounted
|
||||
//------------------------------------------------------------------------------
|
||||
QWeb.addDirective({
|
||||
name: "mounted",
|
||||
priority: 97,
|
||||
atNodeCreation({ ctx, fullName, value, nodeID, addNodeHook }) {
|
||||
ctx.rootContext.shouldDefineOwner = true;
|
||||
const eventName = fullName.slice(5);
|
||||
if (!eventName) {
|
||||
throw new Error("Missing event name with t-on directive");
|
||||
}
|
||||
let extraArgs;
|
||||
let handler = value.replace(/\(.*\)/, function(args) {
|
||||
extraArgs = args.slice(1, -1);
|
||||
return "";
|
||||
});
|
||||
let error = `(function () {throw new Error('Missing handler \\'' + '${handler}' + \`\\' when evaluating template '${ctx.templateName.replace(
|
||||
/`/g,
|
||||
"'"
|
||||
)}'\`)})()`;
|
||||
if (extraArgs) {
|
||||
ctx.addLine(
|
||||
`extra.mountedHandlers[${nodeID}] = (context['${handler}'] || ${error}).bind(owner, ${ctx.formatExpression(
|
||||
extraArgs
|
||||
)});`
|
||||
);
|
||||
} else {
|
||||
ctx.addLine(
|
||||
`extra.mountedHandlers[${nodeID}] = extra.mountedHandlers[${nodeID}] || (context['${handler}'] || ${error}).bind(owner);`
|
||||
);
|
||||
}
|
||||
addNodeHook("insert", `if (context.__owl__.isMounted) { extra.mountedHandlers[${nodeID}](); }`);
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-slot
|
||||
//------------------------------------------------------------------------------
|
||||
QWeb.addDirective({
|
||||
name: "slot",
|
||||
priority: 80,
|
||||
atNodeEncounter({ ctx, value }): boolean {
|
||||
const slotKey = ctx.generateID();
|
||||
ctx.rootContext.shouldDefineOwner = true;
|
||||
ctx.addLine(`const slot${slotKey} = this.slots[context.__owl__.slotId + '_' + '${value}'];`);
|
||||
ctx.addIf(`slot${slotKey}`);
|
||||
ctx.addLine(
|
||||
`slot${slotKey}(context.__owl__.parent, Object.assign({}, extra, {parentNode: c${
|
||||
ctx.parentNode
|
||||
}, vars: extra.vars, parent: owner}));`
|
||||
);
|
||||
ctx.closeIf();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-model
|
||||
//------------------------------------------------------------------------------
|
||||
UTILS.toNumber = function(val: string): number | string {
|
||||
const n = parseFloat(val);
|
||||
return isNaN(n) ? val : n;
|
||||
};
|
||||
|
||||
QWeb.addDirective({
|
||||
name: "model",
|
||||
priority: 42,
|
||||
atNodeCreation({ ctx, nodeID, value, node, fullName }) {
|
||||
const type = node.getAttribute("type");
|
||||
let handler;
|
||||
let event = fullName.includes(".lazy") ? "change" : "input";
|
||||
if (node.tagName === "select") {
|
||||
ctx.addLine(`p${nodeID}.props = {value: context.state['${value}']};`);
|
||||
event = "change";
|
||||
handler = `(ev) => {context.state['${value}'] = ev.target.value}`;
|
||||
} else if (type === "checkbox") {
|
||||
ctx.addLine(`p${nodeID}.props = {checked: context.state['${value}']};`);
|
||||
handler = `(ev) => {context.state['${value}'] = ev.target.checked}`;
|
||||
} else if (type === "radio") {
|
||||
const nodeValue = node.getAttribute("value")!;
|
||||
ctx.addLine(`p${nodeID}.props = {checked:context.state['${value}'] === '${nodeValue}'};`);
|
||||
handler = `(ev) => {context.state['${value}'] = ev.target.value}`;
|
||||
event = "click";
|
||||
} else {
|
||||
ctx.addLine(`p${nodeID}.props = {value: context.state['${value}']};`);
|
||||
const trimCode = fullName.includes(".trim") ? ".trim()" : "";
|
||||
let valueCode = `ev.target.value${trimCode}`;
|
||||
if (fullName.includes(".number")) {
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
valueCode = `utils.toNumber(${valueCode})`;
|
||||
}
|
||||
handler = `(ev) => {context.state['${value}'] = ${valueCode}}`;
|
||||
}
|
||||
ctx.addLine(
|
||||
`extra.handlers['${event}' + ${nodeID}] = extra.handlers['${event}' + ${nodeID}] || (${handler});`
|
||||
);
|
||||
ctx.addLine(`p${nodeID}.on['${event}'] = extra.handlers['${event}' + ${nodeID}];`);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
import { QWeb } from "../qweb/index";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Prop validation helper
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Validate the component props (or next props) against the (static) props
|
||||
* description. This is potentially an expensive operation: it may needs to
|
||||
* visit recursively the props and all the children to check if they are valid.
|
||||
* This is why it is only done in 'dev' mode.
|
||||
*/
|
||||
|
||||
QWeb.utils.validateProps = function(Widget, props: Object) {
|
||||
const propsDef = (<any>Widget).props;
|
||||
if (propsDef instanceof Array) {
|
||||
// list of strings (prop names)
|
||||
for (let i = 0, l = propsDef.length; i < l; i++) {
|
||||
const propName = propsDef[i];
|
||||
if (propName[propName.length - 1] === "?") {
|
||||
// optional prop
|
||||
break;
|
||||
}
|
||||
if (!props[propName]) {
|
||||
throw new Error(`Missing props '${propsDef[i]}' (component '${Widget.name}')`);
|
||||
}
|
||||
}
|
||||
for (let key in props) {
|
||||
if (!propsDef.includes(key) && !propsDef.includes(key + "?")) {
|
||||
throw new Error(`Unknown prop '${key}' given to component '${Widget.name}'`);
|
||||
}
|
||||
}
|
||||
} else if (propsDef) {
|
||||
// propsDef is an object now
|
||||
for (let propName in propsDef) {
|
||||
if (props[propName] === undefined) {
|
||||
if (propsDef[propName] && !propsDef[propName].optional) {
|
||||
throw new Error(`Missing props '${propName}' (component '${Widget.name}')`);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let isValid = isValidProp(props[propName], propsDef[propName]);
|
||||
if (!isValid) {
|
||||
throw new Error(`Props '${propName}' of invalid type in component '${Widget.name}'`);
|
||||
}
|
||||
}
|
||||
for (let propName in props) {
|
||||
if (!(propName in propsDef)) {
|
||||
throw new Error(`Unknown prop '${propName}' given to component '${Widget.name}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if an invidual prop value matches its (static) prop definition
|
||||
*/
|
||||
function isValidProp(prop, propDef): boolean {
|
||||
if (propDef === true) {
|
||||
return true;
|
||||
}
|
||||
if (typeof propDef === "function") {
|
||||
// Check if a value is constructed by some Constructor. Note that there is a
|
||||
// slight abuse of language: we want to consider primitive values as well.
|
||||
//
|
||||
// So, even though 1 is not an instance of Number, we want to consider that
|
||||
// it is valid.
|
||||
if (typeof prop === "object") {
|
||||
return prop instanceof propDef;
|
||||
}
|
||||
return typeof prop === propDef.name.toLowerCase();
|
||||
} else if (propDef instanceof Array) {
|
||||
// If this code is executed, this means that we want to check if a prop
|
||||
// matches at least one of its descriptor.
|
||||
let result = false;
|
||||
for (let i = 0, iLen = propDef.length; i < iLen; i++) {
|
||||
result = result || isValidProp(prop, propDef[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// propsDef is an object
|
||||
let result = isValidProp(prop, propDef.type);
|
||||
if (propDef.type === Array) {
|
||||
for (let i = 0, iLen = prop.length; i < iLen; i++) {
|
||||
result = result && isValidProp(prop[i], propDef.element);
|
||||
}
|
||||
}
|
||||
if (propDef.type === Object) {
|
||||
const shape = propDef.shape;
|
||||
for (let key in shape) {
|
||||
result = result && isValidProp(prop[key], shape[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
+11
-16
@@ -4,23 +4,19 @@
|
||||
*
|
||||
* Note that dynamic values, such as a date or a commit hash are added by rollup
|
||||
*/
|
||||
export { Component } from "./component";
|
||||
import { EventBus } from "./event_bus";
|
||||
import { Observer } from "./observer";
|
||||
|
||||
export const misc = {EventBus, Observer};
|
||||
// we need to import manually the extra directives so they can register
|
||||
// themselves in QWeb, otherwise these files will not even be loaded.
|
||||
import "./qweb_directives";
|
||||
import "./qweb_extensions";
|
||||
import { QWeb } from "./qweb_core";
|
||||
export { QWeb };
|
||||
|
||||
import { Store, ConnectedComponent } from "./store";
|
||||
|
||||
export const store = {Store, ConnectedComponent};
|
||||
import { EventBus } from "./core/event_bus";
|
||||
import { Observer } from "./core/observer";
|
||||
import { QWeb } from "./qweb/index";
|
||||
import { ConnectedComponent } from "./store/connected_component";
|
||||
import { Store } from "./store/store";
|
||||
import * as _utils from "./utils";
|
||||
|
||||
export { Component } from "./component/component";
|
||||
export { QWeb };
|
||||
export const core = { EventBus, Observer };
|
||||
export const store = { Store, ConnectedComponent };
|
||||
export const utils = _utils;
|
||||
|
||||
export const __info__ = {};
|
||||
|
||||
Object.defineProperty(__info__, "mode", {
|
||||
@@ -39,4 +35,3 @@ Object.defineProperty(__info__, "mode", {
|
||||
}
|
||||
}
|
||||
});
|
||||
export const utils = _utils;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Context, QWeb, UTILS } from "./qweb_core";
|
||||
import { QWebExprVar } from "./qweb_expressions";
|
||||
import { Context } from "./context";
|
||||
import { QWebExprVar } from "./expression_parser";
|
||||
import { QWeb } from "./qweb";
|
||||
|
||||
/**
|
||||
* Owl QWeb Directives
|
||||
@@ -18,7 +19,7 @@ import { QWebExprVar } from "./qweb_expressions";
|
||||
//------------------------------------------------------------------------------
|
||||
// t-esc and t-raw
|
||||
//------------------------------------------------------------------------------
|
||||
(<any>UTILS).getFragment = function(str: string): DocumentFragment {
|
||||
QWeb.utils.getFragment = function(str: string): DocumentFragment {
|
||||
const temp = document.createElement("template");
|
||||
temp.innerHTML = str;
|
||||
return temp.content;
|
||||
@@ -57,7 +58,8 @@ function compileValueNode(value: any, node: Element, qweb: QWeb, ctx: Context) {
|
||||
}
|
||||
} else {
|
||||
let fragID = ctx.generateID();
|
||||
ctx.addLine(`var frag${fragID} = this.utils.getFragment(${exprID})`);
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
ctx.addLine(`var frag${fragID} = utils.getFragment(${exprID})`);
|
||||
let tempNodeID = ctx.generateID();
|
||||
ctx.addLine(`var p${tempNodeID} = {hook: {`);
|
||||
ctx.addLine(` insert: n => n.elm.parentNode.replaceChild(frag${fragID}, n.elm),`);
|
||||
@@ -255,11 +257,11 @@ QWeb.addDirective({
|
||||
ctx.addLine(`var _length${keysID} = _${keysID}.length;`);
|
||||
ctx.addLine(`for (let i = 0; i < _length${keysID}; i++) {`);
|
||||
ctx.indent();
|
||||
ctx.addToScope(name + '_first', 'i === 0');
|
||||
ctx.addToScope(name + '_last', `i === _length${keysID} - 1`);
|
||||
ctx.addToScope(name + '_index', 'i');
|
||||
ctx.addToScope(name + "_first", "i === 0");
|
||||
ctx.addToScope(name + "_last", `i === _length${keysID} - 1`);
|
||||
ctx.addToScope(name + "_index", "i");
|
||||
ctx.addToScope(name, `_${keysID}[i]`);
|
||||
ctx.addToScope(name + '_value', `_${valuesID}[i]`);
|
||||
ctx.addToScope(name + "_value", `_${valuesID}[i]`);
|
||||
const nodeCopy = <Element>node.cloneNode(true);
|
||||
let shouldWarn = nodeCopy.tagName !== "t" && !nodeCopy.hasAttribute("t-key");
|
||||
if (!shouldWarn && node.tagName === "t") {
|
||||
@@ -0,0 +1,167 @@
|
||||
import { compileExpr, QWebVar } from "./expression_parser";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Compilation Context
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export class Context {
|
||||
nextID: number = 1;
|
||||
code: string[] = [];
|
||||
variables: { [key: string]: QWebVar } = {};
|
||||
escaping: boolean = false;
|
||||
parentNode: number | null = null;
|
||||
parentTextNode: number | null = null;
|
||||
rootNode: number | null = null;
|
||||
indentLevel: number = 0;
|
||||
rootContext: Context;
|
||||
caller: Element | undefined;
|
||||
shouldDefineOwner: boolean = false;
|
||||
shouldDefineParent: boolean = false;
|
||||
shouldDefineQWeb: boolean = false;
|
||||
shouldDefineUtils: boolean = false;
|
||||
shouldDefineResult: boolean = false;
|
||||
shouldProtectContext: boolean = false;
|
||||
shouldTrackScope: boolean = false;
|
||||
inLoop: boolean = false;
|
||||
inPreTag: boolean = false;
|
||||
templateName: string;
|
||||
allowMultipleRoots: boolean = false;
|
||||
hasParentWidget: boolean = false;
|
||||
scopeVars: any[] = [];
|
||||
|
||||
constructor(name?: string) {
|
||||
this.rootContext = this;
|
||||
this.templateName = name || "noname";
|
||||
this.addLine("var h = this.h;");
|
||||
}
|
||||
|
||||
generateID(): number {
|
||||
const id = this.rootContext.nextID++;
|
||||
return id;
|
||||
}
|
||||
|
||||
generateCode(): string[] {
|
||||
const shouldTrackScope = this.shouldTrackScope && this.scopeVars.length;
|
||||
if (shouldTrackScope) {
|
||||
// add some vars to scope if needed
|
||||
for (let scopeVar of this.scopeVars.reverse()) {
|
||||
let { index, key, indent } = scopeVar;
|
||||
const prefix = new Array(indent + 2).join(" ");
|
||||
this.code.splice(index + 1, 0, prefix + `scope.${key} = context.${key};`);
|
||||
}
|
||||
this.code.unshift(" const scope = Object.create(null);");
|
||||
}
|
||||
if (this.shouldProtectContext) {
|
||||
this.code.unshift(" context = Object.create(context);");
|
||||
}
|
||||
if (this.shouldDefineResult) {
|
||||
this.code.unshift(" let result;");
|
||||
}
|
||||
if (this.shouldDefineOwner) {
|
||||
// this is necessary to prevent some directives (t-forach for ex) to
|
||||
// pollute the rendering context by adding some keys in it.
|
||||
this.code.unshift(" let owner = context;");
|
||||
}
|
||||
if (this.shouldDefineParent) {
|
||||
if (this.hasParentWidget) {
|
||||
this.code.unshift(" let parent = extra.parent;");
|
||||
} else {
|
||||
this.code.unshift(" let parent = context;");
|
||||
}
|
||||
}
|
||||
if (this.shouldDefineQWeb) {
|
||||
this.code.unshift(" let QWeb = this.constructor;");
|
||||
}
|
||||
if (this.shouldDefineUtils) {
|
||||
this.code.unshift(" let utils = this.constructor.utils;");
|
||||
}
|
||||
return this.code;
|
||||
}
|
||||
|
||||
withParent(node: number): Context {
|
||||
if (
|
||||
!this.allowMultipleRoots &&
|
||||
this === this.rootContext &&
|
||||
(this.parentNode || this.parentTextNode)
|
||||
) {
|
||||
throw new Error("A template should not have more than one root node");
|
||||
}
|
||||
if (!this.rootContext.rootNode) {
|
||||
this.rootContext.rootNode = node;
|
||||
}
|
||||
return this.subContext("parentNode", node);
|
||||
}
|
||||
|
||||
subContext(key: keyof Context, value: any): Context {
|
||||
const newContext = Object.create(this);
|
||||
newContext[key] = value;
|
||||
return newContext;
|
||||
}
|
||||
|
||||
indent() {
|
||||
this.indentLevel++;
|
||||
}
|
||||
|
||||
dedent() {
|
||||
this.indentLevel--;
|
||||
}
|
||||
|
||||
addLine(line: string): number {
|
||||
const prefix = new Array(this.indentLevel + 2).join(" ");
|
||||
this.code.push(prefix + line);
|
||||
return this.code.length - 1;
|
||||
}
|
||||
|
||||
addToScope(key: string, expr: string) {
|
||||
const index = this.addLine(`context.${key} = ${expr};`);
|
||||
this.rootContext.scopeVars.push({ index, key, indent: this.indentLevel });
|
||||
}
|
||||
|
||||
addIf(condition: string) {
|
||||
this.addLine(`if (${condition}) {`);
|
||||
this.indent();
|
||||
}
|
||||
|
||||
addElse() {
|
||||
this.dedent();
|
||||
this.addLine("} else {");
|
||||
this.indent();
|
||||
}
|
||||
|
||||
closeIf() {
|
||||
this.dedent();
|
||||
this.addLine("}");
|
||||
}
|
||||
|
||||
getValue(val: any): any {
|
||||
return val in this.variables ? this.getValue(this.variables[val]) : val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an expression for being consumed at render time. Its main job
|
||||
* is to
|
||||
* - replace unknown variables by a lookup in the context
|
||||
* - replace already defined variables by their internal name
|
||||
*/
|
||||
formatExpression(expr: string): string {
|
||||
return compileExpr(expr, this.variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform string interpolation on the given string. Note that if the whole
|
||||
* string is an expression, it simply returns it (formatted and enclosed in
|
||||
* parentheses).
|
||||
* For instance:
|
||||
* 'Hello {{x}}!' -> `Hello ${x}`
|
||||
* '{{x ? 'a': 'b'}}' -> (x ? 'a' : 'b')
|
||||
*/
|
||||
interpolate(s: string): string {
|
||||
let matches = s.match(/\{\{.*?\}\}/g);
|
||||
if (matches && matches[0].length === s.length) {
|
||||
return `(${this.formatExpression(s.slice(2, -2))})`;
|
||||
}
|
||||
|
||||
let r = s.replace(/\{\{.*?\}\}/g, s => "${" + this.formatExpression(s.slice(2, -2)) + "}");
|
||||
return "`" + r + "`";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
import { VNode } from "../vdom/index";
|
||||
import { QWeb } from "./qweb";
|
||||
|
||||
/**
|
||||
* Owl QWeb Extensions
|
||||
*
|
||||
* This file contains the implementation of non standard QWeb directives, added
|
||||
* by Owl and that will only work on Owl projects:
|
||||
*
|
||||
* - t-on
|
||||
* - t-ref
|
||||
* - t-transition
|
||||
* - t-mounted
|
||||
* - t-slot
|
||||
* - t-model
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-on
|
||||
//------------------------------------------------------------------------------
|
||||
// these are pieces of code that will be injected into the event handler if
|
||||
// modifiers are specified
|
||||
export const MODS_CODE = {
|
||||
prevent: "e.preventDefault();",
|
||||
self: "if (e.target !== this.elm) {return}",
|
||||
stop: "e.stopPropagation();"
|
||||
};
|
||||
|
||||
QWeb.addDirective({
|
||||
name: "on",
|
||||
priority: 90,
|
||||
atNodeCreation({ ctx, fullName, value, nodeID }) {
|
||||
ctx.rootContext.shouldDefineOwner = true;
|
||||
const [eventName, ...mods] = fullName.slice(5).split(".");
|
||||
if (!eventName) {
|
||||
throw new Error("Missing event name with t-on directive");
|
||||
}
|
||||
let extraArgs;
|
||||
let handlerName = value.replace(/\(.*\)/, function(args) {
|
||||
extraArgs = args.slice(1, -1);
|
||||
return "";
|
||||
});
|
||||
ctx.addIf(`!context['${handlerName}']`);
|
||||
ctx.addLine(
|
||||
`throw new Error('Missing handler \\'' + '${handlerName}' + \`\\' when evaluating template '${ctx.templateName.replace(
|
||||
/`/g,
|
||||
"'"
|
||||
)}'\`)`
|
||||
);
|
||||
ctx.closeIf();
|
||||
let params = extraArgs ? `owner, ${ctx.formatExpression(extraArgs)}` : "owner";
|
||||
let handler;
|
||||
if (mods.length > 0) {
|
||||
handler = `function (e) {`;
|
||||
handler += mods
|
||||
.map(function(mod) {
|
||||
return MODS_CODE[mod];
|
||||
})
|
||||
.join("");
|
||||
handler += `context['${handlerName}'].call(${params}, e);}`;
|
||||
} else {
|
||||
handler = `context['${handlerName}'].bind(${params})`;
|
||||
}
|
||||
if (extraArgs) {
|
||||
ctx.addLine(`p${nodeID}.on['${eventName}'] = ${handler};`);
|
||||
} else {
|
||||
ctx.addLine(
|
||||
`extra.handlers['${eventName}' + ${nodeID}] = extra.handlers['${eventName}' + ${nodeID}] || ${handler};`
|
||||
);
|
||||
ctx.addLine(`p${nodeID}.on['${eventName}'] = extra.handlers['${eventName}' + ${nodeID}];`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-ref
|
||||
//------------------------------------------------------------------------------
|
||||
QWeb.addDirective({
|
||||
name: "ref",
|
||||
priority: 95,
|
||||
atNodeCreation({ ctx, value, addNodeHook }) {
|
||||
const refKey = `ref${ctx.generateID()}`;
|
||||
ctx.addLine(`const ${refKey} = ${ctx.interpolate(value)};`);
|
||||
addNodeHook("create", `context.refs[${refKey}] = n.elm;`);
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-transition
|
||||
//------------------------------------------------------------------------------
|
||||
QWeb.utils.nextFrame = function(cb: () => void) {
|
||||
requestAnimationFrame(() => requestAnimationFrame(cb));
|
||||
};
|
||||
|
||||
QWeb.utils.transitionInsert = function(vn: VNode, name: string) {
|
||||
const elm = <HTMLElement>vn.elm;
|
||||
// remove potential duplicated vnode that is currently being removed, to
|
||||
// prevent from having twice the same node in the DOM during an animation
|
||||
const dup = elm.parentElement && elm.parentElement!.querySelector(`*[data-owl-key='${vn.key}']`);
|
||||
if (dup) {
|
||||
dup.remove();
|
||||
}
|
||||
|
||||
elm.classList.add(name + "-enter");
|
||||
elm.classList.add(name + "-enter-active");
|
||||
const finalize = () => {
|
||||
elm.classList.remove(name + "-enter-active");
|
||||
elm.classList.remove(name + "-enter-to");
|
||||
};
|
||||
this.nextFrame(() => {
|
||||
elm.classList.remove(name + "-enter");
|
||||
elm.classList.add(name + "-enter-to");
|
||||
whenTransitionEnd(elm, finalize);
|
||||
});
|
||||
};
|
||||
|
||||
QWeb.utils.transitionRemove = function(vn: VNode, name: string, rm: () => void) {
|
||||
const elm = <HTMLElement>vn.elm;
|
||||
elm.setAttribute("data-owl-key", vn.key!);
|
||||
|
||||
elm.classList.add(name + "-leave");
|
||||
elm.classList.add(name + "-leave-active");
|
||||
const finalize = () => {
|
||||
elm.classList.remove(name + "-leave-active");
|
||||
elm.classList.remove(name + "-leave-to");
|
||||
rm();
|
||||
};
|
||||
this.nextFrame(() => {
|
||||
elm.classList.remove(name + "-leave");
|
||||
elm.classList.add(name + "-leave-to");
|
||||
whenTransitionEnd(elm, finalize);
|
||||
});
|
||||
};
|
||||
|
||||
function getTimeout(delays: Array<string>, durations: Array<string>): number {
|
||||
/* istanbul ignore next */
|
||||
while (delays.length < durations.length) {
|
||||
delays = delays.concat(delays);
|
||||
}
|
||||
|
||||
return Math.max.apply(
|
||||
null,
|
||||
durations.map((d, i) => {
|
||||
return toMs(d) + toMs(delays[i]);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Old versions of Chromium (below 61.0.3163.100) formats floating pointer numbers
|
||||
// in a locale-dependent way, using a comma instead of a dot.
|
||||
// If comma is not replaced with a dot, the input will be rounded down (i.e. acting
|
||||
// as a floor function) causing unexpected behaviors
|
||||
function toMs(s: string): number {
|
||||
return Number(s.slice(0, -1).replace(",", ".")) * 1000;
|
||||
}
|
||||
|
||||
function whenTransitionEnd(elm: HTMLElement, cb) {
|
||||
const styles = window.getComputedStyle(elm);
|
||||
const delays: Array<string> = (styles.transitionDelay || "").split(", ");
|
||||
const durations: Array<string> = (styles.transitionDuration || "").split(", ");
|
||||
const timeout: number = getTimeout(delays, durations);
|
||||
if (timeout > 0) {
|
||||
elm.addEventListener("transitionend", cb, { once: true });
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
QWeb.addDirective({
|
||||
name: "transition",
|
||||
priority: 96,
|
||||
atNodeCreation({ ctx, value, addNodeHook }) {
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
let name = value;
|
||||
const hooks = {
|
||||
insert: `utils.transitionInsert(vn, '${name}');`,
|
||||
remove: `utils.transitionRemove(vn, '${name}', rm);`
|
||||
};
|
||||
for (let hookName in hooks) {
|
||||
addNodeHook(hookName, hooks[hookName]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-mounted
|
||||
//------------------------------------------------------------------------------
|
||||
QWeb.addDirective({
|
||||
name: "mounted",
|
||||
priority: 97,
|
||||
atNodeCreation({ ctx, fullName, value, nodeID, addNodeHook }) {
|
||||
ctx.rootContext.shouldDefineOwner = true;
|
||||
const eventName = fullName.slice(5);
|
||||
if (!eventName) {
|
||||
throw new Error("Missing event name with t-on directive");
|
||||
}
|
||||
let extraArgs;
|
||||
let handler = value.replace(/\(.*\)/, function(args) {
|
||||
extraArgs = args.slice(1, -1);
|
||||
return "";
|
||||
});
|
||||
let error = `(function () {throw new Error('Missing handler \\'' + '${handler}' + \`\\' when evaluating template '${ctx.templateName.replace(
|
||||
/`/g,
|
||||
"'"
|
||||
)}'\`)})()`;
|
||||
if (extraArgs) {
|
||||
ctx.addLine(
|
||||
`extra.mountedHandlers[${nodeID}] = (context['${handler}'] || ${error}).bind(owner, ${ctx.formatExpression(
|
||||
extraArgs
|
||||
)});`
|
||||
);
|
||||
} else {
|
||||
ctx.addLine(
|
||||
`extra.mountedHandlers[${nodeID}] = extra.mountedHandlers[${nodeID}] || (context['${handler}'] || ${error}).bind(owner);`
|
||||
);
|
||||
}
|
||||
addNodeHook("insert", `if (context.__owl__.isMounted) { extra.mountedHandlers[${nodeID}](); }`);
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-slot
|
||||
//------------------------------------------------------------------------------
|
||||
QWeb.addDirective({
|
||||
name: "slot",
|
||||
priority: 80,
|
||||
atNodeEncounter({ ctx, value }): boolean {
|
||||
const slotKey = ctx.generateID();
|
||||
ctx.rootContext.shouldDefineOwner = true;
|
||||
ctx.addLine(`const slot${slotKey} = this.slots[context.__owl__.slotId + '_' + '${value}'];`);
|
||||
ctx.addIf(`slot${slotKey}`);
|
||||
ctx.addLine(
|
||||
`slot${slotKey}(context.__owl__.parent, Object.assign({}, extra, {parentNode: c${
|
||||
ctx.parentNode
|
||||
}, vars: extra.vars, parent: owner}));`
|
||||
);
|
||||
ctx.closeIf();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// t-model
|
||||
//------------------------------------------------------------------------------
|
||||
QWeb.utils.toNumber = function(val: string): number | string {
|
||||
const n = parseFloat(val);
|
||||
return isNaN(n) ? val : n;
|
||||
};
|
||||
|
||||
QWeb.addDirective({
|
||||
name: "model",
|
||||
priority: 42,
|
||||
atNodeCreation({ ctx, nodeID, value, node, fullName }) {
|
||||
const type = node.getAttribute("type");
|
||||
let handler;
|
||||
let event = fullName.includes(".lazy") ? "change" : "input";
|
||||
if (node.tagName === "select") {
|
||||
ctx.addLine(`p${nodeID}.props = {value: context.state['${value}']};`);
|
||||
event = "change";
|
||||
handler = `(ev) => {context.state['${value}'] = ev.target.value}`;
|
||||
} else if (type === "checkbox") {
|
||||
ctx.addLine(`p${nodeID}.props = {checked: context.state['${value}']};`);
|
||||
handler = `(ev) => {context.state['${value}'] = ev.target.checked}`;
|
||||
} else if (type === "radio") {
|
||||
const nodeValue = node.getAttribute("value")!;
|
||||
ctx.addLine(`p${nodeID}.props = {checked:context.state['${value}'] === '${nodeValue}'};`);
|
||||
handler = `(ev) => {context.state['${value}'] = ev.target.value}`;
|
||||
event = "click";
|
||||
} else {
|
||||
ctx.addLine(`p${nodeID}.props = {value: context.state['${value}']};`);
|
||||
const trimCode = fullName.includes(".trim") ? ".trim()" : "";
|
||||
let valueCode = `ev.target.value${trimCode}`;
|
||||
if (fullName.includes(".number")) {
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
valueCode = `utils.toNumber(${valueCode})`;
|
||||
}
|
||||
handler = `(ev) => {context.state['${value}'] = ${valueCode}}`;
|
||||
}
|
||||
ctx.addLine(
|
||||
`extra.handlers['${event}' + ${nodeID}] = extra.handlers['${event}' + ${nodeID}] || (${handler});`
|
||||
);
|
||||
ctx.addLine(`p${nodeID}.on['${event}'] = extra.handlers['${event}' + ${nodeID}];`);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
import "./base_directives";
|
||||
import "./extensions";
|
||||
|
||||
export { CompiledTemplate, QWeb } from "./qweb";
|
||||
@@ -1,6 +1,6 @@
|
||||
import { VNode, h, patch } from "./vdom";
|
||||
import { QWebVar, compileExpr } from "./qweb_expressions";
|
||||
import { EventBus } from "./event_bus";
|
||||
import { EventBus } from "../core/event_bus";
|
||||
import { h, patch, VNode } from "../vdom/index";
|
||||
import { Context } from "./context";
|
||||
|
||||
/**
|
||||
* Owl QWeb Engine
|
||||
@@ -80,14 +80,12 @@ const NODE_HOOKS_PARAMS = {
|
||||
};
|
||||
|
||||
interface Utils {
|
||||
h: typeof h;
|
||||
toObj(expr: any): Object;
|
||||
shallowEqual(p1: Object, p2: Object): boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export const UTILS: Utils = {
|
||||
h: h,
|
||||
const UTILS: Utils = {
|
||||
toObj(expr) {
|
||||
if (typeof expr === "string") {
|
||||
expr = expr.trim();
|
||||
@@ -149,9 +147,10 @@ let nextID = 1;
|
||||
//------------------------------------------------------------------------------
|
||||
export class QWeb extends EventBus {
|
||||
templates: { [name: string]: Template } = {};
|
||||
utils = UTILS;
|
||||
static utils = UTILS;
|
||||
static components = Object.create(null);
|
||||
|
||||
h = h;
|
||||
// dev mode enables better error messages or more costly validations
|
||||
static dev: boolean = false;
|
||||
|
||||
@@ -571,7 +570,8 @@ export class QWeb extends EventBus {
|
||||
let formattedValue = v.id || ctx.formatExpression(v);
|
||||
|
||||
if (attName === "class") {
|
||||
formattedValue = `this.utils.toObj(${formattedValue})`;
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
formattedValue = `utils.toObj(${formattedValue})`;
|
||||
if (classObj) {
|
||||
ctx.addLine(`Object.assign(${classObj}, ${formattedValue})`);
|
||||
} else {
|
||||
@@ -673,169 +673,3 @@ export class QWeb extends EventBus {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Compilation Context
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export class Context {
|
||||
nextID: number = 1;
|
||||
code: string[] = [];
|
||||
variables: { [key: string]: QWebVar } = {};
|
||||
escaping: boolean = false;
|
||||
parentNode: number | null = null;
|
||||
parentTextNode: number | null = null;
|
||||
rootNode: number | null = null;
|
||||
indentLevel: number = 0;
|
||||
rootContext: Context;
|
||||
caller: Element | undefined;
|
||||
shouldDefineOwner: boolean = false;
|
||||
shouldDefineParent: boolean = false;
|
||||
shouldDefineQWeb: boolean = false;
|
||||
shouldDefineUtils: boolean = false;
|
||||
shouldDefineResult: boolean = false;
|
||||
shouldProtectContext: boolean = false;
|
||||
shouldTrackScope: boolean = false;
|
||||
inLoop: boolean = false;
|
||||
inPreTag: boolean = false;
|
||||
templateName: string;
|
||||
allowMultipleRoots: boolean = false;
|
||||
hasParentWidget: boolean = false;
|
||||
scopeVars: any[] = [];
|
||||
|
||||
constructor(name?: string) {
|
||||
this.rootContext = this;
|
||||
this.templateName = name || "noname";
|
||||
this.addLine("var h = this.utils.h;");
|
||||
}
|
||||
|
||||
generateID(): number {
|
||||
const id = this.rootContext.nextID++;
|
||||
return id;
|
||||
}
|
||||
|
||||
generateCode(): string[] {
|
||||
const shouldTrackScope = this.shouldTrackScope && this.scopeVars.length;
|
||||
if (shouldTrackScope) {
|
||||
// add some vars to scope if needed
|
||||
for (let scopeVar of this.scopeVars.reverse()) {
|
||||
let { index, key, indent } = scopeVar;
|
||||
const prefix = new Array(indent + 2).join(" ");
|
||||
this.code.splice(index + 1, 0, prefix + `scope.${key} = context.${key};`);
|
||||
}
|
||||
this.code.unshift(" const scope = Object.create(null);");
|
||||
}
|
||||
if (this.shouldProtectContext) {
|
||||
this.code.unshift(" context = Object.create(context);");
|
||||
}
|
||||
if (this.shouldDefineResult) {
|
||||
this.code.unshift(" let result;");
|
||||
}
|
||||
if (this.shouldDefineOwner) {
|
||||
// this is necessary to prevent some directives (t-forach for ex) to
|
||||
// pollute the rendering context by adding some keys in it.
|
||||
this.code.unshift(" let owner = context;");
|
||||
}
|
||||
if (this.shouldDefineParent) {
|
||||
if (this.hasParentWidget) {
|
||||
this.code.unshift(" let parent = extra.parent;");
|
||||
} else {
|
||||
this.code.unshift(" let parent = context;");
|
||||
}
|
||||
}
|
||||
if (this.shouldDefineQWeb) {
|
||||
this.code.unshift(" let QWeb = this.constructor;");
|
||||
}
|
||||
if (this.shouldDefineUtils) {
|
||||
this.code.unshift(" let utils = this.utils;");
|
||||
}
|
||||
return this.code;
|
||||
}
|
||||
|
||||
withParent(node: number): Context {
|
||||
if (
|
||||
!this.allowMultipleRoots &&
|
||||
this === this.rootContext &&
|
||||
(this.parentNode || this.parentTextNode)
|
||||
) {
|
||||
throw new Error("A template should not have more than one root node");
|
||||
}
|
||||
if (!this.rootContext.rootNode) {
|
||||
this.rootContext.rootNode = node;
|
||||
}
|
||||
return this.subContext("parentNode", node);
|
||||
}
|
||||
|
||||
subContext(key: keyof Context, value: any): Context {
|
||||
const newContext = Object.create(this);
|
||||
newContext[key] = value;
|
||||
return newContext;
|
||||
}
|
||||
|
||||
indent() {
|
||||
this.indentLevel++;
|
||||
}
|
||||
|
||||
dedent() {
|
||||
this.indentLevel--;
|
||||
}
|
||||
|
||||
addLine(line: string): number {
|
||||
const prefix = new Array(this.indentLevel + 2).join(" ");
|
||||
this.code.push(prefix + line);
|
||||
return this.code.length - 1;
|
||||
}
|
||||
|
||||
addToScope(key: string, expr: string) {
|
||||
const index = this.addLine(`context.${key} = ${expr};`);
|
||||
this.rootContext.scopeVars.push({ index, key, indent: this.indentLevel });
|
||||
}
|
||||
|
||||
addIf(condition: string) {
|
||||
this.addLine(`if (${condition}) {`);
|
||||
this.indent();
|
||||
}
|
||||
|
||||
addElse() {
|
||||
this.dedent();
|
||||
this.addLine("} else {");
|
||||
this.indent();
|
||||
}
|
||||
|
||||
closeIf() {
|
||||
this.dedent();
|
||||
this.addLine("}");
|
||||
}
|
||||
|
||||
getValue(val: any): any {
|
||||
return val in this.variables ? this.getValue(this.variables[val]) : val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an expression for being consumed at render time. Its main job
|
||||
* is to
|
||||
* - replace unknown variables by a lookup in the context
|
||||
* - replace already defined variables by their internal name
|
||||
*/
|
||||
formatExpression(expr: string): string {
|
||||
return compileExpr(expr, this.variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform string interpolation on the given string. Note that if the whole
|
||||
* string is an expression, it simply returns it (formatted and enclosed in
|
||||
* parentheses).
|
||||
* For instance:
|
||||
* 'Hello {{x}}!' -> `Hello ${x}`
|
||||
* '{{x ? 'a': 'b'}}' -> (x ? 'a' : 'b')
|
||||
*/
|
||||
interpolate(s: string): string {
|
||||
let matches = s.match(/\{\{.*?\}\}/g);
|
||||
if (matches && matches[0].length === s.length) {
|
||||
return `(${this.formatExpression(s.slice(2, -2))})`;
|
||||
}
|
||||
|
||||
let r = s.replace(/\{\{.*?\}\}/g, s => "${" + this.formatExpression(s.slice(2, -2)) + "}");
|
||||
return "`" + r + "`";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { Component, Env } from "../component/component";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Connect function
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
function revNumber<T extends Object>(o: T): number {
|
||||
if (o !== null && typeof o === "object" && (<any>o).__owl__) {
|
||||
return (<any>o).__owl__.rev;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function deepRevNumber<T extends Object>(o: T): number {
|
||||
if (o !== null && typeof o === "object" && (<any>o).__owl__) {
|
||||
return (<any>o).__owl__.deepRev;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
type HashFunction = (a: any, b: any) => number;
|
||||
|
||||
export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S> {
|
||||
deep: boolean = true;
|
||||
getStore(env) {
|
||||
return env.store;
|
||||
}
|
||||
|
||||
hashFunction: HashFunction = ({ storeProps }, options) => {
|
||||
let refFunction = this.deep ? deepRevNumber : revNumber;
|
||||
if ("__owl__" in storeProps) {
|
||||
return refFunction(storeProps);
|
||||
}
|
||||
const { currentStoreProps } = options;
|
||||
let hash = 0;
|
||||
for (let key in storeProps) {
|
||||
const val = storeProps[key];
|
||||
const hashVal = refFunction(val);
|
||||
if (hashVal === 0) {
|
||||
if (val !== currentStoreProps[key]) {
|
||||
options.didChange = true;
|
||||
}
|
||||
} else {
|
||||
hash += hashVal;
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
static mapStoreToProps(storeState, ownProps, getters) {
|
||||
return {};
|
||||
}
|
||||
constructor(parent, props?: any) {
|
||||
super(parent, props);
|
||||
const store = this.getStore(this.env);
|
||||
const ownProps = Object.assign({}, props || {});
|
||||
const storeProps = (<any>this.constructor).mapStoreToProps(
|
||||
store.state,
|
||||
ownProps,
|
||||
store.getters
|
||||
);
|
||||
const mergedProps = Object.assign({}, props || {}, storeProps);
|
||||
this.props = mergedProps;
|
||||
(<any>this.__owl__).ownProps = ownProps;
|
||||
(<any>this.__owl__).currentStoreProps = storeProps;
|
||||
(<any>this.__owl__).store = store;
|
||||
(<any>this.__owl__).storeHash = this.hashFunction(
|
||||
{
|
||||
state: store.state,
|
||||
storeProps: storeProps,
|
||||
revNumber,
|
||||
deepRevNumber
|
||||
},
|
||||
{
|
||||
currentStoreProps: storeProps
|
||||
}
|
||||
);
|
||||
}
|
||||
/**
|
||||
* We do not use the mounted hook here for a subtle reason: we want the
|
||||
* updates to be called for the parents before the children. However,
|
||||
* if we use the mounted hook, this will be done in the reverse order.
|
||||
*/
|
||||
__callMounted() {
|
||||
(<any>this.__owl__).store.on("update", this, this.__checkUpdate);
|
||||
super.__callMounted();
|
||||
}
|
||||
willUnmount() {
|
||||
(<any>this.__owl__).store.off("update", this);
|
||||
super.willUnmount();
|
||||
}
|
||||
|
||||
async __checkUpdate(updateId) {
|
||||
if (updateId === (<any>this.__owl__).currentUpdateId) {
|
||||
return;
|
||||
}
|
||||
const ownProps = (<any>this.__owl__).ownProps;
|
||||
const storeProps = (<any>this.constructor).mapStoreToProps(
|
||||
(<any>this.__owl__).store.state,
|
||||
ownProps,
|
||||
(<any>this.__owl__).store.getters
|
||||
);
|
||||
const options: any = {
|
||||
currentStoreProps: (<any>this.__owl__).currentStoreProps
|
||||
};
|
||||
const storeHash = this.hashFunction(
|
||||
{
|
||||
state: (<any>this.__owl__).store.state,
|
||||
storeProps: storeProps,
|
||||
revNumber,
|
||||
deepRevNumber
|
||||
},
|
||||
options
|
||||
);
|
||||
let didChange = options.didChange;
|
||||
if (storeHash !== (<any>this.__owl__).storeHash) {
|
||||
didChange = true;
|
||||
(<any>this.__owl__).storeHash = storeHash;
|
||||
}
|
||||
if (didChange) {
|
||||
(<any>this.__owl__).currentStoreProps = storeProps;
|
||||
await this.__updateProps(ownProps, false);
|
||||
}
|
||||
}
|
||||
__updateProps(nextProps, forceUpdate, patchQueue?: any[]) {
|
||||
const __owl__ = <any>this.__owl__;
|
||||
__owl__.currentUpdateId = __owl__.store._updateId;
|
||||
if (__owl__.ownProps !== nextProps) {
|
||||
__owl__.currentStoreProps = (<any>this.constructor).mapStoreToProps(
|
||||
__owl__.store.state,
|
||||
nextProps,
|
||||
__owl__.store.getters
|
||||
);
|
||||
}
|
||||
__owl__.ownProps = nextProps;
|
||||
const mergedProps = Object.assign({}, nextProps, __owl__.currentStoreProps);
|
||||
return super.__updateProps(mergedProps, forceUpdate, patchQueue);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, Env } from "./component";
|
||||
import { EventBus } from "./event_bus";
|
||||
import { Observer } from "./observer";
|
||||
import { Env } from "../component/component";
|
||||
import { EventBus } from "../core/event_bus";
|
||||
import { Observer } from "../core/observer";
|
||||
|
||||
/**
|
||||
* Owl Store
|
||||
@@ -166,141 +166,3 @@ export class Store extends EventBus {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Connect function
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
function revNumber<T extends Object>(o: T): number {
|
||||
if (o !== null && typeof o === "object" && (<any>o).__owl__) {
|
||||
return (<any>o).__owl__.rev;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function deepRevNumber<T extends Object>(o: T): number {
|
||||
if (o !== null && typeof o === "object" && (<any>o).__owl__) {
|
||||
return (<any>o).__owl__.deepRev;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
type HashFunction = (a: any, b: any) => number;
|
||||
|
||||
export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S> {
|
||||
deep: boolean = true;
|
||||
getStore(env) {
|
||||
return env.store;
|
||||
}
|
||||
|
||||
hashFunction: HashFunction = ({ storeProps }, options) => {
|
||||
let refFunction = this.deep ? deepRevNumber : revNumber;
|
||||
if ("__owl__" in storeProps) {
|
||||
return refFunction(storeProps);
|
||||
}
|
||||
const { currentStoreProps } = options;
|
||||
let hash = 0;
|
||||
for (let key in storeProps) {
|
||||
const val = storeProps[key];
|
||||
const hashVal = refFunction(val);
|
||||
if (hashVal === 0) {
|
||||
if (val !== currentStoreProps[key]) {
|
||||
options.didChange = true;
|
||||
}
|
||||
} else {
|
||||
hash += hashVal;
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
static mapStoreToProps(storeState, ownProps, getters) {
|
||||
return {};
|
||||
}
|
||||
constructor(parent, props?: any) {
|
||||
super(parent, props);
|
||||
const store = this.getStore(this.env);
|
||||
const ownProps = Object.assign({}, props || {});
|
||||
const storeProps = (<any>this.constructor).mapStoreToProps(
|
||||
store.state,
|
||||
ownProps,
|
||||
store.getters
|
||||
);
|
||||
const mergedProps = Object.assign({}, props || {}, storeProps);
|
||||
this.props = mergedProps;
|
||||
(<any>this.__owl__).ownProps = ownProps;
|
||||
(<any>this.__owl__).currentStoreProps = storeProps;
|
||||
(<any>this.__owl__).store = store;
|
||||
(<any>this.__owl__).storeHash = this.hashFunction(
|
||||
{
|
||||
state: store.state,
|
||||
storeProps: storeProps,
|
||||
revNumber,
|
||||
deepRevNumber
|
||||
},
|
||||
{
|
||||
currentStoreProps: storeProps
|
||||
}
|
||||
);
|
||||
}
|
||||
/**
|
||||
* We do not use the mounted hook here for a subtle reason: we want the
|
||||
* updates to be called for the parents before the children. However,
|
||||
* if we use the mounted hook, this will be done in the reverse order.
|
||||
*/
|
||||
__callMounted() {
|
||||
(<any>this.__owl__).store.on("update", this, this.__checkUpdate);
|
||||
super.__callMounted();
|
||||
}
|
||||
willUnmount() {
|
||||
(<any>this.__owl__).store.off("update", this);
|
||||
super.willUnmount();
|
||||
}
|
||||
|
||||
async __checkUpdate(updateId) {
|
||||
if (updateId === (<any>this.__owl__).currentUpdateId) {
|
||||
return;
|
||||
}
|
||||
const ownProps = (<any>this.__owl__).ownProps;
|
||||
const storeProps = (<any>this.constructor).mapStoreToProps(
|
||||
(<any>this.__owl__).store.state,
|
||||
ownProps,
|
||||
(<any>this.__owl__).store.getters
|
||||
);
|
||||
const options: any = {
|
||||
currentStoreProps: (<any>this.__owl__).currentStoreProps
|
||||
};
|
||||
const storeHash = this.hashFunction(
|
||||
{
|
||||
state: (<any>this.__owl__).store.state,
|
||||
storeProps: storeProps,
|
||||
revNumber,
|
||||
deepRevNumber
|
||||
},
|
||||
options
|
||||
);
|
||||
let didChange = options.didChange;
|
||||
if (storeHash !== (<any>this.__owl__).storeHash) {
|
||||
didChange = true;
|
||||
(<any>this.__owl__).storeHash = storeHash;
|
||||
}
|
||||
if (didChange) {
|
||||
(<any>this.__owl__).currentStoreProps = storeProps;
|
||||
await this.__updateProps(ownProps, false);
|
||||
}
|
||||
}
|
||||
__updateProps(nextProps, forceUpdate, patchQueue?: any[]) {
|
||||
const __owl__ = <any>this.__owl__;
|
||||
__owl__.currentUpdateId = __owl__.store._updateId;
|
||||
if (__owl__.ownProps !== nextProps) {
|
||||
__owl__.currentStoreProps = (<any>this.constructor).mapStoreToProps(
|
||||
__owl__.store.state,
|
||||
nextProps,
|
||||
__owl__.store.getters
|
||||
);
|
||||
}
|
||||
__owl__.ownProps = nextProps;
|
||||
const mergedProps = Object.assign({}, nextProps, __owl__.currentStoreProps);
|
||||
return super.__updateProps(mergedProps, forceUpdate, patchQueue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { attrsModule, classModule, eventListenersModule, propsModule } from "./modules";
|
||||
import { init } from "./vdom";
|
||||
//------------------------------------------------------------------------------
|
||||
// patch
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
export { h, VNode } from "./vdom";
|
||||
|
||||
export const patch = init([eventListenersModule, attrsModule, propsModule, classModule]);
|
||||
@@ -0,0 +1,234 @@
|
||||
import { Module, VNode, VNodeData } from "./vdom";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// module/props.ts
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
function updateProps(oldVnode: VNode, vnode: VNode): void {
|
||||
var key: string,
|
||||
cur: any,
|
||||
old: any,
|
||||
elm = vnode.elm,
|
||||
oldProps = (oldVnode.data as VNodeData).props,
|
||||
props = (vnode.data as VNodeData).props;
|
||||
|
||||
if (!oldProps && !props) return;
|
||||
if (oldProps === props) return;
|
||||
oldProps = oldProps || {};
|
||||
props = props || {};
|
||||
|
||||
for (key in oldProps) {
|
||||
if (!props[key]) {
|
||||
delete (elm as any)[key];
|
||||
}
|
||||
}
|
||||
for (key in props) {
|
||||
cur = props[key];
|
||||
old = oldProps[key];
|
||||
if (old !== cur && (key !== "value" || (elm as any)[key] !== cur)) {
|
||||
(elm as any)[key] = cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const propsModule = {
|
||||
create: updateProps,
|
||||
update: updateProps
|
||||
} as Module;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// module/eventlisteners.ts
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
function invokeHandler(handler: any, vnode?: VNode, event?: Event): void {
|
||||
if (typeof handler === "function") {
|
||||
// call function handler
|
||||
handler.call(vnode, event, vnode);
|
||||
} else if (typeof handler === "object") {
|
||||
// call handler with arguments
|
||||
if (typeof handler[0] === "function") {
|
||||
// special case for single argument for performance
|
||||
if (handler.length === 2) {
|
||||
handler[0].call(vnode, handler[1], event, vnode);
|
||||
} else {
|
||||
var args = handler.slice(1);
|
||||
args.push(event);
|
||||
args.push(vnode);
|
||||
handler[0].apply(vnode, args);
|
||||
}
|
||||
} else {
|
||||
// call multiple handlers
|
||||
for (let i = 0, iLen = handler.length; i < iLen; i++) {
|
||||
invokeHandler(handler[i], vnode, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleEvent(event: Event, vnode: VNode) {
|
||||
var name = event.type,
|
||||
on = (vnode.data as VNodeData).on;
|
||||
|
||||
// call event handler(s) if exists
|
||||
if (on && on[name]) {
|
||||
invokeHandler(on[name], vnode, event);
|
||||
}
|
||||
}
|
||||
|
||||
function createListener() {
|
||||
return function handler(event: Event) {
|
||||
handleEvent(event, (handler as any).vnode);
|
||||
};
|
||||
}
|
||||
|
||||
function updateEventListeners(oldVnode: VNode, vnode?: VNode): void {
|
||||
var oldOn = (oldVnode.data as VNodeData).on,
|
||||
oldListener = (oldVnode as any).listener,
|
||||
oldElm: Element = oldVnode.elm as Element,
|
||||
on = vnode && (vnode.data as VNodeData).on,
|
||||
elm: Element = (vnode && vnode.elm) as Element,
|
||||
name: string;
|
||||
|
||||
// optimization for reused immutable handlers
|
||||
if (oldOn === on) {
|
||||
return;
|
||||
}
|
||||
|
||||
// remove existing listeners which no longer used
|
||||
if (oldOn && oldListener) {
|
||||
// if element changed or deleted we remove all existing listeners unconditionally
|
||||
if (!on) {
|
||||
for (name in oldOn) {
|
||||
// remove listener if element was changed or existing listeners removed
|
||||
oldElm.removeEventListener(name, oldListener, false);
|
||||
}
|
||||
} else {
|
||||
for (name in oldOn) {
|
||||
// remove listener if existing listener removed
|
||||
if (!on[name]) {
|
||||
oldElm.removeEventListener(name, oldListener, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add new listeners which has not already attached
|
||||
if (on) {
|
||||
// reuse existing listener or create new
|
||||
var listener = ((vnode as any).listener = (oldVnode as any).listener || createListener());
|
||||
// update vnode for listener
|
||||
listener.vnode = vnode;
|
||||
|
||||
// if element changed or added we add all needed listeners unconditionally
|
||||
if (!oldOn) {
|
||||
for (name in on) {
|
||||
// add listener if element was changed or new listeners added
|
||||
elm.addEventListener(name, listener, false);
|
||||
}
|
||||
} else {
|
||||
for (name in on) {
|
||||
// add listener if new listener added
|
||||
if (!oldOn[name]) {
|
||||
elm.addEventListener(name, listener, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const eventListenersModule = {
|
||||
create: updateEventListeners,
|
||||
update: updateEventListeners,
|
||||
destroy: updateEventListeners
|
||||
} as Module;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// attributes.ts
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const xlinkNS = "http://www.w3.org/1999/xlink";
|
||||
const xmlNS = "http://www.w3.org/XML/1998/namespace";
|
||||
const colonChar = 58;
|
||||
const xChar = 120;
|
||||
|
||||
function updateAttrs(oldVnode: VNode, vnode: VNode): void {
|
||||
var key: string,
|
||||
elm: Element = vnode.elm as Element,
|
||||
oldAttrs = (oldVnode.data as VNodeData).attrs,
|
||||
attrs = (vnode.data as VNodeData).attrs;
|
||||
|
||||
if (!oldAttrs && !attrs) return;
|
||||
if (oldAttrs === attrs) return;
|
||||
oldAttrs = oldAttrs || {};
|
||||
attrs = attrs || {};
|
||||
|
||||
// update modified attributes, add new attributes
|
||||
for (key in attrs) {
|
||||
const cur = attrs[key];
|
||||
const old = oldAttrs[key];
|
||||
if (old !== cur) {
|
||||
if (cur === true) {
|
||||
elm.setAttribute(key, "");
|
||||
} else if (cur === false) {
|
||||
elm.removeAttribute(key);
|
||||
} else {
|
||||
if (key.charCodeAt(0) !== xChar) {
|
||||
elm.setAttribute(key, cur);
|
||||
} else if (key.charCodeAt(3) === colonChar) {
|
||||
// Assume xml namespace
|
||||
elm.setAttributeNS(xmlNS, key, cur);
|
||||
} else if (key.charCodeAt(5) === colonChar) {
|
||||
// Assume xlink namespace
|
||||
elm.setAttributeNS(xlinkNS, key, cur);
|
||||
} else {
|
||||
elm.setAttribute(key, cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove removed attributes
|
||||
// use `in` operator since the previous `for` iteration uses it (.i.e. add even attributes with undefined value)
|
||||
// the other option is to remove all attributes with value == undefined
|
||||
for (key in oldAttrs) {
|
||||
if (!(key in attrs)) {
|
||||
elm.removeAttribute(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const attrsModule = {
|
||||
create: updateAttrs,
|
||||
update: updateAttrs
|
||||
} as Module;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// class.ts
|
||||
//------------------------------------------------------------------------------
|
||||
function updateClass(oldVnode: VNode, vnode: VNode): void {
|
||||
var cur: any,
|
||||
name: string,
|
||||
elm: Element,
|
||||
oldClass = (oldVnode.data as VNodeData).class,
|
||||
klass = (vnode.data as VNodeData).class;
|
||||
|
||||
if (!oldClass && !klass) return;
|
||||
if (oldClass === klass) return;
|
||||
oldClass = oldClass || {};
|
||||
klass = klass || {};
|
||||
|
||||
elm = vnode.elm as Element;
|
||||
|
||||
for (name in oldClass) {
|
||||
if (!klass[name]) {
|
||||
elm.classList.remove(name);
|
||||
}
|
||||
}
|
||||
for (name in klass) {
|
||||
cur = klass[name];
|
||||
if (cur !== oldClass[name]) {
|
||||
(elm.classList as any)[cur ? "add" : "remove"](name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const classModule = { create: updateClass, update: updateClass } as Module;
|
||||
+16
-256
@@ -27,6 +27,21 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
type Props = Record<string, any>;
|
||||
type Attrs = Record<string, string | number | boolean>;
|
||||
type On = { [N in keyof HTMLElementEventMap]?: (ev: HTMLElementEventMap[N]) => void } & {
|
||||
[event: string]: EventListener;
|
||||
};
|
||||
|
||||
export interface Module {
|
||||
pre: PreHook;
|
||||
create: CreateHook;
|
||||
update: UpdateHook;
|
||||
destroy: DestroyHook;
|
||||
remove: RemoveHook;
|
||||
post: PostHook;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// vnode.ts
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -41,7 +56,7 @@ export interface VNode {
|
||||
key: Key | undefined;
|
||||
}
|
||||
|
||||
interface VNodeData {
|
||||
export interface VNodeData {
|
||||
props?: Props;
|
||||
attrs?: Attrs;
|
||||
on?: On;
|
||||
@@ -631,258 +646,3 @@ export function h(sel: any, b?: any, c?: any): VNode {
|
||||
}
|
||||
return vnode(sel, data, children, text, undefined);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// module/props.ts
|
||||
//------------------------------------------------------------------------------
|
||||
type Props = Record<string, any>;
|
||||
|
||||
function updateProps(oldVnode: VNode, vnode: VNode): void {
|
||||
var key: string,
|
||||
cur: any,
|
||||
old: any,
|
||||
elm = vnode.elm,
|
||||
oldProps = (oldVnode.data as VNodeData).props,
|
||||
props = (vnode.data as VNodeData).props;
|
||||
|
||||
if (!oldProps && !props) return;
|
||||
if (oldProps === props) return;
|
||||
oldProps = oldProps || {};
|
||||
props = props || {};
|
||||
|
||||
for (key in oldProps) {
|
||||
if (!props[key]) {
|
||||
delete (elm as any)[key];
|
||||
}
|
||||
}
|
||||
for (key in props) {
|
||||
cur = props[key];
|
||||
old = oldProps[key];
|
||||
if (old !== cur && (key !== "value" || (elm as any)[key] !== cur)) {
|
||||
(elm as any)[key] = cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const propsModule = {
|
||||
create: updateProps,
|
||||
update: updateProps
|
||||
} as Module;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// module/module.ts
|
||||
//------------------------------------------------------------------------------
|
||||
interface Module {
|
||||
pre: PreHook;
|
||||
create: CreateHook;
|
||||
update: UpdateHook;
|
||||
destroy: DestroyHook;
|
||||
remove: RemoveHook;
|
||||
post: PostHook;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// module/eventlisteners.ts
|
||||
//------------------------------------------------------------------------------
|
||||
type On = { [N in keyof HTMLElementEventMap]?: (ev: HTMLElementEventMap[N]) => void } & {
|
||||
[event: string]: EventListener;
|
||||
};
|
||||
|
||||
function invokeHandler(handler: any, vnode?: VNode, event?: Event): void {
|
||||
if (typeof handler === "function") {
|
||||
// call function handler
|
||||
handler.call(vnode, event, vnode);
|
||||
} else if (typeof handler === "object") {
|
||||
// call handler with arguments
|
||||
if (typeof handler[0] === "function") {
|
||||
// special case for single argument for performance
|
||||
if (handler.length === 2) {
|
||||
handler[0].call(vnode, handler[1], event, vnode);
|
||||
} else {
|
||||
var args = handler.slice(1);
|
||||
args.push(event);
|
||||
args.push(vnode);
|
||||
handler[0].apply(vnode, args);
|
||||
}
|
||||
} else {
|
||||
// call multiple handlers
|
||||
for (let i = 0, iLen = handler.length; i < iLen; i++) {
|
||||
invokeHandler(handler[i], vnode, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleEvent(event: Event, vnode: VNode) {
|
||||
var name = event.type,
|
||||
on = (vnode.data as VNodeData).on;
|
||||
|
||||
// call event handler(s) if exists
|
||||
if (on && on[name]) {
|
||||
invokeHandler(on[name], vnode, event);
|
||||
}
|
||||
}
|
||||
|
||||
function createListener() {
|
||||
return function handler(event: Event) {
|
||||
handleEvent(event, (handler as any).vnode);
|
||||
};
|
||||
}
|
||||
|
||||
function updateEventListeners(oldVnode: VNode, vnode?: VNode): void {
|
||||
var oldOn = (oldVnode.data as VNodeData).on,
|
||||
oldListener = (oldVnode as any).listener,
|
||||
oldElm: Element = oldVnode.elm as Element,
|
||||
on = vnode && (vnode.data as VNodeData).on,
|
||||
elm: Element = (vnode && vnode.elm) as Element,
|
||||
name: string;
|
||||
|
||||
// optimization for reused immutable handlers
|
||||
if (oldOn === on) {
|
||||
return;
|
||||
}
|
||||
|
||||
// remove existing listeners which no longer used
|
||||
if (oldOn && oldListener) {
|
||||
// if element changed or deleted we remove all existing listeners unconditionally
|
||||
if (!on) {
|
||||
for (name in oldOn) {
|
||||
// remove listener if element was changed or existing listeners removed
|
||||
oldElm.removeEventListener(name, oldListener, false);
|
||||
}
|
||||
} else {
|
||||
for (name in oldOn) {
|
||||
// remove listener if existing listener removed
|
||||
if (!on[name]) {
|
||||
oldElm.removeEventListener(name, oldListener, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add new listeners which has not already attached
|
||||
if (on) {
|
||||
// reuse existing listener or create new
|
||||
var listener = ((vnode as any).listener = (oldVnode as any).listener || createListener());
|
||||
// update vnode for listener
|
||||
listener.vnode = vnode;
|
||||
|
||||
// if element changed or added we add all needed listeners unconditionally
|
||||
if (!oldOn) {
|
||||
for (name in on) {
|
||||
// add listener if element was changed or new listeners added
|
||||
elm.addEventListener(name, listener, false);
|
||||
}
|
||||
} else {
|
||||
for (name in on) {
|
||||
// add listener if new listener added
|
||||
if (!oldOn[name]) {
|
||||
elm.addEventListener(name, listener, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const eventListenersModule = {
|
||||
create: updateEventListeners,
|
||||
update: updateEventListeners,
|
||||
destroy: updateEventListeners
|
||||
} as Module;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// attributes.ts
|
||||
//------------------------------------------------------------------------------
|
||||
type Attrs = Record<string, string | number | boolean>;
|
||||
|
||||
const xlinkNS = "http://www.w3.org/1999/xlink";
|
||||
const xmlNS = "http://www.w3.org/XML/1998/namespace";
|
||||
const colonChar = 58;
|
||||
const xChar = 120;
|
||||
|
||||
function updateAttrs(oldVnode: VNode, vnode: VNode): void {
|
||||
var key: string,
|
||||
elm: Element = vnode.elm as Element,
|
||||
oldAttrs = (oldVnode.data as VNodeData).attrs,
|
||||
attrs = (vnode.data as VNodeData).attrs;
|
||||
|
||||
if (!oldAttrs && !attrs) return;
|
||||
if (oldAttrs === attrs) return;
|
||||
oldAttrs = oldAttrs || {};
|
||||
attrs = attrs || {};
|
||||
|
||||
// update modified attributes, add new attributes
|
||||
for (key in attrs) {
|
||||
const cur = attrs[key];
|
||||
const old = oldAttrs[key];
|
||||
if (old !== cur) {
|
||||
if (cur === true) {
|
||||
elm.setAttribute(key, "");
|
||||
} else if (cur === false) {
|
||||
elm.removeAttribute(key);
|
||||
} else {
|
||||
if (key.charCodeAt(0) !== xChar) {
|
||||
elm.setAttribute(key, cur);
|
||||
} else if (key.charCodeAt(3) === colonChar) {
|
||||
// Assume xml namespace
|
||||
elm.setAttributeNS(xmlNS, key, cur);
|
||||
} else if (key.charCodeAt(5) === colonChar) {
|
||||
// Assume xlink namespace
|
||||
elm.setAttributeNS(xlinkNS, key, cur);
|
||||
} else {
|
||||
elm.setAttribute(key, cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove removed attributes
|
||||
// use `in` operator since the previous `for` iteration uses it (.i.e. add even attributes with undefined value)
|
||||
// the other option is to remove all attributes with value == undefined
|
||||
for (key in oldAttrs) {
|
||||
if (!(key in attrs)) {
|
||||
elm.removeAttribute(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const attrsModule = {
|
||||
create: updateAttrs,
|
||||
update: updateAttrs
|
||||
} as Module;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// class.ts
|
||||
//------------------------------------------------------------------------------
|
||||
function updateClass(oldVnode: VNode, vnode: VNode): void {
|
||||
var cur: any,
|
||||
name: string,
|
||||
elm: Element,
|
||||
oldClass = (oldVnode.data as VNodeData).class,
|
||||
klass = (vnode.data as VNodeData).class;
|
||||
|
||||
if (!oldClass && !klass) return;
|
||||
if (oldClass === klass) return;
|
||||
oldClass = oldClass || {};
|
||||
klass = klass || {};
|
||||
|
||||
elm = vnode.elm as Element;
|
||||
|
||||
for (name in oldClass) {
|
||||
if (!klass[name]) {
|
||||
elm.classList.remove(name);
|
||||
}
|
||||
}
|
||||
for (name in klass) {
|
||||
cur = klass[name];
|
||||
if (cur !== oldClass[name]) {
|
||||
(elm.classList as any)[cur ? "add" : "remove"](name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const classModule = { create: updateClass, update: updateClass } as Module;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// patch
|
||||
//------------------------------------------------------------------------------
|
||||
export const patch = init([eventListenersModule, attrsModule, propsModule, classModule]);
|
||||
@@ -3,11 +3,11 @@
|
||||
exports[`animations t-transition combined with component 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -47,11 +47,11 @@ exports[`animations t-transition combined with component 1`] = `
|
||||
exports[`animations t-transition combined with t-component and t-if 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
if (context['state'].display) {
|
||||
@@ -93,15 +93,16 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
|
||||
exports[`animations t-transition with no delay/duration 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('span', p1, c1);
|
||||
p1.hook = {
|
||||
insert: vn => {
|
||||
this.utils.transitionInsert(vn, 'jupiler');
|
||||
utils.transitionInsert(vn, 'jupiler');
|
||||
},
|
||||
remove: (vn, rm) => {
|
||||
this.utils.transitionRemove(vn, 'jupiler', rm);
|
||||
utils.transitionRemove(vn, 'jupiler', rm);
|
||||
},
|
||||
};
|
||||
c1.push({text: \`blue\`});
|
||||
@@ -112,15 +113,16 @@ exports[`animations t-transition with no delay/duration 1`] = `
|
||||
exports[`animations t-transition, on a simple node (insert) 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('span', p1, c1);
|
||||
p1.hook = {
|
||||
insert: vn => {
|
||||
this.utils.transitionInsert(vn, 'chimay');
|
||||
utils.transitionInsert(vn, 'chimay');
|
||||
},
|
||||
remove: (vn, rm) => {
|
||||
this.utils.transitionRemove(vn, 'chimay', rm);
|
||||
utils.transitionRemove(vn, 'chimay', rm);
|
||||
},
|
||||
};
|
||||
c1.push({text: \`blue\`});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, Env } from "../src/component";
|
||||
import { QWeb } from "../src/qweb_core";
|
||||
import { Component, Env } from "../src/component/component";
|
||||
import { QWeb } from "../src/qweb/index";
|
||||
import {
|
||||
makeDeferred,
|
||||
makeTestFixture,
|
||||
|
||||
+66
-64
@@ -3,11 +3,11 @@
|
||||
exports[`async rendering delayed component with t-asyncroot directive 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
@@ -85,11 +85,11 @@ exports[`async rendering delayed component with t-asyncroot directive 1`] = `
|
||||
exports[`async rendering fast component with t-asyncroot directive 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
@@ -167,11 +167,11 @@ exports[`async rendering fast component with t-asyncroot directive 1`] = `
|
||||
exports[`async rendering t-component with t-asyncroot directive: mixed re-renderings 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
@@ -249,11 +249,11 @@ exports[`async rendering t-component with t-asyncroot directive: mixed re-render
|
||||
exports[`class and style attributes with t-component dynamic t-att-style is properly added and updated on widget root el 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -291,11 +291,11 @@ exports[`class and style attributes with t-component dynamic t-att-style is prop
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v2) 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -336,9 +336,10 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v2) 2`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let _2 = {'c':true};
|
||||
Object.assign(_2, this.utils.toObj({d:context['state'].d}))
|
||||
Object.assign(_2, utils.toObj({d:context['state'].d}))
|
||||
let c3 = [], p3 = {key:3,class:_2};
|
||||
var vn3 = h('span', p3, c3);
|
||||
return vn3;
|
||||
@@ -348,11 +349,11 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v3) 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -393,9 +394,10 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el (v3) 2`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let _2 = {'c':true};
|
||||
Object.assign(_2, this.utils.toObj(context['state'].d?'d':''))
|
||||
Object.assign(_2, utils.toObj(context['state'].d?'d':''))
|
||||
let c3 = [], p3 = {key:3,class:_2};
|
||||
var vn3 = h('span', p3, c3);
|
||||
return vn3;
|
||||
@@ -405,11 +407,11 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -448,12 +450,12 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
exports[`composition sub components with some state rendered in a loop 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['state'].numbers;
|
||||
@@ -506,11 +508,11 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
|
||||
exports[`composition t-component with dynamic value 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -547,11 +549,11 @@ exports[`composition t-component with dynamic value 1`] = `
|
||||
exports[`composition t-component with dynamic value 2 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -588,11 +590,11 @@ exports[`composition t-component with dynamic value 2 1`] = `
|
||||
exports[`other directives with t-component t-on with handler bound to argument 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -629,11 +631,11 @@ exports[`other directives with t-component t-on with handler bound to argument 1
|
||||
exports[`other directives with t-component t-on with handler bound to empty object (with non empty inner string) 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -670,11 +672,11 @@ exports[`other directives with t-component t-on with handler bound to empty obje
|
||||
exports[`other directives with t-component t-on with handler bound to empty object 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -711,11 +713,11 @@ exports[`other directives with t-component t-on with handler bound to empty obje
|
||||
exports[`other directives with t-component t-on with handler bound to object 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -752,11 +754,11 @@ exports[`other directives with t-component t-on with handler bound to object 1`]
|
||||
exports[`other directives with t-component t-on with prevent and self modifiers (order matters) 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -793,11 +795,11 @@ exports[`other directives with t-component t-on with prevent and self modifiers
|
||||
exports[`other directives with t-component t-on with self and prevent modifiers (order matters) 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -834,11 +836,11 @@ exports[`other directives with t-component t-on with self and prevent modifiers
|
||||
exports[`other directives with t-component t-on with self modifier 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -875,11 +877,11 @@ exports[`other directives with t-component t-on with self modifier 1`] = `
|
||||
exports[`other directives with t-component t-on with stop and/or prevent modifiers 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -916,11 +918,11 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie
|
||||
exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -958,12 +960,12 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
|
||||
exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument on a t-foreach 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['props'].items;
|
||||
@@ -1017,7 +1019,7 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
|
||||
exports[`t-model directive .lazy modifier 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
@@ -1040,7 +1042,7 @@ exports[`t-model directive .lazy modifier 1`] = `
|
||||
exports[`t-model directive basic use, on an input 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
@@ -1063,7 +1065,7 @@ exports[`t-model directive basic use, on an input 1`] = `
|
||||
exports[`t-model directive on a select 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
@@ -1102,7 +1104,7 @@ exports[`t-model directive on a select 1`] = `
|
||||
exports[`t-model directive on an input type=radio 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 'radio';
|
||||
@@ -1138,7 +1140,7 @@ exports[`t-model directive on an input type=radio 1`] = `
|
||||
exports[`t-model directive on an input, type=checkbox 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 'checkbox';
|
||||
@@ -1164,11 +1166,11 @@ exports[`t-model directive on an input, type=checkbox 1`] = `
|
||||
exports[`t-slot directive can define and call slots 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -1207,7 +1209,7 @@ exports[`t-slot directive can define and call slots 2`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -1232,7 +1234,7 @@ exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = context['props'].to;
|
||||
let c2 = [], p2 = {key:2,attrs:{href: _1}};
|
||||
var vn2 = h('a', p2, c2);
|
||||
@@ -1247,13 +1249,13 @@ exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
|
||||
exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
context = Object.create(context);
|
||||
const scope = Object.create(null);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -1318,7 +1320,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = context['props'].to;
|
||||
let c2 = [], p2 = {key:2,attrs:{href: _1}};
|
||||
var vn2 = h('a', p2, c2);
|
||||
@@ -1333,13 +1335,13 @@ exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
|
||||
exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
context = Object.create(context);
|
||||
const scope = Object.create(null);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -1404,11 +1406,11 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 'User '+context['state'].user.name;
|
||||
@@ -1447,12 +1449,12 @@ exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
||||
exports[`top level sub widgets basic use 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
let result;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
//COMPONENT
|
||||
let def2;
|
||||
let w3 = 3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[3]] : false;
|
||||
@@ -1487,12 +1489,12 @@ exports[`top level sub widgets basic use 1`] = `
|
||||
exports[`top level sub widgets can select a sub widget 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
let result;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
if (context['env'].flag) {
|
||||
//COMPONENT
|
||||
let def2;
|
||||
+2
-2
@@ -3,11 +3,11 @@
|
||||
exports[`props validation props are validated in dev mode (code snapshot) 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.utils;
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, Env } from "../src/component";
|
||||
import { QWeb } from "../src/qweb_core";
|
||||
import { EventBus } from "../src/event_bus";
|
||||
import { Component, Env } from "../../src/component/component";
|
||||
import { QWeb } from "../../src/qweb/qweb";
|
||||
import { EventBus } from "../../src/core/event_bus";
|
||||
import {
|
||||
makeDeferred,
|
||||
makeTestFixture,
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
nextTick,
|
||||
normalize,
|
||||
editInput
|
||||
} from "./helpers";
|
||||
} from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, Env } from "../src/component";
|
||||
import { makeTestFixture, makeTestEnv } from "./helpers";
|
||||
import { QWeb, UTILS } from "../src/qweb_core";
|
||||
import { Component, Env } from "../../src/component/component";
|
||||
import { makeTestFixture, makeTestEnv } from "../helpers";
|
||||
import { QWeb } from "../../src/qweb";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
@@ -251,10 +251,10 @@ describe("props validation", () => {
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
UTILS.validateProps(TestWidget, { someProp: 1 });
|
||||
QWeb.utils.validateProps(TestWidget, { someProp: 1 });
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
UTILS.validateProps(TestWidget, { message: 1 });
|
||||
QWeb.utils.validateProps(TestWidget, { message: 1 });
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
@@ -264,7 +264,7 @@ describe("props validation", () => {
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
UTILS.validateProps(TestWidget, {});
|
||||
QWeb.utils.validateProps(TestWidget, {});
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
@@ -274,7 +274,7 @@ describe("props validation", () => {
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
UTILS.validateProps(TestWidget, { message: 1, flag: true });
|
||||
QWeb.utils.validateProps(TestWidget, { message: 1, flag: true });
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
@@ -284,7 +284,7 @@ describe("props validation", () => {
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
UTILS.validateProps(TestWidget, { message: 1, flag: true });
|
||||
QWeb.utils.validateProps(TestWidget, { message: 1, flag: true });
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
@@ -294,7 +294,7 @@ describe("props validation", () => {
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
UTILS.validateProps(TestWidget, { message: 1});
|
||||
QWeb.utils.validateProps(TestWidget, { message: 1});
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
@@ -304,10 +304,10 @@ describe("props validation", () => {
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
UTILS.validateProps(TestWidget, { message: undefined});
|
||||
QWeb.utils.validateProps(TestWidget, { message: undefined});
|
||||
}).not.toThrow();
|
||||
expect(() => {
|
||||
UTILS.validateProps(TestWidget, { message: null});
|
||||
QWeb.utils.validateProps(TestWidget, { message: null});
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { EventBus } from "../src/event_bus";
|
||||
import { EventBus } from "../../src/core/event_bus";
|
||||
|
||||
describe("event bus behaviour", () => {
|
||||
test("can subscribe and be notified", () => {
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Observer } from "../src/observer";
|
||||
import { nextMicroTick } from "./helpers";
|
||||
import { Observer } from "../../src/core/observer";
|
||||
import { nextMicroTick } from "../helpers";
|
||||
|
||||
describe("observer", () => {
|
||||
test("properly observe objects", () => {
|
||||
+8
-7
@@ -1,8 +1,9 @@
|
||||
import { Env } from "../src/component";
|
||||
import { EvalContext, QWeb, UTILS } from "../src/qweb_core";
|
||||
import { Env } from "../src/component/component";
|
||||
import { EvalContext, QWeb } from "../src/qweb/qweb";
|
||||
import { patch } from "../src/vdom";
|
||||
import "../src/qweb_directives";
|
||||
import "../src/qweb_extensions";
|
||||
import "../src/qweb/base_directives";
|
||||
import "../src/qweb/extensions";
|
||||
import "../src/component/directive";
|
||||
|
||||
export function nextMicroTick(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
@@ -90,15 +91,15 @@ export function renderToString(qweb: QWeb, t: string, context: EvalContext = {})
|
||||
// is useful for animations tests, as we hook before repaints to trigger
|
||||
// animations (thanks to requestAnimationFrame). Patching nextFrame allows to
|
||||
// simulate calls to this hook. One must not forget to unpatch afterwards.
|
||||
let nextFrame = UTILS.nextFrame;
|
||||
let nextFrame = QWeb.utils.nextFrame;
|
||||
export function patchNextFrame(f: Function) {
|
||||
UTILS.nextFrame = (cb: () => void) => {
|
||||
QWeb.utils.nextFrame = (cb: () => void) => {
|
||||
setTimeout(() => f(cb));
|
||||
};
|
||||
}
|
||||
|
||||
export function unpatchNextFrame() {
|
||||
UTILS.nextFrame = nextFrame;
|
||||
QWeb.utils.nextFrame = nextFrame;
|
||||
}
|
||||
|
||||
export async function editInput(input: HTMLInputElement | HTMLTextAreaElement, value: string) {
|
||||
|
||||
+116
-107
@@ -3,7 +3,7 @@
|
||||
exports[`attributes class and t-attf-class with ternary operation 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = 'hello ' + (context['value']?'world':'');
|
||||
let c2 = [], p2 = {key:2,attrs:{class: _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -14,7 +14,7 @@ exports[`attributes class and t-attf-class with ternary operation 1`] = `
|
||||
exports[`attributes dynamic attribute falsy variable 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = context['value'];
|
||||
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -25,7 +25,7 @@ exports[`attributes dynamic attribute falsy variable 1`] = `
|
||||
exports[`attributes dynamic attribute with a dash 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = context['id'];
|
||||
let c2 = [], p2 = {key:2,attrs:{\\"data-action-id\\": _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -36,7 +36,7 @@ exports[`attributes dynamic attribute with a dash 1`] = `
|
||||
exports[`attributes dynamic attributes 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = 'bar';
|
||||
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -47,7 +47,7 @@ exports[`attributes dynamic attributes 1`] = `
|
||||
exports[`attributes dynamic formatted attributes with a dash 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = \`Some text \${context['id']}\`;
|
||||
let c2 = [], p2 = {key:2,attrs:{\\"aria-label\\": _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -58,7 +58,7 @@ exports[`attributes dynamic formatted attributes with a dash 1`] = `
|
||||
exports[`attributes fixed variable 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = context['value'];
|
||||
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -69,7 +69,7 @@ exports[`attributes fixed variable 1`] = `
|
||||
exports[`attributes format expression 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = (context['value']+37);
|
||||
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -80,7 +80,7 @@ exports[`attributes format expression 1`] = `
|
||||
exports[`attributes format expression, other format 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = (context['value']+37);
|
||||
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -91,7 +91,7 @@ exports[`attributes format expression, other format 1`] = `
|
||||
exports[`attributes format literal 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = \`bar\`;
|
||||
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -102,7 +102,7 @@ exports[`attributes format literal 1`] = `
|
||||
exports[`attributes format multiple 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = \`a \${context['value1']} is \${context['value2']} of \${context['value3']} ]\`;
|
||||
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -113,7 +113,7 @@ exports[`attributes format multiple 1`] = `
|
||||
exports[`attributes format value 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = \`b\${context['value']}r\`;
|
||||
let c2 = [], p2 = {key:2,attrs:{foo: _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -124,11 +124,12 @@ exports[`attributes format value 1`] = `
|
||||
exports[`attributes from object variables set previously 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = {a:'b'};
|
||||
let _3 = this.utils.toObj(_2.a);
|
||||
let _3 = utils.toObj(_2.a);
|
||||
let c4 = [], p4 = {key:4,class:_3};
|
||||
var vn4 = h('span', p4, c4);
|
||||
c1.push(vn4);
|
||||
@@ -139,11 +140,12 @@ exports[`attributes from object variables set previously 1`] = `
|
||||
exports[`attributes from variables set previously 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 'def';
|
||||
let _3 = this.utils.toObj(_2);
|
||||
let _3 = utils.toObj(_2);
|
||||
let c4 = [], p4 = {key:4,class:_3};
|
||||
var vn4 = h('span', p4, c4);
|
||||
c1.push(vn4);
|
||||
@@ -154,7 +156,7 @@ exports[`attributes from variables set previously 1`] = `
|
||||
exports[`attributes object 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = context['value'];
|
||||
let c2 = [], p2 = {key:2,attrs:{}};
|
||||
if (_1 instanceof Array) {
|
||||
@@ -172,7 +174,7 @@ exports[`attributes object 1`] = `
|
||||
exports[`attributes static attributes 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = 'a';
|
||||
var _2 = 'b';
|
||||
var _3 = 'c';
|
||||
@@ -185,7 +187,7 @@ exports[`attributes static attributes 1`] = `
|
||||
exports[`attributes static attributes on void elements 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = '/test.jpg';
|
||||
var _2 = 'Test';
|
||||
let c3 = [], p3 = {key:3,attrs:{src: _1,alt: _2}};
|
||||
@@ -197,7 +199,7 @@ exports[`attributes static attributes on void elements 1`] = `
|
||||
exports[`attributes static attributes with dashes 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = 'Close';
|
||||
let c2 = [], p2 = {key:2,attrs:{\\"aria-label\\": _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -208,9 +210,10 @@ exports[`attributes static attributes with dashes 1`] = `
|
||||
exports[`attributes t-att-class and class should combine together 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let _2 = {'hello':true};
|
||||
Object.assign(_2, this.utils.toObj(context['value']))
|
||||
Object.assign(_2, utils.toObj(context['value']))
|
||||
let c3 = [], p3 = {key:3,class:_2};
|
||||
var vn3 = h('div', p3, c3);
|
||||
return vn3;
|
||||
@@ -220,9 +223,10 @@ exports[`attributes t-att-class and class should combine together 1`] = `
|
||||
exports[`attributes t-att-class with object 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let _2 = {'static':true};
|
||||
Object.assign(_2, this.utils.toObj({a:context['b'],c:context['d'],e:context['f']}))
|
||||
Object.assign(_2, utils.toObj({a:context['b'],c:context['d'],e:context['f']}))
|
||||
let c3 = [], p3 = {key:3,class:_2};
|
||||
var vn3 = h('div', p3, c3);
|
||||
return vn3;
|
||||
@@ -232,7 +236,7 @@ exports[`attributes t-att-class with object 1`] = `
|
||||
exports[`attributes t-attf-class should combine with class 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = 'hello ' + \`world\`;
|
||||
let c2 = [], p2 = {key:2,attrs:{class: _1}};
|
||||
var vn2 = h('div', p2, c2);
|
||||
@@ -243,7 +247,7 @@ exports[`attributes t-attf-class should combine with class 1`] = `
|
||||
exports[`attributes tuple literal 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = ['foo','bar'];
|
||||
let c2 = [], p2 = {key:2,attrs:{}};
|
||||
if (_1 instanceof Array) {
|
||||
@@ -261,7 +265,7 @@ exports[`attributes tuple literal 1`] = `
|
||||
exports[`attributes tuple variable 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = context['value'];
|
||||
let c2 = [], p2 = {key:2,attrs:{}};
|
||||
if (_1 instanceof Array) {
|
||||
@@ -279,7 +283,7 @@ exports[`attributes tuple variable 1`] = `
|
||||
exports[`debugging t-debug 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
debugger;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
@@ -297,7 +301,7 @@ exports[`debugging t-debug 1`] = `
|
||||
exports[`debugging t-log 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 42;
|
||||
@@ -310,7 +314,7 @@ exports[`foreach does not pollute the rendering context 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = [1];
|
||||
@@ -340,7 +344,7 @@ exports[`foreach iterate on items (on a element node) 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = [1,2];
|
||||
@@ -373,7 +377,7 @@ exports[`foreach iterate on items 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = [3,2,1];
|
||||
@@ -415,7 +419,7 @@ exports[`foreach iterate, dict param 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['value'];
|
||||
@@ -457,7 +461,7 @@ exports[`foreach iterate, position 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = Array(5);
|
||||
@@ -496,7 +500,7 @@ exports[`foreach warn if no key in some case 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = [1,2];
|
||||
@@ -528,7 +532,7 @@ exports[`foreach warn if no key in some case 1`] = `
|
||||
exports[`loading templates can initialize qweb with a string 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
c1.push({text: \`jupiler\`});
|
||||
@@ -539,7 +543,7 @@ exports[`loading templates can initialize qweb with a string 1`] = `
|
||||
exports[`loading templates can load a few templates from a xml string 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('ul', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -557,8 +561,9 @@ exports[`loading templates can load a few templates from a xml string 1`] = `
|
||||
exports[`misc global 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.constructor.utils;
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = [4,5,6];
|
||||
@@ -624,7 +629,7 @@ exports[`misc global 1`] = `
|
||||
c1.push(vn21);
|
||||
var _22 = context['toto'];
|
||||
if (_22 || _22 === 0) {
|
||||
var frag23 = this.utils.getFragment(_22)
|
||||
var frag23 = utils.getFragment(_22)
|
||||
var p24 = {hook: {
|
||||
insert: n => n.elm.parentNode.replaceChild(frag23, n.elm),
|
||||
}};
|
||||
@@ -640,7 +645,7 @@ exports[`misc global 1`] = `
|
||||
exports[`special cases for some boolean html attributes/properties input type= checkbox, with t-att-checked 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = 'checkbox';
|
||||
var _2 = context['flag'];
|
||||
let c3 = [], p3 = {key:3,attrs:{type: _1,checked: _2},props:{checked: _2}};
|
||||
@@ -652,7 +657,7 @@ exports[`special cases for some boolean html attributes/properties input type= c
|
||||
exports[`special cases for some boolean html attributes/properties various boolean html attributes 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 'checkbox';
|
||||
@@ -692,7 +697,7 @@ exports[`special cases for some boolean html attributes/properties various boole
|
||||
exports[`static templates div with a span child node 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -706,7 +711,7 @@ exports[`static templates div with a span child node 1`] = `
|
||||
exports[`static templates div with a text node 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
c1.push({text: \`word\`});
|
||||
@@ -717,7 +722,7 @@ exports[`static templates div with a text node 1`] = `
|
||||
exports[`static templates empty div 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
return vn1;
|
||||
@@ -727,7 +732,7 @@ exports[`static templates empty div 1`] = `
|
||||
exports[`static templates simple dynamic value 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var _1 = context['text'];
|
||||
if (_1 || _1 === 0) {
|
||||
var vn2 = {text: _1};
|
||||
@@ -739,7 +744,7 @@ exports[`static templates simple dynamic value 1`] = `
|
||||
exports[`static templates simple string 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var vn1 = {text: \`hello vdom\`};
|
||||
return vn1;
|
||||
}"
|
||||
@@ -748,7 +753,7 @@ exports[`static templates simple string 1`] = `
|
||||
exports[`static templates simple string, with some dynamic value 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var vn1 = {text: \`hello \`};
|
||||
var _2 = context['text'];
|
||||
if (_2 || _2 === 0) {
|
||||
@@ -761,7 +766,7 @@ exports[`static templates simple string, with some dynamic value 1`] = `
|
||||
exports[`t-call (template calling basic caller 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
c1.push({text: \`ok\`});
|
||||
@@ -772,7 +777,7 @@ exports[`t-call (template calling basic caller 1`] = `
|
||||
exports[`t-call (template calling inherit context 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 1;
|
||||
@@ -786,7 +791,7 @@ exports[`t-call (template calling inherit context 1`] = `
|
||||
exports[`t-call (template calling scoped parameters 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
{
|
||||
@@ -804,7 +809,7 @@ exports[`t-call (template calling scoped parameters 1`] = `
|
||||
exports[`t-call (template calling with unused body 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c2 = [], p2 = {key:2};
|
||||
var vn2 = h('div', p2, c2);
|
||||
c2.push({text: \`ok\`});
|
||||
@@ -815,7 +820,7 @@ exports[`t-call (template calling with unused body 1`] = `
|
||||
exports[`t-call (template calling with unused setbody 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
{
|
||||
let _1 = 3;
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -829,7 +834,7 @@ exports[`t-call (template calling with unused setbody 1`] = `
|
||||
exports[`t-call (template calling with used body 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c2 = [], p2 = {key:2};
|
||||
var vn2 = h('h1', p2, c2);
|
||||
c2.push({text: \`ok\`});
|
||||
@@ -840,7 +845,7 @@ exports[`t-call (template calling with used body 1`] = `
|
||||
exports[`t-call (template calling with used set body 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('span', p1, c1);
|
||||
{
|
||||
@@ -856,7 +861,7 @@ exports[`t-call (template calling with used set body 1`] = `
|
||||
exports[`t-esc escaping on a node 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
var vn1 = h('span', p1, c1);
|
||||
var _2 = 'ok';
|
||||
@@ -870,7 +875,7 @@ exports[`t-esc escaping on a node 1`] = `
|
||||
exports[`t-esc escaping on a node with a body 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
var vn1 = h('span', p1, c1);
|
||||
var _2 = 'ok';
|
||||
@@ -886,7 +891,7 @@ exports[`t-esc escaping on a node with a body 1`] = `
|
||||
exports[`t-esc escaping on a node with a body, as a default 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
var vn1 = h('span', p1, c1);
|
||||
var _2 = context['var'];
|
||||
@@ -902,7 +907,7 @@ exports[`t-esc escaping on a node with a body, as a default 1`] = `
|
||||
exports[`t-esc literal 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('span', p1, c1);
|
||||
var _2 = 'ok';
|
||||
@@ -916,7 +921,7 @@ exports[`t-esc literal 1`] = `
|
||||
exports[`t-esc variable 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('span', p1, c1);
|
||||
var _2 = context['var'];
|
||||
@@ -930,7 +935,7 @@ exports[`t-esc variable 1`] = `
|
||||
exports[`t-if boolean value condition elif 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
if (context['color']=='black') {
|
||||
@@ -952,7 +957,7 @@ exports[`t-if boolean value condition elif 1`] = `
|
||||
exports[`t-if boolean value condition else 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -976,7 +981,7 @@ exports[`t-if boolean value condition else 1`] = `
|
||||
exports[`t-if boolean value condition false else 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -1000,7 +1005,7 @@ exports[`t-if boolean value condition false else 1`] = `
|
||||
exports[`t-if boolean value condition missing 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('span', p1, c1);
|
||||
if (context['condition']) {
|
||||
@@ -1013,7 +1018,7 @@ exports[`t-if boolean value condition missing 1`] = `
|
||||
exports[`t-if boolean value false condition 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
if (context['condition']) {
|
||||
@@ -1026,7 +1031,7 @@ exports[`t-if boolean value false condition 1`] = `
|
||||
exports[`t-if boolean value true condition 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
if (context['condition']) {
|
||||
@@ -1039,7 +1044,7 @@ exports[`t-if boolean value true condition 1`] = `
|
||||
exports[`t-if can use some boolean operators in expressions 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
if (context['cond1']&&context['cond2']) {
|
||||
@@ -1073,7 +1078,7 @@ exports[`t-if can use some boolean operators in expressions 1`] = `
|
||||
exports[`t-key can use t-key directive on a node 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:context['beer'].id};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['beer'].name;
|
||||
@@ -1088,7 +1093,7 @@ exports[`t-key t-key directive in a list 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('ul', p1, c1);
|
||||
var _2 = context['beers'];
|
||||
@@ -1123,7 +1128,7 @@ exports[`t-on can bind event handler 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
var vn1 = h('button', p1, c1);
|
||||
if (!context['add']) {
|
||||
@@ -1140,7 +1145,7 @@ exports[`t-on can bind handlers with arguments 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
var vn1 = h('button', p1, c1);
|
||||
if (!context['add']) {
|
||||
@@ -1156,7 +1161,7 @@ exports[`t-on can bind handlers with empty object (with non empty inner string)
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
var vn1 = h('button', p1, c1);
|
||||
if (!context['doSomething']) {
|
||||
@@ -1172,7 +1177,7 @@ exports[`t-on can bind handlers with empty object 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
var vn1 = h('button', p1, c1);
|
||||
if (!context['doSomething']) {
|
||||
@@ -1189,7 +1194,7 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
|
||||
) {
|
||||
let owner = context;
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('ul', p1, c1);
|
||||
var _2 = ['someval'];
|
||||
@@ -1226,7 +1231,7 @@ exports[`t-on can bind handlers with object arguments 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
var vn1 = h('button', p1, c1);
|
||||
if (!context['add']) {
|
||||
@@ -1242,7 +1247,7 @@ exports[`t-on can bind two event handlers 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
var vn1 = h('button', p1, c1);
|
||||
if (!context['handleClick']) {
|
||||
@@ -1264,7 +1269,7 @@ exports[`t-on handler is bound to proper owner 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
var vn1 = h('button', p1, c1);
|
||||
if (!context['add']) {
|
||||
@@ -1281,7 +1286,7 @@ exports[`t-on t-on with prevent and self modifiers (order matters) 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
@@ -1304,7 +1309,7 @@ exports[`t-on t-on with prevent and/or stop modifiers 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
@@ -1342,7 +1347,7 @@ exports[`t-on t-on with self and prevent modifiers (order matters) 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
@@ -1365,7 +1370,7 @@ exports[`t-on t-on with self modifier 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let owner = context;
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
@@ -1399,12 +1404,13 @@ exports[`t-on t-on with self modifier 1`] = `
|
||||
exports[`t-raw literal 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('span', p1, c1);
|
||||
var _2 = 'ok';
|
||||
if (_2 || _2 === 0) {
|
||||
var frag3 = this.utils.getFragment(_2)
|
||||
var frag3 = utils.getFragment(_2)
|
||||
var p4 = {hook: {
|
||||
insert: n => n.elm.parentNode.replaceChild(frag3, n.elm),
|
||||
}};
|
||||
@@ -1418,12 +1424,13 @@ exports[`t-raw literal 1`] = `
|
||||
exports[`t-raw not escaping 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['var'];
|
||||
if (_2 || _2 === 0) {
|
||||
var frag3 = this.utils.getFragment(_2)
|
||||
var frag3 = utils.getFragment(_2)
|
||||
var p4 = {hook: {
|
||||
insert: n => n.elm.parentNode.replaceChild(frag3, n.elm),
|
||||
}};
|
||||
@@ -1437,7 +1444,8 @@ exports[`t-raw not escaping 1`] = `
|
||||
exports[`t-raw t-raw and another sibling node 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('span', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -1446,7 +1454,7 @@ exports[`t-raw t-raw and another sibling node 1`] = `
|
||||
c2.push({text: \`hello\`});
|
||||
var _3 = context['var'];
|
||||
if (_3 || _3 === 0) {
|
||||
var frag4 = this.utils.getFragment(_3)
|
||||
var frag4 = utils.getFragment(_3)
|
||||
var p5 = {hook: {
|
||||
insert: n => n.elm.parentNode.replaceChild(frag4, n.elm),
|
||||
}};
|
||||
@@ -1460,12 +1468,13 @@ exports[`t-raw t-raw and another sibling node 1`] = `
|
||||
exports[`t-raw variable 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
let utils = this.constructor.utils;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('span', p1, c1);
|
||||
var _2 = context['var'];
|
||||
if (_2 || _2 === 0) {
|
||||
var frag3 = this.utils.getFragment(_2)
|
||||
var frag3 = utils.getFragment(_2)
|
||||
var p4 = {hook: {
|
||||
insert: n => n.elm.parentNode.replaceChild(frag3, n.elm),
|
||||
}};
|
||||
@@ -1479,7 +1488,7 @@ exports[`t-raw variable 1`] = `
|
||||
exports[`t-ref can get a dynamic ref on a node 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -1498,7 +1507,7 @@ exports[`t-ref can get a dynamic ref on a node 1`] = `
|
||||
exports[`t-ref can get a ref on a node 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -1518,7 +1527,7 @@ exports[`t-ref refs in a loop 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['items'];
|
||||
@@ -1556,7 +1565,7 @@ exports[`t-ref refs in a loop 1`] = `
|
||||
exports[`t-set evaluate value expression 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 1+2;
|
||||
@@ -1570,7 +1579,7 @@ exports[`t-set evaluate value expression 1`] = `
|
||||
exports[`t-set evaluate value expression, part 2 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['somevariable']+2;
|
||||
@@ -1584,7 +1593,7 @@ exports[`t-set evaluate value expression, part 2 1`] = `
|
||||
exports[`t-set set from attribute literal 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 'ok';
|
||||
@@ -1598,7 +1607,7 @@ exports[`t-set set from attribute literal 1`] = `
|
||||
exports[`t-set set from attribute lookup 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['value'];
|
||||
@@ -1612,7 +1621,7 @@ exports[`t-set set from attribute lookup 1`] = `
|
||||
exports[`t-set set from body literal 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
var vn1 = {text: \`ok\`};
|
||||
return vn1;
|
||||
}"
|
||||
@@ -1621,7 +1630,7 @@ exports[`t-set set from body literal 1`] = `
|
||||
exports[`t-set set from body lookup 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['value'];
|
||||
@@ -1635,7 +1644,7 @@ exports[`t-set set from body lookup 1`] = `
|
||||
exports[`t-set set from empty body 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
return vn1;
|
||||
@@ -1645,7 +1654,7 @@ exports[`t-set set from empty body 1`] = `
|
||||
exports[`t-set t-set and t-if 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['value'];
|
||||
@@ -1659,7 +1668,7 @@ exports[`t-set t-set and t-if 1`] = `
|
||||
exports[`t-set t-set evaluates an expression only once 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = context['value']+' artois';
|
||||
@@ -1677,7 +1686,7 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
context = Object.create(context);
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 1;
|
||||
@@ -1714,7 +1723,7 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
|
||||
exports[`t-set value priority 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
var _2 = 1;
|
||||
@@ -1728,7 +1737,7 @@ exports[`t-set value priority 1`] = `
|
||||
exports[`whitespace handling consecutives whitespaces are condensed into a single space 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
c1.push({text: \` abc \`});
|
||||
@@ -1739,7 +1748,7 @@ exports[`whitespace handling consecutives whitespaces are condensed into a singl
|
||||
exports[`whitespace handling nothing is done in pre tags 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('pre', p1, c1);
|
||||
c1.push({text: \` \`});
|
||||
@@ -1750,7 +1759,7 @@ exports[`whitespace handling nothing is done in pre tags 1`] = `
|
||||
exports[`whitespace handling nothing is done in pre tags 2`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('pre', p1, c1);
|
||||
c1.push({text: \`
|
||||
@@ -1763,7 +1772,7 @@ exports[`whitespace handling nothing is done in pre tags 2`] = `
|
||||
exports[`whitespace handling nothing is done in pre tags 3`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('pre', p1, c1);
|
||||
c1.push({text: \`
|
||||
@@ -1776,7 +1785,7 @@ exports[`whitespace handling nothing is done in pre tags 3`] = `
|
||||
exports[`whitespace handling white space only text nodes are condensed into a single space 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
c1.push({text: \` \`});
|
||||
@@ -1787,7 +1796,7 @@ exports[`whitespace handling white space only text nodes are condensed into a si
|
||||
exports[`whitespace handling whitespace only text nodes with newlines are removed 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
var h = this.utils.h;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
let c2 = [], p2 = {key:2};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { QWeb } from "../src/qweb_core";
|
||||
import { normalize, renderToDOM, renderToString, trim } from "./helpers";
|
||||
import { QWeb } from "../../src/qweb/index";
|
||||
import { normalize, renderToDOM, renderToString, trim } from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
@@ -1,4 +1,4 @@
|
||||
import { compileExpr, tokenize } from "../src/qweb_expressions";
|
||||
import { compileExpr, tokenize } from "../../src/qweb/expression_parser";
|
||||
|
||||
describe("tokenizer", () => {
|
||||
test("simple tokens", () => {
|
||||
@@ -1,553 +1,10 @@
|
||||
import { Component, Env } from "../src/component";
|
||||
import { Store, ConnectedComponent } from "../src/store";
|
||||
import { makeTestFixture, makeTestEnv, nextMicroTick, nextTick } from "./helpers";
|
||||
import { misc } from "../src";
|
||||
import { core } from "../../src";
|
||||
import { Component, Env } from "../../src/component/component";
|
||||
import { ConnectedComponent } from "../../src/store/connected_component";
|
||||
import { Store } from "../../src/store/store";
|
||||
import { makeTestEnv, makeTestFixture, nextTick } from "../helpers";
|
||||
|
||||
const Observer = misc.Observer;
|
||||
|
||||
describe("basic use", () => {
|
||||
test("commit a mutation", () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
store.commit("inc", 14);
|
||||
expect(store.state.n).toBe(15);
|
||||
});
|
||||
|
||||
test("dispatch an action", () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
}
|
||||
};
|
||||
const actions = {
|
||||
inc({ commit }, delta) {
|
||||
commit("inc", delta);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations, actions });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
store.dispatch("inc", 14);
|
||||
expect(store.state.n).toBe(15);
|
||||
});
|
||||
|
||||
test("dispatch an action + commit a mutation with positional arguments", () => {
|
||||
const state = { n1: 1, n2: 1, n3: 1 };
|
||||
const mutations = {
|
||||
batchInc({ state }, delta1, delta2, delta3) {
|
||||
state.n1 += delta1;
|
||||
state.n2 += delta2;
|
||||
state.n3 += delta3;
|
||||
}
|
||||
};
|
||||
const actions = {
|
||||
batchInc({ commit }, delta1, delta2, delta3) {
|
||||
commit("batchInc", delta1, delta2, delta3);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations, actions });
|
||||
|
||||
expect(store.state.n1).toBe(1);
|
||||
expect(store.state.n2).toBe(1);
|
||||
expect(store.state.n3).toBe(1);
|
||||
store.dispatch("batchInc", 14, 30, 88);
|
||||
expect(store.state.n1).toBe(15);
|
||||
expect(store.state.n2).toBe(31);
|
||||
expect(store.state.n3).toBe(89);
|
||||
});
|
||||
|
||||
test("modifying state outside of mutations trigger error", () => {
|
||||
const state = { n: 1 };
|
||||
const actions = {
|
||||
inc({ state }) {
|
||||
state.n++;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations: {}, actions });
|
||||
|
||||
expect(() => store.dispatch("inc")).toThrow();
|
||||
expect(() => (store.state.n = 15)).toThrow();
|
||||
});
|
||||
|
||||
test("can dispatch an action in an action", () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
}
|
||||
};
|
||||
const actions = {
|
||||
inc({ commit }, delta) {
|
||||
commit("inc", delta);
|
||||
},
|
||||
inc100({ dispatch }) {
|
||||
dispatch("inc", 100);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations, actions });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
store.dispatch("inc100");
|
||||
expect(store.state.n).toBe(101);
|
||||
});
|
||||
|
||||
test("can commit a mutation in a mutation", () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }) {
|
||||
state.n++;
|
||||
},
|
||||
inc10({ commit }) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
commit("inc");
|
||||
}
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
store.commit("inc10");
|
||||
expect(store.state.n).toBe(11);
|
||||
});
|
||||
|
||||
test("return data from committing a mutation", () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }) {
|
||||
return ++state.n;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
const res = store.commit("inc");
|
||||
expect(store.state.n).toBe(2);
|
||||
expect(res).toBe(2);
|
||||
});
|
||||
|
||||
test("dispatch allow synchronizing between actions", async () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
},
|
||||
setN({ state }, n) {
|
||||
state.n = n;
|
||||
}
|
||||
};
|
||||
const actions = {
|
||||
async dosomething({ commit, dispatch }) {
|
||||
await dispatch("setTo10");
|
||||
commit("inc", 3);
|
||||
},
|
||||
async setTo10({ commit }) {
|
||||
await Promise.resolve();
|
||||
commit("setN", 10);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations, actions });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
store.dispatch("dosomething");
|
||||
expect(store.state.n).toBe(1);
|
||||
await nextTick();
|
||||
expect(store.state.n).toBe(13);
|
||||
});
|
||||
|
||||
test("env is given to actions", () => {
|
||||
expect.assertions(1);
|
||||
const someEnv = <Env>{};
|
||||
const actions = {
|
||||
someaction({ env }) {
|
||||
expect(env).toBe(someEnv);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state: {}, actions, env: someEnv });
|
||||
|
||||
store.dispatch("someaction");
|
||||
});
|
||||
|
||||
test("can have getters from store", async () => {
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
},
|
||||
tasters: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "aaron"
|
||||
}
|
||||
}
|
||||
};
|
||||
const getters = {
|
||||
beerTasterName({ state }, beerID) {
|
||||
return state.tasters[state.beers[beerID].tasterID].name;
|
||||
},
|
||||
bestBeerName({ state }) {
|
||||
return state.beers[1].name;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations: {}, actions: {}, getters });
|
||||
expect(store.getters).toBeDefined();
|
||||
expect((<any>store.getters).bestBeerName()).toBe("bertinchamps");
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||
});
|
||||
|
||||
test("getters are memoized", async () => {
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
},
|
||||
tasters: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "aaron"
|
||||
}
|
||||
}
|
||||
};
|
||||
let n = 0;
|
||||
const getters = {
|
||||
beerTasterName({ state }, beerID) {
|
||||
n++;
|
||||
return state.tasters[state.beers[beerID].tasterID].name;
|
||||
},
|
||||
bestBeerName({ state }) {
|
||||
n++;
|
||||
return state.beers[1].name;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations: {}, actions: {}, getters });
|
||||
expect((<any>store.getters).bestBeerName()).toBe("bertinchamps");
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||
expect(n).toBe(2);
|
||||
expect((<any>store.getters).bestBeerName()).toBe("bertinchamps");
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||
expect(n).toBe(2);
|
||||
});
|
||||
|
||||
test("getters taking Array as argument aren't memoized", async () => {
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
let n = 0;
|
||||
const getters = {
|
||||
getBeerNames({ state }, beerIDs) {
|
||||
n++;
|
||||
return beerIDs.map(beerID => {
|
||||
return state.beers[beerID].name;
|
||||
});
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations: {}, actions: {}, getters });
|
||||
expect((<any>store.getters).getBeerNames([1])).toEqual(["bertinchamps"]);
|
||||
expect(n).toBe(1);
|
||||
expect((<any>store.getters).getBeerNames([1])).toEqual(["bertinchamps"]);
|
||||
expect(n).toBe(2);
|
||||
});
|
||||
|
||||
test("getters cache is nuked on store changes", async () => {
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
},
|
||||
tasters: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "aaron"
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
name: "gery"
|
||||
}
|
||||
}
|
||||
};
|
||||
const mutations = {
|
||||
changeTaster({ state }, { beerID, tasterID }) {
|
||||
state.beers[beerID].tasterID = tasterID;
|
||||
}
|
||||
};
|
||||
let n = 0;
|
||||
const getters = {
|
||||
beerTasterName({ state }, beerID) {
|
||||
n++;
|
||||
return state.tasters[state.beers[beerID].tasterID].name;
|
||||
}
|
||||
};
|
||||
const store = new Store({
|
||||
state,
|
||||
mutations: mutations,
|
||||
actions: {},
|
||||
getters
|
||||
});
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||
expect(n).toBe(1);
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||
expect(n).toBe(1);
|
||||
|
||||
store.commit("changeTaster", { beerID: 1, tasterID: 2 });
|
||||
await nextTick();
|
||||
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("gery");
|
||||
expect(n).toBe(2);
|
||||
});
|
||||
|
||||
test("getters cache is disabled during a mutation", async () => {
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps"
|
||||
}
|
||||
}
|
||||
};
|
||||
const mutations = {
|
||||
renameBeer({ state, getters }, beerID) {
|
||||
expect(getters.beerName(beerID)).toBe("bertinchamps");
|
||||
state.beers[1].name = "chouffe";
|
||||
expect(getters.beerName(beerID)).toBe("chouffe");
|
||||
}
|
||||
};
|
||||
let n = 0;
|
||||
const getters = {
|
||||
beerName({ state }, beerID) {
|
||||
n++;
|
||||
return state.beers[beerID].name;
|
||||
}
|
||||
};
|
||||
const store = new Store({
|
||||
state,
|
||||
mutations: mutations,
|
||||
actions: {},
|
||||
getters
|
||||
});
|
||||
|
||||
store.commit("renameBeer", 1);
|
||||
expect((<any>store.getters).beerName(1)).toBe("chouffe");
|
||||
await nextTick();
|
||||
|
||||
expect(n).toBe(3);
|
||||
});
|
||||
|
||||
test("getters given to actions", async () => {
|
||||
expect.assertions(3);
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
},
|
||||
tasters: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "aaron"
|
||||
}
|
||||
}
|
||||
};
|
||||
const getters = {
|
||||
beerTasterName({ state }, beerID) {
|
||||
return state.tasters[state.beers[beerID].tasterID].name;
|
||||
},
|
||||
bestBeerName({ state }) {
|
||||
return state.beers[1].name;
|
||||
}
|
||||
};
|
||||
const actions = {
|
||||
action({ getters }) {
|
||||
expect(getters).toBeDefined();
|
||||
expect(getters.bestBeerName()).toBe("bertinchamps");
|
||||
expect(getters.beerTasterName(1)).toBe("aaron");
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations: {}, actions, getters });
|
||||
store.dispatch("action");
|
||||
});
|
||||
|
||||
test("getters given to mutations", async () => {
|
||||
expect.assertions(3);
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
},
|
||||
tasters: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "aaron"
|
||||
}
|
||||
}
|
||||
};
|
||||
const getters = {
|
||||
beerTasterName({ state }, beerID) {
|
||||
return state.tasters[state.beers[beerID].tasterID].name;
|
||||
},
|
||||
bestBeerName({ state }) {
|
||||
return state.beers[1].name;
|
||||
}
|
||||
};
|
||||
const mutations = {
|
||||
mutation({ getters }) {
|
||||
expect(getters).toBeDefined();
|
||||
expect(getters.bestBeerName()).toBe("bertinchamps");
|
||||
expect(getters.beerTasterName(1)).toBe("aaron");
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations, actions: {}, getters });
|
||||
store.commit("mutation");
|
||||
});
|
||||
|
||||
test("can use getters inside a getter", () => {
|
||||
const getters = {
|
||||
a({ getters }) {
|
||||
return `${getters.b()}${getters.c(1)}`;
|
||||
},
|
||||
b() {
|
||||
return "b";
|
||||
},
|
||||
c({}, i) {
|
||||
return `c${i}`;
|
||||
}
|
||||
};
|
||||
const store = new Store({ getters });
|
||||
|
||||
expect(store.getters.a()).toBe("bc1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("advanced state properties", () => {
|
||||
test("state in the store is reference equal after mutation", async () => {
|
||||
const state = {};
|
||||
const mutations = {
|
||||
donothing() {}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
expect(store.state).toBe(state);
|
||||
store.commit("donothing");
|
||||
expect(store.state).toBe(state);
|
||||
});
|
||||
|
||||
test("can use array properties in mutations", () => {
|
||||
expect.assertions(3);
|
||||
const state = { a: [1, 2, 3] };
|
||||
const mutations = {
|
||||
m({ state }) {
|
||||
expect(state.a.length).toBe(3);
|
||||
const l = state.a.push(53);
|
||||
expect(l).toBe(4);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
store.commit("m");
|
||||
expect(store.state.a).toEqual([1, 2, 3, 53]);
|
||||
});
|
||||
|
||||
test("can use object assign in store", async () => {
|
||||
const mutations = {
|
||||
dosomething({ state }) {
|
||||
Object.assign(state.westmalle, { a: 3, b: 4 });
|
||||
}
|
||||
};
|
||||
const store = new Store({
|
||||
state: { westmalle: { a: 1, b: 2 } },
|
||||
mutations
|
||||
});
|
||||
store.commit("dosomething");
|
||||
expect(store.state.westmalle).toEqual({ a: 3, b: 4 });
|
||||
});
|
||||
|
||||
test("aku reactive store state 1", async () => {
|
||||
const mutations = {
|
||||
inc({ state }) {
|
||||
state.counter++;
|
||||
}
|
||||
};
|
||||
const state = { counter: 0 };
|
||||
const store = new Store({ state, mutations });
|
||||
expect(store.state.counter).toBe(0);
|
||||
store.commit("inc", {});
|
||||
expect(store.state.counter).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("updates triggered by the store", () => {
|
||||
test("multiple commits trigger one update", async () => {
|
||||
let updateCounter = 0;
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
store.on("update", null, () => updateCounter++);
|
||||
|
||||
store.commit("inc", 14);
|
||||
expect(updateCounter).toBe(0);
|
||||
store.commit("inc", 50);
|
||||
expect(updateCounter).toBe(0);
|
||||
await nextMicroTick();
|
||||
expect(updateCounter).toBe(1);
|
||||
});
|
||||
|
||||
test("empty commits do not trigger updates", async () => {
|
||||
let updateCounter = 0;
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
},
|
||||
noop() {},
|
||||
noop2({ state }) {
|
||||
const val = state.n;
|
||||
state.n = val;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
store.on("update", null, () => updateCounter++);
|
||||
|
||||
store.commit("noop");
|
||||
await nextMicroTick();
|
||||
expect(updateCounter).toBe(0);
|
||||
|
||||
store.commit("inc", 50);
|
||||
await nextMicroTick();
|
||||
expect(updateCounter).toBe(1);
|
||||
|
||||
store.commit("noop2");
|
||||
await nextMicroTick();
|
||||
expect(updateCounter).toBe(1);
|
||||
});
|
||||
});
|
||||
const Observer = core.Observer;
|
||||
|
||||
describe("connecting a component to store", () => {
|
||||
let fixture: HTMLElement;
|
||||
@@ -1301,5 +758,4 @@ describe("connecting a component to store", () => {
|
||||
expect(fixture.innerHTML).toBe("<div>b</div>");
|
||||
expect(steps).toEqual(["willpatch", "patched"]);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,549 @@
|
||||
import { Env } from "../../src/component/component";
|
||||
import { Store } from "../../src/store/store";
|
||||
import { nextMicroTick, nextTick } from "../helpers";
|
||||
|
||||
|
||||
describe("basic use", () => {
|
||||
test("commit a mutation", () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
store.commit("inc", 14);
|
||||
expect(store.state.n).toBe(15);
|
||||
});
|
||||
|
||||
test("dispatch an action", () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
}
|
||||
};
|
||||
const actions = {
|
||||
inc({ commit }, delta) {
|
||||
commit("inc", delta);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations, actions });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
store.dispatch("inc", 14);
|
||||
expect(store.state.n).toBe(15);
|
||||
});
|
||||
|
||||
test("dispatch an action + commit a mutation with positional arguments", () => {
|
||||
const state = { n1: 1, n2: 1, n3: 1 };
|
||||
const mutations = {
|
||||
batchInc({ state }, delta1, delta2, delta3) {
|
||||
state.n1 += delta1;
|
||||
state.n2 += delta2;
|
||||
state.n3 += delta3;
|
||||
}
|
||||
};
|
||||
const actions = {
|
||||
batchInc({ commit }, delta1, delta2, delta3) {
|
||||
commit("batchInc", delta1, delta2, delta3);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations, actions });
|
||||
|
||||
expect(store.state.n1).toBe(1);
|
||||
expect(store.state.n2).toBe(1);
|
||||
expect(store.state.n3).toBe(1);
|
||||
store.dispatch("batchInc", 14, 30, 88);
|
||||
expect(store.state.n1).toBe(15);
|
||||
expect(store.state.n2).toBe(31);
|
||||
expect(store.state.n3).toBe(89);
|
||||
});
|
||||
|
||||
test("modifying state outside of mutations trigger error", () => {
|
||||
const state = { n: 1 };
|
||||
const actions = {
|
||||
inc({ state }) {
|
||||
state.n++;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations: {}, actions });
|
||||
|
||||
expect(() => store.dispatch("inc")).toThrow();
|
||||
expect(() => (store.state.n = 15)).toThrow();
|
||||
});
|
||||
|
||||
test("can dispatch an action in an action", () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
}
|
||||
};
|
||||
const actions = {
|
||||
inc({ commit }, delta) {
|
||||
commit("inc", delta);
|
||||
},
|
||||
inc100({ dispatch }) {
|
||||
dispatch("inc", 100);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations, actions });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
store.dispatch("inc100");
|
||||
expect(store.state.n).toBe(101);
|
||||
});
|
||||
|
||||
test("can commit a mutation in a mutation", () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }) {
|
||||
state.n++;
|
||||
},
|
||||
inc10({ commit }) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
commit("inc");
|
||||
}
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
store.commit("inc10");
|
||||
expect(store.state.n).toBe(11);
|
||||
});
|
||||
|
||||
test("return data from committing a mutation", () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }) {
|
||||
return ++state.n;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
const res = store.commit("inc");
|
||||
expect(store.state.n).toBe(2);
|
||||
expect(res).toBe(2);
|
||||
});
|
||||
|
||||
test("dispatch allow synchronizing between actions", async () => {
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
},
|
||||
setN({ state }, n) {
|
||||
state.n = n;
|
||||
}
|
||||
};
|
||||
const actions = {
|
||||
async dosomething({ commit, dispatch }) {
|
||||
await dispatch("setTo10");
|
||||
commit("inc", 3);
|
||||
},
|
||||
async setTo10({ commit }) {
|
||||
await Promise.resolve();
|
||||
commit("setN", 10);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations, actions });
|
||||
|
||||
expect(store.state.n).toBe(1);
|
||||
store.dispatch("dosomething");
|
||||
expect(store.state.n).toBe(1);
|
||||
await nextTick();
|
||||
expect(store.state.n).toBe(13);
|
||||
});
|
||||
|
||||
test("env is given to actions", () => {
|
||||
expect.assertions(1);
|
||||
const someEnv = <Env>{};
|
||||
const actions = {
|
||||
someaction({ env }) {
|
||||
expect(env).toBe(someEnv);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state: {}, actions, env: someEnv });
|
||||
|
||||
store.dispatch("someaction");
|
||||
});
|
||||
|
||||
test("can have getters from store", async () => {
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
},
|
||||
tasters: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "aaron"
|
||||
}
|
||||
}
|
||||
};
|
||||
const getters = {
|
||||
beerTasterName({ state }, beerID) {
|
||||
return state.tasters[state.beers[beerID].tasterID].name;
|
||||
},
|
||||
bestBeerName({ state }) {
|
||||
return state.beers[1].name;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations: {}, actions: {}, getters });
|
||||
expect(store.getters).toBeDefined();
|
||||
expect((<any>store.getters).bestBeerName()).toBe("bertinchamps");
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||
});
|
||||
|
||||
test("getters are memoized", async () => {
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
},
|
||||
tasters: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "aaron"
|
||||
}
|
||||
}
|
||||
};
|
||||
let n = 0;
|
||||
const getters = {
|
||||
beerTasterName({ state }, beerID) {
|
||||
n++;
|
||||
return state.tasters[state.beers[beerID].tasterID].name;
|
||||
},
|
||||
bestBeerName({ state }) {
|
||||
n++;
|
||||
return state.beers[1].name;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations: {}, actions: {}, getters });
|
||||
expect((<any>store.getters).bestBeerName()).toBe("bertinchamps");
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||
expect(n).toBe(2);
|
||||
expect((<any>store.getters).bestBeerName()).toBe("bertinchamps");
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||
expect(n).toBe(2);
|
||||
});
|
||||
|
||||
test("getters taking Array as argument aren't memoized", async () => {
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
let n = 0;
|
||||
const getters = {
|
||||
getBeerNames({ state }, beerIDs) {
|
||||
n++;
|
||||
return beerIDs.map(beerID => {
|
||||
return state.beers[beerID].name;
|
||||
});
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations: {}, actions: {}, getters });
|
||||
expect((<any>store.getters).getBeerNames([1])).toEqual(["bertinchamps"]);
|
||||
expect(n).toBe(1);
|
||||
expect((<any>store.getters).getBeerNames([1])).toEqual(["bertinchamps"]);
|
||||
expect(n).toBe(2);
|
||||
});
|
||||
|
||||
test("getters cache is nuked on store changes", async () => {
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
},
|
||||
tasters: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "aaron"
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
name: "gery"
|
||||
}
|
||||
}
|
||||
};
|
||||
const mutations = {
|
||||
changeTaster({ state }, { beerID, tasterID }) {
|
||||
state.beers[beerID].tasterID = tasterID;
|
||||
}
|
||||
};
|
||||
let n = 0;
|
||||
const getters = {
|
||||
beerTasterName({ state }, beerID) {
|
||||
n++;
|
||||
return state.tasters[state.beers[beerID].tasterID].name;
|
||||
}
|
||||
};
|
||||
const store = new Store({
|
||||
state,
|
||||
mutations: mutations,
|
||||
actions: {},
|
||||
getters
|
||||
});
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||
expect(n).toBe(1);
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("aaron");
|
||||
expect(n).toBe(1);
|
||||
|
||||
store.commit("changeTaster", { beerID: 1, tasterID: 2 });
|
||||
await nextTick();
|
||||
|
||||
expect((<any>store.getters).beerTasterName(1)).toBe("gery");
|
||||
expect(n).toBe(2);
|
||||
});
|
||||
|
||||
test("getters cache is disabled during a mutation", async () => {
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps"
|
||||
}
|
||||
}
|
||||
};
|
||||
const mutations = {
|
||||
renameBeer({ state, getters }, beerID) {
|
||||
expect(getters.beerName(beerID)).toBe("bertinchamps");
|
||||
state.beers[1].name = "chouffe";
|
||||
expect(getters.beerName(beerID)).toBe("chouffe");
|
||||
}
|
||||
};
|
||||
let n = 0;
|
||||
const getters = {
|
||||
beerName({ state }, beerID) {
|
||||
n++;
|
||||
return state.beers[beerID].name;
|
||||
}
|
||||
};
|
||||
const store = new Store({
|
||||
state,
|
||||
mutations: mutations,
|
||||
actions: {},
|
||||
getters
|
||||
});
|
||||
|
||||
store.commit("renameBeer", 1);
|
||||
expect((<any>store.getters).beerName(1)).toBe("chouffe");
|
||||
await nextTick();
|
||||
|
||||
expect(n).toBe(3);
|
||||
});
|
||||
|
||||
test("getters given to actions", async () => {
|
||||
expect.assertions(3);
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
},
|
||||
tasters: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "aaron"
|
||||
}
|
||||
}
|
||||
};
|
||||
const getters = {
|
||||
beerTasterName({ state }, beerID) {
|
||||
return state.tasters[state.beers[beerID].tasterID].name;
|
||||
},
|
||||
bestBeerName({ state }) {
|
||||
return state.beers[1].name;
|
||||
}
|
||||
};
|
||||
const actions = {
|
||||
action({ getters }) {
|
||||
expect(getters).toBeDefined();
|
||||
expect(getters.bestBeerName()).toBe("bertinchamps");
|
||||
expect(getters.beerTasterName(1)).toBe("aaron");
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations: {}, actions, getters });
|
||||
store.dispatch("action");
|
||||
});
|
||||
|
||||
test("getters given to mutations", async () => {
|
||||
expect.assertions(3);
|
||||
const state = {
|
||||
beers: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "bertinchamps",
|
||||
tasterID: 1
|
||||
}
|
||||
},
|
||||
tasters: {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "aaron"
|
||||
}
|
||||
}
|
||||
};
|
||||
const getters = {
|
||||
beerTasterName({ state }, beerID) {
|
||||
return state.tasters[state.beers[beerID].tasterID].name;
|
||||
},
|
||||
bestBeerName({ state }) {
|
||||
return state.beers[1].name;
|
||||
}
|
||||
};
|
||||
const mutations = {
|
||||
mutation({ getters }) {
|
||||
expect(getters).toBeDefined();
|
||||
expect(getters.bestBeerName()).toBe("bertinchamps");
|
||||
expect(getters.beerTasterName(1)).toBe("aaron");
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations, actions: {}, getters });
|
||||
store.commit("mutation");
|
||||
});
|
||||
|
||||
test("can use getters inside a getter", () => {
|
||||
const getters = {
|
||||
a({ getters }) {
|
||||
return `${getters.b()}${getters.c(1)}`;
|
||||
},
|
||||
b() {
|
||||
return "b";
|
||||
},
|
||||
c({}, i) {
|
||||
return `c${i}`;
|
||||
}
|
||||
};
|
||||
const store = new Store({ getters });
|
||||
|
||||
expect(store.getters.a()).toBe("bc1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("advanced state properties", () => {
|
||||
test("state in the store is reference equal after mutation", async () => {
|
||||
const state = {};
|
||||
const mutations = {
|
||||
donothing() {}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
expect(store.state).toBe(state);
|
||||
store.commit("donothing");
|
||||
expect(store.state).toBe(state);
|
||||
});
|
||||
|
||||
test("can use array properties in mutations", () => {
|
||||
expect.assertions(3);
|
||||
const state = { a: [1, 2, 3] };
|
||||
const mutations = {
|
||||
m({ state }) {
|
||||
expect(state.a.length).toBe(3);
|
||||
const l = state.a.push(53);
|
||||
expect(l).toBe(4);
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
store.commit("m");
|
||||
expect(store.state.a).toEqual([1, 2, 3, 53]);
|
||||
});
|
||||
|
||||
test("can use object assign in store", async () => {
|
||||
const mutations = {
|
||||
dosomething({ state }) {
|
||||
Object.assign(state.westmalle, { a: 3, b: 4 });
|
||||
}
|
||||
};
|
||||
const store = new Store({
|
||||
state: { westmalle: { a: 1, b: 2 } },
|
||||
mutations
|
||||
});
|
||||
store.commit("dosomething");
|
||||
expect(store.state.westmalle).toEqual({ a: 3, b: 4 });
|
||||
});
|
||||
|
||||
test("aku reactive store state 1", async () => {
|
||||
const mutations = {
|
||||
inc({ state }) {
|
||||
state.counter++;
|
||||
}
|
||||
};
|
||||
const state = { counter: 0 };
|
||||
const store = new Store({ state, mutations });
|
||||
expect(store.state.counter).toBe(0);
|
||||
store.commit("inc", {});
|
||||
expect(store.state.counter).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("updates triggered by the store", () => {
|
||||
test("multiple commits trigger one update", async () => {
|
||||
let updateCounter = 0;
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
store.on("update", null, () => updateCounter++);
|
||||
|
||||
store.commit("inc", 14);
|
||||
expect(updateCounter).toBe(0);
|
||||
store.commit("inc", 50);
|
||||
expect(updateCounter).toBe(0);
|
||||
await nextMicroTick();
|
||||
expect(updateCounter).toBe(1);
|
||||
});
|
||||
|
||||
test("empty commits do not trigger updates", async () => {
|
||||
let updateCounter = 0;
|
||||
const state = { n: 1 };
|
||||
const mutations = {
|
||||
inc({ state }, delta) {
|
||||
state.n += delta;
|
||||
},
|
||||
noop() {},
|
||||
noop2({ state }) {
|
||||
const val = state.n;
|
||||
state.n = val;
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, mutations });
|
||||
store.on("update", null, () => updateCounter++);
|
||||
|
||||
store.commit("noop");
|
||||
await nextMicroTick();
|
||||
expect(updateCounter).toBe(0);
|
||||
|
||||
store.commit("inc", 50);
|
||||
await nextMicroTick();
|
||||
expect(updateCounter).toBe(1);
|
||||
|
||||
store.commit("noop2");
|
||||
await nextMicroTick();
|
||||
expect(updateCounter).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
import { h, patch, init } from "../src/vdom";
|
||||
import { h, patch } from "../src/vdom";
|
||||
import { init } from "../src/vdom/vdom";
|
||||
|
||||
function map(list, fn) {
|
||||
var ret: any[] = [];
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
"noImplicitThis": true,
|
||||
"removeComments": false,
|
||||
"declaration": true,
|
||||
"target": "es5",
|
||||
"target": "esnext",
|
||||
"outDir": "dist",
|
||||
"alwaysStrict": true,
|
||||
"noUnusedLocals": true,
|
||||
@@ -15,5 +15,5 @@
|
||||
"strictPropertyInitialization": true,
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"include": ["src/*.ts"]
|
||||
"include": ["src/**/*.ts","src/*.ts"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user