mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] owl: update to v1.0.0alpha5
This commit is contained in:
@@ -379,7 +379,6 @@
|
||||
//------------------------------------------------------------------------------
|
||||
class CompilationContext {
|
||||
constructor(name) {
|
||||
this.nextID = 1;
|
||||
this.code = [];
|
||||
this.variables = {};
|
||||
this.escaping = false;
|
||||
@@ -393,7 +392,6 @@
|
||||
this.shouldDefineUtils = false;
|
||||
this.shouldDefineRefs = false;
|
||||
this.shouldDefineResult = true;
|
||||
this.shouldDefineSibling = true;
|
||||
this.shouldProtectContext = false;
|
||||
this.shouldTrackScope = false;
|
||||
this.loopNumber = 0;
|
||||
@@ -402,16 +400,16 @@
|
||||
this.hasParentWidget = false;
|
||||
this.scopeVars = [];
|
||||
this.currentKey = "";
|
||||
this.lastNodeKey = ""; // temp variable to communicate to previous caller
|
||||
this.templates = {};
|
||||
this.callingLevel = 0;
|
||||
this.inliningLevel = 0;
|
||||
this.rootContext = this;
|
||||
this.templateName = name || "noname";
|
||||
this.templates[this.templateName] = true;
|
||||
this.addLine("var h = this.h;");
|
||||
}
|
||||
generateID() {
|
||||
const id = this.rootContext.nextID++;
|
||||
return id;
|
||||
return CompilationContext.nextID++;
|
||||
}
|
||||
/**
|
||||
* This method generates a "template key", which is basically a unique key
|
||||
@@ -427,8 +425,8 @@
|
||||
for (let i = 0; i < this.loopNumber - 1; i++) {
|
||||
locationExpr += `\${i${i + 1}}__`;
|
||||
}
|
||||
if (this.lastNodeKey || this.currentKey) {
|
||||
const k = this.lastNodeKey || this.currentKey;
|
||||
if (this.currentKey) {
|
||||
const k = this.currentKey;
|
||||
this.addLine(`let k${id} = ${locationExpr}\` + ${k};`);
|
||||
}
|
||||
else {
|
||||
@@ -454,9 +452,6 @@
|
||||
if (this.shouldDefineResult) {
|
||||
this.code.unshift(" let result;");
|
||||
}
|
||||
if (this.shouldDefineSibling) {
|
||||
this.code.unshift(" let sibling = null;");
|
||||
}
|
||||
if (this.shouldDefineRefs) {
|
||||
this.code.unshift(" context.__owl__.refs = context.__owl__.refs || {};");
|
||||
}
|
||||
@@ -498,6 +493,10 @@
|
||||
subContext(key, value) {
|
||||
const newContext = Object.create(this);
|
||||
newContext[key] = value;
|
||||
if (key === "caller") {
|
||||
newContext.callingLevel++;
|
||||
newContext.inliningLevel++;
|
||||
}
|
||||
return newContext;
|
||||
}
|
||||
indent() {
|
||||
@@ -528,6 +527,27 @@
|
||||
this.dedent();
|
||||
this.addLine("}");
|
||||
}
|
||||
/**
|
||||
* Recursively (inverse) fetches the `caller` of a context
|
||||
* Useful to determine to which t-call a t-raw="0" refers
|
||||
*/
|
||||
getCaller(targetLevel) {
|
||||
if (targetLevel === undefined) {
|
||||
targetLevel = this.inliningLevel;
|
||||
}
|
||||
if (targetLevel === this.callingLevel) {
|
||||
return this.caller || null;
|
||||
}
|
||||
const proto = this.__proto__;
|
||||
return proto ? proto.getCaller(targetLevel) : null;
|
||||
}
|
||||
/**
|
||||
* Marks the context with the current recursive level
|
||||
* in which we are for inlining archs (t-raw="0")
|
||||
*/
|
||||
getInliningContext() {
|
||||
return this.subContext("inliningLevel", this.inliningLevel - 1);
|
||||
}
|
||||
getValue(val) {
|
||||
return val in this.variables ? this.getValue(this.variables[val]) : val;
|
||||
}
|
||||
@@ -557,6 +577,7 @@
|
||||
return "`" + r + "`";
|
||||
}
|
||||
}
|
||||
CompilationContext.nextID = 1;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// module/props.ts
|
||||
@@ -853,10 +874,11 @@
|
||||
vnode.elm = api.createComment(vnode.text);
|
||||
}
|
||||
else if (sel !== undefined) {
|
||||
const elm = (vnode.elm =
|
||||
isDef(data) && isDef((i = data.ns))
|
||||
? api.createElementNS(i, sel)
|
||||
: api.createElement(sel));
|
||||
const elm = vnode.elm ||
|
||||
(vnode.elm =
|
||||
isDef(data) && isDef((i = data.ns))
|
||||
? api.createElementNS(i, sel)
|
||||
: api.createElement(sel));
|
||||
for (i = 0, iLen = cbs.create.length; i < iLen; ++i)
|
||||
cbs.create[i](emptyNode, vnode);
|
||||
if (array(children)) {
|
||||
@@ -1548,8 +1570,7 @@
|
||||
if (parentContext) {
|
||||
ctx.templates = Object.create(parentContext.templates);
|
||||
ctx.variables = Object.create(parentContext.variables);
|
||||
ctx.nextID = parentContext.nextID + 1;
|
||||
ctx.parentNode = parentContext.parentNode || ctx.nextID++;
|
||||
ctx.parentNode = parentContext.parentNode || ctx.generateID();
|
||||
ctx.allowMultipleRoots = true;
|
||||
ctx.hasParentWidget = true;
|
||||
ctx.shouldDefineResult = false;
|
||||
@@ -1638,13 +1659,16 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ctx !== ctx.rootContext) {
|
||||
ctx = ctx.subContext("currentKey", ctx.currentKey);
|
||||
}
|
||||
const firstLetter = node.tagName[0];
|
||||
if (firstLetter === firstLetter.toUpperCase()) {
|
||||
// this is a component, we modify in place the xml document to change
|
||||
// <SomeComponent ... /> to <t t-component="SomeComponent" ... />
|
||||
node.setAttribute("t-component", node.tagName);
|
||||
}
|
||||
else if (node.tagName !== 't' && node.hasAttribute('t-component')) {
|
||||
else if (node.tagName !== "t" && node.hasAttribute("t-component")) {
|
||||
throw new Error(`Directive 't-component' can only be used on <t> nodes (used on a <${node.tagName}>)`);
|
||||
}
|
||||
const attributes = node.attributes;
|
||||
@@ -1714,7 +1738,6 @@
|
||||
if (node.nodeName !== "t") {
|
||||
let nodeID = this._compileGenericNode(node, ctx, withHandlers);
|
||||
ctx = ctx.withParent(nodeID);
|
||||
ctx = ctx.subContext("currentKey", ctx.lastNodeKey);
|
||||
let nodeHooks = {};
|
||||
let addNodeHook = function (hook, handler) {
|
||||
nodeHooks[hook] = nodeHooks[hook] || [];
|
||||
@@ -1884,7 +1907,7 @@
|
||||
}
|
||||
}
|
||||
let nodeID = ctx.generateID();
|
||||
let nodeKey = ctx.lastNodeKey || nodeID;
|
||||
let nodeKey = ctx.currentKey || nodeID;
|
||||
const parts = [`key:${nodeKey}`];
|
||||
if (attrs.length + tattrs.length > 0) {
|
||||
parts.push(`attrs:{${attrs.join(",")}}`);
|
||||
@@ -1989,9 +2012,12 @@
|
||||
};
|
||||
QWeb.utils.htmlToVDOM = htmlToVDOM;
|
||||
function compileValueNode(value, node, qweb, ctx) {
|
||||
if (value === "0" && ctx.caller) {
|
||||
qweb._compileNode(ctx.caller, ctx);
|
||||
return;
|
||||
if (value === "0") {
|
||||
const caller = ctx.getCaller();
|
||||
if (caller) {
|
||||
qweb._compileNode(caller, ctx.getInliningContext());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (value.xml instanceof NodeList && !value.id) {
|
||||
for (let node of Array.from(value.xml)) {
|
||||
@@ -2148,11 +2174,9 @@
|
||||
nodeCopy.removeAttribute("t-call");
|
||||
// extract variables from nodecopy
|
||||
const tempCtx = new CompilationContext();
|
||||
tempCtx.nextID = ctx.rootContext.nextID;
|
||||
tempCtx.allowMultipleRoots = true;
|
||||
qweb._compileNode(nodeCopy, tempCtx);
|
||||
const vars = Object.assign({}, ctx.variables, tempCtx.variables);
|
||||
ctx.rootContext.nextID = tempCtx.nextID;
|
||||
const templateMap = Object.create(ctx.templates);
|
||||
// open new scope, if necessary
|
||||
const hasNewVariables = Object.keys(tempCtx.variables).length > 0;
|
||||
@@ -2378,6 +2402,8 @@
|
||||
}
|
||||
elm.classList.add(name + "-enter");
|
||||
elm.classList.add(name + "-enter-active");
|
||||
elm.classList.remove(name + "-leave-active");
|
||||
elm.classList.remove(name + "-leave-to");
|
||||
const finalize = () => {
|
||||
elm.classList.remove(name + "-enter-active");
|
||||
elm.classList.remove(name + "-enter-to");
|
||||
@@ -2394,6 +2420,9 @@
|
||||
elm.classList.add(name + "-leave");
|
||||
elm.classList.add(name + "-leave-active");
|
||||
const finalize = () => {
|
||||
if (!elm.classList.contains(name + "-leave-active")) {
|
||||
return;
|
||||
}
|
||||
elm.classList.remove(name + "-leave-active");
|
||||
elm.classList.remove(name + "-leave-to");
|
||||
rm();
|
||||
@@ -2462,11 +2491,11 @@
|
||||
if (!ctx.parentNode) {
|
||||
ctx.rootContext.shouldDefineResult = true;
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
parentNode = `children${ctx.nextID++}`;
|
||||
parentNode = `children${ctx.generateID()}`;
|
||||
ctx.addLine(`let ${parentNode}= []`);
|
||||
ctx.addLine(`result = {}`);
|
||||
}
|
||||
ctx.addLine(`slot${slotKey}.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: ${parentNode}, vars: extra.vars, parent: owner}));`);
|
||||
ctx.addLine(`slot${slotKey}.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: ${parentNode}, vars: extra.vars, parent: extra.parent || owner}));`);
|
||||
if (!ctx.parentNode) {
|
||||
ctx.addLine(`utils.defineProxy(result, ${parentNode}[0]);`);
|
||||
}
|
||||
@@ -2536,7 +2565,7 @@
|
||||
atNodeEncounter({ ctx, value }) {
|
||||
let id = ctx.generateID();
|
||||
ctx.addLine(`const nodeKey${id} = ${ctx.formatExpression(value)};`);
|
||||
ctx.lastNodeKey = `nodeKey${id}`;
|
||||
ctx.currentKey = `nodeKey${id}`;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2801,10 +2830,6 @@
|
||||
ctx.addLine(`const ${refKey} = ${ctx.interpolate(ref)};`);
|
||||
refExpr = `context.__owl__.refs[${refKey}] = w${componentID};`;
|
||||
}
|
||||
let transitionsInsertCode = "";
|
||||
if (transition) {
|
||||
transitionsInsertCode = `utils.transitionInsert(vn, '${transition}');`;
|
||||
}
|
||||
let finalizeComponentCode = `w${componentID}.destroy();`;
|
||||
if (ref) {
|
||||
finalizeComponentCode += `delete context.__owl__.refs[${refKey}];`;
|
||||
@@ -2813,6 +2838,7 @@
|
||||
finalizeComponentCode = `let finalize = () => {
|
||||
${finalizeComponentCode}
|
||||
};
|
||||
delete w${componentID}.__owl__.transitionInserted;
|
||||
utils.transitionRemove(vn, '${transition}', finalize);`;
|
||||
}
|
||||
let createHook = "";
|
||||
@@ -2935,7 +2961,7 @@
|
||||
styleCode = `.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};w${componentID}.el.style=${tattStyle};});`;
|
||||
}
|
||||
ctx.addLine(`w${componentID}.__updateProps(props${componentID}, extra.fiber${scopeVars &&
|
||||
", " + scopeVars}, sibling)${styleCode};`);
|
||||
", " + scopeVars})${styleCode};`);
|
||||
ctx.addLine(`let pvnode = w${componentID}.__owl__.pvnode;`);
|
||||
if (registerCode) {
|
||||
ctx.addLine(registerCode);
|
||||
@@ -2955,6 +2981,10 @@
|
||||
// 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(`w${componentID} = new W${componentID}(parent, props${componentID});`);
|
||||
if (transition) {
|
||||
ctx.addLine(`const __patch${componentID} = w${componentID}.__patch;`);
|
||||
ctx.addLine(`w${componentID}.__patch = fiber => {__patch${componentID}.call(w${componentID}, fiber); if(!w${componentID}.__owl__.transitionInserted){w${componentID}.__owl__.transitionInserted = true;utils.transitionInsert(w${componentID}.__owl__.vnode, '${transition}');}};`);
|
||||
}
|
||||
ctx.addLine(`parent.__owl__.cmap[${templateKey}] = w${componentID}.__owl__.id;`);
|
||||
if (hasSlots) {
|
||||
const clone = node.cloneNode(true);
|
||||
@@ -2980,9 +3010,10 @@
|
||||
QWeb.slots[`${slotId}_default`] = slotFn;
|
||||
}
|
||||
}
|
||||
ctx.addLine(`let def${defID} = w${componentID}.__prepare(extra.fiber, ${scopeVars}, sibling);`);
|
||||
ctx.addLine(`let def${defID} = w${componentID}.__prepare(extra.fiber, ${scopeVars});`);
|
||||
// hack: specify empty remove hook to prevent the node from being removed from the DOM
|
||||
ctx.addLine(`let pvnode = h('dummy', {key: ${templateKey}, hook: {insert(vn) { let nvn=w${componentID}.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeComponentCode}}}});`);
|
||||
const insertHook = refExpr ? `insert(vn) {${refExpr}},` : "";
|
||||
ctx.addLine(`let pvnode = h('dummy', {key: ${templateKey}, hook: {${insertHook}remove() {},destroy(vn) {${finalizeComponentCode}}}});`);
|
||||
ctx.addLine(`const fiber = w${componentID}.__owl__.currentFiber;`);
|
||||
ctx.addLine(`def${defID}.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; ${createHook}});`);
|
||||
if (registerCode) {
|
||||
@@ -2997,7 +3028,6 @@
|
||||
ctx.addLine(`w${componentID}.__owl__.classObj=${classObj};`);
|
||||
}
|
||||
ctx.addLine(`w${componentID}.__owl__.parentLastFiberId = extra.fiber.id;`);
|
||||
ctx.addLine(`sibling = w${componentID}.__owl__.currentFiber || sibling;`);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -3009,6 +3039,9 @@
|
||||
this.requestAnimationFrame = requestAnimationFrame;
|
||||
}
|
||||
addFiber(fiber) {
|
||||
// if the fiber was remapped into a larger rendering fiber, it may not be a
|
||||
// root fiber. But we only want to register root fibers
|
||||
fiber = fiber.root;
|
||||
return new Promise((resolve, reject) => {
|
||||
if (fiber.error) {
|
||||
return reject(fiber.error);
|
||||
@@ -3084,7 +3117,7 @@
|
||||
* states and in general determine the state of the rendering.
|
||||
*/
|
||||
class Fiber {
|
||||
constructor(parent, component, scope, vars, force, target) {
|
||||
constructor(parent, component, force, target) {
|
||||
this.id = Fiber.nextId++;
|
||||
// isCompleted means that the rendering corresponding to this fiber's work is
|
||||
// done, either because the component has been mounted or patched, or because
|
||||
@@ -3108,15 +3141,17 @@
|
||||
this.vnode = null;
|
||||
this.child = null;
|
||||
this.sibling = null;
|
||||
this.lastChild = null;
|
||||
this.parent = null;
|
||||
this.force = force;
|
||||
this.scope = scope;
|
||||
this.vars = vars;
|
||||
this.component = component;
|
||||
this.force = force;
|
||||
this.target = target;
|
||||
const __owl__ = component.__owl__;
|
||||
this.scope = __owl__.scope;
|
||||
this.vars = __owl__.vars;
|
||||
this.root = parent ? parent.root : this;
|
||||
this.parent = parent;
|
||||
let oldFiber = component.__owl__.currentFiber;
|
||||
let oldFiber = __owl__.currentFiber;
|
||||
if (oldFiber && !oldFiber.isCompleted) {
|
||||
if (oldFiber.root === oldFiber && !parent) {
|
||||
// both oldFiber and this fiber are root fibers
|
||||
@@ -3128,7 +3163,7 @@
|
||||
}
|
||||
}
|
||||
this.root.counter++;
|
||||
component.__owl__.currentFiber = this;
|
||||
__owl__.currentFiber = this;
|
||||
}
|
||||
/**
|
||||
* When the oldFiber is not completed yet, and both oldFiber and this fiber
|
||||
@@ -3146,6 +3181,7 @@
|
||||
// remove relation to children
|
||||
oldFiber.child.parent = null;
|
||||
oldFiber.child = null;
|
||||
oldFiber.lastChild = null;
|
||||
}
|
||||
oldFiber.counter = 1; // re-initialize counter
|
||||
oldFiber.id = Fiber.nextId++;
|
||||
@@ -3158,6 +3194,7 @@
|
||||
*/
|
||||
_remapFiber(oldFiber) {
|
||||
oldFiber.cancel();
|
||||
this.shouldPatch = oldFiber.shouldPatch;
|
||||
if (oldFiber === oldFiber.root) {
|
||||
oldFiber.counter++;
|
||||
}
|
||||
@@ -3166,6 +3203,9 @@
|
||||
this.parent = oldFiber.parent;
|
||||
this.root = this.parent.root;
|
||||
this.sibling = oldFiber.sibling;
|
||||
if (this.parent.lastChild === oldFiber) {
|
||||
this.parent.lastChild = this;
|
||||
}
|
||||
if (this.parent.child === oldFiber) {
|
||||
this.parent.child = this;
|
||||
}
|
||||
@@ -3212,54 +3252,58 @@
|
||||
* are ready, and the scheduler decides to process it.
|
||||
*/
|
||||
complete() {
|
||||
const component = this.component;
|
||||
if (this.target) {
|
||||
component.__patch(this.vnode);
|
||||
this.target.appendChild(component.el);
|
||||
if (document.body.contains(this.target)) {
|
||||
component.__callMounted();
|
||||
}
|
||||
}
|
||||
else if (component.__owl__.isMounted && this === this.root) {
|
||||
this.patchComponents();
|
||||
}
|
||||
let component = this.component;
|
||||
this.isCompleted = true;
|
||||
}
|
||||
/**
|
||||
* Compute and apply the patch queue of the fiber.
|
||||
* 1) Call 'willPatch' 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 reverse order
|
||||
*/
|
||||
patchComponents() {
|
||||
if (!this.target && !component.__owl__.isMounted) {
|
||||
return;
|
||||
}
|
||||
// build patchQueue
|
||||
const patchQueue = [];
|
||||
const doWork = function (f) {
|
||||
if (f.shouldPatch) {
|
||||
patchQueue.push(f);
|
||||
return f.child;
|
||||
}
|
||||
patchQueue.push(f);
|
||||
return f.child;
|
||||
};
|
||||
this._walk(doWork);
|
||||
let component = this.component;
|
||||
const patchLen = patchQueue.length;
|
||||
// call willPatch hook on each fiber of patchQueue
|
||||
for (let i = 0; i < patchLen; i++) {
|
||||
component = patchQueue[i].component;
|
||||
if (component.__owl__.willPatchCB) {
|
||||
component.__owl__.willPatchCB();
|
||||
const fiber = patchQueue[i];
|
||||
if (fiber.shouldPatch) {
|
||||
component = fiber.component;
|
||||
if (component.__owl__.willPatchCB) {
|
||||
component.__owl__.willPatchCB();
|
||||
}
|
||||
component.willPatch();
|
||||
}
|
||||
component.willPatch();
|
||||
}
|
||||
for (let i = 0; i < patchLen; i++) {
|
||||
// call __patch on each fiber of (reversed) patchQueue
|
||||
for (let i = patchLen - 1; i >= 0; i--) {
|
||||
const fiber = patchQueue[i];
|
||||
component = fiber.component;
|
||||
component.__patch(fiber.vnode);
|
||||
if (!fiber.shouldPatch && (!fiber.target || i !== 0)) {
|
||||
component.__owl__.pvnode.elm = component.__owl__.vnode.elm;
|
||||
}
|
||||
component.__owl__.currentFiber = null;
|
||||
}
|
||||
// insert into the DOM (mount case)
|
||||
let inDOM = false;
|
||||
if (this.target) {
|
||||
this.target.appendChild(this.component.el);
|
||||
inDOM = document.body.contains(this.target);
|
||||
}
|
||||
// call patched/mounted hook on each fiber of (reversed) patchQueue
|
||||
for (let i = patchLen - 1; i >= 0; i--) {
|
||||
component = patchQueue[i].component;
|
||||
component.patched();
|
||||
if (component.__owl__.patchedCB) {
|
||||
component.__owl__.patchedCB();
|
||||
const fiber = patchQueue[i];
|
||||
component = fiber.component;
|
||||
if (fiber.shouldPatch && !this.target) {
|
||||
component.patched();
|
||||
if (component.__owl__.patchedCB) {
|
||||
component.__owl__.patchedCB();
|
||||
}
|
||||
}
|
||||
else if (this.target ? inDOM : true) {
|
||||
component.__callMounted();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3358,7 +3402,7 @@
|
||||
throw e;
|
||||
}
|
||||
if (!isValid) {
|
||||
throw new Error(`Props '${propName}' of invalid type in component '${Widget.name}'`);
|
||||
throw new Error(`Invalid Prop '${propName}' in component '${Widget.name}'`);
|
||||
}
|
||||
}
|
||||
for (let propName in props) {
|
||||
@@ -3399,7 +3443,10 @@
|
||||
if (propDef.optional && prop === undefined) {
|
||||
return true;
|
||||
}
|
||||
let result = isValidProp(prop, propDef.type);
|
||||
let result = propDef.type ? isValidProp(prop, propDef.type) : true;
|
||||
if (propDef.validate) {
|
||||
result = result && propDef.validate(prop);
|
||||
}
|
||||
if (propDef.type === Array && propDef.element) {
|
||||
for (let i = 0, iLen = prop.length; i < iLen; i++) {
|
||||
result = result && isValidProp(prop[i], propDef.element);
|
||||
@@ -3501,7 +3548,9 @@
|
||||
observer: null,
|
||||
renderFn: qweb.render.bind(qweb, template),
|
||||
classObj: null,
|
||||
refs: null
|
||||
refs: null,
|
||||
scope: null,
|
||||
vars: null
|
||||
};
|
||||
}
|
||||
/**
|
||||
@@ -3593,12 +3642,13 @@
|
||||
if (__owl__.isMounted) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (!(target instanceof HTMLElement)) {
|
||||
if (!(target instanceof HTMLElement || target instanceof DocumentFragment)) {
|
||||
let message = `Component '${this.constructor.name}' cannot be mounted: the target is not a valid DOM node.`;
|
||||
message += `\nMaybe the DOM is not ready yet? (in that case, you can use owl.utils.whenReady)`;
|
||||
throw new Error(message);
|
||||
}
|
||||
const fiber = new Fiber(null, this, undefined, undefined, false, target);
|
||||
const fiber = new Fiber(null, this, false, target);
|
||||
fiber.shouldPatch = false;
|
||||
if (!__owl__.vnode) {
|
||||
this.__prepareAndRender(fiber);
|
||||
}
|
||||
@@ -3641,7 +3691,7 @@
|
||||
// currentFiber that is already rendered (isRendered is true), so we are
|
||||
// about to be mounted
|
||||
const isMounted = __owl__.isMounted;
|
||||
const fiber = new Fiber(null, this, undefined, undefined, force, null);
|
||||
const fiber = new Fiber(null, this, force, null);
|
||||
Promise.resolve().then(() => {
|
||||
if (__owl__.isMounted || !isMounted) {
|
||||
// we are mounted (__owl__.isMounted), or if we are currently being
|
||||
@@ -3742,13 +3792,6 @@
|
||||
}
|
||||
__callMounted() {
|
||||
const __owl__ = this.__owl__;
|
||||
const children = __owl__.children;
|
||||
for (let id in children) {
|
||||
const comp = children[id];
|
||||
if (!comp.__owl__.isMounted && this.el.contains(comp.el)) {
|
||||
comp.__callMounted();
|
||||
}
|
||||
}
|
||||
__owl__.isMounted = true;
|
||||
__owl__.currentFiber = null;
|
||||
this.mounted();
|
||||
@@ -3763,6 +3806,10 @@
|
||||
}
|
||||
this.willUnmount();
|
||||
__owl__.isMounted = false;
|
||||
if (this.__owl__.currentFiber) {
|
||||
this.__owl__.currentFiber.isCompleted = true;
|
||||
this.__owl__.currentFiber.root.counter = 0;
|
||||
}
|
||||
const children = __owl__.children;
|
||||
for (let id in children) {
|
||||
const comp = children[id];
|
||||
@@ -3775,17 +3822,20 @@
|
||||
* The __updateProps method is called by the t-component directive whenever
|
||||
* it updates a component (so, when the parent template is rerendered).
|
||||
*/
|
||||
async __updateProps(nextProps, parentFiber, scope, vars, previousSibling) {
|
||||
async __updateProps(nextProps, parentFiber, scope, vars) {
|
||||
this.__owl__.scope = scope;
|
||||
this.__owl__.vars = vars;
|
||||
const shouldUpdate = parentFiber.force || this.shouldUpdate(nextProps);
|
||||
if (shouldUpdate) {
|
||||
const __owl__ = this.__owl__;
|
||||
const fiber = new Fiber(parentFiber, this, scope, vars, parentFiber.force, null);
|
||||
const fiber = new Fiber(parentFiber, this, parentFiber.force, null);
|
||||
if (!parentFiber.child) {
|
||||
parentFiber.child = fiber;
|
||||
}
|
||||
else {
|
||||
previousSibling.sibling = fiber;
|
||||
parentFiber.lastChild.sibling = fiber;
|
||||
}
|
||||
parentFiber.lastChild = fiber;
|
||||
const defaultProps = this.constructor.defaultProps;
|
||||
if (defaultProps) {
|
||||
this.__applyDefaultProps(nextProps, defaultProps);
|
||||
@@ -3818,15 +3868,18 @@
|
||||
* subcomponent is created. It gets its scope and vars, if any, from the
|
||||
* parent template.
|
||||
*/
|
||||
__prepare(parentFiber, scope, vars, previousSibling) {
|
||||
const fiber = new Fiber(parentFiber, this, scope, vars, parentFiber.force, null);
|
||||
__prepare(parentFiber, scope, vars) {
|
||||
this.__owl__.scope = scope;
|
||||
this.__owl__.vars = vars;
|
||||
const fiber = new Fiber(parentFiber, this, parentFiber.force, null);
|
||||
fiber.shouldPatch = false;
|
||||
if (!parentFiber.child) {
|
||||
parentFiber.child = fiber;
|
||||
}
|
||||
else {
|
||||
previousSibling.sibling = fiber;
|
||||
parentFiber.lastChild.sibling = fiber;
|
||||
}
|
||||
parentFiber.lastChild = fiber;
|
||||
return this.__prepareAndRender(fiber);
|
||||
}
|
||||
__getTemplate(qweb) {
|
||||
@@ -3908,25 +3961,6 @@
|
||||
fiber.handleError(error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Only called by qweb t-component directive
|
||||
*/
|
||||
__mount(fiber, elm) {
|
||||
if (fiber !== this.__owl__.currentFiber) {
|
||||
fiber = this.__owl__.currentFiber; // TODO: check if we can remove fiber arg
|
||||
}
|
||||
const vnode = fiber.vnode;
|
||||
const __owl__ = this.__owl__;
|
||||
if (__owl__.classObj) {
|
||||
vnode.data.class = Object.assign(vnode.data.class || {}, __owl__.classObj);
|
||||
}
|
||||
__owl__.vnode = patch(elm, vnode);
|
||||
__owl__.currentFiber = null;
|
||||
if (__owl__.parent.__owl__.isMounted && !__owl__.isMounted) {
|
||||
this.__callMounted();
|
||||
}
|
||||
return __owl__.vnode;
|
||||
}
|
||||
/**
|
||||
* Only called by qweb t-component directive (when t-keepalive is set)
|
||||
*/
|
||||
@@ -4086,7 +4120,7 @@
|
||||
}
|
||||
});
|
||||
const __destroy = component.__destroy;
|
||||
component.__destroy = (parent) => {
|
||||
component.__destroy = parent => {
|
||||
ctx.off("update", component);
|
||||
delete mapping[id];
|
||||
__destroy.call(component, parent);
|
||||
@@ -4254,32 +4288,37 @@
|
||||
const isStrictEqual = (a, b) => a === b;
|
||||
function useStore(selector, options = {}) {
|
||||
const component = Component.current;
|
||||
const componentId = component.__owl__.id;
|
||||
const store = options.store || component.env.store;
|
||||
if (!(store instanceof Store)) {
|
||||
throw new Error(`No store found when connecting '${component.constructor.name}'`);
|
||||
}
|
||||
let result = selector(store.state, component.props);
|
||||
const hashFn = store.observer.revNumber.bind(store.observer);
|
||||
let revNumber = hashFn(result) || result;
|
||||
let revNumber = hashFn(result);
|
||||
const isEqual = options.isEqual || isStrictEqual;
|
||||
if (!store.updateFunctions[component.__owl__.id]) {
|
||||
store.updateFunctions[component.__owl__.id] = [];
|
||||
if (!store.updateFunctions[componentId]) {
|
||||
store.updateFunctions[componentId] = [];
|
||||
}
|
||||
const updateFunctions = store.updateFunctions[component.__owl__.id];
|
||||
updateFunctions.push(function () {
|
||||
function selectCompareUpdate(state, props) {
|
||||
const oldResult = result;
|
||||
result = selector(store.state, component.props);
|
||||
result = selector(state, props);
|
||||
const newRevNumber = hashFn(result);
|
||||
if ((newRevNumber > 0 && revNumber !== newRevNumber) ||
|
||||
(newRevNumber === 0 && !isEqual(oldResult, result))) {
|
||||
if ((newRevNumber > 0 && revNumber !== newRevNumber) || !isEqual(oldResult, result)) {
|
||||
revNumber = newRevNumber;
|
||||
if (options.onUpdate) {
|
||||
options.onUpdate(result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
store.updateFunctions[componentId].push(function () {
|
||||
return selectCompareUpdate(store.state, component.props);
|
||||
});
|
||||
useContextWithCB(store, component, function () {
|
||||
let shouldRender = false;
|
||||
for (let fn of updateFunctions) {
|
||||
for (let fn of store.updateFunctions[componentId]) {
|
||||
shouldRender = fn() || shouldRender;
|
||||
}
|
||||
if (shouldRender) {
|
||||
@@ -4287,16 +4326,22 @@
|
||||
}
|
||||
});
|
||||
onWillUpdateProps(props => {
|
||||
delete store.updateFunctions[component.__owl__.id];
|
||||
result = selector(store.state, props);
|
||||
selectCompareUpdate(store.state, props);
|
||||
});
|
||||
const __destroy = component.__destroy;
|
||||
component.__destroy = parent => {
|
||||
delete store.updateFunctions[componentId];
|
||||
__destroy.call(component, parent);
|
||||
};
|
||||
if (typeof result !== "object") {
|
||||
return result;
|
||||
}
|
||||
return new Proxy(result, {
|
||||
get(target, k) {
|
||||
return result[k];
|
||||
},
|
||||
set(target, k, v) {
|
||||
result[k] = v;
|
||||
return true;
|
||||
throw new Error("Store state should only be modified through actions");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -4650,9 +4695,9 @@
|
||||
exports.useState = useState$1;
|
||||
exports.utils = utils;
|
||||
|
||||
exports.__info__.version = '1.0.0-alpha4';
|
||||
exports.__info__.date = '2019-11-22T12:03:23.892Z';
|
||||
exports.__info__.hash = 'ff76747';
|
||||
exports.__info__.version = '1.0.0-alpha5';
|
||||
exports.__info__.date = '2019-11-29T15:12:14.501Z';
|
||||
exports.__info__.hash = 'c126928';
|
||||
exports.__info__.url = 'https://github.com/odoo/owl';
|
||||
|
||||
}(this.owl = this.owl || {}));
|
||||
|
||||
+88
-38
@@ -2,7 +2,10 @@ const COMPONENTS = `// In this example, we show how components can be defined an
|
||||
const { Component, useState } = owl;
|
||||
|
||||
class Greeter extends Component {
|
||||
state = useState({ word: 'Hello' });
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({ word: 'Hello' });
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.state.word = this.state.word === 'Hi' ? 'Hello' : 'Hi';
|
||||
@@ -11,7 +14,10 @@ class Greeter extends Component {
|
||||
|
||||
// Main root component
|
||||
class App extends Component {
|
||||
state = useState({ name: 'World'});
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({ name: 'World'});
|
||||
}
|
||||
}
|
||||
App.components = { Greeter };
|
||||
|
||||
@@ -47,7 +53,10 @@ const ANIMATION = `// The goal of this component is to see how the t-transition
|
||||
const { Component, useState } = owl;
|
||||
|
||||
class Counter extends Component {
|
||||
state = useState({ value: 0 });
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({ value: 0 });
|
||||
}
|
||||
|
||||
increment() {
|
||||
this.state.value++;
|
||||
@@ -55,7 +64,10 @@ class Counter extends Component {
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
state = useState({ flag: false, componentFlag: false, numbers: [] });
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({ flag: false, componentFlag: false, numbers: [] });
|
||||
}
|
||||
|
||||
toggle(key) {
|
||||
this.state[key] = !this.state[key];
|
||||
@@ -213,7 +225,10 @@ class DemoComponent extends Component {
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
state = useState({ n: 0, flag: true });
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({ n: 0, flag: true });
|
||||
}
|
||||
|
||||
increment() {
|
||||
this.state.n++;
|
||||
@@ -282,11 +297,14 @@ function useMouse() {
|
||||
|
||||
// Main root component
|
||||
class App extends owl.Component {
|
||||
// simple state hook (reactive object)
|
||||
counter = useState({ value: 0 });
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
// simple state hook (reactive object)
|
||||
this.counter = useState({ value: 0 });
|
||||
|
||||
// this hooks is bound to the 'mouse' property.
|
||||
mouse = useMouse();
|
||||
// this hooks is bound to the 'mouse' property.
|
||||
this.mouse = useMouse();
|
||||
}
|
||||
|
||||
increment() {
|
||||
this.counter.value++;
|
||||
@@ -318,7 +336,10 @@ const { Component, Context } = owl;
|
||||
const { useContext } = owl.hooks;
|
||||
|
||||
class ToolbarButton extends Component {
|
||||
theme = useContext(this.env.themeContext);
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.theme = useContext(this.env.themeContext);
|
||||
}
|
||||
|
||||
get style () {
|
||||
const theme = this.theme;
|
||||
@@ -451,12 +472,11 @@ const actions = {
|
||||
// TodoItem
|
||||
//------------------------------------------------------------------------------
|
||||
class TodoItem extends Component {
|
||||
state = useState({ isEditing: false });
|
||||
dispatch = useDispatch();
|
||||
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
useAutofocus("input");
|
||||
this.state = useState({ isEditing: false });
|
||||
this.dispatch = useDispatch();
|
||||
}
|
||||
|
||||
handleKeyup(ev) {
|
||||
@@ -483,9 +503,12 @@ class TodoItem extends Component {
|
||||
// TodoApp
|
||||
//------------------------------------------------------------------------------
|
||||
class TodoApp extends Component {
|
||||
state = useState({ filter: "all" });
|
||||
todos = useStore(state => state.todos);
|
||||
dispatch = useDispatch();
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({ filter: "all" });
|
||||
this.todos = useStore(state => state.todos);
|
||||
this.dispatch = useDispatch();
|
||||
}
|
||||
|
||||
get visibleTodos() {
|
||||
switch (this.state.filter) {
|
||||
@@ -1008,7 +1031,10 @@ class FormView extends owl.Component {}
|
||||
FormView.components = { AdvancedComponent };
|
||||
|
||||
class Chatter extends owl.Component {
|
||||
messages = Array.from(Array(100).keys());
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.messages = Array.from(Array(100).keys());
|
||||
}
|
||||
}
|
||||
|
||||
class App extends owl.Component {}
|
||||
@@ -1150,7 +1176,10 @@ const SLOTS = `// We show here how slots can be used to create generic component
|
||||
const { Component, useState } = owl;
|
||||
|
||||
class Card extends Component {
|
||||
state = useState({ showContent: true });
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({ showContent: true });
|
||||
}
|
||||
|
||||
toggleDisplay() {
|
||||
this.state.showContent = !this.state.showContent;
|
||||
@@ -1158,7 +1187,10 @@ class Card extends Component {
|
||||
}
|
||||
|
||||
class Counter extends Component {
|
||||
state = useState({val: 1});
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({val: 1});
|
||||
}
|
||||
|
||||
inc() {
|
||||
this.state.val++;
|
||||
@@ -1167,7 +1199,10 @@ class Counter extends Component {
|
||||
|
||||
// Main root component
|
||||
class App extends Component {
|
||||
state = useState({a: 1, b: 3});
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({a: 1, b: 3});
|
||||
}
|
||||
|
||||
inc(key, delta) {
|
||||
this.state[key] += delta;
|
||||
@@ -1277,7 +1312,10 @@ class SlowComponent extends Component {
|
||||
class NotificationList extends Component {}
|
||||
|
||||
class App extends Component {
|
||||
state = useState({ value: 0, notifs: [] });
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({ value: 0, notifs: [] });
|
||||
}
|
||||
|
||||
increment() {
|
||||
this.state.value++;
|
||||
@@ -1350,13 +1388,16 @@ const FORM = `// This example illustrate how the t-model directive can be used t
|
||||
const { Component, useState } = owl;
|
||||
|
||||
class Form extends Component {
|
||||
state = useState({
|
||||
text: "",
|
||||
othertext: "",
|
||||
number: 11,
|
||||
color: "",
|
||||
bool: false
|
||||
});
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({
|
||||
text: "",
|
||||
othertext: "",
|
||||
number: 11,
|
||||
color: "",
|
||||
bool: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Application setup
|
||||
@@ -1419,7 +1460,10 @@ const { useRef } = owl.hooks;
|
||||
class HelloWorld extends Component {}
|
||||
|
||||
class Counter extends Component {
|
||||
state = useState({ value: 0 });
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.state = useState({ value: 0 });
|
||||
}
|
||||
|
||||
inc() {
|
||||
this.state.value++;
|
||||
@@ -1470,11 +1514,14 @@ class Window extends Component {
|
||||
}
|
||||
|
||||
class WindowManager extends Component {
|
||||
windows = [];
|
||||
nextId = 1;
|
||||
currentZindex = 1;
|
||||
nextLeft = 0;
|
||||
nextTop = 0;
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.windows = [];
|
||||
this.nextId = 1;
|
||||
this.currentZindex = 1;
|
||||
this.nextLeft = 0;
|
||||
this.nextTop = 0;
|
||||
}
|
||||
|
||||
addWindow(name) {
|
||||
const info = this.env.windows.find(w => w.name === name);
|
||||
@@ -1518,7 +1565,10 @@ class WindowManager extends Component {
|
||||
WindowManager.components = { Window };
|
||||
|
||||
class App extends Component {
|
||||
wmRef = useRef("wm");
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.wmRef = useRef("wm");
|
||||
}
|
||||
|
||||
addWindow(name) {
|
||||
this.wmRef.comp.addWindow(name);
|
||||
@@ -1552,7 +1602,7 @@ const WMS_XML = `<templates>
|
||||
<div t-name="Window" class="window" t-att-style="style" t-on-click="updateZIndex">
|
||||
<div class="header">
|
||||
<span t-on-mousedown="startDragAndDrop"><t t-esc="props.info.title"/></span>
|
||||
<span class="close" t-on-click="close">×</span>
|
||||
<span class="close" t-on-click.stop="close">×</span>
|
||||
</div>
|
||||
<t t-slot="default"/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user