elm(todolist): feat: basic todo operations
Signed-off-by: prescientmoon <git@moonythm.dev>
This commit is contained in:
parent
8cd06f56bd
commit
5278cf694d
6
elm/todolist/elm-analyse.json
Normal file
6
elm/todolist/elm-analyse.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"checks": {
|
||||||
|
"ExposeAll": false,
|
||||||
|
"ImportAll": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,36 @@
|
||||||
module Main exposing (..)
|
module Main exposing (..)
|
||||||
|
|
||||||
import Browser
|
import Browser
|
||||||
import Html exposing (Html, text, div, h1, img)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (src)
|
import Html.Attributes exposing (..)
|
||||||
|
import Html.Events exposing (..)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- HELPER TYPES
|
||||||
|
|
||||||
|
|
||||||
|
type alias Todo =
|
||||||
|
{ done : Bool
|
||||||
|
, text : String
|
||||||
|
, id : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---- MODEL ----
|
---- MODEL ----
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{}
|
{ todos : List Todo
|
||||||
|
, lastId : Int
|
||||||
|
, nextTodoText : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
init : ( Model, Cmd Msg )
|
init : Model
|
||||||
init =
|
init =
|
||||||
( {}, Cmd.none )
|
Model [] 0 ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,12 +38,48 @@ init =
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= NoOp
|
= AddTodo
|
||||||
|
| DeleteTodo Int
|
||||||
|
| ToggleTodoCompletion Int
|
||||||
|
| SetNextTodoText String
|
||||||
|
|
||||||
|
|
||||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
update : Msg -> Model -> Model
|
||||||
update msg model =
|
update message model =
|
||||||
( model, Cmd.none )
|
case message of
|
||||||
|
AddTodo ->
|
||||||
|
if model.nextTodoText == "" then
|
||||||
|
model
|
||||||
|
|
||||||
|
else
|
||||||
|
let
|
||||||
|
id =
|
||||||
|
model.lastId + 1
|
||||||
|
|
||||||
|
text =
|
||||||
|
model.nextTodoText
|
||||||
|
in
|
||||||
|
{ model | lastId = id, todos = Todo False text id :: model.todos }
|
||||||
|
|
||||||
|
DeleteTodo id ->
|
||||||
|
{ model | todos = List.filter ((/=) id << .id) model.todos }
|
||||||
|
|
||||||
|
ToggleTodoCompletion id ->
|
||||||
|
{ model
|
||||||
|
| todos =
|
||||||
|
List.map
|
||||||
|
(\todo ->
|
||||||
|
if todo.id == id then
|
||||||
|
{ todo | done = not todo.done }
|
||||||
|
|
||||||
|
else
|
||||||
|
todo
|
||||||
|
)
|
||||||
|
model.todos
|
||||||
|
}
|
||||||
|
|
||||||
|
SetNextTodoText text ->
|
||||||
|
{ model | nextTodoText = text }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,9 +88,69 @@ update msg model =
|
||||||
|
|
||||||
view : Model -> Html Msg
|
view : Model -> Html Msg
|
||||||
view model =
|
view model =
|
||||||
div []
|
div [ class "container" ]
|
||||||
[ img [ src "/logo.svg" ] []
|
[ header model
|
||||||
, h1 [] [ text "Your Elm App is working!" ]
|
, div [ class "todos" ] <|
|
||||||
|
List.map
|
||||||
|
todoView
|
||||||
|
model.todos
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- HEADER
|
||||||
|
|
||||||
|
|
||||||
|
header : Model -> Html Msg
|
||||||
|
header _ =
|
||||||
|
div [ class "header" ]
|
||||||
|
[ button
|
||||||
|
[ class "btn"
|
||||||
|
, onClick AddTodo
|
||||||
|
]
|
||||||
|
[ text "add todo" ]
|
||||||
|
, input
|
||||||
|
[ placeholder "todo text"
|
||||||
|
, onInput SetNextTodoText
|
||||||
|
, class "todoTextInput"
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- _TODO VIEW
|
||||||
|
|
||||||
|
|
||||||
|
todoClasses : Todo -> String
|
||||||
|
todoClasses todo =
|
||||||
|
"todo"
|
||||||
|
++ (if todo.done then
|
||||||
|
" completed"
|
||||||
|
|
||||||
|
else
|
||||||
|
""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
todoView : Todo -> Html Msg
|
||||||
|
todoView todo =
|
||||||
|
div [ class <| todoClasses todo ]
|
||||||
|
[ div [ class "todoCompleted" ]
|
||||||
|
[ input
|
||||||
|
[ checked todo.done
|
||||||
|
, type_ "checkbox"
|
||||||
|
, class "todoCheckbox"
|
||||||
|
, onCheck <| \_ -> ToggleTodoCompletion todo.id
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
, div [ class "todoText" ] [ text todo.text ]
|
||||||
|
, button
|
||||||
|
[ class "btn"
|
||||||
|
, onClick <| DeleteTodo todo.id
|
||||||
|
]
|
||||||
|
[ text "Delete todo" ]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,9 +160,8 @@ view model =
|
||||||
|
|
||||||
main : Program () Model Msg
|
main : Program () Model Msg
|
||||||
main =
|
main =
|
||||||
Browser.element
|
Browser.sandbox
|
||||||
{ view = view
|
{ view = view
|
||||||
, init = \_ -> init
|
, init = init
|
||||||
, update = update
|
, update = update
|
||||||
, subscriptions = always Sub.none
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,18 +9,103 @@
|
||||||
height: inherit;
|
height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--secondary: #009fb7;
|
||||||
|
--primary: #1a213b;
|
||||||
|
--on-secondary: #eff1f4;
|
||||||
|
--disabled: #555555;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Source Sans Pro', 'Trebuchet MS', 'Lucida Grande', 'Bitstream Vera Sans', 'Helvetica Neue', sans-serif;
|
font-family: "Source Sans Pro", "Trebuchet MS", "Lucida Grande",
|
||||||
|
"Bitstream Vera Sans", "Helvetica Neue", sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
text-align: center;
|
background: var(--primary);
|
||||||
color: #293c4b;
|
|
||||||
|
color: var(--on-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
.header {
|
||||||
margin: 20px 0;
|
display: flex;
|
||||||
max-width: 200px;
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
background-color: var(--secondary);
|
||||||
|
color: var(--on-secondary);
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 1rem;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todos {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 67vh;
|
||||||
|
border: 1px solid var(--secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo {
|
||||||
|
transition: filter 0.5s;
|
||||||
|
background: var(--primary);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo:hover {
|
||||||
|
filter: brightness(1.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo.completed {
|
||||||
|
color: var(--disabled);
|
||||||
|
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo.completed > .btn {
|
||||||
|
color: var(--disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo > * {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoText {
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoCompleted {
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoTextInput {
|
||||||
|
background: var(--primary);
|
||||||
|
border: 1px var(--secondary) solid;
|
||||||
|
margin: 1rem;
|
||||||
|
padding: 2px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: var(--on-secondary);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
transition: filter 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoTextInput:focus {
|
||||||
|
filter: brightness(1.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todoCheckbox {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
background: var(--secondary);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue