123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- /**
- * 侧边栏管理类
- */
- 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文件用于处理侧边栏内部的关闭按钮事件
|