import { UiElement } from "./UiElement.js";
import "./Window.css";
export { Window };
/**
* Basic Window Popup Class for tools
*
* options = {
* top: absolute top position in pixel
* left: absolute left position in pixel
* pointerEvent: Use pointer event for window position
* height: in pixel
* width: in pixel
* title: string
* content: HTML-sting or HTML-element
* open: boolean, open window direcly after creating,
* responsive: boolean, fullscreen on small screens
* draggable: boolean, enable drag move,
* mode: slim / default
* }
*
* @author rhess <robin.hess@awi.de>
* @memberof vef.ui
*/
class Window extends UiElement {
static openedWindows = [];
/**
* @param {HTMLElement | string} target default is document.body
* @param {object} options
*/
constructor(target, options) {
super(target || document.body, {
"open": [],
"close": [],
"setTitle": []
});
this.titleEditMode_ = false;
this.title_ = document.createElement("span");
this.content_ = document.createElement("div");
this.header_ = document.createElement("div");
this.opened_ = false;
this.resizeCallback_ = null;
this.initElement_();
this.setOptions(options || {});
this.options_ = options;
this.initDragMove_();
}
/**
* <div class="vef-window">
* <div class="vef-window-header">
* <span class="vef-window-title">{TITLE}</span>
* <a class="vef-window-close"></a>
* </div>
* <div class="vef-window-content">{CONTENT}</div>
* </div>
*/
initElement_() {
const element = this.getElement();
element.classList.add("vef-window");
const pointerContainer = document.createElement("div");
pointerContainer.classList.add("pointer-container");
const header = this.header_;
header.classList.add("vef-window-header");
const close = document.createElement("i");
close.classList.add("fas");
close.classList.add("fa-times");
close.onclick = () => this.close();
this.title_.classList.add("vef-window-title");
this.content_.classList.add("vef-window-content");
header.appendChild(this.title_);
header.appendChild(close);
element.appendChild(pointerContainer);
element.appendChild(header);
element.appendChild(this.content_);
}
/**
* Update the options of the window
*
* @param {object} options
*/
setOptions(options) {
const element = this.getElement();
// a pointer event overrides top and right options
if(options.pointerEvent) {
element.style.left = (options.pointerEvent.clientX - 5) + "px";
element.style.top = (options.pointerEvent.clientY - 5) + "px";
} else {
if (options.left) element.style.left = options.left;
if (options.top) element.style.top = options.top;
}
if (options.height) element.style.height = options.height;
if (options.width) element.style.width = options.width;
if (options.title){
if(!options.editTitle){
this.setTitle(options.title);
}else{
this.setTitleEditable(options.title);
}
}
if (options.content) this.setContent(options.content);
// select different modes for the window
if (options.mode) {
if (options.mode == "slim") {
element.classList.add("slim");
} else if (options.mode == "default") {
element.classList.remove("slim");
}
}
// boolean to open window
if (typeof options.open == "boolean") {
if (options.open) {
this.open();
} else {
this.close();
}
}
// boolean responsive fullscreen on small devices
if (typeof options.responsive == "boolean") {
if (options.responsive) {
element.classList.add("responsive");
} else {
element.classList.remove("responsive");
}
}
// boolean to enable dragging the window
if (typeof options.draggable == "boolean") {
if (options.draggable) {
this.enableDragMove();
} else {
this.disableDragMove();
}
}
if (options.center) this.center();
}
/**
* Centers the window globally in the browsers frame
*/
center() {
const element = this.getElement();
const viewportHeight = document.documentElement.clientHeight;
const viewportWidth = document.documentElement.clientWidth;
element.style.top = (viewportHeight / 2) - (element.clientHeight / 2) + "px";
element.style.left = (viewportWidth / 2) - (element.clientWidth / 2) + "px";
}
/**
* calculate the position and place it inside the
* browser window bounds.
*
* @private
*/
calculatePosition_() {
const element = this.getElement();
const rect = element.getBoundingClientRect();
const viewportWidth = document.documentElement.clientWidth;
const viewportHeight = document.documentElement.clientHeight;
if ((rect.left + rect.width) > viewportWidth) {
let left = viewportWidth - rect.width;
element.style.left = ((left > 0) ? left : 0) + "px";
}
if ((rect.top + rect.height) > viewportHeight) {
let top = viewportHeight - rect.height;
element.style.top = ((top > 0) ? top : 0) + "px";
}
}
/**
* enable the window resize event.
* Used for repositioning the Window on resize
*
* @private
* @override
*/
enableResizeEvent_() {
if (!this.resizeCallback_) {
this.resizeCallback_ = () => this.calculatePosition_();
document.addEventListener("scroll", this.resizeCallback_, true);
window.addEventListener("resize", this.resizeCallback_);
}
}
/**
* disable the window resize event
*
* @private
*/
disableResizeEvent_() {
if (this.resizeCallback_) {
window.removeEventListener("resize", this.resizeCallback_);
document.removeEventListener("scroll", this.resizeCallback_, true);
this.resizeCallback_ = null;
}
}
/**
* Initializes the events for moving the
* window with the mouse
*
* @private
*/
initDragMove_() {
const element = this.getElement();
let parent, x, y, rect, viewportWidth, viewportHeight;
const mouseMove = (e) => {
let left = e.clientX - x;
let top = e.clientY - y;
// keep window in viewport bounds
if ((left + rect.width) > viewportWidth) left = viewportWidth - rect.width;
if ((top + rect.height) > viewportHeight) top = viewportHeight - rect.height;
element.style.top = ((top > 0) ? top : 0) + "px";
element.style.left = ((left > 0) ? left : 0) + "px";
};
const mouseUp = () => {
parent.removeEventListener("mousemove", mouseMove);
document.body.removeEventListener("mouseup", mouseUp);
};
this.header_.addEventListener("mousedown", (e) => {
parent = this.getElement().parentElement;
if (this.draggable_) {
viewportWidth = document.documentElement.clientWidth;
viewportHeight = document.documentElement.clientHeight;
rect = element.getBoundingClientRect();
x = e.clientX - rect.left;
y = e.clientY - rect.top;
parent.addEventListener("mousemove", mouseMove);
document.body.addEventListener("mouseup", mouseUp);
}
});
}
/**
* enable moving the window with the mouse
*/
enableDragMove() {
this.header_.classList.add("draggable");
this.draggable_ = true;
}
/**
* disable moving the window with the mouse
*/
disableDragMove() {
this.header_.classList.remove("draggable");
this.draggable_ = false;
}
/**
* Change the title in the header
*
* @param {string} title
*/
setTitle(title) {
if(title == undefined) return
this.title_.innerHTML = title;
this.title_.title = this.title_.innerText;
}
setTitleEditable(title){
this.title_.innerHTML = title + '<i class="vef vef-rename edit-button"></i>';
if(this.options_.title){
this.title_.innerText = this.options_.title;
this.title_.innerHTML += '<i class="vef vef-rename edit-button"></i>';
}
this.title_.parentElement.classList.remove("draggable");
if(!this.title_.classList.value.includes(["edit-mode","not-edit-mode"])) this.title_.classList.add('not-edit-mode');
this.title_.lastChild.addEventListener("click", () => {
if(this.titleEditMode_){
this.disableTitleEdit();
}else {
this.enableTitleEdit(this.title_);
}
})
}
enableTitleEdit(){
this.title_.classList.add('edit-mode');
this.title_.classList.remove('not-edit-mode');
const a = document.createElement("input");
a.className = "vef-window-title-field";
a.value = this.title_.innerText
this.title_.replaceWith(a);
a.focus();
a.addEventListener("blur", () => {
this.options_.title = a.value;
this.titleEditMode_ = true;
this.disableTitleEdit(a);
});
a.addEventListener("keypress", (e) => {
if(e.key == 'Enter'){
this.options_.title = a.value;
this.titleEditMode_ = true;
this.disableTitleEdit(a);
}
});
this.titleEditMode_ = true;
}
disableTitleEdit(element){
element.replaceWith(this.title_);
this.title_.classList.remove('edit-mode');
this.title_.classList.add('not-edit-mode');
this.titleEditMode_ = false;
this.setTitleEditable(this.options_.title);
}
/**
* Change the content of the window
*
* @param {string | HTMLElement} title
*/
setContent(content) {
if (typeof content === "string") {
this.content_.innerHTML = content;
} else if (content instanceof HTMLElement) {
this.content_.innerHTML = "";
this.content_.appendChild(content);
}
}
/**
* Hide the window by removing it from the parent
*/
close() {
this.getElement().classList.remove("open");
this.disableResizeEvent_();
this.opened_ = false;
const index = Window.openedWindows.indexOf(this)
if (index >= 0) {
Window.openedWindows.splice(index, 1)
}
this.fire("close", this);
}
/**
* Show the window by adding it to the parent
*/
open() {
this.getElement().classList.add("open");
this.calculatePosition_();
this.enableResizeEvent_();
this.opened_ = true;
if (!Window.openedWindows.includes(this)) {
Window.openedWindows.push(this);
}
this.fire("open", this);
}
/**
* dispose the Window
* @override
*/
dispose() {
this.close()
super.dispose();
}
static closeAll() {
for (let i = Window.openedWindows.length - 1; i >= 0; --i) {
Window.openedWindows[i].close();
}
}
}