git.iko.soy/rowtype-yoga/ry-blocks
on main help sign in
Theme switching
commit5ca62b42379280324925deb8ba225c88b4cd4bd5 authorMark Eibes <mark.eibes@gmail.com> dateThu 7 Jan 2021, 21:41 (6 years ago) parent6eb6f3a
4 files changed+128−49
src/Yoga/Block/Atom/Input/View/Container.purs+35−39 src/Yoga/Block/Container/Style.js+5−1 src/Yoga/Block/Container/Style.purs+35−3 src/Yoga/Block/Container/View.purs+53−6
src/Yoga/Block/Atom/Input/View/Container.purs+35−39
@@ -10,33 +10,33 @@ import React.Basic.DOM (CSS, css)
1010import React.Basic.Emotion (Style)
1111import Yoga.Block.Atom.Input.Style as Style
1212
13−type PropsF f
14− = ( css ∷ f Style
15− , isInvalid ∷ f Boolean
16− | Style.Props f (MandatoryProps ())
17− )
13+type PropsF f =
14+ ( css ∷ f Style
15+ , isInvalid ∷ f Boolean
16+ | Style.Props f (MandatoryProps ())
17+ )
1818
19−type MandatoryProps r
20− = ( children ∷ Array JSX
21− , hasFocus ∷ Boolean
22− | r
23− )
19+type MandatoryProps r =
20+ ( children ∷ Array JSX
21+ , hasFocus ∷ Boolean
22+ | r
23+ )
2424
25−type Props
26− = PropsF Id
25+type Props =
26+ PropsF Id
2727
28−type PropsOptional
29− = PropsF OptionalProp
28+type PropsOptional =
29+ PropsF OptionalProp
3030
3131component ∷ ∀ p q. Union p q Props => ReactComponent { | MandatoryProps p }
3232component = rawContainer
3333
34−type Propski
35− = { css ∷ OptionalProp Style
36− , hasFocus ∷ Boolean
37− , isInvalid ∷ OptionalProp Boolean
38− , children ∷ Array JSX
39− }
34+type Propski =
35+ { css ∷ OptionalProp Style
36+ , hasFocus ∷ Boolean
37+ , isInvalid ∷ OptionalProp Boolean
38+ , children ∷ Array JSX
39+ }
4040
4141rawContainer ∷ ∀ p. ReactComponent { | p }
4242rawContainer =
@@ -52,11 +52,11 @@ rawContainer =
5252 { className: "ry-input-container"
5353 , css: Style.inputContainer props
5454 , _data:
55− props.isInvalid
56− # foldMap \isInvalid ->
57− Object.fromHomogeneous
58− { "invalid": show isInvalid
59− }
55+ props.isInvalid
56+ # foldMap \isInvalid ->
57+ Object.fromHomogeneous
58+ { "invalid": show isInvalid
59+ }
6060 , ref
6161 }
6262 /> props.children
@@ -66,11 +66,8 @@ drawPathUntil ∷ Int -> Array Point -> String
6666drawPathUntil idx thePath = do
6767 let
6868 fn { x, y } = i x "%" " " y "%"
69− let
7069 firstFew = Array.take idx thePath
71− let
7270 lastFew = Array.drop idx thePath $> (Array.last firstFew # fromMaybe' \_ -> unsafeCrashWith "ogod")
73− let
7471 rendered = intercalate "," $ fn <$> (firstFew <> lastFew)
7572 i "polygon(" rendered ")"
7673
@@ -87,7 +84,6 @@ mkPath borderX borderY = do
8784 , {- ⌝ -} p (100 - borderX) borderY
8885 , {- ⌜ -} p borderX borderY
8986 ]
90−
9187 outside =
9288 [ {-⌜ -} p 0 0
9389 , {-⌞ -} p 0 100
@@ -118,18 +114,18 @@ containerVariants ∷
118114 }
119115containerVariants =
120116 { focussed:
121− css
122− { clipPath
123− , transition: { duration: 0.6 }
124− }
117+ css
118+ { clipPath
119+ , transition: { duration: 0.6 }
120+ }
125121 , blurred:
126− css
127− { clipPath:
128− drawPathUntil (Array.length path + 1) path
129− }
122+ css
123+ { clipPath:
124+ drawPathUntil (Array.length path + 1) path
125+ }
130126 }
131127 where
132128 clipPath = 6 Array... (Array.length path) <#> \ln -> drawPathUntil ln path
133129
134−type Point
135− = { x ∷ Int, y ∷ Int }
130+type Point =
131+ { x ∷ Int, y ∷ Int }
src/Yoga/Block/Container/Style.js+5−1
@@ -1,2 +1,6 @@
11exports.getComputedStyleImpl = (el, window) => window.getComputedStyle(el)
2−exports.getPropertyValueImpl = (propName, computedStyle) => computedStyle.getPropertyValue(propName)
2+exports.getPropertyValueImpl = (propName, computedStyle) => computedStyle.getPropertyValue(propName)
3+exports.getElementStyle = el => () => el.style
4+exports.setStyleProperty = prop => value => style => () => style.setProperty(prop, value)
5+exports.matchMedia = string => window => () => window.matchMedia(string)
6+exports.matches = matchMedia => () => matchMedia.matches
src/Yoga/Block/Container/Style.purs+35−3
@@ -10,6 +10,7 @@ import Heterogeneous.Mapping (class HMapWithIndex, class MappingWithIndex, hmap,
1010import Unsafe.Coerce (unsafeCoerce)
1111import Web.DOM (Element)
1212import Web.DOM.Document (documentElement)
13+import Web.Event.Internal.Types (EventTarget)
1314import Web.HTML (Window, window)
1415import Web.HTML.HTMLDocument as HTMLDocument
1516import Web.HTML.Window (document)
@@ -47,13 +48,33 @@ foreign import getPropertyValueImpl ∷ EffectFn2 String ComputedStyle String --
4748getPropertyValue ∷ String -> ComputedStyle -> Effect String
4849getPropertyValue = runEffectFn2 getPropertyValueImpl
4950
51+foreign import data ElementStyle ∷ Type
52+
53+foreign import getElementStyle ∷ Element -> Effect ElementStyle
54+
55+foreign import setStyleProperty ∷ String -> String -> ElementStyle -> Effect Unit
56+
57+foreign import data MediaQueryList ∷ Type
58+
59+foreign import matchMedia ∷ String -> Window -> Effect MediaQueryList
60+
61+foreign import matches ∷ MediaQueryList -> Effect Boolean
62+
63+toEventTarget ∷ MediaQueryList -> EventTarget
64+toEventTarget = unsafeCoerce
65+
66+getDocumentElement ∷ MaybeT Effect Element
67+getDocumentElement = do
68+ win <- window # lift
69+ htmlDoc <- document win # lift
70+ let doc = HTMLDocument.toDocument htmlDoc
71+ documentElement doc # MaybeT
72+
5073getDarkOrLightMode ∷ Effect (Maybe DarkOrLightMode)
5174getDarkOrLightMode =
5275 runMaybeT do
5376 win <- window # lift
54− htmlDoc <- document win # lift
55− let doc = HTMLDocument.toDocument htmlDoc
56− docElem ∷ Element <- documentElement doc # MaybeT
77+ docElem ∷ Element <- getDocumentElement
5778 computedStyle <- getComputedStyle docElem win # lift
5879 pv <- getPropertyValue "--theme-variant" computedStyle # lift
5980 if pv == "dark" then
@@ -64,6 +85,17 @@ getDarkOrLightMode =
6485 else
6586 Nothing # pure # MaybeT
6687
88+setDarkOrLightMode ∷ DarkOrLightMode -> Effect Unit
89+setDarkOrLightMode desiredMode =
90+ runMaybeT_ do
91+ docElem <- getDocumentElement
92+ style <- getElementStyle docElem # lift
93+ style
94+ # setStyleProperty "--theme-variant" case desiredMode of
95+ LightMode -> "light"
96+ DarkMode -> "dark"
97+ # lift
98+
6799mkGlobal ∷ Maybe DarkOrLightMode -> Style
68100mkGlobal maybeMode =
69101 css
src/Yoga/Block/Container/View.purs+53−6
@@ -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