import { SAMPLES } from "./samples.js"; const { QWeb, Component } = owl.core; 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]); 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 }); } }); } componentDidUpdate() { 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); this.updateState({ 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.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 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 = \`${this.state.xml}\`\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.updateState({ js: sample.code, css: sample.css || "", 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.updateState({ 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.updateState({ 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.updateState({ 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); });