feat(darkMode): allow switch to darkMode

This commit is contained in:
CLDXiang
2021-02-09 18:23:02 +08:00
parent d0bb377f19
commit 7329016de7
5 changed files with 47 additions and 3 deletions

View File

@@ -33,6 +33,14 @@
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Qwerty Learner</title>
<script>
// Dark mode init
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -11,6 +11,7 @@ import {
faCoffee,
faMicrophone,
faMicrophoneSlash,
faMoon,
} from '@fortawesome/free-solid-svg-icons'
import { faGithub } from '@fortawesome/free-brands-svg-icons'
@@ -27,4 +28,5 @@ library.add(
faCoffee,
faMicrophone,
faMicrophoneSlash,
faMoon,
)

View File

@@ -78,6 +78,20 @@ const Switcher: React.FC<SwitcherPropsType> = ({ state, dispatch }) => {
<span className="py-1 px-3 text-gray-500 text-xs">Ctrl + P</span>
</div>
</div>
<div className="group relative">
<button
className={`${state.darkMode ? 'text-indigo-400' : 'text-gray-400'} text-lg focus:outline-none`}
onClick={(e) => {
dispatch('darkMode')
e.currentTarget.blur()
}}
>
<FontAwesomeIcon icon="moon" fixedWidth />
</button>
<div className="invisible group-hover:visible absolute top-full left-1/2 w-44 -ml-20 pt-2 flex items-center justify-center">
<span className="py-1 px-3 text-gray-500 text-xs"></span>
</div>
</div>
</div>
)
}

View File

@@ -5,14 +5,23 @@ export type SwitcherStateType = {
phonetic: boolean
wordVisible: boolean
sound: boolean
darkMode: boolean
}
export type SwitcherDispatchType = (type: string, newStatus?: boolean) => void
const useSwitcherState = (initialState: { phonetic: boolean; wordVisible: boolean }): [SwitcherStateType, SwitcherDispatchType] => {
const useSwitcherState = (initialState: {
phonetic: boolean
wordVisible: boolean
darkMode?: boolean
}): [SwitcherStateType, SwitcherDispatchType] => {
const [phonetic, setPhonetic] = useState(initialState.phonetic)
const [wordVisible, setWordVisible] = useState(initialState.wordVisible)
const [sound, setSound] = useSetSoundState()
const [darkMode, setDarkMode] = useState(
initialState.darkMode ??
(localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)),
)
const dispatch: SwitcherDispatchType = (type, newStatus) => {
switch (type) {
@@ -25,10 +34,21 @@ const useSwitcherState = (initialState: { phonetic: boolean; wordVisible: boolea
case 'sound':
newStatus === undefined ? setSound(!sound) : setSound(newStatus)
break
case 'darkMode':
const newDarkMode = newStatus ?? !darkMode
setDarkMode(newDarkMode)
if (newDarkMode) {
localStorage.theme = 'dark'
document.documentElement.classList.add('dark')
} else {
localStorage.theme = 'light'
document.documentElement.classList.remove('dark')
}
break
}
}
return [{ phonetic, wordVisible, sound }, dispatch]
return [{ phonetic, wordVisible, sound, darkMode }, dispatch]
}
export default useSwitcherState

View File

@@ -1,6 +1,6 @@
module.exports = {
purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
darkMode: false, // or 'media' or 'class'
darkMode: 'class',
theme: {
extend: {},
borderRadius: {