mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] owl: update to v0.13.0
This commit is contained in:
@@ -1,71 +1,6 @@
|
|||||||
(function (exports) {
|
(function (exports) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
|
||||||
* We define here a simple event bus: it can
|
|
||||||
* - emit events
|
|
||||||
* - add/remove listeners.
|
|
||||||
*
|
|
||||||
* This is a useful pattern of communication in many cases. For OWL, each
|
|
||||||
* components and stores are event buses.
|
|
||||||
*/
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// EventBus
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class EventBus {
|
|
||||||
constructor() {
|
|
||||||
this.subscriptions = {};
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Add a listener for the 'eventType' events.
|
|
||||||
*
|
|
||||||
* Note that the 'owner' of this event can be anything, but will more likely
|
|
||||||
* be a widget or a class. The idea is that the callback will be called with
|
|
||||||
* the proper owner bound.
|
|
||||||
*
|
|
||||||
* Also, the owner should be kind of unique. This will be used to remove the
|
|
||||||
* listener.
|
|
||||||
*/
|
|
||||||
on(eventType, owner, callback) {
|
|
||||||
if (!callback) {
|
|
||||||
throw new Error("Missing callback");
|
|
||||||
}
|
|
||||||
if (!this.subscriptions[eventType]) {
|
|
||||||
this.subscriptions[eventType] = [];
|
|
||||||
}
|
|
||||||
this.subscriptions[eventType].push({
|
|
||||||
owner,
|
|
||||||
callback
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Remove a listener
|
|
||||||
*/
|
|
||||||
off(eventType, owner) {
|
|
||||||
const subs = this.subscriptions[eventType];
|
|
||||||
if (subs) {
|
|
||||||
this.subscriptions[eventType] = subs.filter(s => s.owner !== owner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Emit an event of type 'eventType'. Any extra arguments will be passed to
|
|
||||||
* the listeners callback.
|
|
||||||
*/
|
|
||||||
trigger(eventType, ...args) {
|
|
||||||
const subs = this.subscriptions[eventType] || [];
|
|
||||||
for (let i = 0, iLen = subs.length; i < iLen; i++) {
|
|
||||||
const sub = subs[i];
|
|
||||||
sub.callback.call(sub.owner, ...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Remove all subscriptions.
|
|
||||||
*/
|
|
||||||
clear() {
|
|
||||||
this.subscriptions = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Owl Observer
|
* Owl Observer
|
||||||
*
|
*
|
||||||
@@ -171,9 +106,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
set(target, key, value) {
|
set(target, key, value) {
|
||||||
this.rev++;
|
let alreadyDefined = key in target &&
|
||||||
this._addProp(target, key, value);
|
Object.getOwnPropertyDescriptor(target, key).configurable === false;
|
||||||
target.__owl__.rev++;
|
if (alreadyDefined) {
|
||||||
|
target[key] = value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._addProp(target, key, value);
|
||||||
|
this._updateRevNumber(target);
|
||||||
|
}
|
||||||
this.notifyChange();
|
this.notifyChange();
|
||||||
}
|
}
|
||||||
_observeObj(obj, parent) {
|
_observeObj(obj, parent) {
|
||||||
@@ -201,23 +142,26 @@
|
|||||||
},
|
},
|
||||||
set(newVal) {
|
set(newVal) {
|
||||||
if (newVal !== value) {
|
if (newVal !== value) {
|
||||||
self.rev++;
|
|
||||||
if (!self.allowMutations) {
|
if (!self.allowMutations) {
|
||||||
throw new Error(`Observed state cannot be changed here! (key: "${key}", val: "${newVal}")`);
|
throw new Error(`Observed state cannot be changed here! (key: "${key}", val: "${newVal}")`);
|
||||||
}
|
}
|
||||||
|
self._updateRevNumber(obj);
|
||||||
value = newVal;
|
value = newVal;
|
||||||
self.observe(newVal, obj);
|
self.observe(newVal, obj);
|
||||||
obj.__owl__.rev++;
|
|
||||||
let parent = obj;
|
|
||||||
do {
|
|
||||||
parent.__owl__.deepRev++;
|
|
||||||
} while ((parent = parent.__owl__.parent) && parent !== obj);
|
|
||||||
self.notifyChange();
|
self.notifyChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.observe(value, obj);
|
this.observe(value, obj);
|
||||||
}
|
}
|
||||||
|
_updateRevNumber(target) {
|
||||||
|
this.rev++;
|
||||||
|
target.__owl__.rev++;
|
||||||
|
let parent = target;
|
||||||
|
do {
|
||||||
|
parent.__owl__.deepRev++;
|
||||||
|
} while ((parent = parent.__owl__.parent) && parent !== target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -913,6 +857,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return classes.join(" ");
|
return classes.join(" ");
|
||||||
|
},
|
||||||
|
shallowEqual(p1, p2) {
|
||||||
|
for (let k in p1) {
|
||||||
|
if (p1[k] !== p2[k]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
function parseXML(xml) {
|
function parseXML(xml) {
|
||||||
@@ -1069,7 +1021,11 @@
|
|||||||
template = new Function("context", "extra", ctx.code.join("\n"));
|
template = new Function("context", "extra", ctx.code.join("\n"));
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
throw new Error(`Invalid generated code while compiling template '${ctx.templateName.replace(/`/g, "'")}': ${e.message}`);
|
const templateName = ctx.templateName.replace(/`/g, "'");
|
||||||
|
console.groupCollapsed(`Invalid Code generated by ${templateName}`);
|
||||||
|
console.warn(ctx.code.join("\n"));
|
||||||
|
console.groupEnd();
|
||||||
|
throw new Error(`Invalid generated code while compiling template '${templateName}': ${e.message}`);
|
||||||
}
|
}
|
||||||
if (isDebug) {
|
if (isDebug) {
|
||||||
console.log(`Template: ${this.templates[name].elem.outerHTML}\nCompiled code:\n` +
|
console.log(`Template: ${this.templates[name].elem.outerHTML}\nCompiled code:\n` +
|
||||||
@@ -1471,19 +1427,20 @@
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Perform string interpolation on the given string. Note that if the whole
|
* Perform string interpolation on the given string. Note that if the whole
|
||||||
* string is an expression, it simply returns it (formatted).
|
* string is an expression, it simply returns it (formatted and enclosed in
|
||||||
|
* parentheses).
|
||||||
* For instance:
|
* For instance:
|
||||||
* 'Hello {{x}}!' -> `Hello ${x}`
|
* 'Hello {{x}}!' -> `Hello ${x}`
|
||||||
* '{{x}}' -> x
|
* '{{x ? 'a': 'b'}}' -> (x ? 'a' : 'b')
|
||||||
*/
|
*/
|
||||||
interpolate(s) {
|
interpolate(s) {
|
||||||
let matches = s.match(/\{\{.*?\}\}/g);
|
let matches = s.match(/\{\{.*?\}\}/g);
|
||||||
if (matches && matches[0].length === s.length) {
|
if (matches && matches[0].length === s.length) {
|
||||||
return this.formatExpression(s.slice(2, -2));
|
return `(${this.formatExpression(s.slice(2, -2))})`;
|
||||||
}
|
}
|
||||||
matches = s.match(/\#\{.*?\}/g);
|
matches = s.match(/\#\{.*?\}/g);
|
||||||
if (matches && matches[0].length === s.length) {
|
if (matches && matches[0].length === s.length) {
|
||||||
return this.formatExpression(s.slice(2, -1));
|
return `(${this.formatExpression(s.slice(2, -1))})`;
|
||||||
}
|
}
|
||||||
let formatter = expr => "${" + this.formatExpression(expr) + "}";
|
let formatter = expr => "${" + this.formatExpression(expr) + "}";
|
||||||
let r = s
|
let r = s
|
||||||
@@ -1502,7 +1459,7 @@
|
|||||||
// Widget
|
// Widget
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
let nextId = 1;
|
let nextId = 1;
|
||||||
class Component extends EventBus {
|
class Component {
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -1526,7 +1483,6 @@
|
|||||||
* the t-widget directive in a template)
|
* the t-widget directive in a template)
|
||||||
*/
|
*/
|
||||||
constructor(parent, props) {
|
constructor(parent, props) {
|
||||||
super();
|
|
||||||
this.refs = {};
|
this.refs = {};
|
||||||
const defaultProps = this.constructor.defaultProps;
|
const defaultProps = this.constructor.defaultProps;
|
||||||
if (defaultProps) {
|
if (defaultProps) {
|
||||||
@@ -1565,6 +1521,10 @@
|
|||||||
mountedHandlers: {}
|
mountedHandlers: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* The `el` is the root element of the widget. Note that it could be null:
|
||||||
|
* this is the case if the widget is not mounted yet, or is destroyed.
|
||||||
|
*/
|
||||||
get el() {
|
get el() {
|
||||||
return this.__owl__.vnode ? this.__owl__.vnode.elm : null;
|
return this.__owl__.vnode ? this.__owl__.vnode.elm : null;
|
||||||
}
|
}
|
||||||
@@ -1641,10 +1601,16 @@
|
|||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Public
|
// Public
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Mount the component to a target element.
|
||||||
|
*
|
||||||
|
* This should only be done if the component was created manually. Components
|
||||||
|
* created declaratively in templates are managed by the Owl system.
|
||||||
|
*/
|
||||||
async mount(target) {
|
async mount(target) {
|
||||||
const vnode = await this._prepare();
|
const vnode = await this._prepare();
|
||||||
if (this.__owl__.isDestroyed) {
|
if (this.__owl__.isDestroyed) {
|
||||||
// widget was destroyed before we get here...
|
// component was destroyed before we get here...
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._patch(vnode);
|
this._patch(vnode);
|
||||||
@@ -1653,34 +1619,6 @@
|
|||||||
this._callMounted();
|
this._callMounted();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_callMounted() {
|
|
||||||
const __owl__ = this.__owl__;
|
|
||||||
const children = __owl__.children;
|
|
||||||
for (let id in children) {
|
|
||||||
const comp = children[id];
|
|
||||||
if (!comp.__owl__.isMounted && this.el.contains(comp.el)) {
|
|
||||||
comp._callMounted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
__owl__.isMounted = true;
|
|
||||||
const handlers = __owl__.mountedHandlers;
|
|
||||||
for (let key in handlers) {
|
|
||||||
handlers[key]();
|
|
||||||
}
|
|
||||||
this.mounted();
|
|
||||||
}
|
|
||||||
_callWillUnmount() {
|
|
||||||
this.willUnmount();
|
|
||||||
const __owl__ = this.__owl__;
|
|
||||||
__owl__.isMounted = false;
|
|
||||||
const children = __owl__.children;
|
|
||||||
for (let id in children) {
|
|
||||||
const comp = children[id];
|
|
||||||
if (comp.__owl__.isMounted) {
|
|
||||||
comp._callWillUnmount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unmount() {
|
unmount() {
|
||||||
if (this.__owl__.isMounted) {
|
if (this.__owl__.isMounted) {
|
||||||
this._callWillUnmount();
|
this._callWillUnmount();
|
||||||
@@ -1717,6 +1655,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Destroy the component. This operation is quite complex:
|
||||||
|
* - it recursively destroy all children
|
||||||
|
* - call the willUnmount hooks if necessary
|
||||||
|
* - remove the dom node from the dom
|
||||||
|
*
|
||||||
|
* This should only be called manually if you created the widget. Most widgets
|
||||||
|
* will be automatically destroyed.
|
||||||
|
*/
|
||||||
destroy() {
|
destroy() {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
if (!__owl__.isDestroyed) {
|
if (!__owl__.isDestroyed) {
|
||||||
@@ -1727,26 +1674,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_destroy(parent) {
|
/**
|
||||||
const __owl__ = this.__owl__;
|
* This method is called by the component system whenever its props are
|
||||||
const isMounted = __owl__.isMounted;
|
* updated. If it returns true, then the component will be rendered.
|
||||||
if (isMounted) {
|
* Otherwise, it will skip the rendering (also, its props will not be updated)
|
||||||
this.willUnmount();
|
*/
|
||||||
__owl__.isMounted = false;
|
|
||||||
}
|
|
||||||
const children = __owl__.children;
|
|
||||||
for (let key in children) {
|
|
||||||
children[key]._destroy(this);
|
|
||||||
}
|
|
||||||
if (parent) {
|
|
||||||
let id = __owl__.id;
|
|
||||||
delete parent.__owl__.children[id];
|
|
||||||
__owl__.parent = null;
|
|
||||||
}
|
|
||||||
this.clear();
|
|
||||||
__owl__.isDestroyed = true;
|
|
||||||
delete __owl__.vnode;
|
|
||||||
}
|
|
||||||
shouldUpdate(nextProps) {
|
shouldUpdate(nextProps) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1769,12 +1701,79 @@
|
|||||||
await this.render(true);
|
await this.render(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Sets a key (from the state) to a specific value. This is mostly useful to
|
||||||
|
* work around the limitation in observed value with new keys.
|
||||||
|
*/
|
||||||
set(target, key, value) {
|
set(target, key, value) {
|
||||||
this.__owl__.observer.set(target, key, value);
|
this.__owl__.observer.set(target, key, value);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Emit a custom event of type 'eventType' with the given 'payload' on the
|
||||||
|
* component's el, if it exists. However, note that the event will only bubble
|
||||||
|
* up to the parent DOM nodes. Thus, it must be called between mounted() and
|
||||||
|
* willUnmount().
|
||||||
|
*/
|
||||||
|
trigger(eventType, payload) {
|
||||||
|
if (this.el) {
|
||||||
|
const ev = new CustomEvent(eventType, {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
detail: payload
|
||||||
|
});
|
||||||
|
this.el.dispatchEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Private
|
// Private
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
_destroy(parent) {
|
||||||
|
const __owl__ = this.__owl__;
|
||||||
|
const isMounted = __owl__.isMounted;
|
||||||
|
if (isMounted) {
|
||||||
|
this.willUnmount();
|
||||||
|
__owl__.isMounted = false;
|
||||||
|
}
|
||||||
|
const children = __owl__.children;
|
||||||
|
for (let key in children) {
|
||||||
|
children[key]._destroy(this);
|
||||||
|
}
|
||||||
|
if (parent) {
|
||||||
|
let id = __owl__.id;
|
||||||
|
delete parent.__owl__.children[id];
|
||||||
|
__owl__.parent = null;
|
||||||
|
}
|
||||||
|
__owl__.isDestroyed = true;
|
||||||
|
delete __owl__.vnode;
|
||||||
|
}
|
||||||
|
_callMounted() {
|
||||||
|
const __owl__ = this.__owl__;
|
||||||
|
const children = __owl__.children;
|
||||||
|
for (let id in children) {
|
||||||
|
const comp = children[id];
|
||||||
|
if (!comp.__owl__.isMounted && this.el.contains(comp.el)) {
|
||||||
|
comp._callMounted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__owl__.isMounted = true;
|
||||||
|
const handlers = __owl__.mountedHandlers;
|
||||||
|
for (let key in handlers) {
|
||||||
|
handlers[key]();
|
||||||
|
}
|
||||||
|
this.mounted();
|
||||||
|
}
|
||||||
|
_callWillUnmount() {
|
||||||
|
this.willUnmount();
|
||||||
|
const __owl__ = this.__owl__;
|
||||||
|
__owl__.isMounted = false;
|
||||||
|
const children = __owl__.children;
|
||||||
|
for (let id in children) {
|
||||||
|
const comp = children[id];
|
||||||
|
if (comp.__owl__.isMounted) {
|
||||||
|
comp._callWillUnmount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
async _updateProps(nextProps, forceUpdate = false, patchQueue) {
|
async _updateProps(nextProps, forceUpdate = false, patchQueue) {
|
||||||
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
|
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
|
||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
@@ -1994,6 +1993,71 @@
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We define here a simple event bus: it can
|
||||||
|
* - emit events
|
||||||
|
* - add/remove listeners.
|
||||||
|
*
|
||||||
|
* This is a useful pattern of communication in many cases. For OWL, each
|
||||||
|
* components and stores are event buses.
|
||||||
|
*/
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// EventBus
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
class EventBus {
|
||||||
|
constructor() {
|
||||||
|
this.subscriptions = {};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Add a listener for the 'eventType' events.
|
||||||
|
*
|
||||||
|
* Note that the 'owner' of this event can be anything, but will more likely
|
||||||
|
* be a widget or a class. The idea is that the callback will be called with
|
||||||
|
* the proper owner bound.
|
||||||
|
*
|
||||||
|
* Also, the owner should be kind of unique. This will be used to remove the
|
||||||
|
* listener.
|
||||||
|
*/
|
||||||
|
on(eventType, owner, callback) {
|
||||||
|
if (!callback) {
|
||||||
|
throw new Error("Missing callback");
|
||||||
|
}
|
||||||
|
if (!this.subscriptions[eventType]) {
|
||||||
|
this.subscriptions[eventType] = [];
|
||||||
|
}
|
||||||
|
this.subscriptions[eventType].push({
|
||||||
|
owner,
|
||||||
|
callback
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Remove a listener
|
||||||
|
*/
|
||||||
|
off(eventType, owner) {
|
||||||
|
const subs = this.subscriptions[eventType];
|
||||||
|
if (subs) {
|
||||||
|
this.subscriptions[eventType] = subs.filter(s => s.owner !== owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Emit an event of type 'eventType'. Any extra arguments will be passed to
|
||||||
|
* the listeners callback.
|
||||||
|
*/
|
||||||
|
trigger(eventType, ...args) {
|
||||||
|
const subs = this.subscriptions[eventType] || [];
|
||||||
|
for (let i = 0, iLen = subs.length; i < iLen; i++) {
|
||||||
|
const sub = subs[i];
|
||||||
|
sub.callback.call(sub.owner, ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Remove all subscriptions.
|
||||||
|
*/
|
||||||
|
clear() {
|
||||||
|
this.subscriptions = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Owl QWeb Directives
|
* Owl QWeb Directives
|
||||||
*
|
*
|
||||||
@@ -2281,7 +2345,7 @@
|
|||||||
* - t-on
|
* - t-on
|
||||||
* - t-ref
|
* - t-ref
|
||||||
* - t-transition
|
* - t-transition
|
||||||
* - t-widget/t-props/t-keepalive
|
* - t-widget/t-keepalive
|
||||||
* - t-mounted
|
* - t-mounted
|
||||||
*/
|
*/
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -2426,6 +2490,9 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// t-widget
|
// t-widget
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
|
||||||
|
self: "if (e.target !== vn.elm) {return}"
|
||||||
|
});
|
||||||
/**
|
/**
|
||||||
* The t-widget directive is certainly a complicated and hard to maintain piece
|
* The t-widget directive is certainly a complicated and hard to maintain piece
|
||||||
* of code. To help you, fellow developer, if you have to maintain it, I offer
|
* of code. To help you, fellow developer, if you have to maintain it, I offer
|
||||||
@@ -2438,11 +2505,15 @@
|
|||||||
* ```xml
|
* ```xml
|
||||||
* <t t-widget="child"
|
* <t t-widget="child"
|
||||||
* t-key="'somestring'"
|
* t-key="'somestring'"
|
||||||
* t-props="{flag:state.flag}"
|
* flag="state.flag"
|
||||||
* t-transition="fade"/>
|
* t-transition="fade"/>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* ```js
|
* ```js
|
||||||
|
* // we assign utils on top of the function because it will be useful for
|
||||||
|
* // each widgets
|
||||||
|
* let utils = this.utils;
|
||||||
|
*
|
||||||
* // this is the virtual node representing the parent div
|
* // this is the virtual node representing the parent div
|
||||||
* let c1 = [], p1 = { key: 1 };
|
* let c1 = [], p1 = { key: 1 };
|
||||||
* var vn1 = h("div", p1, c1);
|
* var vn1 = h("div", p1, c1);
|
||||||
@@ -2457,7 +2528,7 @@
|
|||||||
* let _2_index = c1.length;
|
* let _2_index = c1.length;
|
||||||
* c1.push(null);
|
* c1.push(null);
|
||||||
*
|
*
|
||||||
* // def3 is the deferred that will contain later either the new widget
|
* // def3 is the promise that will contain later either the new widget
|
||||||
* // creation, or the props update...
|
* // creation, or the props update...
|
||||||
* let def3;
|
* let def3;
|
||||||
*
|
*
|
||||||
@@ -2476,31 +2547,32 @@
|
|||||||
* // computation, so it is certainly better to do it only once
|
* // computation, so it is certainly better to do it only once
|
||||||
* let props4 = { flag: context["state"].flag };
|
* let props4 = { flag: context["state"].flag };
|
||||||
*
|
*
|
||||||
* // If we have a widget, currently rendering, but not ready yet, and which was
|
* // If we have a widget, currently rendering, but not ready yet, we do not want
|
||||||
* // rendered with different props, we do not want to wait for it to be ready,
|
* // to wait for it to be ready if we can avoid it
|
||||||
* // then update it. We simply destroy it, and start anew.
|
* if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode) {
|
||||||
* if (
|
* // we check if the props are the same. In that case, we can simply reuse
|
||||||
* w4 &&
|
* // the previous rendering and skip all useless work
|
||||||
* w4.__owl__.renderPromise &&
|
* if (utils.shallowEqual(props4, w4.__owl__.renderProps)) {
|
||||||
* !w4.__owl__.isStarted &&
|
* def3 = w4.__owl__.renderPromise;
|
||||||
* props4 !== w4.__owl__.renderProps
|
* } else {
|
||||||
* ) {
|
* // if the props are not the same, we destroy the widget and starts anew.
|
||||||
* w4.destroy();
|
* // this will be faster than waiting for its rendering, then updating it
|
||||||
* w4 = false;
|
* w4.destroy();
|
||||||
|
* w4 = false;
|
||||||
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* if (!w4) {
|
* if (!w4) {
|
||||||
* // in this situation, we need to create a new widget. First step is
|
* // in this situation, we need to create a new widget. First step is
|
||||||
* // to get a reference to the class, then create an instance with
|
* // to get a reference to the class, then create an instance with
|
||||||
* // current context as parent, and the props.
|
* // current context as parent, and the props.
|
||||||
* let W4 = context.widgets["child"];
|
* let W4 = context.widgets && context.widgets[widgetKey4] || QWeb.widgets[widgetKey4];
|
||||||
|
|
||||||
* if (!W4) {
|
* if (!W4) {
|
||||||
* throw new Error("Cannot find the definition of widget 'child'");
|
* throw new Error("Cannot find the definition of widget 'child'");
|
||||||
* }
|
* }
|
||||||
* w4 = new W4(owner, props4);
|
* w4 = new W4(owner, props4);
|
||||||
*
|
*
|
||||||
* let utils = this.utils;
|
|
||||||
*
|
|
||||||
* // Whenever we rerender the parent widget, we need to be sure that we
|
* // Whenever we rerender the parent widget, we need to be sure that we
|
||||||
* // are able to find the widget instance. To do that, we register it to
|
* // are able to find the widget instance. To do that, we register it to
|
||||||
* // the parent cmap (children map). Note that the 'template' key is
|
* // the parent cmap (children map). Note that the 'template' key is
|
||||||
@@ -2557,7 +2629,9 @@
|
|||||||
* } else {
|
* } else {
|
||||||
* // this is the 'update' path of the directive.
|
* // this is the 'update' path of the directive.
|
||||||
* // the call to _updateProps is the actual widget update
|
* // the call to _updateProps is the actual widget update
|
||||||
* def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue);
|
* // Note that we only update the props if we cannot reuse the previous
|
||||||
|
* // rendering work (in the case it was rendered with the same props)
|
||||||
|
* def3 = def3 || w4._updateProps(props4, extra.forceUpdate, extra.patchQueue);
|
||||||
* def3 = def3.then(() => {
|
* def3 = def3.then(() => {
|
||||||
* // if widget was destroyed in the meantime, we do nothing (so, this
|
* // if widget was destroyed in the meantime, we do nothing (so, this
|
||||||
* // means that the parent's element children list will have a null in
|
* // means that the parent's element children list will have a null in
|
||||||
@@ -2588,28 +2662,42 @@
|
|||||||
ctx.rootContext.shouldDefineOwner = true;
|
ctx.rootContext.shouldDefineOwner = true;
|
||||||
ctx.rootContext.shouldDefineQWeb = true;
|
ctx.rootContext.shouldDefineQWeb = true;
|
||||||
ctx.rootContext.shouldDefineUtils = true;
|
ctx.rootContext.shouldDefineUtils = true;
|
||||||
let props = node.getAttribute("t-props");
|
|
||||||
let keepAlive = node.getAttribute("t-keepalive") ? true : false;
|
let keepAlive = node.getAttribute("t-keepalive") ? true : false;
|
||||||
// t-on- events and t-transition
|
// t-on- events and t-transition
|
||||||
const events = [];
|
const events = [];
|
||||||
let transition = "";
|
let transition = "";
|
||||||
const attributes = node.attributes;
|
const attributes = node.attributes;
|
||||||
|
const props = {};
|
||||||
for (let i = 0; i < attributes.length; i++) {
|
for (let i = 0; i < attributes.length; i++) {
|
||||||
const name = attributes[i].name;
|
const name = attributes[i].name;
|
||||||
|
const value = attributes[i].textContent;
|
||||||
if (name.startsWith("t-on-")) {
|
if (name.startsWith("t-on-")) {
|
||||||
events.push([name.slice(5), attributes[i].textContent]);
|
const [eventName, ...mods] = name.slice(5).split(".");
|
||||||
|
let extraArgs;
|
||||||
|
let handlerName = value.replace(/\(.*\)/, function (args) {
|
||||||
|
extraArgs = args.slice(1, -1);
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
events.push([eventName, mods, handlerName, extraArgs]);
|
||||||
}
|
}
|
||||||
else if (name === "t-transition") {
|
else if (name === "t-transition") {
|
||||||
transition = attributes[i].textContent;
|
transition = value;
|
||||||
|
}
|
||||||
|
else if (!name.startsWith("t-")) {
|
||||||
|
if (name !== "class" && name !== "style") {
|
||||||
|
// this is a prop!
|
||||||
|
props[name] = ctx.formatExpression(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let key = node.getAttribute("t-key");
|
let key = node.getAttribute("t-key");
|
||||||
if (key) {
|
if (key) {
|
||||||
key = ctx.formatExpression(key);
|
key = ctx.formatExpression(key);
|
||||||
}
|
}
|
||||||
if (props) {
|
// computing the props string representing the props object
|
||||||
props = ctx.formatExpression(props);
|
let propStr = Object.keys(props)
|
||||||
}
|
.map(k => k + ":" + props[k])
|
||||||
|
.join(",");
|
||||||
let dummyID = ctx.generateID();
|
let dummyID = ctx.generateID();
|
||||||
let defID = ctx.generateID();
|
let defID = ctx.generateID();
|
||||||
let widgetID = ctx.generateID();
|
let widgetID = ctx.generateID();
|
||||||
@@ -2660,7 +2748,7 @@
|
|||||||
tattStyle = attVar;
|
tattStyle = attVar;
|
||||||
}
|
}
|
||||||
let updateClassCode = "";
|
let updateClassCode = "";
|
||||||
if (classAttr || tattClass || styleAttr || tattStyle) {
|
if (classAttr || tattClass || styleAttr || tattStyle || events.length) {
|
||||||
let classCode = "";
|
let classCode = "";
|
||||||
if (classAttr) {
|
if (classAttr) {
|
||||||
classCode =
|
classCode =
|
||||||
@@ -2679,15 +2767,40 @@
|
|||||||
}`;
|
}`;
|
||||||
updateClassCode = `let cl=w${widgetID}.el.classList;for (let k in ${attVar}) {if (${attVar}[k]) {cl.add(k)} else {cl.remove(k)}}`;
|
updateClassCode = `let cl=w${widgetID}.el.classList;for (let k in ${attVar}) {if (${attVar}[k]) {cl.add(k)} else {cl.remove(k)}}`;
|
||||||
}
|
}
|
||||||
|
let eventsCode = events
|
||||||
|
.map(function ([eventName, mods, handlerName, extraArgs]) {
|
||||||
|
let params = extraArgs
|
||||||
|
? `owner, ${ctx.formatExpression(extraArgs)}`
|
||||||
|
: "owner";
|
||||||
|
let handler;
|
||||||
|
if (mods.length > 0) {
|
||||||
|
handler = `function (e) {`;
|
||||||
|
handler += mods
|
||||||
|
.map(function (mod) {
|
||||||
|
return T_WIDGET_MODS_CODE[mod];
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
handler += `owner['${handlerName}'].call(${params}, e);}`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
handler = `owner['${handlerName}'].bind(${params})`;
|
||||||
|
}
|
||||||
|
return `vn.elm.addEventListener('${eventName}', ${handler});`;
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
const styleExpr = tattStyle || (styleAttr ? `'${styleAttr}'` : false);
|
const styleExpr = tattStyle || (styleAttr ? `'${styleAttr}'` : false);
|
||||||
const styleCode = styleExpr ? `vn.elm.style = ${styleExpr}` : "";
|
const styleCode = styleExpr ? `vn.elm.style = ${styleExpr};` : "";
|
||||||
createHook = `vnode.data.hook = {create(_, vn){${classCode}${styleCode}}};`;
|
createHook = `vnode.data.hook = {create(_, vn){${classCode}${styleCode}${eventsCode}}};`;
|
||||||
}
|
}
|
||||||
ctx.addLine(`let w${widgetID} = ${templateID} in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[${templateID}]] : false;`);
|
ctx.addLine(`let w${widgetID} = ${templateID} in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[${templateID}]] : false;`);
|
||||||
ctx.addLine(`let props${widgetID} = ${props || "{}"};`);
|
ctx.addLine(`let props${widgetID} = {${propStr}};`);
|
||||||
ctx.addIf(`w${widgetID} && w${widgetID}.__owl__.renderPromise && !w${widgetID}.__owl__.vnode && props${widgetID} !== w${widgetID}.__owl__.renderProps`);
|
ctx.addIf(`w${widgetID} && w${widgetID}.__owl__.renderPromise && !w${widgetID}.__owl__.vnode`);
|
||||||
|
ctx.addIf(`utils.shallowEqual(props${widgetID}, w${widgetID}.__owl__.renderProps)`);
|
||||||
|
ctx.addLine(`def${defID} = w${widgetID}.__owl__.renderPromise;`);
|
||||||
|
ctx.addElse();
|
||||||
ctx.addLine(`w${widgetID}.destroy();`);
|
ctx.addLine(`w${widgetID}.destroy();`);
|
||||||
ctx.addLine(`w${widgetID} = false`);
|
ctx.addLine(`w${widgetID} = false;`);
|
||||||
|
ctx.closeIf();
|
||||||
ctx.closeIf();
|
ctx.closeIf();
|
||||||
ctx.addIf(`!w${widgetID}`);
|
ctx.addIf(`!w${widgetID}`);
|
||||||
// new widget
|
// new widget
|
||||||
@@ -2697,16 +2810,13 @@
|
|||||||
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;`);
|
||||||
for (let [event, method] of events) {
|
|
||||||
ctx.addLine(`w${widgetID}.on('${event}', owner, owner['${method}'])`);
|
|
||||||
}
|
|
||||||
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
|
||||||
ctx.addLine(`def${defID} = def${defID}.then(vnode=>{${createHook}let pvnode=h(vnode.sel, {key: ${templateID}, hook: {insert(vn) {let nvn=w${widgetID}._mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeWidgetCode}}}});c${ctx.parentNode}[_${dummyID}_index]=pvnode;w${widgetID}.__owl__.pvnode = pvnode;});`);
|
ctx.addLine(`def${defID} = def${defID}.then(vnode=>{${createHook}let pvnode=h(vnode.sel, {key: ${templateID}, hook: {insert(vn) {let nvn=w${widgetID}._mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeWidgetCode}}}});c${ctx.parentNode}[_${dummyID}_index]=pvnode;w${widgetID}.__owl__.pvnode = pvnode;});`);
|
||||||
ctx.addElse();
|
ctx.addElse();
|
||||||
// need to update widget
|
// need to update widget
|
||||||
ctx.addLine(`def${defID} = w${widgetID}._updateProps(props${widgetID}, extra.forceUpdate, extra.patchQueue);`);
|
ctx.addLine(`def${defID} = def${defID} || w${widgetID}._updateProps(props${widgetID}, extra.forceUpdate, extra.patchQueue);`);
|
||||||
let keepAliveCode = "";
|
let keepAliveCode = "";
|
||||||
if (keepAlive) {
|
if (keepAlive) {
|
||||||
keepAliveCode = `pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w${widgetID}.el,vn.elm);vn.elm=w${widgetID}.el;w${widgetID}._remount();};`;
|
keepAliveCode = `pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w${widgetID}.el,vn.elm);vn.elm=w${widgetID}.el;w${widgetID}._remount();};`;
|
||||||
@@ -3068,9 +3178,9 @@
|
|||||||
exports.connect = connect;
|
exports.connect = connect;
|
||||||
exports.utils = utils;
|
exports.utils = utils;
|
||||||
|
|
||||||
exports.__info__.version = '0.12.0';
|
exports.__info__.version = '0.13.0';
|
||||||
exports.__info__.date = '2019-05-29T09:26:54.764Z';
|
exports.__info__.date = '2019-06-05T08:48:29.138Z';
|
||||||
exports.__info__.hash = 'f60904b';
|
exports.__info__.hash = '7d3c374';
|
||||||
exports.__info__.url = 'https://github.com/odoo/owl';
|
exports.__info__.url = 'https://github.com/odoo/owl';
|
||||||
|
|
||||||
}(this.owl = this.owl || {}));
|
}(this.owl = this.owl || {}));
|
||||||
|
|||||||
+30
-27
@@ -39,26 +39,32 @@ const DEFAULT_HTML = `<!DOCTYPE html>
|
|||||||
</html>
|
</html>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const APP_PY = `import sys
|
const APP_PY = `#!/usr/bin/env python3
|
||||||
import thread
|
|
||||||
import webbrowser
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import BaseHTTPServer, SimpleHTTPServer
|
from http.server import SimpleHTTPRequestHandler, HTTPServer
|
||||||
|
|
||||||
def start_server():
|
def start_server():
|
||||||
httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 3600), SimpleHTTPServer.SimpleHTTPRequestHandler)
|
SimpleHTTPRequestHandler.extensions_map['.js'] = 'application/javascript'
|
||||||
|
httpd = HTTPServer(('0.0.0.0', 3600), SimpleHTTPRequestHandler)
|
||||||
httpd.serve_forever()
|
httpd.serve_forever()
|
||||||
|
|
||||||
thread.start_new_thread(start_server,())
|
|
||||||
url = 'http://127.0.0.1:3600'
|
url = 'http://127.0.0.1:3600'
|
||||||
webbrowser.open_new(url)
|
|
||||||
|
|
||||||
while True:
|
if __name__ == "__main__":
|
||||||
try:
|
print("Owl Application")
|
||||||
time.sleep(1)
|
print("---------------")
|
||||||
except KeyboardInterrupt:
|
print("Server running on: {}".format(url))
|
||||||
sys.exit(0)
|
threading.Thread(target=start_server, daemon=True).start()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
httpd.server_close()
|
||||||
|
quit(0)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -242,20 +248,20 @@ class App extends owl.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateCode(ev) {
|
updateCode(ev) {
|
||||||
this.state[ev.type] = ev.value;
|
this.state[ev.detail.type] = ev.detail.value;
|
||||||
}
|
}
|
||||||
toggleLayout() {
|
toggleLayout() {
|
||||||
this.state.splitLayout = !this.state.splitLayout;
|
this.state.splitLayout = !this.state.splitLayout;
|
||||||
}
|
}
|
||||||
updatePanelHeight(ev) {
|
updatePanelHeight(ev) {
|
||||||
if (!ev.delta) {
|
if (!ev.detail.delta) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let height = this.state.topPanelHeight;
|
let height = this.state.topPanelHeight;
|
||||||
if (!height) {
|
if (!height) {
|
||||||
height = document.getElementsByClassName("tabbed-editor")[0].clientHeight;
|
height = document.getElementsByClassName("tabbed-editor")[0].clientHeight;
|
||||||
}
|
}
|
||||||
this.state.topPanelHeight = height + ev.delta;
|
this.state.topPanelHeight = height + ev.detail.delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadCode() {
|
async downloadCode() {
|
||||||
@@ -287,10 +293,11 @@ class TabbedEditor extends owl.Component {
|
|||||||
this.sessions[tab].setUndoManager(new ace.UndoManager());
|
this.sessions[tab].setUndoManager(new ace.UndoManager());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.editor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.editor = ace.edit(this.refs.editor);
|
this.editor = this.editor || ace.edit(this.refs.editor);
|
||||||
|
|
||||||
this.editor.setValue(this.props[this.state.currentTab], -1);
|
this.editor.setValue(this.props[this.state.currentTab], -1);
|
||||||
this.editor.setFontSize("12px");
|
this.editor.setFontSize("12px");
|
||||||
@@ -314,20 +321,16 @@ class TabbedEditor extends owl.Component {
|
|||||||
const session = this.sessions[this.state.currentTab];
|
const session = this.sessions[this.state.currentTab];
|
||||||
session.setValue(this.props[this.state.currentTab], -1);
|
session.setValue(this.props[this.state.currentTab], -1);
|
||||||
this.editor.setSession(session);
|
this.editor.setSession(session);
|
||||||
}
|
this.editor.resize();
|
||||||
|
|
||||||
willUnmount() {
|
|
||||||
this.editor.destroy();
|
|
||||||
delete this.editor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTab(tab) {
|
setTab(tab) {
|
||||||
if (this.state.currentTab !== tab) {
|
if (this.state.currentTab !== tab) {
|
||||||
this.state.currentTab = tab;
|
this.state.currentTab = tab;
|
||||||
const session = this.sessions[this.state.currentTab];
|
const session = this.sessions[this.state.currentTab];
|
||||||
session.doc.setValue(this.props[tab], -1);
|
session.doc.setValue(this.props[tab], -1);
|
||||||
this.editor.setSession(session);
|
this.editor.setSession(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseDown(ev) {
|
onMouseDown(ev) {
|
||||||
|
|||||||
@@ -185,4 +185,6 @@ body {
|
|||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
color: darkred;
|
color: darkred;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
padding-left: 30px;
|
||||||
|
padding-right: 30px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ const WIDGET_COMPOSITION_XML = `<templates>
|
|||||||
|
|
||||||
<div t-name="App">
|
<div t-name="App">
|
||||||
<t t-widget="ClickCounter"/>
|
<t t-widget="ClickCounter"/>
|
||||||
<t t-widget="InputWidget" t-props="{reverse: true}"/>
|
<t t-widget="InputWidget" reverse="true"/>
|
||||||
</div>
|
</div>
|
||||||
</templates>`;
|
</templates>`;
|
||||||
|
|
||||||
@@ -287,7 +287,7 @@ const LIFECYCLE_DEMO_XML = `<templates>
|
|||||||
<button t-on-click="increment">Increment</button>
|
<button t-on-click="increment">Increment</button>
|
||||||
<button t-on-click="toggleSubWidget">ToggleSubWidget</button>
|
<button t-on-click="toggleSubWidget">ToggleSubWidget</button>
|
||||||
<div t-if="state.flag">
|
<div t-if="state.flag">
|
||||||
<t t-widget="HookWidget" t-props="{n:state.n}"/>
|
<t t-widget="HookWidget" n="state.n"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div t-name="HookWidget" t-on-click="increment">Demo Sub Widget. Props: <t t-esc="props.n"/>. State: <t t-esc="state.n"/>. (click on me to update me)</div>
|
<div t-name="HookWidget" t-on-click="increment">Demo Sub Widget. Props: <t t-esc="props.n"/>. State: <t t-esc="state.n"/>. (click on me to update me)</div>
|
||||||
@@ -515,7 +515,7 @@ const TODO_APP_STORE_XML = `<templates>
|
|||||||
<label for="toggle-all"></label>
|
<label for="toggle-all"></label>
|
||||||
<ul class="todo-list">
|
<ul class="todo-list">
|
||||||
<t t-foreach="visibleTodos" t-as="todo">
|
<t t-foreach="visibleTodos" t-as="todo">
|
||||||
<t t-widget="TodoItem" t-key="todo.id" t-props="todo"/>
|
<t t-widget="TodoItem" t-key="todo.id" id="todo.id" completed="todo.completed" title="todo.title"/>
|
||||||
</t>
|
</t>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
+12
-10
@@ -11,7 +11,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div t-name="App" class="playground">
|
<div t-name="App" class="playground">
|
||||||
<div class="left-bar" t-att-style="leftPaneStyle" t-att-class="{split: state.splitLayout}">
|
<div class="left-bar" t-att-class="{split: state.splitLayout}"
|
||||||
|
t-att-style="leftPaneStyle"
|
||||||
|
t-on-updateCode="updateCode">
|
||||||
<div class="menubar">
|
<div class="menubar">
|
||||||
<a class="btn run-code flash" t-on-click="runCode" title="Execute this Code">▶ Run</a>
|
<a class="btn run-code flash" t-on-click="runCode" title="Execute this Code">▶ Run</a>
|
||||||
<select t-on-change="setSample">
|
<select t-on-change="setSample">
|
||||||
@@ -22,20 +24,20 @@
|
|||||||
<a class="btn flash" t-on-click="downloadCode" title="Download a Zip with this Code"><i class="fas fa-download"></i></a>
|
<a class="btn flash" t-on-click="downloadCode" title="Download a Zip with this Code"><i class="fas fa-download"></i></a>
|
||||||
<a class="layout-selector flash" t-on-click="toggleLayout" title="Toggle Layout"><i class="fas" t-att-class="state.splitLayout ? 'fa-toggle-on' : 'fa-toggle-off'"></i></a>
|
<a class="layout-selector flash" t-on-click="toggleLayout" title="Toggle Layout"><i class="fas" t-att-class="state.splitLayout ? 'fa-toggle-on' : 'fa-toggle-off'"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
<t t-widget="TabbedEditor"
|
||||||
|
js="state.js"
|
||||||
|
css="!state.splitLayout and state.css"
|
||||||
|
xml="!state.splitLayout and state.js"
|
||||||
|
t-att-style="topEditorStyle"/>
|
||||||
<t t-if="state.splitLayout">
|
<t t-if="state.splitLayout">
|
||||||
<t t-widget="TabbedEditor"
|
|
||||||
t-props="{js:state.js, css:false, xml: false}"
|
|
||||||
t-on-updateCode="updateCode"
|
|
||||||
t-att-style="topEditorStyle"/>
|
|
||||||
<div class="separator horizontal"/>
|
<div class="separator horizontal"/>
|
||||||
<t t-widget="TabbedEditor" t-keepalive="1"
|
<t t-widget="TabbedEditor" t-keepalive="1"
|
||||||
t-props="{js:false, css:state.css, xml: state.xml, resizeable: true}"
|
js="false"
|
||||||
t-on-updateCode="updateCode"
|
css="state.css"
|
||||||
|
xml="state.xml"
|
||||||
|
resizeable="true"
|
||||||
t-on-updatePanelHeight="updatePanelHeight"/>
|
t-on-updatePanelHeight="updatePanelHeight"/>
|
||||||
</t>
|
</t>
|
||||||
<t t-else="1">
|
|
||||||
<t t-widget="TabbedEditor" t-props="{js:state.js, css:state.css, xml: state.xml, display: 'js|xml|css'}" t-on-updateCode="updateCode"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="separator vertical" t-on-mousedown="onMouseDown"/>
|
<div class="separator vertical" t-on-mousedown="onMouseDown"/>
|
||||||
<div class="right-pane" t-att-style="rightPaneStyle">
|
<div class="right-pane" t-att-style="rightPaneStyle">
|
||||||
|
|||||||
Reference in New Issue
Block a user