Skip to content
Snippets Groups Projects
Commit 9f6e22cc authored by Razzeee's avatar Razzeee Committed by Fady Samir Sadek
Browse files

Update elm to version 0.19 (#1954)

* Update elm to version 0.19
parent f9196709
No related branches found
No related tags found
No related merge requests found
Copyright (c) 2014-2016, Evan Czaplicki
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
port module Todo exposing (..)
{-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering.
This application is broken up into four distinct parts:
1. Model - a full description of the application as data
2. Update - a way to update the model based on user actions
3. View - a way to visualize our model with HTML
This program is not particularly large, so definitely see the following
document for notes on structuring more complex GUIs with Elm:
http://guide.elm-lang.org/architecture/
-}
import Dom
import Task
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Html.Lazy exposing (lazy, lazy2)
import Html.App
import Navigation exposing (Parser)
import String
import String.Extra
import Todo.Task
-- MODEL
-- The full application state of our todo app.
type alias Model =
{ tasks : List Todo.Task.Model
, field : String
, uid : Int
, visibility : String
}
type alias Flags =
Maybe Model
emptyModel : Model
emptyModel =
{ tasks = []
, visibility = "All"
, field = ""
, uid = 0
}
-- UPDATE
-- A description of the kinds of actions that can be performed on the model of
-- our application. See the following post for more info on this pattern and
-- some alternatives: http://guide.elm-lang.org/architecture/
type Msg
= NoOp
| UpdateField String
| Add
| UpdateTask ( Int, Todo.Task.Msg )
| DeleteComplete
| CheckAll Bool
| ChangeVisibility String
-- How we update our Model on any given Message
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case Debug.log "MESSAGE: " msg of
NoOp ->
( model, Cmd.none )
UpdateField str ->
let
newModel =
{ model | field = str }
in
( newModel, save model )
Add ->
let
description =
String.trim model.field
newModel =
if String.isEmpty description then
model
else
{ model
| uid = model.uid + 1
, field = ""
, tasks = model.tasks ++ [ Todo.Task.init description model.uid ]
}
in
( newModel, save newModel )
UpdateTask ( id, taskMsg ) ->
let
updateTask t =
if t.id == id then
Todo.Task.update taskMsg t
else
Just t
newModel =
{ model | tasks = List.filterMap updateTask model.tasks }
in
case taskMsg of
Todo.Task.Focus elementId ->
newModel ! [ save newModel, focusTask elementId ]
_ ->
( newModel, save newModel )
DeleteComplete ->
let
newModel =
{ model
| tasks = List.filter (not << .completed) model.tasks
}
in
( newModel, save newModel )
CheckAll bool ->
let
updateTask t =
{ t | completed = bool }
newModel =
{ model | tasks = List.map updateTask model.tasks }
in
( newModel, save newModel )
ChangeVisibility visibility ->
let
newModel =
{ model | visibility = visibility }
in
( newModel, save model )
focusTask : String -> Cmd Msg
focusTask elementId =
Task.perform (\_ -> NoOp) (\_ -> NoOp) (Dom.focus elementId)
-- VIEW
view : Model -> Html Msg
view model =
div
[ class "todomvc-wrapper"
, style [ ( "visibility", "hidden" ) ]
]
[ section
[ class "todoapp" ]
[ lazy taskEntry model.field
, lazy2 taskList model.visibility model.tasks
, lazy2 controls model.visibility model.tasks
]
, infoFooter
]
taskEntry : String -> Html Msg
taskEntry task =
header
[ class "header" ]
[ h1 [] [ text "todos" ]
, input
[ class "new-todo"
, placeholder "What needs to be done?"
, autofocus True
, value task
, name "newTodo"
, onInput UpdateField
, Todo.Task.onFinish Add NoOp
]
[]
]
taskList : String -> List Todo.Task.Model -> Html Msg
taskList visibility tasks =
let
isVisible todo =
case visibility of
"Completed" ->
todo.completed
"Active" ->
not todo.completed
-- "All"
_ ->
True
allCompleted =
List.all .completed tasks
cssVisibility =
if List.isEmpty tasks then
"hidden"
else
"visible"
in
section
[ class "main"
, style [ ( "visibility", cssVisibility ) ]
]
[ input
[ class "toggle-all"
, id "toggle-all"
, type' "checkbox"
, name "toggle"
, checked allCompleted
, onClick (CheckAll (not allCompleted))
]
[]
, label
[ for "toggle-all" ]
[ text "Mark all as complete" ]
, ul
[ class "todo-list" ]
(List.map
(\task ->
let
id =
task.id
taskView =
Todo.Task.view task
in
Html.App.map (\msg -> UpdateTask ( id, msg )) taskView
)
(List.filter isVisible tasks)
)
]
controls : String -> List Todo.Task.Model -> Html Msg
controls visibility tasks =
let
tasksCompleted =
List.length (List.filter .completed tasks)
tasksLeft =
List.length tasks - tasksCompleted
item_ =
if tasksLeft == 1 then
" item"
else
" items"
in
footer
[ class "footer"
, hidden (List.isEmpty tasks)
]
[ span
[ class "todo-count" ]
[ strong [] [ text (toString tasksLeft) ]
, text (item_ ++ " left")
]
, ul
[ class "filters" ]
[ visibilitySwap "#/" "All" visibility
, text " "
, visibilitySwap "#/active" "Active" visibility
, text " "
, visibilitySwap "#/completed" "Completed" visibility
]
, button
[ class "clear-completed"
, hidden (tasksCompleted == 0)
, onClick DeleteComplete
]
[ text ("Clear completed (" ++ toString tasksCompleted ++ ")") ]
]
visibilitySwap : String -> String -> String -> Html Msg
visibilitySwap uri visibility actualVisibility =
let
className =
if visibility == actualVisibility then
"selected"
else
""
in
li
[ onClick (ChangeVisibility visibility) ]
[ a [ class className, href uri ] [ text visibility ] ]
infoFooter : Html msg
infoFooter =
footer
[ class "info" ]
[ p [] [ text "Double-click to edit a todo" ]
, p []
[ text "Written by "
, a [ href "https://github.com/evancz" ] [ text "Evan Czaplicki" ]
]
, p []
[ text "Part of "
, a [ href "http://todomvc.com" ] [ text "TodoMVC" ]
]
]
-- wire the entire application together
main : Program Flags
main =
Navigation.programWithFlags urlParser
{ urlUpdate = urlUpdate
, view = view
, init = init
, update = update
, subscriptions = subscriptions
}
-- URL PARSERS - check out evancz/url-parser for fancier URL parsing
toUrl : String -> String
toUrl visibility =
"#/" ++ String.toLower visibility
fromUrl : String -> Maybe String
fromUrl hash =
let
cleanHash =
String.dropLeft 2 hash
in
if (List.member cleanHash [ "all", "active", "completed" ]) == True then
Just cleanHash
else
Nothing
urlParser : Parser (Maybe String)
urlParser =
Navigation.makeParser (fromUrl << .hash)
{-| The URL is turned into a Maybe value. If the URL is valid, we just update
our model with the new visibility settings. If it is not a valid URL,
we set the visibility filter to show all tasks.
-}
urlUpdate : Maybe String -> Model -> ( Model, Cmd Msg )
urlUpdate result model =
case result of
Just visibility ->
update (ChangeVisibility (String.Extra.toSentenceCase visibility)) model
Nothing ->
update (ChangeVisibility "All") model
init : Flags -> Maybe String -> ( Model, Cmd Msg )
init flags url =
urlUpdate url (Maybe.withDefault emptyModel flags)
-- interactions with localStorage
port save : Model -> Cmd msg
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
module Todo.Task exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Json.Decode
import String
-- MODEL
type alias Model =
{ description : String
, completed : Bool
, edits : Maybe String
, id : Int
}
init : String -> Int -> Model
init desc id =
{ description = desc
, completed = False
, edits = Nothing
, id = id
}
-- UPDATE
type Msg
= Focus String
| Edit String
| Cancel
| Commit
| Completed Bool
| Delete
update : Msg -> Model -> Maybe Model
update msg model =
case msg of
Focus elementId ->
Just { model | edits = Just model.description }
Edit description ->
Just { model | edits = Just description }
Cancel ->
Just { model | edits = Nothing }
Commit ->
case model.edits of
Nothing ->
Just model
Just rawDescription ->
let
description =
String.trim rawDescription
in
if String.isEmpty description then
Nothing
else
Just
{ model
| edits = Nothing
, description = description
}
Completed bool ->
Just { model | completed = bool }
Delete ->
Nothing
-- VIEW
view : Model -> Html Msg
view model =
let
className =
(if model.completed then
"completed "
else
""
)
++ case model.edits of
Just _ ->
"editing"
Nothing ->
""
description =
Maybe.withDefault model.description model.edits
elementId =
"todo-" ++ toString model.id
in
li
[ class className ]
[ div
[ class "view" ]
[ input
[ class "toggle"
, type' "checkbox"
, checked model.completed
, onClick (Completed (not model.completed))
]
[]
, label
[ onDoubleClick (Focus elementId) ]
[ text description ]
, button
[ class "destroy"
, onClick Delete
]
[]
]
, input
[ class "edit"
, value description
, name "title"
, id (elementId)
, onInput Edit
, onBlur Commit
, onFinish Commit Cancel
]
[]
]
onFinish : msg -> msg -> Attribute msg
onFinish enterMessage escapeMessage =
let
select key =
case key of
13 ->
enterMessage
_ ->
-- Not a 'finish' key, such as ENTER or ESCAPE
escapeMessage
in
on "keydown" (Json.Decode.map select keyCode)
source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"version": "1.0.0",
"summary": "TodoMVC created with Elm and elm-html",
"repository": "https://github.com/evancz/elm-todomvc.git",
"license": "BSD3",
"source-directories": [
"."
],
"exposed-modules": [],
"dependencies": {
"elm-community/string-extra": "1.0.2 <= v < 2.0.0",
"elm-lang/core": "4.0.5 <= v < 5.0.0",
"elm-lang/dom": "1.1.0 <= v < 2.0.0",
"elm-lang/html": "1.1.0 <= v < 2.0.0",
"elm-lang/navigation": "1.0.0 <= v < 2.0.0"
},
"elm-version": "0.17.1 <= v < 0.18.0"
}
{
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"dependencies": {
"direct": {
"elm/browser": "1.0.0",
"elm/core": "1.0.0",
"elm/html": "1.0.0",
"elm/json": "1.0.0"
},
"indirect": {
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.0"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}
\ No newline at end of file
<!doctype html>
<html lang="en" data-framework="elm">
<head>
<meta charset="utf-8">
<title>Elm • TodoMVC</title>
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
<style>
body {
width: auto;
}
.todomvc-wrapper {
visibility: visible !important;
}
</style>
</head>
<head>
<meta charset="utf-8">
<title>Elm • TodoMVC</title>
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
<style>
body {
width: auto;
}
<body>
<script src="build/elm.js"></script>
<script>
(function () {
var result = localStorage.getItem('elm-todo-model');
var savedModel = result ? JSON.parse(result) : null;
var todomvc = Elm.Todo.fullscreen(savedModel);
todomvc.ports.save.subscribe(function (model) {
localStorage.setItem('elm-todo-model', JSON.stringify(model));
});
}());
</script>
<script async src="node_modules/todomvc-common/base.js"></script>
</body>
.todomvc-wrapper {
visibility: visible !important;
}
</style>
<script src="build/elm.js"></script>
</head>
<body>
<script>
(() => {
const storedState = localStorage.getItem('elm-todo-save');
const startingState = storedState ? JSON.parse(storedState) : null;
const app = Elm.Main.init({flags: startingState});
app.ports.setStorage.subscribe(state => {
localStorage.setItem('elm-todo-save', JSON.stringify(state));
});
})();
</script>
</body>
</html>
......@@ -28,11 +28,10 @@ _If you have other helpful links to share, or find any of the links above no lon
## Project Structure
All of the Elm code lives in `Todo.elm` and `Todo/Task.elm` and relies
on the [elm-html][] and [elm-navigation][] packages.
All of the Elm code lives in `Main.elm` and relies
on the [elm-html][] package.
[elm-html]: http://package.elm-lang.org/packages/elm-lang/html/latest/
[elm-navigation]: http://package.elm-lang.org/packages/elm-lang/navigation/latest/
There also is a port handler set up in `index.html` to set the focus on
particular text fields when necessary.
......@@ -45,8 +44,7 @@ on your machine first.
Run the following commands from the root of this project:
```bash
elm-package install -y
elm-make Todo.elm --output build/elm.js
elm make src/Main.elm --output build/elm.js
```
Then open `index.html` in your browser!
......
port module Main exposing (..)
{-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering.
This application is broken up into three key 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
This clean division of concerns is a core part of Elm. You can read more about
this in <http://guide.elm-lang.org/architecture/index.html>
-}
import Browser
import Browser.Dom as Dom
import Html exposing (..)
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 Task
main : Program (Maybe Model) Model Msg
main =
Browser.document
{ init = init
, view = \model -> { title = "Elm • TodoMVC", body = [view model] }
, update = updateWithStorage
, subscriptions = \_ -> Sub.none
}
port setStorage : Model -> Cmd msg
{-| We want to `setStorage` on every update. This function adds the setStorage
command for every step of the update function.
-}
updateWithStorage : Msg -> Model -> ( Model, Cmd Msg )
updateWithStorage msg model =
let
( newModel, cmds ) =
update msg model
in
( newModel
, Cmd.batch [ setStorage newModel, cmds ]
)
-- MODEL
-- The full application state of our todo app.
type alias Model =
{ entries : List Entry
, field : String
, uid : Int
, visibility : String
}
type alias Entry =
{ description : String
, completed : Bool
, editing : Bool
, id : Int
}
emptyModel : Model
emptyModel =
{ entries = []
, visibility = "All"
, field = ""
, uid = 0
}
newEntry : String -> Int -> Entry
newEntry desc id =
{ description = desc
, completed = False
, editing = False
, id = id
}
init : Maybe Model -> ( Model, Cmd Msg )
init maybeModel =
( Maybe.withDefault emptyModel maybeModel
, Cmd.none
)
-- UPDATE
{-| Users of our app can trigger messages by clicking and typing. These
messages are fed into the `update` function as they occur, letting us react
to them.
-}
type Msg
= NoOp
| UpdateField String
| EditingEntry Int Bool
| UpdateEntry Int String
| Add
| Delete Int
| DeleteComplete
| Check Int Bool
| CheckAll Bool
| ChangeVisibility String
-- How we update our Model on a given Msg?
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NoOp ->
( model, Cmd.none )
Add ->
( { model
| uid = model.uid + 1
, field = ""
, entries =
if String.isEmpty model.field then
model.entries
else
model.entries ++ [ newEntry model.field model.uid ]
}
, Cmd.none
)
UpdateField str ->
( { model | field = str }
, Cmd.none
)
EditingEntry id isEditing ->
let
updateEntry t =
if t.id == id then
{ t | editing = isEditing }
else
t
focus =
Dom.focus ("todo-" ++ String.fromInt id)
in
( { model | entries = List.map updateEntry model.entries }
, Task.attempt (\_ -> NoOp) focus
)
UpdateEntry id task ->
let
updateEntry t =
if t.id == id then
{ t | description = task }
else
t
in
( { model | entries = List.map updateEntry model.entries }
, Cmd.none
)
Delete id ->
( { model | entries = List.filter (\t -> t.id /= id) model.entries }
, Cmd.none
)
DeleteComplete ->
( { model | entries = List.filter (not << .completed) model.entries }
, Cmd.none
)
Check id isCompleted ->
let
updateEntry t =
if t.id == id then
{ t | completed = isCompleted }
else
t
in
( { model | entries = List.map updateEntry model.entries }
, Cmd.none
)
CheckAll isCompleted ->
let
updateEntry t =
{ t | completed = isCompleted }
in
( { model | entries = List.map updateEntry model.entries }
, Cmd.none
)
ChangeVisibility visibility ->
( { model | visibility = visibility }
, Cmd.none
)
-- VIEW
view : Model -> Html Msg
view model =
div
[ class "todomvc-wrapper"
, style "visibility" "hidden"
]
[ section
[ class "todoapp" ]
[ lazy viewInput model.field
, lazy2 viewEntries model.visibility model.entries
, lazy2 viewControls model.visibility model.entries
]
, infoFooter
]
viewInput : String -> Html Msg
viewInput task =
header
[ class "header" ]
[ h1 [] [ text "todos" ]
, input
[ class "new-todo"
, placeholder "What needs to be done?"
, autofocus True
, value task
, name "newTodo"
, onInput UpdateField
, onEnter Add
]
[]
]
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)
-- VIEW ALL ENTRIES
viewEntries : String -> List Entry -> Html Msg
viewEntries visibility entries =
let
isVisible todo =
case visibility of
"Completed" ->
todo.completed
"Active" ->
not todo.completed
_ ->
True
allCompleted =
List.all .completed entries
cssVisibility =
if List.isEmpty entries then
"hidden"
else
"visible"
in
section
[ class "main"
, style "visibility" cssVisibility
]
[ input
[ class "toggle-all"
, type_ "checkbox"
, name "toggle"
, checked allCompleted
, onClick (CheckAll (not allCompleted))
]
[]
, label
[ for "toggle-all" ]
[ text "Mark all as complete" ]
, Keyed.ul [ class "todo-list" ] <|
List.map viewKeyedEntry (List.filter isVisible entries)
]
-- VIEW INDIVIDUAL ENTRIES
viewKeyedEntry : Entry -> ( String, Html Msg )
viewKeyedEntry todo =
( String.fromInt todo.id, lazy viewEntry todo )
viewEntry : Entry -> Html Msg
viewEntry todo =
li
[ classList [ ( "completed", todo.completed ), ( "editing", todo.editing ) ] ]
[ div
[ class "view" ]
[ input
[ class "toggle"
, type_ "checkbox"
, checked todo.completed
, onClick (Check todo.id (not todo.completed))
]
[]
, label
[ onDoubleClick (EditingEntry todo.id True) ]
[ text todo.description ]
, button
[ class "destroy"
, onClick (Delete todo.id)
]
[]
]
, input
[ class "edit"
, value todo.description
, name "title"
, id ("todo-" ++ String.fromInt todo.id)
, onInput (UpdateEntry todo.id)
, onBlur (EditingEntry todo.id False)
, onEnter (EditingEntry todo.id False)
]
[]
]
-- VIEW CONTROLS AND FOOTER
viewControls : String -> List Entry -> Html Msg
viewControls visibility entries =
let
entriesCompleted =
List.length (List.filter .completed entries)
entriesLeft =
List.length entries - entriesCompleted
in
footer
[ class "footer"
, hidden (List.isEmpty entries)
]
[ lazy viewControlsCount entriesLeft
, lazy viewControlsFilters visibility
, lazy viewControlsClear entriesCompleted
]
viewControlsCount : Int -> Html Msg
viewControlsCount entriesLeft =
let
item_ =
if entriesLeft == 1 then
" item"
else
" items"
in
span
[ class "todo-count" ]
[ strong [] [ text (String.fromInt entriesLeft) ]
, text (item_ ++ " left")
]
viewControlsFilters : String -> Html Msg
viewControlsFilters visibility =
ul
[ class "filters" ]
[ visibilitySwap "#/" "All" visibility
, text " "
, visibilitySwap "#/active" "Active" visibility
, text " "
, visibilitySwap "#/completed" "Completed" visibility
]
visibilitySwap : String -> String -> String -> Html Msg
visibilitySwap uri visibility actualVisibility =
li
[ onClick (ChangeVisibility visibility) ]
[ a [ href uri, classList [ ( "selected", visibility == actualVisibility ) ] ]
[ text visibility ]
]
viewControlsClear : Int -> Html Msg
viewControlsClear entriesCompleted =
button
[ class "clear-completed"
, hidden (entriesCompleted == 0)
, onClick DeleteComplete
]
[ text ("Clear completed (" ++ String.fromInt entriesCompleted ++ ")")
]
infoFooter : Html msg
infoFooter =
footer [ class "info" ]
[ p [] [ text "Double-click to edit a todo" ]
, p []
[ text "Written by "
, a [ href "https://github.com/evancz" ] [ text "Evan Czaplicki" ]
]
, p []
[ text "Part of "
, a [ href "http://todomvc.com" ] [ text "TodoMVC" ]
]
]
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment