diff --git a/cat/src/Main.gren b/cat/src/Main.gren index b643f15..492f0e3 100644 --- a/cat/src/Main.gren +++ b/cat/src/Main.gren @@ -1,11 +1,11 @@ -module Main exposing (main) +module Main exposing ( main ) -import Node -import Bytes exposing (Bytes) +import Bytes exposing ( Bytes ) import Bytes.Encode as BE -import Stream exposing (Stream) -import Node.Program as Program exposing (Program) import FileSystem +import Node +import Node.Program as Program exposing ( Program ) +import Stream exposing ( Stream ) import Task @@ -19,7 +19,8 @@ main = type alias Model = - { stdout : Stream } + { stdout : Stream + } type Msg @@ -27,28 +28,41 @@ type Msg | ReadResult (Result FileSystem.UnknownFileSystemError Bytes) -init : Program.AppInitTask { model : Model, command : Cmd Msg } +init : + Program.AppInitTask + { model : Model + , command : Cmd Msg + } init = - Program.await Node.initialize <| \nodeConfig -> - Program.await FileSystem.initialize <| \fsPermission -> - Program.startProgram - { model = - { stdout = nodeConfig.stdout - } - , command = - case nodeConfig.args of - [ _, _, file ] -> - FileSystem.openForRead fsPermission file - |> Task.attempt OpenResult - - _ -> - Stream.send nodeConfig.stderr <| - BE.encode <| - BE.string "Exactly one argument is required: the file name to read\n" - } - - -update : Msg -> Model -> { model : Model, command : Cmd Msg } + Program.await Node.initialize + <| (\nodeConfig -> + Program.await FileSystem.initialize + <| (\fsPermission -> + Program.startProgram + { model = + { stdout = nodeConfig.stdout + } + , command = + case nodeConfig.args of + [ _, _, file ] -> + FileSystem.openForRead fsPermission file + |> Task.attempt OpenResult + + _ -> + Stream.send nodeConfig.stderr + <| BE.encode + <| BE.string "Exactly one argument is required: the file name to read\n" + } + ) + ) + + +update : + Msg + -> Model + -> { model : Model + , command : Cmd Msg + } update msg model = case msg of OpenResult (Ok fh) -> @@ -72,4 +86,3 @@ update msg model = { model = model , command = Cmd.none } - diff --git a/counter/src/Main.gren b/counter/src/Main.gren index 1a041ee..5625eea 100644 --- a/counter/src/Main.gren +++ b/counter/src/Main.gren @@ -1,27 +1,31 @@ -module Main exposing (main) +module Main exposing ( main ) import Browser -import Html exposing (Html) +import Html exposing ( Html ) import Html.Attributes as Attribute import Html.Events as Event main = Browser.sandbox - { init = init - , update = update - , view = view - } + { init = init + , update = update + , view = view + } -type alias Model = Int +type alias Model = + Int + init : Model -init = +init = 0 -type Msg = Clicked +type Msg + = Clicked + update : Msg -> Model -> Model update msg model = @@ -31,14 +35,19 @@ update msg model = view : Model -> Html Msg -view model = - Html.div [] - [ Html.span - [ Attribute.id "count" ] - [ Html.text <| String.fromInt model ] +view model = + Html.div + [] + [ Html.span + [ Attribute.id "count" + ] + [ Html.text + <| String.fromInt model + ] , Html.button [ Attribute.id "increase-count" , Event.onClick Clicked ] - [ Html.text "Count" ] + [ Html.text "Count" + ] ] diff --git a/files/src/Main.gren b/files/src/Main.gren index 16344c6..48395a3 100644 --- a/files/src/Main.gren +++ b/files/src/Main.gren @@ -1,7 +1,7 @@ -module Main exposing (main) +module Main exposing ( main ) import Browser -import File exposing (File) +import File exposing ( File ) import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) @@ -13,24 +13,31 @@ import Json.Decode as D main = - Browser.element - { init = init - , view = view - , update = update - , subscriptions = subscriptions - } + Browser.element + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } -- MODEL -type alias Model = Array File +type alias Model = + Array File -init : {} -> { model : Model, command : Cmd Msg } +init : + {} + -> { model : Model + , command : Cmd Msg + } init _ = - { model = [], command = Cmd.none } + { model = [] + , command = Cmd.none + } @@ -38,13 +45,15 @@ init _ = type Msg - = GotFiles (Array File) + = GotFiles (Array File) update msg model = - case msg of - GotFiles files -> - { model = files, command = Cmd.none } + case msg of + GotFiles files -> + { model = files + , command = Cmd.none + } @@ -53,7 +62,7 @@ update msg model = subscriptions : Model -> Sub Msg subscriptions model = - Sub.none + Sub.none @@ -62,19 +71,26 @@ subscriptions model = view : Model -> Html Msg view model = - div [] - [ input - [ type_ "file" - , multiple True - , on "change" (D.map GotFiles filesDecoder) - ] + div [] - , div - [ id "file-view" ] - [ text (Debug.toString model) ] - ] + [ input + [ type_ "file" + , multiple True + , on "change" (D.map GotFiles filesDecoder) + ] + [] + , div + [ id "file-view" + ] + [ text (Debug.toString model) + ] + ] filesDecoder : D.Decoder (Array File) filesDecoder = - D.at ["target","files"] (D.array File.decoder) + D.at + [ "target" + , "files" + ] + (D.array File.decoder) diff --git a/flight_booker/src/Main.gren b/flight_booker/src/Main.gren index b5461a7..ca7f0a3 100644 --- a/flight_booker/src/Main.gren +++ b/flight_booker/src/Main.gren @@ -1,9 +1,9 @@ -module Main exposing (main) +module Main exposing ( main ) import Browser -import Html exposing (Html, text, div, input, button, select, option) -import Html.Attributes exposing (id, style, value, disabled) -import Html.Events as Event exposing (onInput) +import Html exposing ( Html, text, div, input, button, select, option ) +import Html.Attributes exposing ( id, style, value, disabled ) +import Html.Events as Event exposing ( onInput ) import Task @@ -26,6 +26,7 @@ flightTypeToString flightType = case flightType of OneWay -> "One-way flight" + ReturnFlight -> "Return flight" @@ -35,21 +36,22 @@ flightTypeFromString str = case str of "Return flight" -> ReturnFlight + _ -> OneWay type alias Model = { flightType : FlightType - , input1: String - , input2: String + , input1 : String + , input2 : String , date1 : Maybe Date , date2 : Maybe Date } init : Model -init = +init = let date = { year = 2022 @@ -78,18 +80,22 @@ update msg model = { model | flightType = flightTypeFromString asString } Input1Changed str -> - { model | input1 = str - , date1 = parseDate str + { model + | input1 = str + , date1 = parseDate str } Input2Changed str -> - { model | input2 = str - , date2 = parseDate str + { model + | input2 = str + , date2 = parseDate str } + -- VIEW + view : Model -> Html Msg view model = div @@ -114,48 +120,66 @@ view model = , isDisabled = model.flightType == OneWay } , bookButton - { isDisabled = checkIfDisabled model.flightType model.date1 model.date2 } + { isDisabled = checkIfDisabled model.flightType model.date1 model.date2 + } ] flightTypeSelector : Html Msg flightTypeSelector = - select + select [ id "flight-type" , onInput FlightTypeChanged ] - [ option [] [ text ( flightTypeToString OneWay ) ] - , option [] [ text ( flightTypeToString ReturnFlight ) ] + [ option + [] + [ text (flightTypeToString OneWay) + ] + , option + [] + [ text (flightTypeToString ReturnFlight) + ] ] type alias DateInputConfig = - { id: String - , value: String - , date: Maybe Date - , changeMsg: String -> Msg - , isDisabled: Bool + { id : String + , value : String + , date : Maybe Date + , changeMsg : String -> Msg + , isDisabled : Bool } -dateInput: DateInputConfig -> Html Msg + +dateInput : DateInputConfig -> Html Msg dateInput { id = id_, value = value_, date, changeMsg, isDisabled } = input [ id id_ , value value_ , onInput changeMsg , disabled isDisabled - , style "background-color" ( if date == Nothing then "red" else "white" ) + , style + "background-color" + (if date == Nothing then + "red" + else + "white" + ) ] [] -bookButton : { isDisabled : Bool } -> Html Msg +bookButton : + { isDisabled : Bool + } + -> Html Msg bookButton { isDisabled } = button [ id "book" , disabled isDisabled ] - [ text "Book" ] + [ text "Book" + ] @@ -194,11 +218,7 @@ parseDate dateString = formatDate : Date -> String formatDate date = - String.fromInt date.day - ++ "." - ++ String.fromInt date.month - ++ "." - ++ String.fromInt date.year + String.fromInt date.day ++ "." ++ String.fromInt date.month ++ "." ++ String.fromInt date.year compareDate : Date -> Date -> Order @@ -225,14 +245,11 @@ checkIfDisabled flightType maybeDate1 maybeDate2 = ReturnFlight -> let compare = - Maybe.map2 compareDate - maybeDate1 - maybeDate2 + Maybe.map2 compareDate maybeDate1 maybeDate2 in - case compare of - Just GT -> - True - - _ -> - False + case compare of + Just GT -> + True + _ -> + False diff --git a/hello_world/src/Main.gren b/hello_world/src/Main.gren index 144cc2f..164770f 100644 --- a/hello_world/src/Main.gren +++ b/hello_world/src/Main.gren @@ -1,8 +1,11 @@ -module Main exposing (main) +module Main exposing ( main ) + +import Html exposing ( Html, div ) -import Html exposing (Html, div) main : Html a main = - div [] - [ Html.text "Hello, world!" ] + div + [] + [ Html.text "Hello, world!" + ] diff --git a/local_storage/src/Main.gren b/local_storage/src/Main.gren index 7bb12e0..48d9d92 100644 --- a/local_storage/src/Main.gren +++ b/local_storage/src/Main.gren @@ -1,12 +1,13 @@ -module Main exposing (main) +module Main exposing ( main ) import Browser -import Html exposing (Html) -import Html.Events as Events +import Html exposing ( Html ) import Html.Attributes as Attributes -import WebStorage +import Html.Events as Events import LocalStorage import Task +import WebStorage + -- MAIN @@ -21,6 +22,7 @@ main = } + -- MODEL @@ -32,7 +34,11 @@ type alias Model = } -init : {} -> { model : Model, command : Cmd Msg } +init : + {} + -> { model : Model + , command : Cmd Msg + } init _ = { model = { inputKey = "" @@ -47,146 +53,165 @@ init _ = refreshContent : Cmd Msg refreshContent = LocalStorage.length - |> Task.andThen (\length -> - Array.initialize length 0 identity - |> Array.map LocalStorage.keyAtIndex - |> Task.sequence - |> Task.mapError (always WebStorage.AccessError) - ) + |> Task.andThen + (\length -> + Array.initialize length 0 identity + |> Array.map LocalStorage.keyAtIndex + |> Task.sequence + |> Task.mapError (always WebStorage.AccessError) + ) |> Task.attempt ShowContents + -- UPDATE type Msg - = SetKey Key - | SetValue Value - | SaveToLocalStorage Key Value - | SavedToLocalStorage (Result WebStorage.WriteError {}) - | ClearLocalStorage - | ClearedLocalStorage (Result WebStorage.AccessError {}) - | GetFromLocalStorage Key - | GotFromLocalStorage Key (Result WebStorage.ReadError Value) - | RemoveFromLocalStorage Key - | RemovedFromLocalStorage Key (Result WebStorage.AccessError {}) - | ShowContents (Result WebStorage.AccessError (Array String)) + = SetKey Key + | SetValue Value + | SaveToLocalStorage Key Value + | SavedToLocalStorage (Result WebStorage.WriteError {}) + | ClearLocalStorage + | ClearedLocalStorage (Result WebStorage.AccessError {}) + | GetFromLocalStorage Key + | GotFromLocalStorage Key (Result WebStorage.ReadError Value) + | RemoveFromLocalStorage Key + | RemovedFromLocalStorage Key (Result WebStorage.AccessError {}) + | ShowContents (Result WebStorage.AccessError (Array String)) -type alias Key = String +type alias Key = + String -type alias Value = String +type alias Value = + String -type alias Error = String +type alias Error = + String -update : Msg -> Model -> { model : Model, command : Cmd Msg } +update : + Msg + -> Model + -> { model : Model + , command : Cmd Msg + } update msg model = - case msg of - SetKey key -> - { model = { model | inputKey = key } - , command = Cmd.none - } + case msg of + SetKey key -> + { model = { model | inputKey = key } + , command = Cmd.none + } - SetValue value -> - { model = { model | inputValue = value } - , command = Cmd.none - } + SetValue value -> + { model = { model | inputValue = value } + , command = Cmd.none + } - SaveToLocalStorage key value -> - { model = model - , command = - LocalStorage.set key value - |> Task.attempt SavedToLocalStorage - } + SaveToLocalStorage key value -> + { model = model + , command = + LocalStorage.set key value + |> Task.attempt SavedToLocalStorage + } - SavedToLocalStorage (Ok {}) -> - { model = - { model - | inputKey = "" - , inputValue = "" - , message = "Saved <" ++ model.inputKey ++ ", " ++ model.inputValue ++ "> to localstorage" + SavedToLocalStorage (Ok {}) -> + { model = + { model + | inputKey = "" + , inputValue = "" + , message = "Saved <" ++ model.inputKey ++ ", " ++ model.inputValue ++ "> to localstorage" + } + , command = refreshContent } - , command = refreshContent - } - SavedToLocalStorage (Err WebStorage.QuotaExceeded) -> - { model = { model | message = "Couldn't write to local storage as we've used our quota" } - , command = Cmd.none - } + SavedToLocalStorage (Err WebStorage.QuotaExceeded) -> + { model = { model | message = "Couldn\'t write to local storage as we\'ve used our quota" } + , command = Cmd.none + } - SavedToLocalStorage (Err WebStorage.WriteBlocked) -> - { model = { model | message = "Failed to write to localstorage" } - , command = Cmd.none - } + SavedToLocalStorage (Err WebStorage.WriteBlocked) -> + { model = { model | message = "Failed to write to localstorage" } + , command = Cmd.none + } - ClearLocalStorage -> - { model = model - , command = - LocalStorage.clear - |> Task.attempt ClearedLocalStorage - } + ClearLocalStorage -> + { model = model + , command = + LocalStorage.clear + |> Task.attempt ClearedLocalStorage + } - ClearedLocalStorage (Ok {}) -> - { model = { model | message = "Successfully cleared localstorage" } - , command = refreshContent - } + ClearedLocalStorage (Ok {}) -> + { model = { model | message = "Successfully cleared localstorage" } + , command = refreshContent + } - ClearedLocalStorage (Err err) -> - { model = { model | message = "Failed to clear localstorage" } - , command = Cmd.none - } + ClearedLocalStorage (Err err) -> + { model = { model | message = "Failed to clear localstorage" } + , command = Cmd.none + } - GetFromLocalStorage key -> - { model = { model | inputKey = "", inputValue = "" } - , command = - LocalStorage.get key - |> Task.attempt (GotFromLocalStorage key) - } + GetFromLocalStorage key -> + { model = + { model + | inputKey = "" + , inputValue = "" + } + , command = + LocalStorage.get key + |> Task.attempt (GotFromLocalStorage key) + } - GotFromLocalStorage key (Ok value) -> - { model = { model | message = "Got <" ++ key ++ ", " ++ value ++ "> from localstorage." } - , command = refreshContent - } + GotFromLocalStorage key (Ok value) -> + { model = { model | message = "Got <" ++ key ++ ", " ++ value ++ "> from localstorage." } + , command = refreshContent + } - GotFromLocalStorage key (Err WebStorage.NoValue) -> - { model = { model | message = "Found no items with key \"" ++ key ++ "\"" } - , command = Cmd.none - } + GotFromLocalStorage key (Err WebStorage.NoValue) -> + { model = { model | message = "Found no items with key \"" ++ key ++ "\"" } + , command = Cmd.none + } - GotFromLocalStorage _ (Err WebStorage.ReadBlocked) -> - { model = { model | message = "Failed to read from localstorage" } - , command = Cmd.none - } + GotFromLocalStorage _ (Err WebStorage.ReadBlocked) -> + { model = { model | message = "Failed to read from localstorage" } + , command = Cmd.none + } - RemoveFromLocalStorage key -> - { model = { model | inputKey = "", inputValue = "" } - , command = - LocalStorage.remove key - |> Task.attempt (RemovedFromLocalStorage key) - } + RemoveFromLocalStorage key -> + { model = + { model + | inputKey = "" + , inputValue = "" + } + , command = + LocalStorage.remove key + |> Task.attempt (RemovedFromLocalStorage key) + } - RemovedFromLocalStorage key (Ok {}) -> - { model = { model | message = "Removed item with key \"" ++ key ++ "\" from localstorage." } - , command = refreshContent - } + RemovedFromLocalStorage key (Ok {}) -> + { model = { model | message = "Removed item with key \"" ++ key ++ "\" from localstorage." } + , command = refreshContent + } - RemovedFromLocalStorage key (Err err) -> - { model = { model | message = "Failed to remove item with key \"" ++ key ++ "\" from localstorage" } - , command = Cmd.none - } + RemovedFromLocalStorage key (Err err) -> + { model = { model | message = "Failed to remove item with key \"" ++ key ++ "\" from localstorage" } + , command = Cmd.none + } - ShowContents (Ok keys) -> - { model = { model | keys = keys } - , command = Cmd.none - } + ShowContents (Ok keys) -> + { model = { model | keys = keys } + , command = Cmd.none + } + + ShowContents (Err WebStorage.AccessError) -> + { model = { model | message = "Failed to retrieve contents of local storage:" } + , command = Cmd.none + } - ShowContents (Err WebStorage.AccessError) -> - { model = { model | message = "Failed to retrieve contents of local storage:" } - , command = Cmd.none - } -- VIEW @@ -194,8 +219,10 @@ update msg model = view : Model -> Html Msg view model = - Html.div [] - [ Html.div [] + Html.div + [] + [ Html.div + [] [ Html.input [ Attributes.placeholder "Key:" , Attributes.value model.inputKey @@ -210,39 +237,56 @@ view model = [] , Html.button [ Attributes.type_ "button" - , Events.onClick <| SaveToLocalStorage model.inputKey model.inputValue + , Events.onClick + <| SaveToLocalStorage model.inputKey model.inputValue + ] + [ Html.text "๐Ÿ’พ Save" ] - [ Html.text "๐Ÿ’พ Save" ] , Html.button [ Attributes.type_ "button" - , Events.onClick <| GetFromLocalStorage model.inputKey + , Events.onClick + <| GetFromLocalStorage model.inputKey + ] + [ Html.text "๐Ÿ”Ž Get" ] - [ Html.text "๐Ÿ”Ž Get" ] , Html.button [ Attributes.type_ "button" - , Events.onClick <| RemoveFromLocalStorage model.inputKey + , Events.onClick + <| RemoveFromLocalStorage model.inputKey + ] + [ Html.text "๐Ÿ—‘ Remove" ] - [ Html.text "๐Ÿ—‘ Remove" ] , Html.button [ Attributes.type_ "button" , Events.onClick ClearLocalStorage ] - [ Html.text "๐Ÿงน Clear" ] + [ Html.text "๐Ÿงน Clear" + ] + ] + , Html.div + [] + [ Html.p + [] + [ Html.text model.message + ] ] - , Html.div [] - [ Html.p [] [ Html.text model.message ] ] - , Html.div [] - [ Html.p [] - [ Html.text <| - "There are currently " + , Html.div + [] + [ Html.p + [] + [ Html.text + <| "There are currently " ++ String.fromInt (Array.length model.keys) ++ " keys in local storage:" ] ] , Html.ul [] (Array.map viewKey model.keys) - ] + ] viewKey : String -> Html msg viewKey key = - Html.li [] [ Html.text key ] + Html.li + [] + [ Html.text key + ] diff --git a/temperature_converter/src/Main.gren b/temperature_converter/src/Main.gren index 517811e..850a0e0 100644 --- a/temperature_converter/src/Main.gren +++ b/temperature_converter/src/Main.gren @@ -1,7 +1,7 @@ -module Main exposing (main) +module Main exposing ( main ) import Browser -import Html exposing (Html) +import Html exposing ( Html ) import Html.Attributes as Attribute import Html.Events as Event @@ -14,7 +14,8 @@ main = } ---- MODEL + +-- - MODEL type alias Model = @@ -24,13 +25,14 @@ type alias Model = init : Model -init = +init = { celsius = "" , fahrenheit = "" } ---- UPDATE + +-- - UPDATE type Msg @@ -43,9 +45,9 @@ update msg model = case msg of SetCelsius value -> { celsius = value - , fahrenheit = + , fahrenheit = value - |> String.toFloat + |> String.toFloat |> Maybe.map celsiusToFahrenheit |> Maybe.map String.fromFloat |> Maybe.withDefault model.fahrenheit @@ -53,7 +55,7 @@ update msg model = SetFahrenheit value -> { fahrenheit = value - , celsius = + , celsius = value |> String.toFloat |> Maybe.map fahrenheitToCelsius @@ -64,30 +66,32 @@ update msg model = celsiusToFahrenheit : Float -> Float celsiusToFahrenheit c = - c * (9.0/5.0) + 32.0 + c * (9.0 / 5.0) + 32.0 fahrenheitToCelsius : Float -> Float fahrenheitToCelsius f = - (f - 32) * (5.0/9.0) + (f - 32) * (5.0 / 9.0) ---- VIEW + +-- - VIEW view : Model -> Html Msg -view model = - Html.div [] - [ Html.input +view model = + Html.div + [] + [ Html.input [ Attribute.id "celsius" , Event.onInput SetCelsius , Attribute.value model.celsius - ] + ] [] , Html.text "Celsius" - , Html.input + , Html.input [ Attribute.id "fahrenheit" - , Event.onInput SetFahrenheit + , Event.onInput SetFahrenheit , Attribute.value model.fahrenheit ] [] diff --git a/timer/src/Main.gren b/timer/src/Main.gren index 456a47e..4ccca76 100644 --- a/timer/src/Main.gren +++ b/timer/src/Main.gren @@ -1,20 +1,20 @@ -module Main exposing (main) +module Main exposing ( main ) import Browser -import Html exposing (Html,text,div,span,button,input) -import Html.Attributes exposing (style,type_,value) -import Html.Events as Event exposing (onClick,onInput) -import Time exposing (Posix) +import Html exposing ( Html, text, div, span, button, input ) +import Html.Attributes exposing ( style, type_, value ) +import Html.Events as Event exposing ( onClick, onInput ) +import Time exposing ( Posix ) type alias Model = { count : Int - , ticksToCount: Int - } + , ticksToCount : Int + } initialModel : Model -initialModel = +initialModel = { count = 0 , ticksToCount = 30 } @@ -23,109 +23,133 @@ initialModel = subscriptions : Model -> Sub Msg subscriptions model = -- Subscribe to a "Tick" every 100ms if we are not done counting - if model.count < model.ticksToCount then + if model.count < model.ticksToCount then Time.every 100 Tick - else + else Sub.none - -type Msg + +type Msg = Tick Posix - | SliderMoved String + | SliderMoved String | ResetButtonPressed update : Msg -> Model -> Model update msg model = case msg of - Tick _ -> + Tick _ -> { model | count = model.count + 1 } ResetButtonPressed -> { model | count = 0 } SliderMoved val -> - { model | ticksToCount = String.toInt val |> Maybe.withDefault 0 } - + { model + | ticksToCount = + String.toInt val + |> Maybe.withDefault 0 + } + + -- VIEW + view : Model -> Html Msg -view model = - div +view model = + div [ style "display" "flex" , style "flex-direction" "column" , style "width" "200px" , style "padding" "40px" - ] - [ progressBar model + ] + [ progressBar model , elapsedTime model.count - , durationSlider model.ticksToCount - , button [ onClick ResetButtonPressed ] [ text "Reset" ] + , durationSlider model.ticksToCount + , button + [ onClick ResetButtonPressed + ] + [ text "Reset" + ] ] -progressBar: Model -> Html msg +progressBar : Model -> Html msg progressBar { count, ticksToCount } = let -- Make sure we do not ever get more that 100% - percent = + percent = min 100 (toFloat count / toFloat ticksToCount * 100) - in - div + in + div [ style "border" "1px solid grey" , style "border-radius" "4px" - ] - [ span + ] + [ span [ style "display" "block" , style "width" (String.fromFloat percent ++ "%") , style "height" "8px" , style "background-color" "#005FFF" - ] + ] [] ] elapsedTime : Int -> Html Msg -elapsedTime count = - let - seconds = +elapsedTime count = + let + seconds = toFloat count / 10 - asString = + asString = String.fromFloat seconds - in - -- Hacky solution to always show one decimal - if String.contains "." asString then - text (asString ++ "s") - else - text (asString ++ ".0s") + in + -- Hacky solution to always show one decimal + if String.contains "." asString then + text (asString ++ "s") + else + text (asString ++ ".0s") -durationSlider : Int -> Html Msg +durationSlider : Int -> Html Msg durationSlider ticksToCount = - div - [ style "display" "flex" - , style "flex-direction" "row" - ] - [ div [ style "width" "40%" ] [ text "Duration:" ] - , input + div + [ style "display" "flex" + , style "flex-direction" "row" + ] + [ div + [ style "width" "40%" + ] + [ text "Duration:" + ] + , input [ style "width" "60%" - , type_ "range" + , type_ "range" , onInput SliderMoved , value (String.fromInt ticksToCount) - ] - [] + ] + [] ] - + + -- Program + + main : Program {} Model Msg main = Browser.element - { init = \flags -> { model = initialModel, command = Cmd.none } + { init = + \flags -> + { model = initialModel + , command = Cmd.none + } , view = view - , update = \msg model -> { model = update msg model, command = Cmd.none } + , update = + \msg model -> + { model = update msg model + , command = Cmd.none + } , subscriptions = subscriptions } - \ No newline at end of file diff --git a/todo_mvc/src/Main.gren b/todo_mvc/src/Main.gren index c4edba8..4e0bf5a 100644 --- a/todo_mvc/src/Main.gren +++ b/todo_mvc/src/Main.gren @@ -17,18 +17,24 @@ import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) import Html.Keyed as Keyed -import Html.Lazy exposing (lazy, lazy2) -import Json.Encode as JE +import Html.Lazy exposing ( lazy, lazy2 ) import Json.Decode as Json -import Task -import Array exposing (Array) +import Json.Encode as JE import LocalStorage +import Task + main : Program {} Model Msg main = Browser.document { init = init - , view = \model -> { title = "Gren โ€ข TodoMVC", body = [view model] } + , view = + \model -> + { title = "Gren โ€ข TodoMVC" + , body = + [ view model + ] + } , update = updateWithStorage , subscriptions = \_ -> Sub.none } @@ -37,7 +43,12 @@ main = {-| We want to `setStorage` on every update. This function adds the setStorage command for every step of the update function. -} -updateWithStorage : Msg -> Model -> { model : Model, command : Cmd Msg } +updateWithStorage : + Msg + -> Model + -> { model : Model + , command : Cmd Msg + } updateWithStorage msg model = case msg of -- Terminate early to avoid setStorage loop @@ -62,9 +73,9 @@ updateWithStorage msg model = -- MODEL +-- The full application state of our todo app. --- The full application state of our todo app. type alias Model = { entries : Array Entry , field : String @@ -102,10 +113,18 @@ newEntry desc id = entryEncoder : Entry -> JE.Value entryEncoder entry = JE.object - [ { key = "description", value = JE.string entry.description } - , { key = "completed", value = JE.bool entry.completed } - , { key = "editing", value = JE.bool entry.editing } - , { key = "id", value = JE.int entry.id } + [ { key = "description" + , value = JE.string entry.description + } + , { key = "completed" + , value = JE.bool entry.completed + } + , { key = "editing" + , value = JE.bool entry.editing + } + , { key = "id" + , value = JE.int entry.id + } ] @@ -125,11 +144,15 @@ entryDecoder = (Json.field "id" Json.int) -init : {} -> { model : Model, command : Cmd Msg } +init : + {} + -> { model : Model + , command : Cmd Msg + } init _ = - { model = emptyModel - , command = getStorage - } + { model = emptyModel + , command = getStorage + } setStorage : Model -> Cmd Msg @@ -146,16 +169,18 @@ getStorage : Cmd Msg getStorage = LocalStorage.get "gren-todo-save" |> Task.map (Json.decodeString (Json.array entryDecoder)) - |> Task.attempt (\res -> - -- If there is any error at all, we ignore it. - -- A correct model will be set once the model changes. - case res of - Ok (Ok entries) -> - LoadedEntriesFromStorage entries + |> Task.attempt + (\res -> + -- If there is any error at all, we ignore it. + -- A correct model will be set once the model changes. + case res of + Ok (Ok entries) -> + LoadedEntriesFromStorage entries + + _ -> + NoOp + ) - _ -> - NoOp - ) -- UPDATE @@ -181,22 +206,34 @@ type Msg -- How we update our Model on a given Msg? -update : Msg -> Model -> { model : Model, command : Cmd Msg } + + +update : + Msg + -> Model + -> { model : Model + , command : Cmd Msg + } update msg model = case msg of NoOp -> - { model = model, command = Cmd.none } + { model = model + , command = Cmd.none + } Add -> - { model = { model - | uid = model.uid + 1 - , field = "" - , entries = - if String.isEmpty model.field then - model.entries - else - model.entries ++ [ newEntry model.field model.uid ] - } + { model = + { model + | uid = model.uid + 1 + , field = "" + , entries = + if String.isEmpty model.field then + model.entries + else + model.entries + ++ [ newEntry model.field model.uid + ] + } , command = Cmd.none } @@ -284,13 +321,15 @@ view model = [ class "todomvc-wrapper" , style "visibility" "hidden" ] - [ node "link" + [ node + "link" [ attribute "rel" "stylesheet" , attribute "href" "style.css" ] [] , section - [ class "todoapp" ] + [ class "todoapp" + ] [ lazy viewInput model.field , lazy2 viewEntries model.visibility model.entries , lazy2 viewControls model.visibility model.entries @@ -302,8 +341,12 @@ view model = viewInput : String -> Html Msg viewInput task = header - [ class "header" ] - [ h1 [] [ text "todos" ] + [ class "header" + ] + [ h1 + [] + [ text "todos" + ] , input [ class "new-todo" , placeholder "What needs to be done?" @@ -326,7 +369,7 @@ onEnter msg = else Json.fail "not ENTER" in - on "keydown" (Json.andThen isEnter keyCode) + on "keydown" (Json.andThen isEnter keyCode) @@ -356,42 +399,67 @@ viewEntries visibility entries = else "visible" in - section - [ class "main" - , style "visibility" cssVisibility + section + [ class "main" + , style "visibility" cssVisibility + ] + [ input + [ class "toggle-all" + , type_ "checkbox" + , name "toggle" + , checked allCompleted + , onClick (CheckAll (not allCompleted)) ] - [ 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" ] - <| Array.map (\entry -> { key = entry.id, node = entry.message }) - <| Array.map viewKeyedEntry <| (Array.filter isVisible entries) + [] + , label + [ for "toggle-all" + ] + [ text "Mark all as complete" + ] + , Keyed.ul + [ class "todo-list" ] + <| Array.map + (\entry -> + { key = entry.id + , node = entry.message + } + ) + <| Array.map viewKeyedEntry + <| Array.filter isVisible entries + ] -- VIEW INDIVIDUAL ENTRIES -viewKeyedEntry : Entry -> { id : String, message : Html Msg } +viewKeyedEntry : + Entry + -> { id : String + , message : Html Msg + } viewKeyedEntry todo = - { id = String.fromInt todo.id, message = lazy viewEntry todo } + { id = String.fromInt todo.id + , message = lazy viewEntry todo + } viewEntry : Entry -> Html Msg viewEntry todo = li - [ classList [ { class = "completed", enabled = todo.completed }, { class = "editing", enabled = todo.editing } ] ] + [ classList + [ { class = "completed" + , enabled = todo.completed + } + , { class = "editing" + , enabled = todo.editing + } + ] + ] [ div - [ class "view" ] + [ class "view" + ] [ input [ class "toggle" , type_ "checkbox" @@ -400,8 +468,10 @@ viewEntry todo = ] [] , label - [ onDoubleClick (EditingEntry todo.id True) ] - [ text todo.description ] + [ onDoubleClick (EditingEntry todo.id True) + ] + [ text todo.description + ] , button [ class "destroy" , onClick (Delete todo.id) @@ -434,14 +504,14 @@ viewControls visibility entries = entriesLeft = Array.length entries - entriesCompleted in - footer - [ class "footer" - , hidden (Array.isEmpty entries) - ] - [ lazy viewControlsCount entriesLeft - , lazy viewControlsFilters visibility - , lazy viewControlsClear entriesCompleted - ] + footer + [ class "footer" + , hidden (Array.isEmpty entries) + ] + [ lazy viewControlsCount entriesLeft + , lazy viewControlsFilters visibility + , lazy viewControlsClear entriesCompleted + ] viewControlsCount : Int -> Html Msg @@ -453,17 +523,22 @@ viewControlsCount entriesLeft = else " items" in - span - [ class "todo-count" ] - [ strong [] [ text (String.fromInt entriesLeft) ] - , text (item_ ++ " left") + span + [ class "todo-count" + ] + [ strong + [] + [ text (String.fromInt entriesLeft) ] + , text (item_ ++ " left") + ] viewControlsFilters : String -> Html Msg viewControlsFilters visibility = ul - [ class "filters" ] + [ class "filters" + ] [ visibilitySwap "#/" "All" visibility , text " " , visibilitySwap "#/active" "Active" visibility @@ -475,9 +550,18 @@ viewControlsFilters visibility = visibilitySwap : String -> String -> String -> Html Msg visibilitySwap uri visibility actualVisibility = li - [ onClick (ChangeVisibility visibility) ] - [ a [ href uri, classList [ { class = "selected", enabled = visibility == actualVisibility } ] ] - [ text visibility ] + [ onClick (ChangeVisibility visibility) + ] + [ a + [ href uri + , classList + [ { class = "selected" + , enabled = visibility == actualVisibility + } + ] + ] + [ text visibility + ] ] @@ -494,16 +578,35 @@ viewControlsClear entriesCompleted = infoFooter : Html msg infoFooter = - footer [ class "info" ] - [ p [] [ text "Double-click to edit a todo" ] - , p [] + footer + [ class "info" + ] + [ p + [] + [ text "Double-click to edit a todo" + ] + , p + [] [ text "Originally written by " - , a [ href "https://github.com/evancz"] [ text "Evan Czaplici"] + , a + [ href "https://github.com/evancz" + ] + [ text "Evan Czaplici" + ] , text "in Elm, ported to Gren by the " - , a [ href "https://github.com/gren-lang" ] [ text "Gren CONTRIBUTORS" ] + , a + [ href "https://github.com/gren-lang" + ] + [ text "Gren CONTRIBUTORS" + ] ] - , p [] + , p + [] [ text "Part of " - , a [ href "http://todomvc.com" ] [ text "TodoMVC" ] + , a + [ href "http://todomvc.com" + ] + [ text "TodoMVC" + ] ] ]