Skip to content

Instantly share code, notes, and snippets.

@stewart
Last active May 26, 2017 19:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stewart/ce374b8f2511a7db1548b686f0feac68 to your computer and use it in GitHub Desktop.
Save stewart/ce374b8f2511a7db1548b686f0feac68 to your computer and use it in GitHub Desktop.
module Main exposing (..)
import Dom
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Html.Keyed as Keyed
import Json.Decode as Json
import Task
-- model
type alias Model =
{ uid : Id
, title : String
, items : List Item
}
type alias Item =
{ id : Id
, title : String
, completed : Bool
, editing : Bool
}
type alias Id =
Int
-- msg
type Msg
= None
| UpdateTitle String
| CreateItem
| ToggleItem Id Bool
| EditingItem Id Bool
| UpdateItem Id String
-- init
init : Model
init =
{ uid = 0
, title = ""
, items = []
}
-- update
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
None ->
model ! []
UpdateTitle str ->
{ model | title = str } ! []
CreateItem ->
let
item =
{ id = model.uid
, title = model.title
, completed = False
, editing = False
}
in
if String.isEmpty model.title then
model ! []
else
{ model
| uid = model.uid + 1
, title = ""
, items = item :: model.items
}
! []
ToggleItem id completed ->
let
update =
updateItem id (\item -> { item | completed = completed })
in
{ model | items = List.map update model.items } ! []
EditingItem id editing ->
let
update =
updateItem id (\item -> { item | editing = editing })
action =
if editing then
Task.attempt (always None)
(Dom.focus ("item-editing-" ++ (toString id)))
else
Cmd.none
in
{ model | items = List.map update model.items } ! [ action ]
UpdateItem id str ->
let
update =
updateItem id (\item -> { item | title = str })
in
{ model | items = List.map update model.items } ! []
updateItem : Id -> (Item -> Item) -> Item -> Item
updateItem id fn item =
if item.id == id then
fn item
else
item
-- view
view : Model -> Html Msg
view model =
main_ []
[ viewInput model
, viewItems model
]
viewInput : Model -> Html Msg
viewInput { title } =
div [ class "item-input" ]
[ input
[ onInput UpdateTitle
, onEnter CreateItem
, value title
]
[]
]
viewItems : Model -> Html Msg
viewItems model =
let
items =
model.items
|> List.partition (not << .completed)
|> (\( x, y ) -> [ x, y ])
|> List.concat
in
Keyed.ul [ class "item-list" ] (List.map viewKeyedItem items)
viewKeyedItem : Item -> ( String, Html Msg )
viewKeyedItem item =
let
partial =
if item.editing then
viewItemForm item
else
viewItem item
in
( (toString item.id), partial )
viewItemForm : Item -> Html Msg
viewItemForm item =
li
[ class "item item-form" ]
[ input
[ type_ "text"
, id ("item-editing-" ++ (toString item.id))
, value item.title
, onBlur (EditingItem item.id False)
, onInput (UpdateItem item.id)
, onEnter (EditingItem item.id False)
]
[]
]
viewItem : Item -> Html Msg
viewItem item =
li
[ classList
[ ( "item", True )
, ( "is-completed", item.completed )
]
]
[ viewItemCheckbox item
, div
[ class "item-title"
, onDoubleClick (EditingItem item.id True)
]
[ text item.title ]
]
viewItemCheckbox : Item -> Html Msg
viewItemCheckbox item =
let
identifier =
"item-completed-" ++ (toString item.id)
toggle bool =
ToggleItem item.id bool
in
label [ for identifier, class "item-checkbox" ]
[ input
[ type_ "checkbox"
, id identifier
, checked item.completed
, onCheck toggle
]
[]
]
-- misc
onEnter : Msg -> Attribute Msg
onEnter msg =
let
isEnter code =
if code == 13 then
Json.succeed msg
else
Json.fail "Not enter"
in
on "keydown" (Json.andThen isEnter keyCode)
-- main
main : Program Never Model Msg
main =
Html.program
{ init = ( init, Cmd.none )
, update = update
, view = view
, subscriptions = always Sub.none
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment