154 lines
4.2 KiB
JavaScript
154 lines
4.2 KiB
JavaScript
// windows.js
|
||
|
||
document.addEventListener("alpine:init", () => {
|
||
// 1. Globaler Store for Window Managment
|
||
Alpine.store("Yet_WM", {
|
||
topZ: 1000,
|
||
getNewZ() {
|
||
return ++this.topZ;
|
||
},
|
||
});
|
||
|
||
Alpine.data("YetWindow", (id, initialX = 50, initialY = 50) => ({
|
||
id: id,
|
||
pos: { x: parseInt(initialX), y: parseInt(initialY) },
|
||
lastPos: { x: 0, y: 0 },
|
||
dragging: false,
|
||
minimized: false,
|
||
fullscreen: false,
|
||
zIndex: 1000,
|
||
offset: { x: 0, y: 0 },
|
||
|
||
init() {
|
||
// Lade gespeicherten Zustand (einheitlicher Key: yet_win_)
|
||
const saved = JSON.parse(localStorage.getItem(`yet_win_${this.id}`));
|
||
if (saved) {
|
||
this.pos = { x: saved.x, y: saved.y };
|
||
this.minimized = saved.min;
|
||
}
|
||
this.focus();
|
||
this.keepInBounds();
|
||
},
|
||
|
||
focus() {
|
||
this.zIndex = Alpine.store("Yet_WM").getNewZ();
|
||
},
|
||
|
||
startDrag(e) {
|
||
if (e.target.closest("button") || this.fullscreen) return;
|
||
|
||
// Verhindert Text-Markierung während des Verschiebens
|
||
e.preventDefault();
|
||
|
||
this.focus();
|
||
this.dragging = true;
|
||
this.offset.x = e.clientX - this.pos.x;
|
||
this.offset.y = e.clientY - this.pos.y;
|
||
},
|
||
|
||
onDrag(e) {
|
||
if (!this.dragging) return;
|
||
let newX = e.clientX - this.offset.x;
|
||
let newY = e.clientY - this.offset.y;
|
||
|
||
const margin = 20;
|
||
this.pos.x = Math.max(margin - 350, Math.min(newX, window.innerWidth - 50));
|
||
this.pos.y = Math.max(0, Math.min(newY, window.innerHeight - 40));
|
||
},
|
||
|
||
stopDrag() {
|
||
if (this.dragging) {
|
||
this.dragging = false;
|
||
this.save();
|
||
}
|
||
},
|
||
|
||
toggleMinimize() {
|
||
this.minimized = !this.minimized;
|
||
this.save();
|
||
},
|
||
|
||
toggleFullscreen() {
|
||
if (!this.fullscreen) {
|
||
this.lastPos = { ...this.pos };
|
||
this.pos = { x: 0, y: 0 };
|
||
this.fullscreen = true;
|
||
} else {
|
||
this.pos = { ...this.lastPos };
|
||
this.fullscreen = false;
|
||
}
|
||
this.focus();
|
||
},
|
||
|
||
save() {
|
||
localStorage.setItem(
|
||
`yet_win_${this.id}`,
|
||
JSON.stringify({
|
||
x: this.pos.x,
|
||
y: this.pos.y,
|
||
min: this.minimized,
|
||
})
|
||
);
|
||
},
|
||
|
||
keepInBounds() {
|
||
if (this.pos.x > window.innerWidth) this.pos.x = 50;
|
||
if (this.pos.y > window.innerHeight) this.pos.y = 50;
|
||
},
|
||
}));
|
||
});
|
||
|
||
// Definition der Web Component
|
||
class YetWindowElement extends HTMLElement {
|
||
connectedCallback() {
|
||
const id = this.getAttribute("id") || "win_" + Math.random().toString(36).substr(2, 9);
|
||
const title = this.getAttribute("title") || "Window";
|
||
const x = this.getAttribute("x") || "50";
|
||
const y = this.getAttribute("y") || "50";
|
||
const width = this.getAttribute("width") || "450px";
|
||
const headerClass = this.getAttribute("header-class") || "bg-dark text-white";
|
||
|
||
const content = this.innerHTML;
|
||
|
||
this.innerHTML = `
|
||
<div
|
||
x-data="YetWindow('${id}', ${x}, ${y})"
|
||
@mousemove.window="onDrag"
|
||
@mouseup.window="stopDrag"
|
||
@mousedown="focus"
|
||
class="card shadow-lg position-absolute"
|
||
:class="{ 'w-100 h-100 m-0 shadow-none': fullscreen }"
|
||
:style="\`left: \${pos.x}px; top: \${pos.y}px; z-index: \${zIndex}; width: \${fullscreen ? '100vw' : '${width}'}; user-select: \${dragging ? 'none' : 'auto'};\`"
|
||
x-cloak
|
||
>
|
||
<div
|
||
class="card-header d-flex justify-content-between align-items-center drag-handle ${headerClass}"
|
||
@mousedown="startDrag"
|
||
@dblclick="toggleFullscreen"
|
||
style="user-select: none;"
|
||
>
|
||
<h6 class="mb-0">${title}</h6>
|
||
|
||
<div class="d-flex align-items-center gap-1">
|
||
<button class="btn btn-sm btn-outline-light py-0 px-2" @click="toggleFullscreen">
|
||
<span>▢</span>
|
||
</button>
|
||
|
||
<button class="btn btn-sm btn-outline-light py-0 px-2" @click="toggleMinimize">
|
||
<span x-text="minimized ? '+' : '−'"></span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div x-show="!minimized" class="flex-grow-1 overflow-auto">
|
||
<div class="card-body">
|
||
${content}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
}
|
||
|
||
customElements.define("yet-window", YetWindowElement);
|