7
7
{-# LANGUAGE TypeFamilies #-}
8
8
module Reflex.TodoMVC where
9
9
10
- import Prelude hiding (mapM , mapM_ , all , sequence )
10
+ import Prelude hiding (mapM , mapM_ , sequence )
11
11
12
12
import Control.Monad hiding (mapM , mapM_ , forM , forM_ , sequence )
13
13
import Control.Monad.Fix
@@ -23,7 +23,6 @@ import GHCJS.DOM.Types (JSM)
23
23
24
24
import Reflex
25
25
import Reflex.Dom.Core
26
- import Data.Text.Encoding (encodeUtf8 )
27
26
28
27
--------------------------------------------------------------------------------
29
28
-- Model
@@ -115,21 +114,23 @@ taskEntry
115
114
)
116
115
=> m (Event t Task )
117
116
taskEntry = el " header" $ do
118
- -- Create the textbox; it will be cleared whenever the user presses enter
119
- rec let newValueEntered = ffilter (keyCodeIs Enter . fromIntegral ) (_textInput_keypress descriptionBox)
120
- descriptionBox <- textInput $ def
121
- & textInputConfig_setValue .~ fmap (const " " ) newValueEntered
122
- & textInputConfig_attributes .~ constDyn (mconcat [ " class" =: " new-todo"
123
- , " placeholder" =: " What needs to be done?"
124
- , " name" =: " newTodo"
125
- ])
126
- -- -- Request focus on this element when the widget is done being built
127
- -- schedulePostBuild $ liftIO $ focus $ _textInput_element descriptionBox
128
- let -- | Get the current value of the textbox whenever the user hits enter
129
- newValue = tag (current $ _textInput_value descriptionBox) newValueEntered
130
- -- -- Set focus when the user enters a new Task
131
- -- performEvent_ $ fmap (const $ liftIO $ focus $ _textInput_element descriptionBox) newValueEntered
132
- return $ fmap (\ d -> Task d False ) $ fmapMaybe stripDescription newValue
117
+ -- Create the textbox; it will be cleared whenever the user presses enter
118
+ rec let newValueEntered = keypress Enter descriptionBox
119
+ descriptionBox <- inputElement $ def
120
+ & inputElementConfig_setValue .~ fmap (const " " ) newValueEntered
121
+ & inputElementConfig_elementConfig . elementConfig_initialAttributes .~
122
+ mconcat [ " class" =: " new-todo"
123
+ , " placeholder" =: " What needs to be done?"
124
+ , " name" =: " newTodo"
125
+ , " type" =: " text"
126
+ ]
127
+ -- -- Request focus on this element when the widget is done being built
128
+ -- schedulePostBuild $ liftIO $ focus $ _textInput_element descriptionBox
129
+ let -- | Get the current value of the textbox whenever the user hits enter
130
+ newValue = tag (current $ value descriptionBox) newValueEntered
131
+ -- -- Set focus when the user enters a new Task
132
+ -- performEvent_ $ fmap (const $ liftIO $ focus $ _textInput_element descriptionBox) newValueEntered
133
+ return $ fmap (\ d -> Task d False ) $ fmapMaybe stripDescription newValue
133
134
134
135
-- | Display the user's Tasks, subject to a Filter; return requested modifications to the Task list
135
136
taskList
@@ -144,10 +145,9 @@ taskList
144
145
-> Dynamic t (Map k Task )
145
146
-> m (Event t (Map k Task -> Map k Task ))
146
147
taskList activeFilter tasks = elAttr " section" (" class" =: " main" ) $ do
147
- -- Create "toggle all" button
148
148
let toggleAllState = all taskCompleted . Map. elems <$> tasks
149
149
toggleAllAttrs = ffor tasks $ \ t -> " class" =: " toggle-all" <> " name" =: " toggle" <> if Map. null t then " style" =: " visibility:hidden" else mempty
150
- toggleAll <- checkboxView toggleAllAttrs toggleAllState
150
+ toggleAll <- toggleInput toggleAllAttrs toggleAllState
151
151
elAttr " label" (" for" =: " toggle-all" ) $ text " Mark all as complete"
152
152
-- Filter the item list
153
153
let visibleTasks = zipDynWith (Map. filter . satisfiesFilter) activeFilter tasks
@@ -162,11 +162,29 @@ taskList activeFilter tasks = elAttr "section" ("class" =: "main") $ do
162
162
let combineItemChanges = fmap (foldl' (.) id ) . mergeList . map (\ (k, v) -> fmap (flip Map. update k) v) . Map. toList
163
163
itemChangeEvent = fmap combineItemChanges items
164
164
itemChanges = switch $ current itemChangeEvent
165
- -- Change all items' completed state when the toggleAll button is clicked
166
- toggleAllChanges = fmap (\ oldAllCompletedState -> fmap (\ t -> t { taskCompleted = not oldAllCompletedState })) $ tag (current toggleAllState) toggleAll
167
- return $ mergeWith (.) [ itemChanges
168
- , toggleAllChanges
169
- ]
165
+ return itemChanges
166
+
167
+ toggleInput
168
+ :: ( DomBuilder t m
169
+ , DomBuilderSpace m ~ GhcjsDomSpace
170
+ , MonadFix m
171
+ , MonadHold t m
172
+ , PostBuild t m
173
+ )
174
+ => Dynamic t (Map AttributeName Text )
175
+ -> Dynamic t Bool
176
+ -> m (Event t () )
177
+ toggleInput dynAttrs dynChecked = do
178
+ let attrs = (<> " class" =: " toggle" ) . (" type" =: " checkbox" <> ) <$> dynAttrs
179
+ updatedAttrs = fmap Just <$> updated dynAttrs
180
+ updatedChecked = updated dynChecked
181
+ initialAttrs <- sample $ current attrs
182
+ initialChecked <- sample $ current dynChecked
183
+ domEvent Click <$> inputElement (def
184
+ & inputElementConfig_initialChecked .~ initialChecked
185
+ & inputElementConfig_setChecked .~ updatedChecked
186
+ & inputElementConfig_elementConfig . elementConfig_modifyAttributes .~ updatedAttrs
187
+ & inputElementConfig_elementConfig . elementConfig_initialAttributes .~ initialAttrs)
170
188
171
189
buildCompletedCheckbox
172
190
:: ( DomBuilder t m
@@ -181,8 +199,8 @@ buildCompletedCheckbox
181
199
buildCompletedCheckbox todo description = elAttr " div" (" class" =: " view" ) $ do
182
200
-- Display the todo item's completed status, and allow it to be set
183
201
completed <- holdUniqDyn $ fmap taskCompleted todo
184
- completedCheckbox <- checkboxView (constDyn $ " class " =: " toggle " ) completed
185
- let setCompleted = fmap not $ tag (current completed) completedCheckbox
202
+ checkboxClicked <- toggleInput (constDyn mempty ) completed
203
+ let setCompleted = fmap not $ tag (current completed) checkboxClicked
186
204
-- Display the todo item's name for viewing purposes
187
205
(descriptionLabel, _) <- el' " label" $ dynText description
188
206
-- Display the button for deleting the todo item
@@ -213,16 +231,17 @@ todoItem todo = do
213
231
(setCompleted, destroy, startEditing) <- buildCompletedCheckbox todo description
214
232
-- Set the current value of the editBox whenever we start editing (it's not visible in non-editing mode)
215
233
let setEditValue = tag (current description) $ ffilter id $ updated editing'
216
- editBox <- textInput $ def
217
- & textInputConfig_setValue .~ setEditValue
218
- & textInputConfig_attributes .~ constDyn (" class" =: " edit" <> " name" =: " title" )
234
+ editBox <- inputElement $ def
235
+ & inputElementConfig_setValue .~ setEditValue
236
+ & inputElementConfig_elementConfig . elementConfig_initialAttributes
237
+ .~ (" class" =: " edit" <> " name" =: " title" )
219
238
let -- Set the todo item's description when the user leaves the textbox or presses enter in it
220
- setDescription = tag (current $ _textInput_value editBox) $ leftmost
221
- [ void $ ffilter (keyCodeIs Enter . fromIntegral ) $ _textInput_keypress editBox
222
- , void $ ffilter not $ updated $ _textInput_hasFocus editBox
239
+ setDescription = tag (current $ value editBox) $ leftmost
240
+ [ keypress Enter editBox
241
+ , domEvent Blur editBox
223
242
]
224
243
-- Cancel editing (without changing the item's description) when the user presses escape in the textbox
225
- cancelEdit = void $ ffilter (keyCodeIs Escape . fromIntegral ) $ _textInput_keydown editBox
244
+ cancelEdit = keypress Escape editBox
226
245
-- Put together all the ways the todo item can change itself
227
246
changeSelf = mergeWith (>=>) [ fmap (\ c t -> Just $ t { taskCompleted = c }) setCompleted
228
247
, fmap (const $ const Nothing ) destroy
0 commit comments