[IMP] owl: update lib + update samples

This commit is contained in:
Géry Debongnie
2019-04-17 11:44:35 +02:00
parent 83a5baf9a9
commit 8156a9ef6f
3 changed files with 174 additions and 184 deletions
+153 -160
View File
@@ -662,6 +662,138 @@
}
}
//------------------------------------------------------------------------------
// Observer
//------------------------------------------------------------------------------
const methodsToPatch = [
"push",
"pop",
"shift",
"unshift",
"splice",
"sort",
"reverse"
];
const ArrayProto = Array.prototype;
const ModifiedArrayProto = Object.create(ArrayProto);
for (let method of methodsToPatch) {
const initialMethod = ArrayProto[method];
ModifiedArrayProto[method] = function (...args) {
this.__observer__.notifyChange();
this.__owl__.rev++;
let parent = this;
do {
parent.__owl__.deepRev++;
} while ((parent = parent.__owl__.parent));
let inserted;
switch (method) {
case "push":
case "unshift":
inserted = args;
break;
case "splice":
inserted = args.slice(2);
break;
}
if (inserted) {
for (let elem of inserted) {
this.__observer__.observe(elem, this);
}
}
return initialMethod.call(this, ...args);
};
}
class Observer {
constructor() {
this.rev = 1;
this.allowMutations = true;
this.dirty = false;
}
notifyCB() { }
notifyChange() {
this.rev++;
this.dirty = true;
Promise.resolve().then(() => {
if (this.dirty) {
this.dirty = false;
this.notifyCB();
}
});
}
observe(value, parent) {
if (value === null) {
// fun fact: typeof null === 'object'
return;
}
if (typeof value !== "object") {
return;
}
if ("__owl__" in value) {
// already observed
return;
}
if (Array.isArray(value)) {
this._observeArr(value, parent);
}
else {
this._observeObj(value, parent);
}
}
set(target, key, value) {
this._addProp(target, key, value);
target.__owl__.rev++;
this.notifyChange();
}
unobserve(target) {
if (target !== null && typeof target === "object") {
delete target.__owl__;
}
}
_observeObj(obj, parent) {
const keys = Object.keys(obj);
obj.__owl__ = { rev: 1, deepRev: 1, parent };
Object.defineProperty(obj, "__owl__", { enumerable: false });
for (let key of keys) {
this._addProp(obj, key, obj[key]);
}
}
_observeArr(arr, parent) {
arr.__owl__ = { rev: 1, deepRev: 1, parent };
Object.defineProperty(arr, "__owl__", { enumerable: false });
arr.__proto__ = Object.create(ModifiedArrayProto);
arr.__proto__.__observer__ = this;
for (let i = 0; i < arr.length; i++) {
this.observe(arr[i], arr);
}
}
_addProp(obj, key, value) {
var self = this;
Object.defineProperty(obj, key, {
enumerable: true,
get() {
return value;
},
set(newVal) {
if (!self.allowMutations) {
throw new Error(`State cannot be changed outside a mutation! (key: "${key}", val: "${newVal}")`);
}
if (newVal !== value) {
self.unobserve(value);
value = newVal;
self.observe(newVal, obj);
obj.__owl__.rev++;
let parent = obj;
do {
parent.__owl__.deepRev++;
} while ((parent = parent.__owl__.parent));
self.notifyChange();
}
}
});
this.observe(value, obj);
}
}
function escape(str) {
if (str === undefined) {
return "";
@@ -874,7 +1006,8 @@
renderId: 1,
renderPromise: null,
renderProps: props || null,
boundHandlers: {}
boundHandlers: {},
observer: new Observer()
};
}
get el() {
@@ -920,9 +1053,6 @@
* It is not called on the initial render. This is useful to get some
* information which are in the DOM. For example, the current position of the
* scrollbar
*
* Note that at this point, it is not safe to rerender the widget. In
* particular, updateState calls should be avoided.
*/
willPatch() { }
/**
@@ -977,6 +1107,7 @@
}
this._patch(vnode);
target.appendChild(this.el);
this._observeState();
if (document.body.contains(target)) {
this._visitSubTree(w => {
if (!w.__owl__.isMounted && this.el.contains(w.el)) {
@@ -1056,31 +1187,11 @@
if (this.__owl__.isMounted) {
await this.render(true);
}
this.patched();
}
async updateProps(nextProps, forceUpdate = false) {
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
return shouldUpdate ? this._updateProps(nextProps) : Promise.resolve();
}
/**
* This is the safest update method for widget: its job is to update the state
* and rerender (if widget is mounted).
*
* Notes:
* - it checks if we do not add extra keys to the state.
* - it is ok to call updateState before the widget is started. In that
* case, it will simply update the state and will not rerender
*/
async updateState(nextState) {
if (Object.keys(nextState).length === 0) {
return;
}
Object.assign(this.state, nextState);
if (this.__owl__.isStarted) {
await this.render();
}
this.patched();
}
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
@@ -1088,13 +1199,13 @@
await this.willUpdateProps(nextProps);
this.props = nextProps;
await this.render();
this.patched();
}
_patch(vnode) {
this.__owl__.renderPromise = null;
if (this.__owl__.vnode) {
this.willPatch();
this.__owl__.vnode = patch$1(this.__owl__.vnode, vnode);
this.patched();
}
else {
this.__owl__.vnode = patch$1(document.createElement(vnode.sel), vnode);
@@ -1145,6 +1256,7 @@
if (this.__owl__.isMounted) {
return;
}
this._observeState();
if (this.__owl__.parent) {
if (this.__owl__.parent.__owl__.isMounted) {
this.__owl__.isMounted = true;
@@ -1165,6 +1277,12 @@
}
}
}
_observeState() {
if (Object.keys(this.state).length) {
this.__owl__.observer.observe(this.state);
this.__owl__.observer.notifyCB = this.render.bind(this);
}
}
}
const RESERVED_WORDS = "true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,typeof,eval,void,Math,RegExp,Array,Object,Date".split(",");
@@ -1938,6 +2056,9 @@
ctx.addLine(`context.${name} = _${keysID}[i];`);
ctx.addLine(`context.${name}_value = _${valuesID}[i];`);
const nodeCopy = node.cloneNode(true);
if (nodeCopy.tagName !== "t" && !nodeCopy.hasAttribute("t-key")) {
console.warn(`Directive t-foreach should always be used with a t-key! (in template: '${ctx.templateName}')`);
}
nodeCopy.removeAttribute("t-foreach");
qweb._compileNode(nodeCopy, ctx);
ctx.dedent();
@@ -2066,142 +2187,24 @@
}
};
const methodsToPatch = [
"push",
"pop",
"shift",
"unshift",
"splice",
"sort",
"reverse"
];
const ArrayProto = Array.prototype;
const ModifiedArrayProto = Object.create(ArrayProto);
for (let method of methodsToPatch) {
const initialMethod = ArrayProto[method];
ModifiedArrayProto[method] = function (...args) {
this.__observer__.rev++;
this.__owl__.rev++;
let parent = this;
do {
parent.__owl__.deepRev++;
} while ((parent = parent.__owl__.parent));
let inserted;
switch (method) {
case "push":
case "unshift":
inserted = args;
break;
case "splice":
inserted = args.slice(2);
break;
}
if (inserted) {
for (let elem of inserted) {
this.__observer__.observe(elem, this);
}
}
return initialMethod.call(this, ...args);
};
}
function makeObserver() {
const observer = {
rev: 1,
allowMutations: true,
observe: observe,
set: set
};
function set(target, key, value) {
addProp(target, key, value);
target.__owl__.rev++;
observer.rev++;
}
function addProp(obj, key, value) {
Object.defineProperty(obj, key, {
enumerable: true,
get() {
return value;
},
set(newVal) {
if (!observer.allowMutations) {
throw new Error(`State cannot be changed outside a mutation! (key: "${key}", val: "${newVal}")`);
}
if (newVal !== value) {
unobserve(value);
value = newVal;
observe(newVal, obj);
obj.__owl__.rev++;
observer.rev++;
let parent = obj;
do {
parent.__owl__.deepRev++;
} while ((parent = parent.__owl__.parent));
}
}
});
observe(value, obj);
}
function observeObj(obj, parent) {
const keys = Object.keys(obj);
obj.__owl__ = { rev: 1, deepRev: 1, parent };
Object.defineProperty(obj, "__owl__", { enumerable: false });
for (let key of keys) {
addProp(obj, key, obj[key]);
}
}
function observeArr(arr, parent) {
arr.__owl__ = { rev: 1, deepRev: 1, parent };
Object.defineProperty(arr, "__owl__", { enumerable: false });
arr.__proto__ = Object.create(ModifiedArrayProto);
arr.__proto__.__observer__ = observer;
for (let i = 0; i < arr.length; i++) {
observe(arr[i], arr);
}
}
function observe(value, parent) {
if (value === null) {
// fun fact: typeof null === 'object'
return;
}
if (typeof value !== "object") {
return;
}
if ("__owl__" in value) {
// already observed
return;
}
if (Array.isArray(value)) {
observeArr(value, parent);
}
else {
observeObj(value, parent);
}
}
function unobserve(target) {
if (target !== null && typeof target === "object") {
delete target.__owl__;
}
}
return observer;
}
class Store extends EventBus {
constructor(config, options = {}) {
super();
this._commitLevel = 0;
this._isMutating = false;
this.history = [];
this.debug = options.debug || false;
this.state = config.state || {};
this.actions = config.actions;
this.mutations = config.mutations;
this.env = config.env;
this.observer = makeObserver();
this.observer = new Observer();
this.observer.notifyCB = this.trigger.bind(this, "update");
this.observer.allowMutations = false;
this.observer.observe(this.state);
if (this.debug) {
this.history.push({ state: this.state });
}
this.set = this.observer.set.bind(this.observer);
}
dispatch(action, payload) {
if (!this.actions[action]) {
@@ -2225,13 +2228,11 @@
throw new Error(`[Error] mutation ${type} is undefined`);
}
this._commitLevel++;
const currentRev = this.observer.rev;
this._isMutating = true;
this.observer.allowMutations = true;
const res = this.mutations[type].call(null, {
commit: this.commit.bind(this),
state: this.state,
set: this.observer.set
set: this.set
}, payload);
if (this._commitLevel === 1) {
this.observer.allowMutations = false;
@@ -2242,14 +2243,6 @@
payload: payload
});
}
Promise.resolve().then(() => {
if (this._isMutating) {
this._isMutating = false;
if (currentRev !== this.observer.rev) {
this.trigger("update", this.state);
}
}
});
}
this._commitLevel--;
return res;
@@ -2365,9 +2358,9 @@
exports.connect = connect;
exports.Store = Store;
exports._version = '0.6.0';
exports._date = '2019-04-16T15:27:25.412Z';
exports._hash = 'e19540f';
exports._version = '0.7.0';
exports._date = '2019-04-17T09:08:09.148Z';
exports._hash = '4807389';
exports._url = 'https://github.com/odoo/owl';
}(this.owl = this.owl || {}));
+13 -12
View File
@@ -76,7 +76,7 @@ class TabbedEditor extends Component {
const mode = MODES[tab];
this.editor.session.setMode(mode);
this.updateState({ currentTab: tab });
this.state.currentTab = tab;
}
onMouseDown(ev) {
@@ -105,7 +105,7 @@ const TEMPLATE = `
<div class="menubar">
<a class="btn run-code" t-on-click="runCode">▶ Run</a>
<select t-on-change="setSample">
<option t-foreach="SAMPLES" t-as="sample">
<option t-foreach="SAMPLES" t-as="sample" t-key="sample_index">
<t t-esc="sample.description"/>
</option>
</select>
@@ -165,9 +165,12 @@ class App extends Component {
error = e;
}
await this.updateState({ error, displayWelcome: false });
this.state.error = error;
this.state.displayWelcome = false;
if (error) {
this.refs.content.innerHTML = "";
setTimeout(() => {
this.refs.content.innerHTML = "";
});
return;
}
@@ -202,11 +205,9 @@ class App extends Component {
setSample(ev) {
const sample = SAMPLES.find(s => s.description === ev.target.value);
this.updateState({
js: sample.code,
css: sample.css || "",
xml: sample.xml || DEFAULT_XML
});
this.state.js = sample.code;
this.state.css = sample.css || "";
this.state.xml = sample.xml || DEFAULT_XML;
}
get leftPaneStyle() {
@@ -223,7 +224,7 @@ class App extends Component {
onMouseDown() {
const resizer = ev => {
this.updateState({ leftPaneWidth: ev.clientX });
this.state.leftPaneWidth = ev.clientX;
};
document.body.addEventListener("mousemove", resizer);
@@ -242,7 +243,7 @@ class App extends Component {
this.state[ev.type] = ev.value;
}
toggleLayout() {
this.updateState({ splitLayout: !this.state.splitLayout });
this.state.splitLayout = !this.state.splitLayout;
}
updatePanelHeight(ev) {
if (!ev.delta) {
@@ -252,7 +253,7 @@ class App extends Component {
if (!height) {
height = document.getElementsByClassName("tabbed-editor")[0].clientHeight;
}
this.updateState({ topPanelHeight: height + ev.delta });
this.state.topPanelHeight = height + ev.delta;
}
}
+8 -12
View File
@@ -49,7 +49,7 @@ HelloWorld.prototype = Object.create(Component.prototype);
// we show here how to add methods to sub components
HelloWorld.prototype.changeGreeting = function() {
var newGreeting = this.state.greeting === "Hello" ? "Hi" : "Hello";
this.updateState({ greeting: newGreeting });
this.state.greeting = newGreeting;
};
const qweb = new QWeb(TEMPLATES);
@@ -73,7 +73,7 @@ const WIDGET_COMPOSITION = `class Counter extends owl.Component {
}
increment(delta) {
this.updateState({ value: this.state.value + delta });
this.state.value += delta;
}
}
@@ -133,7 +133,7 @@ class HookWidget extends Component {
console.log("willUnmount");
}
increment() {
this.updateState({ n: this.state.n + 1 });
this.state.n++;
}
}
@@ -145,10 +145,10 @@ class ParentWidget extends Component {
this.state = { n: 0, flag: true };
}
increment() {
this.updateState({ n: this.state.n + 1 });
this.state.n++;
}
toggleSubWidget() {
this.updateState({ flag: !this.state.flag });
this.state.flag = !this.state.flag;
}
}
@@ -206,7 +206,7 @@ class Counter extends owl.Component {
}
increment(delta) {
this.updateState({ counter: this.state.counter + delta });
this.state.counter += delta;
}
}
@@ -241,16 +241,12 @@ class App extends owl.Component {
}
setMessageCount(n) {
this.updateState({
messages: messages.slice(0, n)
});
this.state.messages = messages.slice(0,n);
}
removeMessage(data) {
const index = messages.findIndex(m => m.id === data.id);
const n = this.state.messages.length;
messages.splice(index, 1);
this.updateState({ messages: messages.slice(0, n - 1) });
this.state.messages.splice(index, 1);
}
increment(delta) {