246 lines
9.0 KiB
JavaScript
246 lines
9.0 KiB
JavaScript
// ========================================
|
||
// 顶栏合并左右间距功能模块
|
||
// ========================================
|
||
|
||
import { debounce, throttle } from './utils.js';
|
||
|
||
// 存储观察器实例
|
||
let rightPanelObserver = null;
|
||
let leftPanelObserver = null;
|
||
let dockVerticalObserver = null;
|
||
let layoutCenterObserver = null;
|
||
|
||
// 存储更新函数
|
||
let updateRightMargins = null;
|
||
let updateLeftMargins = null;
|
||
|
||
// 检测是否为 macOS 平台
|
||
const isMacOS = () => {
|
||
return navigator.userAgent.includes('Macintosh') || navigator.userAgent.includes('Mac OS X');
|
||
};
|
||
|
||
/**
|
||
* 初始化顶栏合并左右间距功能
|
||
* @param {string} direction - 方向,"right" 或 "left"
|
||
*/
|
||
export const initTabBarsMarginUnified = (direction = "right") => {
|
||
const isRight = direction === "right";
|
||
|
||
// 更新边距的函数
|
||
const updateMargins = () => {
|
||
const isTopbarMerged = document.documentElement.getAttribute('savor-tabbar') === 'merge';
|
||
const tabBarSelector = isRight
|
||
? ".layout__center .layout-tab-bar--readonly"
|
||
: ".layout__center .layout-tab-bar:not(.layout-tab-bar--readonly)";
|
||
const allTabBars = document.querySelectorAll(tabBarSelector);
|
||
|
||
if (!isTopbarMerged) {
|
||
allTabBars.forEach(tabBar => {
|
||
tabBar.style[isRight ? "marginRight" : "marginLeft"] = "0px";
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 获取窗口和drag元素的位置信息
|
||
const drag = document.getElementById("drag");
|
||
let marginValue = "0px";
|
||
|
||
if (drag) {
|
||
if (isRight) {
|
||
// 计算 #drag 到窗口右边界的距离
|
||
const dragRect = drag.getBoundingClientRect();
|
||
const windowWidth = window.innerWidth;
|
||
let distanceToWindowRight = windowWidth - dragRect.right;
|
||
|
||
// 减去右侧dock的宽度
|
||
const rightDock = document.querySelector(".layout__dockr");
|
||
const rightDockWidth = rightDock?.classList.contains("layout--float")
|
||
? rightDock.querySelector(".dock")?.offsetWidth ?? 0
|
||
: rightDock?.offsetWidth ?? 0;
|
||
const rightDockSelector = "#dockRight";
|
||
const rightDockRealWidth = document.querySelector(rightDockSelector)?.offsetWidth ?? 0;
|
||
|
||
distanceToWindowRight -= (rightDockWidth + rightDockRealWidth);
|
||
|
||
// 如果结果是负数,直接为0
|
||
distanceToWindowRight = Math.max(0, distanceToWindowRight);
|
||
|
||
marginValue = `${distanceToWindowRight}px`;
|
||
} else {
|
||
// 计算 #drag 到窗口左边界的距离
|
||
const dragRect = drag.getBoundingClientRect();
|
||
let distanceToWindowLeft = dragRect.left;
|
||
|
||
// 减去左侧dock的宽度
|
||
const leftDock = document.querySelector(".layout__dockl");
|
||
const leftDockWidth = leftDock?.classList.contains("layout--float")
|
||
? leftDock.querySelector(".dock")?.offsetWidth ?? 0
|
||
: leftDock?.offsetWidth ?? 0;
|
||
const leftDockSelector = "#dockLeft";
|
||
const leftDockRealWidth = document.querySelector(leftDockSelector)?.offsetWidth ?? 0;
|
||
|
||
distanceToWindowLeft -= (leftDockWidth + leftDockRealWidth);
|
||
|
||
// 如果结果是负数,直接为0
|
||
distanceToWindowLeft = Math.max(0, distanceToWindowLeft);
|
||
|
||
marginValue = `${distanceToWindowLeft}px`;
|
||
}
|
||
}
|
||
|
||
// 应用 margin
|
||
if (isRight) {
|
||
allTabBars.forEach(tabBar => tabBar.style.marginRight = "0px");
|
||
const resizers = document.querySelectorAll(".layout__center .layout__resize:not(.layout__resize--lr)");
|
||
if (resizers.length === 0) {
|
||
const lastTabBar = allTabBars[allTabBars.length - 1];
|
||
if (lastTabBar && (lastTabBar.closest(".layout__dockr") || lastTabBar.closest(".layout__center"))) {
|
||
lastTabBar.style.marginRight = marginValue;
|
||
}
|
||
} else {
|
||
resizers.forEach(resizer => {
|
||
let prevElement = resizer.previousElementSibling;
|
||
if (!prevElement) return;
|
||
const prevTabBars = prevElement.querySelectorAll(".layout-tab-bar--readonly");
|
||
if (prevTabBars.length > 0) {
|
||
const lastTabBar = prevTabBars[prevTabBars.length - 1];
|
||
if (lastTabBar.closest(".layout__dockr") || lastTabBar.closest(".layout__center")) {
|
||
lastTabBar.style.marginRight = marginValue;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
} else {
|
||
const firstTabBar = allTabBars[0];
|
||
if (firstTabBar) {
|
||
firstTabBar.style.marginLeft = marginValue;
|
||
}
|
||
}
|
||
};
|
||
|
||
// 为右/左侧分别存储更新函数
|
||
if (isRight) {
|
||
updateRightMargins = updateMargins;
|
||
window.updateTabBarsMargin = updateMargins;
|
||
} else {
|
||
updateLeftMargins = updateMargins;
|
||
window.updateTabBarsMarginLeft = updateMargins;
|
||
}
|
||
|
||
// observer 只初始化一次
|
||
const dockSelector = isRight ? ".layout__dockr" : ".layout__dockl";
|
||
const dockVerticalSelector = ".dock--vertical";
|
||
|
||
// 创建观察器
|
||
const panelObserver = new ResizeObserver(updateMargins);
|
||
const dockVerticalObserverInstance = new MutationObserver(updateMargins);
|
||
|
||
function observeDock() {
|
||
const dock = document.querySelector(dockSelector);
|
||
if (dock) panelObserver.observe(dock);
|
||
const dockVertical = document.querySelector(dockVerticalSelector);
|
||
if (dockVertical) {
|
||
dockVerticalObserverInstance.observe(dockVertical, {
|
||
attributes: true,
|
||
attributeFilter: ["class"]
|
||
});
|
||
}
|
||
}
|
||
|
||
// 监听 body 结构变化,dock/dockVertical 变化时重新 observe
|
||
new MutationObserver(() => {
|
||
panelObserver.disconnect();
|
||
dockVerticalObserverInstance.disconnect();
|
||
observeDock();
|
||
updateMargins();
|
||
}).observe(document.body, { childList: true, subtree: true });
|
||
|
||
// 主题和页面加载时刷新
|
||
window.addEventListener("load", updateMargins);
|
||
document.addEventListener("themechange", updateMargins);
|
||
|
||
// 首次初始化
|
||
observeDock();
|
||
updateMargins();
|
||
setTimeout(updateMargins, 500);
|
||
setTimeout(updateMargins, 1000);
|
||
|
||
// 存储观察器实例以便后续清理
|
||
if (isRight) {
|
||
rightPanelObserver = panelObserver;
|
||
dockVerticalObserver = dockVerticalObserverInstance;
|
||
} else {
|
||
leftPanelObserver = panelObserver;
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 清理顶栏合并状态的辅助函数
|
||
*/
|
||
export const cleanupTopbarMerge = () => {
|
||
// 断开观察器
|
||
if (rightPanelObserver) {
|
||
rightPanelObserver.disconnect();
|
||
rightPanelObserver = null;
|
||
}
|
||
if (leftPanelObserver) {
|
||
leftPanelObserver.disconnect();
|
||
leftPanelObserver = null;
|
||
}
|
||
if (dockVerticalObserver) {
|
||
dockVerticalObserver.disconnect();
|
||
dockVerticalObserver = null;
|
||
}
|
||
if (layoutCenterObserver) {
|
||
layoutCenterObserver.disconnect();
|
||
layoutCenterObserver = null;
|
||
}
|
||
|
||
// 重置边距(参考旧版本实现)
|
||
document.querySelectorAll(".layout__center .layout-tab-bar--readonly").forEach(tabBar => {
|
||
tabBar.style.marginRight = "0px";
|
||
});
|
||
document.querySelectorAll(".layout__center .layout-tab-bar:not(.layout-tab-bar--readonly)").forEach(tabBar => {
|
||
tabBar.style.marginLeft = "0px";
|
||
});
|
||
|
||
// 清理全局变量
|
||
if (window.updateTabBarsMargin) {
|
||
window.updateTabBarsMargin = null;
|
||
}
|
||
if (window.updateTabBarsMarginLeft) {
|
||
window.updateTabBarsMarginLeft = null;
|
||
}
|
||
if (window._tabBarsResizeObserver) {
|
||
window._tabBarsResizeObserver.disconnect();
|
||
window._tabBarsResizeObserver = null;
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 初始化左右两侧的顶栏合并功能
|
||
*/
|
||
export const initTopbarMergeMargins = () => {
|
||
initTabBarsMarginUnified("right");
|
||
initTabBarsMarginUnified("left");
|
||
|
||
// layoutCenter observer 保留
|
||
const layoutCenter = document.querySelector(".layout__center");
|
||
if (layoutCenter) {
|
||
let marginUpdateTimer = null;
|
||
const resizeObserver = new MutationObserver(() => {
|
||
if (marginUpdateTimer) return;
|
||
marginUpdateTimer = requestAnimationFrame(() => {
|
||
window.updateTabBarsMargin?.();
|
||
window.updateTabBarsMarginLeft?.();
|
||
marginUpdateTimer = null;
|
||
});
|
||
});
|
||
resizeObserver.observe(layoutCenter, { childList: true, subtree: true });
|
||
layoutCenterObserver = resizeObserver;
|
||
window._tabBarsResizeObserver = resizeObserver;
|
||
}
|
||
};
|
||
|
||
// 初始化功能
|
||
initTopbarMergeMargins(); |