diff options
Diffstat (limited to 'src/components/shared/ThemeSwitcher.tsx')
| -rw-r--r-- | src/components/shared/ThemeSwitcher.tsx | 84 |
1 files changed, 62 insertions, 22 deletions
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); |
