Skip to content

Commit 1204045

Browse files
committed
feat: Keyboard example -> use FRP.Event (TODO: cleanup, when parent widget is destroyed)
1 parent 1edc7f5 commit 1204045

File tree

4 files changed

+80
-64
lines changed

4 files changed

+80
-64
lines changed

examples/spago.dhall

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,17 @@ You can edit this file as you like.
66
{ name =
77
"purescript-concur-react-examples"
88
, dependencies =
9-
[ "affjax", "argonaut", "concur-core", "concur-react", "routing" ]
9+
[ "affjax"
10+
, "argonaut"
11+
, "concur-core"
12+
, "concur-react"
13+
, "routing"
14+
, "debug"
15+
, "web-html"
16+
, "web-uievents"
17+
, "event"
18+
, "web-events"
19+
]
1020
, sources =
1121
[ "src/**/*.purs" ]
1222
, packages =

examples/src/Test/Keyboard.js

-29
This file was deleted.

examples/src/Test/Keyboard.purs

+66-32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
module Test.Keyboard where
22

3+
import Prelude
4+
35
import Concur.Core.Types (Widget)
46
import Concur.React (HTML)
57
import Concur.React.DOM as D
@@ -19,7 +21,20 @@ import Effect.Aff (Aff)
1921
import Effect.Aff.Class (liftAff)
2022
import Effect.Aff.Compat (EffectFnAff, fromEffectFnAff)
2123
import Effect.Class (liftEffect)
24+
import FRP.Event (Event)
25+
import FRP.Event as FRP.Event
2226
import React.SyntheticEvent as R
27+
import Web.Event.Event (EventType(..))
28+
import Web.Event.Event as Web.Event.Event
29+
import Web.Event.EventTarget as Web.Event.EventTarget
30+
import Web.HTML as Web.HTML
31+
import Web.HTML.Window as Web.HTML.Window
32+
import Web.UIEvent.KeyboardEvent (KeyboardEvent)
33+
import Web.UIEvent.KeyboardEvent as Web.UIEvent.KeyboardEvent
34+
import Web.DOM.Document as Web.DOM.Document
35+
import Web.HTML.HTMLDocument as Web.HTML.HTMLDocument
36+
import Effect.AVar as Effect.AVar
37+
import Effect.Aff.AVar as Effect.Aff.AVar
2338

2439

2540
-- A virtual keyboard, that also demonstrates how to handle document level events
@@ -30,34 +45,45 @@ import React.SyntheticEvent as R
3045
-- A never-ending virtual keypad widget.
3146
-- Allows the user to navigate and select a key. Displays the selected key.
3247
keypadWidget :: forall a. Widget HTML a
33-
keypadWidget = go Enter "" <|> toggleEvents
34-
where
35-
go focus msg = do
36-
keyPressed <- virtualKeyInput focus <|> D.div' [D.text msg]
37-
go keyPressed $ "You clicked: " <> show keyPressed
48+
keypadWidget = do
49+
keyboardRef <- liftEffect $ do
50+
keyboardRef <- Effect.AVar.empty
51+
52+
-- TODO: don't silently fail to put, use Channel instead of EVar
53+
stopListeningKeydownEvent <- FRP.Event.subscribe documentKeydownEvent \keyboardEvent -> void $ Effect.AVar.tryPut keyboardEvent keyboardRef
54+
55+
pure keyboardRef
56+
57+
let (awaitKeyboardEvent :: Aff KeyboardEvent) = Effect.Aff.AVar.take keyboardRef
58+
59+
go awaitKeyboardEvent Enter ""
3860

39-
-- On off button for key events
40-
toggleEvents :: forall a. Widget HTML a
41-
toggleEvents = go false
4261
where
43-
go enabled = do
44-
_ <- D.button [P.onClick] [D.text $ if enabled then "stop listening" else "start listening"]
45-
liftEffect (if enabled then stopListening else startListening)
46-
go (not enabled)
62+
go awaitKeyboardEvent focus msg = do
63+
keyPressed <- virtualKeyInput awaitKeyboardEvent focus <|> D.div' [D.text msg]
64+
go awaitKeyboardEvent keyPressed ("You clicked: " <> show keyPressed)
65+
66+
-- | -- On off button for key events
67+
-- | toggleEvents :: forall a. Widget HTML a
68+
-- | toggleEvents = go false
69+
-- | where
70+
-- | go enabled = do
71+
-- | _ <- D.button [P.onClick] [D.text $ if enabled then "stop listening" else "start listening"]
72+
-- | liftEffect (if enabled then stopListening else startListening)
73+
-- | go (not enabled)
4774

4875
-- Displays a keypad with the supplied initial focus.
4976
-- Allows the user to navigate and select a key. Returns the selected key.
50-
virtualKeyInput :: Focus -> Widget HTML Key
51-
virtualKeyInput focus = do
52-
evt <- liftAff awaitKey <|> keypadButtons focus
53-
key <- liftEffect $ toKey evt
77+
virtualKeyInput :: Aff KeyboardEvent -> Focus -> Widget HTML Key
78+
virtualKeyInput awaitKeyboardEvent focus = do
79+
(key :: Maybe Key) <- liftAff (map toKey awaitKeyboardEvent) <|> keypadButtons focus
5480
case key of
5581
Just Enter -> pure focus
56-
Nothing -> virtualKeyInput focus
57-
Just ArrowUp -> virtualKeyInput (transition focus U)
58-
Just ArrowDown -> virtualKeyInput (transition focus D)
59-
Just ArrowLeft -> virtualKeyInput (transition focus L)
60-
Just ArrowRight -> virtualKeyInput (transition focus R)
82+
Nothing -> virtualKeyInput awaitKeyboardEvent focus
83+
Just ArrowUp -> virtualKeyInput awaitKeyboardEvent (transition focus U)
84+
Just ArrowDown -> virtualKeyInput awaitKeyboardEvent (transition focus D)
85+
Just ArrowLeft -> virtualKeyInput awaitKeyboardEvent (transition focus L)
86+
Just ArrowRight -> virtualKeyInput awaitKeyboardEvent (transition focus R)
6187

6288
-- Dispay only. Renders the keypad buttons with the supplied focus
6389
keypadButtons :: forall a. Focus -> Widget HTML a
@@ -82,15 +108,24 @@ keypadButtons focus = D.table' $ pure $ D.tbody'
82108

83109
-- FFI ------------------------------------------------------------
84110

85-
-- Start and stop listening for keyboard events
86-
foreign import startListening :: Effect Unit
87-
foreign import stopListening :: Effect Unit
111+
documentKeydownEvent :: Event KeyboardEvent
112+
documentKeydownEvent = FRP.Event.makeEvent \push -> do
113+
window <- Web.HTML.window
114+
115+
document <- Web.HTML.Window.document window
116+
117+
eventListener <- Web.Event.EventTarget.eventListener
118+
(\event ->
119+
case Web.UIEvent.KeyboardEvent.fromEvent event of
120+
Nothing -> pure unit
121+
Just event' -> push event'
122+
)
123+
124+
let eventType = EventType "keydown"
88125

89-
-- Await a key input. Requires that we are listening for events.
90-
foreign import _awaitKey :: EffectFnAff R.SyntheticKeyboardEvent
91-
awaitKey :: Aff R.SyntheticKeyboardEvent
92-
awaitKey = fromEffectFnAff _awaitKey
126+
Web.Event.EventTarget.addEventListener eventType eventListener false (Web.HTML.HTMLDocument.toEventTarget document)
93127

128+
pure $ Web.Event.EventTarget.removeEventListener eventType eventListener false (Web.HTML.HTMLDocument.toEventTarget document)
94129

95130
-- Data structures ------------------------------------------------
96131

@@ -120,10 +155,9 @@ type Focus = Key
120155

121156
data Dir = U | D | L | R
122157

123-
toKey :: R.SyntheticKeyboardEvent -> Effect (Maybe Key)
124-
toKey event = do
125-
k <- R.key event
126-
pure $ case k of
158+
toKey :: KeyboardEvent -> Maybe Key
159+
toKey event =
160+
case Web.UIEvent.KeyboardEvent.key event of
127161
"ArrowUp" -> Just ArrowUp
128162
"ArrowDown" -> Just ArrowDown
129163
"ArrowLeft" -> Just ArrowLeft

packages.dhall

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
let upstream =
22
https://github.com/purescript/package-sets/releases/download/psc-0.13.8-20210118/packages.dhall sha256:a59c5c93a68d5d066f3815a89f398bcf00e130a51cb185b2da29b20e2d8ae115
33

4-
let overrides = { concur-react = ./spago.dhall as Location }
4+
let overrides = {=}
5+
-- let overrides = { concur-react = ./spago.dhall as Location }
56

67
let additions =
78
{ concur-core =
@@ -15,7 +16,7 @@ let additions =
1516
, "event"
1617
]
1718
, repo =
18-
"https://github.com/srghma/purescript-concur-core"
19+
"https://github.com/purescript-concur/purescript-concur-core"
1920
, version =
2021
"master"
2122
}

0 commit comments

Comments
 (0)