summaryrefslogtreecommitdiff
path: root/src/components/shared
diff options
context:
space:
mode:
authorHaishan <[email protected]>2022-03-06 17:23:14 +0800
committerGitHub <[email protected]>2022-03-06 17:23:14 +0800
commit8b2e4b6609deea56fdcafdf88172e577e5fadc38 (patch)
tree9eae0c4359bbdced9afef25f67de799dcb50ac92 /src/components/shared
parent3e87d8b52e62c6f8da48176c81267bacd36e3965 (diff)
parentd87cc00fc83bb2bc5c1ffa69b4dad71d51e1cc66 (diff)
Merge pull request #675 from haishanh/automatic-color-scheme
Diffstat (limited to 'src/components/shared')
-rw-r--r--src/components/shared/ThemeSwitcher.module.css28
-rw-r--r--src/components/shared/ThemeSwitcher.module.scss58
-rw-r--r--src/components/shared/ThemeSwitcher.tsx84
3 files changed, 120 insertions, 50 deletions
diff --git a/src/components/shared/ThemeSwitcher.module.css b/src/components/shared/ThemeSwitcher.module.css
deleted file mode 100644
index 919c86c..0000000
--- a/src/components/shared/ThemeSwitcher.module.css
+++ /dev/null
@@ -1,28 +0,0 @@
-.iconWrapper {
- --sz: 40px;
-
- width: var(--sz);
- height: var(--sz);
- display: flex;
- justify-content: center;
- align-items: center;
-
- outline: none;
- padding: 5px;
- color: var(--color-text);
- border-radius: 100%;
- border: 1px solid transparent;
-}
-.iconWrapper:hover {
- opacity: 0.6;
-}
-.iconWrapper:focus {
- border-color: var(--color-focus-blue);
-}
-
-.themeSwitchContainer {
- appearance: none;
- user-select: none;
- background: none;
- cursor: pointer;
-}
diff --git a/src/components/shared/ThemeSwitcher.module.scss b/src/components/shared/ThemeSwitcher.module.scss
new file mode 100644
index 0000000..c5de126
--- /dev/null
+++ b/src/components/shared/ThemeSwitcher.module.scss
@@ -0,0 +1,58 @@
+.iconWrapper {
+ --sz: 40px;
+
+ width: var(--sz);
+ height: var(--sz);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ outline: none;
+ padding: 5px;
+ color: var(--color-text);
+ border-radius: 100%;
+ border: 1px solid transparent;
+}
+.iconWrapper:hover {
+ opacity: 0.6;
+}
+.iconWrapper:focus {
+ border-color: var(--color-focus-blue);
+}
+
+.themeSwitchContainer {
+ --sz: 40px;
+
+ position: relative;
+ display: flex;
+ align-items: center;
+ height: var(--sz);
+ select {
+ cursor: pointer;
+ padding-left: var(--sz);
+ width: var(--sz);
+ height: var(--sz);
+ appearance: none;
+ outline: none;
+ border-radius: 100%;
+ border: 1px solid transparent;
+ background: var(--color-bg-sidebar);
+ &:focus {
+ border-color: var(--color-focus-blue);
+ }
+ option {
+ // this has effect in Firefox
+ // Chrome and Safari use the native menu
+ background: var(--color-bg-sidebar);
+ }
+ }
+ .iconWrapper {
+ pointer-events: none;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ left: 0;
+ top: 0;
+ }
+}
diff --git a/src/components/shared/ThemeSwitcher.tsx b/src/components/shared/ThemeSwitcher.tsx
index fba5b0b..45b60bc 100644
--- a/src/components/shared/ThemeSwitcher.tsx
+++ b/src/components/shared/ThemeSwitcher.tsx
@@ -1,5 +1,4 @@
import Tooltip from '@reach/tooltip';
-import cx from 'clsx';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { connect } from 'src/components/StateProvider';
@@ -7,28 +6,40 @@ import { framerMotionResouce } from 'src/misc/motion';
import { getTheme, switchTheme } from 'src/store/app';
import { State } from 'src/store/types';
-import s from './ThemeSwitcher.module.css';
+import s from './ThemeSwitcher.module.scss';
export function ThemeSwitcherImpl({ theme, dispatch }) {
const { t } = useTranslation();
- const switchThemeHooked = React.useCallback(() => {
- dispatch(switchTheme());
- }, [dispatch]);
+ const themeIcon = React.useMemo(() => {
+ switch (theme) {
+ case 'dark':
+ return <MoonA />;
+ case 'auto':
+ return <Auto />;
+ case 'light':
+ return <Sun />;
+ default:
+ console.assert(false, 'Unknown theme');
+ return <MoonA />;
+ }
+ }, [theme]);
+
+ const onChange = React.useCallback(
+ (e: React.ChangeEvent<HTMLSelectElement>) => dispatch(switchTheme(e.target.value)),
+ [dispatch]
+ );
return (
- <Tooltip
- label={t('theme')}
- aria-label={
- 'switch to ' + (theme === 'light' ? 'dark' : 'light') + ' theme'
- }
- >
- <button
- className={cx(s.iconWrapper, s.themeSwitchContainer)}
- onClick={switchThemeHooked}
- >
- {theme === 'light' ? <MoonA /> : <Sun />}
- </button>
+ <Tooltip label={t('switch_theme')} aria-label={'switch theme'}>
+ <div className={s.themeSwitchContainer}>
+ <span className={s.iconWrapper}>{themeIcon}</span>
+ <select onChange={onChange}>
+ <option value="auto">Auto</option>
+ <option value="dark">Dark</option>
+ <option value="light">Light</option>
+ </select>
+ </div>
</Tooltip>
);
}
@@ -75,11 +86,7 @@ function Sun() {
strokeLinejoin="round"
>
<circle cx="12" cy="12" r="5"></circle>
- <motion.g
- initial={{ scale: 0.8 }}
- animate={{ scale: 1 }}
- transition={{ duration: 0.7 }}
- >
+ <motion.g initial={{ scale: 0.7 }} animate={{ scale: 1 }} transition={{ duration: 0.5 }}>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
@@ -93,5 +100,38 @@ function Sun() {
);
}
+function Auto() {
+ const module = framerMotionResouce.read();
+ const motion = module.motion;
+
+ return (
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="20"
+ height="20"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ strokeWidth="2"
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ >
+ <circle cx="12" cy="12" r="11" />
+ <clipPath id="cut-off-bottom">
+ <motion.rect
+ x="12"
+ y="0"
+ width="12"
+ height="24"
+ initial={{ rotate: -30 }}
+ animate={{ rotate: 0 }}
+ transition={{ duration: 0.7 }}
+ />
+ </clipPath>
+ <circle cx="12" cy="12" r="6" clipPath="url(#cut-off-bottom)" fill="currentColor" />
+ </svg>
+ );
+}
+
const mapState = (s: State) => ({ theme: getTheme(s) });
export const ThemeSwitcher = connect(mapState)(ThemeSwitcherImpl);