mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] update owl to v2.0.0-beta-7
This commit is contained in:
@@ -212,7 +212,8 @@
|
|||||||
}
|
}
|
||||||
function makePropSetter(name) {
|
function makePropSetter(name) {
|
||||||
return function setProp(value) {
|
return function setProp(value) {
|
||||||
this[name] = value || "";
|
// support 0, fallback to empty string for other falsy values
|
||||||
|
this[name] = value === 0 ? 0 : value || "";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function isProp(tag, key) {
|
function isProp(tag, key) {
|
||||||
@@ -518,7 +519,7 @@
|
|||||||
const characterDataSetData = getDescriptor$1(characterDataProto, "data").set;
|
const characterDataSetData = getDescriptor$1(characterDataProto, "data").set;
|
||||||
const nodeGetFirstChild = getDescriptor$1(nodeProto$2, "firstChild").get;
|
const nodeGetFirstChild = getDescriptor$1(nodeProto$2, "firstChild").get;
|
||||||
const nodeGetNextSibling = getDescriptor$1(nodeProto$2, "nextSibling").get;
|
const nodeGetNextSibling = getDescriptor$1(nodeProto$2, "nextSibling").get;
|
||||||
const NO_OP$1 = () => { };
|
const NO_OP = () => { };
|
||||||
const cache$1 = {};
|
const cache$1 = {};
|
||||||
/**
|
/**
|
||||||
* Compiling blocks is a multi-step process:
|
* Compiling blocks is a multi-step process:
|
||||||
@@ -822,7 +823,7 @@
|
|||||||
idx: info.idx,
|
idx: info.idx,
|
||||||
refIdx: info.refIdx,
|
refIdx: info.refIdx,
|
||||||
setData: makeRefSetter(index, ctx.refList),
|
setData: makeRefSetter(index, ctx.refList),
|
||||||
updateData: NO_OP$1,
|
updateData: NO_OP,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1623,6 +1624,11 @@
|
|||||||
if (key === TARGET) {
|
if (key === TARGET) {
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
// non-writable non-configurable properties cannot be made reactive
|
||||||
|
const desc = Object.getOwnPropertyDescriptor(target, key);
|
||||||
|
if (desc && !desc.writable && !desc.configurable) {
|
||||||
|
return Reflect.get(target, key, proxy);
|
||||||
|
}
|
||||||
observeTargetKey(target, key, callback);
|
observeTargetKey(target, key, callback);
|
||||||
return possiblyReactive(Reflect.get(target, key, proxy), callback);
|
return possiblyReactive(Reflect.get(target, key, proxy), callback);
|
||||||
},
|
},
|
||||||
@@ -1695,6 +1701,23 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Creates a forEach function that will delegate to forEach on the underlying
|
||||||
|
* collection while observing key changes, and keys as they're iterated over,
|
||||||
|
* and making the passed keys/values reactive.
|
||||||
|
*
|
||||||
|
* @param target @see reactive
|
||||||
|
* @param callback @see reactive
|
||||||
|
*/
|
||||||
|
function makeForEachObserver(target, callback) {
|
||||||
|
return function forEach(forEachCb, thisArg) {
|
||||||
|
observeTargetKey(target, KEYCHANGES, callback);
|
||||||
|
target.forEach(function (val, key, targetObj) {
|
||||||
|
observeTargetKey(target, key, callback);
|
||||||
|
forEachCb.call(thisArg, possiblyReactive(val, callback), possiblyReactive(key, callback), possiblyReactive(targetObj, callback));
|
||||||
|
}, thisArg);
|
||||||
|
};
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Creates a function that will delegate to an underlying method, and check if
|
* Creates a function that will delegate to an underlying method, and check if
|
||||||
* that method has modified the presence or value of a key, and notify the
|
* that method has modified the presence or value of a key, and notify the
|
||||||
@@ -1753,6 +1776,7 @@
|
|||||||
values: makeIteratorObserver("values", target, callback),
|
values: makeIteratorObserver("values", target, callback),
|
||||||
entries: makeIteratorObserver("entries", target, callback),
|
entries: makeIteratorObserver("entries", target, callback),
|
||||||
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
|
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
|
||||||
|
forEach: makeForEachObserver(target, callback),
|
||||||
clear: makeClearNotifier(target),
|
clear: makeClearNotifier(target),
|
||||||
get size() {
|
get size() {
|
||||||
observeTargetKey(target, KEYCHANGES, callback);
|
observeTargetKey(target, KEYCHANGES, callback);
|
||||||
@@ -1768,6 +1792,7 @@
|
|||||||
values: makeIteratorObserver("values", target, callback),
|
values: makeIteratorObserver("values", target, callback),
|
||||||
entries: makeIteratorObserver("entries", target, callback),
|
entries: makeIteratorObserver("entries", target, callback),
|
||||||
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
|
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
|
||||||
|
forEach: makeForEachObserver(target, callback),
|
||||||
clear: makeClearNotifier(target),
|
clear: makeClearNotifier(target),
|
||||||
get size() {
|
get size() {
|
||||||
observeTargetKey(target, KEYCHANGES, callback);
|
observeTargetKey(target, KEYCHANGES, callback);
|
||||||
@@ -1821,11 +1846,13 @@
|
|||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
if (!called) {
|
if (!called) {
|
||||||
called = true;
|
called = true;
|
||||||
callback();
|
|
||||||
// wait for all calls in this microtick to fall through before resetting "called"
|
// wait for all calls in this microtick to fall through before resetting "called"
|
||||||
// so that only the first call to the batched function calls the original callback
|
// so that only the first call to the batched function calls the original callback.
|
||||||
await Promise.resolve();
|
// Schedule this before calling the callback so that calls to the batched function
|
||||||
called = false;
|
// within the callback will proceed only after resetting called to false, and have
|
||||||
|
// a chance to execute the callback again
|
||||||
|
Promise.resolve().then(() => (called = false));
|
||||||
|
callback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1888,7 +1915,7 @@
|
|||||||
// Maps fibers to thrown errors
|
// Maps fibers to thrown errors
|
||||||
const fibersInError = new WeakMap();
|
const fibersInError = new WeakMap();
|
||||||
const nodeErrorHandlers = new WeakMap();
|
const nodeErrorHandlers = new WeakMap();
|
||||||
function _handleError(node, error, isFirstRound = false) {
|
function _handleError(node, error) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1898,22 +1925,19 @@
|
|||||||
}
|
}
|
||||||
const errorHandlers = nodeErrorHandlers.get(node);
|
const errorHandlers = nodeErrorHandlers.get(node);
|
||||||
if (errorHandlers) {
|
if (errorHandlers) {
|
||||||
let stopped = false;
|
let handled = false;
|
||||||
// execute in the opposite order
|
// execute in the opposite order
|
||||||
for (let i = errorHandlers.length - 1; i >= 0; i--) {
|
for (let i = errorHandlers.length - 1; i >= 0; i--) {
|
||||||
try {
|
try {
|
||||||
errorHandlers[i](error);
|
errorHandlers[i](error);
|
||||||
stopped = true;
|
handled = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stopped) {
|
if (handled) {
|
||||||
if (isFirstRound && fiber && fiber.node.fiber) {
|
|
||||||
fiber.root.counter--;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1931,7 +1955,7 @@
|
|||||||
current = current.parent;
|
current = current.parent;
|
||||||
} while (current);
|
} while (current);
|
||||||
fibersInError.set(fiber.root, error);
|
fibersInError.set(fiber.root, error);
|
||||||
const handled = _handleError(node, error, true);
|
const handled = _handleError(node, error);
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
console.warn(`[Owl] Unhandled error. Destroying the root component`);
|
console.warn(`[Owl] Unhandled error. Destroying the root component`);
|
||||||
try {
|
try {
|
||||||
@@ -1955,8 +1979,14 @@
|
|||||||
let current = node.fiber;
|
let current = node.fiber;
|
||||||
if (current) {
|
if (current) {
|
||||||
let root = current.root;
|
let root = current.root;
|
||||||
root.counter = root.counter + 1 - cancelFibers(current.children);
|
// lock root fiber because canceling children fibers may destroy components,
|
||||||
|
// which means any arbitrary code can be run in onWillDestroy, which may
|
||||||
|
// trigger new renderings
|
||||||
|
root.locked = true;
|
||||||
|
root.setCounter(root.counter + 1 - cancelFibers(current.children));
|
||||||
|
root.locked = false;
|
||||||
current.children = [];
|
current.children = [];
|
||||||
|
current.childrenMap = {};
|
||||||
current.bdom = null;
|
current.bdom = null;
|
||||||
if (fibersInError.has(current)) {
|
if (fibersInError.has(current)) {
|
||||||
fibersInError.delete(current);
|
fibersInError.delete(current);
|
||||||
@@ -1974,13 +2004,21 @@
|
|||||||
}
|
}
|
||||||
return fiber;
|
return fiber;
|
||||||
}
|
}
|
||||||
|
function throwOnRender() {
|
||||||
|
throw new Error("Attempted to render cancelled fiber");
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @returns number of not-yet rendered fibers cancelled
|
* @returns number of not-yet rendered fibers cancelled
|
||||||
*/
|
*/
|
||||||
function cancelFibers(fibers) {
|
function cancelFibers(fibers) {
|
||||||
let result = 0;
|
let result = 0;
|
||||||
for (let fiber of fibers) {
|
for (let fiber of fibers) {
|
||||||
fiber.node.fiber = null;
|
let node = fiber.node;
|
||||||
|
fiber.render = throwOnRender;
|
||||||
|
if (node.status === 0 /* NEW */) {
|
||||||
|
node.destroy();
|
||||||
|
}
|
||||||
|
node.fiber = null;
|
||||||
if (fiber.bdom) {
|
if (fiber.bdom) {
|
||||||
// if fiber has been rendered, this means that the component props have
|
// if fiber has been rendered, this means that the component props have
|
||||||
// been updated. however, this fiber will not be patched to the dom, so
|
// been updated. however, this fiber will not be patched to the dom, so
|
||||||
@@ -1988,7 +2026,7 @@
|
|||||||
// the same props, and skip the render completely. With the next line,
|
// the same props, and skip the render completely. With the next line,
|
||||||
// we kindly request the component code to force a render, so it works as
|
// we kindly request the component code to force a render, so it works as
|
||||||
// expected.
|
// expected.
|
||||||
fiber.node.forceNextRender = true;
|
node.forceNextRender = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result++;
|
result++;
|
||||||
@@ -2003,12 +2041,13 @@
|
|||||||
this.children = [];
|
this.children = [];
|
||||||
this.appliedToDom = false;
|
this.appliedToDom = false;
|
||||||
this.deep = false;
|
this.deep = false;
|
||||||
|
this.childrenMap = {};
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
if (parent) {
|
if (parent) {
|
||||||
this.deep = parent.deep;
|
this.deep = parent.deep;
|
||||||
const root = parent.root;
|
const root = parent.root;
|
||||||
root.counter++;
|
root.setCounter(root.counter + 1);
|
||||||
this.root = root;
|
this.root = root;
|
||||||
parent.children.push(this);
|
parent.children.push(this);
|
||||||
}
|
}
|
||||||
@@ -2016,6 +2055,42 @@
|
|||||||
this.root = this;
|
this.root = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
render() {
|
||||||
|
// if some parent has a fiber => register in followup
|
||||||
|
let prev = this.root.node;
|
||||||
|
let scheduler = prev.app.scheduler;
|
||||||
|
let current = prev.parent;
|
||||||
|
while (current) {
|
||||||
|
if (current.fiber) {
|
||||||
|
let root = current.fiber.root;
|
||||||
|
if (root.counter === 0 && prev.parentKey in current.fiber.childrenMap) {
|
||||||
|
current = root.node;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scheduler.delayedRenders.push(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev = current;
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
// there are no current rendering from above => we can render
|
||||||
|
this._render();
|
||||||
|
}
|
||||||
|
_render() {
|
||||||
|
const node = this.node;
|
||||||
|
const root = this.root;
|
||||||
|
if (root) {
|
||||||
|
try {
|
||||||
|
this.bdom = true;
|
||||||
|
this.bdom = node.renderFn();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
handleError({ node, error: e });
|
||||||
|
}
|
||||||
|
root.setCounter(root.counter - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class RootFiber extends Fiber {
|
class RootFiber extends Fiber {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -2077,6 +2152,12 @@
|
|||||||
handleError({ fiber: current || this, error: e });
|
handleError({ fiber: current || this, error: e });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setCounter(newValue) {
|
||||||
|
this.counter = newValue;
|
||||||
|
if (newValue === 0) {
|
||||||
|
this.node.app.scheduler.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class MountFiber extends RootFiber {
|
class MountFiber extends RootFiber {
|
||||||
constructor(node, target, options = {}) {
|
constructor(node, target, options = {}) {
|
||||||
@@ -2088,6 +2169,7 @@
|
|||||||
let current = this;
|
let current = this;
|
||||||
try {
|
try {
|
||||||
const node = this.node;
|
const node = this.node;
|
||||||
|
node.children = this.childrenMap;
|
||||||
node.app.constructor.validateTarget(this.target);
|
node.app.constructor.validateTarget(this.target);
|
||||||
if (node.bdom) {
|
if (node.bdom) {
|
||||||
// this is a complicated situation: if we mount a fiber with an existing
|
// this is a complicated situation: if we mount a fiber with an existing
|
||||||
@@ -2293,17 +2375,10 @@
|
|||||||
const node = getCurrent();
|
const node = getCurrent();
|
||||||
let render = batchedRenderFunctions.get(node);
|
let render = batchedRenderFunctions.get(node);
|
||||||
if (!render) {
|
if (!render) {
|
||||||
render = batched(node.render.bind(node));
|
render = batched(node.render.bind(node, false));
|
||||||
batchedRenderFunctions.set(node, render);
|
batchedRenderFunctions.set(node, render);
|
||||||
// manual implementation of onWillDestroy to break cyclic dependency
|
// manual implementation of onWillDestroy to break cyclic dependency
|
||||||
node.willDestroy.push(clearReactivesForCallback.bind(null, render));
|
node.willDestroy.push(clearReactivesForCallback.bind(null, render));
|
||||||
if (node.app.dev) {
|
|
||||||
Object.defineProperty(node, "subscriptions", {
|
|
||||||
get() {
|
|
||||||
return getSubscriptions(render);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return reactive(state, render);
|
return reactive(state, render);
|
||||||
}
|
}
|
||||||
@@ -2318,14 +2393,8 @@
|
|||||||
function component(name, props, key, ctx, parent) {
|
function component(name, props, key, ctx, parent) {
|
||||||
let node = ctx.children[key];
|
let node = ctx.children[key];
|
||||||
let isDynamic = typeof name !== "string";
|
let isDynamic = typeof name !== "string";
|
||||||
if (node) {
|
if (node && node.status === 2 /* DESTROYED */) {
|
||||||
if (node.status < 1 /* MOUNTED */) {
|
node = undefined;
|
||||||
node.destroy();
|
|
||||||
node = undefined;
|
|
||||||
}
|
|
||||||
else if (node.status === 2 /* DESTROYED */) {
|
|
||||||
node = undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (isDynamic && node && node.component.constructor !== name) {
|
if (isDynamic && node && node.component.constructor !== name) {
|
||||||
node = undefined;
|
node = undefined;
|
||||||
@@ -2356,14 +2425,15 @@
|
|||||||
throw new Error(`Cannot find the definition of component "${name}"`);
|
throw new Error(`Cannot find the definition of component "${name}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node = new ComponentNode(C, props, ctx.app, ctx);
|
node = new ComponentNode(C, props, ctx.app, ctx, key);
|
||||||
ctx.children[key] = node;
|
ctx.children[key] = node;
|
||||||
node.initiateRender(new Fiber(node, parentFiber));
|
node.initiateRender(new Fiber(node, parentFiber));
|
||||||
}
|
}
|
||||||
|
parentFiber.childrenMap[key] = node;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
class ComponentNode {
|
class ComponentNode {
|
||||||
constructor(C, props, app, parent) {
|
constructor(C, props, app, parent, parentKey) {
|
||||||
this.fiber = null;
|
this.fiber = null;
|
||||||
this.bdom = null;
|
this.bdom = null;
|
||||||
this.status = 0 /* NEW */;
|
this.status = 0 /* NEW */;
|
||||||
@@ -2379,7 +2449,8 @@
|
|||||||
this.willDestroy = [];
|
this.willDestroy = [];
|
||||||
currentNode = this;
|
currentNode = this;
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.parent = parent || null;
|
this.parent = parent;
|
||||||
|
this.parentKey = parentKey;
|
||||||
this.level = parent ? parent.level + 1 : 0;
|
this.level = parent ? parent.level + 1 : 0;
|
||||||
applyDefaultProps(props, C);
|
applyDefaultProps(props, C);
|
||||||
const env = (parent && parent.childEnv) || app.env;
|
const env = (parent && parent.childEnv) || app.env;
|
||||||
@@ -2409,12 +2480,12 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.status === 0 /* NEW */ && this.fiber === fiber) {
|
if (this.status === 0 /* NEW */ && this.fiber === fiber) {
|
||||||
this._render(fiber);
|
fiber.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async render(deep = false) {
|
async render(deep) {
|
||||||
let current = this.fiber;
|
let current = this.fiber;
|
||||||
if (current && current.root.locked) {
|
if (current && (current.root.locked || current.bdom === true)) {
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
// situation may have changed after the microtask tick
|
// situation may have changed after the microtask tick
|
||||||
current = this.fiber;
|
current = this.fiber;
|
||||||
@@ -2453,16 +2524,7 @@
|
|||||||
// embedded in a rendering coming from above, so the fiber will be rendered
|
// embedded in a rendering coming from above, so the fiber will be rendered
|
||||||
// in the next microtick anyway, so we should not render it again.
|
// in the next microtick anyway, so we should not render it again.
|
||||||
if (this.fiber === fiber && (current || !fiber.parent)) {
|
if (this.fiber === fiber && (current || !fiber.parent)) {
|
||||||
this._render(fiber);
|
fiber.render();
|
||||||
}
|
|
||||||
}
|
|
||||||
_render(fiber) {
|
|
||||||
try {
|
|
||||||
fiber.bdom = this.renderFn();
|
|
||||||
fiber.root.counter--;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
handleError({ node: this, error: e });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
destroy() {
|
destroy() {
|
||||||
@@ -2482,8 +2544,15 @@
|
|||||||
for (let child of Object.values(this.children)) {
|
for (let child of Object.values(this.children)) {
|
||||||
child._destroy();
|
child._destroy();
|
||||||
}
|
}
|
||||||
for (let cb of this.willDestroy) {
|
if (this.willDestroy.length) {
|
||||||
cb.call(component);
|
try {
|
||||||
|
for (let cb of this.willDestroy) {
|
||||||
|
cb.call(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
handleError({ error: e, node: this });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.status = 2 /* DESTROYED */;
|
this.status = 2 /* DESTROYED */;
|
||||||
}
|
}
|
||||||
@@ -2502,7 +2571,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
component.props = props;
|
component.props = props;
|
||||||
this._render(fiber);
|
fiber.render();
|
||||||
const parentRoot = parentFiber.root;
|
const parentRoot = parentFiber.root;
|
||||||
if (this.willPatch.length) {
|
if (this.willPatch.length) {
|
||||||
parentRoot.willPatch.push(fiber);
|
parentRoot.willPatch.push(fiber);
|
||||||
@@ -2549,6 +2618,7 @@
|
|||||||
bdom.mount(parent, anchor);
|
bdom.mount(parent, anchor);
|
||||||
this.status = 1 /* MOUNTED */;
|
this.status = 1 /* MOUNTED */;
|
||||||
this.fiber.appliedToDom = true;
|
this.fiber.appliedToDom = true;
|
||||||
|
this.children = this.fiber.childrenMap;
|
||||||
this.fiber = null;
|
this.fiber = null;
|
||||||
}
|
}
|
||||||
moveBefore(other, afterNode) {
|
moveBefore(other, afterNode) {
|
||||||
@@ -2564,10 +2634,8 @@
|
|||||||
}
|
}
|
||||||
_patch() {
|
_patch() {
|
||||||
const hasChildren = Object.keys(this.children).length > 0;
|
const hasChildren = Object.keys(this.children).length > 0;
|
||||||
|
this.children = this.fiber.childrenMap;
|
||||||
this.bdom.patch(this.fiber.bdom, hasChildren);
|
this.bdom.patch(this.fiber.bdom, hasChildren);
|
||||||
if (hasChildren) {
|
|
||||||
this.cleanOutdatedChildren();
|
|
||||||
}
|
|
||||||
this.fiber.appliedToDom = true;
|
this.fiber.appliedToDom = true;
|
||||||
this.fiber = null;
|
this.fiber = null;
|
||||||
}
|
}
|
||||||
@@ -2577,18 +2645,15 @@
|
|||||||
remove() {
|
remove() {
|
||||||
this.bdom.remove();
|
this.bdom.remove();
|
||||||
}
|
}
|
||||||
cleanOutdatedChildren() {
|
// ---------------------------------------------------------------------------
|
||||||
const children = this.children;
|
// Some debug helpers
|
||||||
for (const key in children) {
|
// ---------------------------------------------------------------------------
|
||||||
const node = children[key];
|
get name() {
|
||||||
const status = node.status;
|
return this.component.constructor.name;
|
||||||
if (status !== 1 /* MOUNTED */) {
|
}
|
||||||
delete children[key];
|
get subscriptions() {
|
||||||
if (status !== 2 /* DESTROYED */) {
|
const render = batchedRenderFunctions.get(this);
|
||||||
node.destroy();
|
return render ? getSubscriptions(render) : [];
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2598,59 +2663,59 @@
|
|||||||
class Scheduler {
|
class Scheduler {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.tasks = new Set();
|
this.tasks = new Set();
|
||||||
this.isRunning = false;
|
this.frame = 0;
|
||||||
|
this.delayedRenders = [];
|
||||||
this.requestAnimationFrame = Scheduler.requestAnimationFrame;
|
this.requestAnimationFrame = Scheduler.requestAnimationFrame;
|
||||||
}
|
}
|
||||||
start() {
|
|
||||||
this.isRunning = true;
|
|
||||||
this.scheduleTasks();
|
|
||||||
}
|
|
||||||
stop() {
|
|
||||||
this.isRunning = false;
|
|
||||||
}
|
|
||||||
addFiber(fiber) {
|
addFiber(fiber) {
|
||||||
this.tasks.add(fiber.root);
|
this.tasks.add(fiber.root);
|
||||||
if (!this.isRunning) {
|
|
||||||
this.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Process all current tasks. This only applies to the fibers that are ready.
|
* Process all current tasks. This only applies to the fibers that are ready.
|
||||||
* Other tasks are left unchanged.
|
* Other tasks are left unchanged.
|
||||||
*/
|
*/
|
||||||
flush() {
|
flush() {
|
||||||
this.tasks.forEach((fiber) => {
|
if (this.delayedRenders.length) {
|
||||||
if (fiber.root !== fiber) {
|
let renders = this.delayedRenders;
|
||||||
this.tasks.delete(fiber);
|
this.delayedRenders = [];
|
||||||
return;
|
for (let f of renders) {
|
||||||
}
|
if (f.root && f.node.status !== 2 /* DESTROYED */ && f.node.fiber === f) {
|
||||||
const hasError = fibersInError.has(fiber);
|
f.render();
|
||||||
if (hasError && fiber.counter !== 0) {
|
|
||||||
this.tasks.delete(fiber);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fiber.node.status === 2 /* DESTROYED */) {
|
|
||||||
this.tasks.delete(fiber);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fiber.counter === 0) {
|
|
||||||
if (!hasError) {
|
|
||||||
fiber.complete();
|
|
||||||
}
|
}
|
||||||
this.tasks.delete(fiber);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
if (this.tasks.size === 0) {
|
if (this.frame === 0) {
|
||||||
this.stop();
|
this.frame = this.requestAnimationFrame(() => {
|
||||||
|
this.frame = 0;
|
||||||
|
this.tasks.forEach((fiber) => this.processFiber(fiber));
|
||||||
|
for (let task of this.tasks) {
|
||||||
|
if (task.node.status === 2 /* DESTROYED */) {
|
||||||
|
this.tasks.delete(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scheduleTasks() {
|
processFiber(fiber) {
|
||||||
this.requestAnimationFrame(() => {
|
if (fiber.root !== fiber) {
|
||||||
this.flush();
|
this.tasks.delete(fiber);
|
||||||
if (this.isRunning) {
|
return;
|
||||||
this.scheduleTasks();
|
}
|
||||||
|
const hasError = fibersInError.has(fiber);
|
||||||
|
if (hasError && fiber.counter !== 0) {
|
||||||
|
this.tasks.delete(fiber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fiber.node.status === 2 /* DESTROYED */) {
|
||||||
|
this.tasks.delete(fiber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fiber.counter === 0) {
|
||||||
|
if (!hasError) {
|
||||||
|
fiber.complete();
|
||||||
}
|
}
|
||||||
});
|
this.tasks.delete(fiber);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// capture the value of requestAnimationFrame as soon as possible, to avoid
|
// capture the value of requestAnimationFrame as soon as possible, to avoid
|
||||||
@@ -3939,8 +4004,11 @@
|
|||||||
let slotStr = [];
|
let slotStr = [];
|
||||||
for (let slotName in ast.slots) {
|
for (let slotName in ast.slots) {
|
||||||
const slotAst = ast.slots[slotName];
|
const slotAst = ast.slots[slotName];
|
||||||
const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on);
|
const params = [];
|
||||||
const params = [`__render: ${name}, __ctx: ${ctxStr}`];
|
if (slotAst.content) {
|
||||||
|
const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on);
|
||||||
|
params.push(`__render: ${name}, __ctx: ${ctxStr}`);
|
||||||
|
}
|
||||||
const scope = ast.slots[slotName].scope;
|
const scope = ast.slots[slotName].scope;
|
||||||
if (scope) {
|
if (scope) {
|
||||||
params.push(`__scope: "${scope}"`);
|
params.push(`__scope: "${scope}"`);
|
||||||
@@ -4042,7 +4110,7 @@
|
|||||||
if (dynamic) {
|
if (dynamic) {
|
||||||
let name = this.generateId("slot");
|
let name = this.generateId("slot");
|
||||||
this.define(name, slotName);
|
this.define(name, slotName);
|
||||||
blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}), ${dynamic}, ${scope})`;
|
blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}, ${dynamic}, ${scope}))`;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope})`;
|
blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope})`;
|
||||||
@@ -4583,28 +4651,26 @@
|
|||||||
slotNode.removeAttribute("t-set-slot");
|
slotNode.removeAttribute("t-set-slot");
|
||||||
slotNode.remove();
|
slotNode.remove();
|
||||||
const slotAst = parseNode(slotNode, ctx);
|
const slotAst = parseNode(slotNode, ctx);
|
||||||
if (slotAst) {
|
let on = null;
|
||||||
let on = null;
|
let attrs = null;
|
||||||
let attrs = null;
|
let scope = null;
|
||||||
let scope = null;
|
for (let attributeName of slotNode.getAttributeNames()) {
|
||||||
for (let attributeName of slotNode.getAttributeNames()) {
|
const value = slotNode.getAttribute(attributeName);
|
||||||
const value = slotNode.getAttribute(attributeName);
|
if (attributeName === "t-slot-scope") {
|
||||||
if (attributeName === "t-slot-scope") {
|
scope = value;
|
||||||
scope = value;
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
else if (attributeName.startsWith("t-on-")) {
|
||||||
else if (attributeName.startsWith("t-on-")) {
|
on = on || {};
|
||||||
on = on || {};
|
on[attributeName.slice(5)] = value;
|
||||||
on[attributeName.slice(5)] = value;
|
}
|
||||||
}
|
else {
|
||||||
else {
|
attrs = attrs || {};
|
||||||
attrs = attrs || {};
|
attrs[attributeName] = value;
|
||||||
attrs[attributeName] = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
slots = slots || {};
|
|
||||||
slots[name] = { content: slotAst, on, attrs, scope };
|
|
||||||
}
|
}
|
||||||
|
slots = slots || {};
|
||||||
|
slots[name] = { content: slotAst, on, attrs, scope };
|
||||||
}
|
}
|
||||||
// default slot
|
// default slot
|
||||||
const defaultContent = parseChildNodes(clone, ctx);
|
const defaultContent = parseChildNodes(clone, ctx);
|
||||||
@@ -4835,12 +4901,26 @@
|
|||||||
return new Function("bdom, helpers", code);
|
return new Function("bdom, helpers", code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TIMEOUT = Symbol("timeout");
|
||||||
function wrapError(fn, hookName) {
|
function wrapError(fn, hookName) {
|
||||||
const error = new Error(`The following error occurred in ${hookName}: `);
|
const error = new Error(`The following error occurred in ${hookName}: `);
|
||||||
|
const timeoutError = new Error(`${hookName}'s promise hasn't resolved after 3 seconds`);
|
||||||
|
const node = getCurrent();
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
try {
|
try {
|
||||||
const result = fn(...args);
|
const result = fn(...args);
|
||||||
if (result instanceof Promise) {
|
if (result instanceof Promise) {
|
||||||
|
if (hookName === "onWillStart" || hookName === "onWillUpdateProps") {
|
||||||
|
const fiber = node.fiber;
|
||||||
|
Promise.race([
|
||||||
|
result,
|
||||||
|
new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)),
|
||||||
|
]).then((res) => {
|
||||||
|
if (res === TIMEOUT && node.fiber === fiber) {
|
||||||
|
console.warn(timeoutError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
return result.catch((cause) => {
|
return result.catch((cause) => {
|
||||||
error.cause = cause;
|
error.cause = cause;
|
||||||
if (cause instanceof Error) {
|
if (cause instanceof Error) {
|
||||||
@@ -4936,7 +5016,7 @@
|
|||||||
}
|
}
|
||||||
setup() { }
|
setup() { }
|
||||||
render(deep = false) {
|
render(deep = false) {
|
||||||
this.__owl__.render(deep);
|
this.__owl__.render(deep === true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Component.template = "";
|
Component.template = "";
|
||||||
@@ -5233,13 +5313,19 @@
|
|||||||
}
|
}
|
||||||
this.helpers = makeHelpers(this.getTemplate.bind(this));
|
this.helpers = makeHelpers(this.getTemplate.bind(this));
|
||||||
}
|
}
|
||||||
addTemplate(name, template, options = {}) {
|
addTemplate(name, template) {
|
||||||
if (name in this.rawTemplates && !options.allowDuplicate) {
|
if (name in this.rawTemplates) {
|
||||||
throw new Error(`Template ${name} already defined`);
|
const rawTemplate = this.rawTemplates[name];
|
||||||
|
const currentAsString = typeof rawTemplate === "string" ? rawTemplate : rawTemplate.outerHTML;
|
||||||
|
const newAsString = typeof template === "string" ? template : template.outerHTML;
|
||||||
|
if (currentAsString === newAsString) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error(`Template ${name} already defined with different content`);
|
||||||
}
|
}
|
||||||
this.rawTemplates[name] = template;
|
this.rawTemplates[name] = template;
|
||||||
}
|
}
|
||||||
addTemplates(xml, options = {}) {
|
addTemplates(xml) {
|
||||||
if (!xml) {
|
if (!xml) {
|
||||||
// empty string
|
// empty string
|
||||||
return;
|
return;
|
||||||
@@ -5247,7 +5333,7 @@
|
|||||||
xml = xml instanceof Document ? xml : parseXML(xml);
|
xml = xml instanceof Document ? xml : parseXML(xml);
|
||||||
for (const template of xml.querySelectorAll("[t-name]")) {
|
for (const template of xml.querySelectorAll("[t-name]")) {
|
||||||
const name = template.getAttribute("t-name");
|
const name = template.getAttribute("t-name");
|
||||||
this.addTemplate(name, template, options);
|
this.addTemplate(name, template);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getTemplate(name) {
|
getTemplate(name) {
|
||||||
@@ -5318,7 +5404,7 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
|
|||||||
return prom;
|
return prom;
|
||||||
}
|
}
|
||||||
makeNode(Component, props) {
|
makeNode(Component, props) {
|
||||||
return new ComponentNode(Component, props, this);
|
return new ComponentNode(Component, props, this, null, null);
|
||||||
}
|
}
|
||||||
mountNode(node, target, options) {
|
mountNode(node, target, options) {
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
@@ -5350,6 +5436,7 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
|
|||||||
}
|
}
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.root) {
|
if (this.root) {
|
||||||
|
this.scheduler.flush();
|
||||||
this.root.destroy();
|
this.root.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5415,10 +5502,6 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
|
|||||||
const node = getCurrent();
|
const node = getCurrent();
|
||||||
node.childEnv = extendEnv(node.childEnv, envExtension);
|
node.childEnv = extendEnv(node.childEnv, envExtension);
|
||||||
}
|
}
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// useEffect
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
const NO_OP = () => { };
|
|
||||||
/**
|
/**
|
||||||
* This hook will run a callback when a component is mounted and patched, and
|
* This hook will run a callback when a component is mounted and patched, and
|
||||||
* will run a cleanup function before patching and before unmounting the
|
* will run a cleanup function before patching and before unmounting the
|
||||||
@@ -5436,18 +5519,20 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
|
|||||||
let dependencies;
|
let dependencies;
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
dependencies = computeDependencies();
|
dependencies = computeDependencies();
|
||||||
cleanup = effect(...dependencies) || NO_OP;
|
cleanup = effect(...dependencies);
|
||||||
});
|
});
|
||||||
onPatched(() => {
|
onPatched(() => {
|
||||||
const newDeps = computeDependencies();
|
const newDeps = computeDependencies();
|
||||||
const shouldReapply = newDeps.some((val, i) => val !== dependencies[i]);
|
const shouldReapply = newDeps.some((val, i) => val !== dependencies[i]);
|
||||||
if (shouldReapply) {
|
if (shouldReapply) {
|
||||||
dependencies = newDeps;
|
dependencies = newDeps;
|
||||||
cleanup();
|
if (cleanup) {
|
||||||
cleanup = effect(...dependencies) || NO_OP;
|
cleanup();
|
||||||
|
}
|
||||||
|
cleanup = effect(...dependencies);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
onWillUnmount(() => cleanup());
|
onWillUnmount(() => cleanup && cleanup());
|
||||||
}
|
}
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// useExternalListener
|
// useExternalListener
|
||||||
@@ -5527,9 +5612,9 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
|
|||||||
Object.defineProperty(exports, '__esModule', { value: true });
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||||||
|
|
||||||
|
|
||||||
__info__.version = '2.0.0-beta.3';
|
__info__.version = '2.0.0-beta-7';
|
||||||
__info__.date = '2022-03-08T11:47:57.414Z';
|
__info__.date = '2022-04-27T09:08:50.320Z';
|
||||||
__info__.hash = 'c356351';
|
__info__.hash = '0cd66c8';
|
||||||
__info__.url = 'https://github.com/odoo/owl';
|
__info__.url = 'https://github.com/odoo/owl';
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user