[IMP] owl: update to v1.0.0

This commit is contained in:
Géry Debongnie
2020-01-09 14:50:10 +01:00
parent 7aed9a6f82
commit bf2c2bff55
2 changed files with 111 additions and 25 deletions
+104 -18
View File
@@ -837,7 +837,7 @@
};
// note that the space after typeof is relevant. It makes sure that the formatted
// expression has a space after typeof
const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;".split(",");
const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;,in ".split(",");
let tokenizeString = function (expr) {
let s = expr[0];
let start = s;
@@ -981,7 +981,7 @@
* the arrow operator, then we add the current (or some previous tokens) token to
* the list of variables so it does not get replaced by a lookup in the context
*/
function compileExpr(expr, scope) {
function compileExprToArray(expr, scope) {
scope = Object.create(scope);
const tokens = tokenize(expr);
for (let i = 0; i < tokens.length; i++) {
@@ -1017,6 +1017,7 @@
}
}
if (isVar) {
token.varName = token.value;
if (token.value in scope && "id" in scope[token.value]) {
token.value = scope[token.value].expr;
}
@@ -1026,7 +1027,12 @@
}
}
}
return tokens.map(t => t.value).join("");
return tokens;
}
function compileExpr(expr, scope) {
return compileExprToArray(expr, scope)
.map(t => t.value)
.join("");
}
const INTERP_REGEXP = /\{\{.*?\}\}/g;
@@ -1044,6 +1050,7 @@
this.indentLevel = 0;
this.shouldDefineParent = false;
this.shouldDefineScope = false;
this.protectedScopeNumber = 0;
this.shouldDefineQWeb = false;
this.shouldDefineUtils = false;
this.shouldDefineRefs = false;
@@ -1164,6 +1171,24 @@
this.rootContext.shouldDefineScope = true;
return compileExpr(expr, this.variables);
}
captureExpression(expr) {
this.rootContext.shouldDefineScope = true;
const argId = this.generateID();
const tokens = compileExprToArray(expr, this.variables);
const done = new Set();
return tokens
.map(tok => {
if (tok.varName) {
if (!done.has(tok.varName)) {
done.add(tok.varName);
this.addLine(`const ${tok.varName}_${argId} = ${tok.value};`);
}
tok.value = `${tok.varName}_${argId}`;
}
return tok.value;
})
.join("");
}
/**
* Perform string interpolation on the given string. Note that if the whole
* string is an expression, it simply returns it (formatted and enclosed in
@@ -1180,14 +1205,19 @@
let r = s.replace(/\{\{.*?\}\}/g, s => "${" + this.formatExpression(s.slice(2, -2)) + "}");
return "`" + r + "`";
}
startProtectScope() {
startProtectScope(codeBlock) {
const protectID = this.generateID();
this.rootContext.protectedScopeNumber++;
this.rootContext.shouldDefineScope = true;
const scopeExpr = codeBlock
? `Object.create(scope);`
: `Object.assign(Object.create(context), scope);`;
this.addLine(`let _origScope${protectID} = scope;`);
this.addLine(`scope = Object.assign(Object.create(context), scope);`);
this.addLine(`scope = ${scopeExpr}`);
return protectID;
}
stopProtectScope(protectID) {
this.rootContext.protectedScopeNumber--;
this.addLine(`scope = _origScope${protectID};`);
}
}
@@ -1312,6 +1342,9 @@
remove: "(vn, rm)",
destroy: "()"
};
function isComponent(obj) {
return obj && obj.hasOwnProperty("__owl__");
}
const UTILS = {
zero: Symbol("zero"),
toObj(expr) {
@@ -1350,10 +1383,21 @@
.join("");
},
getComponent(obj) {
while (obj && !obj.hasOwnProperty("__owl__")) {
while (obj && !isComponent(obj)) {
obj = obj.__proto__;
}
return obj;
},
getScope(obj, property) {
const obj0 = obj;
while (obj && !obj.hasOwnProperty(property)) {
const newObj = obj.__proto__;
if (!newObj || isComponent(newObj)) {
return obj0;
}
obj = newObj;
}
return obj;
}
};
function parseXML(xml) {
@@ -2107,7 +2151,12 @@
qwebvar.expr = `scope.${variable}`;
if (value) {
const formattedValue = ctx.formatExpression(value);
ctx.addLine(`${qwebvar.expr} = ${formattedValue};`);
let scopeExpr = `scope`;
if (ctx.protectedScopeNumber) {
ctx.rootContext.shouldDefineUtils = true;
scopeExpr = `utils.getScope(scope, '${variable}')`;
}
ctx.addLine(`${scopeExpr}.${variable} = ${formattedValue};`);
qwebvar.value = formattedValue;
}
if (hasBody) {
@@ -2231,9 +2280,9 @@
// ------------------------------------------------
const callingScope = hasBody ? "scope" : "Object.assign(Object.create(context), scope)";
const parentComponent = `utils.getComponent(context)`;
const keyCode = ctx.loopNumber || ctx.hasKey0 ? `, key: ${ctx.generateTemplateKey()}` : "";
const key = ctx.generateTemplateKey();
const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result";
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}${keyCode}})`;
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}, key: ${key}})`;
if (ctx.parentNode) {
ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`);
}
@@ -2277,7 +2326,7 @@
ctx.addLine(`_${valuesID} = Object.values(_${arrayID});`);
ctx.closeIf();
ctx.addLine(`let _length${keysID} = _${keysID}.length;`);
let varsID = ctx.startProtectScope();
let varsID = ctx.startProtectScope(true);
const loopVar = `i${ctx.loopNumber}`;
ctx.addLine(`for (let ${loopVar} = 0; ${loopVar} < _length${keysID}; ${loopVar}++) {`);
ctx.indent();
@@ -2374,7 +2423,7 @@
ctx.rootContext.shouldDefineUtils = true;
const comp = `utils.getComponent(context)`;
if (args) {
let argId = ctx.generateID();
const argId = ctx.generateID();
ctx.addLine(`let args${argId} = [${ctx.formatExpression(args)}];`);
code = `${comp}['${name}'](...args${argId}, e);`;
putInCache = false;
@@ -2385,8 +2434,9 @@
}
else {
// if we get here, then it is an expression
// we need to capture every variable in it
putInCache = false;
code = ctx.formatExpression(value);
code = ctx.captureExpression(value);
}
const modCode = mods.map(mod => modcodes[mod]).join("");
let handler = `function (e) {if (!context.__owl__.isMounted){return}${modCode}${code}}`;
@@ -3484,9 +3534,23 @@
const selectorStack = [];
const parts = [];
let rules = [];
function generateSelector(stackIndex, parentSelector) {
const parts = [];
for (const selector of selectorStack[stackIndex]) {
let part = (parentSelector && parentSelector + " " + selector) || selector;
if (part.includes("&")) {
part = selector.replace(/&/g, parentSelector || "");
}
if (stackIndex < selectorStack.length - 1) {
part = generateSelector(stackIndex + 1, part);
}
parts.push(part);
}
return parts.join(", ");
}
function generateRules() {
if (rules.length) {
parts.push(selectorStack.join(" ") + " {");
parts.push(generateSelector(0) + " {");
parts.push(...rules);
parts.push("}");
rules = [];
@@ -3501,7 +3565,7 @@
else {
if (tokens[0] === "{") {
generateRules();
selectorStack.push(token);
selectorStack.push(token.split(/\s*,\s*/));
tokens.shift();
}
if (tokens[0] === ";") {
@@ -4344,6 +4408,27 @@
const component = Component.current;
component.env = Object.assign(Object.create(component.env), nextEnv);
}
// -----------------------------------------------------------------------------
// useExternalListener
// -----------------------------------------------------------------------------
/**
* When a component needs to listen to DOM Events on element(s) that are not
* part of his hierarchy, we can use the `useExternalListener` hook.
* It will correctly add and remove the event listener, whenever the
* component is mounted and unmounted.
*
* Example:
* a menu needs to listen to the click on window to be closed automatically
*
* Usage:
* in the constructor of the OWL component that needs to be notified,
* `useExternalListener(window, 'click', this._doSomething);`
* */
function useExternalListener(target, eventName, handler, eventParams) {
const boundHandler = handler.bind(Component.current);
onMounted(() => target.addEventListener(eventName, boundHandler, eventParams));
onWillUnmount(() => target.removeEventListener(eventName, boundHandler, eventParams));
}
var _hooks = /*#__PURE__*/Object.freeze({
__proto__: null,
@@ -4355,7 +4440,8 @@
onWillStart: onWillStart,
onWillUpdateProps: onWillUpdateProps,
useRef: useRef,
useSubEnv: useSubEnv
useSubEnv: useSubEnv,
useExternalListener: useExternalListener
});
class Store extends Context {
@@ -4972,9 +5058,9 @@
exports.useState = useState$1;
exports.utils = utils;
exports.__info__.version = '1.0.0-beta5';
exports.__info__.date = '2019-12-20T10:27:22.467Z';
exports.__info__.hash = 'b63cd4c';
exports.__info__.version = '1.0.0';
exports.__info__.date = '2020-01-09T13:45:31.837Z';
exports.__info__.hash = '3d0f046';
exports.__info__.url = 'https://github.com/odoo/owl';
}(this.owl = this.owl || {}));
+7 -7
View File
@@ -455,7 +455,7 @@ const actions = {
todo.completed = !todo.completed;
},
clearCompleted({ state, dispatch }) {
for (let todo of state.todos) {
for (let todo of state.todos.slice()) {
if (todo.completed) {
dispatch("removeTodo", todo.id);
}
@@ -1089,20 +1089,20 @@ const RESPONSIVE_XML = `<templates>
<button>Button!</button>
</div>
<t t-name="maincontent">
<FormView />
<Chatter />
</t>
<div t-name="App" class="app" t-att-class="{mobile: env.isMobile, desktop: !env.isMobile}">
<t t-set="maincontent">
<FormView />
<Chatter />
</t>
<Navbar/>
<ControlPanel/>
<div class="content-wrapper" t-if="!env.isMobile">
<div class="content">
<t t-raw="maincontent"/>
<t t-call="maincontent"/>
</div>
</div>
<t t-else="">
<t t-raw="maincontent"/>
<t t-call="maincontent"/>
</t>
</div>
</templates>