UNPKG

@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
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