@system-ui-js/base
Version:
A comprehensive UI system library with window management, task bars, and draggable components
521 lines (520 loc) • 18.5 kB
JavaScript
import k from "html2canvas";
function T(r, e) {
r.style.left = e.x + "px", r.style.top = e.y + "px";
}
function L(r, e = {}, n = {}) {
let t = !1, o = { x: 0, y: 0 };
const m = n.movedElement || r, x = (c) => ({
x: c.clientX,
y: c.clientY
}), w = (c) => {
const y = c.getBoundingClientRect();
return {
x: y.left,
y: y.top
};
}, l = (c) => {
if (console.log("mouseDown"), n.disabled) return;
c.preventDefault(), t = !0;
const y = x(c), p = w(m);
o = {
x: y.x - p.x,
y: y.y - p.y
}, e.start?.(y), document.addEventListener("mousemove", i), document.addEventListener("mouseup", f);
}, i = (c) => {
if (!t) return;
console.log("mouseMove", c), c.preventDefault();
const y = x(c), p = {
x: y.x - o.x,
y: y.y - o.y
};
T(m, p), e.move?.(y);
}, f = (c) => {
if (!t) return;
t = !1;
const y = x(c), p = {
x: y.x - o.x,
y: y.y - o.y
};
T(m, p), e.end?.(y), document.removeEventListener("mousemove", i), document.removeEventListener("mouseup", f);
};
return r.addEventListener("mousedown", l), {
triggerElement: r,
// 触发拖拽的元素
targetElement: m,
// 实际移动的元素
cleanup: () => {
r.removeEventListener("mousedown", l), document.removeEventListener("mousemove", i), document.removeEventListener("mouseup", f);
}
};
}
const S = 1e3, D = 1e4;
class A {
groups = /* @__PURE__ */ new Map();
defaultGroupName = "default";
/**
* 向组中添加元素
* @param component 组件对象
* @param groupName 组名
*/
addItem(e, n = this.defaultGroupName) {
let t = this.groups.get(n);
t || (t = {
items: /* @__PURE__ */ new Map(),
alwaysTopItems: /* @__PURE__ */ new Set(),
maxIndex: S,
maxAlwaysTopIndex: D
}, this.groups.set(n, t));
const o = ++t.maxIndex;
t.items.set(e, o), this.applyStyle(e, o);
}
/**
* 从组中移除元素
* @param component 组件对象
* @param groupName 组名
*/
removeItem(e, n = this.defaultGroupName) {
const t = this.groups.get(n);
t && (t.items.delete(e), t.alwaysTopItems.delete(e));
}
/**
* 提升元素层级到最顶层
* @param component 组件对象
* @param groupName 组名
*/
bringToFront(e, n = this.defaultGroupName) {
const t = this.groups.get(n);
if (!(!t || !t.items.has(e)))
if (t.alwaysTopItems.has(e)) {
const o = ++t.maxAlwaysTopIndex;
t.items.set(e, o), this.applyStyle(e, o);
} else {
const o = ++t.maxIndex;
t.items.set(e, o), this.applyStyle(e, o);
}
}
/**
* 设置元素永远置顶
* @param component 组件对象
* @param groupName 组名
*/
setAlwaysOnTop(e, n = this.defaultGroupName) {
const t = this.groups.get(n);
if (!t || !t.items.has(e) || t.alwaysTopItems.has(e))
return;
t.alwaysTopItems.add(e);
const o = ++t.maxAlwaysTopIndex;
t.items.set(e, o), this.applyStyle(e, o);
}
/**
* 取消元素永远置顶
* @param component 组件对象
* @param groupName 组名
*/
removeAlwaysOnTop(e, n = this.defaultGroupName) {
const t = this.groups.get(n);
if (!t || !t.items.has(e))
return;
t.alwaysTopItems.delete(e);
const o = ++t.maxIndex;
t.items.set(e, o), this.applyStyle(e, o);
}
/**
* 检查元素是否永远置顶
* @param component 组件对象
* @param groupName 组名
*/
isAlwaysOnTop(e, n = this.defaultGroupName) {
const t = this.groups.get(n);
return t ? t.alwaysTopItems.has(e) : !1;
}
/**
* 降低元素层级到最底层
* @param component 组件对象
* @param groupName 组名
*/
sendToBack(e, n = this.defaultGroupName) {
const t = this.groups.get(n);
if (!t || !t.items.has(e) || t.alwaysTopItems.has(e))
return;
let o = S;
for (const [x, w] of t.items.entries())
!t.alwaysTopItems.has(x) && w < o && (o = w);
const m = o - 1;
t.items.set(e, m), this.applyStyle(e, m);
}
/**
* 当有新的普通组件置顶时,确保永远置顶的组件仍然在最上层
* @param groupName 组名
*/
ensureAlwaysTopItemsOnTop(e) {
const n = this.groups.get(e);
if (n)
for (const t of n.alwaysTopItems) {
const o = ++n.maxAlwaysTopIndex;
n.items.set(t, o), this.applyStyle(t, o);
}
}
/**
* 获取元素的当前层级
* @param component 组件对象
* @param groupName 组名
*/
getIndex(e, n = this.defaultGroupName) {
const t = this.groups.get(n);
if (t)
return t.items.get(e);
}
/**
* 应用样式到组件元素
* @param component 组件对象
* @param index z-index 值
*/
applyStyle(e, n) {
const t = e.getElement();
t && (t.style.zIndex = n.toString(), t.style.position = t.style.position || "absolute");
}
/**
* 重写 bringToFront 方法,确保永远置顶的组件始终在最上层
*/
bringToFrontWithAlwaysTopCheck(e, n = this.defaultGroupName) {
this.bringToFront(e, n);
const t = this.groups.get(n);
t && !t.alwaysTopItems.has(e) && t.alwaysTopItems.size > 0 && this.ensureAlwaysTopItemsOnTop(n);
}
}
const I = new A();
function $(r, e) {
r.style.width = e.x + "px", r.style.height = e.y + "px";
}
function N(r, e) {
r.style.left = e.x + "px", r.style.top = e.y + "px";
}
function W(r, e, n) {
const t = { ...r };
return e && (t.x = Math.max(t.x, e.x), t.y = Math.max(t.y, e.y)), n && (t.x = Math.min(t.x, n.x), t.y = Math.min(t.y, n.y)), t;
}
function O(r, e = {}, n = {}) {
const {
disabled: t = !1,
resizedElement: o = r,
minSize: m = { x: 50, y: 50 },
maxSize: x,
handles: w = ["n", "s", "e", "w", "ne", "nw", "se", "sw"],
handleSize: l = 5
} = n;
let i = {
isResizing: !1,
handle: null,
startMousePos: { x: 0, y: 0 },
startSize: { x: 0, y: 0 },
startPosition: { x: 0, y: 0 }
};
const f = [], E = (a) => ({
x: a.clientX,
y: a.clientY
}), c = (a) => {
const s = a.getBoundingClientRect();
return {
x: s.width,
y: s.height
};
}, y = (a) => {
const s = a.getBoundingClientRect();
return {
x: s.left,
y: s.top
};
}, p = (a) => {
const s = document.createElement("div");
switch (s.className = `resize-handle resize-handle-${a}`, s.style.position = "absolute", s.style.backgroundColor = "transparent", s.style.zIndex = "1000", a) {
case "n":
s.style.top = "0px", s.style.left = `${l}px`, s.style.right = `${l}px`, s.style.height = `${l}px`, s.style.cursor = "n-resize";
break;
case "s":
s.style.bottom = "0px", s.style.left = `${l}px`, s.style.right = `${l}px`, s.style.height = `${l}px`, s.style.cursor = "s-resize";
break;
case "e":
s.style.right = "0px", s.style.top = `${l}px`, s.style.bottom = `${l}px`, s.style.width = `${l}px`, s.style.cursor = "e-resize";
break;
case "w":
s.style.left = "0px", s.style.top = `${l}px`, s.style.bottom = `${l}px`, s.style.width = `${l}px`, s.style.cursor = "w-resize";
break;
case "ne":
s.style.top = "0px", s.style.right = "0px", s.style.width = `${l}px`, s.style.height = `${l}px`, s.style.cursor = "ne-resize";
break;
case "nw":
s.style.top = "0px", s.style.left = "0px", s.style.width = `${l}px`, s.style.height = `${l}px`, s.style.cursor = "nw-resize";
break;
case "se":
s.style.bottom = "0px", s.style.right = "0px", s.style.width = `${l}px`, s.style.height = `${l}px`, s.style.cursor = "se-resize";
break;
case "sw":
s.style.bottom = "0px", s.style.left = "0px", s.style.width = `${l}px`, s.style.height = `${l}px`, s.style.cursor = "sw-resize";
break;
}
return s.addEventListener("mousedown", (h) => P(h, a)), s;
}, P = (a, s) => {
t || (a.preventDefault(), a.stopPropagation(), i.isResizing = !0, i.handle = s, i.startMousePos = E(a), i.startSize = c(o), i.startPosition = y(o), e.start?.(i.startMousePos, i.startSize), document.addEventListener("mousemove", b), document.addEventListener("mouseup", v));
}, b = (a) => {
if (!i.isResizing || !i.handle) return;
a.preventDefault();
const s = E(a), h = {
x: s.x - i.startMousePos.x,
y: s.y - i.startMousePos.y
};
let d = { ...i.startSize }, u = { ...i.startPosition };
switch (i.handle) {
case "n":
d.y = i.startSize.y - h.y, u.y = i.startPosition.y + h.y;
break;
case "s":
d.y = i.startSize.y + h.y;
break;
case "e":
d.x = i.startSize.x + h.x;
break;
case "w":
d.x = i.startSize.x - h.x, u.x = i.startPosition.x + h.x;
break;
case "ne":
d.x = i.startSize.x + h.x, d.y = i.startSize.y - h.y, u.y = i.startPosition.y + h.y;
break;
case "nw":
d.x = i.startSize.x - h.x, d.y = i.startSize.y - h.y, u.x = i.startPosition.x + h.x, u.y = i.startPosition.y + h.y;
break;
case "se":
d.x = i.startSize.x + h.x, d.y = i.startSize.y + h.y;
break;
case "sw":
d.x = i.startSize.x - h.x, d.y = i.startSize.y + h.y, u.x = i.startPosition.x + h.x;
break;
}
const g = W(d, m, x);
i.handle.includes("w") && g.x !== d.x && (u.x = i.startPosition.x + (i.startSize.x - g.x)), i.handle.includes("n") && g.y !== d.y && (u.y = i.startPosition.y + (i.startSize.y - g.y)), $(o, g), (i.handle.includes("w") || i.handle.includes("n")) && N(o, u), e.resize?.(s, g);
}, v = (a) => {
if (!i.isResizing) return;
i.isResizing = !1;
const s = E(a), h = c(o);
e.end?.(s, h), i.handle = null, document.removeEventListener("mousemove", b), document.removeEventListener("mouseup", v);
};
return getComputedStyle(r).position === "static" && (r.style.position = "relative"), w.forEach((a) => {
const s = p(a);
r.appendChild(s), f.push(s);
}), {
triggerElement: r,
targetElement: o,
handles: f,
cleanup: () => {
f.forEach((a) => {
a.remove();
}), document.removeEventListener("mousemove", b), document.removeEventListener("mouseup", v);
}
};
}
class C {
static instance;
windows = /* @__PURE__ */ new Map();
windowCounter = 0;
constructor() {
}
static getInstance() {
return C.instance || (C.instance = new C()), C.instance;
}
// 注册窗口
registerWindow(e) {
const n = `window_${this.windowCounter++}`;
return this.windows.set(n, e), n;
}
// 注销窗口
unregisterWindow(e) {
this.windows.delete(e);
}
// 获取窗口
getWindow(e) {
return this.windows.get(e);
}
// 获取所有窗口
getAllWindows() {
return Array.from(this.windows.values());
}
// 获取窗口数量
getWindowCount() {
return this.windows.size;
}
// 关闭所有窗口
closeAllWindows() {
this.windows.forEach((e) => {
e.close();
}), this.windows.clear();
}
// 最小化所有窗口
minimizeAllWindows() {
this.windows.forEach((e) => {
e.minimize();
});
}
// 查找特定类名的窗口
findWindowsByClassName(e) {
return this.getAllWindows().filter(
(n) => n.getElement().classList.contains(e)
);
}
// 置顶所有窗口到前台
bringAllToFront() {
this.windows.forEach((e) => {
e.focus();
});
}
// 获取窗口截图
async captureWindowScreenshot(e) {
const n = this.getWindow(e);
if (!n)
return null;
try {
const t = n.getElement();
return !t || t.offsetWidth === 0 || t.offsetHeight === 0 ? null : (await k(t, {
useCORS: !0,
allowTaint: !1,
background: void 0,
logging: !1
})).toDataURL("image/png");
} catch (t) {
return console.error("Failed to capture window screenshot:", t), null;
}
}
// 获取所有窗口的截图
async captureAllWindowScreenshots() {
const e = /* @__PURE__ */ new Map();
for (const [n] of this.windows) {
const t = await this.captureWindowScreenshot(n);
t && e.set(n, t);
}
return e;
}
}
const M = C.getInstance(), z = "window";
class H {
element;
isAlwaysToFront = !1;
windowId;
constructor(e, n, t, o) {
this.element = document.createElement("div"), this.element.className = `sys-ui_window ${n.className}`, this.element.style.width = (t?.x || 100) + "px", this.element.style.height = (t?.y || 100) + "px", this.element.style.left = (o?.x || 0) + "px", this.element.style.top = (o?.y || 0) + "px", this.element.style.position = "absolute", I.addItem(this, z), e.titleBar && this.element.appendChild(e.titleBar), e.content && this.element.appendChild(e.content), e.statusBar && this.element.appendChild(e.statusBar), e.titleBar && L(e.titleBar, {}, {
movedElement: this.element
}), O(this.element, {}, {}), this.element.addEventListener("mousedown", () => {
this.focus();
}), this.windowId = M.registerWindow(this), this.beforeCreated?.(), this.afterCreated && setTimeout(this.afterCreated.bind(this), 0);
}
getElement() {
return this.element;
}
getWindowId() {
return this.windowId;
}
// 关闭
close() {
this.beforeDestroyed?.(), M.unregisterWindow(this.windowId), this.element.remove(), I.removeItem(this, z), this.afterDestroyed?.();
}
// 最大化
maximize() {
}
// 最小化
minimize() {
}
// 恢复
restore() {
}
// 将窗口置于顶层
focus() {
this.isAlwaysToFront || I.bringToFront(this, z);
}
// 永远置顶
alwaysToFront() {
this.isAlwaysToFront = !0, I.setAlwaysOnTop(this, z);
}
}
class R {
containerDOM;
components = [];
// 默认撑满
constructor(e) {
this.containerDOM = document.createElement("div"), this.containerDOM.className = "sys-ui_container", e.appendChild(this.containerDOM);
}
addComponent(e) {
this.components.push(e), this.containerDOM.appendChild(e.getElement());
}
removeComponent(e) {
this.components.splice(this.components.indexOf(e), 1), this.containerDOM.removeChild(e.getElement());
}
}
function F(r) {
return new R(r);
}
class _ {
element;
leftContainer;
centerContainer;
rightContainer;
constructor(e, n) {
this.element = document.createElement("div"), this.element.className = `sys-ui_taskbar ${n.className || ""}`.trim(), this.element.style.position = "fixed", this.element.style.zIndex = "1000", this.setPositionStyles(n.position), this.leftContainer = document.createElement("div"), this.leftContainer.className = "sys-ui_taskbar-left", this.centerContainer = document.createElement("div"), this.centerContainer.className = "sys-ui_taskbar-center", this.rightContainer = document.createElement("div"), this.rightContainer.className = "sys-ui_taskbar-right", this.setContainerStyles(n.position), this.element.appendChild(this.leftContainer), this.element.appendChild(this.centerContainer), this.element.appendChild(this.rightContainer), e.left && this.leftContainer.appendChild(e.left), e.center && this.centerContainer.appendChild(e.center), e.right && this.rightContainer.appendChild(e.right);
}
setPositionStyles(e) {
switch (e) {
case "top":
this.element.style.top = "0", this.element.style.left = "0", this.element.style.right = "0", this.element.style.height = "40px";
break;
case "bottom":
this.element.style.bottom = "0", this.element.style.left = "0", this.element.style.right = "0", this.element.style.height = "40px";
break;
case "left":
this.element.style.top = "0", this.element.style.left = "0", this.element.style.bottom = "0", this.element.style.width = "60px";
break;
case "right":
this.element.style.top = "0", this.element.style.right = "0", this.element.style.bottom = "0", this.element.style.width = "60px";
break;
}
}
setContainerStyles(e) {
e === "top" || e === "bottom" ? (this.element.style.display = "flex", this.element.style.flexDirection = "row", this.element.style.alignItems = "center", this.leftContainer.style.display = "flex", this.leftContainer.style.alignItems = "center", this.leftContainer.style.justifyContent = "flex-start", this.leftContainer.style.flex = "0 0 auto", this.centerContainer.style.display = "flex", this.centerContainer.style.alignItems = "center", this.centerContainer.style.justifyContent = "center", this.centerContainer.style.flex = "1 1 auto", this.rightContainer.style.display = "flex", this.rightContainer.style.alignItems = "center", this.rightContainer.style.justifyContent = "flex-end", this.rightContainer.style.flex = "0 0 auto") : (this.element.style.display = "flex", this.element.style.flexDirection = "column", this.element.style.alignItems = "center", this.leftContainer.style.display = "flex", this.leftContainer.style.flexDirection = "column", this.leftContainer.style.alignItems = "center", this.leftContainer.style.justifyContent = "flex-start", this.leftContainer.style.flex = "0 0 auto", this.centerContainer.style.display = "flex", this.centerContainer.style.flexDirection = "column", this.centerContainer.style.alignItems = "center", this.centerContainer.style.justifyContent = "center", this.centerContainer.style.flex = "1 1 auto", this.rightContainer.style.display = "flex", this.rightContainer.style.flexDirection = "column", this.rightContainer.style.alignItems = "center", this.rightContainer.style.justifyContent = "flex-end", this.rightContainer.style.flex = "0 0 auto");
}
// Get the main element to append to DOM
getElement() {
return this.element;
}
// Update elements in specific positions
setLeftElement(e) {
this.leftContainer.innerHTML = "", this.leftContainer.appendChild(e);
}
setCenterElement(e) {
this.centerContainer.innerHTML = "", this.centerContainer.appendChild(e);
}
setRightElement(e) {
this.rightContainer.innerHTML = "", this.rightContainer.appendChild(e);
}
// Clear specific positions
clearLeft() {
this.leftContainer.innerHTML = "";
}
clearCenter() {
this.centerContainer.innerHTML = "";
}
clearRight() {
this.rightContainer.innerHTML = "";
}
// Clear all positions
clearAll() {
this.clearLeft(), this.clearCenter(), this.clearRight();
}
// Remove taskbar from DOM
destroy() {
this.element.remove();
}
}
export {
R as SysUiContainer,
_ as SysUiTaskBar,
H as SysUiWindow,
F as initContainer,
M as windowManager
};
//# sourceMappingURL=index.es.js.map