Skip to content

Instantly share code, notes, and snippets.

@chendrix
Created May 17, 2015
Embed
What would you like to do?
Bug somewhere?
module TodoMe where
{-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering.
This application is broken up into four distinct parts:
1. Model - a full definition of the application's state
2. Update - a way to step the application state forward
3. View - a way to visualize our application state with HTML
4. Inputs - the signals necessary to manage events
This clean division of concerns is a core part of Elm. You can read more about
this in the Pong tutorial: http://elm-lang.org/blog/Pong.elm
This program is not particularly large, so definitely see the following
document for notes on structuring more complex GUIs with Elm:
http://elm-lang.org/learn/Architecture.elm
-}
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Html.Lazy exposing (lazy, lazy2, lazy3)
import Json.Decode as Json
import Signal exposing (Signal, Address)
import String
import Window
type alias Model =
{ tasks : List Task
, field : String
, uid : Int
, visibility : String
}
type alias Task =
{ description : String
, completed : Bool
, editing : Bool
, id : Int
}
newTask : String -> Int -> Task
newTask desc id =
{ description = desc
, completed = False
, editing = False
, id = id
}
emptyModel : Model
emptyModel =
{ tasks = []
, field = ""
, uid = 0
, visibility = "All"
}
type Action
= NoOp
| UpdateField String
| EditingTask Int Bool
| UpdateTask Int String
| Add
| Delete Int
| DeleteComplete
| Check Int Bool
| CheckAll Bool
| ChangeVisibility String
update : Action -> Model -> Model
update action model =
case action of
NoOp -> model
UpdateField f ->
{ model | field <- f }
EditingTask id isEditing ->
let updateTask t = if t.id == id then { t | editing <- isEditing } else t
in
{ model | tasks <- List.map updateTask model.tasks }
UpdateTask id task ->
let updateTask t = if t.id == id then { t | description <- task } else t
in
{ model | tasks <- List.map updateTask model.tasks }
Add ->
{ model |
uid <- model.uid + 1,
field <- "",
tasks <-
if String.isEmpty model.field
then model.tasks
else model.tasks ++ [newTask model.field model.uid]
}
Delete id ->
{ model | tasks <- List.filter (\t -> t.id /= id) model.tasks }
DeleteComplete ->
{ model | tasks <- List.filter (not << .completed) model.tasks }
Check id isChecked ->
let updateTask t = if t.id == id then { t | completed <- isChecked } else t
in
{ model | tasks <- List.map updateTask model.tasks }
CheckAll isChecked ->
let updateTask t = { t | completed <- isChecked }
in
{ model | tasks <- List.map updateTask model.tasks }
ChangeVisibility visibility ->
{ model | visibility <- visibility }
view : Address Action -> Model -> Html
view address model =
div
[ class "todomvc-wrapper"
, style [ ("visibility", "hidden") ]
]
[ section
[ id "todoapp" ]
[ lazy2 taskEntry address model.field
--, lazy3 taskList address model.visibility model.tasks
--, lazy3 controls address model.visibility model.tasks
]
--, infoFooter
]
onEnter : Address a -> a -> Attribute
onEnter address value =
on "keydown"
(Json.customDecoder keyCode is13)
(\_ -> Signal.message address value)
is13 : Int -> Result String ()
is13 code =
if code == 13 then Ok () else Err "not the right key code"
taskEntry : Address Action -> String -> Html
taskEntry address task =
header
[ id "header" ]
[ h1 [] [ text "todos" ]
, input
[ id "new-todo"
, placeholder "What needs to be done?"
, autofocus True
, value task
, name "newTodo"
, on "input" targetValue (Signal.message address << UpdateField)
, onEnter address Add
]
[]
]
--taskList : Address Action -> Visibility -> List Task -> Html
--taskList address visibility tasks =
--controls : Address Action -> Visibility -> List Task -> Html
--taskList address visibility tasks =
--infoFooter : Html
main : Signal Html
main =
Signal.map (view actions.address) model
model : Signal Model
model =
Signal.foldp update initialModel actions.signal
initialModel : Model
initialModel =
Maybe.withDefault emptyModel getStorage
actions : Signal.Mailbox Action
actions =
Signal.mailbox NoOp
port focus : Signal String
port focus =
let needsFocus act =
case act of
EditingTask id bool -> bool
_ -> False
toSelector (EditingTask id _) = ("#todo-" ++ toString id)
in
actions.signal
|> Signal.filter needsFocus (EditingTask 0 True)
|> Signal.map toSelector
port getStorage : Maybe Model
port setStorage : Signal Model
port setStorage = model
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment