Commit 41eaaadf authored by Eduard Kyvenko's avatar Eduard Kyvenko Committed by Sam Saccone

Updated elm example to 0.17.1 (#1667)

* Updated elm example to 0.17.1

* Excluded elm compiled assets and packages from jscs

* Removed focus port
parent 07d0b47c
......@@ -38,6 +38,8 @@
"examples/ember-cli/todomvc/dist/**/*.js",
"examples/ember-cli/todomvc/testem.js",
"examples/ember-cli/assets/**/*.js",
"examples/elm/build/elm.js",
"examples/elm/elm-stuff/**",
"examples/humble/js/**",
"examples/js_of_ocaml/js/*.js",
"examples/polymer/elements/elements.build.js",
......
node_modules/todomvc-app-css/*
elm-stuff
node_modules/todomvc-app-css
!node_modules/todomvc-app-css/index.css
node_modules/todomvc-common/*
node_modules/todomvc-common
!node_modules/todomvc-common/base.css
!node_modules/todomvc-common/base.js
module Task where
import Html (..)
import Html.Attributes (..)
import Html.Events (..)
import Json.Decode as Json
import LocalChannel as LC
import Maybe
import Signal
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 Action
= Focus
| Edit String
| Cancel
| Commit
| Completed Bool
| Delete
update : Action -> Model -> Maybe Model
update update task =
case update of
Focus ->
Just { task | edits <- Just task.description }
Edit description ->
Just { task | edits <- Just description }
Cancel ->
Just { task | edits <- Nothing }
Commit ->
case task.edits of
Nothing ->
Just task
Just rawDescription ->
let description = String.trim rawDescription in
if String.isEmpty description then Nothing else
Just
{ task |
edits <- Nothing,
description <- description
}
Completed bool ->
Just { task | completed <- bool }
Delete ->
Nothing
-- VIEW
view : LC.LocalChannel (Int, Action) -> Model -> Html
view channel task =
let className =
(if task.completed then "completed " else "") ++
case task.edits of
Just _ -> "editing"
Nothing -> ""
description =
Maybe.withDefault task.description task.edits
in
li
[ class className ]
[ div
[ class "view" ]
[ input
[ class "toggle"
, type' "checkbox"
, checked task.completed
, onClick (LC.send channel (task.id, Completed (not task.completed)))
]
[]
, label
[ onDoubleClick (LC.send channel (task.id, Focus)) ]
[ text description ]
, button
[ class "destroy"
, onClick (LC.send channel (task.id, Delete))
]
[]
]
, input
[ class "edit"
, value description
, name "title"
, id ("todo-" ++ toString task.id)
, on "input" targetValue (\desc -> LC.send channel (task.id, Edit desc))
, onBlur (LC.send channel (task.id, Commit))
, onFinish
(LC.send channel (task.id, Commit))
(LC.send channel (task.id, Cancel))
]
[]
]
onFinish : Signal.Message -> Signal.Message -> Attribute
onFinish enterMessage escapeMessage =
let select key =
case key of
13 -> Ok enterMessage
27 -> Ok escapeMessage
_ -> Err "Not a 'finish' key, such as ENTER or ESCAPE"
in
on "keydown" (Json.customDecoder keyCode select) identity
module Todo where
port module Todo exposing (..)
{-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering.
This application is broken up into four distinct parts:
......@@ -6,40 +7,41 @@ 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
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
http://guide.elm-lang.org/architecture/
-}
import Html (..)
import Html.Attributes (..)
import Html.Events (..)
import Html.Lazy (lazy, lazy2)
import List
import LocalChannel as LC
import Maybe
import Signal
import String
import Dom
import Task
import Window
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 Task.Model
, field : String
, uid : Int
{ tasks : List Todo.Task.Model
, field : String
, uid : Int
, visibility : String
}
type alias Flags =
Maybe Model
emptyModel : Model
emptyModel =
{ tasks = []
......@@ -49,221 +51,340 @@ emptyModel =
}
-- UPDATE
-- 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://elm-lang.org/learn/Architecture.elm
type Action
-- some alternatives: http://guide.elm-lang.org/architecture/
type Msg
= NoOp
| UpdateField String
| Add
| UpdateTask (Int, Task.Action)
| UpdateTask ( Int, Todo.Task.Msg )
| DeleteComplete
| CheckAll Bool
| ChangeVisibility String
-- How we update our Model on any given Action
update : Action -> Model -> Model
update action model =
case action of
NoOp -> model
UpdateField str ->
{ model | field <- str }
Add ->
let description = String.trim model.field in
if String.isEmpty description then model else
{ model |
uid <- model.uid + 1,
field <- "",
tasks <- model.tasks ++ [Task.init description model.uid]
}
UpdateTask (id, taskAction) ->
let updateTask t =
if t.id == id then Task.update taskAction t else Just t
in
{ model | tasks <- List.filterMap updateTask model.tasks }
-- 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)
DeleteComplete ->
{ model | tasks <- List.filter (not << .completed) model.tasks }
CheckAll bool ->
let updateTask t = { t | completed <- bool }
in { model | tasks <- List.map updateTask model.tasks }
ChangeVisibility visibility ->
{ model | visibility <- visibility }
-- VIEW
view : Model -> Html
view : Model -> Html Msg
view model =
div
[ class "todomvc-wrapper"
, style [ ("visibility", "hidden") ]
]
[ section
[ id "todoapp" ]
[ lazy taskEntry model.field
, lazy2 taskList model.visibility model.tasks
, lazy2 controls model.visibility model.tasks
]
, infoFooter
]
taskEntry : String -> Html
[ 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
[ 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.send actions << UpdateField)
, Task.onFinish (Signal.send actions Add) (Signal.send actions NoOp)
]
[]
]
taskList : String -> List Task.Model -> Html
[ 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 =
let
isVisible todo =
case visibility of
"Completed" -> todo.completed
"Active" -> not todo.completed
"All" -> True
"Completed" ->
todo.completed
"Active" ->
not todo.completed
-- "All"
_ ->
True
allCompleted = List.all .completed tasks
allCompleted =
List.all .completed tasks
cssVisibility = if List.isEmpty tasks then "hidden" else "visible"
cssVisibility =
if List.isEmpty tasks then
"hidden"
else
"visible"
in
section
[ id "main"
, style [ ("visibility", cssVisibility) ]
]
[ input
[ id "toggle-all"
, type' "checkbox"
, name "toggle"
, checked allCompleted
, onClick (Signal.send actions (CheckAll (not allCompleted)))
]
[]
, label
[ for "toggle-all" ]
[ text "Mark all as complete" ]
, ul
[ id "todo-list" ]
(List.map (Task.view taskActions) (List.filter isVisible tasks))
]
controls : String -> List Task.Model -> Html
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" ]
, 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"
let
tasksCompleted =
List.length (List.filter .completed tasks)
tasksLeft =
List.length tasks - tasksCompleted
item_ =
if tasksLeft == 1 then
" item"
else
" items"
in
footer
[ id "footer"
, hidden (List.isEmpty tasks)
]
[ span
[ id "todo-count" ]
[ strong [] [ text (toString tasksLeft) ]
, text (item_ ++ " left")
]
, ul
[ id "filters" ]
[ visibilitySwap "#/" "All" visibility
, text " "
, visibilitySwap "#/active" "Active" visibility
, text " "
, visibilitySwap "#/completed" "Completed" visibility
]
, button
[ class "clear-completed"
, id "clear-completed"
, hidden (tasksCompleted == 0)
, onClick (Signal.send actions DeleteComplete)
]
[ text ("Clear completed (" ++ toString tasksCompleted ++ ")") ]
]
visibilitySwap : String -> String -> String -> Html
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 (Signal.send actions (ChangeVisibility visibility)) ]
[ a [ class className, href uri ] [ text visibility ] ]
let
className =
if visibility == actualVisibility then
"selected"
else
""
in
li
[ onClick (ChangeVisibility visibility) ]
[ a [ class className, href uri ] [ text visibility ] ]
infoFooter : Html
infoFooter : Html msg
infoFooter =
footer [ id "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" ]
]
]
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" ]
]
]
-- SIGNALS
-- wire the entire application together
main : Signal Html
main : Program Flags
main =
Signal.map view model
Navigation.programWithFlags urlParser
{ urlUpdate = urlUpdate
, view = view
, init = init
, update = update
, subscriptions = subscriptions
}
-- manage the model of our application over time
model : Signal Model
model =
Signal.foldp update initialModel allActions
-- URL PARSERS - check out evancz/url-parser for fancier URL parsing
initialModel : Model
initialModel =
Maybe.withDefault emptyModel savedModel
toUrl : String -> String
toUrl visibility =
"#/" ++ String.toLower visibility
allActions : Signal Action
allActions =
Signal.merge
(Signal.subscribe actions)
(Signal.map ChangeVisibility route)
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
-- interactions with localStorage
port savedModel : Maybe Model
port save : Signal Model
port save = model
urlParser : Parser (Maybe String)
urlParser =
Navigation.makeParser (fromUrl << .hash)
-- routing
port route : Signal String
-- actions from user input
actions : Signal.Channel Action
{-| 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
taskActions : LC.LocalChannel (Int, Task.Action)
Nothing ->
update (ChangeVisibility "All") model
port focus : Signal (Maybe Int)
port focus =
let toSelector action =
case action of
UpdateTask (id, Task.Focus) -> Just id
_ -> Nothing
in
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)
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -8,8 +8,11 @@
],
"exposed-modules": [],
"dependencies": {
"elm-lang/core": "1.0.0 <= v < 2.0.0",
"evancz/elm-html": "1.0.0 <= v < 2.0.0",
"evancz/local-channel": "1.0.0 <= v < 2.0.0"
}
}
\ No newline at end of file
"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"
}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -16,47 +16,17 @@
</head>
<body>
<script src="elm.js"></script>
<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.fullscreen(Elm.Todo, {
savedModel: savedModel,
route: getRoute()
});
var todomvc = Elm.Todo.fullscreen(savedModel);
todomvc.ports.save.subscribe(function (model) {
localStorage.setItem('elm-todo-model', JSON.stringify(model));
});
// Routing
window.addEventListener('popstate', function () {
todomvc.ports.route.send(getRoute());
}, false);
function getRoute() {
var hash = location.href.split('#')[1] || '';
var route = hash.replace('/', '');
if (['all', 'active', 'completed'].indexOf(route) >= 0) {
return route[0].toUpperCase() + route.substr(1);
}
return 'All';
}
// Setting focus manually
todomvc.ports.focus.subscribe(function (id) {
setTimeout(function () {
if (id === null) {
return;
}
var node = document.getElementById('todo-' + id);
if (document.activeElement !== node) {
node.focus();
}
}, 50);
});
}());
</script>
<script async src="node_modules/todomvc-common/base.js"></script>
......
......@@ -15,12 +15,9 @@ button {
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
-ms-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
......@@ -32,22 +29,19 @@ body {
max-width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 300;
}
button,
input[type="checkbox"] {
outline: none;
:focus {
outline: 0;
}
.hidden {
display: none;
}
#todoapp {
.todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
......@@ -55,25 +49,25 @@ input[type="checkbox"] {
0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
#todoapp input::-webkit-input-placeholder {
.todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp input::-moz-placeholder {
.todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp input::input-placeholder {
.todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
#todoapp h1 {
.todoapp h1 {
position: absolute;
top: -155px;
width: 100%;
......@@ -83,11 +77,10 @@ input[type="checkbox"] {
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
#new-todo,
.new-todo,
.edit {
position: relative;
margin: 0;
......@@ -97,27 +90,23 @@ input[type="checkbox"] {
font-weight: inherit;
line-height: 1.4em;
border: 0;
outline: none;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-ms-box-sizing: border-box;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#new-todo {
.new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
}
#main {
.main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
......@@ -127,7 +116,7 @@ label[for='toggle-all'] {
display: none;
}
#toggle-all {
.toggle-all {
position: absolute;
top: -55px;
left: -12px;
......@@ -137,50 +126,50 @@ label[for='toggle-all'] {
border: none; /* Mobile Safari */
}
#toggle-all:before {
.toggle-all:before {
content: '❯';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
#toggle-all:checked:before {
.toggle-all:checked:before {
color: #737373;
}
#todo-list {
.todo-list {
margin: 0;
padding: 0;
list-style: none;
}
#todo-list li {
.todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
#todo-list li:last-child {
.todo-list li:last-child {
border-bottom: none;
}
#todo-list li.editing {
.todo-list li.editing {
border-bottom: none;
padding: 0;
}
#todo-list li.editing .edit {
.todo-list li.editing .edit {
display: block;
width: 506px;
padding: 13px 17px 12px 17px;
padding: 12px 16px;
margin: 0 0 0 43px;
}
#todo-list li.editing .view {
.todo-list li.editing .view {
display: none;
}
#todo-list li .toggle {
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
......@@ -191,20 +180,18 @@ label[for='toggle-all'] {
margin: auto 0;
border: none; /* Mobile Safari */
-webkit-appearance: none;
-ms-appearance: none;
appearance: none;
}
#todo-list li .toggle:after {
.todo-list li .toggle:after {
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
}
#todo-list li .toggle:checked:after {
.todo-list li .toggle:checked:after {
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
}
#todo-list li label {
white-space: pre-line;
.todo-list li label {
word-break: break-all;
padding: 15px 60px 15px 15px;
margin-left: 45px;
......@@ -213,12 +200,12 @@ label[for='toggle-all'] {
transition: color 0.4s;
}
#todo-list li.completed label {
.todo-list li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
#todo-list li .destroy {
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
......@@ -233,27 +220,27 @@ label[for='toggle-all'] {
transition: color 0.2s ease-out;
}
#todo-list li .destroy:hover {
.todo-list li .destroy:hover {
color: #af5b5e;
}
#todo-list li .destroy:after {
.todo-list li .destroy:after {
content: '×';
}
#todo-list li:hover .destroy {
.todo-list li:hover .destroy {
display: block;
}
#todo-list li .edit {
.todo-list li .edit {
display: none;
}
#todo-list li.editing:last-child {
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
#footer {
.footer {
color: #777;
padding: 10px 15px;
height: 20px;
......@@ -261,7 +248,7 @@ label[for='toggle-all'] {
border-top: 1px solid #e6e6e6;
}
#footer:before {
.footer:before {
content: '';
position: absolute;
right: 0;
......@@ -276,16 +263,16 @@ label[for='toggle-all'] {
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
#todo-count {
.todo-count {
float: left;
text-align: left;
}
#todo-count strong {
.todo-count strong {
font-weight: 300;
}
#filters {
.filters {
margin: 0;
padding: 0;
list-style: none;
......@@ -294,11 +281,11 @@ label[for='toggle-all'] {
left: 0;
}
#filters li {
.filters li {
display: inline;
}
#filters li a {
.filters li a {
color: inherit;
margin: 3px;
padding: 3px 7px;
......@@ -307,39 +294,28 @@ label[for='toggle-all'] {
border-radius: 3px;
}
#filters li a.selected,
#filters li a:hover {
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
#filters li a.selected {
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
#clear-completed,
html #clear-completed:active {
.clear-completed,
html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
cursor: pointer;
visibility: hidden;
position: relative;
}
#clear-completed::after {
visibility: visible;
content: 'Clear completed';
position: absolute;
right: 0;
white-space: nowrap;
}
#clear-completed:hover::after {
.clear-completed:hover {
text-decoration: underline;
}
#info {
.info {
margin: 65px auto 0;
color: #bfbfbf;
font-size: 10px;
......@@ -347,17 +323,17 @@ html #clear-completed:active {
text-align: center;
}
#info p {
.info p {
line-height: 1;
}
#info a {
.info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
#info a:hover {
.info a:hover {
text-decoration: underline;
}
......@@ -366,16 +342,16 @@ html #clear-completed:active {
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all,
#todo-list li .toggle {
.toggle-all,
.todo-list li .toggle {
background: none;
}
#todo-list li .toggle {
.todo-list li .toggle {
height: 40px;
}
#toggle-all {
.toggle-all {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
......@@ -384,11 +360,11 @@ html #clear-completed:active {
}
@media (max-width: 430px) {
#footer {
.footer {
height: 50px;
}
#filters {
.filters {
bottom: 10px;
}
}
......@@ -114,7 +114,12 @@
})({});
if (location.hostname === 'todomvc.com') {
window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script'));
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-31081062-1', 'auto');
ga('send', 'pageview');
}
/* jshint ignore:end */
......@@ -228,7 +233,7 @@
xhr.onload = function (e) {
var parsedResponse = JSON.parse(e.target.responseText);
if (parsedResponse instanceof Array) {
var count = parsedResponse.length
var count = parsedResponse.length;
if (count !== 0) {
issueLink.innerHTML = 'This app has ' + count + ' open issues';
document.getElementById('issue-count').style.display = 'inline';
......
{
"private": true,
"dependencies": {
"todomvc-app-css": "^1.0.0",
"todomvc-common": "^1.0.1"
"todomvc-app-css": "^2.0.6",
"todomvc-common": "^1.0.2"
}
}
# Elm TodoMVC Example
> A functional reactive language for interactive applications
> A functional language for interactive applications
> _[Elm](http://elm-lang.org/)_
......@@ -14,7 +14,7 @@ Here are some links you may find helpful:
* [Try Elm](http://elm-lang.org/try)
* [Learn Elm](http://elm-lang.org/Learn.elm)
* [Elm Snippets](http://www.share-elm.com/)
* [An Introduction to Elm](http://guide.elm-lang.org/)
Get help from other Elm users:
......@@ -28,25 +28,25 @@ _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 `Task.elm` and relies
on the [elm-html][] library.
All of the Elm code lives in `Todo.elm` and `Todo/Task.elm` and relies
on the [elm-html][] and [elm-navigation][] packages.
[elm-html]: http://library.elm-lang.org/catalog/evancz-elm-html/latest
[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.
## Build Instructions
You need to install
[elm](https://github.com/elm-lang/elm-platform/blob/master/README.md#elm-platform)
You need to install [elm](http://elm-lang.org/install)
on your machine first.
Run the following commands from the root of this project:
```bash
elm-package install
elm-make Todo.elm --output build/Todo.js
elm-package install -y
elm-make Todo.elm --output build/elm.js
```
Then open `index.html` in your browser!
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment