Selaa lähdekoodia

impl switch for dark mode

Yuki Takei 6 vuotta sitten
vanhempi
sitoutus
9d45fa415b

+ 60 - 15
src/client/js/components/Navbar/PersonalDropdown.jsx

@@ -25,6 +25,34 @@ const PersonalDropdown = (props) => {
     window.location.href = '/logout';
     window.location.href = '/logout';
   };
   };
 
 
+  const followOsCheckboxModifiedHandler = (bool) => {
+    // reset user preference
+    if (bool) {
+      appContainer.setColorSchemePreference(null);
+    }
+    // set preferDarkModeByMediaQuery as users preference
+    else {
+      appContainer.setColorSchemePreference(appContainer.state.preferDarkModeByMediaQuery);
+    }
+  };
+
+  const userPreferenceSwitchModifiedHandler = (bool) => {
+    appContainer.setColorSchemePreference(bool);
+  };
+
+
+  /*
+   * render
+   */
+  const { preferDarkModeByMediaQuery, preferDarkModeByUser } = appContainer.state;
+  const isUserPreferenceExists = preferDarkModeByUser != null;
+  const isDarkMode = () => {
+    if (isUserPreferenceExists) {
+      return preferDarkModeByUser;
+    }
+    return preferDarkModeByMediaQuery;
+  };
+
   return (
   return (
     <>
     <>
       {/* Button */}
       {/* Button */}
@@ -45,22 +73,39 @@ const PersonalDropdown = (props) => {
 
 
         <div className="dropdown-divider"></div>
         <div className="dropdown-divider"></div>
 
 
-        <h6 className="dropdown-header">Switching Mode</h6>
-        <form className="px-4 form-inline">
-          Light
-          <div className="custom-control custom-switch custom-checkbox-secondary ml-2">
-            <input
-              id="isDarkMode"
-              className="custom-control-input"
-              type="checkbox"
-              checked={appContainer.state.isDarkMode}
-              onChange={(e) => {
-                appContainer.changeColorScheme(e.target.checked);
-              }}
-            />
-            <label className="custom-control-label" htmlFor="isDarkMode"></label>
+        <h6 className="dropdown-header">Color Scheme</h6>
+        <form className="px-4">
+          <div className="form-row align-items-center">
+            <div className="col-auto">
+              <div className="custom-control custom-checkbox">
+                <input
+                  id="cbFollowOs"
+                  className="custom-control-input"
+                  type="checkbox"
+                  checked={!isUserPreferenceExists}
+                  onChange={e => followOsCheckboxModifiedHandler(e.target.checked)}
+                />
+                <label className="custom-control-label" htmlFor="cbFollowOs">Use OS Setting</label>
+              </div>
+            </div>
+          </div>
+          <div className="form-row align-items-center">
+            <div className="col-auto d-flex">
+              <span className={isUserPreferenceExists ? '' : 'text-muted'}>Light</span>
+              <div className="custom-control custom-switch custom-checkbox-secondary ml-2">
+                <input
+                  id="swUserPreference"
+                  className="custom-control-input"
+                  type="checkbox"
+                  checked={isDarkMode()}
+                  disabled={!isUserPreferenceExists}
+                  onChange={e => userPreferenceSwitchModifiedHandler(e.target.checked)}
+                />
+                <label className="custom-control-label" htmlFor="swUserPreference"></label>
+              </div>
+              <span className={isUserPreferenceExists ? '' : 'text-muted'}>Dark</span>
+            </div>
           </div>
           </div>
-          Dark
         </form>
         </form>
 
 
         <div className="dropdown-divider"></div>
         <div className="dropdown-divider"></div>

+ 55 - 18
src/client/js/services/AppContainer.js

@@ -31,7 +31,8 @@ export default class AppContainer extends Container {
 
 
     this.state = {
     this.state = {
       editorMode: null,
       editorMode: null,
-      isDarkMode: false,
+      preferDarkModeByMediaQuery: false,
+      preferDarkModeByUser: null,
     };
     };
 
 
     const body = document.querySelector('body');
     const body = document.querySelector('body');
@@ -103,25 +104,26 @@ export default class AppContainer extends Container {
     this.initPlugins();
     this.initPlugins();
   }
   }
 
 
-  initColorScheme() {
-    function switchScheme(mql) {
-      // switch to dark mode
-      if (mql.matches) {
-        document.documentElement.setAttribute('dark', 'true');
-      }
-      // switch to light mode
-      else {
-        document.documentElement.removeAttribute('dark');
-      }
-    }
+  async initColorScheme() {
+    const switchStateByMediaQuery = (mql) => {
+      const preferDarkMode = mql.matches;
+      this.setState({ preferDarkModeByMediaQuery: preferDarkMode });
+
+      this.applyColorScheme();
+    };
 
 
     const mqlForDarkMode = window.matchMedia('(prefers-color-scheme: dark)');
     const mqlForDarkMode = window.matchMedia('(prefers-color-scheme: dark)');
+    // add event listener
+    mqlForDarkMode.addListener(switchStateByMediaQuery);
 
 
-    // initialize
-    switchScheme(mqlForDarkMode);
+    // restore settings from localStorage
+    const { localStorage } = window;
+    if (localStorage.preferDarkModeByUser != null) {
+      await this.setState({ preferDarkModeByUser: localStorage.preferDarkModeByUser === 'true' });
+    }
 
 
-    // add event listener
-    mqlForDarkMode.addListener(switchScheme);
+    // initialize
+    switchStateByMediaQuery(mqlForDarkMode);
   }
   }
 
 
   initPlugins() {
   initPlugins() {
@@ -341,8 +343,43 @@ export default class AppContainer extends Container {
     targetComponent.launchDrawioModal(beginLineNumber, endLineNumber);
     targetComponent.launchDrawioModal(beginLineNumber, endLineNumber);
   }
   }
 
 
-  changeColorScheme(isDarkMode) {
-    this.setState({ isDarkMode });
+  /**
+   * Set color scheme preference by user
+   * @param {boolean} isDarkMode
+   */
+  async setColorSchemePreference(isDarkMode) {
+    await this.setState({ preferDarkModeByUser: isDarkMode });
+
+    // store settings to localStorage
+    const { localStorage } = window;
+    if (isDarkMode == null) {
+      delete localStorage.removeItem('preferDarkModeByUser');
+    }
+    else {
+      localStorage.preferDarkModeByUser = isDarkMode;
+    }
+
+    this.applyColorScheme();
+  }
+
+  /**
+   * Apply color scheme as 'dark' attribute of <html></html>
+   */
+  applyColorScheme() {
+    const { preferDarkModeByMediaQuery, preferDarkModeByUser } = this.state;
+    let isDarkMode = preferDarkModeByMediaQuery;
+    if (preferDarkModeByUser != null) {
+      isDarkMode = preferDarkModeByUser;
+    }
+
+    // switch to dark mode
+    if (isDarkMode) {
+      document.documentElement.setAttribute('dark', 'true');
+    }
+    // switch to light mode
+    else {
+      document.documentElement.removeAttribute('dark');
+    }
   }
   }
 
 
   async apiGet(path, params) {
   async apiGet(path, params) {