mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] owl: update to v0.14.0
This commit is contained in:
@@ -210,14 +210,7 @@
|
|||||||
*/
|
*/
|
||||||
function vnode(sel, data, children, text, elm) {
|
function vnode(sel, data, children, text, elm) {
|
||||||
let key = data === undefined ? undefined : data.key;
|
let key = data === undefined ? undefined : data.key;
|
||||||
return {
|
return { sel, data, children, text, elm, key };
|
||||||
sel: sel,
|
|
||||||
data: data,
|
|
||||||
children: children,
|
|
||||||
text: text,
|
|
||||||
elm: elm,
|
|
||||||
key: key
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// snabbdom.ts
|
// snabbdom.ts
|
||||||
@@ -885,7 +878,7 @@
|
|||||||
"(": "LEFT_PAREN",
|
"(": "LEFT_PAREN",
|
||||||
")": "RIGHT_PAREN"
|
")": "RIGHT_PAREN"
|
||||||
};
|
};
|
||||||
const OPERATORS = [".", "===", "==", "+", "!", "||", "&&", ">", "?", "-", "*"];
|
const OPERATORS = ".,===,==,+,!,||,&&,>=,>,<=,<,?,-,*,/,%".split(',');
|
||||||
let tokenizeString = function (expr) {
|
let tokenizeString = function (expr) {
|
||||||
let s = expr[0];
|
let s = expr[0];
|
||||||
let start = s;
|
let start = s;
|
||||||
@@ -997,7 +990,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (expr.length) {
|
if (expr.length) {
|
||||||
throw new Error("Tokenizer error...");
|
throw new Error(`Tokenizer error: could not tokenize "${expr}"`);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1125,6 +1118,10 @@
|
|||||||
// use case is that component's templates are qweb dependant, and need to be
|
// use case is that component's templates are qweb dependant, and need to be
|
||||||
// able to map a qweb instance to a template name.
|
// able to map a qweb instance to a template name.
|
||||||
this.id = nextID++;
|
this.id = nextID++;
|
||||||
|
// slots contains sub templates defined with t-set inside t-widget nodes, and
|
||||||
|
// are meant to be used by the t-slot directive.
|
||||||
|
this.slots = {};
|
||||||
|
this.nextSlotId = 1;
|
||||||
if (data) {
|
if (data) {
|
||||||
this.addTemplates(data);
|
this.addTemplates(data);
|
||||||
}
|
}
|
||||||
@@ -1288,13 +1285,16 @@
|
|||||||
if (ctx.parentNode) {
|
if (ctx.parentNode) {
|
||||||
ctx.addLine(`c${ctx.parentNode}.push({text: \`${text}\`});`);
|
ctx.addLine(`c${ctx.parentNode}.push({text: \`${text}\`});`);
|
||||||
}
|
}
|
||||||
|
else if (ctx.parentTextNode) {
|
||||||
|
ctx.addLine(`vn${ctx.parentTextNode}.text += \`${text}\`;`);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// this is an unusual situation: this text node is the result of the
|
// this is an unusual situation: this text node is the result of the
|
||||||
// template rendering.
|
// template rendering.
|
||||||
let nodeID = ctx.generateID();
|
let nodeID = ctx.generateID();
|
||||||
ctx.addLine(`var vn${nodeID} = {text: \`${text}\`};`);
|
ctx.addLine(`var vn${nodeID} = {text: \`${text}\`};`);
|
||||||
ctx.rootContext.rootNode = nodeID;
|
ctx.rootContext.rootNode = nodeID;
|
||||||
ctx.rootContext.parentNode = nodeID;
|
ctx.rootContext.parentTextNode = nodeID;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1543,6 +1543,7 @@
|
|||||||
this.variables = {};
|
this.variables = {};
|
||||||
this.escaping = false;
|
this.escaping = false;
|
||||||
this.parentNode = null;
|
this.parentNode = null;
|
||||||
|
this.parentTextNode = null;
|
||||||
this.rootNode = null;
|
this.rootNode = null;
|
||||||
this.indentLevel = 0;
|
this.indentLevel = 0;
|
||||||
this.shouldDefineOwner = false;
|
this.shouldDefineOwner = false;
|
||||||
@@ -1560,7 +1561,7 @@
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
withParent(node) {
|
withParent(node) {
|
||||||
if (this === this.rootContext && this.parentNode) {
|
if (this === this.rootContext && (this.parentNode || this.parentTextNode)) {
|
||||||
throw new Error("A template should not have more than one root node");
|
throw new Error("A template should not have more than one root node");
|
||||||
}
|
}
|
||||||
if (!this.rootContext.rootNode) {
|
if (!this.rootContext.rootNode) {
|
||||||
@@ -2268,11 +2269,19 @@
|
|||||||
exprID = value.id;
|
exprID = value.id;
|
||||||
}
|
}
|
||||||
ctx.addIf(`${exprID} || ${exprID} === 0`);
|
ctx.addIf(`${exprID} || ${exprID} === 0`);
|
||||||
if (!ctx.parentNode) {
|
|
||||||
throw new Error("Should not have a text node without a parent");
|
|
||||||
}
|
|
||||||
if (ctx.escaping) {
|
if (ctx.escaping) {
|
||||||
ctx.addLine(`c${ctx.parentNode}.push({text: ${exprID}});`);
|
if (ctx.parentTextNode) {
|
||||||
|
ctx.addLine(`vn${ctx.parentTextNode}.text += ${exprID};`);
|
||||||
|
}
|
||||||
|
else if (ctx.parentNode) {
|
||||||
|
ctx.addLine(`c${ctx.parentNode}.push({text: ${exprID}});`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let nodeID = ctx.generateID();
|
||||||
|
ctx.rootContext.rootNode = nodeID;
|
||||||
|
ctx.rootContext.parentTextNode = nodeID;
|
||||||
|
ctx.addLine(`var vn${nodeID} = {text: ${exprID}};`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let fragID = ctx.generateID();
|
let fragID = ctx.generateID();
|
||||||
@@ -2524,6 +2533,7 @@
|
|||||||
* - t-transition
|
* - t-transition
|
||||||
* - t-widget/t-keepalive
|
* - t-widget/t-keepalive
|
||||||
* - t-mounted
|
* - t-mounted
|
||||||
|
* - t-slot
|
||||||
*/
|
*/
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// t-on
|
// t-on
|
||||||
@@ -2595,7 +2605,15 @@
|
|||||||
UTILS.nextFrame = function (cb) {
|
UTILS.nextFrame = function (cb) {
|
||||||
requestAnimationFrame(() => requestAnimationFrame(cb));
|
requestAnimationFrame(() => requestAnimationFrame(cb));
|
||||||
};
|
};
|
||||||
UTILS.transitionInsert = function (elm, name) {
|
UTILS.transitionInsert = function (vn, name) {
|
||||||
|
const elm = vn.elm;
|
||||||
|
// remove potential duplicated vnode that is currently being removed, to
|
||||||
|
// prevent from having twice the same node in the DOM during an animation
|
||||||
|
const dup = elm.parentElement &&
|
||||||
|
elm.parentElement.querySelector(`*[data-owl-key='${vn.key}']`);
|
||||||
|
if (dup) {
|
||||||
|
dup.remove();
|
||||||
|
}
|
||||||
elm.classList.add(name + "-enter");
|
elm.classList.add(name + "-enter");
|
||||||
elm.classList.add(name + "-enter-active");
|
elm.classList.add(name + "-enter-active");
|
||||||
const finalize = () => {
|
const finalize = () => {
|
||||||
@@ -2608,7 +2626,9 @@
|
|||||||
whenTransitionEnd(elm, finalize);
|
whenTransitionEnd(elm, finalize);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
UTILS.transitionRemove = function (elm, name, rm) {
|
UTILS.transitionRemove = function (vn, name, rm) {
|
||||||
|
const elm = vn.elm;
|
||||||
|
elm.setAttribute("data-owl-key", vn.key);
|
||||||
elm.classList.add(name + "-leave");
|
elm.classList.add(name + "-leave");
|
||||||
elm.classList.add(name + "-leave-active");
|
elm.classList.add(name + "-leave-active");
|
||||||
const finalize = () => {
|
const finalize = () => {
|
||||||
@@ -2656,8 +2676,8 @@
|
|||||||
atNodeCreation({ value, addNodeHook }) {
|
atNodeCreation({ value, addNodeHook }) {
|
||||||
let name = value;
|
let name = value;
|
||||||
const hooks = {
|
const hooks = {
|
||||||
insert: `this.utils.transitionInsert(vn.elm, '${name}');`,
|
insert: `this.utils.transitionInsert(vn, '${name}');`,
|
||||||
remove: `this.utils.transitionRemove(vn.elm, '${name}', rm);`
|
remove: `this.utils.transitionRemove(vn, '${name}', rm);`
|
||||||
};
|
};
|
||||||
for (let hookName in hooks) {
|
for (let hookName in hooks) {
|
||||||
addNodeHook(hookName, hooks[hookName]);
|
addNodeHook(hookName, hooks[hookName]);
|
||||||
@@ -2780,7 +2800,7 @@
|
|||||||
* let nvn = w4._mount(vnode, vn.elm);
|
* let nvn = w4._mount(vnode, vn.elm);
|
||||||
* pvnode.elm = nvn.elm;
|
* pvnode.elm = nvn.elm;
|
||||||
* // what follows is only present if there are animations on the widget
|
* // what follows is only present if there are animations on the widget
|
||||||
* utils.transitionInsert(vn.elm, "fade");
|
* utils.transitionInsert(vn, "fade");
|
||||||
* },
|
* },
|
||||||
* remove() {
|
* remove() {
|
||||||
* // override with empty function to prevent from removing the node
|
* // override with empty function to prevent from removing the node
|
||||||
@@ -2793,7 +2813,7 @@
|
|||||||
* let finalize = () => {
|
* let finalize = () => {
|
||||||
* w4.destroy();
|
* w4.destroy();
|
||||||
* };
|
* };
|
||||||
* utils.transitionRemove(vn.elm, "fade", finalize);
|
* utils.transitionRemove(vn, "fade", finalize);
|
||||||
* }
|
* }
|
||||||
* };
|
* };
|
||||||
* // the pvnode is inserted at the correct position in the div's children
|
* // the pvnode is inserted at the correct position in the div's children
|
||||||
@@ -2834,7 +2854,7 @@
|
|||||||
name: "widget",
|
name: "widget",
|
||||||
extraNames: ["props", "keepalive"],
|
extraNames: ["props", "keepalive"],
|
||||||
priority: 100,
|
priority: 100,
|
||||||
atNodeEncounter({ ctx, value, node }) {
|
atNodeEncounter({ ctx, value, node, qweb }) {
|
||||||
ctx.addLine("//WIDGET");
|
ctx.addLine("//WIDGET");
|
||||||
ctx.rootContext.shouldDefineOwner = true;
|
ctx.rootContext.shouldDefineOwner = true;
|
||||||
ctx.rootContext.shouldDefineQWeb = true;
|
ctx.rootContext.shouldDefineQWeb = true;
|
||||||
@@ -2902,7 +2922,7 @@
|
|||||||
}
|
}
|
||||||
let transitionsInsertCode = "";
|
let transitionsInsertCode = "";
|
||||||
if (transition) {
|
if (transition) {
|
||||||
transitionsInsertCode = `utils.transitionInsert(vn.elm, '${transition}');`;
|
transitionsInsertCode = `utils.transitionInsert(vn, '${transition}');`;
|
||||||
}
|
}
|
||||||
let finalizeWidgetCode = `w${widgetID}.${keepAlive ? "unmount" : "destroy"}();`;
|
let finalizeWidgetCode = `w${widgetID}.${keepAlive ? "unmount" : "destroy"}();`;
|
||||||
if (ref && !keepAlive) {
|
if (ref && !keepAlive) {
|
||||||
@@ -2912,7 +2932,7 @@
|
|||||||
finalizeWidgetCode = `let finalize = () => {
|
finalizeWidgetCode = `let finalize = () => {
|
||||||
${finalizeWidgetCode}
|
${finalizeWidgetCode}
|
||||||
};
|
};
|
||||||
utils.transitionRemove(vn.elm, '${transition}', finalize);`;
|
utils.transitionRemove(vn, '${transition}', finalize);`;
|
||||||
}
|
}
|
||||||
let createHook = "";
|
let createHook = "";
|
||||||
let classAttr = node.getAttribute("class");
|
let classAttr = node.getAttribute("class");
|
||||||
@@ -2987,6 +3007,19 @@
|
|||||||
ctx.addLine(`if (!W${widgetID}) {throw new Error('Cannot find the definition of widget "' + widgetKey${widgetID} + '"')}`);
|
ctx.addLine(`if (!W${widgetID}) {throw new Error('Cannot find the definition of widget "' + widgetKey${widgetID} + '"')}`);
|
||||||
ctx.addLine(`w${widgetID} = new W${widgetID}(owner, props${widgetID});`);
|
ctx.addLine(`w${widgetID} = new W${widgetID}(owner, props${widgetID});`);
|
||||||
ctx.addLine(`context.__owl__.cmap[${templateID}] = w${widgetID}.__owl__.id;`);
|
ctx.addLine(`context.__owl__.cmap[${templateID}] = w${widgetID}.__owl__.id;`);
|
||||||
|
// SLOTS
|
||||||
|
const slotNodes = node.querySelectorAll("[t-set]");
|
||||||
|
if (slotNodes.length) {
|
||||||
|
const slotId = qweb.nextSlotId++;
|
||||||
|
for (let i = 0, length = slotNodes.length; i < length; i++) {
|
||||||
|
const slotNode = slotNodes[i];
|
||||||
|
const key = slotNode.getAttribute("t-set");
|
||||||
|
slotNode.removeAttribute("t-set");
|
||||||
|
const slotFn = qweb._compile(`slot_${key}_template`, slotNode);
|
||||||
|
qweb.slots[`${slotId}_${key}`] = slotFn.bind(qweb);
|
||||||
|
}
|
||||||
|
ctx.addLine(`w${widgetID}.__owl__.slotId = ${slotId};`);
|
||||||
|
}
|
||||||
ctx.addLine(`def${defID} = w${widgetID}._prepare();`);
|
ctx.addLine(`def${defID} = w${widgetID}._prepare();`);
|
||||||
// hack: specify empty remove hook to prevent the node from being removed from the DOM
|
// hack: specify empty remove hook to prevent the node from being removed from the DOM
|
||||||
// FIXME: click to re-add widget during remove transition -> leak
|
// FIXME: click to re-add widget during remove transition -> leak
|
||||||
@@ -3036,6 +3069,19 @@
|
|||||||
addNodeHook("insert", `if (context.__owl__.isMounted) { extra.mountedHandlers[${nodeID}](); }`);
|
addNodeHook("insert", `if (context.__owl__.isMounted) { extra.mountedHandlers[${nodeID}](); }`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// t-slot
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
QWeb.addDirective({
|
||||||
|
name: "slot",
|
||||||
|
priority: 80,
|
||||||
|
atNodeEncounter({ ctx, value }) {
|
||||||
|
const slotKey = ctx.generateID();
|
||||||
|
ctx.addLine(`const slot${slotKey} = this.slots[context.__owl__.slotId + '_' + '${value}'];`);
|
||||||
|
ctx.addLine(`c${ctx.parentNode}.push(slot${slotKey}(context.__owl__.parent, extra));`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
class Store extends EventBus {
|
class Store extends EventBus {
|
||||||
constructor(config, options = {}) {
|
constructor(config, options = {}) {
|
||||||
@@ -3122,8 +3168,9 @@
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
let nextID$1 = 1;
|
let nextID$1 = 1;
|
||||||
function connect(mapStateToProps, options = {}) {
|
function connect(Comp, mapStoreToProps, options = {}) {
|
||||||
let hashFunction = options.hashFunction || null;
|
let hashFunction = options.hashFunction || null;
|
||||||
|
const getStore = options.getStore || (env => env.store);
|
||||||
if (!hashFunction) {
|
if (!hashFunction) {
|
||||||
let deep = "deep" in options ? options.deep : true;
|
let deep = "deep" in options ? options.deep : true;
|
||||||
let defaultRevFunction = deep ? deepRevNumber : revNumber;
|
let defaultRevFunction = deep ? deepRevNumber : revNumber;
|
||||||
@@ -3148,77 +3195,77 @@
|
|||||||
return hash;
|
return hash;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return function (Comp) {
|
const Result = class extends Comp {
|
||||||
const Result = class extends Comp {
|
constructor(parent, props) {
|
||||||
constructor(parent, props) {
|
const env = parent instanceof Component ? parent.env : parent;
|
||||||
const env = parent instanceof Component ? parent.env : parent;
|
const store = getStore(env);
|
||||||
const ownProps = Object.assign({}, props || {});
|
const ownProps = Object.assign({}, props || {});
|
||||||
const storeProps = mapStateToProps(env.store.state, ownProps, env.store.getters);
|
const storeProps = mapStoreToProps(store.state, ownProps, store.getters);
|
||||||
const mergedProps = Object.assign({}, props || {}, storeProps);
|
const mergedProps = Object.assign({}, props || {}, storeProps);
|
||||||
super(parent, mergedProps);
|
super(parent, mergedProps);
|
||||||
this.__owl__.ownProps = ownProps;
|
this.__owl__.ownProps = ownProps;
|
||||||
|
this.__owl__.currentStoreProps = storeProps;
|
||||||
|
this.__owl__.store = store;
|
||||||
|
this.__owl__.storeHash = hashFunction({
|
||||||
|
state: store.state,
|
||||||
|
storeProps: storeProps,
|
||||||
|
revNumber,
|
||||||
|
deepRevNumber
|
||||||
|
}, {
|
||||||
|
currentStoreProps: storeProps
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* We do not use the mounted hook here for a subtle reason: we want the
|
||||||
|
* updates to be called for the parents before the children. However,
|
||||||
|
* if we use the mounted hook, this will be done in the reverse order.
|
||||||
|
*/
|
||||||
|
_callMounted() {
|
||||||
|
this.__owl__.store.on("update", this, this._checkUpdate);
|
||||||
|
super._callMounted();
|
||||||
|
}
|
||||||
|
willUnmount() {
|
||||||
|
this.__owl__.store.off("update", this);
|
||||||
|
super.willUnmount();
|
||||||
|
}
|
||||||
|
_checkUpdate() {
|
||||||
|
const ownProps = this.__owl__.ownProps;
|
||||||
|
const storeProps = mapStoreToProps(this.__owl__.store.state, ownProps, this.__owl__.store.getters);
|
||||||
|
const options = {
|
||||||
|
currentStoreProps: this.__owl__.currentStoreProps
|
||||||
|
};
|
||||||
|
const storeHash = hashFunction({
|
||||||
|
state: this.__owl__.store.state,
|
||||||
|
storeProps: storeProps,
|
||||||
|
revNumber,
|
||||||
|
deepRevNumber
|
||||||
|
}, options);
|
||||||
|
let didChange = options.didChange;
|
||||||
|
if (storeHash !== this.__owl__.storeHash) {
|
||||||
|
didChange = true;
|
||||||
|
this.__owl__.storeHash = storeHash;
|
||||||
|
}
|
||||||
|
if (didChange) {
|
||||||
this.__owl__.currentStoreProps = storeProps;
|
this.__owl__.currentStoreProps = storeProps;
|
||||||
this.__owl__.storeHash = hashFunction({
|
this._updateProps(ownProps, false);
|
||||||
state: env.store.state,
|
|
||||||
storeProps: storeProps,
|
|
||||||
revNumber,
|
|
||||||
deepRevNumber
|
|
||||||
}, {
|
|
||||||
currentStoreProps: storeProps
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
/**
|
}
|
||||||
* We do not use the mounted hook here for a subtle reason: we want the
|
_updateProps(nextProps, forceUpdate, patchQueue) {
|
||||||
* updates to be called for the parents before the children. However,
|
if (this.__owl__.ownProps !== nextProps) {
|
||||||
* if we use the mounted hook, this will be done in the reverse order.
|
this.__owl__.currentStoreProps = mapStoreToProps(this.__owl__.store.state, nextProps, this.__owl__.store.getters);
|
||||||
*/
|
|
||||||
_callMounted() {
|
|
||||||
this.env.store.on("update", this, this._checkUpdate);
|
|
||||||
super._callMounted();
|
|
||||||
}
|
}
|
||||||
willUnmount() {
|
this.__owl__.ownProps = nextProps;
|
||||||
this.env.store.off("update", this);
|
const mergedProps = Object.assign({}, nextProps, this.__owl__.currentStoreProps);
|
||||||
super.willUnmount();
|
return super._updateProps(mergedProps, forceUpdate, patchQueue);
|
||||||
}
|
}
|
||||||
_checkUpdate() {
|
|
||||||
const ownProps = this.__owl__.ownProps;
|
|
||||||
const storeProps = mapStateToProps(this.env.store.state, ownProps, this.env.store.getters);
|
|
||||||
const options = {
|
|
||||||
currentStoreProps: this.__owl__.currentStoreProps
|
|
||||||
};
|
|
||||||
const storeHash = hashFunction({
|
|
||||||
state: this.env.store.state,
|
|
||||||
storeProps: storeProps,
|
|
||||||
revNumber,
|
|
||||||
deepRevNumber
|
|
||||||
}, options);
|
|
||||||
let didChange = options.didChange;
|
|
||||||
if (storeHash !== this.__owl__.storeHash) {
|
|
||||||
didChange = true;
|
|
||||||
this.__owl__.storeHash = storeHash;
|
|
||||||
}
|
|
||||||
if (didChange) {
|
|
||||||
this.__owl__.currentStoreProps = storeProps;
|
|
||||||
this._updateProps(ownProps, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_updateProps(nextProps, forceUpdate, patchQueue) {
|
|
||||||
if (this.__owl__.ownProps !== nextProps) {
|
|
||||||
this.__owl__.currentStoreProps = mapStateToProps(this.env.store.state, nextProps, this.env.store.getters);
|
|
||||||
}
|
|
||||||
this.__owl__.ownProps = nextProps;
|
|
||||||
const mergedProps = Object.assign({}, nextProps, this.__owl__.currentStoreProps);
|
|
||||||
return super._updateProps(mergedProps, forceUpdate, patchQueue);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// we assign here a unique name to the resulting anonymous class.
|
|
||||||
// this is necessary for Owl to be able to properly deduce templates.
|
|
||||||
// Otherwise, all connected components would have the same name, and then
|
|
||||||
// each component after the first will necessarily have the same template.
|
|
||||||
let name = `ConnectedComponent${nextID$1++}`;
|
|
||||||
Object.defineProperty(Result, "name", { value: name });
|
|
||||||
return Result;
|
|
||||||
};
|
};
|
||||||
|
// we assign here a unique name to the resulting anonymous class.
|
||||||
|
// this is necessary for Owl to be able to properly deduce templates.
|
||||||
|
// Otherwise, all connected components would have the same name, and then
|
||||||
|
// each component after the first will necessarily have the same template.
|
||||||
|
let name = `ConnectedComponent${nextID$1++}`;
|
||||||
|
Object.defineProperty(Result, "name", { value: name });
|
||||||
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3233,12 +3280,14 @@
|
|||||||
* - debounce
|
* - debounce
|
||||||
*/
|
*/
|
||||||
function whenReady(fn) {
|
function whenReady(fn) {
|
||||||
if (document.readyState !== "loading") {
|
return new Promise(function (resolve) {
|
||||||
fn();
|
if (document.readyState !== "loading") {
|
||||||
}
|
resolve();
|
||||||
else {
|
}
|
||||||
document.addEventListener("DOMContentLoaded", fn, false);
|
else {
|
||||||
}
|
document.addEventListener("DOMContentLoaded", resolve, false);
|
||||||
|
}
|
||||||
|
}).then(fn || function () { });
|
||||||
}
|
}
|
||||||
const loadedScripts = {};
|
const loadedScripts = {};
|
||||||
function loadJS(url) {
|
function loadJS(url) {
|
||||||
@@ -3353,9 +3402,9 @@
|
|||||||
exports.connect = connect;
|
exports.connect = connect;
|
||||||
exports.utils = utils;
|
exports.utils = utils;
|
||||||
|
|
||||||
exports.__info__.version = '0.13.0';
|
exports.__info__.version = '0.14.0';
|
||||||
exports.__info__.date = '2019-06-08T14:30:19.424Z';
|
exports.__info__.date = '2019-06-13T10:11:15.338Z';
|
||||||
exports.__info__.hash = '2a19aeb';
|
exports.__info__.hash = '2701a7d';
|
||||||
exports.__info__.url = 'https://github.com/odoo/owl';
|
exports.__info__.url = 'https://github.com/odoo/owl';
|
||||||
|
|
||||||
}(this.owl = this.owl || {}));
|
}(this.owl = this.owl || {}));
|
||||||
|
|||||||
+14
-14
@@ -127,22 +127,22 @@ async function makeApp(js, css, xml) {
|
|||||||
.map(l => (l === "" ? "" : " " + l))
|
.map(l => (l === "" ? "" : " " + l))
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
const JS = `async function startApp() {
|
const JS = `
|
||||||
// Loading templates
|
async function loadTemplates() {
|
||||||
let TEMPLATES;
|
|
||||||
try {
|
try {
|
||||||
TEMPLATES = await owl.utils.loadTemplates('app.xml');
|
return owl.utils.loadTemplates('app.xml');
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
document.write(\`This app requires a static server. If you have python installed, try 'python app.py'\`);
|
console.error(\`This app requires a static server. If you have python installed, try 'python app.py'\`);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function start([TEMPLATES]) {
|
||||||
// Application code
|
// Application code
|
||||||
${processedJS}
|
${processedJS}
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for DOM ready before starting
|
Promise.all([loadTemplates(), owl.utils.whenReady()]).then(start);
|
||||||
owl.utils.whenReady(startApp);`;
|
`;
|
||||||
|
|
||||||
zip.file("app.js", JS);
|
zip.file("app.js", JS);
|
||||||
zip.file("app.css", css);
|
zip.file("app.css", css);
|
||||||
@@ -354,13 +354,13 @@ class TabbedEditor extends owl.Component {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
async function start() {
|
async function start() {
|
||||||
document.title = `${document.title} (v${owl.__info__.version})`;
|
document.title = `${document.title} (v${owl.__info__.version})`;
|
||||||
const templates = await owl.utils.loadTemplates("templates.xml");
|
const [templates] = await Promise.all([
|
||||||
|
owl.utils.loadTemplates("templates.xml"),
|
||||||
|
owl.utils.whenReady()
|
||||||
|
]);
|
||||||
const qweb = new owl.QWeb(templates);
|
const qweb = new owl.QWeb(templates);
|
||||||
const env = { qweb };
|
const app = new App({ qweb });
|
||||||
owl.utils.whenReady(() => {
|
app.mount(document.body);
|
||||||
const app = new App(env);
|
|
||||||
app.mount(document.body);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start();
|
start();
|
||||||
|
|||||||
+134
-13
@@ -187,7 +187,7 @@ const ANIMATION_CSS = `button {
|
|||||||
|
|
||||||
.flash {
|
.flash {
|
||||||
background-position: center;
|
background-position: center;
|
||||||
transition: background 0.5s;
|
transition: background .6s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flash:active {
|
.flash:active {
|
||||||
@@ -208,7 +208,7 @@ const ANIMATION_CSS = `button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fade-enter-active, .fade-leave-active {
|
.fade-enter-active, .fade-leave-active {
|
||||||
transition: opacity .5s;
|
transition: opacity .6s;
|
||||||
}
|
}
|
||||||
.fade-enter, .fade-leave-to {
|
.fade-enter, .fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -233,7 +233,6 @@ const ANIMATION_CSS = `button {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
const LIFECYCLE_DEMO = `class HookWidget extends owl.Component {
|
const LIFECYCLE_DEMO = `class HookWidget extends owl.Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
@@ -384,11 +383,11 @@ class TodoItem extends owl.Component {
|
|||||||
state = { isEditing: false };
|
state = { isEditing: false };
|
||||||
|
|
||||||
removeTodo() {
|
removeTodo() {
|
||||||
this.env.store.dispatch("removeTodo", this.props.id);
|
this.env.dispatch("removeTodo", this.props.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleTodo() {
|
toggleTodo() {
|
||||||
this.env.store.dispatch("toggleTodo", this.props.id);
|
this.env.dispatch("toggleTodo", this.props.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async editTodo() {
|
async editTodo() {
|
||||||
@@ -420,7 +419,7 @@ class TodoItem extends owl.Component {
|
|||||||
if (!value) {
|
if (!value) {
|
||||||
this.removeTodo(this.props.id);
|
this.removeTodo(this.props.id);
|
||||||
} else {
|
} else {
|
||||||
this.env.store.dispatch("editTodo", {
|
this.env.dispatch("editTodo", {
|
||||||
id: this.props.id,
|
id: this.props.id,
|
||||||
title: value
|
title: value
|
||||||
});
|
});
|
||||||
@@ -432,7 +431,7 @@ class TodoItem extends owl.Component {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// TodoApp
|
// TodoApp
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
function mapStateToProps(state) {
|
function mapStoreToProps(state) {
|
||||||
return {
|
return {
|
||||||
todos: state.todos
|
todos: state.todos
|
||||||
};
|
};
|
||||||
@@ -470,18 +469,18 @@ class TodoApp extends owl.Component {
|
|||||||
if (ev.keyCode === ENTER_KEY) {
|
if (ev.keyCode === ENTER_KEY) {
|
||||||
const title = ev.target.value;
|
const title = ev.target.value;
|
||||||
if (title.trim()) {
|
if (title.trim()) {
|
||||||
this.env.store.dispatch("addTodo", title);
|
this.env.dispatch("addTodo", title);
|
||||||
}
|
}
|
||||||
ev.target.value = "";
|
ev.target.value = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCompleted() {
|
clearCompleted() {
|
||||||
this.env.store.dispatch("clearCompleted");
|
this.env.dispatch("clearCompleted");
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleAll() {
|
toggleAll() {
|
||||||
this.env.store.dispatch("toggleAll", !this.allChecked);
|
this.env.dispatch("toggleAll", !this.allChecked);
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilter(filter) {
|
setFilter(filter) {
|
||||||
@@ -489,7 +488,7 @@ class TodoApp extends owl.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConnectedTodoApp = owl.connect(mapStateToProps)(TodoApp);
|
const ConnectedTodoApp = owl.connect(TodoApp, mapStoreToProps);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// App Initialization
|
// App Initialization
|
||||||
@@ -498,7 +497,8 @@ const store = makeStore();
|
|||||||
const qweb = new owl.QWeb(TEMPLATES);
|
const qweb = new owl.QWeb(TEMPLATES);
|
||||||
const env = {
|
const env = {
|
||||||
qweb,
|
qweb,
|
||||||
store
|
store,
|
||||||
|
dispatch: store.dispatch.bind(store),
|
||||||
};
|
};
|
||||||
const app = new ConnectedTodoApp(env);
|
const app = new ConnectedTodoApp(env);
|
||||||
app.mount(document.body);
|
app.mount(document.body);
|
||||||
@@ -1081,6 +1081,121 @@ const RESPONSIVE_CSS = `body {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const SLOTS = `// This example will not work if your browser does not support ESNext class fields
|
||||||
|
|
||||||
|
// We show here how slots can be used to create generic components. In this
|
||||||
|
// example, the Card component is basically only a container, and is created
|
||||||
|
// by giving it slots, inside the t-widget directive.
|
||||||
|
|
||||||
|
class Card extends owl.Component {
|
||||||
|
state = { fullDisplay: true };
|
||||||
|
|
||||||
|
toggleDisplay() {
|
||||||
|
this.state.fullDisplay = !this.state.fullDisplay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Counter extends owl.Component {
|
||||||
|
state = {val: 1};
|
||||||
|
|
||||||
|
inc() {
|
||||||
|
this.state.val++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main root widget
|
||||||
|
class App extends owl.Component {
|
||||||
|
widgets = {Card, Counter};
|
||||||
|
state = {a: 1, b: 3};
|
||||||
|
|
||||||
|
inc(key, delta) {
|
||||||
|
this.state[key] += delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application setup
|
||||||
|
const qweb = new owl.QWeb(TEMPLATES);
|
||||||
|
const app = new App({ qweb });
|
||||||
|
app.mount(document.body);`;
|
||||||
|
|
||||||
|
const SLOTS_XML = `<templates>
|
||||||
|
<div t-name="Card" class="card" t-att-class="state.fullDisplay ? 'full' : 'small'">
|
||||||
|
<div class="card-title">
|
||||||
|
<t t-esc="props.title"/><button t-on-click="toggleDisplay">Toggle</button>
|
||||||
|
</div>
|
||||||
|
<t t-if="state.fullDisplay">
|
||||||
|
<div class="card-content" >
|
||||||
|
<t t-slot="content"/>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<t t-slot="footer"/>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div t-name="Counter">
|
||||||
|
<t t-esc="state.val"/><button t-on-click="inc">Inc</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div t-name="App" class="main">
|
||||||
|
<t t-widget="Card" title="'Title card A'">
|
||||||
|
<t t-set="content">Content of card 1... [<t t-esc="state.a"/>]</t>
|
||||||
|
<t t-set="footer"><button t-on-click="inc('a', 1)">Increment A</button></t>
|
||||||
|
</t>
|
||||||
|
<t t-widget="Card" title="'Title card B'">
|
||||||
|
<div t-set="content">
|
||||||
|
<div>Card 2... [<t t-esc="state.b"/>]</div>
|
||||||
|
<t t-widget="Counter"/>
|
||||||
|
</div>
|
||||||
|
<t t-set="footer"><button t-on-click="inc('b', -1)">Decrement B</button></t>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</templates>`;
|
||||||
|
|
||||||
|
const SLOTS_CSS = `.main {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
width: 200px;
|
||||||
|
height: 100px;
|
||||||
|
margin: 10px;
|
||||||
|
border: 1px solid gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card.full {
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card.small {
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
flex: 0 0 25px;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: darkcyan;
|
||||||
|
color: white;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title button {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding: 5px;
|
||||||
|
border-top: 1px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
border-top: 1px solid white;
|
||||||
|
}`;
|
||||||
|
|
||||||
const EMPTY = `class App extends owl.Component {
|
const EMPTY = `class App extends owl.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1112,7 +1227,7 @@ export const SAMPLES = [
|
|||||||
description: "Animations",
|
description: "Animations",
|
||||||
code: ANIMATION,
|
code: ANIMATION,
|
||||||
xml: ANIMATION_XML,
|
xml: ANIMATION_XML,
|
||||||
css: ANIMATION_CSS,
|
css: ANIMATION_CSS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Lifecycle demo",
|
description: "Lifecycle demo",
|
||||||
@@ -1131,6 +1246,12 @@ export const SAMPLES = [
|
|||||||
css: RESPONSIVE_CSS,
|
css: RESPONSIVE_CSS,
|
||||||
xml: RESPONSIVE_XML
|
xml: RESPONSIVE_XML
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Slots",
|
||||||
|
code: SLOTS,
|
||||||
|
xml: SLOTS_XML,
|
||||||
|
css: SLOTS_CSS
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Empty",
|
description: "Empty",
|
||||||
code: EMPTY
|
code: EMPTY
|
||||||
|
|||||||
Reference in New Issue
Block a user