2016-03-08 16:30:09 +00:00
|
|
|
module Material.Textfield where
|
|
|
|
|
|
|
|
{-| From the [Material Design Lite documentation](http://www.getmdl.io/components/#textfields-section):
|
|
|
|
|
|
|
|
> The Material Design Lite (MDL) text field component is an enhanced version of
|
|
|
|
> the standard HTML `<input type="text">` and `<input type="textarea">` elements.
|
|
|
|
> A text field consists of a horizontal line indicating where keyboard input
|
|
|
|
> can occur and, typically, text that clearly communicates the intended
|
|
|
|
> contents of the text field. The MDL text field component provides various
|
|
|
|
> types of text fields, and allows you to add both display and click effects.
|
2016-03-13 21:47:00 +00:00
|
|
|
>
|
2016-03-08 16:30:09 +00:00
|
|
|
> Text fields are a common feature of most user interfaces, regardless of a
|
|
|
|
> site's content or function. Their design and use is therefore an important
|
2016-03-13 21:47:00 +00:00
|
|
|
> factor in the overall user experience. See the text field component's
|
|
|
|
> [Material Design specifications page](https://www.google.com/design/spec/components/text-fields.html)
|
2016-03-08 16:30:09 +00:00
|
|
|
> for details.
|
2016-03-13 21:47:00 +00:00
|
|
|
>
|
2016-03-08 16:30:09 +00:00
|
|
|
> The enhanced text field component has a more vivid visual look than a standard
|
|
|
|
> text field, and may be initially or programmatically disabled. There are three
|
|
|
|
> main types of text fields in the text field component, each with its own basic
|
|
|
|
> coding requirements. The types are single-line, multi-line, and expandable.
|
|
|
|
|
2016-03-13 21:47:00 +00:00
|
|
|
This implementation provides only single-line.
|
2016-03-08 16:30:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
# Configuration
|
|
|
|
@docs Kind, Label
|
|
|
|
|
|
|
|
# Component
|
|
|
|
@docs Action, Model, model, update, view
|
|
|
|
-}
|
|
|
|
|
|
|
|
import Html exposing (..)
|
|
|
|
import Html.Attributes exposing (..)
|
|
|
|
import Html.Events exposing (..)
|
|
|
|
|
|
|
|
import Material.Aux exposing (..)
|
|
|
|
|
|
|
|
|
|
|
|
-- MODEL
|
|
|
|
|
|
|
|
|
|
|
|
{-| Label configuration. The `text` is the text of the label;
|
|
|
|
the label floats if `float` is True.
|
|
|
|
-}
|
|
|
|
type alias Label =
|
|
|
|
{ text : String
|
|
|
|
, float : Bool
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
{-| Kind of textfield. Currently supports only single-line inputs.
|
|
|
|
-}
|
|
|
|
type Kind
|
|
|
|
= SingleLine
|
|
|
|
{-
|
|
|
|
| MultiLine (Maybe Int) -- Max no. of rows or no limit
|
|
|
|
-- TODO. Should prevent key event for ENTER
|
|
|
|
-- when number of rows exceeds maxrows argument to constructor:
|
|
|
|
|
|
|
|
MaterialTextfield.prototype.onKeyDown_ = function(event) {
|
|
|
|
var currentRowCount = event.target.value.split('\n').length;
|
|
|
|
if (event.keyCode === 13) {
|
|
|
|
if (currentRowCount >= this.maxRows) {
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
|
|
{-| Model. The textfield is in its error-state if `error` is not `Nothing`.
|
|
|
|
The contents of the field is `value`.
|
|
|
|
-}
|
|
|
|
type alias Model =
|
|
|
|
{ label : Maybe Label
|
|
|
|
, error : Maybe String
|
|
|
|
, kind : Kind
|
|
|
|
, isDisabled : Bool
|
|
|
|
, isFocused : Bool
|
|
|
|
, value : String
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
{-| Default model. No label, error, or value.
|
|
|
|
-}
|
|
|
|
model : Model
|
|
|
|
model =
|
|
|
|
{ label = Nothing
|
|
|
|
, error = Nothing
|
|
|
|
, kind = SingleLine
|
|
|
|
, isDisabled = False
|
|
|
|
, isFocused = False
|
|
|
|
, value = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-- ACTIONS, UPDATE
|
|
|
|
|
|
|
|
|
|
|
|
{-| Component actions. `Input` carries the new value of the field.
|
|
|
|
-}
|
|
|
|
type Action
|
|
|
|
= Input String
|
|
|
|
| Blur
|
|
|
|
| Focus
|
|
|
|
|
|
|
|
|
|
|
|
{-| Component update.
|
|
|
|
-}
|
|
|
|
update : Action -> Model -> Model
|
|
|
|
update action model =
|
|
|
|
case action of
|
|
|
|
Input str ->
|
|
|
|
{ model | value = str }
|
|
|
|
|
|
|
|
Blur ->
|
|
|
|
{ model | isFocused = False }
|
|
|
|
|
|
|
|
Focus ->
|
|
|
|
{ model | isFocused = True }
|
|
|
|
|
|
|
|
|
|
|
|
-- VIEW
|
|
|
|
|
|
|
|
|
|
|
|
{-| Component view.
|
|
|
|
-}
|
|
|
|
view : Signal.Address Action -> Model -> Html
|
|
|
|
view addr model =
|
|
|
|
let hasFloat = model.label |> Maybe.map .float |> Maybe.withDefault False
|
|
|
|
hasError = model.error |> Maybe.map (always True) |> Maybe.withDefault False
|
|
|
|
in
|
|
|
|
filter div
|
|
|
|
[ classList
|
|
|
|
[ ("mdl-textfield", True)
|
|
|
|
, ("mdl-js-textfield", True)
|
|
|
|
, ("is-upgraded", True)
|
|
|
|
, ("mdl-textfield--floating-label", hasFloat)
|
|
|
|
, ("is-invalid", hasError)
|
|
|
|
, ("is-dirty", model.value /= "")
|
|
|
|
, ("is-focused", model.isFocused && not model.isDisabled)
|
|
|
|
, ("is-disabled", model.isDisabled)
|
|
|
|
]
|
|
|
|
]
|
|
|
|
[ Just <| input
|
|
|
|
[ class "mdl-textfield__input"
|
|
|
|
, style [ ("outline", "none") ]
|
|
|
|
, type' "text"
|
|
|
|
, disabled model.isDisabled
|
|
|
|
, value model.value
|
|
|
|
, Html.Events.on "input" targetValue (\s -> Signal.message addr (Input s))
|
|
|
|
, onBlur addr Blur
|
|
|
|
, onFocus addr Focus
|
|
|
|
]
|
|
|
|
[]
|
|
|
|
, model.label |> Maybe.map (\l ->
|
|
|
|
label [class "mdl-textfield__label"] [text l.text])
|
|
|
|
, model.error |> Maybe.map (\e ->
|
|
|
|
span [class "mdl-textfield__error"] [text e])
|
|
|
|
]
|