[IMP] update owl to v2.0.0-alpha.3

This commit is contained in:
Géry Debongnie
2022-02-25 10:57:58 +01:00
parent d4267b55be
commit 7a3e50c86f
+350 -270
View File
@@ -212,7 +212,7 @@
}
function makePropSetter(name) {
return function setProp(value) {
this[name] = value;
this[name] = value || "";
};
}
function isProp(tag, key) {
@@ -1501,6 +1501,210 @@
return new Markup(value);
}
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
const TARGET = Symbol("Target");
// Escape hatch to prevent reactivity system to turn something into a reactive
const SKIP = Symbol("Skip");
// Special key to subscribe to, to be notified of key creation/deletion
const KEYCHANGES = Symbol("Key changes");
const objectToString = Object.prototype.toString;
/**
* Checks whether a given value can be made into a reactive object.
*
* @param value the value to check
* @returns whether the value can be made reactive
*/
function canBeMadeReactive(value) {
if (typeof value !== "object") {
return false;
}
// extract "RawType" from strings like "[object RawType]" => this lets us
// ignore many native objects such as Promise (whose toString is [object Promise])
// or Date ([object Date]).
const rawType = objectToString.call(value).slice(8, -1);
return rawType === "Object" || rawType === "Array";
}
/**
* Mark an object or array so that it is ignored by the reactivity system
*
* @param value the value to mark
* @returns the object itself
*/
function markRaw(value) {
value[SKIP] = true;
return value;
}
/**
* Given a reactive objet, return the raw (non reactive) underlying object
*
* @param value a reactive value
* @returns the underlying value
*/
function toRaw(value) {
return value[TARGET] || value;
}
const targetToKeysToCallbacks = new WeakMap();
/**
* Observes a given key on a target with an callback. The callback will be
* called when the given key changes on the target.
*
* @param target the target whose key should be observed
* @param key the key to observe (or Symbol(KEYCHANGES) for key creation
* or deletion)
* @param callback the function to call when the key changes
*/
function observeTargetKey(target, key, callback) {
if (!targetToKeysToCallbacks.get(target)) {
targetToKeysToCallbacks.set(target, new Map());
}
const keyToCallbacks = targetToKeysToCallbacks.get(target);
if (!keyToCallbacks.get(key)) {
keyToCallbacks.set(key, new Set());
}
keyToCallbacks.get(key).add(callback);
if (!callbacksToTargets.has(callback)) {
callbacksToTargets.set(callback, new Set());
}
callbacksToTargets.get(callback).add(target);
}
/**
* Notify Reactives that are observing a given target that a key has changed on
* the target.
*
* @param target target whose Reactives should be notified that the target was
* changed.
* @param key the key that changed (or Symbol `KEYCHANGES` if a key was created
* or deleted)
*/
function notifyReactives(target, key) {
const keyToCallbacks = targetToKeysToCallbacks.get(target);
if (!keyToCallbacks) {
return;
}
const callbacks = keyToCallbacks.get(key);
if (!callbacks) {
return;
}
// Loop on copy because clearReactivesForCallback will modify the set in place
for (const callback of [...callbacks]) {
clearReactivesForCallback(callback);
callback();
}
}
const callbacksToTargets = new WeakMap();
/**
* Clears all subscriptions of the Reactives associated with a given callback.
*
* @param callback the callback for which the reactives need to be cleared
*/
function clearReactivesForCallback(callback) {
const targetsToClear = callbacksToTargets.get(callback);
if (!targetsToClear) {
return;
}
for (const target of targetsToClear) {
const observedKeys = targetToKeysToCallbacks.get(target);
if (!observedKeys) {
continue;
}
for (const callbacks of observedKeys.values()) {
callbacks.delete(callback);
}
}
targetsToClear.clear();
}
const reactiveCache = new WeakMap();
/**
* Creates a reactive proxy for an object. Reading data on the reactive object
* subscribes to changes to the data. Writing data on the object will cause the
* notify callback to be called if there are suscriptions to that data. Nested
* objects and arrays are automatically made reactive as well.
*
* Whenever you are notified of a change, all subscriptions are cleared, and if
* you would like to be notified of any further changes, you should go read
* the underlying data again. We assume that if you don't go read it again after
* being notified, it means that you are no longer interested in that data.
*
* Subscriptions:
* + Reading a property on an object will subscribe you to changes in the value
* of that property.
* + Accessing an object keys (eg with Object.keys or with `for..in`) will
* subscribe you to the creation/deletion of keys. Checking the presence of a
* key on the object with 'in' has the same effect.
* - getOwnPropertyDescriptor does not currently subscribe you to the property.
* This is a choice that was made because changing a key's value will trigger
* this trap and we do not want to subscribe by writes. This also means that
* Object.hasOwnProperty doesn't subscribe as it goes through this trap.
*
* @param target the object for which to create a reactive proxy
* @param callback the function to call when an observed property of the
* reactive has changed
* @returns a proxy that tracks changes to it
*/
function reactive(target, callback = () => { }) {
if (!canBeMadeReactive(target)) {
throw new Error(`Cannot make the given value reactive`);
}
if (SKIP in target) {
return target;
}
const originalTarget = target[TARGET];
if (originalTarget) {
return reactive(originalTarget, callback);
}
if (!reactiveCache.has(target)) {
reactiveCache.set(target, new Map());
}
const reactivesForTarget = reactiveCache.get(target);
if (!reactivesForTarget.has(callback)) {
const proxy = new Proxy(target, {
get(target, key, proxy) {
if (key === TARGET) {
return target;
}
observeTargetKey(target, key, callback);
const value = Reflect.get(target, key, proxy);
if (!canBeMadeReactive(value)) {
return value;
}
return reactive(value, callback);
},
set(target, key, value, proxy) {
const isNewKey = !Object.hasOwnProperty.call(target, key);
const originalValue = Reflect.get(target, key, proxy);
const ret = Reflect.set(target, key, value, proxy);
if (isNewKey) {
notifyReactives(target, KEYCHANGES);
}
// While Array length may trigger the set trap, it's not actually set by this
// method but is updated behind the scenes, and the trap is not called with the
// new value. We disable the "same-value-optimization" for it because of that.
if (originalValue !== value || (Array.isArray(target) && key === "length")) {
notifyReactives(target, key);
}
return ret;
},
deleteProperty(target, key) {
const ret = Reflect.deleteProperty(target, key);
notifyReactives(target, KEYCHANGES);
notifyReactives(target, key);
return ret;
},
ownKeys(target) {
observeTargetKey(target, KEYCHANGES, callback);
return Reflect.ownKeys(target);
},
has(target, key) {
// TODO: this observes all key changes instead of only the presence of the argument key
observeTargetKey(target, KEYCHANGES, callback);
return Reflect.has(target, key);
},
});
reactivesForTarget.set(callback, proxy);
}
return reactivesForTarget.get(callback);
}
/**
* This file contains utility functions that will be injected in each template,
* to perform various useful tasks in the compiled code.
@@ -1510,7 +1714,7 @@
}
function callSlot(ctx, parent, key, name, dynamic, extra, defaultContent) {
key = key + "__slot_" + name;
const slots = (ctx.props && ctx.props.slots) || {};
const slots = ctx.props[TARGET].slots || {};
const { __render, __ctx, __scope } = slots[name] || {};
const slotScope = Object.create(__ctx || {});
if (__scope) {
@@ -1778,8 +1982,7 @@
function makeChildFiber(node, parent) {
let current = node.fiber;
if (current) {
let root = parent.root;
cancelFibers(root, current.children);
cancelFibers(current.children);
current.root = null;
}
return new Fiber(node, parent);
@@ -1788,9 +1991,8 @@
let current = node.fiber;
if (current) {
let root = current.root;
root.counter -= cancelFibers(root, current.children);
root.counter = root.counter + 1 - cancelFibers(current.children);
current.children = [];
root.counter++;
current.bdom = null;
if (fibersInError.has(current)) {
fibersInError.delete(current);
@@ -1811,15 +2013,14 @@
/**
* @returns number of not-yet rendered fibers cancelled
*/
function cancelFibers(root, fibers) {
function cancelFibers(fibers) {
let result = 0;
for (let fiber of fibers) {
fiber.node.fiber = null;
fiber.root = root;
if (!fiber.bdom) {
result++;
}
result += cancelFibers(root, fiber.children);
result += cancelFibers(fiber.children);
}
return result;
}
@@ -1828,9 +2029,11 @@
this.bdom = null;
this.children = [];
this.appliedToDom = false;
this.deep = false;
this.node = node;
this.parent = parent;
if (parent) {
this.deep = parent.deep;
const root = parent.root;
root.counter++;
this.root = root;
@@ -1873,7 +2076,7 @@
}
current = undefined;
// Step 2: patching the dom
node.patch();
node._patch();
this.locked = false;
// Step 4: calling all mounted lifecycle hooks
let mountedFibers = this.mounted;
@@ -1960,6 +2163,39 @@
function useComponent() {
return currentNode.component;
}
// -----------------------------------------------------------------------------
// Integration with reactivity system (useState)
// -----------------------------------------------------------------------------
const batchedRenderFunctions = new WeakMap();
/**
* Creates a reactive object that will be observed by the current component.
* Reading data from the returned object (eg during rendering) will cause the
* component to subscribe to that data and be rerendered when it changes.
*
* @param state the state to observe
* @returns a reactive object that will cause the component to re-render on
* relevant changes
* @see reactive
*/
function useState(state) {
const node = getCurrent();
let render = batchedRenderFunctions.get(node);
if (!render) {
render = batched(node.render.bind(node));
batchedRenderFunctions.set(node, render);
// manual implementation of onWillDestroy to break cyclic dependency
node.willDestroy.push(clearReactivesForCallback.bind(null, render));
}
return reactive(state, render);
}
function arePropsDifferent(props1, props2) {
for (let k in props1) {
if (props1[k] !== props2[k]) {
return true;
}
}
return Object.keys(props1).length !== Object.keys(props2).length;
}
function component(name, props, key, ctx, parent) {
let node = ctx.children[key];
let isDynamic = typeof name !== "string";
@@ -1977,7 +2213,10 @@
}
const parentFiber = ctx.fiber;
if (node) {
node.updateAndRender(props, parentFiber);
const currentProps = node.component.props[TARGET];
if (parentFiber.deep || arePropsDifferent(currentProps, props)) {
node.updateAndRender(props, parentFiber);
}
}
else {
// new component
@@ -1993,8 +2232,7 @@
}
node = new ComponentNode(C, props, ctx.app, ctx);
ctx.children[key] = node;
const fiber = makeChildFiber(node, parentFiber);
node.initiateRender(fiber);
node.initiateRender(new Fiber(node, parentFiber));
}
return node;
}
@@ -2019,6 +2257,7 @@
applyDefaultProps(props, C);
const env = (parent && parent.childEnv) || app.env;
this.childEnv = env;
props = useState(props);
this.component = new C(props, env, this);
this.renderFn = app.getTemplate(C.template).bind(this.component, this.component, this);
this.component.setup();
@@ -2046,20 +2285,29 @@
this._render(fiber);
}
}
async render() {
async render(deep = false) {
let current = this.fiber;
if (current && current.root.locked) {
await Promise.resolve();
// situation may have changed after the microtask tick
current = this.fiber;
}
if (current && !current.bdom && !fibersInError.has(current)) {
return;
if (current) {
if (!current.bdom && !fibersInError.has(current)) {
if (deep) {
// we want the render from this point on to be with deep=true
current.deep = deep;
}
return;
}
// if current rendering was with deep=true, we want this one to be the same
deep = deep || current.deep;
}
if (!this.bdom && !current) {
else if (!this.bdom) {
return;
}
const fiber = makeRootFiber(this);
fiber.deep = deep;
this.fiber = fiber;
this.app.scheduler.addFiber(fiber);
await Promise.resolve();
@@ -2118,6 +2366,9 @@
this.fiber = fiber;
const component = this.component;
applyDefaultProps(props, component.constructor);
currentNode = this;
props = useState(props);
currentNode = null;
const prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props)));
await prom;
if (fiber !== this.fiber) {
@@ -2177,6 +2428,14 @@
this.bdom.moveBefore(other ? other.bdom : null, afterNode);
}
patch() {
if (this.fiber && this.fiber.parent) {
// we only patch here renderings coming from above. renderings initiated
// by the component will be patched independently in the appropriate
// fiber.complete
this._patch();
}
}
_patch() {
const hasChildren = Object.keys(this.children).length > 0;
this.bdom.patch(this.fiber.bdom, hasChildren);
if (hasChildren) {
@@ -2206,53 +2465,86 @@
}
}
function wrapError(fn, hookName) {
const error = new Error(`The following error occurred in ${hookName}: `);
return (...args) => {
try {
const result = fn(...args);
if (result instanceof Promise) {
return result.catch((cause) => {
error.cause = cause;
if (cause instanceof Error) {
error.message += `"${cause.message}"`;
}
throw error;
});
}
return result;
}
catch (cause) {
if (cause instanceof Error) {
error.message += `"${cause.message}"`;
}
throw error;
}
};
}
// -----------------------------------------------------------------------------
// hooks
// -----------------------------------------------------------------------------
function onWillStart(fn) {
const node = getCurrent();
node.willStart.push(fn.bind(node.component));
const decorate = node.app.dev ? wrapError : (fn) => fn;
node.willStart.push(decorate(fn.bind(node.component), "onWillStart"));
}
function onWillUpdateProps(fn) {
const node = getCurrent();
node.willUpdateProps.push(fn.bind(node.component));
const decorate = node.app.dev ? wrapError : (fn) => fn;
node.willUpdateProps.push(decorate(fn.bind(node.component), "onWillUpdateProps"));
}
function onMounted(fn) {
const node = getCurrent();
node.mounted.push(fn.bind(node.component));
const decorate = node.app.dev ? wrapError : (fn) => fn;
node.mounted.push(decorate(fn.bind(node.component), "onMounted"));
}
function onWillPatch(fn) {
const node = getCurrent();
node.willPatch.unshift(fn.bind(node.component));
const decorate = node.app.dev ? wrapError : (fn) => fn;
node.willPatch.unshift(decorate(fn.bind(node.component), "onWillPatch"));
}
function onPatched(fn) {
const node = getCurrent();
node.patched.push(fn.bind(node.component));
const decorate = node.app.dev ? wrapError : (fn) => fn;
node.patched.push(decorate(fn.bind(node.component), "onPatched"));
}
function onWillUnmount(fn) {
const node = getCurrent();
node.willUnmount.unshift(fn.bind(node.component));
const decorate = node.app.dev ? wrapError : (fn) => fn;
node.willUnmount.unshift(decorate(fn.bind(node.component), "onWillUnmount"));
}
function onWillDestroy(fn) {
const node = getCurrent();
node.willDestroy.push(fn.bind(node.component));
const decorate = node.app.dev ? wrapError : (fn) => fn;
node.willDestroy.push(decorate(fn.bind(node.component), "onWillDestroy"));
}
function onWillRender(fn) {
const node = getCurrent();
const renderFn = node.renderFn;
node.renderFn = () => {
const decorate = node.app.dev ? wrapError : (fn) => fn;
node.renderFn = decorate(() => {
fn.call(node.component);
return renderFn();
};
}, "onWillRender");
}
function onRendered(fn) {
const node = getCurrent();
const renderFn = node.renderFn;
node.renderFn = () => {
const decorate = node.app.dev ? wrapError : (fn) => fn;
node.renderFn = decorate(() => {
const result = renderFn();
fn.call(node.component);
return result;
};
}, "onRendered");
}
function onError(callback) {
const node = getCurrent();
@@ -3549,17 +3841,13 @@
slotDef = `{${slotStr.join(", ")}}`;
}
if (slotDef && !(ast.dynamicProps || hasSlotsProp)) {
props.push(`slots: ${slotDef}`);
this.helpers.add("markRaw");
props.push(`slots: markRaw(${slotDef})`);
}
const propStr = `{${props.join(",")}}`;
let propString = propStr;
if (ast.dynamicProps) {
if (!props.length) {
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)})`;
}
else {
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)}, ${propStr})`;
}
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)}${props.length ? ", " + propStr : ""})`;
}
let propVar;
if ((slotDef && (ast.dynamicProps || hasSlotsProp)) || this.dev) {
@@ -3568,7 +3856,8 @@
propString = propVar;
}
if (slotDef && (ast.dynamicProps || hasSlotsProp)) {
this.addLine(`${propVar}.slots = Object.assign(${slotDef}, ${propVar}.slots)`);
this.helpers.add("markRaw");
this.addLine(`${propVar}.slots = markRaw(Object.assign(${slotDef}, ${propVar}.slots))`);
}
// cmap key
const key = this.generateComponentKey();
@@ -3768,6 +4057,9 @@
if (tagName === "t" && !dynamicTag) {
return null;
}
if (tagName.startsWith("block-")) {
throw new Error(`Invalid tag name: '${tagName}'`);
}
ctx = Object.assign({}, ctx);
if (tagName === "pre") {
ctx.inPreTag = true;
@@ -3833,6 +4125,9 @@
ctx.tModelInfo = model;
}
}
else if (attr.startsWith("block-")) {
throw new Error(`Invalid attribute: '${attr}'`);
}
else if (attr !== "t-name") {
if (attr.startsWith("t-") && !attr.startsWith("t-att")) {
throw new Error(`Unknown QWeb directive: '${attr}'`);
@@ -4450,7 +4745,13 @@
if (!(name in this.templates)) {
const rawTemplate = this.rawTemplates[name];
if (rawTemplate === undefined) {
throw new Error(`Missing template: "${name}"`);
let extraInfo = "";
try {
const componentName = getCurrent().component.constructor.name;
extraInfo = ` (for component "${componentName}")`;
}
catch { }
throw new Error(`Missing template: "${name}"${extraInfo}`);
}
const templateFn = this._compileTemplate(name, rawTemplate);
// first add a function to lazily get the template, in case there is a
@@ -4491,8 +4792,8 @@
this.__owl__ = node;
}
setup() { }
render() {
this.__owl__.render();
render(deep = false) {
this.__owl__.render(deep);
}
}
Component.template = "";
@@ -4626,10 +4927,13 @@
// interactions with other code, such as test frameworks that override them
Scheduler.requestAnimationFrame = window.requestAnimationFrame.bind(window);
const DEV_MSG = `Owl is running in 'dev' mode.
const DEV_MSG = () => {
const hash = window.owl ? window.owl.__info__.hash : "master";
return `Owl is running in 'dev' mode.
This is not suitable for production use.
See https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode for more information.`;
See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration for more information.`;
};
class App extends TemplateSet {
constructor(Root, config = {}) {
super(config);
@@ -4640,7 +4944,7 @@ See https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode for mor
this.dev = true;
}
if (this.dev && !config.test) {
console.info(DEV_MSG);
console.info(DEV_MSG());
}
const descrs = Object.getOwnPropertyDescriptors(config.env || {});
this.env = Object.freeze(Object.defineProperties({}, descrs));
@@ -4745,231 +5049,6 @@ See https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode for mor
return true;
}
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
const TARGET = Symbol("Target");
// Escape hatch to prevent reactivity system to turn something into a reactive
const SKIP = Symbol("Skip");
// Special key to subscribe to, to be notified of key creation/deletion
const KEYCHANGES = Symbol("Key changes");
const objectToString = Object.prototype.toString;
/**
* Checks whether a given value can be made into a reactive object.
*
* @param value the value to check
* @returns whether the value can be made reactive
*/
function canBeMadeReactive(value) {
if (typeof value !== "object") {
return false;
}
// extract "RawType" from strings like "[object RawType]" => this lets us
// ignore many native objects such as Promise (whose toString is [object Promise])
// or Date ([object Date]).
const rawType = objectToString.call(value).slice(8, -1);
return rawType === "Object" || rawType === "Array";
}
/**
* Mark an object or array so that it is ignored by the reactivity system
*
* @param value the value to mark
* @returns the object itself
*/
function markRaw(value) {
value[SKIP] = true;
return value;
}
/**
* Given a reactive objet, return the raw (non reactive) underlying object
*
* @param value a reactive value
* @returns the underlying value
*/
function toRaw(value) {
return value[TARGET];
}
const targetToKeysToCallbacks = new WeakMap();
/**
* Observes a given key on a target with an callback. The callback will be
* called when the given key changes on the target.
*
* @param target the target whose key should be observed
* @param key the key to observe (or Symbol(KEYCHANGES) for key creation
* or deletion)
* @param callback the function to call when the key changes
*/
function observeTargetKey(target, key, callback) {
if (!targetToKeysToCallbacks.get(target)) {
targetToKeysToCallbacks.set(target, new Map());
}
const keyToCallbacks = targetToKeysToCallbacks.get(target);
if (!keyToCallbacks.get(key)) {
keyToCallbacks.set(key, new Set());
}
keyToCallbacks.get(key).add(callback);
if (!callbacksToTargets.has(callback)) {
callbacksToTargets.set(callback, new Set());
}
callbacksToTargets.get(callback).add(target);
}
/**
* Notify Reactives that are observing a given target that a key has changed on
* the target.
*
* @param target target whose Reactives should be notified that the target was
* changed.
* @param key the key that changed (or Symbol `KEYCHANGES` if a key was created
* or deleted)
*/
function notifyReactives(target, key) {
const keyToCallbacks = targetToKeysToCallbacks.get(target);
if (!keyToCallbacks) {
return;
}
const callbacks = keyToCallbacks.get(key);
if (!callbacks) {
return;
}
// Loop on copy because clearReactivesForCallback will modify the set in place
for (const callback of [...callbacks]) {
clearReactivesForCallback(callback);
callback();
}
}
const callbacksToTargets = new WeakMap();
/**
* Clears all subscriptions of the Reactives associated with a given callback.
*
* @param callback the callback for which the reactives need to be cleared
*/
function clearReactivesForCallback(callback) {
const targetsToClear = callbacksToTargets.get(callback);
if (!targetsToClear) {
return;
}
for (const target of targetsToClear) {
const observedKeys = targetToKeysToCallbacks.get(target);
if (!observedKeys) {
continue;
}
for (const callbacks of observedKeys.values()) {
callbacks.delete(callback);
}
}
targetsToClear.clear();
}
const reactiveCache = new WeakMap();
/**
* Creates a reactive proxy for an object. Reading data on the reactive object
* subscribes to changes to the data. Writing data on the object will cause the
* notify callback to be called if there are suscriptions to that data. Nested
* objects and arrays are automatically made reactive as well.
*
* Whenever you are notified of a change, all subscriptions are cleared, and if
* you would like to be notified of any further changes, you should go read
* the underlying data again. We assume that if you don't go read it again after
* being notified, it means that you are no longer interested in that data.
*
* Subscriptions:
* + Reading a property on an object will subscribe you to changes in the value
* of that property.
* + Accessing an object keys (eg with Object.keys or with `for..in`) will
* subscribe you to the creation/deletion of keys. Checking the presence of a
* key on the object with 'in' has the same effect.
* - getOwnPropertyDescriptor does not currently subscribe you to the property.
* This is a choice that was made because changing a key's value will trigger
* this trap and we do not want to subscribe by writes. This also means that
* Object.hasOwnProperty doesn't subscribe as it goes through this trap.
*
* @param target the object for which to create a reactive proxy
* @param callback the function to call when an observed property of the
* reactive has changed
* @returns a proxy that tracks changes to it
*/
function reactive(target, callback = () => { }) {
if (!canBeMadeReactive(target)) {
throw new Error(`Cannot make the given value reactive`);
}
if (SKIP in target) {
return target;
}
const originalTarget = target[TARGET];
if (originalTarget) {
return reactive(originalTarget, callback);
}
if (!reactiveCache.has(target)) {
reactiveCache.set(target, new Map());
}
const reactivesForTarget = reactiveCache.get(target);
if (!reactivesForTarget.has(callback)) {
const proxy = new Proxy(target, {
get(target, key, proxy) {
if (key === TARGET) {
return target;
}
observeTargetKey(target, key, callback);
const value = Reflect.get(target, key, proxy);
if (!canBeMadeReactive(value)) {
return value;
}
return reactive(value, callback);
},
set(target, key, value, proxy) {
const isNewKey = !Object.hasOwnProperty.call(target, key);
const originalValue = Reflect.get(target, key, proxy);
const ret = Reflect.set(target, key, value, proxy);
if (isNewKey) {
notifyReactives(target, KEYCHANGES);
}
// While Array length may trigger the set trap, it's not actually set by this
// method but is updated behind the scenes, and the trap is not called with the
// new value. We disable the "same-value-optimization" for it because of that.
if (originalValue !== value || (Array.isArray(target) && key === "length")) {
notifyReactives(target, key);
}
return ret;
},
deleteProperty(target, key) {
const ret = Reflect.deleteProperty(target, key);
notifyReactives(target, KEYCHANGES);
notifyReactives(target, key);
return ret;
},
ownKeys(target) {
observeTargetKey(target, KEYCHANGES, callback);
return Reflect.ownKeys(target);
},
has(target, key) {
// TODO: this observes all key changes instead of only the presence of the argument key
observeTargetKey(target, KEYCHANGES, callback);
return Reflect.has(target, key);
},
});
reactivesForTarget.set(callback, proxy);
}
return reactivesForTarget.get(callback);
}
const batchedRenderFunctions = new WeakMap();
/**
* Creates a reactive object that will be observed by the current component.
* Reading data from the returned object (eg during rendering) will cause the
* component to subscribe to that data and be rerendered when it changes.
*
* @param state the state to observe
* @returns a reactive object that will cause the component to re-render on
* relevant changes
* @see reactive
*/
function useState(state) {
const node = getCurrent();
if (!batchedRenderFunctions.has(node)) {
batchedRenderFunctions.set(node, batched(() => node.render()));
onWillDestroy(() => clearReactivesForCallback(render));
}
const render = batchedRenderFunctions.get(node);
const reactiveState = reactive(state, render);
return reactiveState;
}
// -----------------------------------------------------------------------------
// useRef
// -----------------------------------------------------------------------------
@@ -5075,6 +5154,7 @@ See https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode for mor
config.shouldNormalizeDom = false;
config.mainEventHandler = mainEventHandler;
UTILS.Portal = Portal;
UTILS.markRaw = markRaw;
const blockDom = {
config,
// bdom entry points
@@ -5129,9 +5209,9 @@ See https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode for mor
Object.defineProperty(exports, '__esModule', { value: true });
__info__.version = '2.0.0-alpha.2';
__info__.date = '2022-02-14T12:42:57.502Z';
__info__.hash = '4a922ed';
__info__.version = '2.0.0-alpha.3';
__info__.date = '2022-02-25T09:57:47.473Z';
__info__.hash = '076b0d7';
__info__.url = 'https://github.com/odoo/owl';