Skip to content

Instantly share code, notes, and snippets.

@zwhitchcox
Last active October 21, 2016 01:06
Show Gist options
  • Select an option

  • Save zwhitchcox/7fce616f55d1cdc3cc0b1fb79ee72168 to your computer and use it in GitHub Desktop.

Select an option

Save zwhitchcox/7fce616f55d1cdc3cc0b1fb79ee72168 to your computer and use it in GitHub Desktop.
port module Training exposing (..)
import Html exposing (..)
import Html.App exposing (programWithFlags)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Html.Keyed as Keyed
import Html.Lazy exposing (lazy, lazy2)
import Json.Decode as Json
import String
main : Program (Maybe Model)
main =
programWithFlags
{ init = init
, view = view
, update = updateWithStorage
, subscriptions = \_ -> Sub.none
}
type alias Model =
{ todos : List Todo
, newTodo : String
, visibility : String
, id : Int
}
type alias Todo =
{ description : String
, completed : Bool
, id : Int
}
emptyModel : Model
emptyModel =
{ todos = []
, newTodo = ""
, visibility = "All"
, id = 0
}
init : Maybe Model -> ( Model, Cmd Msg )
init savedState =
Maybe.withDefault emptyModel savedState ! []
port setStorage : Model -> Cmd msg
updateWithStorage : Msg -> Model -> ( Model, Cmd Msg )
updateWithStorage msg model =
let
( newModel, cmds ) =
update msg model
in
( newModel
, Cmd.batch [ setStorage newModel, cmds ]
)
type Msg
= DeleteCompleted
| ToggleAll Bool
| UpdateTodo Int String
| Delete Int
| ChangeVisibility String
| AddTodo
| UpdateNewTodo String
| Check Int Bool
| NoOp
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Check id isCompleted ->
let
updateTodo todo =
if todo.id == id then
{ todo | completed = isCompleted }
else
todo
in
{ model | todos = List.map updateTodo model.todos }
! []
UpdateTodo id task ->
let
updateTodo t =
if t.id == id then
{ t | description = task }
else
t
in
{ model | todos = List.map updateTodo model.todos }
! []
Delete id ->
{ model | todos = List.filter (\t -> t.id /= id) model.todos }
! []
DeleteCompleted ->
{ model | todos = List.filter (not << .completed) model.todos }
! []
ToggleAll isCompleted ->
let
updateTodo t =
{ t | completed = isCompleted }
in
{ model | todos = List.map updateTodo model.todos }
! []
ChangeVisibility visibility ->
{ model | visibility = visibility }
! []
AddTodo ->
if (not (String.isEmpty model.newTodo)) then
{ model
| id = model.id + 1
, todos = model.todos ++ [ newTodo model.newTodo model.id ]
, newTodo = ""
}
! []
else
model
! []
UpdateNewTodo description ->
{ model | newTodo = description }
! []
NoOp ->
model
! []
newTodo : String -> Int -> Todo
newTodo description id =
{ description = description
, id = id
, completed = False
}
view : Model -> Html Msg
view model =
div []
[ section
[]
[ lazy viewNewTodo model.newTodo
, lazy2 viewTodos model.visibility model.todos
, lazy2 viewControls model.visibility model.todos
]
]
viewNewTodo : String -> Html Msg
viewNewTodo newTodo =
input
[ type' "text"
, placeholder "What needs to be done?"
, autofocus True
, value newTodo
, onInput UpdateNewTodo
, onEnter AddTodo
]
[]
onEnter : Msg -> Attribute Msg
onEnter msg =
let
tagger code =
if code == 13 then
msg
else
NoOp
in
on "keydown" (Json.map tagger keyCode)
viewTodos : String -> List Todo -> Html Msg
viewTodos visibility todos =
let
isVisible todo =
case visibility of
"Completed" ->
todo.completed
"Active" ->
not todo.completed
_ ->
True
cssVisibility =
if List.isEmpty todos then
"hidden"
else
"visible"
allCompleted =
List.all .completed todos
in
section
[ style [ ( "visibility", cssVisibility ) ] ]
[ br [] []
, button
[ onClick (ToggleAll (not allCompleted)) ]
[ text "Toggle all todos" ]
, Keyed.ul [] <|
List.map viewKeyedTodo (List.filter isVisible todos)
]
viewKeyedTodo : Todo -> ( String, Html Msg )
viewKeyedTodo todo =
( toString todo.id, lazy viewTodo todo )
viewTodo : Todo -> Html Msg
viewTodo todo =
li []
[ input
[ type' "checkbox"
, checked todo.completed
, onClick (Check todo.id (not todo.completed))
]
[]
, input
[ value todo.description
, onInput (UpdateTodo todo.id)
]
[]
, button [ onClick (Delete todo.id) ] [ text "X" ]
]
viewControls : String -> List Todo -> Html Msg
viewControls visibility todos =
let
todosCompleted =
List.length (List.filter .completed todos)
todosLeft =
List.length todos - todosCompleted
in
footer
[ hidden (List.isEmpty todos)
]
[ lazy viewControlsCount todosLeft
, viewControlsFilters
, lazy viewControlsClear todosCompleted
]
viewControlsCount : Int -> Html Msg
viewControlsCount left =
let
pluralized =
if left == 1 then
" todo"
else
" todos"
in
text (toString left ++ pluralized)
viewControlsFilters : Html Msg
viewControlsFilters =
ul []
[ visibilityLi "#/" "All"
, visibilityLi "#/completed" "Completed"
, visibilityLi "#/active" "Active"
]
visibilityLi : String -> String -> Html Msg
visibilityLi uri visible =
li []
[ a
[ href uri
, onClick (ChangeVisibility visible)
]
[ text visible ]
]
viewControlsClear : Int -> Html Msg
viewControlsClear completed =
button
[ onClick DeleteCompleted ]
[ text ("Delete " ++ (toString completed) ++ " completed items") ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment