[IMP] playground: update Owl to 0.9.0

This commit is contained in:
Géry Debongnie
2019-05-03 12:28:59 +02:00
parent 7642ce4e2c
commit 62469c4264
14 changed files with 625 additions and 537 deletions
+233 -187
View File
@@ -965,23 +965,6 @@
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* Attach a child widget to a given html element
*
* This is most of the time not necessary, since widgets should primarily be
* created/managed with the t-widget directive in a qweb template. However,
* for the cases where we need more control, this method will do what is
* necessary to make sure all the proper hooks are called (for example,
* mounted/willUnmount)
*
* Note that this method makes a few assumptions:
* - the child widget is indeed a child of the current widget
* - the target is inside the dom of the current widget (typically a ref)
*/
attachChild(child, target) {
target.appendChild(child.el);
child.__mount();
}
async mount(target) {
const vnode = await this._prepare();
if (this.__owl__.isDestroyed) {
@@ -991,63 +974,93 @@
this._patch(vnode);
target.appendChild(this.el);
if (document.body.contains(target)) {
this._visitSubTree(w => {
if (!w.__owl__.isMounted && this.el.contains(w.el)) {
w.__owl__.isMounted = true;
w.mounted();
return true;
this._callMounted();
}
}
_callMounted() {
const children = this.__owl__.children;
for (let id in children) {
const comp = children[id];
if (!comp.__owl__.isMounted && this.el.contains(comp.el)) {
comp._callMounted();
}
}
this.__owl__.isMounted = true;
this.mounted();
}
_callWillUnmount() {
this.willUnmount();
this.__owl__.isMounted = false;
const children = this.__owl__.children;
for (let id in children) {
const comp = children[id];
if (comp.__owl__.isMounted) {
comp._callWillUnmount();
}
return false;
});
}
}
unmount() {
if (this.el) {
this._visitSubTree(w => {
if (w.__owl__.isMounted) {
w.willUnmount();
w.__owl__.isMounted = false;
return true;
}
return false;
});
if (this.__owl__.isMounted) {
this._callWillUnmount();
this.el.remove();
}
}
async render(force = false) {
async render(force = false, patchQueue) {
if (this.__owl__.isDestroyed) {
return;
}
const renderVDom = this._render(force);
const shouldCallPatchHooks = !patchQueue;
if (shouldCallPatchHooks) {
patchQueue = [];
}
const renderVDom = this._render(force, patchQueue);
const renderId = this.__owl__.renderId;
const vnode = await renderVDom;
if (renderId === this.__owl__.renderId) {
// we only update the vnode and the actual DOM if no other rendering
// occurred between now and when the render method was initially called.
if (shouldCallPatchHooks) {
for (let i = 0; i < patchQueue.length; i++) {
const c = patchQueue[i];
c.__owl__.willPatchVal = c.willPatch();
}
}
this._patch(vnode);
if (shouldCallPatchHooks) {
for (let i = patchQueue.length - 1; i >= 0; i--) {
const c = patchQueue[i];
c.patched(c.__owl__.willPatchVal);
}
}
}
}
destroy() {
if (!this.__owl__.isDestroyed) {
for (let id in this.__owl__.children) {
this.__owl__.children[id].destroy();
const el = this.el;
this._destroy(this.__owl__.parent);
if (el) {
el.remove();
}
if (this.__owl__.isMounted) {
}
}
_destroy(parent) {
const isMounted = this.__owl__.isMounted;
if (isMounted) {
this.willUnmount();
}
if (this.el) {
this.el.remove();
this.__owl__.isMounted = false;
delete this.__owl__.vnode;
}
if (this.__owl__.parent) {
const children = Object.values(this.__owl__.children);
for (let child of children) {
child._destroy(this);
}
if (parent) {
let id = this.__owl__.id;
delete this.__owl__.parent.__owl__.children[id];
delete parent.__owl__.children[id];
this.__owl__.parent = null;
}
this.clear();
this.__owl__.isDestroyed = true;
}
delete this.__owl__.vnode;
}
shouldUpdate(nextProps) {
return true;
@@ -1070,38 +1083,36 @@
await this.render(true);
}
}
async updateProps(nextProps, forceUpdate = false) {
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
return shouldUpdate ? this._updateProps(nextProps) : Promise.resolve();
}
set(target, key, value) {
this.__owl__.observer.set(target, key, value);
}
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
async _updateProps(nextProps) {
async _updateProps(nextProps, forceUpdate = false, patchQueue) {
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
if (shouldUpdate) {
await this.willUpdateProps(nextProps);
this.props = nextProps;
await this.render();
await this.render(false, patchQueue);
}
}
_patch(vnode) {
this.__owl__.renderPromise = null;
if (this.__owl__.vnode) {
const isMounted = this.__owl__.isMounted;
const snapshot = isMounted && this.willPatch();
this.__owl__.vnode = patch(this.__owl__.vnode, vnode);
if (isMounted) {
this.patched(snapshot);
}
}
else {
this.__owl__.vnode = patch(document.createElement(vnode.sel), vnode);
}
}
async _prepare() {
_prepare() {
this.__owl__.renderProps = this.props;
this.__owl__.renderPromise = this.willStart().then(() => {
this.__owl__.renderPromise = this._prepareAndRender();
return this.__owl__.renderPromise;
}
async _prepareAndRender() {
await this.willStart();
if (this.__owl__.isDestroyed) {
return Promise.resolve(h("div"));
}
@@ -1109,22 +1120,24 @@
if (this.inlineTemplate) {
this.env.qweb.addTemplate(this.inlineTemplate, this.inlineTemplate, true);
}
this.__owl__.render = this.env.qweb.render.bind(this.env.qweb, this.inlineTemplate || this.template);
this._observeState();
return this._render();
});
return this.__owl__.renderPromise;
}
async _render(force = false) {
async _render(force = false, patchQueue = []) {
if (this.__owl__.isMounted) {
patchQueue.push(this);
}
this.__owl__.renderId++;
const promises = [];
const template = this.inlineTemplate || this.template;
if (this.__owl__.observer) {
this.__owl__.observer.allowMutations = false;
}
let vnode = this.env.qweb.render(template, this, {
let vnode = this.__owl__.render(this, {
promises,
handlers: this.__owl__.boundHandlers,
forceUpdate: force
forceUpdate: force,
patchQueue
});
if (this.__owl__.observer) {
this.__owl__.observer.allowMutations = true;
@@ -1144,32 +1157,17 @@
*/
_mount(vnode, elm) {
this.__owl__.vnode = patch(elm, vnode);
this.__mount();
if (this.__owl__.parent &&
this.__owl__.parent.__owl__.isMounted &&
!this.__owl__.isMounted) {
this._callMounted();
}
return this.__owl__.vnode;
}
__mount() {
if (this.__owl__.isMounted) {
return;
}
this._observeState();
if (this.__owl__.parent) {
if (this.__owl__.parent.__owl__.isMounted) {
if (!this.__owl__.isMounted) {
this.__owl__.isMounted = true;
this.mounted();
const children = this.__owl__.children;
for (let id in children) {
children[id].__mount();
}
}
}
}
_visitSubTree(callback) {
const shouldVisitChildren = callback(this);
if (shouldVisitChildren) {
const children = this.__owl__.children;
for (let id in children) {
children[id]._visitSubTree(callback);
}
}
}
_observeState() {
@@ -1200,6 +1198,62 @@
];
const lineBreakRE = /[\r\n]/;
const whitespaceRE = /\s+/g;
function parseXML(xml) {
const parser = new DOMParser();
const doc = parser.parseFromString(xml, "text/xml");
if (doc.getElementsByTagName("parsererror").length) {
throw new Error("Invalid XML in template");
}
return doc;
}
const UTILS = {
h: h,
getFragment(str) {
const temp = document.createElement("template");
temp.innerHTML = str;
return temp.content;
},
objectToAttrString(obj) {
let classes = [];
for (let k in obj) {
if (obj[k]) {
classes.push(k);
}
}
return classes.join(" ");
},
nextFrame(cb) {
requestAnimationFrame(() => requestAnimationFrame(cb));
},
transitionCreate(elm, name) {
elm.classList.add(name + "-enter");
elm.classList.add(name + "-enter-active");
},
transitionInsert(elm, name) {
const finalize = () => {
elm.classList.remove(name + "-enter-active");
elm.classList.remove(name + "-enter-to");
};
elm.addEventListener("transitionend", finalize);
this.nextFrame(() => {
elm.classList.remove(name + "-enter");
elm.classList.add(name + "-enter-to");
});
},
transitionRemove(elm, name, rm) {
elm.classList.add(name + "-leave");
elm.classList.add(name + "-leave-active");
elm.addEventListener("transitionend", () => {
elm.classList.remove(name + "-leave-active");
elm.classList.remove(name + "-enter-to");
rm();
});
this.nextFrame(() => {
elm.classList.remove(name + "-leave");
elm.classList.add(name + "-leave-to");
});
}
};
//------------------------------------------------------------------------------
// Compilation Context
//------------------------------------------------------------------------------
@@ -1335,26 +1389,9 @@
//------------------------------------------------------------------------------
class QWeb {
constructor(data) {
this.processedTemplates = {};
this.templates = {};
this.directives = [];
this.utils = {
h: h,
getFragment(str) {
const temp = document.createElement("template");
temp.innerHTML = str;
return temp.content;
},
objectToAttrString(obj) {
let classes = [];
for (let k in obj) {
if (obj[k]) {
classes.push(k);
}
}
return classes.join(" ");
}
};
this.utils = UTILS;
this.directiveNames = {
as: 1,
name: 1,
@@ -1364,7 +1401,8 @@
props: 1,
key: 1,
keepalive: 1,
debug: 1
debug: 1,
log: 1
};
[
forEachDirective,
@@ -1377,6 +1415,9 @@
callDirective,
onDirective,
refDirective,
transitionDirective,
debugDirective,
logDirective,
widgetDirective
].forEach(d => this.addDirective(d));
if (data) {
@@ -1392,26 +1433,30 @@
* Add a template to the internal template map. Note that it is not
* immediately compiled.
*/
addTemplate(name, template, allowDuplicates = false) {
if (name in this.processedTemplates) {
if (allowDuplicates) {
addTemplate(name, xmlString, allowDuplicates = false) {
if (name in this.templates && allowDuplicates) {
return;
}
else {
throw new Error(`Template ${name} already defined`);
}
}
const parser = new DOMParser();
const doc = parser.parseFromString(template, "text/xml");
const doc = parseXML(xmlString);
if (!doc.firstChild) {
throw new Error("Invalid template (should not be empty)");
}
if (doc.getElementsByTagName("parsererror").length) {
throw new Error("Invalid XML in template");
this._addTemplate(name, doc.firstChild);
}
_addTemplate(name, elem) {
if (name in this.templates) {
throw new Error(`Template ${name} already defined`);
}
let elem = doc.firstChild;
this._processTemplate(elem);
this.processedTemplates[name] = elem;
const template = {
elem,
fn: (context, extra) => {
const compiledFunction = this._compile(name, elem);
template.fn = compiledFunction;
return compiledFunction.call(this, context, extra);
}
};
this.templates[name] = template;
}
_processTemplate(elem) {
let tbranch = elem.querySelectorAll("[t-elif], [t-else]");
@@ -1453,19 +1498,14 @@
* the name given by the t-name attribute.
*/
loadTemplates(xmlstr) {
const parser = new DOMParser();
const doc = parser.parseFromString(xmlstr, "text/xml");
if (doc.getElementsByTagName("parsererror").length) {
throw new Error("Invalid XML in template");
}
const doc = parseXML(xmlstr);
const templates = doc.getElementsByTagName("templates")[0];
if (!templates) {
return;
}
for (let elem of templates.children) {
const name = elem.getAttribute("t-name");
this._processTemplate(elem);
this.processedTemplates[name] = elem;
this._addTemplate(name, elem);
}
}
/**
@@ -1474,20 +1514,16 @@
* @param {string} name the template should already have been added
*/
render(name, context = {}, extra = null) {
if (!(name in this.processedTemplates)) {
const template = this.templates[name];
if (!template) {
throw new Error(`Template ${name} does not exist`);
}
const template = this.templates[name] || this._compile(name);
return template.call(this, context, extra);
return template.fn.call(this, context, extra);
}
_compile(name) {
if (name in this.templates) {
return this.templates[name];
}
const mainNode = this.processedTemplates[name];
const isDebug = mainNode.attributes.hasOwnProperty("t-debug");
_compile(name, elem) {
const isDebug = elem.attributes.hasOwnProperty("t-debug");
const ctx = new Context(name);
this._compileNode(mainNode, ctx);
this._compileNode(elem, ctx);
if (ctx.shouldProtectContext) {
ctx.code.unshift(" context = Object.create(context);");
}
@@ -1500,9 +1536,6 @@
throw new Error("A template should have one root node");
}
ctx.addLine(`return vn${ctx.rootNode};`);
if (isDebug) {
ctx.code.unshift(" debugger");
}
let template;
try {
template = new Function("context", "extra", ctx.code.join("\n"));
@@ -1511,9 +1544,9 @@
throw new Error(`Invalid generated code while compiling template '${ctx.templateName.replace(/`/g, "'")}': ${e.message}`);
}
if (isDebug) {
console.log(`Template: ${this.processedTemplates[name].outerHTML}\nCompiled code:\n` + template.toString());
console.log(`Template: ${this.templates[name].elem.outerHTML}\nCompiled code:\n` +
template.toString());
}
this.templates[name] = template;
return template;
}
/**
@@ -1890,7 +1923,7 @@
throw new Error("Invalid tag for t-call directive (should be 't')");
}
const subTemplate = node.getAttribute("t-call");
const nodeTemplate = qweb.processedTemplates[subTemplate];
const nodeTemplate = qweb.templates[subTemplate];
if (!nodeTemplate) {
throw new Error(`Cannot find template "${subTemplate}" (t-call)`);
}
@@ -1918,7 +1951,7 @@
.subContext("caller", nodeCopy)
.subContext("variables", Object.create(vars))
.subContext("definedVariables", Object.create(definedVariables));
qweb._compileNode(nodeTemplate, subCtx);
qweb._compileNode(nodeTemplate.elem, subCtx);
// close new scope
if (hasNewVariables) {
ctx.dedent();
@@ -1952,7 +1985,19 @@
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")) {
let shouldWarn = nodeCopy.tagName !== "t" && !nodeCopy.hasAttribute("t-key");
if (!shouldWarn && node.tagName === "t") {
if (node.hasAttribute("t-widget") && !node.hasAttribute("t-key")) {
shouldWarn = true;
}
if (!shouldWarn &&
node.children.length === 1 &&
node.children[0].tagName !== 't' &&
!node.children[0].hasAttribute("t-key")) {
shouldWarn = true;
}
}
if (shouldWarn) {
console.warn(`Directive t-foreach should always be used with a t-key! (in template: '${ctx.templateName}')`);
}
nodeCopy.removeAttribute("t-foreach");
@@ -1989,13 +2034,47 @@
const refDirective = {
name: "ref",
priority: 95,
atNodeCreation({ ctx, node }) {
let ref = node.getAttribute("t-ref");
ctx.addLine(`p${ctx.parentNode}.hook = {
create: (_, n) => context.refs[${ctx.formatExpression(ref)}] = n.elm,
atNodeCreation({ ctx, nodeID, value }) {
const refKey = `ref${ctx.generateID()}`;
ctx.addLine(`const ${refKey} = ${ctx.formatExpression(value)}`);
ctx.addLine(`p${nodeID}.hook = {
create: (_, n) => context.refs[${refKey}] = n.elm,
};`);
}
};
const transitionDirective = {
name: "transition",
priority: 96,
atNodeCreation({ ctx, value }) {
let name = value;
ctx.addLine(`p${ctx.parentNode}.hook = {
create: (_, n) => {
this.utils.transitionCreate(n.elm, '${name}');
},
insert: vn => {
this.utils.transitionInsert(vn.elm, '${name}');
},
remove: (vn, rm) => {
this.utils.transitionRemove(vn.elm, '${name}', rm);
}
};`);
}
};
const debugDirective = {
name: "debug",
priority: 99,
atNodeEncounter({ ctx }) {
ctx.addLine("debugger;");
}
};
const logDirective = {
name: "log",
priority: 99,
atNodeEncounter({ ctx, value }) {
const expr = ctx.formatExpression(value);
ctx.addLine(`console.log(${expr})`);
}
};
const widgetDirective = {
name: "widget",
priority: 100,
@@ -2043,7 +2122,7 @@
// check if we can reuse current rendering promise
ctx.addIf(`w${widgetID} && w${widgetID}.__owl__.renderPromise`);
ctx.addIf(`w${widgetID}.__owl__.isStarted`);
ctx.addLine(`def${defID} = w${widgetID}.updateProps(props${widgetID}, extra.forceUpdate);`);
ctx.addLine(`def${defID} = w${widgetID}._updateProps(props${widgetID}, extra.forceUpdate, extra.patchQueue);`);
ctx.addElse();
ctx.addLine(`isNew${widgetID} = true`);
ctx.addIf(`props${widgetID} === w${widgetID}.__owl__.renderProps`);
@@ -2056,7 +2135,7 @@
ctx.closeIf();
ctx.addIf(`!def${defID}`);
ctx.addIf(`w${widgetID}`);
ctx.addLine(`def${defID} = w${widgetID}.updateProps(props${widgetID}, extra.forceUpdate);`);
ctx.addLine(`def${defID} = w${widgetID}._updateProps(props${widgetID}, extra.forceUpdate, extra.patchQueue);`);
ctx.addElse();
ctx.addLine(`w${widgetID} = new context.widgets['${value}'](owner, props${widgetID});`);
ctx.addLine(`context.__owl__.cmap[${templateID}] = w${widgetID}.__owl__.id;`);
@@ -2228,7 +2307,7 @@
}
if (didChange) {
this.__owl__.currentStoreProps = storeProps;
this.updateProps(ownProps, false);
this._updateProps(ownProps, false);
}
});
super.mounted();
@@ -2237,13 +2316,13 @@
this.env.store.off("update", this);
super.willUnmount();
}
updateProps(nextProps, forceUpdate) {
_updateProps(nextProps, forceUpdate) {
if (this.__owl__.ownProps !== nextProps) {
this.__owl__.currentStoreProps = mapStateToProps(this.env.store.state, nextProps);
}
this.__owl__.ownProps = nextProps;
const mergedProps = Object.assign({}, nextProps, this.__owl__.currentStoreProps);
return super.updateProps(mergedProps, forceUpdate);
return super._updateProps(mergedProps, forceUpdate);
}
};
};
@@ -2263,19 +2342,6 @@
.replace(/"/g, "&#x27;")
.replace(/`/g, "&#x60;");
}
/**
* Remove trailing and leading spaces
*/
function htmlTrim(s) {
let result = s.replace(/(^\s+|\s+$)/g, "");
if (s[0] === " ") {
result = " " + result;
}
if (result !== " " && s[s.length - 1] === " ") {
result = result + " ";
}
return result;
}
function memoize(f, hash) {
if (!hash) {
hash = args => args.map(a => String(a)).join(",");
@@ -2317,24 +2383,6 @@
}
};
}
/**
* Find a node in a tree.
*
* This will traverse the tree (depth first) and return the first child that
* matches the predicate, if any
*/
function findInTree(tree, predicate) {
if (predicate(tree)) {
return tree;
}
for (let child of tree.children) {
let match = findInTree(child, predicate);
if (match) {
return match;
}
}
return null;
}
function patch$1(C, patchName, patch) {
const proto = C.prototype;
if (!proto.__patches) {
@@ -2422,10 +2470,8 @@
var _utils = /*#__PURE__*/Object.freeze({
escape: escape,
htmlTrim: htmlTrim,
memoize: memoize,
debounce: debounce,
findInTree: findInTree,
patch: patch$1,
unpatch: unpatch,
loadTemplates: loadTemplates,
@@ -2443,9 +2489,9 @@
exports.connect = connect;
exports.Store = Store;
exports._version = '0.8.0';
exports._date = '2019-04-26T13:32:23.079Z';
exports._hash = 'b44f274';
exports._version = '0.9.0';
exports._date = '2019-05-03T10:06:05.040Z';
exports._hash = '5b9abb6';
exports._url = 'https://github.com/odoo/owl';
}(this.owl = this.owl || {}));
-7
View File
@@ -1,7 +0,0 @@
{
"name": "owl-playground",
"description": "Odoo Web Library",
"scripts": {
"start": "python server.py"
}
}
+4 -4
View File
@@ -6,7 +6,7 @@ async function owlSourceCode() {
if (owlJS) {
return owlJS;
}
const result = await fetch("/libs/owl.js");
const result = await fetch("../owl.js");
owlJS = await result.text();
return owlJS;
}
@@ -196,7 +196,7 @@ class App extends owl.Component {
// inject js
const owlScript = doc.createElement("script");
owlScript.type = "text/javascript";
owlScript.src = "../libs/owl.js";
owlScript.src = "../owl.js";
owlScript.addEventListener("load", () => {
const script = doc.createElement("script");
script.type = "text/javascript";
@@ -278,8 +278,8 @@ class App extends owl.Component {
}
async downloadCode() {
await owl.utils.loadJS("../libs/FileSaver.min.js");
await owl.utils.loadJS("../libs/jszip.min.js");
await owl.utils.loadJS("libs/FileSaver.min.js");
await owl.utils.loadJS("libs/jszip.min.js");
const zip = new JSZip();
+2 -2
View File
@@ -5,8 +5,8 @@
<title>OWL Playground</title>
<link rel="icon" href="data:,">
<script src="../libs/ace.js" type="text/javascript" charset="utf-8"></script>
<script src="../libs/owl.js"></script>
<script src="libs/ace.js" type="text/javascript" charset="utf-8"></script>
<script src="../owl.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/solid.css" integrity="sha384-QokYePQSOwpBDuhlHOsX0ymF6R/vLk/UQVz3WHa6wygxI5oGTmDTv8wahFOSspdm" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/fontawesome.css" integrity="sha384-vd1e11sR28tEK9YANUtpIOdjGW14pS87bUBuOIoBILVWLFnS+MCX9T6MMf0VdPGq" crossorigin="anonymous">
+98 -29
View File
@@ -54,7 +54,6 @@ const WIDGET_COMPOSITION = `class ClickCounter extends owl.Component {
}
}
let nextId = 1;
class App extends owl.Component {
@@ -72,7 +71,8 @@ class App extends owl.Component {
const qweb = new owl.QWeb(TEMPLATES);
const app = new App({ qweb });
app.mount(document.body);`;
app.mount(document.body);
`;
const WIDGET_COMPOSITION_XML = `<templates>
<button t-name="clickcounter" t-on-click="increment">
@@ -95,9 +95,61 @@ const WIDGET_COMPOSITION_CSS = `button {
width: 220px;
}`;
const LIFECYCLE_DEMO = `const { Component, QWeb } = owl;
const ANIMATION = `// This example will not work if your browser does not support ESNext class fields
class App extends owl.Component {
template = "app";
state = {flag: 0};
class HookWidget extends Component {
toggle() {
this.state.flag = !this.state.flag;
}
}
const qweb = new owl.QWeb(TEMPLATES);
const app = new App({qweb});
app.mount(document.body);
`;
const ANIMATION_XML = `<templates>
<div t-name="app">
<button t-on-click="toggle">
Click Me!
</button>
<div>
<div t-if="state.flag" class="square" t-transition="fade">Hello</div>
</div>
</div>
</templates>
`;
const ANIMATION_CSS = `button {
width: 100px;
height: 30px;
font-size: 20px;
}
.square {
background-color: red;
width: 100px;
height: 100px;
color: white;
margin: 20px;
font-size: 24px;
line-height: 100px;
text-align: center;
line-height: 100px;
}
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
`;
const LIFECYCLE_DEMO = `class HookWidget extends owl.Component {
constructor() {
super(...arguments);
this.template = "demo.hookwidget";
@@ -127,7 +179,7 @@ class HookWidget extends Component {
}
}
class ParentWidget extends Component {
class ParentWidget extends owl.Component {
constructor() {
super(...arguments);
this.widgets = { HookWidget };
@@ -142,7 +194,7 @@ class ParentWidget extends Component {
}
}
const qweb = new QWeb(TEMPLATES);
const qweb = new owl.QWeb(TEMPLATES);
const widget = new ParentWidget({ qweb });
widget.mount(document.body);
`;
@@ -190,9 +242,7 @@ class Counter extends owl.Component {
constructor(parent, props) {
super(parent, props);
this.template = "counter";
this.state = {
counter: props.initialState || 0
};
this.state = { counter: props.initialState || 0 };
}
increment(delta) {
@@ -225,9 +275,7 @@ class App extends owl.Component {
super(...arguments);
this.template = "root";
this.widgets = { Message };
this.state = {
messages: messages.slice(0, 10)
};
this.state = { messages: messages.slice(0, 10) };
}
setMessageCount(n) {
@@ -368,7 +416,10 @@ const actions = {
},
toggleAll({ state, commit }, completed) {
state.todos.forEach(todo => {
commit("editTodo", { id: todo.id, completed });
commit("editTodo", {
id: todo.id,
completed
});
});
},
editTodo({ commit }, { id, title }) {
@@ -379,7 +430,11 @@ const actions = {
const mutations = {
addTodo({ state }, title) {
const id = state.nextId++;
const todo = { id, title, completed: false };
const todo = {
id,
title,
completed: false
};
state.todos.push(todo);
},
removeTodo({ state }, id) {
@@ -402,8 +457,15 @@ function makeStore() {
window.localStorage.getItem(LOCALSTORAGE_KEY) || "[]"
);
const nextId = Math.max(0, ...todos.map(t => t.id || 0)) + 1;
const state = { todos, nextId };
const store = new owl.Store({ state, actions, mutations });
const state = {
todos,
nextId
};
const store = new owl.Store({
state,
actions,
mutations
});
store.on("update", null, () => {
const state = JSON.stringify(store.state.todos);
window.localStorage.setItem(LOCALSTORAGE_KEY, state);
@@ -467,7 +529,9 @@ class TodoItem extends owl.Component {
// TodoApp
//------------------------------------------------------------------------------
function mapStateToProps(state) {
return { todos: state.todos };
return {
todos: state.todos
};
}
class TodoApp extends owl.Component {
@@ -970,16 +1034,14 @@ html .clear-completed:active {
}
`;
const RESPONSIVE = `const { Component, QWeb, utils } = owl;
class SubWidget extends Component {
const RESPONSIVE = `class SubWidget extends owl.Component {
constructor() {
super(...arguments);
this.template = "subwidget";
}
}
class ResponsiveWidget extends Component {
class ResponsiveWidget extends owl.Component {
constructor() {
super(...arguments);
this.template = "responsivewidget";
@@ -992,7 +1054,7 @@ function isMobile() {
}
const env = {
qweb: new QWeb(TEMPLATES),
qweb: new owl.QWeb(TEMPLATES),
isMobile: isMobile()
};
@@ -1001,10 +1063,12 @@ widget.mount(document.body);
window.addEventListener(
"resize",
utils.debounce(function() {
owl.utils.debounce(function() {
const _isMobile = isMobile();
if (_isMobile !== env.isMobile) {
widget.updateEnv({ isMobile: _isMobile });
widget.updateEnv({
isMobile: _isMobile
});
}
}, 20)
);
@@ -1039,13 +1103,12 @@ const RESPONSIVE_CSS = `.info {
margin: 30px;
}`;
const EMPTY = `const {Component, QWeb} = owl;
class Widget extends Component {
const EMPTY = `class App extends owl.Component {
}
const qweb = new QWeb(TEMPLATES);
const widget = new Widget({qweb});
widget.mount(document.body);
const qweb = new owl.QWeb(TEMPLATES);
const app = new App({qweb});
app.mount(document.body);
`;
export const SAMPLES = [
@@ -1067,6 +1130,12 @@ export const SAMPLES = [
xml: WIDGET_COMPOSITION_XML,
css: WIDGET_COMPOSITION_CSS
},
{
description: "Animations",
code: ANIMATION,
xml: ANIMATION_XML,
css: ANIMATION_CSS,
},
{
description: "Lifecycle demo",
code: LIFECYCLE_DEMO,
-20
View File
@@ -1,20 +0,0 @@
import sys
import thread
import webbrowser
import time
import BaseHTTPServer, SimpleHTTPServer
def start_server():
httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 3600), SimpleHTTPServer.SimpleHTTPRequestHandler)
httpd.serve_forever()
thread.start_new_thread(start_server,())
url = 'http://127.0.0.1:3600'
webbrowser.open_new(url)
while True:
try:
time.sleep(1)
except KeyboardInterrupt:
sys.exit(0)