Created
March 26, 2019 06:04
-
-
Save TwistingTwists/97afddba14f691163f2ef54c227fe1db to your computer and use it in GitHub Desktop.
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
port module Main exposing (main) | |
import Browser | |
import Html exposing (Html) | |
import Html.Attributes | |
import Html.Events | |
main = | |
Browser.element | |
{ init = init | |
, update = update | |
, subscriptions = \m -> Sub.none | |
, view = view | |
} | |
type alias Model = | |
{ amount : String | |
, ratePercent : String | |
} | |
init : () -> ( Model, Cmd msg ) | |
init _ = | |
( { amount = "$1000.87" |> currency | |
, ratePercent = "3.427857%" |> percent | |
} | |
, Cmd.none | |
) | |
type Msg | |
= UpdateAmount String (Cmd Msg) | |
| UpdateRatePercent String (Cmd Msg) | |
update : Msg -> Model -> ( Model, Cmd Msg ) | |
update msg model = | |
case msg of | |
UpdateAmount s c -> ( { model | amount = s }, c ) | |
UpdateRatePercent s c -> ( { model | ratePercent = s }, c ) | |
view : Model -> Html Msg | |
view model = | |
let | |
amount = model.amount |> unFormat |> toFloatOrZero | |
rate = model.ratePercent |> unFormat |> toFloatOrZero |> (\f -> f / 100) | |
in | |
Html.div [] | |
[ form model | |
[ currencyField "Amount" .amount UpdateAmount | |
, percentField "Rate" .ratePercent UpdateRatePercent | |
] | |
, Html.div [ Html.Attributes.class "results" ] | |
[ Html.label [] [ Html.text "Results" ] | |
, Html.div [Html.Attributes.class "value"] | |
[ (amount * rate) |> String.fromFloat |> currency |> Html.text | |
] | |
] | |
] | |
-- abstractions around fields | |
form : model -> List (model -> Html msg) -> Html msg | |
form model fields = | |
Html.form [] | |
(List.map (\f -> f model) fields) | |
type alias FieldConfig t model msg = | |
{ label : String | |
, value : model -> t | |
, onInput : t -> msg | |
, onFocus : model -> msg | |
, onBlur : model -> msg | |
, class : model -> String | |
} | |
numberField : FieldConfig String model msg -> model -> Html msg | |
numberField config model = | |
Html.div [ Html.Attributes.class "field" ] | |
[ Html.label [] | |
[ Html.text config.label | |
] | |
, Html.input | |
[ Html.Attributes.value (config.value model) | |
, Html.Attributes.class (config.class model) | |
, Html.Attributes.id | |
(config.label | |
|> String.replace " " "_" | |
) | |
, Html.Events.onInput config.onInput | |
, Html.Events.onFocus (config.onFocus model) | |
, Html.Events.onBlur (config.onBlur model) | |
] | |
[] | |
] | |
formattedNumberField : String -> (model -> String) -> (model -> String) -> (String -> Cmd msg -> msg) -> model -> Html msg | |
formattedNumberField label formatter getter updateMsg model = | |
numberField | |
-- FieldConfig | |
{ label = label | |
, value = getter | |
, onInput = \s -> updateMsg s Cmd.none | |
, onFocus = \m -> updateMsg (m |> getter |> unFormat) (selectElementId label) | |
, onBlur = \m -> updateMsg (formatter m) Cmd.none | |
, class = \m -> m |> getter |> unFormat |> String.toFloat |> numberFieldClass | |
} | |
model | |
currencyField : String -> (model -> String) -> (String -> Cmd msg -> msg) -> model -> Html msg | |
currencyField label getter updateMsg model = | |
formattedNumberField label (\m -> m |> getter |> currency) getter updateMsg model | |
percentField : String -> (model -> String) -> (String -> Cmd msg -> msg) -> model -> Html msg | |
percentField label getter updateMsg model = | |
formattedNumberField label (\m -> m |> getter |> percent) getter updateMsg model | |
-- everything else is pretty much all just code to handle formatting (and unformatting) | |
toFloatOrZero : String -> Float | |
toFloatOrZero s = | |
case String.toFloat s of | |
Just f -> f | |
Nothing -> 0 | |
numberFieldClass : Maybe Float -> String | |
numberFieldClass f = | |
case f of | |
Just v -> "" | |
Nothing -> "error" | |
unFormat : String -> String | |
unFormat s = | |
s | |
|> String.replace "," "" | |
|> String.replace "$" "" | |
|> String.replace "%" "" | |
floatSplit : Float -> ( String, String ) | |
floatSplit f = | |
case f |> String.fromFloat |> String.split "." of | |
[ a, b ] -> | |
( a, b ) | |
[ a ] -> | |
( a, "0" ) | |
_ -> | |
( "0", "0" ) | |
niceFloatSplit : Float -> ( String, String ) | |
niceFloatSplit f = | |
let | |
( n, mantissa ) = | |
floatSplit f | |
in | |
( n |> chunksOfRight 3 |> String.join ",", mantissa ) | |
-- rounded to the hundredths (cents) | |
currency : String -> String | |
currency s = | |
let | |
( n, mantissa ) = | |
s |> unFormat |> toFloatOrZero |> (\f -> f * 100) |> round |> (\r -> toFloat(r) / 100) |> niceFloatSplit | |
in | |
"$" ++ n ++ "." ++ String.left 2 (String.padRight 2 '0' mantissa) | |
-- rounded to the hundredths | |
percent : String -> String | |
percent s = | |
let | |
( n, mantissa ) = | |
s |> unFormat |> toFloatOrZero |> niceFloatSplit | |
in | |
n ++ "." ++ String.left 2 (String.padRight 2 '0' mantissa) ++ "%" | |
chunksOfRight : Int -> String -> List String | |
chunksOfRight k s = | |
let | |
len = | |
String.length s | |
k2 = | |
2 * k | |
chunksOfR sp = | |
if String.length sp > k2 then | |
String.right k sp :: chunksOfR (String.dropRight k sp) | |
else | |
String.right k sp :: [ String.dropRight k sp ] | |
in | |
if len > k2 then | |
List.reverse (chunksOfR s) | |
else if len > k then | |
String.dropRight k s :: [ String.right k s ] | |
else | |
[ s ] | |
port selectElementId : String -> Cmd msg | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment