@@ -24,6 +24,19 @@ declare global {
2424 }
2525}
2626
27+ type DocumentTheme = 'dark-mode' | 'light-mode' ;
28+ export type Theme = DocumentTheme | 'system' ;
29+
30+ const kDefaultTheme : Theme = 'system' ;
31+ const kThemeSettingsKey = 'theme' ;
32+ export const kThemeOptions : { label : string ; value : Theme } [ ] = [
33+ { label : 'Dark mode' , value : 'dark-mode' } ,
34+ { label : 'Light mode' , value : 'light-mode' } ,
35+ { label : 'System' , value : 'system' } ,
36+ ] as const satisfies { label : string ; value : Theme } [ ] ;
37+
38+ const prefersDarkScheme = window . matchMedia ( '(prefers-color-scheme: dark)' ) ;
39+
2740export function applyTheme ( ) {
2841 if ( document . playwrightThemeInitialized )
2942 return ;
@@ -36,49 +49,57 @@ export function applyTheme() {
3649 document . body . classList . add ( 'inactive' ) ;
3750 } , false ) ;
3851
39- const prefersDarkScheme = window . matchMedia ( '(prefers-color-scheme: dark)' ) ;
40- const defaultTheme = prefersDarkScheme . matches ? 'dark-mode' : 'light-mode' ;
52+ updateDocumentTheme ( currentTheme ( ) ) ;
4153
42- const currentTheme = settings . getString ( 'theme' , defaultTheme ) ;
43- if ( currentTheme === 'dark-mode' )
44- document . documentElement . classList . add ( 'dark-mode' ) ;
45- else
46- document . documentElement . classList . add ( 'light-mode' ) ;
54+ prefersDarkScheme . addEventListener ( 'change' , ( ) => {
55+ updateDocumentTheme ( currentTheme ( ) ) ;
56+ } ) ;
4757}
4858
49- type Theme = 'dark-mode' | 'light-mode' ;
59+ const listeners = new Set < ( theme : DocumentTheme ) => void > ( ) ;
60+ function updateDocumentTheme ( newTheme : Theme ) {
61+ const oldDocumentTheme = currentDocumentTheme ( ) ;
62+ const newDocumentTheme = newTheme === 'system'
63+ ? ( prefersDarkScheme . matches ? 'dark-mode' : 'light-mode' )
64+ : newTheme ;
5065
51- const listeners = new Set < ( theme : Theme ) => void > ( ) ;
52- export function toggleTheme ( ) {
53- const oldTheme = currentTheme ( ) ;
54- const newTheme = oldTheme === 'dark-mode' ? 'light-mode' : 'dark-mode' ;
66+ if ( oldDocumentTheme === newDocumentTheme )
67+ return ;
5568
56- if ( oldTheme )
57- document . documentElement . classList . remove ( oldTheme ) ;
58- document . documentElement . classList . add ( newTheme ) ;
59- settings . setString ( 'theme' , newTheme ) ;
69+ if ( oldDocumentTheme )
70+ document . documentElement . classList . remove ( oldDocumentTheme ) ;
71+ document . documentElement . classList . add ( newDocumentTheme ) ;
6072 for ( const listener of listeners )
61- listener ( newTheme ) ;
73+ listener ( newDocumentTheme ) ;
6274}
6375
64- export function addThemeListener ( listener : ( theme : 'light-mode' | 'dark-mode' ) => void ) {
76+ export function addThemeListener ( listener : ( theme : DocumentTheme ) => void ) {
6577 listeners . add ( listener ) ;
6678}
6779
68- export function removeThemeListener ( listener : ( theme : Theme ) => void ) {
80+ export function removeThemeListener ( listener : ( theme : DocumentTheme ) => void ) {
6981 listeners . delete ( listener ) ;
7082}
7183
72- export function currentTheme ( ) : Theme {
73- return document . documentElement . classList . contains ( 'dark-mode' ) ? 'dark-mode' : 'light-mode' ;
84+ function currentTheme ( ) : Theme {
85+ return settings . getString ( kThemeSettingsKey , kDefaultTheme ) ;
7486}
7587
76- export function useDarkModeSetting ( ) : [ boolean , ( value : boolean ) => void ] {
77- const [ theme , setTheme ] = React . useState ( currentTheme ( ) === 'dark-mode' ) ;
78- return [ theme , ( value : boolean ) => {
79- const current = currentTheme ( ) === 'dark-mode' ;
80- if ( current !== value )
81- toggleTheme ( ) ;
82- setTheme ( value ) ;
83- } ] ;
88+ export function currentDocumentTheme ( ) : DocumentTheme | null {
89+ if ( document . documentElement . classList . contains ( 'dark-mode' ) )
90+ return 'dark-mode' ;
91+ if ( document . documentElement . classList . contains ( 'light-mode' ) )
92+ return 'light-mode' ;
93+ return null ;
94+ }
95+
96+ export function useThemeSetting ( ) : [ Theme , ( value : Theme ) => void ] {
97+ const [ theme , setTheme ] = React . useState < Theme > ( currentTheme ( ) ) ;
98+
99+ React . useEffect ( ( ) => {
100+ settings . setString ( kThemeSettingsKey , theme ) ;
101+ updateDocumentTheme ( theme ) ;
102+ } , [ theme ] ) ;
103+
104+ return [ theme , setTheme ] ;
84105}
0 commit comments