mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
imp: allow adding templates in xml tab
This commit is contained in:
+17
-6
@@ -75,27 +75,30 @@ body {
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CONTENT ****************************************/
|
/* RIGHT PANE ****************************************/
|
||||||
.content {
|
.right-pane {
|
||||||
background-color: lightgray;
|
background-color: lightgray;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content iframe {
|
.right-pane iframe {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content .welcome {
|
.content {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.right-pane .welcome {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
color: gray;
|
color: gray;
|
||||||
margin-top: 50px;
|
margin-top: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content .url {
|
.right-pane .url {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
@@ -104,7 +107,15 @@ body {
|
|||||||
color: gray;
|
color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content .note {
|
.right-pane .note {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right-pane .error {
|
||||||
|
height: 100%;
|
||||||
|
padding-top: 40%;
|
||||||
|
font-size: 30px;
|
||||||
|
color: darkred;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|||||||
+34
-7
@@ -8,13 +8,16 @@ const MODES = {
|
|||||||
xml: "ace/mode/xml"
|
xml: "ace/mode/xml"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_XML = `<templates>
|
||||||
|
</templates>`;
|
||||||
|
|
||||||
const TEMPLATE = `
|
const TEMPLATE = `
|
||||||
<div class="playground">
|
<div class="playground">
|
||||||
<div class="left-bar">
|
<div class="left-bar">
|
||||||
<div class="menubar">
|
<div class="menubar">
|
||||||
<a class="tab" t-att-class="{active: state.currentTab==='js'}" t-on-click="setTab('js')">JS</a>
|
<a class="tab" t-att-class="{active: state.currentTab==='js'}" t-on-click="setTab('js')">JS</a>
|
||||||
<a class="tab" t-att-class="{active: state.currentTab==='css'}" t-on-click="setTab('css')">CSS</a>
|
|
||||||
<a class="tab" t-att-class="{active: state.currentTab==='xml'}" t-on-click="setTab('xml')">XML</a>
|
<a class="tab" t-att-class="{active: state.currentTab==='xml'}" t-on-click="setTab('xml')">XML</a>
|
||||||
|
<a class="tab" t-att-class="{active: state.currentTab==='css'}" t-on-click="setTab('css')">CSS</a>
|
||||||
<div class="right-thing">
|
<div class="right-thing">
|
||||||
<select t-on-change="setSample">
|
<select t-on-change="setSample">
|
||||||
<option t-foreach="SAMPLES" t-as="sample">
|
<option t-foreach="SAMPLES" t-as="sample">
|
||||||
@@ -26,13 +29,17 @@ const TEMPLATE = `
|
|||||||
</div>
|
</div>
|
||||||
<div class="code-editor" t-ref="editor"></div>
|
<div class="code-editor" t-ref="editor"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content" t-ref="content">
|
<div class="right-pane">
|
||||||
<div class="welcome">
|
<div class="welcome" t-if="state.displayWelcome">
|
||||||
<div>🦉 Odoo Web Lab 🦉</div>
|
<div>🦉 Odoo Web Lab 🦉</div>
|
||||||
<div>v<t t-esc="version"/></div>
|
<div>v<t t-esc="version"/></div>
|
||||||
<div class="url"><a href="https://github.com/odoo/owl">https://github.com/odoo/owl</a></div>
|
<div class="url"><a href="https://github.com/odoo/owl">https://github.com/odoo/owl</a></div>
|
||||||
<div class="note">Note: these examples require a recent browser to work without a transpilation step. </div>
|
<div class="note">Note: these examples require a recent browser to work without a transpilation step. </div>
|
||||||
</div>
|
</div>
|
||||||
|
<div t-if="state.error" class="error">
|
||||||
|
<t t-esc="state.error.message"/>
|
||||||
|
</div>
|
||||||
|
<div class="content" t-ref="content"/>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
@@ -46,7 +53,9 @@ class Playground extends Component {
|
|||||||
currentTab: "js",
|
currentTab: "js",
|
||||||
js: SAMPLES[0].code,
|
js: SAMPLES[0].code,
|
||||||
css: SAMPLES[0].css || "",
|
css: SAMPLES[0].css || "",
|
||||||
xml: ""
|
xml: SAMPLES[0].xml || DEFAULT_XML,
|
||||||
|
error: false,
|
||||||
|
displayWelcome: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,9 +68,24 @@ class Playground extends Component {
|
|||||||
this.editor.session.setMode("ace/mode/javascript");
|
this.editor.session.setMode("ace/mode/javascript");
|
||||||
}
|
}
|
||||||
|
|
||||||
runCode() {
|
async runCode() {
|
||||||
this.updateStateFromEditor();
|
this.updateStateFromEditor();
|
||||||
|
|
||||||
|
// check templates
|
||||||
|
var qweb = new owl.core.QWeb();
|
||||||
|
var error = false;
|
||||||
|
try {
|
||||||
|
qweb.loadTemplates(this.state.xml);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.updateState({ error, displayWelcome: false });
|
||||||
|
if (error) {
|
||||||
|
this.refs.content.innerHTML = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// create iframe
|
// create iframe
|
||||||
const iframe = document.createElement("iframe");
|
const iframe = document.createElement("iframe");
|
||||||
|
|
||||||
@@ -74,7 +98,10 @@ class Playground extends Component {
|
|||||||
owlScript.addEventListener("load", () => {
|
owlScript.addEventListener("load", () => {
|
||||||
const script = doc.createElement("script");
|
const script = doc.createElement("script");
|
||||||
script.type = "text/javascript";
|
script.type = "text/javascript";
|
||||||
script.innerHTML = this.state.js;
|
const content = `window.TEMPLATES = \`${this.state.xml}\`\n${
|
||||||
|
this.state.js
|
||||||
|
}`;
|
||||||
|
script.innerHTML = content;
|
||||||
doc.body.appendChild(script);
|
doc.body.appendChild(script);
|
||||||
});
|
});
|
||||||
doc.head.appendChild(owlScript);
|
doc.head.appendChild(owlScript);
|
||||||
@@ -93,7 +120,7 @@ class Playground extends Component {
|
|||||||
this.updateState({
|
this.updateState({
|
||||||
js: sample.code,
|
js: sample.code,
|
||||||
css: sample.css || "",
|
css: sample.css || "",
|
||||||
xml: ""
|
xml: sample.xml || DEFAULT_XML
|
||||||
});
|
});
|
||||||
this.editor.setValue(this.state[this.state.currentTab], -1);
|
this.editor.setValue(this.state[this.state.currentTab], -1);
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-46
@@ -1,7 +1,7 @@
|
|||||||
const HELLO_WORLD = `class HelloWorld extends owl.core.Component {
|
const HELLO_WORLD = `class HelloWorld extends owl.core.Component {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
this.inlineTemplate = \`<div>Hello <t t-esc="props.name"/></div>\`;
|
this.inlineTemplate = \`<div class="hello">Hello <t t-esc="props.name"/></div>\`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13,9 +13,14 @@ const hello = new HelloWorld(env, { name: "World" });
|
|||||||
hello.mount(document.body);
|
hello.mount(document.body);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const HELLO_WORLD_CSS = `.hello {
|
||||||
|
color: darkred;
|
||||||
|
font-size: 30px;
|
||||||
|
}`;
|
||||||
|
|
||||||
const HELLO_WORLD_ESNEXT = `// This example will not work if your browser does not support ESNext Class Fields
|
const HELLO_WORLD_ESNEXT = `// This example will not work if your browser does not support ESNext Class Fields
|
||||||
class HelloWorld extends owl.core.Component {
|
class HelloWorld extends owl.core.Component {
|
||||||
inlineTemplate = \`<div>Hello <t t-esc="props.name"/></div>\`;
|
inlineTemplate = \`<div class="hello">Hello <t t-esc="props.name"/></div>\`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const env = {
|
const env = {
|
||||||
@@ -99,12 +104,7 @@ for (let i = 1; i < 16000; i++) {
|
|||||||
class Counter extends owl.core.Component {
|
class Counter extends owl.core.Component {
|
||||||
constructor(parent, props) {
|
constructor(parent, props) {
|
||||||
super(parent, props);
|
super(parent, props);
|
||||||
this.inlineTemplate = \`
|
this.template = "counter";
|
||||||
<div>
|
|
||||||
<button t-on-click="increment(-1)">-</button>
|
|
||||||
<span style="font-weight:bold">Value: <t t-esc="state.counter"/></span>
|
|
||||||
<button t-on-click="increment(1)">+</button>
|
|
||||||
</div>\`;
|
|
||||||
this.state = {
|
this.state = {
|
||||||
counter: props.initialState || 0
|
counter: props.initialState || 0
|
||||||
};
|
};
|
||||||
@@ -122,13 +122,7 @@ class Message extends owl.core.Component {
|
|||||||
|
|
||||||
constructor(parent, props) {
|
constructor(parent, props) {
|
||||||
super(parent, props);
|
super(parent, props);
|
||||||
this.inlineTemplate = \`
|
this.template = "message";
|
||||||
<div class="message">
|
|
||||||
<span class="author"><t t-esc="props.author"/></span>
|
|
||||||
<span class="msg"><t t-esc="props.msg"/></span>
|
|
||||||
<button class="remove" t-on-click="removeMessage">Remove</button>
|
|
||||||
<t t-widget="Counter" t-props="{initialState: props.id}"/>
|
|
||||||
</div>\`;
|
|
||||||
this.widgets = { Counter };
|
this.widgets = { Counter };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,34 +136,10 @@ class Message extends owl.core.Component {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Root Widget
|
// Root Widget
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
const template = \`
|
|
||||||
<div class="main">
|
|
||||||
<div class="left-thing">
|
|
||||||
<div class="counter">
|
|
||||||
<button t-on-click="increment(-1)">-</button>
|
|
||||||
<span style="font-weight:bold">Value: <t t-esc="state.messages.length"/></span>
|
|
||||||
<button t-on-click="increment(1)">+</button>
|
|
||||||
</div>
|
|
||||||
<button t-on-click="setMessageCount(10)">10 messages</button>
|
|
||||||
<button t-on-click="setMessageCount(20)">20 messages</button>
|
|
||||||
<button t-on-click="setMessageCount(500)">500 messages</button>
|
|
||||||
<button t-on-click="setMessageCount(1000)">1000 messages</button>
|
|
||||||
<button t-on-click="setMessageCount(5000)">5000 messages</button>
|
|
||||||
<button t-on-click="setMessageCount(15000)">15000 messages</button>
|
|
||||||
</div>
|
|
||||||
<div class="right-thing">
|
|
||||||
<div class="content">
|
|
||||||
<t t-foreach="state.messages" t-as="message">
|
|
||||||
<t t-widget="Message" t-att-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>\`;
|
|
||||||
|
|
||||||
class App extends owl.core.Component {
|
class App extends owl.core.Component {
|
||||||
constructor(parent, props) {
|
constructor(parent, props) {
|
||||||
super(parent, props);
|
super(parent, props);
|
||||||
this.inlineTemplate = template;
|
this.template = "root";
|
||||||
this.widgets = { Message };
|
this.widgets = { Message };
|
||||||
this.state = {
|
this.state = {
|
||||||
messages: messages.slice(0, 10)
|
messages: messages.slice(0, 10)
|
||||||
@@ -198,9 +168,9 @@ class App extends owl.core.Component {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Application initialization
|
// Application initialization
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
const env = {
|
const qweb = new owl.core.QWeb();
|
||||||
qweb: new owl.core.QWeb()
|
qweb.loadTemplates(TEMPLATES);
|
||||||
};
|
const env = {qweb};
|
||||||
|
|
||||||
const app = new App(env);
|
const app = new App(env);
|
||||||
app.mount(document.body);
|
app.mount(document.body);
|
||||||
@@ -252,6 +222,45 @@ const BENCHMARK_APP_CSS = `.main {
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
const BENCHMARK_APP_XML = `<templates>
|
||||||
|
<div t-name="root" class="main">
|
||||||
|
<div class="left-thing">
|
||||||
|
<div class="counter">
|
||||||
|
<button t-on-click="increment(-1)">-</button>
|
||||||
|
<span style="font-weight:bold">Value: <t t-esc="state.messages.length"/></span>
|
||||||
|
<button t-on-click="increment(1)">+</button>
|
||||||
|
</div>
|
||||||
|
<button t-on-click="setMessageCount(10)">10 messages</button>
|
||||||
|
<button t-on-click="setMessageCount(20)">20 messages</button>
|
||||||
|
<button t-on-click="setMessageCount(500)">500 messages</button>
|
||||||
|
<button t-on-click="setMessageCount(1000)">1000 messages</button>
|
||||||
|
<button t-on-click="setMessageCount(5000)">5000 messages</button>
|
||||||
|
<button t-on-click="setMessageCount(15000)">15000 messages</button>
|
||||||
|
</div>
|
||||||
|
<div class="right-thing">
|
||||||
|
<div class="content">
|
||||||
|
<t t-foreach="state.messages" t-as="message">
|
||||||
|
<t t-widget="Message" t-att-key="message.id" t-props="message" t-on-remove_message="removeMessage"/>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div t-name="message" class="message">
|
||||||
|
<span class="author"><t t-esc="props.author"/></span>
|
||||||
|
<span class="msg"><t t-esc="props.msg"/></span>
|
||||||
|
<button class="remove" t-on-click="removeMessage">Remove</button>
|
||||||
|
<t t-widget="Counter" t-props="{initialState: props.id}"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div t-name="counter">
|
||||||
|
<button t-on-click="increment(-1)">-</button>
|
||||||
|
<span style="font-weight:bold">Value: <t t-esc="state.counter"/></span>
|
||||||
|
<button t-on-click="increment(1)">+</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</templates>`;
|
||||||
|
|
||||||
const STATE_MANAGEMENT = `// todo`;
|
const STATE_MANAGEMENT = `// todo`;
|
||||||
|
|
||||||
const EMPTY = ``;
|
const EMPTY = ``;
|
||||||
@@ -259,11 +268,13 @@ const EMPTY = ``;
|
|||||||
export const SAMPLES = [
|
export const SAMPLES = [
|
||||||
{
|
{
|
||||||
description: "Hello World",
|
description: "Hello World",
|
||||||
code: HELLO_WORLD
|
code: HELLO_WORLD,
|
||||||
|
css: HELLO_WORLD_CSS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Hello World (ESNext)",
|
description: "Hello World (ESNext)",
|
||||||
code: HELLO_WORLD_ESNEXT
|
code: HELLO_WORLD_ESNEXT,
|
||||||
|
css: HELLO_WORLD_CSS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Widget Composition",
|
description: "Widget Composition",
|
||||||
@@ -272,7 +283,8 @@ export const SAMPLES = [
|
|||||||
{
|
{
|
||||||
description: "Benchmark application",
|
description: "Benchmark application",
|
||||||
code: BENCHMARK_APP,
|
code: BENCHMARK_APP,
|
||||||
css: BENCHMARK_APP_CSS
|
css: BENCHMARK_APP_CSS,
|
||||||
|
xml: BENCHMARK_APP_XML
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "State management app",
|
description: "State management app",
|
||||||
|
|||||||
Reference in New Issue
Block a user