git.iko.soy/rowtype-yoga/ry-blocks
on main help sign in
UseMediaQuery
commite3df4432cae8c3984320c11ac4398f5efd9873e4 authorMark Eibes <mark.eibes@gmail.com> dateSat 9 Jul 2022, 13:09 (4 years ago) parent5b149c0
8 files changed+91−43
.vscode/settings.json+2−2 src/MediaQuery.js+24 src/MediaQuery.purs+9 src/MediaQuery/Types.purs+9 src/Yoga/Block/Container/Style.js−24 src/Yoga/Block/Container/Style.purs−9 src/Yoga/Block/Container/View.purs+11−8 src/Yoga/Block/Hook/UseMediaQuery.purs+36
.vscode/settings.json+2−2
@@ -1,6 +1,6 @@
11{
22 "editor.formatOnSave": true,
3− "workbench.colorTheme": "Halcyon",
3+ "workbench.colorTheme": "Night Owl Light",
44 "search.useGlobalIgnoreFiles": true,
55 "files.watcherExclude": {
66 "**/.spago/**": true,
@@ -9,4 +9,4 @@
99 "**/.storybook/**": true,
1010 "**/.github/**": true
1111 }
12−}
12+}
src/MediaQuery.jsnew file+24
@@ -0,0 +1,24 @@
1+// This is for the browser tests to work
2+// JSDOM does not have support for matchMedia
3+// So we just pretend nothing every matches
4+// And never invoke the listeners
5+const stubMatchMedia = () => {
6+ if (window.matchMedia) return
7+ window.matchMedia = (name) => ({
8+ matches: false,
9+ addEventListener: () => {},
10+ removeEventListener: () => {}
11+ })
12+}
13+export function matchMedia(string) {
14+ return (window) => () => {
15+ stubMatchMedia()
16+ return window.matchMedia(string)
17+ }
18+}
19+export function matches(matchMedia) {
20+ return () => {
21+ stubMatchMedia()
22+ return matchMedia.matches
23+ }
24+}
src/MediaQuery.pursnew file+9
@@ -0,0 +1,9 @@
1+module MediaQuery where
2+
3+import Effect (Effect)
4+import MediaQuery.Types (MediaQueryList)
5+import Web.HTML (Window)
6+
7+foreign import matchMedia ∷ String → Window → Effect MediaQueryList
8+
9+foreign import matches ∷ MediaQueryList → Effect Boolean
src/MediaQuery/Types.pursnew file+9
@@ -0,0 +1,9 @@
1+module MediaQuery.Types where
2+
3+import Unsafe.Coerce (unsafeCoerce)
4+import Web.Event.EventTarget (EventTarget)
5+
6+foreign import data MediaQueryList ∷ Type
7+
8+toEventTarget ∷ MediaQueryList → EventTarget
9+toEventTarget = unsafeCoerce
src/Yoga/Block/Container/Style.js−24
@@ -1,15 +1,3 @@
1−// This is for the browser tests to work
2−// JSDOM does not have support for matchMedia
3−// So we just pretend nothing every matches
4−// And never invoke the listeners
5−const stubMatchMedia = () => {
6− if (window.matchMedia) return
7− window.matchMedia = (name) => ({
8− matches: false,
9− addEventListener: () => {},
10− removeEventListener: () => {}
11− })
12−}
131
142export function getComputedStyleImpl(el, window) {
153 return window.getComputedStyle(el)
@@ -23,15 +11,3 @@ export function getElementStyle(el) {
2311export function setStyleProperty(prop) {
2412 return (value) => (style) => () => style.setProperty(prop, value)
2513}
26−export function matchMedia(string) {
27− return (window) => () => {
28− stubMatchMedia()
29− return window.matchMedia(string)
30− }
31−}
32−export function matches(matchMedia) {
33− return () => {
34− stubMatchMedia()
35− return matchMedia.matches
36− }
37−}
src/Yoga/Block/Container/Style.purs−9
@@ -58,15 +58,6 @@ foreign import getElementStyle ∷ Element → Effect ElementStyle
5858
5959foreign import setStyleProperty ∷ String → String → ElementStyle → Effect Unit
6060
61−foreign import data MediaQueryList ∷ Type
62−
63−foreign import matchMedia ∷ String → Window → Effect MediaQueryList
64−
65−foreign import matches ∷ MediaQueryList → Effect Boolean
66−
67−toEventTarget ∷ MediaQueryList → EventTarget
68−toEventTarget = unsafeCoerce
69−
7061getDocumentElement ∷ MaybeT Effect Element
7162getDocumentElement = do
7263 win ← window # lift
src/Yoga/Block/Container/View.purs+11−8
@@ -10,13 +10,16 @@ import Yoga.Prelude.View
1010
1111import Data.Array as Array
1212import Fahrtwind as F
13+import MediaQuery (matchMedia, matches)
14+import MediaQuery.Types (MediaQueryList)
15+import MediaQuery.Types as MediaQueryList
1316import React.Basic.DOM as R
1417import React.Basic.Emotion as E
1518import React.Basic.Hooks as React
1619import Web.Event.EventTarget (addEventListener, eventListener, removeEventListener)
1720import Web.HTML (window)
1821import Web.HTML.Event.EventTypes as Event
19−import Yoga.Block.Container.Style (DarkOrLightMode(..), matchMedia, matches, setDarkOrLightMode)
22+import Yoga.Block.Container.Style (DarkOrLightMode(..), setDarkOrLightMode)
2023import Yoga.Block.Container.Style as Styles
2124
2225type PropsF f =
@@ -32,10 +35,10 @@ type Props =
3235component ∷ ∀ p q. Union p q Props => ReactComponent { | p }
3336component = rawComponent
3437
35−mkPrefersDark ∷ Effect Styles.MediaQueryList
38+mkPrefersDark ∷ Effect MediaQueryList
3639mkPrefersDark = matchMedia "(prefers-color-scheme: dark)" =<< window
3740
38−mkPrefersLight ∷ Effect Styles.MediaQueryList
41+mkPrefersLight ∷ Effect MediaQueryList
3942mkPrefersLight = matchMedia "(prefers-color-scheme: light)" =<< window
4043
4144rawComponent ∷ ∀ p. ReactComponent { | p }
@@ -64,18 +67,18 @@ rawComponent =
6467 whenM (matches prefersDarkMediaQuery) do
6568 setSystemThemeVariant (Just DarkMode)
6669 notifySystemThemeChanged DarkMode
67− addEventListener Event.change darkModeListener true (Styles.toEventTarget prefersDarkMediaQuery)
70+ addEventListener Event.change darkModeListener true (MediaQueryList.toEventTarget prefersDarkMediaQuery)
6871 -- Light Mode listener
6972 lightModeListener <-
7073 eventListener \_ -> do
7174 whenM (matches prefersLightMediaQuery) do
7275 setSystemThemeVariant (Just LightMode)
7376 notifySystemThemeChanged LightMode
74− addEventListener Event.change darkModeListener true (Styles.toEventTarget prefersDarkMediaQuery)
75− addEventListener Event.change lightModeListener true (Styles.toEventTarget prefersLightMediaQuery)
77+ addEventListener Event.change darkModeListener true (MediaQueryList.toEventTarget prefersDarkMediaQuery)
78+ addEventListener Event.change lightModeListener true (MediaQueryList.toEventTarget prefersLightMediaQuery)
7679 pure do
77− removeEventListener Event.change darkModeListener true (Styles.toEventTarget prefersDarkMediaQuery)
78− removeEventListener Event.change lightModeListener true (Styles.toEventTarget prefersLightMediaQuery)
80+ removeEventListener Event.change darkModeListener true (MediaQueryList.toEventTarget prefersDarkMediaQuery)
81+ removeEventListener Event.change lightModeListener true (MediaQueryList.toEventTarget prefersLightMediaQuery)
7982 pure
8083 $ fragment
8184 [ R.div' </ { ref }
src/Yoga/Block/Hook/UseMediaQuery.pursnew file+36
@@ -0,0 +1,36 @@
1+module Yoga.Block.Hook.UseMediaQuery where
2+
3+import Prelude
4+
5+import Data.Newtype (class Newtype)
6+import Data.Tuple.Nested ((/\))
7+import Effect.Unsafe (unsafePerformEffect)
8+import MediaQuery (matchMedia, matches)
9+import MediaQuery.Types as MQL
10+import React.Basic.Hooks (UseEffect, UseState, Hook, coerceHook)
11+import React.Basic.Hooks as React
12+import Web.Event.EventTarget (addEventListener, eventListener, removeEventListener)
13+import Web.HTML (window)
14+import Web.HTML.Event.EventTypes as Event
15+
16+newtype UseMediaQuery hooks = UseMediaQuery
17+ (UseEffect String (UseState Boolean hooks))
18+
19+derive instance Newtype (UseMediaQuery hooks) _
20+
21+useMediaQuery :: String -> Hook UseMediaQuery Boolean
22+useMediaQuery query = coerceHook React.do
23+ let check = window >>= matchMedia query >>= matches
24+
25+ queryMatches /\ setMatches <-
26+ React.useState' (unsafePerformEffect check)
27+
28+ React.useEffect query do
29+ queryList <- window >>= matchMedia query
30+ let target = MQL.toEventTarget queryList
31+ listener <- eventListener (const (check >>= setMatches))
32+ addEventListener Event.change listener true target
33+ pure do
34+ removeEventListener Event.change listener true target
35+
36+ pure queryMatches