[IMP] owl: update to v1.0.0alpha5

This commit is contained in:
Géry Debongnie
2019-11-29 16:19:27 +01:00
parent 583daa7902
commit bf3f0ff91c
2 changed files with 262 additions and 167 deletions
+174 -129
View File
@@ -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
View File
@@ -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>