mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] owl: update to v0.22.0
This commit is contained in:
@@ -385,6 +385,7 @@
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const INTERP_REGEXP = /\{\{.*?\}\}/g;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Compilation Context
|
// Compilation Context
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -402,6 +403,7 @@
|
|||||||
this.shouldDefineParent = false;
|
this.shouldDefineParent = false;
|
||||||
this.shouldDefineQWeb = false;
|
this.shouldDefineQWeb = false;
|
||||||
this.shouldDefineUtils = false;
|
this.shouldDefineUtils = false;
|
||||||
|
this.shouldDefineRefs = false;
|
||||||
this.shouldDefineResult = true;
|
this.shouldDefineResult = true;
|
||||||
this.shouldProtectContext = false;
|
this.shouldProtectContext = false;
|
||||||
this.shouldTrackScope = false;
|
this.shouldTrackScope = false;
|
||||||
@@ -439,6 +441,9 @@
|
|||||||
if (this.shouldDefineResult) {
|
if (this.shouldDefineResult) {
|
||||||
this.code.unshift(" let result;");
|
this.code.unshift(" let result;");
|
||||||
}
|
}
|
||||||
|
if (this.shouldDefineRefs) {
|
||||||
|
this.code.unshift(" context.__owl__.refs = context.__owl__.refs || {};");
|
||||||
|
}
|
||||||
if (this.shouldDefineOwner) {
|
if (this.shouldDefineOwner) {
|
||||||
// this is necessary to prevent some directives (t-forach for ex) to
|
// this is necessary to prevent some directives (t-forach for ex) to
|
||||||
// pollute the rendering context by adding some keys in it.
|
// pollute the rendering context by adding some keys in it.
|
||||||
@@ -528,7 +533,7 @@
|
|||||||
* '{{x ? 'a': 'b'}}' -> (x ? 'a' : 'b')
|
* '{{x ? 'a': 'b'}}' -> (x ? 'a' : 'b')
|
||||||
*/
|
*/
|
||||||
interpolate(s) {
|
interpolate(s) {
|
||||||
let matches = s.match(/\{\{.*?\}\}/g);
|
let matches = s.match(INTERP_REGEXP);
|
||||||
if (matches && matches[0].length === s.length) {
|
if (matches && matches[0].length === s.length) {
|
||||||
return `(${this.formatExpression(s.slice(2, -2))})`;
|
return `(${this.formatExpression(s.slice(2, -2))})`;
|
||||||
}
|
}
|
||||||
@@ -1133,9 +1138,13 @@
|
|||||||
data.ns = "http://www.w3.org/2000/svg";
|
data.ns = "http://www.w3.org/2000/svg";
|
||||||
if (sel !== "foreignObject" && children !== undefined) {
|
if (sel !== "foreignObject" && children !== undefined) {
|
||||||
for (let i = 0, iLen = children.length; i < iLen; ++i) {
|
for (let i = 0, iLen = children.length; i < iLen; ++i) {
|
||||||
let childData = children[i].data;
|
const child = children[i];
|
||||||
|
if (child === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let childData = child.data;
|
||||||
if (childData !== undefined) {
|
if (childData !== undefined) {
|
||||||
addNS(childData, children[i].children, children[i].sel);
|
addNS(childData, child.children, child.sel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1174,12 +1183,6 @@
|
|||||||
children[i] = vnode(undefined, undefined, undefined, children[i], undefined);
|
children[i] = vnode(undefined, undefined, undefined, children[i], undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sel[0] === "s" &&
|
|
||||||
sel[1] === "v" &&
|
|
||||||
sel[2] === "g" &&
|
|
||||||
(sel.length === 3 || sel[3] === "." || sel[3] === "#")) {
|
|
||||||
addNS(data, children, sel);
|
|
||||||
}
|
|
||||||
return vnode(sel, data, children, text, undefined);
|
return vnode(sel, data, children, text, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1232,9 +1235,7 @@
|
|||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
throw new Error("Error while fetching xml templates");
|
throw new Error("Error while fetching xml templates");
|
||||||
}
|
}
|
||||||
let templates = await result.text();
|
return await result.text();
|
||||||
templates = templates.replace(/<!--[\s\S]*?-->/g, "");
|
|
||||||
return templates;
|
|
||||||
}
|
}
|
||||||
function escape(str) {
|
function escape(str) {
|
||||||
if (str === undefined) {
|
if (str === undefined) {
|
||||||
@@ -1322,10 +1323,15 @@
|
|||||||
}
|
}
|
||||||
return expr;
|
return expr;
|
||||||
},
|
},
|
||||||
shallowEqual
|
shallowEqual,
|
||||||
|
addNameSpace(vnode) {
|
||||||
|
addNS(vnode.data, vnode.children, vnode.sel);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
function parseXML(xml) {
|
function parseXML(xml) {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
|
// we remove comments from the xml string
|
||||||
|
xml = xml.replace(/<!--[\s\S]*?-->/g, "");
|
||||||
const doc = parser.parseFromString(xml, "text/xml");
|
const doc = parser.parseFromString(xml, "text/xml");
|
||||||
if (doc.getElementsByTagName("parsererror").length) {
|
if (doc.getElementsByTagName("parsererror").length) {
|
||||||
let msg = "Invalid XML in template.";
|
let msg = "Invalid XML in template.";
|
||||||
@@ -1436,7 +1442,7 @@
|
|||||||
this._processTemplate(elem);
|
this._processTemplate(elem);
|
||||||
const template = {
|
const template = {
|
||||||
elem,
|
elem,
|
||||||
fn: (context, extra) => {
|
fn: function (context, extra) {
|
||||||
const compiledFunction = this._compile(name, elem);
|
const compiledFunction = this._compile(name, elem);
|
||||||
template.fn = compiledFunction;
|
template.fn = compiledFunction;
|
||||||
return compiledFunction.call(this, context, extra);
|
return compiledFunction.call(this, context, extra);
|
||||||
@@ -1497,8 +1503,8 @@
|
|||||||
* to render a full component tree, since this is an asynchronous operation.
|
* to render a full component tree, since this is an asynchronous operation.
|
||||||
* This method can only render templates without components.
|
* This method can only render templates without components.
|
||||||
*/
|
*/
|
||||||
renderToString(name, context = {}) {
|
renderToString(name, context = {}, extra) {
|
||||||
const vnode = this.render(name, context);
|
const vnode = this.render(name, context, extra);
|
||||||
if (vnode.sel === undefined) {
|
if (vnode.sel === undefined) {
|
||||||
return vnode.text;
|
return vnode.text;
|
||||||
}
|
}
|
||||||
@@ -1530,8 +1536,8 @@
|
|||||||
if (parentContext) {
|
if (parentContext) {
|
||||||
ctx.templates = Object.create(parentContext.templates);
|
ctx.templates = Object.create(parentContext.templates);
|
||||||
ctx.variables = Object.create(parentContext.variables);
|
ctx.variables = Object.create(parentContext.variables);
|
||||||
ctx.nextID = parentContext.parentNode + 1;
|
ctx.nextID = parentContext.nextID + 1;
|
||||||
ctx.parentNode = parentContext.parentNode;
|
ctx.parentNode = parentContext.parentNode || ctx.nextID++;
|
||||||
ctx.allowMultipleRoots = true;
|
ctx.allowMultipleRoots = true;
|
||||||
ctx.hasParentWidget = true;
|
ctx.hasParentWidget = true;
|
||||||
ctx.shouldDefineResult = false;
|
ctx.shouldDefineResult = false;
|
||||||
@@ -1539,12 +1545,12 @@
|
|||||||
for (let v in parentContext.variables) {
|
for (let v in parentContext.variables) {
|
||||||
let variable = parentContext.variables[v];
|
let variable = parentContext.variables[v];
|
||||||
if (variable.id) {
|
if (variable.id) {
|
||||||
ctx.addLine(`let ${variable.id} = extra.vars.${variable.id}`);
|
ctx.addLine(`let ${variable.id} = extra.fiber.vars.${variable.id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (parentContext) {
|
if (parentContext) {
|
||||||
ctx.addLine(" Object.assign(context, extra.scope);");
|
ctx.addLine(" Object.assign(context, extra.fiber.scope);");
|
||||||
}
|
}
|
||||||
this._compileNode(elem, ctx);
|
this._compileNode(elem, ctx);
|
||||||
if (!parentContext) {
|
if (!parentContext) {
|
||||||
@@ -1702,6 +1708,16 @@
|
|||||||
ctx = ctx.subContext("inPreTag", true);
|
ctx = ctx.subContext("inPreTag", true);
|
||||||
}
|
}
|
||||||
this._compileChildren(node, ctx);
|
this._compileChildren(node, ctx);
|
||||||
|
// svg support
|
||||||
|
// we hadd svg namespace if it is a svg or if it is a g, but only if it is
|
||||||
|
// the root node. This is the easiest way to support svg sub components:
|
||||||
|
// they need to have a g tag as root. Otherwise, we would need a complete
|
||||||
|
// list of allowed svg tags.
|
||||||
|
const shouldAddNS = node.nodeName === "svg" || (node.nodeName === "g" && ctx.rootNode === ctx.parentNode);
|
||||||
|
if (shouldAddNS) {
|
||||||
|
ctx.rootContext.shouldDefineUtils = true;
|
||||||
|
ctx.addLine(`utils.addNameSpace(vn${ctx.parentNode});`);
|
||||||
|
}
|
||||||
for (let { directive, value, fullName } of validDirectives) {
|
for (let { directive, value, fullName } of validDirectives) {
|
||||||
if (directive.finalize) {
|
if (directive.finalize) {
|
||||||
directive.finalize({ node, qweb: this, ctx, fullName, value });
|
directive.finalize({ node, qweb: this, ctx, fullName, value });
|
||||||
@@ -2125,7 +2141,7 @@
|
|||||||
.join(",");
|
.join(",");
|
||||||
varCode = `{${content}}`;
|
varCode = `{${content}}`;
|
||||||
}
|
}
|
||||||
ctx.addLine(`this.recursiveFns['${subTemplateName}'].call(this, context, Object.assign({}, extra, {parentNode: c${ctx.parentNode}, vars: ${varCode}, scope}));`);
|
ctx.addLine(`this.recursiveFns['${subTemplateName}'].call(this, context, Object.assign({}, extra, {parentNode: c${ctx.parentNode}, fiber: {vars: ${varCode}, scope}}));`);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
templateMap[subTemplate] = true;
|
templateMap[subTemplate] = true;
|
||||||
@@ -2298,9 +2314,10 @@
|
|||||||
name: "ref",
|
name: "ref",
|
||||||
priority: 95,
|
priority: 95,
|
||||||
atNodeCreation({ ctx, value, addNodeHook }) {
|
atNodeCreation({ ctx, value, addNodeHook }) {
|
||||||
|
ctx.rootContext.shouldDefineRefs = true;
|
||||||
const refKey = `ref${ctx.generateID()}`;
|
const refKey = `ref${ctx.generateID()}`;
|
||||||
ctx.addLine(`const ${refKey} = ${ctx.interpolate(value)};`);
|
ctx.addLine(`const ${refKey} = ${ctx.interpolate(value)};`);
|
||||||
addNodeHook("create", `context.refs[${refKey}] = n.elm;`);
|
addNodeHook("create", `context.__owl__.refs[${refKey}] = n.elm;`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -2445,7 +2462,7 @@
|
|||||||
const type = node.getAttribute("type");
|
const type = node.getAttribute("type");
|
||||||
let handler;
|
let handler;
|
||||||
let event = fullName.includes(".lazy") ? "change" : "input";
|
let event = fullName.includes(".lazy") ? "change" : "input";
|
||||||
const expr = ctx.formatExpression(`state.${value}`);
|
const expr = ctx.formatExpression(value);
|
||||||
if (node.tagName === "select") {
|
if (node.tagName === "select") {
|
||||||
ctx.addLine(`p${nodeID}.props = {value: ${expr}};`);
|
ctx.addLine(`p${nodeID}.props = {value: ${expr}};`);
|
||||||
addNodeHook("create", `n.elm.value=${expr};`);
|
addNodeHook("create", `n.elm.value=${expr};`);
|
||||||
@@ -2732,9 +2749,10 @@
|
|||||||
let refExpr = "";
|
let refExpr = "";
|
||||||
let refKey = "";
|
let refKey = "";
|
||||||
if (ref) {
|
if (ref) {
|
||||||
|
ctx.rootContext.shouldDefineRefs = true;
|
||||||
refKey = `ref${ctx.generateID()}`;
|
refKey = `ref${ctx.generateID()}`;
|
||||||
ctx.addLine(`const ${refKey} = ${ctx.interpolate(ref)};`);
|
ctx.addLine(`const ${refKey} = ${ctx.interpolate(ref)};`);
|
||||||
refExpr = `context.refs[${refKey}] = w${componentID};`;
|
refExpr = `context.__owl__.refs[${refKey}] = w${componentID};`;
|
||||||
}
|
}
|
||||||
let transitionsInsertCode = "";
|
let transitionsInsertCode = "";
|
||||||
if (transition) {
|
if (transition) {
|
||||||
@@ -2742,7 +2760,7 @@
|
|||||||
}
|
}
|
||||||
let finalizeComponentCode = `w${componentID}.${keepAlive ? "unmount" : "destroy"}();`;
|
let finalizeComponentCode = `w${componentID}.${keepAlive ? "unmount" : "destroy"}();`;
|
||||||
if (ref && !keepAlive) {
|
if (ref && !keepAlive) {
|
||||||
finalizeComponentCode += `delete context.refs[${refKey}];`;
|
finalizeComponentCode += `delete context.__owl__.refs[${refKey}];`;
|
||||||
}
|
}
|
||||||
if (transition) {
|
if (transition) {
|
||||||
finalizeComponentCode = `let finalize = () => {
|
finalizeComponentCode = `let finalize = () => {
|
||||||
@@ -2825,8 +2843,10 @@
|
|||||||
ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`);
|
ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`);
|
||||||
}
|
}
|
||||||
let shouldProxy = false;
|
let shouldProxy = false;
|
||||||
|
if (async || keepAlive) {
|
||||||
|
ctx.addLine(`const fiber${componentID} = Object.assign(Object.create(extra.fiber), {patchQueue: []});`);
|
||||||
|
}
|
||||||
if (async) {
|
if (async) {
|
||||||
ctx.addLine(`const patchQueue${componentID} = [];`);
|
|
||||||
ctx.addLine(`c${ctx.parentNode}.push(w${componentID} && w${componentID}.__owl__.pvnode || null);`);
|
ctx.addLine(`c${ctx.parentNode}.push(w${componentID} && w${componentID}.__owl__.pvnode || null);`);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -2849,9 +2869,9 @@
|
|||||||
else {
|
else {
|
||||||
ctx.addLine(`let props${componentID} = {${propStr}};`);
|
ctx.addLine(`let props${componentID} = {${propStr}};`);
|
||||||
}
|
}
|
||||||
ctx.addIf(`w${componentID} && w${componentID}.__owl__.renderPromise && !w${componentID}.__owl__.vnode`);
|
ctx.addIf(`w${componentID} && w${componentID}.__owl__.currentFiber && !w${componentID}.__owl__.vnode`);
|
||||||
ctx.addIf(`utils.shallowEqual(props${componentID}, w${componentID}.__owl__.renderProps)`);
|
ctx.addIf(`utils.shallowEqual(props${componentID}, w${componentID}.__owl__.currentFiber.props)`);
|
||||||
ctx.addLine(`def${defID} = w${componentID}.__owl__.renderPromise;`);
|
ctx.addLine(`def${defID} = w${componentID}.__owl__.currentFiber.promise;`);
|
||||||
ctx.addElse();
|
ctx.addElse();
|
||||||
ctx.addLine(`w${componentID}.destroy();`);
|
ctx.addLine(`w${componentID}.destroy();`);
|
||||||
ctx.addLine(`w${componentID} = false;`);
|
ctx.addLine(`w${componentID} = false;`);
|
||||||
@@ -2859,8 +2879,13 @@
|
|||||||
ctx.closeIf();
|
ctx.closeIf();
|
||||||
ctx.addIf(`!w${componentID}`);
|
ctx.addIf(`!w${componentID}`);
|
||||||
// new component
|
// new component
|
||||||
ctx.addLine(`let componentKey${componentID} = ${ctx.interpolate(value)};`);
|
let dynamicFallback = "";
|
||||||
ctx.addLine(`let W${componentID} = context.constructor.components[componentKey${componentID}] || QWeb.components[componentKey${componentID}];`);
|
if (!value.match(INTERP_REGEXP)) {
|
||||||
|
dynamicFallback = `|| ${ctx.formatExpression(value)}`;
|
||||||
|
}
|
||||||
|
const interpValue = ctx.interpolate(value);
|
||||||
|
ctx.addLine(`let componentKey${componentID} = ${interpValue};`);
|
||||||
|
ctx.addLine(`let W${componentID} = context.constructor.components[componentKey${componentID}] || QWeb.components[componentKey${componentID}]${dynamicFallback};`);
|
||||||
// maybe only do this in dev mode...
|
// maybe only do this in dev mode...
|
||||||
ctx.addLine(`if (!W${componentID}) {throw new Error('Cannot find the definition of component "' + componentKey${componentID} + '"')}`);
|
ctx.addLine(`if (!W${componentID}) {throw new Error('Cannot find the definition of component "' + componentKey${componentID} + '"')}`);
|
||||||
if (QWeb.dev) {
|
if (QWeb.dev) {
|
||||||
@@ -2901,14 +2926,16 @@
|
|||||||
QWeb.slots[`${slotId}_default`] = slotFn;
|
QWeb.slots[`${slotId}_default`] = slotFn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let scopeVars = "";
|
let scopeVars;
|
||||||
if (hasSlots) {
|
if (hasSlots) {
|
||||||
scopeVars += ctx.scopeVars.length ? `Object.assign({}, scope)` : varDefs.length ? `{}` : "";
|
let scope = ctx.scopeVars.length ? `Object.assign({}, scope)` : `{}`;
|
||||||
if (varDefs.length) {
|
let vars = varDefs.length ? `{${varDefs.join(",")}}` : "undefined";
|
||||||
scopeVars += `, {${varDefs.join(",")}}`;
|
scopeVars = `${scope}, ${vars}`;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ctx.addLine(`def${defID} = w${componentID}.__prepare(${scopeVars});`);
|
else {
|
||||||
|
scopeVars = "undefined, undefined";
|
||||||
|
}
|
||||||
|
ctx.addLine(`def${defID} = w${componentID}.__prepare(extra.fiber, ${scopeVars});`);
|
||||||
// hack: specify empty remove hook to prevent the node from being removed from the DOM
|
// hack: specify empty remove hook to prevent the node from being removed from the DOM
|
||||||
let registerCode = `c${ctx.parentNode}[_${dummyID}_index]=pvnode;`;
|
let registerCode = `c${ctx.parentNode}[_${dummyID}_index]=pvnode;`;
|
||||||
if (shouldProxy) {
|
if (shouldProxy) {
|
||||||
@@ -2917,18 +2944,18 @@
|
|||||||
ctx.addLine(`def${defID} = def${defID}.then(vnode=>{if (w${componentID}.__owl__.isDestroyed){return}${createHook}let pvnode=h(vnode.sel, {key: ${templateID}, hook: {insert(vn) {let nvn=w${componentID}.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeComponentCode}}}});${registerCode}w${componentID}.__owl__.pvnode = pvnode;});`);
|
ctx.addLine(`def${defID} = def${defID}.then(vnode=>{if (w${componentID}.__owl__.isDestroyed){return}${createHook}let pvnode=h(vnode.sel, {key: ${templateID}, hook: {insert(vn) {let nvn=w${componentID}.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeComponentCode}}}});${registerCode}w${componentID}.__owl__.pvnode = pvnode;});`);
|
||||||
ctx.addElse();
|
ctx.addElse();
|
||||||
// need to update component
|
// need to update component
|
||||||
let patchQueueCode = async ? `patchQueue${componentID}` : "extra.patchQueue";
|
let patchQueueCode = async || keepAlive ? `fiber${componentID}` : "extra.fiber";
|
||||||
if (keepAlive) {
|
if (keepAlive) {
|
||||||
// if we have t-keepalive="1", the component could be unmounted, but then
|
// if we have t-keepalive="1", the component could be unmounted, but then
|
||||||
// we __updateProps is called. This is ok, but we do not want to call
|
// we __updateProps is called. This is ok, but we do not want to call
|
||||||
// the willPatch/patched hooks of the component in this case, so we
|
// the willPatch/patched hooks of the component in this case, so we
|
||||||
// disable the patch queue
|
// disable the patch queue
|
||||||
patchQueueCode = `w${componentID}.__owl__.isMounted ? ${patchQueueCode} : []`;
|
patchQueueCode = `w${componentID}.__owl__.isMounted ? extra.fiber : fiber${componentID}`;
|
||||||
}
|
}
|
||||||
if (QWeb.dev) {
|
if (QWeb.dev) {
|
||||||
ctx.addLine(`utils.validateProps(w${componentID}.constructor, props${componentID})`);
|
ctx.addLine(`utils.validateProps(w${componentID}.constructor, props${componentID})`);
|
||||||
}
|
}
|
||||||
ctx.addLine(`def${defID} = def${defID} || w${componentID}.__updateProps(props${componentID}, extra.forceUpdate, ${patchQueueCode}${scopeVars &&
|
ctx.addLine(`def${defID} = def${defID} || w${componentID}.__updateProps(props${componentID}, ${patchQueueCode}${scopeVars &&
|
||||||
", " + scopeVars});`);
|
", " + scopeVars});`);
|
||||||
let keepAliveCode = "";
|
let keepAliveCode = "";
|
||||||
if (keepAlive) {
|
if (keepAlive) {
|
||||||
@@ -2940,7 +2967,7 @@
|
|||||||
ctx.addLine(`w${componentID}.__owl__.classObj=${classObj};`);
|
ctx.addLine(`w${componentID}.__owl__.classObj=${classObj};`);
|
||||||
}
|
}
|
||||||
if (async) {
|
if (async) {
|
||||||
ctx.addLine(`def${defID}.then(w${componentID}.__applyPatchQueue.bind(w${componentID}, patchQueue${componentID}));`);
|
ctx.addLine(`def${defID}.then(w${componentID}.__applyPatchQueue.bind(w${componentID}, fiber${componentID}));`);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ctx.addLine(`extra.promises.push(def${defID});`);
|
ctx.addLine(`extra.promises.push(def${defID});`);
|
||||||
@@ -3075,8 +3102,8 @@
|
|||||||
* the t-component directive in a template)
|
* the t-component directive in a template)
|
||||||
*/
|
*/
|
||||||
constructor(parent, props) {
|
constructor(parent, props) {
|
||||||
this.refs = {};
|
|
||||||
const defaultProps = this.constructor.defaultProps;
|
const defaultProps = this.constructor.defaultProps;
|
||||||
|
Component._current = this;
|
||||||
if (defaultProps) {
|
if (defaultProps) {
|
||||||
props = this.__applyDefaultProps(props, defaultProps);
|
props = this.__applyDefaultProps(props, defaultProps);
|
||||||
}
|
}
|
||||||
@@ -3121,14 +3148,14 @@
|
|||||||
parent: p,
|
parent: p,
|
||||||
children: {},
|
children: {},
|
||||||
cmap: {},
|
cmap: {},
|
||||||
renderId: 1,
|
currentFiber: null,
|
||||||
renderPromise: null,
|
|
||||||
renderProps: props || null,
|
|
||||||
boundHandlers: {},
|
boundHandlers: {},
|
||||||
mountedHandlers: {},
|
mountedHandlers: {},
|
||||||
|
willUnmountCB: null,
|
||||||
observer: null,
|
observer: null,
|
||||||
render: null,
|
render: null,
|
||||||
classObj: null
|
classObj: null,
|
||||||
|
refs: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -3229,8 +3256,10 @@
|
|||||||
if (__owl__.isMounted) {
|
if (__owl__.isMounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const fiber = this.__createFiber(false, undefined, undefined, undefined);
|
||||||
if (!__owl__.vnode) {
|
if (!__owl__.vnode) {
|
||||||
const vnode = await this.__prepare();
|
fiber.promise = this.__prepareAndRender(fiber);
|
||||||
|
const vnode = await fiber.promise;
|
||||||
if (__owl__.isDestroyed) {
|
if (__owl__.isDestroyed) {
|
||||||
// component was destroyed before we get here...
|
// component was destroyed before we get here...
|
||||||
return;
|
return;
|
||||||
@@ -3238,9 +3267,10 @@
|
|||||||
this.__patch(vnode);
|
this.__patch(vnode);
|
||||||
}
|
}
|
||||||
else if (renderBeforeRemount) {
|
else if (renderBeforeRemount) {
|
||||||
const patchQueue = [];
|
fiber.patchQueue.push(fiber);
|
||||||
await this.__render(false, patchQueue, undefined, undefined);
|
fiber.promise = this.__render(fiber);
|
||||||
this.__applyPatchQueue(patchQueue);
|
await fiber.promise;
|
||||||
|
this.__applyPatchQueue(fiber);
|
||||||
}
|
}
|
||||||
target.appendChild(this.el);
|
target.appendChild(this.el);
|
||||||
if (document.body.contains(target)) {
|
if (document.body.contains(target)) {
|
||||||
@@ -3271,15 +3301,34 @@
|
|||||||
if (!__owl__.isMounted) {
|
if (!__owl__.isMounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const patchQueue = [];
|
const fiber = this.__createFiber(force, undefined, undefined, undefined);
|
||||||
const renderId = ++__owl__.renderId;
|
fiber.patchQueue.push(fiber);
|
||||||
await this.__render(force, patchQueue, undefined, undefined);
|
fiber.promise = this.__render(fiber);
|
||||||
if (__owl__.isMounted && renderId === __owl__.renderId) {
|
await fiber.promise;
|
||||||
|
if (__owl__.isMounted && fiber === __owl__.currentFiber) {
|
||||||
// we only update the vnode and the actual DOM if no other rendering
|
// we only update the vnode and the actual DOM if no other rendering
|
||||||
// occurred between now and when the render method was initially called.
|
// occurred between now and when the render method was initially called.
|
||||||
this.__applyPatchQueue(patchQueue);
|
this.__applyPatchQueue(fiber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
__createFiber(force, scope, vars, parent) {
|
||||||
|
const fiber = {
|
||||||
|
force,
|
||||||
|
scope,
|
||||||
|
vars,
|
||||||
|
rootFiber: null,
|
||||||
|
isCancelled: false,
|
||||||
|
component: this,
|
||||||
|
vnode: null,
|
||||||
|
patchQueue: parent ? parent.patchQueue : [],
|
||||||
|
willPatchResult: null,
|
||||||
|
props: this.props,
|
||||||
|
promise: null
|
||||||
|
};
|
||||||
|
fiber.rootFiber = parent ? parent.rootFiber : fiber;
|
||||||
|
this.__owl__.currentFiber = fiber;
|
||||||
|
return fiber;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Destroy the component. This operation is quite complex:
|
* Destroy the component. This operation is quite complex:
|
||||||
* - it recursively destroy all children
|
* - it recursively destroy all children
|
||||||
@@ -3386,19 +3435,22 @@
|
|||||||
}
|
}
|
||||||
__owl__.isMounted = true;
|
__owl__.isMounted = true;
|
||||||
const handlers = __owl__.mountedHandlers;
|
const handlers = __owl__.mountedHandlers;
|
||||||
for (let key in handlers) {
|
|
||||||
handlers[key]();
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
this.mounted();
|
this.mounted();
|
||||||
|
for (let key in handlers) {
|
||||||
|
handlers[key]();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
errorHandler(e, this);
|
errorHandler(e, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
__callWillUnmount() {
|
__callWillUnmount() {
|
||||||
this.willUnmount();
|
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
|
if (__owl__.willUnmountCB) {
|
||||||
|
__owl__.willUnmountCB();
|
||||||
|
}
|
||||||
|
this.willUnmount();
|
||||||
__owl__.isMounted = false;
|
__owl__.isMounted = false;
|
||||||
const children = __owl__.children;
|
const children = __owl__.children;
|
||||||
for (let id in children) {
|
for (let id in children) {
|
||||||
@@ -3412,8 +3464,8 @@
|
|||||||
* The __updateProps method is called by the t-component directive whenever
|
* The __updateProps method is called by the t-component directive whenever
|
||||||
* it updates a component (so, when the parent template is rerendered).
|
* it updates a component (so, when the parent template is rerendered).
|
||||||
*/
|
*/
|
||||||
async __updateProps(nextProps, forceUpdate = false, patchQueue, scope, vars) {
|
async __updateProps(nextProps, parentFiber, scope, vars) {
|
||||||
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
|
const shouldUpdate = parentFiber.force || this.shouldUpdate(nextProps);
|
||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
const defaultProps = this.constructor.defaultProps;
|
const defaultProps = this.constructor.defaultProps;
|
||||||
if (defaultProps) {
|
if (defaultProps) {
|
||||||
@@ -3421,7 +3473,9 @@
|
|||||||
}
|
}
|
||||||
await this.willUpdateProps(nextProps);
|
await this.willUpdateProps(nextProps);
|
||||||
this.props = nextProps;
|
this.props = nextProps;
|
||||||
await this.__render(forceUpdate, patchQueue, scope, vars);
|
const fiber = this.__createFiber(parentFiber.force, scope, vars, parentFiber);
|
||||||
|
fiber.patchQueue.push(fiber);
|
||||||
|
await this.__render(fiber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -3433,13 +3487,17 @@
|
|||||||
const target = __owl__.vnode || document.createElement(vnode.sel);
|
const target = __owl__.vnode || document.createElement(vnode.sel);
|
||||||
__owl__.vnode = patch(target, vnode);
|
__owl__.vnode = patch(target, vnode);
|
||||||
}
|
}
|
||||||
__prepare(scope, vars) {
|
/**
|
||||||
const __owl__ = this.__owl__;
|
* The __prepare method is only called by the t-component directive, when a
|
||||||
__owl__.renderProps = this.props;
|
* subcomponent is created. It gets its scope and vars, if any, from the
|
||||||
__owl__.renderPromise = this.__prepareAndRender(scope, vars);
|
* parent template.
|
||||||
return __owl__.renderPromise;
|
*/
|
||||||
|
__prepare(parentFiber, scope, vars) {
|
||||||
|
const fiber = this.__createFiber(parentFiber.force, scope, vars, parentFiber);
|
||||||
|
fiber.promise = this.__prepareAndRender(fiber);
|
||||||
|
return fiber.promise;
|
||||||
}
|
}
|
||||||
async __prepareAndRender(scope, vars) {
|
async __prepareAndRender(fiber) {
|
||||||
try {
|
try {
|
||||||
await this.willStart();
|
await this.willStart();
|
||||||
}
|
}
|
||||||
@@ -3475,14 +3533,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
__owl__.render = qweb.render.bind(qweb, p._template);
|
__owl__.render = qweb.render.bind(qweb, p._template);
|
||||||
this.__observeState();
|
return this.__render(fiber);
|
||||||
return this.__render(false, [], scope, vars);
|
|
||||||
}
|
}
|
||||||
__render(force = false, patchQueue = [], scope, vars) {
|
__render(fiber) {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const patch = [this];
|
|
||||||
patchQueue.push(patch);
|
|
||||||
if (__owl__.observer) {
|
if (__owl__.observer) {
|
||||||
__owl__.observer.allowMutations = false;
|
__owl__.observer.allowMutations = false;
|
||||||
}
|
}
|
||||||
@@ -3492,17 +3547,14 @@
|
|||||||
promises,
|
promises,
|
||||||
handlers: __owl__.boundHandlers,
|
handlers: __owl__.boundHandlers,
|
||||||
mountedHandlers: __owl__.mountedHandlers,
|
mountedHandlers: __owl__.mountedHandlers,
|
||||||
forceUpdate: force,
|
fiber: fiber
|
||||||
patchQueue,
|
|
||||||
scope,
|
|
||||||
vars
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
vnode = __owl__.vnode || h("div");
|
vnode = __owl__.vnode || h("div");
|
||||||
errorHandler(e, this);
|
errorHandler(e, this);
|
||||||
}
|
}
|
||||||
patch.push(vnode);
|
fiber.vnode = vnode;
|
||||||
if (__owl__.observer) {
|
if (__owl__.observer) {
|
||||||
__owl__.observer.allowMutations = true;
|
__owl__.observer.allowMutations = true;
|
||||||
}
|
}
|
||||||
@@ -3544,18 +3596,6 @@
|
|||||||
this.mounted();
|
this.mounted();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Enable the observe feature on the state. We only create an observer if
|
|
||||||
* there is some state to be observed.
|
|
||||||
*/
|
|
||||||
__observeState() {
|
|
||||||
if (this.state) {
|
|
||||||
const __owl__ = this.__owl__;
|
|
||||||
__owl__.observer = new Observer();
|
|
||||||
this.state = __owl__.observer.observe(this.state);
|
|
||||||
__owl__.observer.notifyCB = this.render.bind(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Apply default props (only top level).
|
* Apply default props (only top level).
|
||||||
*
|
*
|
||||||
@@ -3572,29 +3612,30 @@
|
|||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Apply the given patch queue. A patch is a pair [c, vn], where c is a
|
* Apply the given patch queue from a fiber.
|
||||||
* Component instance and vn a VNode.
|
|
||||||
* 1) Call 'willPatch' on the component of each patch
|
* 1) Call 'willPatch' on the component of each patch
|
||||||
* 2) Call '__patch' on the component of each patch
|
* 2) Call '__patch' on the component of each patch
|
||||||
* 3) Call 'patched' on the component of each patch, in inverse order
|
* 3) Call 'patched' on the component of each patch, in reverse order
|
||||||
*/
|
*/
|
||||||
__applyPatchQueue(patchQueue) {
|
__applyPatchQueue(fiber) {
|
||||||
|
const patchQueue = fiber.patchQueue;
|
||||||
let component = this;
|
let component = this;
|
||||||
try {
|
try {
|
||||||
const patchLen = patchQueue.length;
|
const patchLen = patchQueue.length;
|
||||||
for (let i = 0; i < patchLen; i++) {
|
for (let i = 0; i < patchLen; i++) {
|
||||||
const patch = patchQueue[i];
|
const fiber = patchQueue[i];
|
||||||
component = patch[0];
|
component = fiber.component;
|
||||||
patch.push(patch[0].willPatch());
|
fiber.willPatchResult = component.willPatch();
|
||||||
}
|
}
|
||||||
for (let i = 0; i < patchLen; i++) {
|
for (let i = 0; i < patchLen; i++) {
|
||||||
const patch = patchQueue[i];
|
const fiber = patchQueue[i];
|
||||||
patch[0].__patch(patch[1]);
|
component = fiber.component;
|
||||||
|
component.__patch(fiber.vnode);
|
||||||
}
|
}
|
||||||
for (let i = patchLen - 1; i >= 0; i--) {
|
for (let i = patchLen - 1; i >= 0; i--) {
|
||||||
const patch = patchQueue[i];
|
const fiber = patchQueue[i];
|
||||||
component = patch[0];
|
component = fiber.component;
|
||||||
patch[0].patched(patch[2]);
|
component.patched(fiber.willPatchResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
@@ -3604,6 +3645,7 @@
|
|||||||
}
|
}
|
||||||
Component.template = null;
|
Component.template = null;
|
||||||
Component._template = null;
|
Component._template = null;
|
||||||
|
Component._current = null;
|
||||||
Component.components = {};
|
Component.components = {};
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Error handling
|
// Error handling
|
||||||
@@ -3675,7 +3717,7 @@
|
|||||||
/**
|
/**
|
||||||
* Need to do this here so 'deep' can be overrided by subcomponent easily
|
* Need to do this here so 'deep' can be overrided by subcomponent easily
|
||||||
*/
|
*/
|
||||||
async __prepareAndRender(scope, vars) {
|
async __prepareAndRender(fiber) {
|
||||||
const store = this.getStore(this.env);
|
const store = this.getStore(this.env);
|
||||||
const ownProps = this.props || {};
|
const ownProps = this.props || {};
|
||||||
this.storeProps = this.constructor.mapStoreToProps(store.state, ownProps, store.getters);
|
this.storeProps = this.constructor.mapStoreToProps(store.state, ownProps, store.getters);
|
||||||
@@ -3688,7 +3730,7 @@
|
|||||||
prevStoreProps: this.storeProps
|
prevStoreProps: this.storeProps
|
||||||
});
|
});
|
||||||
this.__owl__.rev = observer.rev;
|
this.__owl__.rev = observer.rev;
|
||||||
return super.__prepareAndRender(scope, vars);
|
return super.__prepareAndRender(fiber);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* We do not use the mounted hook here for a subtle reason: we want the
|
* We do not use the mounted hook here for a subtle reason: we want the
|
||||||
@@ -3730,9 +3772,9 @@
|
|||||||
this.__owl__.renderPromise = super.render(force);
|
this.__owl__.renderPromise = super.render(force);
|
||||||
return this.__owl__.renderPromise;
|
return this.__owl__.renderPromise;
|
||||||
}
|
}
|
||||||
async __updateProps(nextProps, f, p, s, v) {
|
async __updateProps(nextProps, f, s, v) {
|
||||||
this.__updateStoreProps(nextProps);
|
this.__updateStoreProps(nextProps);
|
||||||
return super.__updateProps(nextProps, f, p, s, v);
|
return super.__updateProps(nextProps, f, s, v);
|
||||||
}
|
}
|
||||||
__updateStoreProps(nextProps) {
|
__updateStoreProps(nextProps) {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
@@ -3840,9 +3882,10 @@
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
function xml(strings) {
|
function xml(strings, ...args) {
|
||||||
const name = `__template__${QWeb.nextId++}`;
|
const name = `__template__${QWeb.nextId++}`;
|
||||||
QWeb.registerTemplate(name, strings[0]);
|
const value = String.raw(strings, ...args);
|
||||||
|
QWeb.registerTemplate(name, value);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3850,6 +3893,79 @@
|
|||||||
xml: xml
|
xml: xml
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Owl Hook System
|
||||||
|
*
|
||||||
|
* This file introduces the concept of hooks, similar to React or Vue hooks.
|
||||||
|
* We have currently an implementation of:
|
||||||
|
* - useState (reactive state)
|
||||||
|
* - onMounted
|
||||||
|
* - onWillUnmount
|
||||||
|
* - useRef
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* useState hook
|
||||||
|
*
|
||||||
|
* This is the main way a component can be made reactive. The useState hook
|
||||||
|
* will return an observed object (or array). Changes to that value will then
|
||||||
|
* trigger a rerendering of the current component.
|
||||||
|
*/
|
||||||
|
function useState(state) {
|
||||||
|
const component = Component._current;
|
||||||
|
const __owl__ = component.__owl__;
|
||||||
|
if (!__owl__.observer) {
|
||||||
|
__owl__.observer = new Observer();
|
||||||
|
__owl__.observer.notifyCB = component.render.bind(component);
|
||||||
|
}
|
||||||
|
return __owl__.observer.observe(state);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Mounted hook. The callback will be called when the current component is
|
||||||
|
* mounted. Note that the component mounted method is called first.
|
||||||
|
*/
|
||||||
|
let nextID = 1;
|
||||||
|
function onMounted(cb) {
|
||||||
|
const component = Component._current;
|
||||||
|
component.__owl__.mountedHandlers[`h${nextID++}`] = cb;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* willUnmount hook. The callback will be called when the current component is
|
||||||
|
* willUnmounted. Note that the component mounted method is called last.
|
||||||
|
*/
|
||||||
|
function onWillUnmount(cb) {
|
||||||
|
const component = Component._current;
|
||||||
|
if (component.__owl__.willUnmountCB) {
|
||||||
|
const current = component.__owl__.willUnmountCB;
|
||||||
|
component.__owl__.willUnmountCB = function () {
|
||||||
|
cb.call(component);
|
||||||
|
current.call(component);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
component.__owl__.willUnmountCB = cb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function useRef(name) {
|
||||||
|
const __owl__ = Component._current.__owl__;
|
||||||
|
return {
|
||||||
|
get el() {
|
||||||
|
const val = __owl__.refs && __owl__.refs[name];
|
||||||
|
return val instanceof HTMLElement ? val : null;
|
||||||
|
},
|
||||||
|
get comp() {
|
||||||
|
const val = __owl__.refs && __owl__.refs[name];
|
||||||
|
return val instanceof Component ? val : null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var _hooks = /*#__PURE__*/Object.freeze({
|
||||||
|
useState: useState,
|
||||||
|
onMounted: onMounted,
|
||||||
|
onWillUnmount: onWillUnmount,
|
||||||
|
useRef: useRef
|
||||||
|
});
|
||||||
|
|
||||||
class Link extends Component {
|
class Link extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
@@ -3893,26 +4009,17 @@
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
class RouteComponent extends Component {
|
class RouteComponent extends Component {
|
||||||
constructor(parent, props) {
|
get routeComponent() {
|
||||||
super(parent, props);
|
return this.env.router.currentRoute && this.env.router.currentRoute.component;
|
||||||
this.routes = [];
|
|
||||||
const router = this.env.router;
|
|
||||||
for (let name of router.routeIds) {
|
|
||||||
const route = router.routes[name];
|
|
||||||
if (route.component) {
|
|
||||||
this.routes.push({
|
|
||||||
name: route.name,
|
|
||||||
component: "__component__" + route.name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RouteComponent.template = xml `
|
RouteComponent.template = xml `
|
||||||
<t t-foreach="routes" t-as="route">
|
<t>
|
||||||
<t t-if="env.router.currentRouteName === route.name">
|
<t
|
||||||
<t t-component="{{route.component}}" t-props="env.router.currentParams"/>
|
t-if="routeComponent"
|
||||||
</t>
|
t-component="routeComponent"
|
||||||
|
t-key="env.router.currentRouteName"
|
||||||
|
t-props="env.router.currentParams" />
|
||||||
</t>
|
</t>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -4125,11 +4232,13 @@
|
|||||||
*
|
*
|
||||||
* Note that dynamic values, such as a date or a commit hash are added by rollup
|
* Note that dynamic values, such as a date or a commit hash are added by rollup
|
||||||
*/
|
*/
|
||||||
|
const useState$1 = useState;
|
||||||
const core = { EventBus, Observer };
|
const core = { EventBus, Observer };
|
||||||
const router = { Router, RouteComponent, Link };
|
const router = { Router, RouteComponent, Link };
|
||||||
const store = { Store, ConnectedComponent };
|
const store = { Store, ConnectedComponent };
|
||||||
const utils = _utils;
|
const utils = _utils;
|
||||||
const tags = _tags;
|
const tags = _tags;
|
||||||
|
const hooks$1 = _hooks;
|
||||||
const __info__ = {};
|
const __info__ = {};
|
||||||
Object.defineProperty(__info__, "mode", {
|
Object.defineProperty(__info__, "mode", {
|
||||||
get() {
|
get() {
|
||||||
@@ -4151,14 +4260,16 @@
|
|||||||
exports.QWeb = QWeb;
|
exports.QWeb = QWeb;
|
||||||
exports.__info__ = __info__;
|
exports.__info__ = __info__;
|
||||||
exports.core = core;
|
exports.core = core;
|
||||||
|
exports.hooks = hooks$1;
|
||||||
exports.router = router;
|
exports.router = router;
|
||||||
exports.store = store;
|
exports.store = store;
|
||||||
exports.tags = tags;
|
exports.tags = tags;
|
||||||
|
exports.useState = useState$1;
|
||||||
exports.utils = utils;
|
exports.utils = utils;
|
||||||
|
|
||||||
exports.__info__.version = '0.21.0';
|
exports.__info__.version = '0.22.0';
|
||||||
exports.__info__.date = '2019-09-12T12:21:59.533Z';
|
exports.__info__.date = '2019-10-01T19:05:23.486Z';
|
||||||
exports.__info__.hash = '14ceb38';
|
exports.__info__.hash = 'b859fcb';
|
||||||
exports.__info__.url = 'https://github.com/odoo/owl';
|
exports.__info__.url = 'https://github.com/odoo/owl';
|
||||||
|
|
||||||
}(this.owl = this.owl || {}));
|
}(this.owl = this.owl || {}));
|
||||||
|
|||||||
+11
-10
@@ -1,5 +1,5 @@
|
|||||||
import { SAMPLES } from "./samples.js";
|
import { SAMPLES } from "./samples.js";
|
||||||
|
const {useState, useRef} = owl.hooks;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Constants, helpers, utils
|
// Constants, helpers, utils
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -156,18 +156,18 @@ Promise.all([loadTemplates(), owl.utils.whenReady()]).then(start);
|
|||||||
class TabbedEditor extends owl.Component {
|
class TabbedEditor extends owl.Component {
|
||||||
constructor(parent, props) {
|
constructor(parent, props) {
|
||||||
super(parent, props);
|
super(parent, props);
|
||||||
this.state = {
|
this.state = useState({
|
||||||
currentTab: props.js ? "js" : props.xml ? "xml" : "css"
|
currentTab: props.js ? "js" : props.xml ? "xml" : "css"
|
||||||
};
|
});
|
||||||
this.setTab = owl.utils.debounce(this.setTab, 250, true);
|
this.setTab = owl.utils.debounce(this.setTab, 250, true);
|
||||||
|
|
||||||
this.sessions = {};
|
this.sessions = {};
|
||||||
this._setupSessions(props);
|
this._setupSessions(props);
|
||||||
this.editor = null;
|
this.editorNode = useRef("editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.editor = this.editor || ace.edit(this.refs.editor);
|
this.editor = this.editor || ace.edit(this.editorNode.el);
|
||||||
|
|
||||||
this.editor.setValue(this.props[this.state.currentTab], -1);
|
this.editor.setValue(this.props[this.state.currentTab], -1);
|
||||||
this.editor.setFontSize("12px");
|
this.editor.setFontSize("12px");
|
||||||
@@ -250,7 +250,7 @@ class App extends owl.Component {
|
|||||||
this.version = owl.__info__.version;
|
this.version = owl.__info__.version;
|
||||||
this.SAMPLES = SAMPLES;
|
this.SAMPLES = SAMPLES;
|
||||||
|
|
||||||
this.state = {
|
this.state = useState({
|
||||||
js: SAMPLES[0].code,
|
js: SAMPLES[0].code,
|
||||||
css: SAMPLES[0].css || "",
|
css: SAMPLES[0].css || "",
|
||||||
xml: SAMPLES[0].xml || DEFAULT_XML,
|
xml: SAMPLES[0].xml || DEFAULT_XML,
|
||||||
@@ -259,18 +259,19 @@ class App extends owl.Component {
|
|||||||
splitLayout: true,
|
splitLayout: true,
|
||||||
leftPaneWidth: Math.ceil(window.innerWidth / 2),
|
leftPaneWidth: Math.ceil(window.innerWidth / 2),
|
||||||
topPanelHeight: null
|
topPanelHeight: null
|
||||||
};
|
});
|
||||||
|
|
||||||
this.toggleLayout = owl.utils.debounce(this.toggleLayout, 250, true);
|
this.toggleLayout = owl.utils.debounce(this.toggleLayout, 250, true);
|
||||||
this.runCode = owl.utils.debounce(this.runCode, 250, true);
|
this.runCode = owl.utils.debounce(this.runCode, 250, true);
|
||||||
this.downloadCode = owl.utils.debounce(this.downloadCode, 250, true);
|
this.downloadCode = owl.utils.debounce(this.downloadCode, 250, true);
|
||||||
|
this.content = useRef("content");
|
||||||
}
|
}
|
||||||
|
|
||||||
displayError(error) {
|
displayError(error) {
|
||||||
this.state.error = error;
|
this.state.error = error;
|
||||||
if (error) {
|
if (error) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.refs.content.innerHTML = "";
|
this.content.el.innerHTML = "";
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -296,8 +297,8 @@ class App extends owl.Component {
|
|||||||
} else {
|
} else {
|
||||||
this.state.error = false;
|
this.state.error = false;
|
||||||
}
|
}
|
||||||
this.refs.content.innerHTML = "";
|
this.content.el.innerHTML = "";
|
||||||
this.refs.content.appendChild(subiframe);
|
this.content.el.appendChild(subiframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSample(ev) {
|
setSample(ev) {
|
||||||
|
|||||||
+137
-52
@@ -1,7 +1,8 @@
|
|||||||
const COMPONENTS = `// In this example, we show how components can be defined and created.
|
const COMPONENTS = `// In this example, we show how components can be defined and created.
|
||||||
|
const { Component, useState } = owl;
|
||||||
|
|
||||||
class Greeter extends owl.Component {
|
class Greeter extends Component {
|
||||||
state = { word: 'Hello' };
|
state = useState({ word: 'Hello' });
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
this.state.word = this.state.word === 'Hi' ? 'Hello' : 'Hi'
|
this.state.word = this.state.word === 'Hi' ? 'Hello' : 'Hi'
|
||||||
@@ -9,9 +10,9 @@ class Greeter extends owl.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Main root component
|
// Main root component
|
||||||
class App extends owl.Component {
|
class App extends Component {
|
||||||
static components = { Greeter };
|
static components = { Greeter };
|
||||||
state = { name: 'World'};
|
state = useState({ name: 'World'});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application setup
|
// Application setup
|
||||||
@@ -45,17 +46,18 @@ const COMPONENTS_CSS = `.greeter {
|
|||||||
|
|
||||||
const ANIMATION = `// The goal of this component is to see how the t-transition directive can be
|
const ANIMATION = `// The goal of this component is to see how the t-transition directive can be
|
||||||
// used to generate simple transition effects.
|
// used to generate simple transition effects.
|
||||||
|
const { Component, useState } = owl;
|
||||||
|
|
||||||
class Counter extends owl.Component {
|
class Counter extends Component {
|
||||||
state = { value: 0 };
|
state = useState({ value: 0 });
|
||||||
|
|
||||||
increment() {
|
increment() {
|
||||||
this.state.value++;
|
this.state.value++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class App extends owl.Component {
|
class App extends Component {
|
||||||
state = { flag: false, componentFlag: false, numbers: [] };
|
state = useState({ flag: false, componentFlag: false, numbers: [] });
|
||||||
static components = { Counter };
|
static components = { Counter };
|
||||||
|
|
||||||
toggle(key) {
|
toggle(key) {
|
||||||
@@ -183,11 +185,12 @@ const LIFECYCLE_DEMO = `// This example shows all the possible lifecycle hooks
|
|||||||
// methods in the console. Try modifying its state by clicking on it, or by
|
// methods in the console. Try modifying its state by clicking on it, or by
|
||||||
// clicking on the two main buttons, and look into the console to see what
|
// clicking on the two main buttons, and look into the console to see what
|
||||||
// happens.
|
// happens.
|
||||||
|
const { Component, useState } = owl;
|
||||||
|
|
||||||
class DemoComponent extends owl.Component {
|
class DemoComponent extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
this.state = { n: 0 };
|
this.state = useState({ n: 0 });
|
||||||
console.log("constructor");
|
console.log("constructor");
|
||||||
}
|
}
|
||||||
async willStart() {
|
async willStart() {
|
||||||
@@ -213,9 +216,9 @@ class DemoComponent extends owl.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class App extends owl.Component {
|
class App extends Component {
|
||||||
static components = { DemoComponent };
|
static components = { DemoComponent };
|
||||||
state = { n: 0, flag: true };
|
state = useState({ n: 0, flag: true });
|
||||||
|
|
||||||
increment() {
|
increment() {
|
||||||
this.state.n++;
|
this.state.n++;
|
||||||
@@ -259,12 +262,70 @@ const LIFECYCLE_CSS = `button {
|
|||||||
width: 250px;
|
width: 250px;
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
const HOOKS_DEMO = `// In this example, we show how hooks can be used or defined.
|
||||||
|
const {useState, onMounted, onWillUnmount} = owl.hooks;
|
||||||
|
|
||||||
|
// We define here a custom behaviour: this hook tracks the state of the mouse
|
||||||
|
// position
|
||||||
|
function useMouse() {
|
||||||
|
const position = useState({x:0, y: 0});
|
||||||
|
|
||||||
|
function update(e) {
|
||||||
|
position.x = e.clientX;
|
||||||
|
position.y = e.clientY;
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('mousemove', update);
|
||||||
|
});
|
||||||
|
onWillUnmount(() => {
|
||||||
|
window.removeEventListener('mousemove', update);
|
||||||
|
});
|
||||||
|
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main root component
|
||||||
|
class App extends owl.Component {
|
||||||
|
// simple state hook (reactive object)
|
||||||
|
counter = useState({ value: 0 });
|
||||||
|
|
||||||
|
// this hooks is bound to the 'mouse' property.
|
||||||
|
mouse = useMouse();
|
||||||
|
|
||||||
|
increment() {
|
||||||
|
this.counter.value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application setup
|
||||||
|
const qweb = new owl.QWeb(TEMPLATES);
|
||||||
|
const app = new App({ qweb });
|
||||||
|
app.mount(document.body);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const HOOKS_DEMO_XML = `<templates>
|
||||||
|
<div t-name="App">
|
||||||
|
<button t-on-click="increment">Click! <t t-esc="counter.value"/></button>
|
||||||
|
<div>Mouse: <t t-esc="mouse.x"/>, <t t-esc="mouse.y"/></div>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const HOOKS_CSS = `button {
|
||||||
|
width: 120px;
|
||||||
|
height: 35px;
|
||||||
|
font-size: 16px;
|
||||||
|
}`;
|
||||||
|
|
||||||
const TODO_APP_STORE = `// This example is an implementation of the TodoList application, from the
|
const TODO_APP_STORE = `// This example is an implementation of the TodoList application, from the
|
||||||
// www.todomvc.com project. This is a non trivial application with some
|
// www.todomvc.com project. This is a non trivial application with some
|
||||||
// interesting user interactions. It uses the local storage for persistence.
|
// interesting user interactions. It uses the local storage for persistence.
|
||||||
//
|
//
|
||||||
// In this implementation, we use the owl Store class to manage the state. It
|
// In this implementation, we use the owl Store class to manage the state. It
|
||||||
// is very similar to the VueX store.
|
// is very similar to the VueX store.
|
||||||
|
const { Component, useState } = owl;
|
||||||
|
const { useRef } = owl.hooks;
|
||||||
|
|
||||||
const ENTER_KEY = 13;
|
const ENTER_KEY = 13;
|
||||||
const ESC_KEY = 27;
|
const ESC_KEY = 27;
|
||||||
@@ -335,8 +396,9 @@ function makeStore() {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// TodoItem
|
// TodoItem
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
class TodoItem extends owl.Component {
|
class TodoItem extends Component {
|
||||||
state = { isEditing: false };
|
state = useState({ isEditing: false });
|
||||||
|
inputRef = useRef("input");
|
||||||
|
|
||||||
removeTodo() {
|
removeTodo() {
|
||||||
this.env.store.dispatch("removeTodo", this.props.id);
|
this.env.store.dispatch("removeTodo", this.props.id);
|
||||||
@@ -351,9 +413,9 @@ class TodoItem extends owl.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
focusInput() {
|
focusInput() {
|
||||||
this.refs.input.value = "";
|
this.inputRef.el.value = "";
|
||||||
this.refs.input.focus();
|
this.inputRef.el.focus();
|
||||||
this.refs.input.value = this.props.title;
|
this.inputRef.el.value = this.props.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyup(ev) {
|
handleKeyup(ev) {
|
||||||
@@ -389,7 +451,7 @@ class TodoItem extends owl.Component {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
class TodoApp extends owl.store.ConnectedComponent {
|
class TodoApp extends owl.store.ConnectedComponent {
|
||||||
static components = { TodoItem };
|
static components = { TodoItem };
|
||||||
state = { filter: "all" };
|
state = useState({ filter: "all" });
|
||||||
|
|
||||||
static mapStoreToProps(state) {
|
static mapStoreToProps(state) {
|
||||||
return {
|
return {
|
||||||
@@ -1052,17 +1114,18 @@ const SLOTS = `// We show here how slots can be used to create generic component
|
|||||||
//
|
//
|
||||||
// Note that the t-on-click event, defined in the App template, is executed in
|
// Note that the t-on-click event, defined in the App template, is executed in
|
||||||
// the context of the App component, even though it is inside the Card component
|
// the context of the App component, even though it is inside the Card component
|
||||||
|
const { Component, useState } = owl;
|
||||||
|
|
||||||
class Card extends owl.Component {
|
class Card extends Component {
|
||||||
state = { showContent: true };
|
state = useState({ showContent: true });
|
||||||
|
|
||||||
toggleDisplay() {
|
toggleDisplay() {
|
||||||
this.state.showContent = !this.state.showContent;
|
this.state.showContent = !this.state.showContent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Counter extends owl.Component {
|
class Counter extends Component {
|
||||||
state = {val: 1};
|
state = useState({val: 1});
|
||||||
|
|
||||||
inc() {
|
inc() {
|
||||||
this.state.val++;
|
this.state.val++;
|
||||||
@@ -1070,9 +1133,9 @@ class Counter extends owl.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Main root component
|
// Main root component
|
||||||
class App extends owl.Component {
|
class App extends Component {
|
||||||
static components = {Card, Counter};
|
static components = {Card, Counter};
|
||||||
state = {a: 1, b: 3};
|
state = useState({a: 1, b: 3});
|
||||||
|
|
||||||
inc(key, delta) {
|
inc(key, delta) {
|
||||||
this.state[key] += delta;
|
this.state[key] += delta;
|
||||||
@@ -1168,8 +1231,9 @@ const ASYNC_COMPONENTS = `// This example will not work if your browser does not
|
|||||||
// However, we don't want renderings of the other sub component to be delayed
|
// However, we don't want renderings of the other sub component to be delayed
|
||||||
// because of the slow component. We use the 't-asyncroot' directive for this
|
// because of the slow component. We use the 't-asyncroot' directive for this
|
||||||
// purpose. Try removing it to see the difference.
|
// purpose. Try removing it to see the difference.
|
||||||
|
const { Component, useState } = owl;
|
||||||
|
|
||||||
class SlowComponent extends owl.Component {
|
class SlowComponent extends Component {
|
||||||
willUpdateProps() {
|
willUpdateProps() {
|
||||||
// simulate a component that needs to perform async stuff (e.g. an RPC)
|
// simulate a component that needs to perform async stuff (e.g. an RPC)
|
||||||
// with the updated props before re-rendering itself
|
// with the updated props before re-rendering itself
|
||||||
@@ -1177,11 +1241,11 @@ class SlowComponent extends owl.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotificationList extends owl.Component {}
|
class NotificationList extends Component {}
|
||||||
|
|
||||||
class App extends owl.Component {
|
class App extends Component {
|
||||||
static components = {SlowComponent, NotificationList};
|
static components = {SlowComponent, NotificationList};
|
||||||
state = { value: 0, notifs: [] };
|
state = useState({ value: 0, notifs: [] });
|
||||||
|
|
||||||
increment() {
|
increment() {
|
||||||
this.state.value++;
|
this.state.value++;
|
||||||
@@ -1250,15 +1314,16 @@ const FORM = `// This example illustrate how the t-model directive can be used t
|
|||||||
// data between html inputs (and select/textareas) and the state of a component.
|
// data between html inputs (and select/textareas) and the state of a component.
|
||||||
// Note that there are two controls with t-model="color": they are totally
|
// Note that there are two controls with t-model="color": they are totally
|
||||||
// synchronized.
|
// synchronized.
|
||||||
|
const { Component, useState } = owl;
|
||||||
|
|
||||||
class Form extends owl.Component {
|
class Form extends Component {
|
||||||
state = {
|
state = useState({
|
||||||
text: "",
|
text: "",
|
||||||
othertext: "",
|
othertext: "",
|
||||||
number: 11,
|
number: 11,
|
||||||
color: "",
|
color: "",
|
||||||
bool: false
|
bool: false
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application setup
|
// Application setup
|
||||||
@@ -1271,20 +1336,20 @@ const FORM_XML = `<templates>
|
|||||||
<div t-name="Form">
|
<div t-name="Form">
|
||||||
<h1>Form</h1>
|
<h1>Form</h1>
|
||||||
<div>
|
<div>
|
||||||
Text (immediate): <input t-model="text"/>
|
Text (immediate): <input t-model="state.text"/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Other text (lazy): <input t-model.lazy="othertext"/>
|
Other text (lazy): <input t-model.lazy="state.othertext"/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Number: <input t-model.number="number"/>
|
Number: <input t-model.number="state.number"/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Boolean: <input type="checkbox" t-model="bool"/>
|
Boolean: <input type="checkbox" t-model="state.bool"/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Color, with a select:
|
Color, with a select:
|
||||||
<select t-model="color">
|
<select t-model="state.color">
|
||||||
<option value="">Select a color</option>
|
<option value="">Select a color</option>
|
||||||
<option value="red">Red</option>
|
<option value="red">Red</option>
|
||||||
<option value="blue">Blue</option>
|
<option value="blue">Blue</option>
|
||||||
@@ -1292,8 +1357,8 @@ const FORM_XML = `<templates>
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Color, with radio buttons:
|
Color, with radio buttons:
|
||||||
<span><input type="radio" name="color" id="red" value="red" t-model="color"/><label for="red">Red</label></span>
|
<span><input type="radio" name="color" id="red" value="red" t-model="state.color"/><label for="red">Red</label></span>
|
||||||
<span><input type="radio" name="color" id="blue" value="blue" t-model="color"/><label for="blue">Blue</label></span>
|
<span><input type="radio" name="color" id="blue" value="blue" t-model="state.color"/><label for="blue">Blue</label></span>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<h1>State</h1>
|
<h1>State</h1>
|
||||||
@@ -1316,26 +1381,31 @@ const WMS = `// This example is slightly more complex than usual. We demonstrate
|
|||||||
// - minimal width/height
|
// - minimal width/height
|
||||||
// - better heuristic for initial window position
|
// - better heuristic for initial window position
|
||||||
// - ...
|
// - ...
|
||||||
|
const { Component, useState } = owl;
|
||||||
|
const { useRef } = owl.hooks;
|
||||||
|
|
||||||
class HelloWorld extends owl.Component {}
|
class HelloWorld extends Component {}
|
||||||
|
|
||||||
class Counter extends owl.Component {
|
class Counter extends Component {
|
||||||
state = { value: 0 };
|
state = useState({ value: 0 });
|
||||||
|
|
||||||
inc() {
|
inc() {
|
||||||
this.state.value++;
|
this.state.value++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Window extends owl.Component {
|
class Window extends Component {
|
||||||
|
|
||||||
get style() {
|
get style() {
|
||||||
let { width, height, top, left, zindex } = this.props.info;
|
let { width, height, top, left, zindex } = this.props.info;
|
||||||
|
|
||||||
return \`width: \${width}px;height: \${height}px;top:\${top}px;left:\${left}px;z-index:\${zindex}\`;
|
return \`width: \${width}px;height: \${height}px;top:\${top}px;left:\${left}px;z-index:\${zindex}\`;
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.trigger("close-window", { id: this.props.info.id });
|
this.trigger("close-window", { id: this.props.info.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
startDragAndDrop(ev) {
|
startDragAndDrop(ev) {
|
||||||
this.updateZIndex();
|
this.updateZIndex();
|
||||||
this.el.classList.add('dragging');
|
this.el.classList.add('dragging');
|
||||||
@@ -1361,31 +1431,37 @@ class Window extends owl.Component {
|
|||||||
self.trigger("set-window-position", options);
|
self.trigger("set-window-position", options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateZIndex() {
|
updateZIndex() {
|
||||||
this.trigger("update-z-index", { id: this.props.info.id });
|
this.trigger("update-z-index", { id: this.props.info.id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WindowManager extends owl.Component {
|
class WindowManager extends Component {
|
||||||
static components = { Window };
|
static components = { Window };
|
||||||
windows = [];
|
windows = [];
|
||||||
nextId = 1;
|
nextId = 1;
|
||||||
currentZindex = 1;
|
currentZindex = 1;
|
||||||
|
nextLeft = 0;
|
||||||
|
nextTop = 0;
|
||||||
|
|
||||||
addWindow(name) {
|
addWindow(name) {
|
||||||
const info = this.env.windows.find(w => w.name === name);
|
const info = this.env.windows.find(w => w.name === name);
|
||||||
const id = \`w\${this.nextId++}\`;
|
this.nextLeft = this.nextLeft + 30;
|
||||||
|
this.nextTop = this.nextTop + 30;
|
||||||
this.windows.push({
|
this.windows.push({
|
||||||
id: id,
|
id: this.nextId++,
|
||||||
title: info.title,
|
title: info.title,
|
||||||
width: info.defaultWidth,
|
width: info.defaultWidth,
|
||||||
height: info.defaultHeight,
|
height: info.defaultHeight,
|
||||||
top: 0,
|
top: this.nextTop,
|
||||||
left: 0,
|
left: this.nextLeft,
|
||||||
zindex: this.currentZindex++
|
zindex: this.currentZindex++,
|
||||||
|
component: info.component
|
||||||
});
|
});
|
||||||
this.constructor.components[id] = info.component;
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
closeWindow(ev) {
|
closeWindow(ev) {
|
||||||
const id = ev.detail.id;
|
const id = ev.detail.id;
|
||||||
delete this.constructor.components[id];
|
delete this.constructor.components[id];
|
||||||
@@ -1393,12 +1469,14 @@ class WindowManager extends owl.Component {
|
|||||||
this.windows.splice(index, 1);
|
this.windows.splice(index, 1);
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
setWindowPosition(ev) {
|
setWindowPosition(ev) {
|
||||||
const id = ev.detail.id;
|
const id = ev.detail.id;
|
||||||
const w = this.windows.find(w => w.id === id);
|
const w = this.windows.find(w => w.id === id);
|
||||||
w.top = ev.detail.top;
|
w.top = ev.detail.top;
|
||||||
w.left = ev.detail.left;
|
w.left = ev.detail.left;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateZIndex(ev) {
|
updateZIndex(ev) {
|
||||||
const id = ev.detail.id;
|
const id = ev.detail.id;
|
||||||
const w = this.windows.find(w => w.id === id);
|
const w = this.windows.find(w => w.id === id);
|
||||||
@@ -1407,11 +1485,12 @@ class WindowManager extends owl.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class App extends owl.Component {
|
class App extends Component {
|
||||||
static components = { WindowManager };
|
static components = { WindowManager };
|
||||||
|
wmRef = useRef("wm");
|
||||||
|
|
||||||
addWindow(name) {
|
addWindow(name) {
|
||||||
this.refs.wm.addWindow(name);
|
this.wmRef.comp.addWindow(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1452,7 +1531,7 @@ const WMS_XML = `<templates>
|
|||||||
t-on-update-z-index="updateZIndex"
|
t-on-update-z-index="updateZIndex"
|
||||||
t-on-set-window-position="setWindowPosition">
|
t-on-set-window-position="setWindowPosition">
|
||||||
<Window t-foreach="windows" t-as="w" t-key="w.id" info="w">
|
<Window t-foreach="windows" t-as="w" t-key="w.id" info="w">
|
||||||
<t t-component="{{w.id}}"/>
|
<t t-component="w.component"/>
|
||||||
</Window>
|
</Window>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1570,6 +1649,12 @@ export const SAMPLES = [
|
|||||||
xml: LIFECYCLE_DEMO_XML,
|
xml: LIFECYCLE_DEMO_XML,
|
||||||
css: LIFECYCLE_CSS
|
css: LIFECYCLE_CSS
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Hooks",
|
||||||
|
code: HOOKS_DEMO,
|
||||||
|
xml: HOOKS_DEMO_XML,
|
||||||
|
css: HOOKS_CSS
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Todo List App (with store)",
|
description: "Todo List App (with store)",
|
||||||
code: TODO_APP_STORE,
|
code: TODO_APP_STORE,
|
||||||
|
|||||||
Reference in New Issue
Block a user