import { SAMPLES } from "./samples.js"; const { QWeb, Component } = owl; const MODES = { js: "ace/mode/javascript", css: "ace/mode/css", xml: "ace/mode/xml" }; const DEFAULT_XML = ` `; //------------------------------------------------------------------------------ // Tabbed editor //------------------------------------------------------------------------------ const EDITOR_TEMPLATE = `
JS XML CSS
`; class TabbedEditor extends Component { constructor() { super(...arguments); this.inlineTemplate = EDITOR_TEMPLATE; 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() { this.editor = ace.edit(this.refs.editor); // remove this for xml/css (?) this.editor.session.setOption("useWorker", false); this.editor.setValue(this.props[this.state.currentTab], -1); this.editor.setFontSize("13px"); this.editor.setTheme("ace/theme/monokai"); this.editor.session.setMode(MODES[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() { if (this.editor) { window.dispatchEvent(new Event("resize")); this.editor.setValue(this.props[this.state.currentTab], -1); } } willUnmount() { this.editor.destroy(); delete this.editor; } setTab(tab) { this.editor.setValue(this.props[tab], -1); const mode = MODES[tab]; this.editor.session.setMode(mode); const tabSize = tab === "xml" ? 2 : 4; this.editor.session.setOption("tabSize", tabSize); 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); }); } } } //------------------------------------------------------------------------------ // MAIN APP //------------------------------------------------------------------------------ const TEMPLATE = `
🦉 Odoo Web Lab 🦉
v
Note: these examples require a recent browser to work without a transpilation step.
`; class App extends Component { constructor(...args) { super(...args); this.version = owl._version; this.SAMPLES = SAMPLES; this.inlineTemplate = TEMPLATE; this.widgets = { TabbedEditor }; this.state = { js: SAMPLES[0].code, css: SAMPLES[0].css || "", xml: SAMPLES[0].xml || DEFAULT_XML, error: false, displayWelcome: true, splitLayout: true, leftPaneWidth: Math.ceil(window.innerWidth / 2), topPanelHeight: null }; } async runCode() { // check templates var qweb = new owl.QWeb(); var error = false; const sanitizedXML = this.state.xml.replace(//g, ""); try { qweb.loadTemplates(sanitizedXML); } catch (e) { error = e; } this.state.error = error; this.state.displayWelcome = false; if (error) { setTimeout(() => { this.refs.content.innerHTML = ""; }); return; } // create iframe const iframe = document.createElement("iframe"); iframe.onload = () => { const doc = iframe.contentWindow.document; // inject js const owlScript = doc.createElement("script"); owlScript.type = "text/javascript"; owlScript.src = "libs/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; 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.appendChild(iframe); } setSample(ev) { const sample = SAMPLES.find(s => s.description === ev.target.value); this.state.js = sample.code; this.state.css = sample.css || ""; this.state.xml = sample.xml || DEFAULT_XML; } get leftPaneStyle() { return `width:${this.state.leftPaneWidth}px`; } get rightPaneStyle() { return `width:${window.innerWidth - 6 - this.state.leftPaneWidth}px`; } get topEditorStyle() { return `flex: 0 0 ${this.state.topPanelHeight}px`; } onMouseDown() { const resizer = ev => { this.state.leftPaneWidth = ev.clientX; }; document.body.addEventListener("mousemove", resizer); for (let iframe of document.getElementsByTagName("iframe")) { iframe.classList.add("disabled"); } document.body.addEventListener("mouseup", () => { document.body.removeEventListener("mousemove", resizer); for (let iframe of document.getElementsByTagName("iframe")) { iframe.classList.remove("disabled"); } }); } updateCode(ev) { this.state[ev.type] = ev.value; } toggleLayout() { this.state.splitLayout = !this.state.splitLayout; } updatePanelHeight(ev) { if (!ev.delta) { return; } let height = this.state.topPanelHeight; if (!height) { height = document.getElementsByClassName("tabbed-editor")[0].clientHeight; } this.state.topPanelHeight = height + ev.delta; } } //------------------------------------------------------------------------------ // Application initialization //------------------------------------------------------------------------------ document.title = `${document.title} (v${owl._version})`; document.addEventListener("DOMContentLoaded", async function() { const qweb = new QWeb(); const env = { qweb }; const app = new App(env); app.mount(document.body); });