This repository has been archived by the owner on Mar 18, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 28
/
04-Message-Only.purs
173 lines (149 loc) · 5.52 KB
/
04-Message-Only.purs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
module ParentChildRelationships.ChildlikeComponents.MessageOnly where
import Prelude
-- Imports for lesson
import Control.Monad.State (get, modify_)
import Data.Maybe (Maybe(..))
import Halogen as H
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
-- Imports for scaffolding
import CSS (em, marginTop)
import Data.Array ((:))
import Data.Const (Const)
import Data.Symbol (SProxy(..))
import Effect (Effect)
import Effect.Aff (Aff, launchAff_)
import Halogen (ComponentHTML)
import Halogen.Aff (awaitBody)
import Halogen.HTML.CSS as CSS
import Halogen.VDom.Driver (runUI)
type State = Int
data Action
= Increment
| Decrement
| NotifyParentAboutState
| NotifyParentTextMessage String
type Message = String
textAndButtonComponent :: StateActionMessageComponent State Action
textAndButtonComponent =
{ initialState: 0
, render
, handleAction
}
render :: StateAndActionRenderer State Action
render counterState =
let yourMessage = "Insert your message here"
in
HH.div_
[ HH.div_
[ HH.button
[ HE.onClick \_ -> Just NotifyParentAboutState ]
[ HH.text "Log current counter value" ]
]
, HH.div_
[ HH.button
[ HE.onClick \_ -> Just Decrement ]
[ HH.text $ "-"]
, HH.button
[ HE.onClick \_ -> Just Increment ]
[ HH.text $ "+"]
]
, HH.div_ [ HH.text $ show counterState ]
, HH.div_
[ HH.button
[ HE.onClick \_ -> Just $ NotifyParentTextMessage yourMessage ]
[ HH.text $ "Log '" <> yourMessage <> "' to the page."]]
]
handleAction :: HandleAction_StateStringMessage State Action
handleAction = case _ of
Increment -> do
modify_ (\oldState -> oldState + 1)
Decrement -> do
modify_ (\oldState -> oldState - 1)
NotifyParentAboutState -> do
currentState <- get
H.raise $ show currentState
NotifyParentTextMessage message -> do
H.raise $ message
main :: Effect Unit
main = runStateActionMessageComponent textAndButtonComponent
-- Scaffolded Code --
-- | Renders HTML that can respond to events by translating them
-- | into a value of the `action` that one uses to handle the event.
type DynamicHtml action = ComponentHTML action () Aff
-- | A function that uses the `state` type's value to render HTML
-- | with simple event-handling via the `action` type.
type StateAndActionRenderer state action = (state -> DynamicHtml action)
-- | When an `action` type's value is received, this function
-- | determines how to update the component (e.g. state updates) and
-- | can raise a `String` message to its parent component.
type HandleAction_StateStringMessage state action =
(action -> H.HalogenM state action () String Aff Unit)
-- | Combines all the code we need to define a simple componenet that supports
-- | state and simple event handlinGg
type StateActionMessageComponent state action =
{ initialState :: state
, render :: StateAndActionRenderer state action
, handleAction :: HandleAction_StateStringMessage state action
}
-- | Runs a component that converts the value of the `input` type provided
-- | by the parent (an `Int`) to a value of the `state` type as the
-- | child's initial state value, which is used to render dynamic HTML
-- | with event handling via the `action` type.
runStateActionMessageComponent :: forall state action.
StateActionMessageComponent state action
-> Effect Unit
runStateActionMessageComponent childSpec = do
launchAff_ do
body <- awaitBody
runUI (parentComponent $ stateActionMessageComponent childSpec) unit body
type ChildComponentWithStringMessage = H.Component HH.HTML (Const Unit) Unit String Aff
-- | Wraps Halogen types cleanly, so that one gets very clear compiler errors
stateActionMessageComponent :: forall state action.
StateActionMessageComponent state action
-> ChildComponentWithStringMessage
stateActionMessageComponent spec =
H.mkComponent
{ initialState: \_ -> spec.initialState
, render: spec.render
, eval: H.mkEval $ H.defaultEval { handleAction = spec.handleAction }
}
data ParentAction = AddMessage String
type ParentState = Array String
type ParentQuery = Const Unit
type ParentComponent = H.Component HH.HTML ParentQuery Unit Void Aff
_child :: SProxy "child"
_child = SProxy
type NoQuery_StringMessage {- index -}
= H.Slot (Const Unit) String {- index -}
type ChildSlots = ( child :: NoQuery_StringMessage Unit )
parentComponent :: ChildComponentWithStringMessage -> ParentComponent
parentComponent childComp =
H.mkComponent
{ initialState: const []
, render: parentHtml
, eval: H.mkEval $ H.defaultEval { handleAction = handleParentAction }
}
where
parentHtml :: ParentState -> H.ComponentHTML ParentAction ChildSlots Aff
parentHtml logArray =
HH.div_
[ HH.div_
[ HH.text "Use your child component's html to send messages to \
\the parent. It will render the message below them all"
]
, HH.slot _child unit childComp unit (\msg -> Just $ AddMessage msg)
, HH.div
[ CSS.style do
marginTop $ em 3.0
]
(logArray <#> \log ->
HH.div_ [ HH.text $ "Message received: " <> log]
)
]
handleParentAction
:: ParentAction
-> H.HalogenM ParentState ParentAction ChildSlots Void Aff Unit
handleParentAction (AddMessage msg) = do
-- add the message to the front of the array
modify_ (\array -> msg : array)