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) {
|
||||
let key = data === undefined ? undefined : data.key;
|
||||
return {
|
||||
sel: sel,
|
||||
data: data,
|
||||
children: children,
|
||||
text: text,
|
||||
elm: elm,
|
||||
key: key
|
||||
};
|
||||
return { sel, data, children, text, elm, key };
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// snabbdom.ts
|
||||
@@ -885,7 +878,7 @@
|
||||
"(": "LEFT_PAREN",
|
||||
")": "RIGHT_PAREN"
|
||||
};
|
||||
const OPERATORS = [".", "===", "==", "+", "!", "||", "&&", ">", "?", "-", "*"];
|
||||
const OPERATORS = ".,===,==,+,!,||,&&,>=,>,<=,<,?,-,*,/,%".split(',');
|
||||
let tokenizeString = function (expr) {
|
||||
let s = expr[0];
|
||||
let start = s;
|
||||
@@ -997,7 +990,7 @@
|
||||
}
|
||||
}
|
||||
if (expr.length) {
|
||||
throw new Error("Tokenizer error...");
|
||||
throw new Error(`Tokenizer error: could not tokenize "${expr}"`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1125,6 +1118,10 @@
|
||||
// use case is that component's templates are qweb dependant, and need to be
|
||||
// able to map a qweb instance to a template name.
|
||||
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) {
|
||||
this.addTemplates(data);
|
||||
}
|
||||
@@ -1288,13 +1285,16 @@
|
||||
if (ctx.parentNode) {
|
||||
ctx.addLine(`c${ctx.parentNode}.push({text: \`${text}\`});`);
|
||||
}
|
||||
else if (ctx.parentTextNode) {
|
||||
ctx.addLine(`vn${ctx.parentTextNode}.text += \`${text}\`;`);
|
||||
}
|
||||
else {
|
||||
// this is an unusual situation: this text node is the result of the
|
||||
// template rendering.
|
||||
let nodeID = ctx.generateID();
|
||||
ctx.addLine(`var vn${nodeID} = {text: \`${text}\`};`);
|
||||
ctx.rootContext.rootNode = nodeID;
|
||||
ctx.rootContext.parentNode = nodeID;
|
||||
ctx.rootContext.parentTextNode = nodeID;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1543,6 +1543,7 @@
|
||||
this.variables = {};
|
||||
this.escaping = false;
|
||||
this.parentNode = null;
|
||||
this.parentTextNode = null;
|
||||
this.rootNode = null;
|
||||
this.indentLevel = 0;
|
||||
this.shouldDefineOwner = false;
|
||||
@@ -1560,7 +1561,7 @@
|
||||
return id;
|
||||
}
|
||||
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");
|
||||
}
|
||||
if (!this.rootContext.rootNode) {
|
||||
@@ -2268,11 +2269,19 @@
|
||||
exprID = value.id;
|
||||
}
|
||||
ctx.addIf(`${exprID} || ${exprID} === 0`);
|
||||
if (!ctx.parentNode) {
|
||||
throw new Error("Should not have a text node without a parent");
|
||||
}
|
||||
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 {
|
||||
let fragID = ctx.generateID();
|
||||
@@ -2524,6 +2533,7 @@
|
||||
* - t-transition
|
||||
* - t-widget/t-keepalive
|
||||
* - t-mounted
|
||||
* - t-slot
|
||||
*/
|
||||
//------------------------------------------------------------------------------
|
||||
// t-on
|
||||
@@ -2595,7 +2605,15 @@
|
||||
UTILS.nextFrame = function (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-active");
|
||||
const finalize = () => {
|
||||
@@ -2608,7 +2626,9 @@
|
||||
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-active");
|
||||
const finalize = () => {
|
||||
@@ -2656,8 +2676,8 @@
|
||||
atNodeCreation({ value, addNodeHook }) {
|
||||
let name = value;
|
||||
const hooks = {
|
||||
insert: `this.utils.transitionInsert(vn.elm, '${name}');`,
|
||||
remove: `this.utils.transitionRemove(vn.elm, '${name}', rm);`
|
||||
insert: `this.utils.transitionInsert(vn, '${name}');`,
|
||||
remove: `this.utils.transitionRemove(vn, '${name}', rm);`
|
||||
};
|
||||
for (let hookName in hooks) {
|
||||
addNodeHook(hookName, hooks[hookName]);
|
||||
@@ -2780,7 +2800,7 @@
|
||||
* let nvn = w4._mount(vnode, vn.elm);
|
||||
* pvnode.elm = nvn.elm;
|
||||
* // what follows is only present if there are animations on the widget
|
||||
* utils.transitionInsert(vn.elm, "fade");
|
||||
* utils.transitionInsert(vn, "fade");
|
||||
* },
|
||||
* remove() {
|
||||
* // override with empty function to prevent from removing the node
|
||||
@@ -2793,7 +2813,7 @@
|
||||
* let finalize = () => {
|
||||
* 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
|
||||
@@ -2834,7 +2854,7 @@
|
||||
name: "widget",
|
||||
extraNames: ["props", "keepalive"],
|
||||
priority: 100,
|
||||
atNodeEncounter({ ctx, value, node }) {
|
||||
atNodeEncounter({ ctx, value, node, qweb }) {
|
||||
ctx.addLine("//WIDGET");
|
||||
ctx.rootContext.shouldDefineOwner = true;
|
||||
ctx.rootContext.shouldDefineQWeb = true;
|
||||
@@ -2902,7 +2922,7 @@
|
||||
}
|
||||
let transitionsInsertCode = "";
|
||||
if (transition) {
|
||||
transitionsInsertCode = `utils.transitionInsert(vn.elm, '${transition}');`;
|
||||
transitionsInsertCode = `utils.transitionInsert(vn, '${transition}');`;
|
||||
}
|
||||
let finalizeWidgetCode = `w${widgetID}.${keepAlive ? "unmount" : "destroy"}();`;
|
||||
if (ref && !keepAlive) {
|
||||
@@ -2912,7 +2932,7 @@
|
||||
finalizeWidgetCode = `let finalize = () => {
|
||||
${finalizeWidgetCode}
|
||||
};
|
||||
utils.transitionRemove(vn.elm, '${transition}', finalize);`;
|
||||
utils.transitionRemove(vn, '${transition}', finalize);`;
|
||||
}
|
||||
let createHook = "";
|
||||
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(`w${widgetID} = new W${widgetID}(owner, props${widgetID});`);
|
||||
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();`);
|
||||
// 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
|
||||
@@ -3036,6 +3069,19 @@
|
||||
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 {
|
||||
constructor(config, options = {}) {
|
||||
@@ -3122,8 +3168,9 @@
|
||||
return 0;
|
||||
}
|
||||
let nextID$1 = 1;
|
||||
function connect(mapStateToProps, options = {}) {
|
||||
function connect(Comp, mapStoreToProps, options = {}) {
|
||||
let hashFunction = options.hashFunction || null;
|
||||
const getStore = options.getStore || (env => env.store);
|
||||
if (!hashFunction) {
|
||||
let deep = "deep" in options ? options.deep : true;
|
||||
let defaultRevFunction = deep ? deepRevNumber : revNumber;
|
||||
@@ -3148,77 +3195,77 @@
|
||||
return hash;
|
||||
};
|
||||
}
|
||||
return function (Comp) {
|
||||
const Result = class extends Comp {
|
||||
constructor(parent, props) {
|
||||
const env = parent instanceof Component ? parent.env : parent;
|
||||
const ownProps = Object.assign({}, props || {});
|
||||
const storeProps = mapStateToProps(env.store.state, ownProps, env.store.getters);
|
||||
const mergedProps = Object.assign({}, props || {}, storeProps);
|
||||
super(parent, mergedProps);
|
||||
this.__owl__.ownProps = ownProps;
|
||||
const Result = class extends Comp {
|
||||
constructor(parent, props) {
|
||||
const env = parent instanceof Component ? parent.env : parent;
|
||||
const store = getStore(env);
|
||||
const ownProps = Object.assign({}, props || {});
|
||||
const storeProps = mapStoreToProps(store.state, ownProps, store.getters);
|
||||
const mergedProps = Object.assign({}, props || {}, storeProps);
|
||||
super(parent, mergedProps);
|
||||
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__.storeHash = hashFunction({
|
||||
state: env.store.state,
|
||||
storeProps: storeProps,
|
||||
revNumber,
|
||||
deepRevNumber
|
||||
}, {
|
||||
currentStoreProps: storeProps
|
||||
});
|
||||
this._updateProps(ownProps, false);
|
||||
}
|
||||
/**
|
||||
* 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.env.store.on("update", this, this._checkUpdate);
|
||||
super._callMounted();
|
||||
}
|
||||
_updateProps(nextProps, forceUpdate, patchQueue) {
|
||||
if (this.__owl__.ownProps !== nextProps) {
|
||||
this.__owl__.currentStoreProps = mapStoreToProps(this.__owl__.store.state, nextProps, this.__owl__.store.getters);
|
||||
}
|
||||
willUnmount() {
|
||||
this.env.store.off("update", this);
|
||||
super.willUnmount();
|
||||
}
|
||||
_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;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3233,12 +3280,14 @@
|
||||
* - debounce
|
||||
*/
|
||||
function whenReady(fn) {
|
||||
if (document.readyState !== "loading") {
|
||||
fn();
|
||||
}
|
||||
else {
|
||||
document.addEventListener("DOMContentLoaded", fn, false);
|
||||
}
|
||||
return new Promise(function (resolve) {
|
||||
if (document.readyState !== "loading") {
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
document.addEventListener("DOMContentLoaded", resolve, false);
|
||||
}
|
||||
}).then(fn || function () { });
|
||||
}
|
||||
const loadedScripts = {};
|
||||
function loadJS(url) {
|
||||
@@ -3353,9 +3402,9 @@
|
||||
exports.connect = connect;
|
||||
exports.utils = utils;
|
||||
|
||||
exports.__info__.version = '0.13.0';
|
||||
exports.__info__.date = '2019-06-08T14:30:19.424Z';
|
||||
exports.__info__.hash = '2a19aeb';
|
||||
exports.__info__.version = '0.14.0';
|
||||
exports.__info__.date = '2019-06-13T10:11:15.338Z';
|
||||
exports.__info__.hash = '2701a7d';
|
||||
exports.__info__.url = 'https://github.com/odoo/owl';
|
||||
|
||||
}(this.owl = this.owl || {}));
|
||||
|
||||
+14
-14
@@ -127,22 +127,22 @@ async function makeApp(js, css, xml) {
|
||||
.map(l => (l === "" ? "" : " " + l))
|
||||
.join("\n");
|
||||
|
||||
const JS = `async function startApp() {
|
||||
// Loading templates
|
||||
let TEMPLATES;
|
||||
const JS = `
|
||||
async function loadTemplates() {
|
||||
try {
|
||||
TEMPLATES = await owl.utils.loadTemplates('app.xml');
|
||||
return owl.utils.loadTemplates('app.xml');
|
||||
} catch(e) {
|
||||
document.write(\`This app requires a static server. If you have python installed, try 'python app.py'\`);
|
||||
return;
|
||||
console.error(\`This app requires a static server. If you have python installed, try 'python app.py'\`);
|
||||
}
|
||||
}
|
||||
|
||||
function start([TEMPLATES]) {
|
||||
// Application code
|
||||
${processedJS}
|
||||
}
|
||||
|
||||
// wait for DOM ready before starting
|
||||
owl.utils.whenReady(startApp);`;
|
||||
Promise.all([loadTemplates(), owl.utils.whenReady()]).then(start);
|
||||
`;
|
||||
|
||||
zip.file("app.js", JS);
|
||||
zip.file("app.css", css);
|
||||
@@ -354,13 +354,13 @@ class TabbedEditor extends owl.Component {
|
||||
//------------------------------------------------------------------------------
|
||||
async function start() {
|
||||
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 env = { qweb };
|
||||
owl.utils.whenReady(() => {
|
||||
const app = new App(env);
|
||||
app.mount(document.body);
|
||||
});
|
||||
const app = new App({ qweb });
|
||||
app.mount(document.body);
|
||||
}
|
||||
|
||||
start();
|
||||
|
||||
+134
-13
@@ -187,7 +187,7 @@ const ANIMATION_CSS = `button {
|
||||
|
||||
.flash {
|
||||
background-position: center;
|
||||
transition: background 0.5s;
|
||||
transition: background .6s;
|
||||
}
|
||||
|
||||
.flash:active {
|
||||
@@ -208,7 +208,7 @@ const ANIMATION_CSS = `button {
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity .5s;
|
||||
transition: opacity .6s;
|
||||
}
|
||||
.fade-enter, .fade-leave-to {
|
||||
opacity: 0;
|
||||
@@ -233,7 +233,6 @@ const ANIMATION_CSS = `button {
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
const LIFECYCLE_DEMO = `class HookWidget extends owl.Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
@@ -384,11 +383,11 @@ class TodoItem extends owl.Component {
|
||||
state = { isEditing: false };
|
||||
|
||||
removeTodo() {
|
||||
this.env.store.dispatch("removeTodo", this.props.id);
|
||||
this.env.dispatch("removeTodo", this.props.id);
|
||||
}
|
||||
|
||||
toggleTodo() {
|
||||
this.env.store.dispatch("toggleTodo", this.props.id);
|
||||
this.env.dispatch("toggleTodo", this.props.id);
|
||||
}
|
||||
|
||||
async editTodo() {
|
||||
@@ -420,7 +419,7 @@ class TodoItem extends owl.Component {
|
||||
if (!value) {
|
||||
this.removeTodo(this.props.id);
|
||||
} else {
|
||||
this.env.store.dispatch("editTodo", {
|
||||
this.env.dispatch("editTodo", {
|
||||
id: this.props.id,
|
||||
title: value
|
||||
});
|
||||
@@ -432,7 +431,7 @@ class TodoItem extends owl.Component {
|
||||
//------------------------------------------------------------------------------
|
||||
// TodoApp
|
||||
//------------------------------------------------------------------------------
|
||||
function mapStateToProps(state) {
|
||||
function mapStoreToProps(state) {
|
||||
return {
|
||||
todos: state.todos
|
||||
};
|
||||
@@ -470,18 +469,18 @@ class TodoApp extends owl.Component {
|
||||
if (ev.keyCode === ENTER_KEY) {
|
||||
const title = ev.target.value;
|
||||
if (title.trim()) {
|
||||
this.env.store.dispatch("addTodo", title);
|
||||
this.env.dispatch("addTodo", title);
|
||||
}
|
||||
ev.target.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
clearCompleted() {
|
||||
this.env.store.dispatch("clearCompleted");
|
||||
this.env.dispatch("clearCompleted");
|
||||
}
|
||||
|
||||
toggleAll() {
|
||||
this.env.store.dispatch("toggleAll", !this.allChecked);
|
||||
this.env.dispatch("toggleAll", !this.allChecked);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -498,7 +497,8 @@ const store = makeStore();
|
||||
const qweb = new owl.QWeb(TEMPLATES);
|
||||
const env = {
|
||||
qweb,
|
||||
store
|
||||
store,
|
||||
dispatch: store.dispatch.bind(store),
|
||||
};
|
||||
const app = new ConnectedTodoApp(env);
|
||||
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 {
|
||||
}
|
||||
|
||||
@@ -1112,7 +1227,7 @@ export const SAMPLES = [
|
||||
description: "Animations",
|
||||
code: ANIMATION,
|
||||
xml: ANIMATION_XML,
|
||||
css: ANIMATION_CSS,
|
||||
css: ANIMATION_CSS
|
||||
},
|
||||
{
|
||||
description: "Lifecycle demo",
|
||||
@@ -1131,6 +1246,12 @@ export const SAMPLES = [
|
||||
css: RESPONSIVE_CSS,
|
||||
xml: RESPONSIVE_XML
|
||||
},
|
||||
{
|
||||
description: "Slots",
|
||||
code: SLOTS,
|
||||
xml: SLOTS_XML,
|
||||
css: SLOTS_CSS
|
||||
},
|
||||
{
|
||||
description: "Empty",
|
||||
code: EMPTY
|
||||
|
||||
Reference in New Issue
Block a user