@@ -8,13 +8,18 @@ import Effect.Unsafe (unsafePerformEffect)
88import React.Basic.DOM as R
99import React.Basic.Emotion as E
1010import React.Basic.Hooks (reactComponent)
11+import React.Basic.Hooks as React
1112import Unsafe.Coerce (unsafeCoerce)
12−import Yoga.Block.Container.Style (DarkOrLightMode)
13+import Web.Event.EventTarget (addEventListener, eventListener, removeEventListener)
14+import Web.HTML (window)
15+import Web.HTML.Event.EventTypes as Event
16+import Yoga.Block.Container.Style (DarkOrLightMode(..), matchMedia, matches, setDarkOrLightMode)
1317import Yoga.Block.Container.Style as Styles
1418
1519type PropsF f =
1620 ( content ∷ JSX
1721 , themeVariant ∷ f (Maybe DarkOrLightMode)
22+ , onPreferredSystemThemeChange ∷ f (DarkOrLightMode -> Effect Unit)
1823 )
1924
2025type Props =
@@ -23,20 +28,62 @@ type Props =
2328component ∷ ∀ p q. Union p q Props => ReactComponent { | p }
2429component = rawComponent
2530
31+mkPrefersDark ∷ Effect Styles.MediaQueryList
32+mkPrefersDark = matchMedia "(prefers-color-scheme: dark)" =<< window
33+
34+mkPrefersLight ∷ Effect Styles.MediaQueryList
35+mkPrefersLight = matchMedia "(prefers-color-scheme: light)" =<< window
36+
2637rawComponent ∷ ∀ p. ReactComponent { | p }
2738rawComponent =
2839 unsafeCoerce
2940 $ unsafePerformEffect
30− $ reactComponent "Container" \({ content, themeVariant } ∷ { | PropsF OptionalProp }) -> React.do
41+ $ reactComponent "Container" \(props@{ content } ∷ { | PropsF OptionalProp }) -> React.do
42+ let propsThemeVariant = props.themeVariant # opToMaybe # join
43+ let notifySystemThemeChanged = props.onPreferredSystemThemeChange ?|| mempty
44+ systemThemeVariant /\ setSystemThemeVariant <- React.useState' Nothing
45+ useEffect propsThemeVariant do
46+ for_ propsThemeVariant setDarkOrLightMode
47+ mempty
48+ useEffectOnce do
49+ prefersDarkMediaQuery <- mkPrefersDark
50+ prefersLightMediaQuery <- mkPrefersLight
51+ -- Init system preference
52+ whenM (matches prefersDarkMediaQuery) do
53+ setSystemThemeVariant (Just DarkMode)
54+ notifySystemThemeChanged DarkMode
55+ whenM (matches prefersLightMediaQuery) do
56+ setSystemThemeVariant (Just LightMode)
57+ notifySystemThemeChanged LightMode
58+ -- Dark Mode listener
59+ darkModeListener <-
60+ eventListener \_ -> do
61+ whenM (matches prefersDarkMediaQuery) do
62+ setSystemThemeVariant (Just DarkMode)
63+ notifySystemThemeChanged DarkMode
64+ addEventListener Event.change darkModeListener true (Styles.toEventTarget prefersDarkMediaQuery)
65+ -- Light Mode listener
66+ lightModeListener <-
67+ eventListener \_ -> do
68+ whenM (matches prefersLightMediaQuery) do
69+ setSystemThemeVariant (Just LightMode)
70+ notifySystemThemeChanged LightMode
71+ addEventListener Event.change darkModeListener true (Styles.toEventTarget prefersDarkMediaQuery)
72+ addEventListener Event.change lightModeListener true (Styles.toEventTarget prefersLightMediaQuery)
73+ pure do
74+ removeEventListener Event.change darkModeListener true (Styles.toEventTarget prefersDarkMediaQuery)
75+ removeEventListener Event.change lightModeListener true (Styles.toEventTarget prefersLightMediaQuery)
3176 pure
3277 $ R.div_
3378 $ Array.cons
3479 ( element E.global
3580 { styles:
36− case opToMaybe themeVariant # join of
37− Nothing -> Styles.global
38− Just Styles.DarkMode -> Styles.darkMode
39− Just Styles.LightMode -> Styles.lightMode
81+ case propsThemeVariant, systemThemeVariant of
82+ Nothing, Nothing -> Styles.global
83+ Just Styles.DarkMode, _ -> Styles.darkMode
84+ Just Styles.LightMode, _ -> Styles.lightMode
85+ Nothing, Just DarkMode -> Styles.darkMode
86+ Nothing, Just LightMode -> Styles.lightMode
4087 }
4188 )
4289 [ content