Created
July 4, 2017 13:28
-
-
Save anonymous/6e1df73bd0cc16443d87c58647eead17 to your computer and use it in GitHub Desktop.
Todo list
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "version": "1.0.0", | |
| "summary": "Attempt to write own version of todo list (WiP)\n", | |
| "repository": "https://github.com/user/project.git", | |
| "license": "BSD3", | |
| "source-directories": [ | |
| "." | |
| ], | |
| "exposed-modules": [], | |
| "dependencies": { | |
| "elm-lang/core": "5.1.1 <= v < 5.1.1", | |
| "elm-lang/html": "2.0.0 <= v < 2.0.0" | |
| }, | |
| "elm-version": "0.18.0 <= v < 0.19.0" | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <html> | |
| <head> | |
| <style> | |
| body { | |
| background: #fff; | |
| font-size: 16px; | |
| font-family: Consolas, "monospace"; | |
| } | |
| </style> | |
| <style id="has-float-label"> | |
| .has-float-label { | |
| display: -webkit-box; | |
| display: -ms-flexbox; | |
| display: flex; | |
| position: relative; | |
| } | |
| .has-float-label label, .has-float-label > span { | |
| position: absolute; | |
| left: 5px; | |
| top: 1px; | |
| cursor: text; | |
| font-size: .75em; | |
| opacity: 1; | |
| -webkit-transition: all .2s; | |
| transition: all .2s; | |
| } | |
| .has-float-label select { | |
| -webkit-appearance: none; | |
| -moz-appearance: none; | |
| appearance: none; | |
| } | |
| .has-float-label input, .has-float-label select { | |
| font-size: inherit; | |
| padding: 0 .5em; | |
| padding-top: 1em; | |
| margin-bottom: 2px; | |
| border: 0; | |
| border-radius: 0; | |
| border-bottom: 2px solid rgba(0, 0, 0, 0.1); | |
| } | |
| .has-float-label input::-webkit-input-placeholder, .has-float-label select::-webkit-input-placeholder { | |
| opacity: 1; | |
| -webkit-transition: all .2s; | |
| transition: all .2s; | |
| } | |
| .has-float-label input:placeholder-shown:not(:focus)::-webkit-input-placeholder, .has-float-label select:placeholder-shown:not(:focus)::-webkit-input-placeholder { | |
| opacity: 0; | |
| } | |
| .has-float-label input:placeholder-shown:not(:focus) + *:not(.input-has-content), .has-float-label select:placeholder-shown:not(:focus) + *:not(.input-has-content) { | |
| font-size: 1.3em; | |
| opacity: .5; | |
| pointer-events: none; | |
| top: .25em; | |
| left: .5em; | |
| } | |
| .has-float-label input:focus, .has-float-label select:focus { | |
| outline: none; | |
| border-color: rgba(0, 0, 0, 0.5); | |
| } | |
| .has-float-label.input-list-container input { | |
| padding-bottom: 0.3em; | |
| } | |
| .has-float-label.input-list-container input:placeholder-shown:not(:focus) + *:not(.input-has-content) { | |
| font-size: 1.3em; | |
| opacity: .5; | |
| pointer-events: none; | |
| top: 1em; | |
| } | |
| .has-float-label.select-container::after { | |
| content: "\2335"; | |
| position: absolute; | |
| top: 50%; | |
| right: 5px; | |
| -webkit-transform: translateY(-50%); | |
| transform: translateY(-50%); | |
| font-weight: bold; | |
| pointer-events: none; | |
| } | |
| </style> | |
| <style> | |
| .tickbox { | |
| display: -webkit-box; | |
| display: -ms-flexbox; | |
| display: flex; | |
| -webkit-box-pack: start; | |
| -ms-flex-pack: start; | |
| justify-content: flex-start; | |
| -webkit-box-align: center; | |
| -ms-flex-align: center; | |
| align-items: center; | |
| padding: 2px; | |
| cursor: pointer; | |
| } | |
| .tickbox input[type="checkbox"] { | |
| -webkit-appearance: none; | |
| -moz-appearance: none; | |
| appearance: none; | |
| -webkit-transition: all 0.3s; | |
| transition: all 0.3s; | |
| position: relative; | |
| display: -webkit-box; | |
| display: -ms-flexbox; | |
| display: flex; | |
| -webkit-box-pack: center; | |
| -ms-flex-pack: center; | |
| justify-content: center; | |
| -webkit-box-align: center; | |
| -ms-flex-align: center; | |
| align-items: center; | |
| width: 20px; | |
| height: 20px; | |
| margin: 0 5px; | |
| } | |
| .tickbox input:before, | |
| .tickbox input:checked:before { | |
| color: #000; | |
| -webkit-transition: all 0.3s; | |
| transition: all 0.3s; | |
| cursor: pointer; | |
| z-index: 1; | |
| } | |
| .tickbox input:before { | |
| content: "\2610"; | |
| font-size: 2em; | |
| } | |
| .tickbox input:disabled:before { | |
| content: "\274c"; | |
| font-size: 1em; | |
| color: #444; | |
| } | |
| .tickbox input:checked:before { | |
| content: "\2611"; | |
| color: #0f0; | |
| } | |
| </style> | |
| <style> | |
| ul { | |
| list-style-type: none; | |
| padding: 5px; | |
| margin: 5px 0; | |
| } | |
| </style> | |
| <style> | |
| input[type="text"] { | |
| width: 300px; | |
| max-width: 100%; | |
| } | |
| input[type="checkbox"]:checked + span { | |
| text-decoration: line-through; | |
| } | |
| .filters > .selected { | |
| color: #ccc; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <script> | |
| var app = Elm.Main.fullscreen() | |
| </script> | |
| </body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module Main exposing (..) | |
| import Html exposing (Html, Attribute, div, ul, label, span, li, a, input, text) | |
| import Html.Attributes exposing (..) | |
| import Html.Events exposing (on, onInput, onClick, keyCode) | |
| import Tuple exposing (first, second) | |
| import Json.Decode as Json | |
| main = | |
| Html.beginnerProgram | |
| { model = model | |
| , view = view | |
| , update = update | |
| } | |
| -- MODEL | |
| type alias Task = | |
| { id : Int | |
| , description : String | |
| , isComplete : Bool | |
| } | |
| type alias Model = | |
| { uid : Int | |
| , description : String | |
| , tasks : List Task | |
| , visibilityFilter : String | |
| } | |
| model : Model | |
| model = | |
| Model 3 "" initStartTasks "all" | |
| initStartTasks : List Task | |
| initStartTasks = | |
| List.map mapTask startingTasks | |
| startingTasks : List ( Int, String ) | |
| startingTasks = | |
| [ ( 0, "Buy milk" ) | |
| , ( 1, "Read paper" ) | |
| , ( 2, "Post letter" ) | |
| ] | |
| mapTask : ( Int, String ) -> Task | |
| mapTask obj = | |
| taskInit (first obj) (second obj) | |
| taskInit : Int -> String -> Task | |
| taskInit id description = | |
| { id = id | |
| , description = description | |
| , isComplete = False | |
| } | |
| -- UPDATE | |
| type Msg | |
| = DescriptionChange String | |
| | Add | |
| | ApplyFilter String | |
| | ToggleStatus Int Bool | |
| update : Msg -> Model -> Model | |
| update msg model = | |
| case msg of | |
| DescriptionChange newContent -> | |
| { model | description = newContent } | |
| Add -> | |
| { model | |
| | uid = model.uid + 1 | |
| , description = "" | |
| , tasks = | |
| if String.isEmpty model.description then | |
| model.tasks | |
| else | |
| model.tasks ++ [ taskInit model.uid model.description ] | |
| } | |
| ApplyFilter filter -> | |
| { model | visibilityFilter = filter } | |
| ToggleStatus id isCompleted -> | |
| let | |
| updateEntry t = | |
| if t.id == id then | |
| { t | isComplete = isCompleted } | |
| else | |
| t | |
| in | |
| { model | tasks = List.map updateEntry model.tasks } | |
| -- VIEW | |
| view : Model -> Html Msg | |
| view model = | |
| div [ id "root" ] | |
| [ viewInputBox model | |
| , div [] [ viewTasks model ] | |
| , div [] [ viewFilters model.visibilityFilter ] | |
| ] | |
| viewInputBox : Model -> Html Msg | |
| viewInputBox model = | |
| div [ class "has-float-label" ] | |
| [ input [ type_ "text", placeholder " ", value model.description, onInput DescriptionChange, onEnter Add ] [] | |
| , label [] [ text "What do you need to do?" ] | |
| ] | |
| viewTasks : Model -> Html Msg | |
| viewTasks model = | |
| let | |
| liItems = | |
| List.map viewTaskItem (List.filter (taskFilter model.visibilityFilter) model.tasks) | |
| in | |
| ul [ id "task-list" ] | |
| ([] ++ liItems) | |
| taskFilter filter = | |
| filterTasks filter | |
| filterTasks : String -> Task -> Bool | |
| filterTasks filter task = | |
| case filter of | |
| "active" -> | |
| not task.isComplete | |
| "complete" -> | |
| task.isComplete | |
| _ -> | |
| True | |
| viewTaskItem : Task -> Html Msg | |
| viewTaskItem task = | |
| li [ class "list-item", id ("task-" ++ toString task.id) ] | |
| [ label [ class "list-item__checkbox tickbox" ] | |
| [ input [ type_ "checkbox", checked task.isComplete, onClick (ToggleStatus task.id (not task.isComplete)) ] [] | |
| , span [] [ text task.description ] | |
| ] | |
| ] | |
| viewFilters : String -> Html Msg | |
| viewFilters filter = | |
| div [ class "filters" ] | |
| [ visibilityFilter "all" filter | |
| , text " " | |
| , visibilityFilter "active" filter | |
| , text " " | |
| , visibilityFilter "complete" filter | |
| ] | |
| visibilityFilter: String -> String -> Html Msg | |
| visibilityFilter filter activeFilter = | |
| a [ href ("#/" ++ filter), classList [("selected", filter == activeFilter)], onClick (ApplyFilter filter) ] | |
| [ text (capitalise filter) ] | |
| capitalise: String -> String | |
| capitalise str = | |
| (String.toUpper (String.left 1 str)) ++ String.slice 1 (String.length str) str | |
| 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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment