elm-optimize-level-2/testcases/elm-css-realworld/TextInput.elm

352 lines
9.4 KiB
Elm
Raw Normal View History

module TextInput exposing
( Config
, InputType(..)
, init
, isDisabled
, onBlur
, onChange
, onFocus
, toHtml
, withInputId
, withInputType
, withPotentialErrorMessage
)
{-| -}
import Css
import Css.Global
import Css.Transitions
import Html.Styled as Html exposing (Html, div, text)
import Html.Styled.Attributes as Attributes
import Html.Styled.Events as Events
{-| The config record keeps a description of how the input should look when rendered
-}
type Config msg
= Config (InternalConfig msg)
type alias InternalConfig msg =
{ inputType : InputType
, label : String
, value : String
, inputId : String
, error : Maybe String
, onChange : Maybe (String -> msg)
, onFocus : Maybe msg
, onBlur : Maybe msg
, disabled : Bool
}
{-| The input type record decides which input type should be rendered
-}
type InputType
= TextInput
| TelephoneInput
| EmailInput
{-| Initialize a text input configuration
-}
init : String -> String -> Config msg
init label value =
Config
{ inputType = TextInput
, label = label
, value = value
, inputId = labelAsId label
, error = Nothing
, onChange = Nothing
, onFocus = Nothing
, onBlur = Nothing
, disabled = False
}
{-| -}
onChange : (String -> msg) -> Config msg -> Config msg
onChange handler (Config config) =
Config { config | onChange = Just handler }
{-| The message which should be triggered every time the input gains focus.
-}
onFocus : msg -> Config msg -> Config msg
onFocus msg (Config config) =
Config { config | onFocus = Just msg }
{-| The message which should be triggered every time the input loses focus.
-}
onBlur : msg -> Config msg -> Config msg
onBlur msg (Config config) =
Config { config | onBlur = Just msg }
{-| -}
withPotentialErrorMessage : Maybe String -> Config msg -> Config msg
withPotentialErrorMessage error (Config config) =
Config { config | error = error }
{-| -}
withInputId : String -> Config msg -> Config msg
withInputId inputId (Config config) =
Config { config | inputId = inputId }
{-| -}
withInputType : InputType -> Config msg -> Config msg
withInputType inputType (Config config) =
Config { config | inputType = inputType }
{-| -}
isDisabled : Bool -> Config msg -> Config msg
isDisabled disabled (Config config) =
Config { config | disabled = disabled }
{-| Render the input to a HTML node.
-}
toHtml : Config msg -> Html msg
toHtml (Config config) =
let
hasError =
config.error /= Nothing
labelTransitionInitial =
Css.Transitions.transition
[ Css.Transitions.transform3 250.0 0.0 Css.Transitions.easeIn
]
labelTransitionSelected =
Css.Transitions.transition
[ Css.Transitions.transform3 150.0 0.0 Css.Transitions.easeOut
]
selectedLabelStyle =
Css.batch
[ Css.transforms
[ Css.translateY (Css.pct -35)
, Css.translateX (Css.pct -20)
, Css.scale 0.6
]
, labelTransitionSelected
]
selectedInputStyle =
Css.batch
[ Css.paddingTop <| Css.px 24
]
defaultInputAttributes =
[ Attributes.id config.inputId
, Attributes.type_ (inputTypeToString config.inputType)
, Attributes.value config.value
, Attributes.disabled config.disabled
, Attributes.css
[ Css.pseudoElement "-ms-clear"
[ Css.display Css.none ]
, Css.pseudoElement "-ms-reveal"
[ Css.display Css.none ]
, Css.border Css.zero
, Css.outline Css.zero
, Css.borderRadius (Css.px 4)
, Css.boxShadow5 Css.zero Css.zero Css.zero (Css.px 1) <|
if hasError then
errorColor
else
borderColor
, Css.backgroundColor <| Css.hex "000000"
, Css.lineHeight <| Css.rem 1
, Css.width <| Css.pct 100
, Css.height <| Css.px 60
, Css.paddingTop <| Css.px 12
, Css.paddingBottom <| Css.px 8
, Css.paddingLeft <| Css.px 12
, Css.paddingRight <| Css.px 12
, inputTransitions ToIdle
, Css.Global.generalSiblings
[ Css.Global.typeSelector "label"
[ labelTransitionInitial ]
]
, if config.disabled then
Css.batch
[ Css.boxShadow5 Css.zero Css.zero Css.zero (Css.px 1) boxShadowColor
]
else
Css.batch
[ Css.hover
[ Css.boxShadow5 Css.zero Css.zero (Css.px 8) Css.zero boxShadowColor
, Css.borderColor <| Css.hex "000000"
, inputTransitions ToHover
]
, Css.focus
[ Css.border3 (Css.px 2) Css.solid <|
if hasError then
errorColor
else
selectedBorderColor
, selectedInputStyle
, Css.Global.generalSiblings
[ Css.Global.typeSelector "label"
[ selectedLabelStyle ]
]
, inputTransitions ToFocus
]
]
, if config.value /= "" then
selectedInputStyle
else
Css.batch []
]
]
actualInputAttributes =
[ Maybe.map Events.onInput config.onChange
, Maybe.map Events.onFocus config.onFocus
, Maybe.map Events.onBlur config.onBlur
]
|> List.filterMap identity
|> List.append defaultInputAttributes
in
div
[ Attributes.css
[ Css.position Css.relative
, Css.fontSize <| Css.px 20
, Css.lineHeight <| Css.px 30
]
]
[ Html.input
actualInputAttributes
[]
, Html.label
[ Attributes.for config.inputId
, Attributes.css
[ Css.position Css.absolute
, Css.top <| Css.px 15
, Css.left <| Css.px 12
, Css.property "transition" "0.1s all ease-in-out"
, Css.pointerEvents Css.none
, if config.disabled then
Css.color disabledColor
else
Css.batch []
, if config.value /= "" then
Css.important selectedLabelStyle
else
Css.batch []
]
]
[ Html.text config.label ]
, div
[ Attributes.css
[ Css.color errorColor
, Css.lineHeight <| Css.px 24
, Css.fontSize <| Css.px 14
]
]
[ text <|
if config.disabled then
nonBreakingSpace
else
Maybe.withDefault nonBreakingSpace config.error
]
]
borderColor : Css.Color
borderColor =
Css.hex "606568"
selectedBorderColor : Css.Color
selectedBorderColor =
Css.hex "00957A"
errorColor : Css.Color
errorColor =
Css.hex "ED0000"
disabledColor : Css.Color
disabledColor =
Css.hex "888B8E"
boxShadowColor : Css.Color
boxShadowColor =
Css.hex "EBEBEC"
nonBreakingSpace : String
nonBreakingSpace =
"\u{00A0}"
labelAsId : String -> String
labelAsId label =
label
|> String.toLower
|> String.replace " " "-"
inputTypeToString : InputType -> String
inputTypeToString inputType =
case inputType of
TextInput ->
"text"
TelephoneInput ->
"tel"
EmailInput ->
"email"
-- Transitions
type Transition
= ToFocus
| ToHover
| ToIdle
inputTransitions : Transition -> Css.Style
inputTransitions event =
case event of
ToFocus ->
Css.Transitions.transition
[ Css.Transitions.borderColor3 500.0 0.0 Css.Transitions.easeOut
, Css.Transitions.borderRadius3 250.0 0.0 Css.Transitions.easeOut
, Css.Transitions.boxShadow3 250.0 0.0 Css.Transitions.easeOut
]
ToHover ->
Css.Transitions.transition
[ Css.Transitions.borderColor3 175.0 0.0 Css.Transitions.easeOut
, Css.Transitions.borderRadius3 175.0 0.0 Css.Transitions.easeOut
, Css.Transitions.boxShadow3 175.0 0.0 Css.Transitions.easeOut
]
ToIdle ->
Css.Transitions.transition
[ Css.Transitions.borderColor3 250.0 0.0 Css.Transitions.easeIn
, Css.Transitions.borderRadius3 250.0 0.0 Css.Transitions.easeIn
, Css.Transitions.boxShadow3 250.0 0.0 Css.Transitions.easeIn
]