Skip to content

Instantly share code, notes, and snippets.

@TwistingTwists
Created March 26, 2019 06:04
Show Gist options
  • Save TwistingTwists/97afddba14f691163f2ef54c227fe1db to your computer and use it in GitHub Desktop.
Save TwistingTwists/97afddba14f691163f2ef54c227fe1db to your computer and use it in GitHub Desktop.
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