|
@@ -0,0 +1,243 @@
|
|
|
+/**
|
|
|
+ * 侧边栏管理类
|
|
|
+ */
|
|
|
+class SidebarManager {
|
|
|
+ /**
|
|
|
+ * @constant {Object} 配置常量
|
|
|
+ */
|
|
|
+ static CONFIG = {
|
|
|
+ SIDEBAR_ID: "paiwise-sidebar",
|
|
|
+ WRAPPER_ID: "paiwise-main-wrapper",
|
|
|
+ STORAGE_KEY: "sidebarOpen",
|
|
|
+ };
|
|
|
+
|
|
|
+ constructor() {
|
|
|
+ this.sidebarId = SidebarManager.CONFIG.SIDEBAR_ID;
|
|
|
+ this.wrapperId = SidebarManager.CONFIG.WRAPPER_ID;
|
|
|
+ this.isOpen = false;
|
|
|
+ this.init();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化侧边栏
|
|
|
+ */
|
|
|
+ async init() {
|
|
|
+ try {
|
|
|
+ await this.checkAndRestoreState();
|
|
|
+ this.setupEventListeners();
|
|
|
+ } catch (error) {
|
|
|
+ console.error("Sidebar initialization failed:", error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置事件监听器
|
|
|
+ */
|
|
|
+ setupEventListeners() {
|
|
|
+ // 监听页面加载完成事件
|
|
|
+ window.addEventListener("load", () => this.checkAndRestoreState());
|
|
|
+
|
|
|
+ // 监听来自 sidebar 的消息
|
|
|
+ window.addEventListener("message", this.handleMessage.bind(this));
|
|
|
+
|
|
|
+ // 监听窗口大小变化
|
|
|
+ window.addEventListener(
|
|
|
+ "resize",
|
|
|
+ Utils.debounce(() => this.handleResize(), 100)
|
|
|
+ );
|
|
|
+
|
|
|
+ // 监听页面可见性变化
|
|
|
+ document.addEventListener("visibilitychange", () => {
|
|
|
+ if (!document.hidden) {
|
|
|
+ this.checkAndRestoreState();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监听 history 变化
|
|
|
+ window.addEventListener("popstate", () => {
|
|
|
+ this.checkAndRestoreState();
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监听页面 URL 变化
|
|
|
+ let lastUrl = location.href;
|
|
|
+ new MutationObserver(() => {
|
|
|
+ const url = location.href;
|
|
|
+ if (url !== lastUrl) {
|
|
|
+ lastUrl = url;
|
|
|
+ this.checkAndRestoreState();
|
|
|
+ }
|
|
|
+ }).observe(document, { subtree: true, childList: true });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理接收到的消息
|
|
|
+ * @param {MessageEvent} event
|
|
|
+ */
|
|
|
+ handleMessage(event) {
|
|
|
+ if (event.data.action === "closeSidebar") {
|
|
|
+ this.removeSidebar();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理窗口大小变化
|
|
|
+ */
|
|
|
+ handleResize() {
|
|
|
+ const sidebar = document.getElementById(this.sidebarId);
|
|
|
+ if (sidebar && this.isOpen) {
|
|
|
+ sidebar.style.height = `${window.innerHeight}px`;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查并恢复侧边栏状态
|
|
|
+ */
|
|
|
+ async checkAndRestoreState() {
|
|
|
+ try {
|
|
|
+ const isOpen = await Utils.getStorageData(
|
|
|
+ SidebarManager.CONFIG.STORAGE_KEY
|
|
|
+ );
|
|
|
+ if (isOpen && !this.isOpen) {
|
|
|
+ // 只有当应该打开且当前未打开时才创建
|
|
|
+ this.createSidebar();
|
|
|
+ } else if (!isOpen && this.isOpen) {
|
|
|
+ // 只有当应该关闭且当前打开时才移除
|
|
|
+ this.removeSidebar();
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("Failed to restore sidebar state:", error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 包装页面内容
|
|
|
+ */
|
|
|
+ wrapPageContent() {
|
|
|
+ if (document.getElementById(this.wrapperId)) return;
|
|
|
+
|
|
|
+ const wrapper = document.createElement("div");
|
|
|
+ wrapper.id = this.wrapperId;
|
|
|
+
|
|
|
+ // 添加初始样式
|
|
|
+ wrapper.style.width = "100%";
|
|
|
+
|
|
|
+ while (document.body.firstChild) {
|
|
|
+ if (document.body.firstChild.id !== this.sidebarId) {
|
|
|
+ wrapper.appendChild(document.body.firstChild);
|
|
|
+ } else {
|
|
|
+ document.body.removeChild(document.body.firstChild);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ document.body.appendChild(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解除页面内容包装
|
|
|
+ */
|
|
|
+ unwrapPageContent() {
|
|
|
+ const wrapper = document.getElementById(this.wrapperId);
|
|
|
+ if (!wrapper) return;
|
|
|
+
|
|
|
+ while (wrapper.firstChild) {
|
|
|
+ document.body.insertBefore(wrapper.firstChild, wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ wrapper.remove();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建侧边栏
|
|
|
+ */
|
|
|
+ async createSidebar() {
|
|
|
+ if (document.getElementById(this.sidebarId)) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ this.wrapPageContent();
|
|
|
+
|
|
|
+ const iframe = document.createElement("iframe");
|
|
|
+ iframe.id = this.sidebarId;
|
|
|
+ iframe.src = chrome.runtime.getURL("sidebar.html");
|
|
|
+ document.body.appendChild(iframe);
|
|
|
+
|
|
|
+ // 使用 RAF 确保 DOM 更新后再添加动画类
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ const wrapper = document.getElementById(this.wrapperId);
|
|
|
+ const sidebar = document.getElementById(this.sidebarId);
|
|
|
+
|
|
|
+ // 先触发重排
|
|
|
+ sidebar.getBoundingClientRect();
|
|
|
+
|
|
|
+ // 添加动画类
|
|
|
+ if (wrapper) wrapper.classList.add("sidebar-open");
|
|
|
+ if (sidebar) sidebar.classList.add("show");
|
|
|
+ });
|
|
|
+
|
|
|
+ this.isOpen = true;
|
|
|
+ await Utils.setStorageData(SidebarManager.CONFIG.STORAGE_KEY, true);
|
|
|
+ } catch (error) {
|
|
|
+ console.error("Failed to create sidebar:", error);
|
|
|
+ this.unwrapPageContent();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 移除侧边栏
|
|
|
+ */
|
|
|
+ async removeSidebar() {
|
|
|
+ if (!this.isOpen) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const sidebar = document.getElementById(this.sidebarId);
|
|
|
+ const wrapper = document.getElementById(this.wrapperId);
|
|
|
+
|
|
|
+ if (sidebar && wrapper) {
|
|
|
+ // 移除动画类
|
|
|
+ wrapper.classList.remove("sidebar-open");
|
|
|
+ sidebar.classList.remove("show");
|
|
|
+
|
|
|
+ // 等待动画完成后再移除元素
|
|
|
+ await new Promise((resolve) => {
|
|
|
+ sidebar.addEventListener(
|
|
|
+ "transitionend",
|
|
|
+ () => {
|
|
|
+ this.unwrapPageContent();
|
|
|
+ sidebar.remove();
|
|
|
+ resolve();
|
|
|
+ },
|
|
|
+ { once: true }
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ this.isOpen = false;
|
|
|
+ await Utils.setStorageData(SidebarManager.CONFIG.STORAGE_KEY, false);
|
|
|
+ } catch (error) {
|
|
|
+ console.error("Failed to remove sidebar:", error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 切换侧边栏显示状态
|
|
|
+ */
|
|
|
+ toggle() {
|
|
|
+ if (this.isOpen) {
|
|
|
+ this.removeSidebar();
|
|
|
+ } else {
|
|
|
+ this.createSidebar();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化侧边栏管理器
|
|
|
+const sidebarManager = new SidebarManager();
|
|
|
+
|
|
|
+// 监听来自背景脚本的消息
|
|
|
+chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
+ if (message.action === "toggleSidebar") {
|
|
|
+ sidebarManager.toggle();
|
|
|
+ sendResponse({ success: true });
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+// 创建一个新的js文件用于处理侧边栏内部的关闭按钮事件
|