1 port module Todo exposing (..)
3 {-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering.
5 This application is broken up into four distinct parts:
7 1. Model - a full description of the application as data
8 2. Update - a way to update the model based on user actions
9 3. View - a way to visualize our model with HTML
11 This program is not particularly large, so definitely see the following
12 document for notes on structuring more complex GUIs with Elm:
13 http://guide.elm-lang.org/architecture/
18 import Html exposing (..)
19 import Html.Attributes exposing (..)
20 import Html.Events exposing (..)
21 import Html.Lazy exposing (lazy, lazy2)
23 import Navigation exposing (Parser)
30 -- The full application state of our todo app.
34 { tasks : List Todo.Task.Model
56 -- A description of the kinds of actions that can be performed on the model of
57 -- our application. See the following post for more info on this pattern and
58 -- some alternatives: http://guide.elm-lang.org/architecture/
65 | UpdateTask ( Int, Todo.Task.Msg )
68 | ChangeVisibility String
72 -- How we update our Model on any given Message
75 update : Msg -> Model -> ( Model, Cmd Msg )
77 case Debug.log "MESSAGE: " msg of
84 { model | field = str }
86 ( newModel, save model )
91 String.trim model.field
94 if String.isEmpty description then
100 , tasks = model.tasks ++ [ Todo.Task.init description model.uid ]
103 ( newModel, save newModel )
105 UpdateTask ( id, taskMsg ) ->
109 Todo.Task.update taskMsg t
114 { model | tasks = List.filterMap updateTask model.tasks }
117 Todo.Task.Focus elementId ->
118 newModel ! [ save newModel, focusTask elementId ]
121 ( newModel, save newModel )
127 | tasks = List.filter (not << .completed) model.tasks
130 ( newModel, save newModel )
135 { t | completed = bool }
138 { model | tasks = List.map updateTask model.tasks }
140 ( newModel, save newModel )
142 ChangeVisibility visibility ->
145 { model | visibility = visibility }
147 ( newModel, save model )
150 focusTask : String -> Cmd Msg
151 focusTask elementId =
152 Task.perform (\_ -> NoOp) (\_ -> NoOp) (Dom.focus elementId)
159 view : Model -> Html Msg
162 [ class "todomvc-wrapper"
163 , style [ ( "visibility", "hidden" ) ]
167 [ lazy taskEntry model.field
168 , lazy2 taskList model.visibility model.tasks
169 , lazy2 controls model.visibility model.tasks
175 taskEntry : String -> Html Msg
179 [ h1 [] [ text "todos" ]
182 , placeholder "What needs to be done?"
186 , onInput UpdateField
187 , Todo.Task.onFinish Add NoOp
193 taskList : String -> List Todo.Task.Model -> Html Msg
194 taskList visibility tasks =
209 List.all .completed tasks
212 if List.isEmpty tasks then
219 , style [ ( "visibility", cssVisibility ) ]
225 , checked allCompleted
226 , onClick (CheckAll (not allCompleted))
231 [ text "Mark all as complete" ]
233 [ class "todo-list" ]
243 Html.App.map (\msg -> UpdateTask ( id, msg )) taskView
245 (List.filter isVisible tasks)
250 controls : String -> List Todo.Task.Model -> Html Msg
251 controls visibility tasks =
254 List.length (List.filter .completed tasks)
257 List.length tasks - tasksCompleted
260 if tasksLeft == 1 then
267 , hidden (List.isEmpty tasks)
270 [ class "todo-count" ]
271 [ strong [] [ text (toString tasksLeft) ]
272 , text (item_ ++ " left")
276 [ visibilitySwap "#/" "All" visibility
278 , visibilitySwap "#/active" "Active" visibility
280 , visibilitySwap "#/completed" "Completed" visibility
283 [ class "clear-completed"
284 , hidden (tasksCompleted == 0)
285 , onClick DeleteComplete
287 [ text ("Clear completed (" ++ toString tasksCompleted ++ ")") ]
291 visibilitySwap : String -> String -> String -> Html Msg
292 visibilitySwap uri visibility actualVisibility =
295 if visibility == actualVisibility then
301 [ onClick (ChangeVisibility visibility) ]
302 [ a [ class className, href uri ] [ text visibility ] ]
305 infoFooter : Html msg
309 [ p [] [ text "Double-click to edit a todo" ]
312 , a [ href "https://github.com/evancz" ] [ text "Evan Czaplicki" ]
316 , a [ href "http://todomvc.com" ] [ text "TodoMVC" ]
322 -- wire the entire application together
327 Navigation.programWithFlags urlParser
328 { urlUpdate = urlUpdate
332 , subscriptions = subscriptions
337 -- URL PARSERS - check out evancz/url-parser for fancier URL parsing
340 toUrl : String -> String
342 "#/" ++ String.toLower visibility
345 fromUrl : String -> Maybe String
349 String.dropLeft 2 hash
351 if (List.member cleanHash [ "all", "active", "completed" ]) == True then
357 urlParser : Parser (Maybe String)
359 Navigation.makeParser (fromUrl << .hash)
362 {-| The URL is turned into a Maybe value. If the URL is valid, we just update
363 our model with the new visibility settings. If it is not a valid URL,
364 we set the visibility filter to show all tasks.
366 urlUpdate : Maybe String -> Model -> ( Model, Cmd Msg )
367 urlUpdate result model =
370 update (ChangeVisibility (String.Extra.toSentenceCase visibility)) model
373 update (ChangeVisibility "All") model
376 init : Flags -> Maybe String -> ( Model, Cmd Msg )
378 urlUpdate url (Maybe.withDefault emptyModel flags)
382 -- interactions with localStorage
385 port save : Model -> Cmd msg
388 subscriptions : Model -> Sub Msg
389 subscriptions model =