From 35121dff5923f46dbf7a4f415c9564b626302177 Mon Sep 17 00:00:00 2001 From: "Simon Genin (ges)" Date: Wed, 14 Oct 2020 15:15:15 +0200 Subject: [PATCH] wip --- package.json | 1 + tools/playground/app.js | 484 ++++++++++++----------------- tools/playground/index.html | 36 ++- tools/playground/old_app.js | 427 +++++++++++++++++++++++++ tools/playground/old_index.html | 21 ++ tools/playground/old_templates.xml | 57 ++++ tools/playground/templates.xml | 126 ++++---- 7 files changed, 801 insertions(+), 351 deletions(-) create mode 100644 tools/playground/old_app.js create mode 100644 tools/playground/old_index.html create mode 100644 tools/playground/old_templates.xml diff --git a/package.json b/package.json index c0056277..55c33b2c 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "jest": "^23.6.0", "jest-environment-jsdom": "^24.7.1", "live-server": "^1.2.1", + "monaco-editor": "^0.21.2", "npm-run-all": "^4.1.5", "prettier": "^2.0.4", "rollup": "^1.6.0", diff --git a/tools/playground/app.js b/tools/playground/app.js index 3345f67a..f9d88f81 100644 --- a/tools/playground/app.js +++ b/tools/playground/app.js @@ -1,4 +1,4 @@ -import { SAMPLES } from "./samples.js"; +import { SAMPLES as samples } from "./samples.js" ; const { useState, useRef, onMounted, onWillUnmount } = owl.hooks; //------------------------------------------------------------------------------ // Constants, helpers, utils @@ -6,18 +6,18 @@ const { useState, useRef, onMounted, onWillUnmount } = owl.hooks; let owlJS; async function owlSourceCode() { - if (owlJS) { + if (owlJS) { + return owlJS; + } + const result = await fetch("../owl.js"); + owlJS = await result.text(); return owlJS; - } - const result = await fetch("../owl.js"); - owlJS = await result.text(); - return owlJS; } const MODES = { - js: "ace/mode/javascript", - css: "ace/mode/css", - xml: "ace/mode/xml" + js: "ace/mode/javascript", + css: "ace/mode/css", + xml: "ace/mode/xml" }; const DEFAULT_XML = ` @@ -71,22 +71,23 @@ if __name__ == "__main__": * Make an iframe, with all the js, css and xml properly injected. */ function makeCodeIframe(js, css, xml) { - const sanitizedXML = xml.replace(//g, ""); + const sanitizedXML = xml.replace(//g, ""); - // create iframe - const iframe = document.createElement("iframe"); + // create iframe + const iframe = document.createElement("iframe"); + iframe.className += "h-full w-full"; - 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 = ` + 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 = ` { owl.config.mode = 'dev'; let templates = \`${sanitizedXML}\`; @@ -94,32 +95,32 @@ function makeCodeIframe(js, css, xml) { owl.Component.env = { qweb }; } ${js}`; - script.innerHTML = content; - doc.body.appendChild(script); - }); - doc.head.appendChild(owlScript); + script.innerHTML = content; + doc.body.appendChild(script); + }); + doc.head.appendChild(owlScript); - // inject css - const style = document.createElement("style"); - style.innerHTML = css; - doc.head.appendChild(style); - }; - return iframe; + // 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"); + await owl.utils.loadJS("libs/jszip.min.js"); - const zip = new JSZip(); - const processedJS = js - .split("\n") - .map(l => (l === "" ? "" : " " + l)) - .join("\n"); + const zip = new JSZip(); + const processedJS = js + .split("\n") + .map(l => (l === "" ? "" : " " + l)) + .join("\n"); - const JS = ` + const JS = ` /** * This is the javascript code defined in the playground. * In a larger application, this code should probably be moved in different @@ -150,278 +151,193 @@ async function start() { start(); `; - zip.file("app.js", JS); - zip.file("app.css", css); - zip.file("app.py", APP_PY); - zip.file("app.xml", xml); - zip.file("index.html", DEFAULT_HTML); - zip.file("owl.js", owlSourceCode()); - return zip.generateAsync({ type: "blob" }); + zip.file("app.js", JS); + zip.file("app.css", css); + zip.file("app.py", APP_PY); + zip.file("app.xml", xml); + zip.file("index.html", DEFAULT_HTML); + zip.file("owl.js", owlSourceCode()); + return zip.generateAsync({ type: "blob" }); } //------------------------------------------------------------------------------ // SAMPLES //------------------------------------------------------------------------------ function loadSamples() { - let result = SAMPLES.slice(); - const localSample = localStorage.getItem("owl-playground-local-sample"); - if (localSample) { - const { js, css, xml } = JSON.parse(localSample); - result.unshift({ - description: "Local Storage Code", - code: js, - xml, - css - }); - } - return result; + let result = samples.slice(); + const localSample = localStorage.getItem("owl-playground-local-sample"); + if (localSample) { + const { js, css, xml } = JSON.parse(localSample); + result.unshift({ + description: "Local Storage Code", + code: js, + xml, + css + }); + } + return result; } function saveLocalSample(js, css, xml) { - const str = JSON.stringify({ js, css, xml }); - localStorage.setItem("owl-playground-local-sample", str); + const str = JSON.stringify({ js, css, xml }); + localStorage.setItem("owl-playground-local-sample", str); } function deleteLocalSample() { - localStorage.removeItem("owl-playground-local-sample"); + localStorage.removeItem("owl-playground-local-sample"); } function useSamples() { - const samples = loadSamples(); - const component = owl.Component.current; - let interval; - - onMounted(() => { - const state = component.state; - interval = setInterval(() => { - if (component.isDirty) { - saveLocalSample(state.js, state.css, state.xml); - } - }, 1000); - }); - onWillUnmount(() => { - clearInterval(interval); - }); - return samples; -} -//------------------------------------------------------------------------------ -// Tabbed editor -//------------------------------------------------------------------------------ -class TabbedEditor extends owl.Component { - constructor(parent, props) { - super(parent, props); - this.state = useState({ - currentTab: props.js !== false ? "js" : props.xml ? "xml" : "css" - }); - this.setTab = owl.utils.debounce(this.setTab, 250, true); - - this.sessions = {}; - this._setupSessions(props); - this.editorNode = useRef("editor"); - this._updateCode = this._updateCode.bind(this); - } - - mounted() { - this.editor = this.editor || ace.edit(this.editorNode.el); - - 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", this._updateCode); - this.interval = setInterval(this._updateCode, 3000); - } - - willUnmount() { - clearInterval(this.interval); - this.editor.off("blur", this._updateCode); - } - willUpdateProps(nextProps) { - this._setupSessions(nextProps); - } - - patched() { - const session = this.sessions[this.state.currentTab]; - let content = this.props[this.state.currentTab]; - if (content === false) { - const tab = this.props.js !== false ? "js" : this.props.xml ? "xml" : "css"; - content = this.props[tab]; - this.state.currentTab = tab; - } - if (this.editor.getValue() !== content) { - session.setValue(content, -1); - this.editor.setSession(session); - this.editor.resize(); - } - } - - 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); - }); - } - } - - _setupSessions(props) { - for (let tab of ["js", "xml", "css"]) { - if (props[tab] !== false && !this.sessions[tab]) { - this.sessions[tab] = new ace.EditSession(props[tab], MODES[tab]); - this.sessions[tab].setOption("useWorker", false); - const tabSize = tab === "xml" ? 2 : 4; - this.sessions[tab].setOption("tabSize", tabSize); - this.sessions[tab].setUndoManager(new ace.UndoManager()); - } - } - } - - _updateCode() { - const editorValue = this.editor.getValue(); - const propsValue = this.props[this.state.currentTab]; - if (editorValue !== propsValue) { - this.trigger("updateCode", { - type: this.state.currentTab, - value: editorValue - }); - } - } + return loadSamples(); } //------------------------------------------------------------------------------ // MAIN APP //------------------------------------------------------------------------------ -class App extends owl.Component { - constructor(...args) { - super(...args); - this.version = owl.__info__.version; - this.SAMPLES = useSamples(); - this.isDirty = false; - this.state = useState({ - js: this.SAMPLES[0].code, - css: this.SAMPLES[0].css || "", - xml: this.SAMPLES[0].xml || DEFAULT_XML, - displayWelcome: true, - splitLayout: true, - leftPaneWidth: Math.ceil(window.innerWidth / 2), - 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); - this.content = useRef("content"); - } - - runCode() { - this.content.el.innerHTML = ""; - this.state.displayWelcome = false; - - const { js, css, xml } = this.state; - const subiframe = makeCodeIframe(js, css, xml); - this.content.el.appendChild(subiframe); - } - - setSample(ev) { - const sample = this.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; - deleteLocalSample(); - this.isDirty = false; - } - - 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) { - if (this.state[ev.detail.type] !== ev.detail.value) { - this.state[ev.detail.type] = ev.detail.value; - this.isDirty = true; - } - } - toggleLayout() { - this.state.splitLayout = !this.state.splitLayout; - } - updatePanelHeight(ev) { - if (!ev.detail.delta) { - return; - } - let height = this.state.topPanelHeight; - if (!height) { - height = document.getElementsByClassName("tabbed-editor")[0].clientHeight; - } - this.state.topPanelHeight = height + ev.detail.delta; - } - - async downloadCode() { - const { js, css, xml } = this.state; - const content = await makeApp(js, css, xml); - await owl.utils.loadJS("libs/FileSaver.min.js"); - saveAs(content, "app.zip"); - } +class Tab extends owl.Component { } -App.components = { TabbedEditor }; + +class Editor extends owl.Component { + + static props = ['js', 'xml', 'css'] + + state = useState({ + currentExtension: 'js', + js: this.props.js, + css: this.props.css, + xml: this.props.xml + }) + + editor = null; + + willUpdateProps(nextProps) { + console.log(nextProps); + this.state.js = nextProps.js; + this.state.css = nextProps.css; + this.state.xml = nextProps.xml; + this._openFile(this.state.currentExtension, false) + } + + _run(ev) { + this._saveCodeState(); + this.trigger('run-project', { + js: this.state.js, + xml: this.state.xml, + css: this.state.css + }) + } + + _openFile(extension, save = true) { + if (save) this._saveCodeState(); + this.state.currentExtension = extension; + let language = 'javascript'; + switch (extension) { + case 'xml': + language = 'xml'; + break; + case 'css': + language = 'css'; + break; + } + + const model = monaco.editor.createModel( + this.state[this.state.currentExtension], + language, + null + ) + + this.editor.setModel(model) + } + + mounted() { + this.editor = monaco.editor.create(document.getElementById('container'), { + value: this.state[this.state.currentExtension], + language: 'javascript', + automaticLayout: true, + minimap: { + enabled: false + } + }); + } + + _saveCodeState() { + this.state[this.state.currentExtension] = this.editor.getValue(); + } + + _openCSS(ev) { + this._openFile('css') + } + + _openXML(ev) { + this._openFile('xml') + } + + _openJS(ev) { + this._openFile('js') + } + +} + +Editor.components = { Tab } + + +class App extends owl.Component { + + samples = []; + + constructor(...args) { + super(...args); + this.version = owl.__info__.version; + this.samples = useSamples(); + + this.state = useState({ + js: this.samples[0].code, + css: this.samples[0].css || "", + xml: this.samples[0].xml || DEFAULT_XML, + }); + + this.runCode = owl.utils.debounce(this.runCode, 250, true); + this.downloadCode = owl.utils.debounce(this.downloadCode, 250, true); + this.content = useRef("iframe-container"); + } + + + _run(ev) { + this.content.el.innerHTML = ""; + const { js, css, xml } = ev.detail; + const subiframe = makeCodeIframe(js, css, xml); + this.content.el.appendChild(subiframe); + } + + _setSample(ev) { + const sample = this.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; + deleteLocalSample(); + } + + +} + +App.components = { Editor } //------------------------------------------------------------------------------ // Application initialization //------------------------------------------------------------------------------ async function start() { - document.title = `${document.title} (v${owl.__info__.version})`; - const commit = `https://github.com/odoo/owl/commit/${owl.__info__.hash}`; - console.info(`This application is using Owl built with the following commit:`, commit); - const [templates] = await Promise.all([ - owl.utils.loadFile("templates.xml"), - owl.utils.whenReady() - ]); - const qweb = new owl.QWeb({ templates }); - owl.Component.env = { qweb }; - const app = new App(); - app.mount(document.body); + document.title = `${document.title} (v${owl.__info__.version})`; + const commit = `https://github.com/odoo/owl/commit/${owl.__info__.hash}`; + console.info(`This application is using Owl built with the following commit:`, commit); + const [templates] = await Promise.all([ + owl.utils.loadFile("templates.xml"), + owl.utils.whenReady() + ]); + const qweb = new owl.QWeb({ templates }); + owl.Component.env = { qweb }; + const app = new App(); + app.mount(document.body); } start(); diff --git a/tools/playground/index.html b/tools/playground/index.html index 4fae2f13..f834f58b 100644 --- a/tools/playground/index.html +++ b/tools/playground/index.html @@ -1,20 +1,26 @@ + + - - - - OWL Playground - - - + + + + - - - - - + + + - - - + + + + + + \ No newline at end of file diff --git a/tools/playground/old_app.js b/tools/playground/old_app.js new file mode 100644 index 00000000..3345f67a --- /dev/null +++ b/tools/playground/old_app.js @@ -0,0 +1,427 @@ +import { SAMPLES } from "./samples.js"; +const { useState, useRef, onMounted, onWillUnmount } = owl.hooks; +//------------------------------------------------------------------------------ +// Constants, helpers, utils +//------------------------------------------------------------------------------ +let owlJS; + +async function owlSourceCode() { + if (owlJS) { + return owlJS; + } + const result = await fetch("../owl.js"); + owlJS = await result.text(); + return owlJS; +} + +const MODES = { + js: "ace/mode/javascript", + css: "ace/mode/css", + xml: "ace/mode/xml" +}; + +const DEFAULT_XML = ` +`; + +const DEFAULT_HTML = ` + + + + OWL App + + + + + + + + + +`; + +const APP_PY = `#!/usr/bin/env python3 + +import threading +import time + +from http.server import SimpleHTTPRequestHandler, HTTPServer + +def start_server(): + SimpleHTTPRequestHandler.extensions_map['.js'] = 'application/javascript' + httpd = HTTPServer(('0.0.0.0', 3600), SimpleHTTPRequestHandler) + httpd.serve_forever() + +url = 'http://127.0.0.1:3600' + +if __name__ == "__main__": + print("Owl Application") + print("---------------") + print("Server running on: {}".format(url)) + threading.Thread(target=start_server, daemon=True).start() + + while True: + try: + time.sleep(1) + except KeyboardInterrupt: + httpd.server_close() + quit(0) +`; + +/** + * Make an iframe, with all the js, css and xml properly injected. + */ +function makeCodeIframe(js, css, xml) { + const sanitizedXML = xml.replace(//g, ""); + + + // 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 = ` + { + owl.config.mode = 'dev'; + let templates = \`${sanitizedXML}\`; + const qweb = new owl.QWeb({ templates }); + owl.Component.env = { qweb }; + } + ${js}`; + script.innerHTML = content; + 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 = ` +/** + * This is the javascript code defined in the playground. + * In a larger application, this code should probably be moved in different + * sub files. + */ +function app() { +${processedJS} +} + +/** + * Initialization code + * This code load templates, and make sure everything is properly connected. + */ +async function start() { + let templates; + try { + templates = await owl.utils.loadFile('app.xml'); + } catch(e) { + console.error(\`This app requires a static server. If you have python installed, try 'python app.py'\`); + return; + } + const env = { qweb: new owl.QWeb({templates})}; + owl.Component.env = env; + await owl.utils.whenReady(); + app(); +} + +start(); +`; + + zip.file("app.js", JS); + zip.file("app.css", css); + zip.file("app.py", APP_PY); + zip.file("app.xml", xml); + zip.file("index.html", DEFAULT_HTML); + zip.file("owl.js", owlSourceCode()); + return zip.generateAsync({ type: "blob" }); +} + +//------------------------------------------------------------------------------ +// SAMPLES +//------------------------------------------------------------------------------ +function loadSamples() { + let result = SAMPLES.slice(); + const localSample = localStorage.getItem("owl-playground-local-sample"); + if (localSample) { + const { js, css, xml } = JSON.parse(localSample); + result.unshift({ + description: "Local Storage Code", + code: js, + xml, + css + }); + } + return result; +} + +function saveLocalSample(js, css, xml) { + const str = JSON.stringify({ js, css, xml }); + localStorage.setItem("owl-playground-local-sample", str); +} + +function deleteLocalSample() { + localStorage.removeItem("owl-playground-local-sample"); +} + +function useSamples() { + const samples = loadSamples(); + const component = owl.Component.current; + let interval; + + onMounted(() => { + const state = component.state; + interval = setInterval(() => { + if (component.isDirty) { + saveLocalSample(state.js, state.css, state.xml); + } + }, 1000); + }); + onWillUnmount(() => { + clearInterval(interval); + }); + return samples; +} +//------------------------------------------------------------------------------ +// Tabbed editor +//------------------------------------------------------------------------------ +class TabbedEditor extends owl.Component { + constructor(parent, props) { + super(parent, props); + this.state = useState({ + currentTab: props.js !== false ? "js" : props.xml ? "xml" : "css" + }); + this.setTab = owl.utils.debounce(this.setTab, 250, true); + + this.sessions = {}; + this._setupSessions(props); + this.editorNode = useRef("editor"); + this._updateCode = this._updateCode.bind(this); + } + + mounted() { + this.editor = this.editor || ace.edit(this.editorNode.el); + + 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", this._updateCode); + this.interval = setInterval(this._updateCode, 3000); + } + + willUnmount() { + clearInterval(this.interval); + this.editor.off("blur", this._updateCode); + } + willUpdateProps(nextProps) { + this._setupSessions(nextProps); + } + + patched() { + const session = this.sessions[this.state.currentTab]; + let content = this.props[this.state.currentTab]; + if (content === false) { + const tab = this.props.js !== false ? "js" : this.props.xml ? "xml" : "css"; + content = this.props[tab]; + this.state.currentTab = tab; + } + if (this.editor.getValue() !== content) { + session.setValue(content, -1); + this.editor.setSession(session); + this.editor.resize(); + } + } + + 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); + }); + } + } + + _setupSessions(props) { + for (let tab of ["js", "xml", "css"]) { + if (props[tab] !== false && !this.sessions[tab]) { + this.sessions[tab] = new ace.EditSession(props[tab], MODES[tab]); + this.sessions[tab].setOption("useWorker", false); + const tabSize = tab === "xml" ? 2 : 4; + this.sessions[tab].setOption("tabSize", tabSize); + this.sessions[tab].setUndoManager(new ace.UndoManager()); + } + } + } + + _updateCode() { + const editorValue = this.editor.getValue(); + const propsValue = this.props[this.state.currentTab]; + if (editorValue !== propsValue) { + this.trigger("updateCode", { + type: this.state.currentTab, + value: editorValue + }); + } + } +} + +//------------------------------------------------------------------------------ +// MAIN APP +//------------------------------------------------------------------------------ +class App extends owl.Component { + constructor(...args) { + super(...args); + this.version = owl.__info__.version; + this.SAMPLES = useSamples(); + this.isDirty = false; + + this.state = useState({ + js: this.SAMPLES[0].code, + css: this.SAMPLES[0].css || "", + xml: this.SAMPLES[0].xml || DEFAULT_XML, + displayWelcome: true, + splitLayout: true, + leftPaneWidth: Math.ceil(window.innerWidth / 2), + 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); + this.content = useRef("content"); + } + + runCode() { + this.content.el.innerHTML = ""; + this.state.displayWelcome = false; + + const { js, css, xml } = this.state; + const subiframe = makeCodeIframe(js, css, xml); + this.content.el.appendChild(subiframe); + } + + setSample(ev) { + const sample = this.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; + deleteLocalSample(); + this.isDirty = false; + } + + 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) { + if (this.state[ev.detail.type] !== ev.detail.value) { + this.state[ev.detail.type] = ev.detail.value; + this.isDirty = true; + } + } + toggleLayout() { + this.state.splitLayout = !this.state.splitLayout; + } + updatePanelHeight(ev) { + if (!ev.detail.delta) { + return; + } + let height = this.state.topPanelHeight; + if (!height) { + height = document.getElementsByClassName("tabbed-editor")[0].clientHeight; + } + this.state.topPanelHeight = height + ev.detail.delta; + } + + async downloadCode() { + const { js, css, xml } = this.state; + const content = await makeApp(js, css, xml); + await owl.utils.loadJS("libs/FileSaver.min.js"); + saveAs(content, "app.zip"); + } +} +App.components = { TabbedEditor }; + +//------------------------------------------------------------------------------ +// Application initialization +//------------------------------------------------------------------------------ +async function start() { + document.title = `${document.title} (v${owl.__info__.version})`; + const commit = `https://github.com/odoo/owl/commit/${owl.__info__.hash}`; + console.info(`This application is using Owl built with the following commit:`, commit); + const [templates] = await Promise.all([ + owl.utils.loadFile("templates.xml"), + owl.utils.whenReady() + ]); + const qweb = new owl.QWeb({ templates }); + owl.Component.env = { qweb }; + const app = new App(); + app.mount(document.body); +} + +start(); diff --git a/tools/playground/old_index.html b/tools/playground/old_index.html new file mode 100644 index 00000000..ad801391 --- /dev/null +++ b/tools/playground/old_index.html @@ -0,0 +1,21 @@ + + + + + + OWL Playground + + + + + + + + + + + + + + + diff --git a/tools/playground/old_templates.xml b/tools/playground/old_templates.xml new file mode 100644 index 00000000..c0c1300a --- /dev/null +++ b/tools/playground/old_templates.xml @@ -0,0 +1,57 @@ + +
+
+ + + + + +
+
+
+ +
+
+ + + +
+ + +
+
+
+
+
🦉 Odoo Web Library 🦉
+
v
+ +
+

Note: these examples are using recent features of Javascript, and require a recent browser to work without a transpilation step! + For example, it makes use of class fields and class static fields. These examples should work in a recent Chrome version. +

+
+
+
+
+
+ \ No newline at end of file diff --git a/tools/playground/templates.xml b/tools/playground/templates.xml index c0c1300a..cc3c1b18 100644 --- a/tools/playground/templates.xml +++ b/tools/playground/templates.xml @@ -1,57 +1,79 @@ -
-
- - - - - -
-
-
-
-
-