mirror of
https://github.com/correl/elm-mdl.git
synced 2025-01-11 19:12:57 +00:00
Added component features to textfield and button (needs better docs, though).
This commit is contained in:
parent
a5d96c2258
commit
73256c80db
6 changed files with 275 additions and 139 deletions
|
@ -11,7 +11,8 @@ import Material.Icon as Icon
|
|||
import Material.Style exposing (Style)
|
||||
|
||||
import Material.Textfield as Textfield
|
||||
import Material.Component exposing (..)
|
||||
import Material.Component as Component
|
||||
import Material.Component.All as Setup
|
||||
|
||||
|
||||
-- MODEL
|
||||
|
@ -19,9 +20,11 @@ import Material.Component exposing (..)
|
|||
|
||||
type alias Index = (Int, Int)
|
||||
|
||||
|
||||
type alias View =
|
||||
Signal.Address Button.Action -> Button.Model -> List Style -> List Html -> Html
|
||||
|
||||
|
||||
type alias View' =
|
||||
Signal.Address Button.Action -> Button.Model -> Html
|
||||
|
||||
|
@ -66,7 +69,7 @@ model =
|
|||
buttons
|
||||
|> List.concatMap (List.map <| \(idx, (ripple, _, _)) -> (idx, Button.model ripple))
|
||||
|> Dict.fromList
|
||||
, componentState = state0
|
||||
, componentState = Setup.state
|
||||
}
|
||||
|
||||
|
||||
|
@ -75,16 +78,15 @@ model =
|
|||
|
||||
type Action
|
||||
= Action Index Button.Action
|
||||
| State' (Material.Component.Action Material.Component.State (Maybe Action))
|
||||
| State (Setup.Action Action)
|
||||
| Click
|
||||
|
||||
| Input String
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ clicked : String
|
||||
, buttons : Dict.Dict Index Button.Model
|
||||
, componentState : State
|
||||
-- TODO: Exposed Action should not be parametric.
|
||||
, componentState : Setup.State
|
||||
}
|
||||
|
||||
|
||||
|
@ -101,20 +103,28 @@ update action model =
|
|||
)
|
||||
|> Maybe.withDefault (model, Effects.none)
|
||||
|
||||
State' action' ->
|
||||
Material.Component.update State' update action' model
|
||||
State action' ->
|
||||
Component.update State update action' model
|
||||
|
||||
Click ->
|
||||
( tf.map (\m -> { m | value = "You clicked!" }) model, Effects.none )
|
||||
|
||||
Input str ->
|
||||
( tf.map (\m -> { m | value = "You wrote '" ++ str ++ "' in the other guy."}) model
|
||||
, Effects.none
|
||||
)
|
||||
|
||||
|
||||
|
||||
instance = Component.instance State
|
||||
instance' = Component.instance' State
|
||||
|
||||
|
||||
tf = instance <| Textfield.component Textfield.model 4
|
||||
|
||||
|
||||
|
||||
-- VIEW
|
||||
|
||||
tf = instance State' (textfieldWidget Textfield.model 4)
|
||||
|
||||
|
||||
view : Signal.Address Action -> Model -> Html
|
||||
|
@ -151,12 +161,12 @@ view addr model =
|
|||
)
|
||||
|> (\contents ->
|
||||
div []
|
||||
[ instance' State' (buttonWidget (Button.model True) 1 |> onClick Click) addr model [] [ text "Click me (1)" ]
|
||||
, instance' State' (buttonWidget (Button.model False) 2) addr model [] [ text "Click me (2)" ]
|
||||
, instance' State' (textfieldWidget Textfield.model 3) addr model
|
||||
[ instance' (Button.component Button.flat (Button.model True) 1 |> onClick Click) addr model [] [ text "Click me (1)" ]
|
||||
, instance' (Button.component Button.raised (Button.model False) 2) addr model [] [ text "Click me (2)" ]
|
||||
, instance' (Textfield.component Textfield.model 3 |> Textfield.onInput Input) addr model
|
||||
, tf.view addr model
|
||||
, Grid.grid [] contents
|
||||
]
|
||||
)
|
||||
|
||||
--i = instance' State' (buttonWidget (Button.model True) 1) -- addr model.componentState [] [ text "Click me (1)" ]
|
||||
--i = instance' State (buttonWidget (Button.model True) 1) -- addr model.componentState [] [ text "Click me (1)" ]
|
||||
|
|
|
@ -2,6 +2,7 @@ module Material.Button
|
|||
( Model, model, Action(Click), update
|
||||
, flat, raised, fab, minifab, icon
|
||||
, colored, primary, accent
|
||||
, View, component, Component, onClick
|
||||
) where
|
||||
|
||||
{-| From the [Material Design Lite documentation](http://www.getmdl.io/components/#buttons-section):
|
||||
|
@ -25,8 +26,8 @@ module Material.Button
|
|||
See also the
|
||||
[Material Design Specification]([https://www.google.com/design/spec/components/buttons.html).
|
||||
|
||||
# Component
|
||||
@docs Model, model, Action, update
|
||||
# Elm architecture
|
||||
@docs Model, model, Action, update, View
|
||||
|
||||
# Style
|
||||
@docs colored, primary, accent
|
||||
|
@ -38,6 +39,9 @@ for details about what type of buttons are appropriate for which situations.
|
|||
|
||||
@docs flat, raised, fab, minifab, icon
|
||||
|
||||
# Component
|
||||
@docs Component, component, onClick
|
||||
|
||||
-}
|
||||
|
||||
import Html exposing (..)
|
||||
|
@ -49,6 +53,7 @@ import Signal exposing (Address, forwardTo)
|
|||
import Material.Helpers as Helpers
|
||||
import Material.Style exposing (Style, cs, cs', styled)
|
||||
import Material.Ripple as Ripple
|
||||
import Material.Component as Component exposing (Component, Indexed)
|
||||
|
||||
{-| MDL button.
|
||||
-}
|
||||
|
@ -137,7 +142,7 @@ view kind addr model styling html =
|
|||
)
|
||||
[ Helpers.blurOn "mouseup"
|
||||
, Helpers.blurOn "mouseleave"
|
||||
, onClick addr Click
|
||||
, Html.Events.onClick addr Click
|
||||
]
|
||||
(case model of
|
||||
S (Just ripple) ->
|
||||
|
@ -151,12 +156,10 @@ view kind addr model styling html =
|
|||
_ -> html)
|
||||
|
||||
|
||||
-- Fake address (for link buttons).
|
||||
|
||||
|
||||
addr : Signal.Address Action
|
||||
addr = (Signal.mailbox Click).address
|
||||
|
||||
{-| Type of button views.
|
||||
-}
|
||||
type alias View =
|
||||
Address Action -> Model -> List Style -> List Html -> Html
|
||||
|
||||
|
||||
{-| From the
|
||||
|
@ -179,7 +182,7 @@ Example use (uncolored flat button, assuming properly setup model):
|
|||
flatButton = Button.flat addr model Button.Plain [text "Click me!"]
|
||||
|
||||
-}
|
||||
flat : Address Action -> Model -> List Style -> List Html -> Html
|
||||
flat : View
|
||||
flat = view ""
|
||||
|
||||
|
||||
|
@ -200,7 +203,7 @@ Example use (colored raised button, assuming properly setup model):
|
|||
raisedButton = Button.raised addr model Button.Colored [text "Click me!"]
|
||||
|
||||
-}
|
||||
raised : Address Action -> Model -> List Style -> List Html -> Html
|
||||
raised : View
|
||||
raised = view "mdl-button--raised"
|
||||
|
||||
|
||||
|
@ -226,13 +229,13 @@ Example use (colored with a '+' icon):
|
|||
fabButton : Html
|
||||
fabButton = fab addr model Colored [Icon.i "add"]
|
||||
-}
|
||||
fab : Address Action -> Model -> List Style -> List Html -> Html
|
||||
fab : View
|
||||
fab = view "mdl-button--fab"
|
||||
|
||||
|
||||
{-| Mini-sized variant of a Floating Action Button; refer to `fab`.
|
||||
-}
|
||||
minifab : Address Action -> Model -> List Style -> List Html -> Html
|
||||
minifab : View
|
||||
minifab = view "mdl-button--mini-fab"
|
||||
|
||||
|
||||
|
@ -248,6 +251,45 @@ Example use (no color, displaying a '+' icon):
|
|||
iconButton : Html
|
||||
iconButton = icon addr model Plain [Icon.i "add"]
|
||||
-}
|
||||
icon : Address Action -> Model -> List Style -> List Html -> Html
|
||||
icon : View
|
||||
icon = view "mdl-button--icon"
|
||||
|
||||
|
||||
|
||||
-- COMPONENT
|
||||
|
||||
|
||||
{-| Button component type.
|
||||
-}
|
||||
type alias Component state obs =
|
||||
Component.Component
|
||||
Model
|
||||
{ state | button : Indexed Model }
|
||||
Action
|
||||
obs
|
||||
(List Style -> List Html -> Html)
|
||||
|
||||
|
||||
{-| Component constructor. Provide the view function your button should
|
||||
have as the first argument, e.g.,
|
||||
|
||||
button = Button.component Button.minifab (Button.model True) 0
|
||||
|
||||
-}
|
||||
component : View -> Model -> Int -> Component state action
|
||||
component view =
|
||||
Component.setup view update .button (\x y -> {y | button = x})
|
||||
|
||||
|
||||
{-| Lift the button Click action to your own action. E.g.,
|
||||
-}
|
||||
onClick : obs -> Component state obs -> Component state obs
|
||||
onClick o component =
|
||||
(\action ->
|
||||
case action of
|
||||
Click -> Just o
|
||||
_ -> Nothing)
|
||||
|> Component.addObserver component
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,63 +1,82 @@
|
|||
module Material.Component where
|
||||
module Material.Component
|
||||
( Component, setup, addObserver
|
||||
, Instance, instance, instance'
|
||||
, update
|
||||
, Indexed
|
||||
, View, Update, Action
|
||||
) where
|
||||
|
||||
{-|
|
||||
|
||||
# Types
|
||||
|
||||
## Elm architecture types
|
||||
@docs View, Update, Action
|
||||
|
||||
## Component types
|
||||
@docs Component, Instance
|
||||
|
||||
## Helpers
|
||||
@docs Indexed
|
||||
|
||||
# For component consumers
|
||||
@docs instance, instance'
|
||||
@docs update
|
||||
|
||||
# For component authors
|
||||
@docs component, addObserver
|
||||
-}
|
||||
|
||||
import Effects exposing (Effects)
|
||||
import Html exposing (Html)
|
||||
import Dict exposing (Dict)
|
||||
|
||||
import Material.Button as Button
|
||||
import Material.Style exposing (Style)
|
||||
import Material.Textfield as Textfield
|
||||
|
||||
import Material.Helpers exposing (map1, map2, map1st, map2nd)
|
||||
|
||||
|
||||
map1 : (a -> a') -> (a, b, c) -> (a', b, c)
|
||||
map1 f (x,y,z) = (f x, y, z)
|
||||
-- TYPES
|
||||
|
||||
|
||||
map2 : (b -> b') -> (a, b, c) -> (a, b', c)
|
||||
map2 f (x,y,z) = (x, f y, z)
|
||||
|
||||
|
||||
map1st : (a -> c) -> (a,b) -> (c,b)
|
||||
map1st f (x,y) = (f x, y)
|
||||
|
||||
|
||||
map2nd : (b -> c) -> (a,b) -> (a,c)
|
||||
map2nd f (x,y) = (x, f y)
|
||||
{-| Indexed families of things.
|
||||
-}
|
||||
type alias Indexed a =
|
||||
Dict Int a
|
||||
|
||||
|
||||
{- Variant of EA update function type, where effects may be
|
||||
lifted to a different type.
|
||||
-}
|
||||
type alias Update' model action action' =
|
||||
action -> model -> (model, Effects action')
|
||||
|
||||
|
||||
{-| Standard EA update function type.
|
||||
-}
|
||||
type alias Update model action =
|
||||
Update' model action action
|
||||
|
||||
|
||||
type alias Step model action action' =
|
||||
action -> model -> (model, Effects action, action')
|
||||
|
||||
|
||||
type alias State =
|
||||
{ button : Indexed Button.Model
|
||||
, textfield : Indexed Textfield.Model
|
||||
}
|
||||
|
||||
|
||||
state0 : State
|
||||
state0 =
|
||||
{ button = Dict.empty
|
||||
, textfield = Dict.empty
|
||||
}
|
||||
{-| Standard EA view function type.
|
||||
-}
|
||||
type alias View model action a =
|
||||
Signal.Address action -> model -> a
|
||||
|
||||
|
||||
{-| Generic component action.
|
||||
-}
|
||||
type Action model obs =
|
||||
A (model -> (model, Effects (Action model obs), obs))
|
||||
|
||||
|
||||
{-| Generic model.
|
||||
-}
|
||||
type alias Model model state =
|
||||
{ model | componentState : state }
|
||||
|
||||
|
||||
-- FOR CONSUMERS
|
||||
|
||||
|
||||
update :
|
||||
(Action state (Maybe action) -> action) ->
|
||||
Update (Model model state) action ->
|
||||
|
@ -79,39 +98,13 @@ update fwd update' (A f) model =
|
|||
|> map2nd (\fx' -> Effects.batch [ fx, fx' ])
|
||||
|
||||
|
||||
pack : (Step model action obs) -> action -> Action model obs
|
||||
pack update action =
|
||||
A (update action >> map2 (Effects.map (pack update)))
|
||||
-- CAUTION. Potential crash from update/update name clash.
|
||||
|
||||
-- COMPONENT
|
||||
|
||||
|
||||
type alias View model action a =
|
||||
Signal.Address action -> model -> a
|
||||
|
||||
|
||||
type alias Component model action a =
|
||||
{ view : View model action a
|
||||
, update : Update model action
|
||||
}
|
||||
|
||||
|
||||
type alias Indexed a =
|
||||
Dict Int a
|
||||
|
||||
buttonComponent : Component Button.Model Button.Action (List Style -> List Html -> Html)
|
||||
buttonComponent =
|
||||
{ view = Button.raised
|
||||
, update = Button.update
|
||||
}
|
||||
|
||||
textfieldComponent : Component Textfield.Model Textfield.Action Html
|
||||
textfieldComponent =
|
||||
{ view = Textfield.view
|
||||
, update = \action model -> (Textfield.update action model, Effects.none)
|
||||
}
|
||||
|
||||
|
||||
type alias Widget submodel model action obs a =
|
||||
{-| Component type.
|
||||
-}
|
||||
type alias Component submodel model action obs a =
|
||||
{ view : View model action a
|
||||
, update : Update model action
|
||||
, observe : action -> Maybe obs
|
||||
|
@ -120,15 +113,30 @@ type alias Widget submodel model action obs a =
|
|||
}
|
||||
|
||||
|
||||
widget :
|
||||
Component submodel action a -> -- Given a "Component submodel ..."
|
||||
{-| Component constructor. You must supply
|
||||
|
||||
1. A view function
|
||||
2. An update function
|
||||
3. A getter
|
||||
4. A setter
|
||||
|
||||
This will produce a function which needs only
|
||||
|
||||
5. An initial model, and
|
||||
6. an id
|
||||
|
||||
to fit with the `instance` function.
|
||||
-}
|
||||
setup :
|
||||
View submodel action a -> -- Given a view function,
|
||||
Update submodel action -> -- an update function
|
||||
(model -> Indexed submodel) -> -- a getter
|
||||
(Indexed submodel -> model -> model) -> -- a setter
|
||||
submodel -> -- an initial model for this instance
|
||||
Int -> -- an id for this instance
|
||||
Widget submodel model action obs a -- ... produce a "Widget ..."
|
||||
Int -> -- an instance id (*)
|
||||
Component submodel model action obs a -- ... produce a Component.
|
||||
|
||||
widget component get set model0 id =
|
||||
setup view update get set model0 id =
|
||||
let
|
||||
get' model =
|
||||
Dict.get id (get model) |> Maybe.withDefault model0
|
||||
|
@ -137,13 +145,11 @@ widget component get set model0 id =
|
|||
set (Dict.insert id submodel (get model)) model
|
||||
in
|
||||
{ view =
|
||||
\addr model -> component.view addr (get' model)
|
||||
|
||||
\addr model -> view addr (get' model)
|
||||
, update =
|
||||
\action model ->
|
||||
component.update action (get' model)
|
||||
update action (get' model)
|
||||
|> map1st (flip set' model)
|
||||
|
||||
, getModel = get'
|
||||
, setModel = set'
|
||||
, observe = \_ -> Nothing
|
||||
|
@ -151,11 +157,27 @@ widget component get set model0 id =
|
|||
|
||||
|
||||
|
||||
{- EA update function variant where running the function
|
||||
produces not just a new model and an effect, but also an
|
||||
observation.
|
||||
-}
|
||||
type alias Step model action obs =
|
||||
action -> model -> (model, Effects action, obs)
|
||||
|
||||
|
||||
{- Convert an update function to a step function by applying a
|
||||
function that converts the action input to the update function into
|
||||
an observation.
|
||||
-}
|
||||
observe : (action -> obs) -> Update model action -> Step model action obs
|
||||
observe f update action =
|
||||
update action >> (\(model', effects) -> (model', effects, f action))
|
||||
|
||||
|
||||
{-| Type of component instances. A component instance contains a view,
|
||||
and get/set/map for, well, getting, setting, and mapping the component
|
||||
model.
|
||||
-}
|
||||
type alias Instance submodel model action a =
|
||||
{ view : View model action a
|
||||
, get : model -> submodel
|
||||
|
@ -164,9 +186,22 @@ type alias Instance submodel model action a =
|
|||
}
|
||||
|
||||
|
||||
{- Partially apply a step (update + observation) function to an action,
|
||||
producing a generic Action.
|
||||
-}
|
||||
pack : (Step model action obs) -> action -> Action model obs
|
||||
pack update action =
|
||||
A (update action >> map2 (Effects.map (pack update)))
|
||||
|
||||
|
||||
{-| Instantiate a component. You must supply:
|
||||
|
||||
1. A function embedding `Action` into your actions.
|
||||
2. A component
|
||||
-}
|
||||
instance :
|
||||
(Action model (Maybe action) -> action) ->
|
||||
Widget submodel model subaction action a ->
|
||||
Component submodel model subaction action a ->
|
||||
Instance submodel (Model master model) action a
|
||||
instance lift widget =
|
||||
let
|
||||
|
@ -176,55 +211,48 @@ instance lift widget =
|
|||
set x model =
|
||||
{ model | componentState = widget.setModel x model.componentState }
|
||||
|
||||
fwd =
|
||||
pack (observe widget.observe widget.update) >> lift
|
||||
in
|
||||
{ view =
|
||||
\addr model ->
|
||||
widget.view (Signal.forwardTo addr (pack (observe widget.observe widget.update) >> lift)) model.componentState
|
||||
widget.view (Signal.forwardTo addr fwd) model.componentState
|
||||
, get = get
|
||||
, set = set
|
||||
, map = \f model -> set (f (get model)) model
|
||||
}
|
||||
|
||||
|
||||
|
||||
{-| Convenience function for instantiating components whose models
|
||||
you never need to read or write. (E.g,. Snackbar in Toast form.)
|
||||
-}
|
||||
instance' :
|
||||
(Action model (Maybe action) -> action) ->
|
||||
Widget submodel model subaction action a ->
|
||||
Component submodel model subaction action a ->
|
||||
View (Model m model) action a
|
||||
|
||||
instance' lift widget = (instance lift widget).view
|
||||
|
||||
|
||||
type alias ButtonStates a =
|
||||
{ a | button : Indexed Button.Model }
|
||||
|
||||
buttonWidget : Button.Model -> Int -> Widget Button.Model (ButtonStates m) Button.Action (Maybe obs) (List Style -> List Html -> Html)
|
||||
buttonWidget model =
|
||||
widget buttonComponent .button (\x y -> {y | button = x}) model
|
||||
|
||||
type Test = State' (Action State (Maybe Test))
|
||||
|
||||
|
||||
addObserver widget f =
|
||||
{ widget
|
||||
{-| Add an observer to a component.
|
||||
-}
|
||||
addObserver
|
||||
: { c | observe : a -> Maybe b }
|
||||
-> (a -> Maybe b)
|
||||
-> { c | observe : a -> Maybe b }
|
||||
addObserver component f =
|
||||
{ component
|
||||
| observe =
|
||||
\action ->
|
||||
case f action of
|
||||
Nothing -> widget.observe action
|
||||
Nothing -> component.observe action
|
||||
x -> x
|
||||
}
|
||||
|
||||
|
||||
|
||||
onClick f widget =
|
||||
(\action ->
|
||||
case action of
|
||||
Button.Click -> Just f
|
||||
_ -> Nothing)
|
||||
|> addObserver widget
|
||||
|
||||
|
||||
|
||||
|
||||
{-
|
||||
|
||||
type alias TextfieldStates a =
|
||||
{ a | textfield : Indexed Textfield.Model }
|
||||
|
@ -235,3 +263,5 @@ textfieldWidget : Textfield.Model -> Int -> Widget Textfield.Model (TextfieldSta
|
|||
textfieldWidget model =
|
||||
widget textfieldComponent .textfield (\x y -> { y | textfield = x}) model
|
||||
|
||||
-}
|
||||
|
||||
|
|
|
@ -9,17 +9,6 @@ filter elem attr html =
|
|||
elem attr (List.filterMap (\x -> x) html)
|
||||
|
||||
|
||||
mapWithIndex : (Int -> a -> b) -> List a -> List b
|
||||
mapWithIndex f xs =
|
||||
let
|
||||
loop k ys =
|
||||
case ys of
|
||||
[] -> []
|
||||
y :: ys -> f k y :: loop (k+1) ys
|
||||
in
|
||||
loop 0 xs
|
||||
|
||||
|
||||
effect : Effects b -> a -> (a, Effects b)
|
||||
effect e x = (x, e)
|
||||
|
||||
|
@ -43,3 +32,24 @@ clip lower upper k = Basics.max lower (Basics.min k upper)
|
|||
blurOn : String -> Html.Attribute
|
||||
blurOn evt =
|
||||
Html.Attributes.attribute ("on" ++ evt) <| "this.blur()"
|
||||
|
||||
|
||||
-- TUPLES
|
||||
|
||||
|
||||
map1 : (a -> a') -> (a, b, c) -> (a', b, c)
|
||||
map1 f (x,y,z) = (f x, y, z)
|
||||
|
||||
|
||||
map2 : (b -> b') -> (a, b, c) -> (a, b', c)
|
||||
map2 f (x,y,z) = (x, f y, z)
|
||||
|
||||
|
||||
map1st : (a -> c) -> (a,b) -> (c,b)
|
||||
map1st f (x,y) = (f x, y)
|
||||
|
||||
|
||||
map2nd : (b -> c) -> (a,b) -> (a,c)
|
||||
map2nd f (x,y) = (x, f y)
|
||||
|
||||
|
||||
|
|
|
@ -279,7 +279,7 @@ tabsView addr model tabs =
|
|||
, ("mds-js-ripple-effect--ignore-events", model.rippleTabs)
|
||||
]
|
||||
]
|
||||
(tabs |> mapWithIndex (\tabIndex tab ->
|
||||
(tabs |> List.indexedMap (\tabIndex tab ->
|
||||
filter a
|
||||
[ classList
|
||||
[ ("mdl-layout__tab", True)
|
||||
|
|
|
@ -26,15 +26,21 @@ This implementation provides only single-line.
|
|||
# Configuration
|
||||
@docs Kind, Label
|
||||
|
||||
# Component
|
||||
# Elm Architecture
|
||||
@docs Action, Model, model, update, view
|
||||
|
||||
# Component
|
||||
@docs component, Component, onInput
|
||||
|
||||
-}
|
||||
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (..)
|
||||
import Effects
|
||||
|
||||
import Material.Helpers exposing (filter)
|
||||
import Material.Component as Component exposing (Indexed)
|
||||
|
||||
|
||||
-- MODEL
|
||||
|
@ -160,3 +166,41 @@ view addr model =
|
|||
, model.error |> Maybe.map (\e ->
|
||||
span [class "mdl-textfield__error"] [text e])
|
||||
]
|
||||
|
||||
|
||||
|
||||
-- COMPONENT
|
||||
|
||||
|
||||
{-| Textfield component type.
|
||||
-}
|
||||
type alias Component state obs =
|
||||
Component.Component
|
||||
Model
|
||||
{ state | textfield : Indexed Model }
|
||||
Action
|
||||
obs
|
||||
Html
|
||||
|
||||
|
||||
{-| Component constructor.
|
||||
-}
|
||||
component : Model -> Int -> Component state action
|
||||
component =
|
||||
let
|
||||
update' action model = (update action model, Effects.none)
|
||||
in
|
||||
Component.setup view update' .textfield (\x y -> {y | textfield = x})
|
||||
|
||||
|
||||
{-| Lift the button Click action to your own action. E.g.,
|
||||
-}
|
||||
onInput : (String -> obs) -> Component state obs -> Component state obs
|
||||
onInput f component =
|
||||
(\action ->
|
||||
case action of
|
||||
Input str -> Just (f str)
|
||||
_ -> Nothing)
|
||||
|> Component.addObserver component
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue