Commit 1a6cd77

HPCesia <me@hpcesia.com>
2025-04-04 13:13:52
refactor: darkmode button
1 parent 3071821
Changed files (1)
src
components
src/components/widgets/DarkModeButton.astro
@@ -33,64 +33,93 @@ const { class: className, showText, ...rest } = Astro.props;
   function setup() {
     const darkmodeBtns = document.querySelectorAll('button.darkmode-btn');
 
-    darkmodeBtns.forEach((btn) => {
-      const checkbox = btn.querySelector('input[type="checkbox"]') as HTMLInputElement;
-      const text = btn.querySelector('.darkmode-text');
+    // 获取当前主题模式
+    function getCurrentMode(): 'system' | 'light' | 'dark' {
+      if (!('darkMode' in localStorage)) {
+        return 'system';
+      }
+      return localStorage.darkMode === 'true' ? 'dark' : 'light';
+    }
 
-      // 初始化状态
-      if ('darkMode' in localStorage) {
-        checkbox.checked = localStorage.darkMode === 'true';
-        checkbox.indeterminate = false;
-      } else {
-        checkbox.indeterminate = true;
+    // 获取下一个主题模式
+    function getNextMode(
+      currentMode: 'system' | 'light' | 'dark'
+    ): 'system' | 'light' | 'dark' {
+      switch (currentMode) {
+        case 'system':
+          return 'light';
+        case 'light':
+          return 'dark';
+        case 'dark':
+          return 'system';
       }
+    }
 
-      // 更新文本和标题
-      function updateText() {
-        if (!text) return;
+    darkmodeBtns.forEach((btn) => {
+      const checkbox = btn.querySelector('input[type="checkbox"]') as HTMLInputElement;
+      const text = btn.querySelector('.darkmode-text');
 
-        const textContent = checkbox.indeterminate
-          ? btn.getAttribute('data-text-auto')
-          : checkbox.checked
-            ? btn.getAttribute('data-text-dark')
-            : btn.getAttribute('data-text-light');
+      // 更新UI状态和文本
+      function updateUI(mode: 'system' | 'light' | 'dark') {
+        if (mode === 'system') {
+          checkbox.indeterminate = true;
+        } else {
+          checkbox.indeterminate = false;
+          checkbox.checked = mode === 'dark';
+        }
 
-        text.textContent = textContent;
-        btn.setAttribute('title', textContent || '');
-      }
+        if (text) {
+          const textContent =
+            mode === 'system'
+              ? btn.getAttribute('data-text-auto')
+              : mode === 'dark'
+                ? btn.getAttribute('data-text-dark')
+                : btn.getAttribute('data-text-light');
 
-      // 更新主题
-      function updateTheme() {
-        if (checkbox.indeterminate) {
-          localStorage.removeItem('darkMode');
-          const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
-          document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
-        } else {
-          localStorage.darkMode = checkbox.checked;
-          document.documentElement.setAttribute(
-            'data-theme',
-            checkbox.checked ? 'dark' : 'light'
-          );
+          text.textContent = textContent;
+          btn.setAttribute('title', textContent || '');
         }
       }
 
+      // 点击处理
       btn.addEventListener('click', () => {
-        if (checkbox.indeterminate) {
-          checkbox.indeterminate = false;
-          checkbox.checked = false;
-        } else if (!checkbox.checked) {
-          checkbox.checked = true;
+        const currentMode = getCurrentMode();
+        const nextMode = getNextMode(currentMode);
+
+        // 更新 localStorage
+        if (nextMode === 'system') {
+          localStorage.removeItem('darkMode');
         } else {
-          checkbox.checked = false;
-          checkbox.indeterminate = true;
+          localStorage.darkMode = nextMode === 'dark';
         }
 
-        updateText();
-        updateTheme();
+        // 触发主题变化事件
+        const isDark =
+          nextMode === 'system'
+            ? window.matchMedia('(prefers-color-scheme: dark)').matches
+            : nextMode === 'dark';
+
+        document.dispatchEvent(
+          new CustomEvent('blog:darkmode-change', {
+            detail: { isDark, nextMode },
+          })
+        );
+      });
+
+      document.addEventListener('blog:darkmode-change', (e) => {
+        // @ts-expect-error CustomEvent.detail is not defined in TypeScript
+        const { nextMode } = e.detail;
+        updateUI(nextMode);
       });
 
-      updateText();
-      updateTheme();
+      // 初始化 UI
+      updateUI(getCurrentMode());
+    });
+
+    document.addEventListener('blog:darkmode-change', (e) => {
+      // @ts-expect-error CustomEvent.detail is not defined in TypeScript
+      const { isDark } = e.detail;
+      document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
     });
   }