mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
193 lines
5.1 KiB
JavaScript
193 lines
5.1 KiB
JavaScript
|
|
// 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
|
||
|
|
// demonstration. Managing windows can be as complex as we want. For example,
|
||
|
|
// we could implement the following features:
|
||
|
|
// - resizing windows
|
||
|
|
// - minimizing windows
|
||
|
|
// - configuration options for windows to make a window non resizeable
|
||
|
|
// - minimal width/height
|
||
|
|
// - better heuristic for initial window position
|
||
|
|
// - ...
|
||
|
|
import { Component, useState, mount, useRef, reactive, useEnv, onMounted } from "@odoo/owl";
|
||
|
|
|
||
|
|
// -----------------------------------------------------------------------------
|
||
|
|
// Window manager code
|
||
|
|
// -----------------------------------------------------------------------------
|
||
|
|
|
||
|
|
class WindowManager {
|
||
|
|
// contains all components with metadata
|
||
|
|
static Windows = {};
|
||
|
|
windows = {}; // mapping id => info
|
||
|
|
nextId = 1;
|
||
|
|
|
||
|
|
add(type) {
|
||
|
|
const Comp = WindowManager.Windows[type];
|
||
|
|
const left = 50 + Math.round(Math.random()*(window.innerWidth - 50 - Comp.defaultWidth));
|
||
|
|
const top = 50 + Math.round(Math.random()*(window.innerHeight - 100 - Comp.defaultHeight));
|
||
|
|
const id = this.nextId++;
|
||
|
|
this.windows[id] = {
|
||
|
|
id,
|
||
|
|
title: Comp.defaultTitle,
|
||
|
|
width: Comp.defaultWidth,
|
||
|
|
height: Comp.defaultHeight,
|
||
|
|
left,
|
||
|
|
top,
|
||
|
|
Component: Comp,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
close(id) {
|
||
|
|
delete this.windows[id];
|
||
|
|
}
|
||
|
|
|
||
|
|
updatePosition(id, left, top) {
|
||
|
|
const w = this.windows[id];
|
||
|
|
w.left = left;
|
||
|
|
w.top = top;
|
||
|
|
}
|
||
|
|
|
||
|
|
getWindows() {
|
||
|
|
return Object.values(this.windows);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
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";
|
||
|
|
static nextZIndex = 1;
|
||
|
|
zIndex = 0;
|
||
|
|
|
||
|
|
setup() {
|
||
|
|
this.windowService = useWindowService();
|
||
|
|
this.root = useRef('root');
|
||
|
|
onMounted(this.updateZIndex);
|
||
|
|
}
|
||
|
|
|
||
|
|
get style() {
|
||
|
|
let { width, height, top, left } = this.props.info;
|
||
|
|
return `width: ${width}px;height: ${height}px;top:${top}px;left:${left}px;z-index:${this.zIndex}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
close() {
|
||
|
|
this.windowService.close(this.props.info.id);
|
||
|
|
}
|
||
|
|
|
||
|
|
startDragAndDrop(ev) {
|
||
|
|
this.updateZIndex();
|
||
|
|
const self = this;
|
||
|
|
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;
|
||
|
|
|
||
|
|
window.addEventListener("mousemove", moveWindow);
|
||
|
|
window.addEventListener("mouseup", stopDnD, { once: true });
|
||
|
|
|
||
|
|
function moveWindow(ev) {
|
||
|
|
left = Math.max(offsetX + ev.pageX, 0);
|
||
|
|
top = Math.max(offsetY + ev.pageY, 0);
|
||
|
|
el.style.left = `${left}px`;
|
||
|
|
el.style.top = `${top}px`;
|
||
|
|
}
|
||
|
|
function stopDnD() {
|
||
|
|
window.removeEventListener("mousemove", moveWindow);
|
||
|
|
el.classList.remove('dragging');
|
||
|
|
|
||
|
|
if (top !== undefined && left !== undefined) {
|
||
|
|
self.windowService.updatePosition(current.id, left, top);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
updateZIndex() {
|
||
|
|
this.zIndex = Window.nextZIndex++;
|
||
|
|
this.root.el.style['z-index'] = this.zIndex;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// -----------------------------------------------------------------------------
|
||
|
|
// 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() {
|
||
|
|
this.windowService = useWindowService();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// -----------------------------------------------------------------------------
|
||
|
|
// Root Component
|
||
|
|
// -----------------------------------------------------------------------------
|
||
|
|
|
||
|
|
class Root extends Component {
|
||
|
|
static template = "Root";
|
||
|
|
static components = { WindowContainer };
|
||
|
|
|
||
|
|
setup() {
|
||
|
|
this.windowService = useWindowService();
|
||
|
|
}
|
||
|
|
|
||
|
|
addWindow(type) {
|
||
|
|
this.windowService.add(type);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// -----------------------------------------------------------------------------
|
||
|
|
// Setup
|
||
|
|
// -----------------------------------------------------------------------------
|
||
|
|
|
||
|
|
const env = {
|
||
|
|
windowService: createWindowService(),
|
||
|
|
};
|
||
|
|
|
||
|
|
mount(Root, document.body, { templates: TEMPLATES, env, dev: true });
|