[IMP] update owl/playground (to v0.11.0)

This commit is contained in:
Géry Debongnie
2019-05-17 23:39:22 +02:00
parent 5cd6f3ebb3
commit 78b4938fbd
5 changed files with 1012 additions and 834 deletions
+780 -618
View File
File diff suppressed because it is too large Load Diff
+170 -138
View File
@@ -1,5 +1,8 @@
import { SAMPLES } from "./samples.js"; import { SAMPLES } from "./samples.js";
//------------------------------------------------------------------------------
// Constants, helpers, utils
//------------------------------------------------------------------------------
let owlJS; let owlJS;
async function owlSourceCode() { async function owlSourceCode() {
@@ -58,82 +61,90 @@ while True:
sys.exit(0) sys.exit(0)
`; `;
//------------------------------------------------------------------------------ /**
// Tabbed editor * Make an iframe, with all the js, css and xml properly injected.
//------------------------------------------------------------------------------ */
class TabbedEditor extends owl.Component { function makeCodeIframe(js, css, xml, errorHandler) {
constructor() { // check templates
super(...arguments); var qweb = new owl.QWeb();
this.template = "tabbed-editor"; const sanitizedXML = xml.replace(/<!--[\s\S]*?-->/g, "");
this.state = {
currentTab: this.props.display.split("|")[0]
};
this.tabs = {
js: this.props.display.includes("js"),
xml: this.props.display.includes("xml"),
css: this.props.display.includes("css")
};
}
mounted() { // will throw error if there is something wrong with xml
this.editor = ace.edit(this.refs.editor); qweb.addTemplates(sanitizedXML);
// remove this for xml/css (?) // create iframe
this.editor.session.setOption("useWorker", false); const iframe = document.createElement("iframe");
this.editor.setValue(this.props[this.state.currentTab], -1);
this.editor.setFontSize("12px"); iframe.onload = () => {
this.editor.setTheme("ace/theme/monokai"); const doc = iframe.contentDocument;
this.editor.session.setMode(MODES[this.state.currentTab]); // inject js
const tabSize = this.state.currentTab === "xml" ? 2 : 4; const owlScript = doc.createElement("script");
this.editor.session.setOption("tabSize", tabSize); owlScript.type = "text/javascript";
this.editor.on("blur", () => { owlScript.src = "../owl.js";
const editorValue = this.editor.getValue(); owlScript.addEventListener("load", () => {
const propsValue = this.props[this.state.currentTab]; const script = doc.createElement("script");
if (editorValue !== propsValue) { script.type = "text/javascript";
this.trigger("updateCode", { const content = `window.TEMPLATES = \`${sanitizedXML}\`\n${js}`;
type: this.state.currentTab, script.innerHTML = content;
value: editorValue iframe.contentWindow.addEventListener("error", errorHandler);
}); iframe.contentWindow.addEventListener("unhandledrejection", errorHandler);
} setTimeout(function() {
if (iframe.contentWindow) {
iframe.contentWindow.removeEventListener("error", errorHandler);
iframe.contentWindow.removeEventListener(
"unhandledrejection",
errorHandler
);
}
}, 200);
doc.body.appendChild(script);
}); });
doc.head.appendChild(owlScript);
// inject css
const style = document.createElement("style");
style.innerHTML = css;
doc.head.appendChild(style);
};
return iframe;
}
/**
* Make a zip file containing a functioning application
*/
async function makeApp(js, css, xml) {
await owl.utils.loadJS("libs/jszip.min.js");
const zip = new JSZip();
const processedJS = js
.split("\n")
.map(l => (l === "" ? "" : " " + l))
.join("\n");
const JS = `async function startApp() {
// Loading templates
let TEMPLATES;
try {
TEMPLATES = await 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;
} }
patched() { // Application code
if (this.editor) { ${processedJS}
window.dispatchEvent(new Event("resize")); }
this.editor.setValue(this.props[this.state.currentTab], -1);
}
}
willUnmount() { // wait for DOM ready before starting
this.editor.destroy(); owl.utils.whenReady(startApp);`;
delete this.editor;
}
setTab(tab) { zip.file("app.js", JS);
this.editor.setValue(this.props[tab], -1); zip.file("app.css", css);
zip.file("app.py", APP_PY);
const mode = MODES[tab]; zip.file("app.xml", xml);
this.editor.session.setMode(mode); zip.file("index.html", DEFAULT_HTML);
const tabSize = tab === "xml" ? 2 : 4; zip.file("owl.js", owlSourceCode());
this.editor.session.setOption("tabSize", tabSize); return zip.generateAsync({ type: "blob" });
this.state.currentTab = tab;
}
onMouseDown(ev) {
if (ev.target.tagName === "DIV") {
let y = ev.clientY;
const resizer = ev => {
const delta = ev.clientY - y;
y = ev.clientY;
this.trigger("updatePanelHeight", { delta });
};
document.body.addEventListener("mousemove", resizer);
document.body.addEventListener("mouseup", () => {
document.body.removeEventListener("mousemove", resizer);
});
}
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -142,7 +153,6 @@ class TabbedEditor extends owl.Component {
class App extends owl.Component { class App extends owl.Component {
constructor(...args) { constructor(...args) {
super(...args); super(...args);
this.template = "playground";
this.version = owl._version; this.version = owl._version;
this.SAMPLES = SAMPLES; this.SAMPLES = SAMPLES;
this.widgets = { TabbedEditor }; this.widgets = { TabbedEditor };
@@ -157,6 +167,10 @@ class App extends owl.Component {
leftPaneWidth: Math.ceil(window.innerWidth / 2), leftPaneWidth: Math.ceil(window.innerWidth / 2),
topPanelHeight: null topPanelHeight: null
}; };
this.toggleLayout = owl.utils.debounce(this.toggleLayout, 250, true);
this.runCode = owl.utils.debounce(this.runCode, 250, true);
this.downloadCode = owl.utils.debounce(this.downloadCode, 250, true);
} }
displayError(error) { displayError(error) {
@@ -169,61 +183,26 @@ class App extends owl.Component {
} }
} }
async runCode() { runCode() {
this.state.displayWelcome = false; this.state.displayWelcome = false;
// check templates let subiframe;
var qweb = new owl.QWeb(); let error = false;
var error = false; const errorHandler = e => this.displayError(e.message || e.reason.message);
const sanitizedXML = this.state.xml.replace(/<!--[\s\S]*?-->/g, "");
try { try {
qweb.loadTemplates(sanitizedXML); const { js, css, xml } = this.state;
subiframe = makeCodeIframe(js, css, xml, errorHandler);
} catch (e) { } catch (e) {
//probably problem with the templates
error = e; error = e;
} }
if (error) { if (error) {
this.displayError(error.message); this.displayError(error.message);
return; return;
} else { } else {
this.state.error = false; this.state.error = false;
} }
// create iframe
const iframe = document.createElement("iframe");
iframe.onload = () => {
const doc = iframe.contentDocument;
// inject js
const owlScript = doc.createElement("script");
owlScript.type = "text/javascript";
owlScript.src = "../owl.js";
owlScript.addEventListener("load", () => {
const script = doc.createElement("script");
script.type = "text/javascript";
const content = `window.TEMPLATES = \`${sanitizedXML}\`\n${
this.state.js
}`;
script.innerHTML = content;
const errorHandler = e => this.displayError(e.message || e.reason.message);
iframe.contentWindow.addEventListener("error", errorHandler);
iframe.contentWindow.addEventListener("unhandledrejection", errorHandler);
setTimeout(function() {
if (iframe.contentWindow) {
iframe.contentWindow.removeEventListener("error", errorHandler);
iframe.contentWindow.removeEventListener("unhandledrejection", errorHandler);
}
}, 200);
doc.body.appendChild(script);
});
doc.head.appendChild(owlScript);
// inject css
const style = document.createElement("style");
style.innerHTML = this.state.css;
doc.head.appendChild(style);
};
this.refs.content.innerHTML = ""; this.refs.content.innerHTML = "";
this.refs.content.appendChild(iframe); this.refs.content.appendChild(subiframe);
} }
setSample(ev) { setSample(ev) {
@@ -280,38 +259,91 @@ class App extends owl.Component {
} }
async downloadCode() { async downloadCode() {
const { js, css, xml } = this.state;
const content = await makeApp(js, css, xml);
await owl.utils.loadJS("libs/FileSaver.min.js"); await owl.utils.loadJS("libs/FileSaver.min.js");
await owl.utils.loadJS("libs/jszip.min.js"); saveAs(content, "app.zip");
const zip = new JSZip();
const JS = `async function startApp() {
// Loading templates
let TEMPLATES;
try {
TEMPLATES = await 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;
} }
// Application code
${this.state.js.split('\n').map(l => l === '' ? '' : ' ' + l).join('\n')}
} }
// wait for DOM ready before starting //------------------------------------------------------------------------------
owl.utils.whenReady(startApp);`; // Tabbed editor
//------------------------------------------------------------------------------
class TabbedEditor extends owl.Component {
constructor(parent, props) {
super(parent, props);
this.state = {
currentTab: props.js ? "js" : props.xml ? "xml" : "css"
};
this.setTab = owl.utils.debounce(this.setTab, 250, true);
zip.file("app.js", JS); this.sessions = {};
zip.file("app.css", this.state.css); for (let tab of ["js", "xml", "css"]) {
zip.file("app.py", APP_PY); if (props[tab]) {
zip.file("app.xml", this.state.xml); this.sessions[tab] = new ace.EditSession(props[tab], MODES[tab]);
zip.file("index.html", DEFAULT_HTML); this.sessions[tab].setOption("useWorker", false);
zip.file("owl.js", owlSourceCode()); const tabSize = tab === "xml" ? 2 : 4;
zip.generateAsync({ type: "blob" }).then(function(content) { this.sessions[tab].setOption("tabSize", tabSize);
saveAs(content, "app.zip"); this.sessions[tab].setUndoManager(new ace.UndoManager());
}
}
}
mounted() {
this.editor = ace.edit(this.refs.editor);
this.editor.setValue(this.props[this.state.currentTab], -1);
this.editor.setFontSize("12px");
this.editor.setTheme("ace/theme/monokai");
this.editor.setSession(this.sessions[this.state.currentTab]);
const tabSize = this.state.currentTab === "xml" ? 2 : 4;
this.editor.session.setOption("tabSize", tabSize);
this.editor.on("blur", () => {
const editorValue = this.editor.getValue();
const propsValue = this.props[this.state.currentTab];
if (editorValue !== propsValue) {
this.trigger("updateCode", {
type: this.state.currentTab,
value: editorValue
});
}
}); });
} }
patched() {
const session = this.sessions[this.state.currentTab];
session.setValue(this.props[this.state.currentTab], -1);
this.editor.setSession(session);
}
willUnmount() {
this.editor.destroy();
delete this.editor;
}
setTab(tab) {
if (this.state.currentTab !== tab) {
this.state.currentTab = tab;
const session = this.sessions[this.state.currentTab];
session.doc.setValue(this.props[tab], -1);
this.editor.setSession(session);
}
}
onMouseDown(ev) {
if (ev.target.tagName === "DIV") {
let y = ev.clientY;
const resizer = ev => {
const delta = ev.clientY - y;
y = ev.clientY;
this.trigger("updatePanelHeight", { delta });
};
document.body.addEventListener("mousemove", resizer);
document.body.addEventListener("mouseup", () => {
document.body.removeEventListener("mousemove", resizer);
});
}
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
+2 -1
View File
@@ -22,7 +22,7 @@ body {
display: grid; display: grid;
height: 100%; height: 100%;
width: 100%; width: 100%;
grid-template-columns: auto 6px 100%; grid-template-columns: auto 8px 100%;
} }
/* LEFT BAR ****************************************/ /* LEFT BAR ****************************************/
@@ -115,6 +115,7 @@ body {
width: 34px; width: 34px;
text-align: center; text-align: center;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
text-transform: uppercase;
} }
.tab.active { .tab.active {
+42 -67
View File
@@ -1,7 +1,6 @@
const CLICK_COUNTER = `class ClickCounter extends owl.Component { const CLICK_COUNTER = `class ClickCounter extends owl.Component {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.template = "clickcounter";
this.state = { value: 0 }; this.state = { value: 0 };
} }
@@ -16,7 +15,7 @@ counter.mount(document.body);
`; `;
const CLICK_COUNTER_XML = `<templates> const CLICK_COUNTER_XML = `<templates>
<button t-name="clickcounter" t-on-click="increment"> <button t-name="ClickCounter" t-on-click="increment">
Click Me! [<t t-esc="state.value"/>] Click Me! [<t t-esc="state.value"/>]
</button> </button>
</templates>`; </templates>`;
@@ -29,7 +28,6 @@ const CLICK_COUNTER_CSS = `button {
const CLICK_COUNTER_ESNEXT = `// This example will not work if your browser does not support ESNext class fields const CLICK_COUNTER_ESNEXT = `// This example will not work if your browser does not support ESNext class fields
class ClickCounter extends owl.Component { class ClickCounter extends owl.Component {
template = "clickcounter";
state = { value: 0 }; state = { value: 0 };
increment() { increment() {
@@ -45,7 +43,6 @@ counter.mount(document.body);
const WIDGET_COMPOSITION = `class ClickCounter extends owl.Component { const WIDGET_COMPOSITION = `class ClickCounter extends owl.Component {
constructor(parent, props) { constructor(parent, props) {
super(parent, props); super(parent, props);
this.template = "clickcounter";
this.state = { value: props.initialState || 0 }; this.state = { value: props.initialState || 0 };
} }
@@ -59,7 +56,6 @@ let nextId = 1;
class App extends owl.Component { class App extends owl.Component {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.template = "app";
this.state = { counters: [] } this.state = { counters: [] }
this.widgets = { ClickCounter }; this.widgets = { ClickCounter };
} }
@@ -75,11 +71,11 @@ app.mount(document.body);
`; `;
const WIDGET_COMPOSITION_XML = `<templates> const WIDGET_COMPOSITION_XML = `<templates>
<button t-name="clickcounter" t-on-click="increment"> <button t-name="ClickCounter" t-on-click="increment">
Click Me! [<t t-esc="state.value"/>] Click Me! [<t t-esc="state.value"/>]
</button> </button>
<div t-name="app"> <div t-name="App">
<div><button t-on-click="addCounter">Add a counter</button></div> <div><button t-on-click="addCounter">Add a counter</button></div>
<div> <div>
<t t-foreach="state.counters" t-as="counter"> <t t-foreach="state.counters" t-as="counter">
@@ -97,7 +93,6 @@ const WIDGET_COMPOSITION_CSS = `button {
const ANIMATION = `// This example will not work if your browser does not support ESNext class fields const ANIMATION = `// This example will not work if your browser does not support ESNext class fields
class App extends owl.Component { class App extends owl.Component {
template = "app";
state = {flag: 0}; state = {flag: 0};
toggle() { toggle() {
@@ -111,7 +106,7 @@ app.mount(document.body);
`; `;
const ANIMATION_XML = `<templates> const ANIMATION_XML = `<templates>
<div t-name="app"> <div t-name="App">
<button t-on-click="toggle"> <button t-on-click="toggle">
Click Me! Click Me!
</button> </button>
@@ -152,7 +147,6 @@ const ANIMATION_CSS = `button {
const LIFECYCLE_DEMO = `class HookWidget extends owl.Component { const LIFECYCLE_DEMO = `class HookWidget extends owl.Component {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.template = "demo.hookwidget";
this.state = { n: 0 }; this.state = { n: 0 };
console.log("constructor"); console.log("constructor");
} }
@@ -183,7 +177,6 @@ class ParentWidget extends owl.Component {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.widgets = { HookWidget }; this.widgets = { HookWidget };
this.template = "demo.parentwidget";
this.state = { n: 0, flag: true }; this.state = { n: 0, flag: true };
} }
increment() { increment() {
@@ -200,14 +193,14 @@ widget.mount(document.body);
`; `;
const LIFECYCLE_DEMO_XML = `<templates> const LIFECYCLE_DEMO_XML = `<templates>
<div t-name="demo.parentwidget"> <div t-name="ParentWidget">
<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" t-props="{n:state.n}"/>
</div> </div>
</div> </div>
<div t-name="demo.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>
</templates>`; </templates>`;
const BENCHMARK_APP = `//------------------------------------------------------------------------------ const BENCHMARK_APP = `//------------------------------------------------------------------------------
@@ -241,7 +234,6 @@ for (let i = 1; i < 16000; i++) {
class Counter extends owl.Component { class Counter extends owl.Component {
constructor(parent, props) { constructor(parent, props) {
super(parent, props); super(parent, props);
this.template = "counter";
this.state = { counter: props.initialState || 0 }; this.state = { counter: props.initialState || 0 };
} }
@@ -256,7 +248,6 @@ class Counter extends owl.Component {
class Message extends owl.Component { class Message extends owl.Component {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.template = "message";
this.widgets = { Counter }; this.widgets = { Counter };
} }
@@ -273,7 +264,6 @@ class Message extends owl.Component {
class App extends owl.Component { class App extends owl.Component {
constructor() { constructor() {
super(...arguments); super(...arguments);
this.template = "root";
this.widgets = { Message }; this.widgets = { Message };
this.state = { messages: messages.slice(0, 10) }; this.state = { messages: messages.slice(0, 10) };
} }
@@ -351,7 +341,7 @@ const BENCHMARK_APP_CSS = `.main {
}`; }`;
const BENCHMARK_APP_XML = `<templates> const BENCHMARK_APP_XML = `<templates>
<div t-name="root" class="main"> <div t-name="App" class="main">
<div class="left-thing"> <div class="left-thing">
<div class="counter"> <div class="counter">
<button t-on-click="increment(-1)">-</button> <button t-on-click="increment(-1)">-</button>
@@ -374,14 +364,14 @@ const BENCHMARK_APP_XML = `<templates>
</div> </div>
</div> </div>
<div t-name="message" class="message"> <div t-name="Message" class="message">
<span class="author"><t t-esc="props.author"/></span> <span class="author"><t t-esc="props.author"/></span>
<span class="msg"><t t-esc="props.msg"/></span> <span class="msg"><t t-esc="props.msg"/></span>
<button class="remove" t-on-click="removeMessage">Remove</button> <button class="remove" t-on-click="removeMessage">Remove</button>
<t t-widget="Counter" t-props="{initialState: props.id}"/> <t t-widget="Counter" t-props="{initialState: props.id}"/>
</div> </div>
<div t-name="counter"> <div t-name="Counter">
<button t-on-click="increment(-1)">-</button> <button t-on-click="increment(-1)">-</button>
<span style="font-weight:bold">Value: <t t-esc="state.counter"/></span> <span style="font-weight:bold">Value: <t t-esc="state.counter"/></span>
<button t-on-click="increment(1)">+</button> <button t-on-click="increment(1)">+</button>
@@ -477,7 +467,6 @@ function makeStore() {
// TodoItem // TodoItem
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class TodoItem extends owl.Component { class TodoItem extends owl.Component {
template = "todoitem";
state = { isEditing: false }; state = { isEditing: false };
removeTodo() { removeTodo() {
@@ -490,11 +479,12 @@ class TodoItem extends owl.Component {
async editTodo() { async editTodo() {
this.state.isEditing = true; this.state.isEditing = true;
setTimeout(() => { }
this.refs.input.value = "";
this.refs.input.focus(); focusInput() {
this.refs.input.value = this.props.title; this.refs.input.value = "";
}); this.refs.input.focus();
this.refs.input.value = this.props.title;
} }
handleKeyup(ev) { handleKeyup(ev) {
@@ -535,7 +525,6 @@ function mapStateToProps(state) {
} }
class TodoApp extends owl.Component { class TodoApp extends owl.Component {
template = "todoapp";
widgets = { TodoItem }; widgets = { TodoItem };
state = { filter: "all" }; state = { filter: "all" };
@@ -602,7 +591,7 @@ app.mount(document.body);
`; `;
const TODO_APP_STORE_XML = `<templates> const TODO_APP_STORE_XML = `<templates>
<section t-name="todoapp" class="todoapp"> <section t-name="TodoApp" class="todoapp">
<header class="header"> <header class="header">
<h1>todos</h1> <h1>todos</h1>
<input class="new-todo" autofocus="true" autocomplete="off" placeholder="What needs to be done?" t-on-keyup="addTodo"/> <input class="new-todo" autofocus="true" autocomplete="off" placeholder="What needs to be done?" t-on-keyup="addTodo"/>
@@ -640,7 +629,7 @@ const TODO_APP_STORE_XML = `<templates>
</footer> </footer>
</section> </section>
<li t-name="todoitem" class="todo" t-att-class="{completed: props.completed, editing: state.isEditing}"> <li t-name="TodoItem" class="todo" t-att-class="{completed: props.completed, editing: state.isEditing}">
<div class="view"> <div class="view">
<input class="toggle" type="checkbox" t-on-change="toggleTodo" t-att-checked="props.completed"/> <input class="toggle" type="checkbox" t-on-change="toggleTodo" t-att-checked="props.completed"/>
<label t-on-dblclick="editTodo"> <label t-on-dblclick="editTodo">
@@ -648,7 +637,7 @@ const TODO_APP_STORE_XML = `<templates>
</label> </label>
<button class="destroy" t-on-click="removeTodo"></button> <button class="destroy" t-on-click="removeTodo"></button>
</div> </div>
<input class="edit" t-ref="'input'" t-if="state.isEditing" t-att-value="props.title" t-on-keyup="handleKeyup" t-on-blur="handleBlur"/> <input class="edit" t-ref="'input'" t-if="state.isEditing" t-att-value="props.title" t-on-keyup="handleKeyup" t-mounted="focusInput" t-on-blur="handleBlur"/>
</li> </li>
</templates>`; </templates>`;
@@ -1034,39 +1023,25 @@ html .clear-completed:active {
} }
`; `;
const RESPONSIVE = `class Navbar extends owl.Component { const RESPONSIVE = `class Navbar extends owl.Component {}
template="navbar";
}
class ControlPanel extends owl.Component { class ControlPanel extends owl.Component {
template="controlpanel";
widgets = { MobileSearchView }; widgets = { MobileSearchView };
} }
class FormView extends owl.Component { class FormView extends owl.Component {
template="formview";
widgets = { AdvancedWidget }; widgets = { AdvancedWidget };
} }
class AdvancedWidget extends owl.Component { class AdvancedWidget extends owl.Component {}
template="advancedwidget";
}
class Chatter extends owl.Component { class Chatter extends owl.Component {}
template="chatter";
}
class MobileSearchView extends owl.Component { class MobileSearchView extends owl.Component {}
template="mobilesearchview";
}
class App extends owl.Component { class App extends owl.Component {
constructor() { widgets = { Navbar, ControlPanel, FormView, Chatter };
super(...arguments);
this.template = "app";
this.widgets = { Navbar, ControlPanel, FormView, Chatter };
}
} }
function isMobile() { function isMobile() {
@@ -1081,39 +1056,39 @@ const env = {
const app = new App(env); const app = new App(env);
app.mount(document.body); app.mount(document.body);
window.addEventListener( function updateEnv() {
"resize", const _isMobile = isMobile();
owl.utils.debounce(function() { if (_isMobile !== env.isMobile) {
const _isMobile = isMobile(); app.updateEnv({
if (_isMobile !== env.isMobile) { isMobile: _isMobile
app.updateEnv({ });
isMobile: _isMobile }
}); }
}
}, 20) window.addEventListener("resize", owl.utils.debounce(updateEnv, 20));
);`; `;
const RESPONSIVE_XML = `<templates> const RESPONSIVE_XML = `<templates>
<div t-name="navbar" class="navbar">Navbar</div> <div t-name="Navbar" class="navbar">Navbar</div>
<div t-name="controlpanel" class="controlpanel"> <div t-name="ControlPanel" class="controlpanel">
<h2>controlpanel</h2> <h2>controlpanel</h2>
<t t-if="env.isMobile" t-widget="MobileSearchView"/> <t t-if="env.isMobile" t-widget="MobileSearchView"/>
</div> </div>
<div t-name="formview" class="formview"> <div t-name="FormView" class="formview">
<h2>formview</h2> <h2>formview</h2>
<t t-if="!env.isMobile" t-widget="AdvancedWidget"/> <t t-if="!env.isMobile" t-widget="AdvancedWidget"/>
</div> </div>
<div t-name="chatter" class="chatter"> <div t-name="Chatter" class="chatter">
<h2>Chatter</h2> <h2>Chatter</h2>
<t t-foreach="100" t-as="item"><div>Message <t t-esc="item"/></div></t> <t t-foreach="100" t-as="item"><div>Message <t t-esc="item"/></div></t>
</div> </div>
<div t-name="mobilesearchview">MOBILE searchview</div> <div t-name="MobileSearchView">MOBILE searchview</div>
<div t-name="app" class="app" t-att-class="{mobile: env.isMobile, desktop: !env.isMobile}"> <div t-name="App" class="app" t-att-class="{mobile: env.isMobile, desktop: !env.isMobile}">
<t t-widget="Navbar"/> <t t-widget="Navbar"/>
<t t-widget="ControlPanel"/> <t t-widget="ControlPanel"/>
<div class="content-wrapper" t-if="!env.isMobile"> <div class="content-wrapper" t-if="!env.isMobile">
@@ -1128,7 +1103,7 @@ const RESPONSIVE_XML = `<templates>
</t> </t>
</div> </div>
<div t-name="advancedwidget"> <div t-name="AdvancedWidget">
This widget is only created in desktop mode. This widget is only created in desktop mode.
<button>Button!</button> <button>Button!</button>
</div> </div>
+18 -10
View File
@@ -1,14 +1,16 @@
<templates> <templates>
<div t-name="tabbed-editor" class="tabbed-editor" t-att-style="props.style"> <div t-name="TabbedEditor" class="tabbed-editor">
<div class="tabBar" t-att-class="{resizeable: props.resizeable}" t-on-mousedown="onMouseDown"> <div class="tabBar" t-att-class="{resizeable: props.resizeable}" t-on-mousedown="onMouseDown">
<a t-if="tabs.js" class="tab flash" t-att-class="{active: state.currentTab==='js'}" t-on-click="setTab('js')">JS</a> <t t-foreach="['js', 'xml', 'css']" t-as="tab">
<a t-if="tabs.xml" class="tab flash" t-att-class="{active: state.currentTab==='xml'}" t-on-click="setTab('xml')">XML</a> <a t-ref="tab" t-if="props[tab]" class="tab flash" t-att-class="{active: state.currentTab===tab}" t-on-click="setTab(tab)">
<a t-if="tabs.css" class="tab flash" t-att-class="{active: state.currentTab==='css'}" t-on-click="setTab('css')">CSS</a> <t t-esc="tab"/>
</a>
</t>
</div> </div>
<div class="code-editor" t-ref="'editor'"></div> <div class="code-editor" t-ref="'editor'"></div>
</div> </div>
<div t-name="playground" 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-style="leftPaneStyle" t-att-class="{split: state.splitLayout}">
<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>
@@ -20,13 +22,19 @@
<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-if="!state.splitLayout"> <t t-if="state.splitLayout">
<t t-widget="TabbedEditor" t-props="{js:state.js, css:state.css, xml: state.xml, display: 'js|xml|css'}" t-on-updateCode="updateCode"/> <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"/>
<t t-widget="TabbedEditor" t-keepalive="1"
t-props="{js:false, css:state.css, xml: state.xml, resizeable: true}"
t-on-updateCode="updateCode"
t-on-updatePanelHeight="updatePanelHeight"/>
</t> </t>
<t t-else="1"> <t t-else="1">
<t t-widget="TabbedEditor" t-props="{js:state.js, css:state.css, xml: state.xml, display: 'js', style:topEditorStyle}" t-on-updateCode="updateCode"/> <t t-widget="TabbedEditor" t-props="{js:state.js, css:state.css, xml: state.xml, display: 'js|xml|css'}" t-on-updateCode="updateCode"/>
<div class="separator horizontal"/>
<t t-widget="TabbedEditor" t-keepalive="1" t-props="{js:state.js, css:state.css, xml: state.xml, display: 'xml|css', resizeable: true}" t-on-updateCode="updateCode" t-on-updatePanelHeight="updatePanelHeight"/>
</t> </t>
</div> </div>
<div class="separator vertical" t-on-mousedown="onMouseDown"/> <div class="separator vertical" t-on-mousedown="onMouseDown"/>