11import { defaultColors , type Colors } from "./colors"
22import { defaultIcons , type Icons } from "./icons"
3- import { createSubscriber , MediaQuery } from "svelte/reactivity"
3+ import { MediaQuery } from "svelte/reactivity"
44import { on } from "svelte/events"
55
66export type Theme = 'light' | 'dark' | 'system'
77
88export const themes : Theme [ ] = [ 'light' , 'dark' , 'system' ]
99
1010export interface Labels {
11- light : string
12- dark : string
13- system : string
11+ light : string
12+ dark : string
13+ system : string
1414}
1515
1616export const defaultLabels = {
17- light : 'Light' ,
18- dark : 'Dark' ,
19- system : 'System' ,
17+ light : 'Light' ,
18+ dark : 'Dark' ,
19+ system : 'System' ,
2020}
2121
2222export interface Config {
23- key : string
24- colors : Colors
25- icons : Icons
26- labels : Labels
23+ key : string
24+ colors : Colors
25+ icons : Icons
26+ labels : Labels
2727}
2828
2929class ThemeState {
30+ #mq = new MediaQuery ( '(prefers-color-scheme: dark)' )
31+ #system = $derived < Theme > ( this . #mq. current ? 'dark' : 'light' )
3032 #override = $state < Theme > ( 'system' )
31- #system: Theme
32- #value: Theme
33- #subscribe: VoidFunction
34- #update ?: VoidFunction
33+ #value = $derived ( this . #override === ' system' ? this . #system : this . #override )
34+
35+ #subscribers = 0
36+ #off ?: VoidFunction
3537
3638 colors = defaultColors
3739 icons = defaultIcons
3840 labels = defaultLabels
3941
40- constructor ( ) {
41- const prefersDark = new MediaQuery ( '(prefers-color-scheme: dark)' )
42- this . #system = $derived ( prefersDark . current ? 'dark' : 'light' )
43- this . #value = $derived ( this . #override === 'system' ? this . #system : this . #override)
44-
45- this . #subscribe = createSubscriber ( update => {
46- this . #update = update
47- const saved : Theme = localStorage . theme ?? 'system'
48- this . #override = saved
49- update ( )
50-
51- return on ( window , 'storage' , ( event : StorageEvent ) => {
52- if ( event . key === 'theme' ) {
53- this . #override = event . newValue as Theme
54- update ( )
42+ private subscribe ( ) {
43+ if ( $effect . tracking ( ) ) {
44+ $effect ( ( ) => {
45+ if ( this . #subscribers === 0 ) {
46+ const saved : Theme = localStorage . theme ?? 'system'
47+ this . #override = saved
48+
49+ this . #off = on ( window , 'storage' , ( event : StorageEvent ) => {
50+ if ( event . key === 'theme' ) {
51+ this . #override = event . newValue as Theme
52+ }
53+ } )
54+
55+ $effect ( ( ) => {
56+ document . documentElement . classList . toggle ( 'dark' , this . #value === 'dark' )
57+ } )
5558 }
56- } )
57- } )
5859
59- $effect . root ( ( ) => {
60- $effect . pre ( ( ) => {
61- document . documentElement . classList . toggle ( 'dark' , this . #value === 'dark' )
60+ this . #subscribers++
61+
62+ return ( ) => {
63+ this . #subscribers--
64+ if ( this . #subscribers === 0 ) {
65+ this . #off?.( )
66+ this . #off = undefined
67+ }
68+ }
6269 } )
63- } )
70+ }
6471 }
6572
6673 get system ( ) {
74+ this . subscribe ( )
6775 return this . #system
6876 }
6977
7078 get override ( ) {
71- this . # subscribe( )
79+ this . subscribe ( )
7280 return this . #override
7381 }
7482
7583 get current ( ) {
76- this . # subscribe( )
84+ this . subscribe ( )
7785 return this . #value
7886 }
7987
8088 set current ( value : Theme ) {
89+ this . subscribe ( )
8190 switch ( value ) {
8291 case 'dark' :
8392 case 'light' :
@@ -88,7 +97,6 @@ class ThemeState {
8897 break
8998 }
9099 this . #override = value
91- this . #update?.( )
92100 }
93101}
94102
0 commit comments