Przeglądaj źródła

Merge pull request #8338 from weseek/feat/137151-user-settings-light-dark-mode-toggle

feat: Enable toggling between light and dark modes from user settings
Shun Miyazawa 2 lat temu
rodzic
commit
875f6537e3

+ 7 - 0
apps/app/public/static/locales/en_US/translation.json

@@ -257,6 +257,13 @@
       "description": "You can set whether or not the sidebar will always be open when the screen width is large. If the screen width is small, the sidebar will always be closed."
     }
   },
+  "color_mode_settings": {
+    "light": "Light",
+    "dark": "Dark",
+    "system": "System",
+    "settings": "Color mode settings",
+    "description": "Select whether to display in light mode, dark mode, or a system-specific display.<br>Only supported themes can be switched."
+  },
   "editor_settings": {
     "editor_settings": "Editor Settings"
   },

+ 7 - 0
apps/app/public/static/locales/ja_JP/translation.json

@@ -258,6 +258,13 @@
       "description": "画面幅が大きい場合に、サイドバーを常時開いた状態にするかどうかを設定できます。画面幅が小さい場合はサイドバーは常に閉じた状態となります。"
     }
   },
+  "color_mode_settings": {
+    "light": "ライト",
+    "dark": "ダーク",
+    "system": "システム",
+    "settings": "カラーモードの設定",
+    "description": "ライトモードかダークモード、もしくはシステム合わせた表示をするか選択します。<br>対応したテーマのみ切り替えることができます。"
+  },
   "editor_settings": {
     "editor_settings": "エディター設定",
     "common_settings": {

+ 7 - 0
apps/app/public/static/locales/zh_CN/translation.json

@@ -248,6 +248,13 @@
       "description": "您可以设置当屏幕宽度较大时,侧边栏是否始终打开。 如果屏幕宽度较小,侧边栏将始终关闭。"
     }
   },
+  "color_mode_settings": {
+    "light": "灯光",
+    "dark": "暗处",
+    "system": "系统",
+    "settings": "色彩模式设置",
+    "description": "选择是以浅色模式、深色模式还是系统特定的显示方式显示。<br>只能切换支持的主题。"
+  },
   "editor_settings": {
     "editor_settings": "编辑器设置"
   },

+ 15 - 0
apps/app/src/components/Me/ColorModeSettings.module.scss

@@ -0,0 +1,15 @@
+@use '@growi/core/scss/bootstrap/init' as *;
+
+.color-settings :global {
+  .btn {
+    font-weight: bold;
+    color: var(--color-global);
+    background-color: transparent;
+    border-width: 3px;
+  }
+
+  .btn-outline-secondary {
+    border-color: $gray-400;
+  }
+}
+

+ 62 - 0
apps/app/src/components/Me/ColorModeSettings.tsx

@@ -0,0 +1,62 @@
+import React, { useCallback } from 'react';
+
+import { useTranslation } from 'react-i18next';
+
+import { Themes, useNextThemes } from '~/stores/use-next-themes';
+
+import styles from './ColorModeSettings.module.scss';
+
+export const ColorModeSettings = (): JSX.Element => {
+  const { t } = useTranslation();
+
+  const { setTheme, theme } = useNextThemes();
+
+  const isActive = useCallback((targetTheme: Themes) => {
+    return targetTheme === theme;
+  }, [theme]);
+
+  return (
+    <div className={`color-settings ${styles['color-settings']}`}>
+      <h2 className="border-bottom mb-4">{t('color_mode_settings.settings')}</h2>
+
+      <div className="offset-md-3">
+        <div className="d-flex">
+          <button
+            type="button"
+            onClick={() => { setTheme(Themes.LIGHT) }}
+            // eslint-disable-next-line max-len
+            className={`btn py-2 px-4 me-4 d-flex align-items-center justify-content-center ${isActive(Themes.LIGHT) ? 'btn-outline-primary' : 'btn-outline-secondary'}`}
+          >
+            <span className="material-symbols-outlined fs-5 me-1">light_mode</span>
+            <span>{t('color_mode_settings.light')}</span>
+          </button>
+
+          <button
+            type="button"
+            onClick={() => { setTheme(Themes.DARK) }}
+            // eslint-disable-next-line max-len
+            className={`btn py-2 px-4 me-4 d-flex align-items-center justify-content-center ${isActive(Themes.DARK) ? 'btn-outline-primary' : 'btn-outline-secondary'}`}
+          >
+            <span className="material-symbols-outlined fs-5 me-1">dark_mode</span>
+            <span>{t('color_mode_settings.dark')}</span>
+          </button>
+
+          <button
+            type="button"
+            onClick={() => { setTheme(Themes.SYSTEM) }}
+            // eslint-disable-next-line max-len
+            className={`btn py-2 px-4 d-flex align-items-center justify-content-center ${isActive(Themes.SYSTEM) ? 'btn-outline-primary' : 'btn-outline-secondary'}`}
+          >
+            <span className="material-symbols-outlined fs-5 me-1">devices</span>
+            <span>{t('color_mode_settings.system')}</span>
+          </button>
+        </div>
+
+        <div className="mt-3 text-muted">
+          {/* eslint-disable-next-line react/no-danger */}
+          <span dangerouslySetInnerHTML={{ __html: t('color_mode_settings.description') }} />
+        </div>
+      </div>
+    </div>
+  );
+};

+ 6 - 1
apps/app/src/components/Me/OtherSettings.tsx

@@ -1,3 +1,4 @@
+import { ColorModeSettings } from './ColorModeSettings';
 import { QuestionnaireSettings } from './QuestionnaireSettings';
 import { UISettings } from './UISettings';
 
@@ -6,12 +7,16 @@ const OtherSettings = (): JSX.Element => {
   return (
     <>
       <div className="mt-4">
-        <QuestionnaireSettings />
+        <ColorModeSettings />
       </div>
 
       <div className="mt-4">
         <UISettings />
       </div>
+
+      <div className="mt-4">
+        <QuestionnaireSettings />
+      </div>
     </>
   );
 };