@@ -1010,53 +1042,56 @@ body {
`;
const SLOTS = /*js*/ `
+
// We show here how slots can be used to create generic components.
// In this example, the Card component is basically only a container. It is not
// aware of its content. It just knows where it should be (with t-slot).
// The parent component define the content with t-set-slot.
//
-// Note that the t-on-click event, defined in the App template, is executed in
-// the context of the App component, even though it is inside the Card component
+// Note that the t-on-click event, defined in the Root template, is executed in
+// the context of the Root component, even though it is inside the Card component
const { Component, useState, mount } = owl;
class Card extends Component {
- setup() {
- this.state = useState({ showContent: true });
- }
+ static template = "Card";
+
+ setup() {
+ this.state = useState({ showContent: true });
+ }
- toggleDisplay() {
- this.state.showContent = !this.state.showContent;
- }
+ toggleDisplay() {
+ this.state.showContent = !this.state.showContent;
+ }
}
-Card.template = "Card";
class Counter extends Component {
- setup() {
- this.state = useState({val: 1});
- }
+ static template = "Counter";
+
+ setup() {
+ this.state = useState({val: 1});
+ }
- inc() {
- this.state.val++;
- }
+ inc() {
+ this.state.val++;
+ }
}
-Counter.template = "Counter";
// Main root component
-class App extends Component {
- setup() {
- this.state = useState({a: 1, b: 3});
- this.inc = this.inc.bind(this);
- }
+class Root extends Component {
+ static template = "Root"
+ static components = { Card, Counter };
+
+ setup() {
+ this.state = useState({a: 1, b: 3});
+ }
- inc(key, delta) {
- this.state[key] += delta;
- }
+ inc(key, delta) {
+ this.state[key] += delta;
+ }
}
-App.components = {Card, Counter};
-App.template = "App";
// Application setup
-mount(App, document.body);
+mount(Root, document.body, { templates: TEMPLATES, dev: true});
`;
const SLOTS_XML = /*xml*/`
@@ -1079,20 +1114,21 @@ const SLOTS_XML = /*xml*/`
Inc
+
Content of card 1... [ ]
- Increment A
+ Increment A
Card 2... [ ]
- Decrement B
+ Decrement B
-`;
+
+`;
const SLOTS_CSS = /*css*/ `
.main {
@@ -1147,20 +1183,21 @@ const FORM = /*js*/`
const { Component, useState, mount } = owl;
class Form extends Component {
- setup() {
- this.state = useState({
- text: "",
- othertext: "",
- number: 11,
- color: "",
- bool: false
- });
- }
+ static template = "Form";
+
+ setup() {
+ this.state = useState({
+ text: "",
+ othertext: "",
+ number: 11,
+ color: "",
+ bool: false
+ });
+ }
}
-Form.template = "Form";
// Application setup
-mount(Form, document.body);
+mount(Form, document.body, { templates: TEMPLATES, dev: true });
`;
const FORM_XML = /*xml*/ `
@@ -1203,113 +1240,6 @@ const FORM_XML = /*xml*/ `
`;
-const PORTAL_COMPONENTS = /*js*/`
-// This shows the expected use case of Portal
-// which is to implement something similar
-// to bootstrap modal
-const { Component, useState, mount, Portal } = owl;
-
-class Modal extends Component {}
-Modal.components = { Portal };
-Modal.template = "Modal";
-
-class Dialog extends Component {}
-Dialog.components = { Modal };
-Dialog.template = "Dialog";
-
-class Interstellar extends Component {}
-Interstellar.template = "Interstellar";
-
-// Main root component
-class App extends Component {
- state = useState({
- name: 'Portal used for Dialog (Modal)',
- dialog: false,
- text: 'Hello !',
- });
-}
-App.components = { Dialog , Interstellar };
-App.template = "App";
-
-// Application setup
-mount(App, document.body);
-`;
-
-const PORTAL_XML = /*xml*/`
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
This is a subComponent
-
The events it triggers will go through the Portal and be teleported
- on the other side of the wormhole it has created
-
Close the wormhole
-
-
-
-
-
Open Dialog
-
-
-
-
-
-
-`;
-
-const PORTAL_CSS = /*css*/`
-.owl-modal-supercontainer {
- position: static;
-}
-.owl-modal-backdrop {
- position: fixed;
- top: 0;
- left:0;
- background-color: #000000;
- opacity: 0.5;
- width: 100vw;
- height: 100vh;
- z-index: 1000;
-}
-.owl-modal-container {
- opacity:1;
- z-index: 1050;
- position: fixed;
- top: 0;
- left:0;
- width: 100%;
- height: 100%;
-}
-.owl-dialog-body {
- max-width: 500px;
- margin: 0 auto;
- position: relative;
- text-align: center;
- padding: 2rem;
- background-color: #FFFFFF;
- max-height: 100%;
-}
-.owl-interstellar {
- border: groove;
-}`;
-
const WMS = /*js*/`
// This example is slightly more complex than usual. We demonstrate
// here a way to manage sub windows in Owl, declaratively. This is still just a
@@ -1321,46 +1251,82 @@ const WMS = /*js*/`
// - minimal width/height
// - better heuristic for initial window position
// - ...
-const { Component, useState, mount, useRef } = owl;
-const { useBus } = utils;
+const { Component, useState, mount, useRef, reactive, useEnv, onMounted } = owl;
-class HelloWorld extends Component {}
-HelloWorld.template = "HelloWorld";
+// -----------------------------------------------------------------------------
+// Window manager code
+// -----------------------------------------------------------------------------
-class Counter extends Component {
- setup() {
- this.state = useState({ value: 0 });
+class WindowManager {
+ // contains all components with metadata
+ static Windows = {};
+ static nextZIndex = 1;
+ activeWindows = [];
+ nextId = 1;
+
+ add(type) {
+ const Comp = WindowManager.Windows[type];
+ const left = Math.round(Math.random()*(window.innerHeight - Comp.defaultHeight));
+ const top = Math.round(Math.random()*(window.innerWidth - Comp.defaultWidth));
+ this.activeWindows.push({
+ id: this.nextId++,
+ title: Comp.defaultTitle,
+ width: Comp.defaultWidth,
+ height: Comp.defaultHeight,
+ top,
+ left,
+ Component: Comp,
+ });
}
-
- inc() {
- this.state.value++;
+
+ close(id) {
+ const index = this.activeWindows.findIndex(w => w.id === id);
+ this.activeWindows.splice(index, 1);
}
}
-Counter.template = "Counter";
+
+function createWindowService() {
+ return reactive(new WindowManager());
+}
+
+function useWindowService() {
+ const env = useEnv();
+ return useState(env.windowService);
+}
+
+// -----------------------------------------------------------------------------
+// Generic Window Component
+// -----------------------------------------------------------------------------
class Window extends Component {
+ static template = "Window";
+
+ setup() {
+ this.windowService = useWindowService();
+ this.root = useRef('root');
+ onMounted(this.updateZIndex);
+ }
get style() {
- let { width, height, top, left, zindex } = this.props.info;
-
- return \`width: \${width}px;height: \${height}px;top:\${top}px;left:\${left}px;z-index:\${zindex}\`;
+ let { width, height, top, left } = this.props.info;
+ return \`width: $\{width}px;height: $\{height}px;top:$\{top}px;left:$\{left}px\`;
}
close() {
- this.env.wservice.close(this.props.info.id);
+ this.windowService.close(this.props.info.id);
}
startDragAndDrop(ev) {
this.updateZIndex();
- this.el.classList.add('dragging');
+ const root = this.root;
+ const el = root.el;
+ el.classList.add('dragging');
const current = this.props.info;
const offsetX = current.left - ev.pageX;
const offsetY = current.top - ev.pageY;
let left, top;
- const el = this.el;
- const self = this;
window.addEventListener("mousemove", moveWindow);
window.addEventListener("mouseup", stopDnD, { once: true });
@@ -1372,7 +1338,7 @@ class Window extends Component {
}
function stopDnD() {
window.removeEventListener("mousemove", moveWindow);
- self.el.classList.remove('dragging');
+ el.classList.remove('dragging');
if (top !== undefined && left !== undefined) {
current.top = top;
@@ -1382,104 +1348,83 @@ class Window extends Component {
}
updateZIndex() {
- this.env.wservice.updateZIndex(this.props.info, this.el);
+ this.root.el.style['z-index'] = WindowManager.neztZIndex++;
}
}
-Window.template = "Window";
-class WindowManager extends Component {
+// -----------------------------------------------------------------------------
+// Two concrete Window type implementations
+// -----------------------------------------------------------------------------
+
+class HelloWorld extends Component {
+ static template = "HelloWorld";
+ static defaultTitle = "Hello Owl!";
+ static defaultWidth = 200;
+ static defaultHeight = 100;
+}
+
+
+class Counter extends Component {
+ static template = "Counter";
+ static defaultTitle = "Click Counter";
+ static defaultWidth = 300;
+ static defaultHeight = 120;
+
+ state = useState({ value: 0 });
+
+ inc() {
+ this.state.value++;
+ }
+}
+
+// register window components
+WindowManager.Windows.Hello = HelloWorld;
+WindowManager.Windows.Counter = Counter;
+
+// -----------------------------------------------------------------------------
+// Window Container
+// -----------------------------------------------------------------------------
+
+class WindowContainer extends Component {
+ static template = "WindowContainer";
+ static components = { Window };
+
setup() {
- useBus(this.env.wservice, "update", () => this.render());
+ this.windowService = useWindowService();
}
}
-WindowManager.components = { Window };
-WindowManager.template = "WindowManager";
-class App extends Component {
+// -----------------------------------------------------------------------------
+// Root Component
+// -----------------------------------------------------------------------------
+
+class Root extends Component {
+ static template = "Root";
+ static components = { WindowContainer };
+
setup() {
- this.addWindow = this.addWindow.bind(this);
+ this.windowService = useWindowService();
}
- addWindow(name) {
- this.env.wservice.add(name);
+
+ addWindow(type) {
+ this.windowService.add(type);
}
}
-App.components = { WindowManager };
-App.template = "App";
-const windows = [
- {
- name: "Hello",
- title: "Hello",
- component: HelloWorld,
- defaultWidth: 200,
- defaultHeight: 100
- },
- {
- name: "Counter",
- title: "Click Counter",
- component: Counter,
- defaultWidth: 300,
- defaultHeight: 120
- }
-];
-
-function windowService() {
- let activeWindows = [];
- let nextId = 0;
- const bus = new owl.EventBus();
-
- let nextTop = 0;
- let nextLeft = 0;
- let nextZIndex = 1;
-
- function add(name) {
- const info = windows.find((w) => w.name === name);
-
- activeWindows.push({
- id: nextId++,
- title: info.title,
- width: info.defaultWidth,
- height: info.defaultHeight,
- top: nextTop,
- left: nextLeft,
- zindex: nextZIndex++,
- component: info.component
- });
-
- bus.trigger("update");
- nextTop += 30;
- nextLeft += 30;
- }
-
- function close(id) {
- activeWindows = activeWindows.filter(w => w.id !== id);
- bus.trigger("update");
- }
-
- const wservice = Object.assign(bus, {
- add, close,
- updateZIndex(window, el) {
- window.zindex = nextZIndex++;
- el.style["z-index"] = window.zindex;
- }
- });
-
- Object.defineProperty(wservice, "activeWindows", {
- get() { return activeWindows; }
- })
- return wservice;
-}
+// -----------------------------------------------------------------------------
+// Setup
+// -----------------------------------------------------------------------------
const env = {
- wservice: windowService(),
+ windowService: createWindowService(),
};
-mount(App, document.body, { env });
+mount(Root, document.body, { templates: TEMPLATES, env, dev: true });
`;
const WMS_XML =/*xml*/`
-
+
-
-
-
+
+
+
-
-
+
+
- World
+ Some content here...
- Inc
+ Click
@@ -1585,45 +1530,34 @@ body {
}`;
const SFC =/*js*/`
-// This example illustrates how Owl enables single file components,
-// which include code, template and style.
-//
-// This is very useful in some situations, such as testing or quick prototyping.
-// Note that this example has no external xml or css file, everything is
-// contained in a single js file.
+// This example illustrates how one can write Owl components with
+// inline templates.
const { Component, useState, xml, css, mount } = owl;
// Counter component
-const COUNTER_TEMPLATE = xml\`
-
- Click! [ ]
- \`;
-
-const COUNTER_STYLE = css\`
- button {
- color: blue;
- }\`;
-
class Counter extends Component {
+ static template = xml\`
+
+ Click! [ ]
+ \`;
+
state = useState({ value: 0})
}
-Counter.template = COUNTER_TEMPLATE;
-Counter.style = COUNTER_STYLE;
-// App
-const APP_TEMPLATE = xml\`
-
-
-
-
\`;
-
-class App extends Component {}
-App.template = APP_TEMPLATE;
-App.components = { Counter };
+// Root
+class Root extends Component {
+ static template = xml\`
+
+
+
+
\`;
+
+ static components = { Counter };
+}
// Application setup
-mount(App, document.body);
+mount(Root, document.body, { templates: TEMPLATES, dev: true});
`;
export const SAMPLES = [
@@ -1639,7 +1573,7 @@ export const SAMPLES = [
xml: FORM_XML
},
{
- description: "Single File Components",
+ description: "Inline templates",
code: SFC
},
{
@@ -1649,7 +1583,7 @@ export const SAMPLES = [
css: LIFECYCLE_CSS
},
{
- description: "Hooks",
+ description: "Customized hook",
code: HOOKS_DEMO,
xml: HOOKS_DEMO_XML,
css: HOOKS_CSS
@@ -1678,10 +1612,4 @@ export const SAMPLES = [
xml: WMS_XML,
css: WMS_CSS,
},
- {
- description: "Portal (Dialog)",
- code: PORTAL_COMPONENTS,
- xml: PORTAL_XML,
- css: PORTAL_CSS,
- },
];