mirror of
https://github.com/correl/elm-mdl.git
synced 2024-11-23 11:09:51 +00:00
Draft toggles
This commit is contained in:
parent
103c74b880
commit
dadacac273
3 changed files with 384 additions and 0 deletions
282
src/Material/Toggles.elm
Normal file
282
src/Material/Toggles.elm
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
module Material.Toggles
|
||||||
|
( Model, model
|
||||||
|
, Action, update
|
||||||
|
, switch, checkbox, radio
|
||||||
|
, instance, fwdChange
|
||||||
|
, Container, Observer, Instance
|
||||||
|
, Radio, Checkbox, Switch
|
||||||
|
) where
|
||||||
|
|
||||||
|
{-| From the [Material Design Lite documentation](http://www.getmdl.io/index.html#toggles-section/checkbox):
|
||||||
|
|
||||||
|
> The Material Design Lite (MDL) checkbox component is an enhanced version of the
|
||||||
|
> standard HTML `<input type="checkbox">` element. A checkbox consists of a small
|
||||||
|
> square and, typically, text that clearly communicates a binary condition that
|
||||||
|
> will be set or unset when the user clicks or touches it. Checkboxes typically,
|
||||||
|
> but not necessarily, appear in groups, and can be selected and deselected
|
||||||
|
> individually. The MDL checkbox component allows you to add display and click
|
||||||
|
> effects.
|
||||||
|
>
|
||||||
|
> Checkboxes are a common feature of most user interfaces, regardless of a site's
|
||||||
|
> content or function. Their design and use is therefore an important factor in
|
||||||
|
> the overall user experience. [...]
|
||||||
|
>
|
||||||
|
> The enhanced checkbox component has a more vivid visual look than a standard
|
||||||
|
> checkbox, and may be initially or programmatically disabled.
|
||||||
|
|
||||||
|
See also the
|
||||||
|
[Material Design Specification](https://www.google.com/design/spec/components/selection-controls.html#).
|
||||||
|
|
||||||
|
Refer to [this site](http://debois.github.io/elm-mdl#/toggles)
|
||||||
|
for a live demo.
|
||||||
|
|
||||||
|
@docs Model, model, Action, update
|
||||||
|
@docs view
|
||||||
|
|
||||||
|
# Component support
|
||||||
|
|
||||||
|
@docs Container, Observer, Instance, instance, fwdTemplate
|
||||||
|
-}
|
||||||
|
|
||||||
|
|
||||||
|
import Effects exposing (Effects, none)
|
||||||
|
import Html exposing (..)
|
||||||
|
import Html.Attributes exposing (type', class, disabled, checked)
|
||||||
|
import Html.Events exposing (on, onFocus, onBlur)
|
||||||
|
import Json.Decode as Decode
|
||||||
|
import Signal exposing (Address, forwardTo, message)
|
||||||
|
|
||||||
|
import Material.Component as Component exposing (Indexed)
|
||||||
|
import Material.Style as Style exposing (Style, cs, cs', styled, attribute, multiple)
|
||||||
|
import Material.Helpers exposing (map1st, map2nd, blurOn, filter)
|
||||||
|
import Material.Ripple as Ripple
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
|
type State = S Ripple.Model
|
||||||
|
|
||||||
|
|
||||||
|
{-| Component model.
|
||||||
|
-}
|
||||||
|
type alias Model =
|
||||||
|
{ isFocused : Bool
|
||||||
|
, isDisabled : Bool
|
||||||
|
, value : Bool
|
||||||
|
, ripple : Bool
|
||||||
|
, state : State
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Default component model constructor.
|
||||||
|
-}
|
||||||
|
model : Model
|
||||||
|
model =
|
||||||
|
{ isFocused = False
|
||||||
|
, isDisabled = False
|
||||||
|
, value = False
|
||||||
|
, ripple = True
|
||||||
|
, state = S Ripple.model
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
state : Model -> Ripple.Model
|
||||||
|
state model =
|
||||||
|
case model.state of
|
||||||
|
S ripple ->
|
||||||
|
ripple
|
||||||
|
|
||||||
|
-- ACTION, UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
{-| Component action.
|
||||||
|
-}
|
||||||
|
type Action
|
||||||
|
= Change
|
||||||
|
| Ripple Ripple.Action
|
||||||
|
| SetFocus Bool
|
||||||
|
|
||||||
|
|
||||||
|
{-| Component update.
|
||||||
|
-}
|
||||||
|
update : Action -> Model -> (Model, Effects Action)
|
||||||
|
update action model =
|
||||||
|
case action of
|
||||||
|
Change ->
|
||||||
|
( { model | value = not model.value }, none )
|
||||||
|
|
||||||
|
Ripple rip ->
|
||||||
|
Ripple.update rip (state model)
|
||||||
|
|> map1st (\r -> { model | state = S r })
|
||||||
|
|> map2nd (Effects.map Ripple)
|
||||||
|
|
||||||
|
SetFocus focus ->
|
||||||
|
( { model | isFocused = focus }, none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
top : String -> Address Action -> Model -> List Style -> List Html -> Html
|
||||||
|
top name addr model styles elems =
|
||||||
|
styled label
|
||||||
|
[ cs ("mdl-" ++ name)
|
||||||
|
, cs ("mdl-js-" ++ name)
|
||||||
|
, cs' "mdl-js-ripple-effect" model.ripple
|
||||||
|
, cs' "mdl-js-ripple-effect--ignore-events" model.ripple
|
||||||
|
, cs "is-upgraded"
|
||||||
|
, cs' "is-checked" model.value
|
||||||
|
, attribute <| on "change" Decode.value (always (message addr Change))
|
||||||
|
, attribute <| blurOn "mouseup"
|
||||||
|
, attribute <| onFocus addr (SetFocus True)
|
||||||
|
, attribute <| onBlur addr (SetFocus False)
|
||||||
|
, multiple styles
|
||||||
|
]
|
||||||
|
(if model.ripple then
|
||||||
|
(Ripple.view
|
||||||
|
( forwardTo addr Ripple)
|
||||||
|
[ class "mdl-switch__ripple-container mdl-js-ripple-effect mdl-ripple--center" ]
|
||||||
|
(state model)
|
||||||
|
) :: elems
|
||||||
|
else
|
||||||
|
elems)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
checkbox : Address Action -> Model -> List Style -> Html
|
||||||
|
checkbox addr model styles =
|
||||||
|
[ input
|
||||||
|
[ type' "checkbox"
|
||||||
|
, class ("mdl-checkbox__input")
|
||||||
|
, disabled model.isDisabled
|
||||||
|
, checked model.value
|
||||||
|
{- TODO: the checked attribute is not rendered. Switch still seems to
|
||||||
|
work, though, but accessibility is probably compromised.
|
||||||
|
https://github.com/evancz/elm-html/issues/91
|
||||||
|
-}
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, span [ class ("mdl-checkbox__label") ] []
|
||||||
|
, span [ class "mdl-checkbox__focus-helper" ] []
|
||||||
|
, span
|
||||||
|
[ class "mdl-checkbox__box-outline" ]
|
||||||
|
[ span
|
||||||
|
[ class "mdl-checkbox__tick-outline" ]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|> top "checkbox" addr model styles
|
||||||
|
|
||||||
|
|
||||||
|
{-| TODO
|
||||||
|
-}
|
||||||
|
switch : Address Action -> Model -> List Style -> Html
|
||||||
|
switch addr model styles =
|
||||||
|
[ input
|
||||||
|
[ type' "checkbox"
|
||||||
|
, class "mdl-switch__input"
|
||||||
|
, disabled model.isDisabled
|
||||||
|
, checked model.value
|
||||||
|
{- TODO: the checked attribute is not rendered. Switch still seems to
|
||||||
|
work, though, but accessibility is probably compromised.
|
||||||
|
https://github.com/evancz/elm-html/issues/91
|
||||||
|
-}
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, span [ class "mdl-switch__label" ] []
|
||||||
|
, div [ class "mdl-switch__track" ] []
|
||||||
|
, div
|
||||||
|
[ class "mdl-switch__thumb" ]
|
||||||
|
[ span [ class "mdl-switch__focus-helper" ] [] ]
|
||||||
|
]
|
||||||
|
|> top "switch" addr model styles
|
||||||
|
|
||||||
|
|
||||||
|
type alias RadioId =
|
||||||
|
(String, String)
|
||||||
|
|
||||||
|
|
||||||
|
radio : Address Action -> Model -> List Style -> RadioId -> List Html -> Html
|
||||||
|
radio addr model styles (value, name) elems =
|
||||||
|
[ input
|
||||||
|
[ type' "radio"
|
||||||
|
, class "mdl-radio__button"
|
||||||
|
, disabled model.isDisabled
|
||||||
|
, checked model.value
|
||||||
|
, Html.Attributes.value value
|
||||||
|
, Html.Attributes.name name
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, span [ class "mdl-radio__label" ] elems
|
||||||
|
, span [ class "mdl-radio__outer-circle" ] []
|
||||||
|
, span [ class "mdl-radio__inner-circle" ] []
|
||||||
|
]
|
||||||
|
|> top "radio" addr model styles
|
||||||
|
|
||||||
|
|
||||||
|
-- COMPONENT
|
||||||
|
|
||||||
|
|
||||||
|
{-|
|
||||||
|
-}
|
||||||
|
type alias View a =
|
||||||
|
Address Action -> Model -> List Style -> a
|
||||||
|
|
||||||
|
|
||||||
|
{-|
|
||||||
|
-}
|
||||||
|
type alias Container c =
|
||||||
|
{ c | toggles : Indexed Model }
|
||||||
|
|
||||||
|
|
||||||
|
{-|
|
||||||
|
-}
|
||||||
|
type alias Observer obs =
|
||||||
|
Component.Observer Action obs
|
||||||
|
|
||||||
|
|
||||||
|
{-|
|
||||||
|
-}
|
||||||
|
type alias Instance container obs v =
|
||||||
|
Component.Instance
|
||||||
|
Model container Action obs (List Style -> v)
|
||||||
|
|
||||||
|
type alias Radio container obs =
|
||||||
|
Instance container obs (RadioId -> List Html -> Html)
|
||||||
|
|
||||||
|
type alias Checkbox container obs =
|
||||||
|
Instance container obs Html
|
||||||
|
|
||||||
|
type alias Switch container obs =
|
||||||
|
Instance container obs Html
|
||||||
|
|
||||||
|
|
||||||
|
{-| Create a component instance. Example usage, assuming you have a type
|
||||||
|
`Action` with a constructor ...
|
||||||
|
-}
|
||||||
|
instance :
|
||||||
|
Int
|
||||||
|
-> (Component.Action (Container c) obs -> obs)
|
||||||
|
-> (View v)
|
||||||
|
-> Model
|
||||||
|
-> List (Observer obs)
|
||||||
|
-> Instance (Container c) obs v
|
||||||
|
|
||||||
|
instance id lift view model0 observers =
|
||||||
|
Component.instance
|
||||||
|
view update .toggles (\x y -> {y | toggles = x}) id lift model0 observers
|
||||||
|
|
||||||
|
{-|
|
||||||
|
-}
|
||||||
|
fwdChange : obs -> (Observer obs)
|
||||||
|
fwdChange obs action =
|
||||||
|
case action of
|
||||||
|
Change -> Just obs
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
|
||||||
|
|
64
src/Material/Toggles/Common.elm
Normal file
64
src/Material/Toggles/Common.elm
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
module Material.Toggles.Common where
|
||||||
|
|
||||||
|
import Signal exposing (Address, message, forwardTo)
|
||||||
|
import Html exposing (label)
|
||||||
|
import Html.Attributes exposing (class)
|
||||||
|
import Html.Events exposing (on, onFocus, onBlur)
|
||||||
|
import Json.Decode as Decode
|
||||||
|
|
||||||
|
import Material.Helpers exposing (blurOn)
|
||||||
|
import Material.Ripple as Ripple
|
||||||
|
import Material.Style exposing (styled, cs, cs', attribute, multiple)
|
||||||
|
|
||||||
|
|
||||||
|
{-| Component action.
|
||||||
|
-}
|
||||||
|
type Action
|
||||||
|
= Change
|
||||||
|
| Ripple Ripple.Action
|
||||||
|
| SetFocus Bool
|
||||||
|
|
||||||
|
|
||||||
|
{-| Component update.
|
||||||
|
-}
|
||||||
|
update : Action -> Model -> (Model, Effects Action)
|
||||||
|
update action model =
|
||||||
|
case action of
|
||||||
|
Change ->
|
||||||
|
( { model | value = not model.value }, none )
|
||||||
|
|
||||||
|
Ripple rip ->
|
||||||
|
Ripple.update rip (state model)
|
||||||
|
|> map1st (\r -> { model | state = S r })
|
||||||
|
|> map2nd (Effects.map Ripple)
|
||||||
|
|
||||||
|
SetFocus focus ->
|
||||||
|
( { model | isFocused = focus }, none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
top : String -> Address Action -> Model -> List Style -> List Html -> Html
|
||||||
|
top name addr model styles elems =
|
||||||
|
styled label
|
||||||
|
[ cs ("mdl-" ++ name)
|
||||||
|
, cs ("mdl-js-" ++ name)
|
||||||
|
, cs' "mdl-js-ripple-effect" model.ripple
|
||||||
|
, cs' "mdl-js-ripple-effect--ignore-events" model.ripple
|
||||||
|
, cs "is-upgraded"
|
||||||
|
, cs' "is-checked" model.value
|
||||||
|
, attribute <| on "change" Decode.value (always (message addr Change))
|
||||||
|
, attribute <| blurOn "mouseup"
|
||||||
|
, attribute <| onFocus addr (SetFocus True)
|
||||||
|
, attribute <| onBlur addr (SetFocus False)
|
||||||
|
, multiple styles
|
||||||
|
]
|
||||||
|
(if model.ripple then
|
||||||
|
(Ripple.view
|
||||||
|
( forwardTo addr Ripple)
|
||||||
|
[ class "mdl-switch__ripple-container mdl-js-ripple-effect mdl-ripple--center" ]
|
||||||
|
(state model)
|
||||||
|
) :: elems
|
||||||
|
else
|
||||||
|
elems)
|
||||||
|
|
||||||
|
|
38
src/Material/Toggles/Helpers.elm
Normal file
38
src/Material/Toggles/Helpers.elm
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
module Material.Toggles.Common where
|
||||||
|
|
||||||
|
import Signal exposing (Address, message, forwardTo)
|
||||||
|
import Html exposing (label)
|
||||||
|
import Html.Attributes exposing (class)
|
||||||
|
import Html.Events exposing (on, onFocus, onBlur)
|
||||||
|
import Json.Decode as Decode
|
||||||
|
|
||||||
|
import Material.Helpers exposing (blurOn)
|
||||||
|
import Material.Ripple as Ripple
|
||||||
|
import Material.Style exposing (styled, cs, cs', attribute, multiple)
|
||||||
|
|
||||||
|
|
||||||
|
top : String -> Address Action -> Model -> List Style -> List Html -> Html
|
||||||
|
top name addr model styles elems =
|
||||||
|
styled label
|
||||||
|
[ cs ("mdl-" ++ name)
|
||||||
|
, cs ("mdl-js-" ++ name)
|
||||||
|
, cs' "mdl-js-ripple-effect" model.ripple
|
||||||
|
, cs' "mdl-js-ripple-effect--ignore-events" model.ripple
|
||||||
|
, cs "is-upgraded"
|
||||||
|
, cs' "is-checked" model.value
|
||||||
|
, attribute <| on "change" Decode.value (always (message addr Change))
|
||||||
|
, attribute <| blurOn "mouseup"
|
||||||
|
, attribute <| onFocus addr (SetFocus True)
|
||||||
|
, attribute <| onBlur addr (SetFocus False)
|
||||||
|
, multiple styles
|
||||||
|
]
|
||||||
|
(if model.ripple then
|
||||||
|
(Ripple.view
|
||||||
|
( forwardTo addr Ripple)
|
||||||
|
[ class "mdl-switch__ripple-container mdl-js-ripple-effect mdl-ripple--center" ]
|
||||||
|
(state model)
|
||||||
|
) :: elems
|
||||||
|
else
|
||||||
|
elems)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue