mirror of
https://github.com/correl/elm-mdl.git
synced 2024-11-23 11:09:51 +00:00
Added snackbar documentation. Fixes #9.
This commit is contained in:
parent
f0a8591265
commit
040d524d24
6 changed files with 222 additions and 211 deletions
3
Makefile
3
Makefile
|
@ -6,6 +6,9 @@ comp:
|
||||||
demo:
|
demo:
|
||||||
(cd demo; elm-make Demo.elm --warn --output ../elm.js)
|
(cd demo; elm-make Demo.elm --warn --output ../elm.js)
|
||||||
|
|
||||||
|
docs:
|
||||||
|
elm-make --docs=docs.json
|
||||||
|
|
||||||
wip-pages :
|
wip-pages :
|
||||||
elm-make examples/Demo.elm --output $(PAGES)/wip.js
|
elm-make examples/Demo.elm --output $(PAGES)/wip.js
|
||||||
(cd $(PAGES); git commit -am "Update."; git push origin gh-pages)
|
(cd $(PAGES); git commit -am "Update."; git push origin gh-pages)
|
||||||
|
|
|
@ -116,7 +116,7 @@ nth k xs =
|
||||||
|
|
||||||
update : Action -> Model -> ( Model, Effects Action )
|
update : Action -> Model -> ( Model, Effects Action )
|
||||||
update action model =
|
update action model =
|
||||||
case action of
|
case Debug.log "Action" action of
|
||||||
LayoutAction a ->
|
LayoutAction a ->
|
||||||
let
|
let
|
||||||
( lifted, layoutFx ) =
|
( lifted, layoutFx ) =
|
||||||
|
@ -203,7 +203,7 @@ tabs =
|
||||||
, ("Textfields", "textfields", \addr model ->
|
, ("Textfields", "textfields", \addr model ->
|
||||||
Demo.Textfields.view (Signal.forwardTo addr TextfieldAction) model.textfields)
|
Demo.Textfields.view (Signal.forwardTo addr TextfieldAction) model.textfields)
|
||||||
{-
|
{-
|
||||||
, ("Template", \addr model ->
|
, ("Template", "tempate", \addr model ->
|
||||||
[Demo.Template.view (Signal.forwardTo addr TemplateAction) model.template])
|
[Demo.Template.view (Signal.forwardTo addr TemplateAction) model.template])
|
||||||
-}
|
-}
|
||||||
]
|
]
|
||||||
|
|
|
@ -22,11 +22,13 @@ import Demo.Page as Page
|
||||||
|
|
||||||
|
|
||||||
type alias Mdl =
|
type alias Mdl =
|
||||||
Material.Model Action
|
Material.Model
|
||||||
|
|
||||||
|
|
||||||
type Square'
|
type Square'
|
||||||
= Appearing
|
= Appearing
|
||||||
|
| Waiting
|
||||||
|
| Active
|
||||||
| Idle
|
| Idle
|
||||||
| Disappearing
|
| Disappearing
|
||||||
|
|
||||||
|
@ -38,6 +40,7 @@ type alias Square =
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ count : Int
|
{ count : Int
|
||||||
, squares : List Square
|
, squares : List Square
|
||||||
|
, snackbar : Snackbar.Model Int
|
||||||
, mdl : Mdl
|
, mdl : Mdl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +49,7 @@ model : Model
|
||||||
model =
|
model =
|
||||||
{ count = 0
|
{ count = 0
|
||||||
, squares = []
|
, squares = []
|
||||||
|
, snackbar = Snackbar.model
|
||||||
, mdl = Material.model
|
, mdl = Material.model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,19 +61,20 @@ type Action
|
||||||
= AddSnackbar
|
= AddSnackbar
|
||||||
| AddToast
|
| AddToast
|
||||||
| Appear Int
|
| Appear Int
|
||||||
| Disappear Int
|
|
||||||
| Gone Int
|
| Gone Int
|
||||||
|
| Snackbar (Snackbar.Action Int)
|
||||||
| MDL (Material.Action Action)
|
| MDL (Material.Action Action)
|
||||||
|
|
||||||
|
|
||||||
add : Model -> (Int -> Snackbar.Contents Action) -> (Model, Effects Action)
|
add : (Int -> Snackbar.Contents Int) -> Model -> (Model, Effects Action)
|
||||||
add model f =
|
add f model =
|
||||||
let
|
let
|
||||||
(mdl', fx) =
|
(snackbar', fx) =
|
||||||
Snackbar.add (f model.count) snackbar model.mdl
|
Snackbar.add (f model.count) model.snackbar
|
||||||
|
|> map2nd (Effects.map Snackbar)
|
||||||
model' =
|
model' =
|
||||||
{ model
|
{ model
|
||||||
| mdl = mdl'
|
| snackbar = snackbar'
|
||||||
, count = model.count + 1
|
, count = model.count + 1
|
||||||
, squares = (model.count, Appearing) :: model.squares
|
, squares = (model.count, Appearing) :: model.squares
|
||||||
}
|
}
|
||||||
|
@ -92,24 +97,31 @@ mapSquare k f model =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
update : Action -> Model -> (Model, Effects Action)
|
update : Action -> Model -> (Model, Effects Action)
|
||||||
update action model =
|
update action model =
|
||||||
case action of
|
case action of
|
||||||
AddSnackbar ->
|
AddSnackbar ->
|
||||||
add model
|
add (\k -> Snackbar.snackbar k ("Snackbar message #" ++ toString k) "UNDO") model
|
||||||
<| \k -> Snackbar.snackbar ("Snackbar message #" ++ toString k) "UNDO" (Disappear k)
|
|
||||||
|
|
||||||
AddToast ->
|
AddToast ->
|
||||||
add model
|
add (\k -> Snackbar.toast k <| "Toast message #" ++ toString k) model
|
||||||
<| \k -> Snackbar.toast <| "Toast message #" ++ toString k
|
|
||||||
|
|
||||||
Appear k ->
|
Appear k ->
|
||||||
|
( model |> mapSquare k (\sq -> if sq == Appearing then Waiting else sq)
|
||||||
|
, none
|
||||||
|
)
|
||||||
|
|
||||||
|
Snackbar (Snackbar.Begin k) ->
|
||||||
|
( model |> mapSquare k (always Active)
|
||||||
|
, none
|
||||||
|
)
|
||||||
|
|
||||||
|
Snackbar (Snackbar.End k) ->
|
||||||
( model |> mapSquare k (always Idle)
|
( model |> mapSquare k (always Idle)
|
||||||
, none
|
, none
|
||||||
)
|
)
|
||||||
|
|
||||||
Disappear k ->
|
Snackbar (Snackbar.Click k) ->
|
||||||
( model |> mapSquare k (always Disappearing)
|
( model |> mapSquare k (always Disappearing)
|
||||||
, delay transitionLength (Gone k)
|
, delay transitionLength (Gone k)
|
||||||
)
|
)
|
||||||
|
@ -120,6 +132,11 @@ update action model =
|
||||||
}
|
}
|
||||||
, none)
|
, none)
|
||||||
|
|
||||||
|
Snackbar action' ->
|
||||||
|
Snackbar.update action' model.snackbar
|
||||||
|
|> map1st (\s -> { model | snackbar = s })
|
||||||
|
|> map2nd (Effects.map Snackbar)
|
||||||
|
|
||||||
MDL action' ->
|
MDL action' ->
|
||||||
Material.update MDL action' model.mdl
|
Material.update MDL action' model.mdl
|
||||||
|> map1st (\m -> { model | mdl = m })
|
|> map1st (\m -> { model | mdl = m })
|
||||||
|
@ -143,12 +160,6 @@ addToastButton =
|
||||||
[ Button.fwdClick AddToast ]
|
[ Button.fwdClick AddToast ]
|
||||||
|
|
||||||
|
|
||||||
-- TODO: Bad name
|
|
||||||
snackbar : Snackbar.Instance Mdl Action
|
|
||||||
snackbar =
|
|
||||||
Snackbar.instance MDL Snackbar.model
|
|
||||||
|
|
||||||
|
|
||||||
boxHeight : String
|
boxHeight : String
|
||||||
boxHeight = "48px"
|
boxHeight = "48px"
|
||||||
|
|
||||||
|
@ -166,27 +177,38 @@ transitions =
|
||||||
("transition"
|
("transition"
|
||||||
, "box-shadow 333ms ease-in-out 0s, "
|
, "box-shadow 333ms ease-in-out 0s, "
|
||||||
++ "width " ++ toString transitionLength ++ "ms, "
|
++ "width " ++ toString transitionLength ++ "ms, "
|
||||||
++ "height " ++ toString transitionLength ++ "ms"
|
++ "height " ++ toString transitionLength ++ "ms, "
|
||||||
|
++ "background-color " ++ toString transitionLength ++ "ms"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
clickView : Model -> Square -> Html
|
clickView : Model -> Square -> Html
|
||||||
clickView model (k, square) =
|
clickView model (k, square) =
|
||||||
let
|
let
|
||||||
color =
|
palette =
|
||||||
Array.get ((k + 4) % Array.length Color.palette) Color.palette
|
Array.get ((k + 4) % Array.length Color.palette) Color.palette
|
||||||
|> Maybe.withDefault Color.Teal
|
|> Maybe.withDefault Color.Teal
|
||||||
|> flip Color.color Color.S500
|
|
||||||
|
shade =
|
||||||
|
case square of
|
||||||
|
Idle ->
|
||||||
|
Color.S100
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Color.S500
|
||||||
|
|
||||||
|
color =
|
||||||
|
Color.color palette shade
|
||||||
|
|
||||||
selected' =
|
selected' =
|
||||||
Snackbar.activeAction (snackbar.get model.mdl) == Just (Disappear k)
|
square == Active
|
||||||
|
|
||||||
(width, height, margin, selected) =
|
(width, height, margin, selected) =
|
||||||
case square of
|
if square == Appearing || square == Disappearing then
|
||||||
Idle ->
|
("0", "0", "16px 0", False)
|
||||||
(boxWidth, boxHeight, "16px 16px", selected')
|
else
|
||||||
_ ->
|
(boxWidth, boxHeight, "16px 16px", selected')
|
||||||
("0", "0", "16px 0", False)
|
|
||||||
in
|
in
|
||||||
div
|
div
|
||||||
[ style
|
[ style
|
||||||
|
@ -236,7 +258,7 @@ view addr model =
|
||||||
[ text """Click the buttons below to activate the snackbar. Note that
|
[ text """Click the buttons below to activate the snackbar. Note that
|
||||||
multiple activations are automatically queued."""
|
multiple activations are automatically queued."""
|
||||||
]
|
]
|
||||||
, grid [ ] --css "margin-top" "32px" ]
|
, grid [ ]
|
||||||
[ cell
|
[ cell
|
||||||
[ size All 2, size Phone 2, align Top ]
|
[ size All 2, size Phone 2, align Top ]
|
||||||
[ addToastButton.view addr model.mdl
|
[ addToastButton.view addr model.mdl
|
||||||
|
@ -261,7 +283,7 @@ view addr model =
|
||||||
]
|
]
|
||||||
(model.squares |> List.reverse |> List.map (clickView model))
|
(model.squares |> List.reverse |> List.map (clickView model))
|
||||||
]
|
]
|
||||||
, snackbar.view addr model.mdl
|
, Snackbar.view (Signal.forwardTo addr Snackbar) model.snackbar
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import Demo.Page as Page
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ mdl : Material.Model Action
|
{ mdl : Material.Model
|
||||||
, rx : (String, Regex.Regex)
|
, rx : (String, Regex.Regex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ m0 =
|
||||||
|
|
||||||
|
|
||||||
type alias Mdl =
|
type alias Mdl =
|
||||||
Material.Model Action
|
Material.Model
|
||||||
|
|
||||||
|
|
||||||
field0 : Textfield.Instance Mdl Action
|
field0 : Textfield.Instance Mdl Action
|
||||||
|
|
|
@ -36,6 +36,10 @@ We recommend going with the library's
|
||||||
[component support](http://github.com/debois/elm-mdl/blob/master/examples/Component.elm)
|
[component support](http://github.com/debois/elm-mdl/blob/master/examples/Component.elm)
|
||||||
rather than working directly in plain Elm Architecture.
|
rather than working directly in plain Elm Architecture.
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
Using TEA, Style.
|
||||||
|
|
||||||
# Component Support
|
# Component Support
|
||||||
|
|
||||||
This module contains only convenience functions for working with nested
|
This module contains only convenience functions for working with nested
|
||||||
|
@ -153,16 +157,16 @@ import Material.Component as Component exposing (Indexed)
|
||||||
user actions in their model (notably Snackbar), the model is generic in the
|
user actions in their model (notably Snackbar), the model is generic in the
|
||||||
type of such "observations".
|
type of such "observations".
|
||||||
-}
|
-}
|
||||||
type alias Model obs =
|
type alias Model =
|
||||||
{ button : Indexed Button.Model
|
{ button : Indexed Button.Model
|
||||||
, textfield : Indexed Textfield.Model
|
, textfield : Indexed Textfield.Model
|
||||||
, snackbar : Maybe (Snackbar.Model obs)
|
, snackbar : Maybe (Snackbar.Model Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Initial model.
|
{-| Initial model.
|
||||||
-}
|
-}
|
||||||
model : Model obs
|
model : Model
|
||||||
model =
|
model =
|
||||||
{ button = Dict.empty
|
{ button = Dict.empty
|
||||||
, textfield = Dict.empty
|
, textfield = Dict.empty
|
||||||
|
@ -173,7 +177,7 @@ model =
|
||||||
{-| Action encompassing actions of all Material components.
|
{-| Action encompassing actions of all Material components.
|
||||||
-}
|
-}
|
||||||
type alias Action obs =
|
type alias Action obs =
|
||||||
Component.Action (Model obs) obs
|
Component.Action Model obs
|
||||||
|
|
||||||
|
|
||||||
{-| Update function for the above Action.
|
{-| Update function for the above Action.
|
||||||
|
@ -181,7 +185,7 @@ type alias Action obs =
|
||||||
update :
|
update :
|
||||||
(Action obs -> obs)
|
(Action obs -> obs)
|
||||||
-> Action obs
|
-> Action obs
|
||||||
-> Model obs
|
-> Model
|
||||||
-> (Model obs, Effects obs)
|
-> (Model, Effects obs)
|
||||||
update =
|
update =
|
||||||
Component.update
|
Component.update
|
||||||
|
|
|
@ -1,23 +1,26 @@
|
||||||
module Material.Snackbar
|
module Material.Snackbar
|
||||||
( Contents, Model, model, toast, snackbar, isActive, activeAction
|
( Contents, Model, add, model, toast, snackbar
|
||||||
, Action(Add, Action), update
|
, Action(Begin, End, Click), update
|
||||||
, view
|
, view
|
||||||
, Instance, instance, add
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
{-| TODO
|
{-| Material Design "Snackbar" component.
|
||||||
|
|
||||||
# Model
|
For live demo and intended use, see
|
||||||
@docs Contents, Model, model, toast, snackbar, isActive, activeAction
|
[here](http://localhost:8000/Demo.elm#/snackbar).
|
||||||
|
|
||||||
# Action, Update
|
# Generating messages
|
||||||
|
@docs Contents, toast, snackbar, add
|
||||||
|
|
||||||
|
# Elm Architecture
|
||||||
|
|
||||||
|
@docs Model, model
|
||||||
@docs Action, update
|
@docs Action, update
|
||||||
|
|
||||||
# View
|
|
||||||
@docs view
|
@docs view
|
||||||
|
|
||||||
# Component support
|
# Component support
|
||||||
@docs Instance, add, instance
|
Snackbar does not have component support. It must be used as a regular TEA
|
||||||
|
component.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
|
@ -28,33 +31,49 @@ import Task
|
||||||
import Time exposing (Time)
|
import Time exposing (Time)
|
||||||
import Maybe exposing (andThen)
|
import Maybe exposing (andThen)
|
||||||
|
|
||||||
import Material.Component as Component exposing (Indexed)
|
import Material.Helpers exposing (map2nd, delay)
|
||||||
import Material.Helpers exposing (mapFx, addFx, delay)
|
|
||||||
|
|
||||||
|
|
||||||
-- MODEL
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
{-| TODO
|
|
||||||
|
{-| Defines a single snackbar message. Usually, you would use either `toast`
|
||||||
|
or `snackbar` to construct `Contents`.
|
||||||
|
|
||||||
|
- `message` defines the (text) message displayed
|
||||||
|
- `action` defines a label for the action-button in the snackbar. If
|
||||||
|
no action is provided, the snackbar is a message-only toast.
|
||||||
|
- `payload` defines the data returned by Snackbar actions for this message.
|
||||||
|
You will usually choose this to be an Action of yours for later dispatch,
|
||||||
|
e.g., if your snackbar has an "Undo" action, you would store the
|
||||||
|
corresponding action as the payload.
|
||||||
|
- `timeout` is the amount of time the snackbar should be visible
|
||||||
|
- `fade` is the duration of the fading animation of the snackbar.
|
||||||
|
|
||||||
|
If you are satsified with the default timeout and fade, do not construct
|
||||||
|
values of this type yourself; use `snackbar` and `toast` below instead.
|
||||||
-}
|
-}
|
||||||
type alias Contents a =
|
type alias Contents a =
|
||||||
{ message : String
|
{ message : String
|
||||||
, action : Maybe (String, a)
|
, action : Maybe String
|
||||||
|
, payload : a
|
||||||
, timeout : Time
|
, timeout : Time
|
||||||
, fade : Time
|
, fade : Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| TODO
|
{-| Do not construct this yourself; use `model` below.
|
||||||
-}
|
-}
|
||||||
type alias Model a =
|
type alias Model a =
|
||||||
{ queue : List (Contents a)
|
{ queue : List (Contents a)
|
||||||
, state : State' a
|
, state : State a
|
||||||
, seq : Int
|
, seq : Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| TODO
|
{-| Default snackbar model.
|
||||||
-}
|
-}
|
||||||
model : Model a
|
model : Model a
|
||||||
model =
|
model =
|
||||||
|
@ -64,66 +83,37 @@ model =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Generate toast with given payload and message. Timeout is 2750ms, fade 250ms.
|
||||||
|
|
||||||
{-| Generate default toast with given message.
|
|
||||||
Timeout is 2750ms, fade 250ms.
|
|
||||||
-}
|
-}
|
||||||
toast : String -> Contents a
|
toast : a -> String -> Contents a
|
||||||
toast message =
|
toast payload message =
|
||||||
{ message = message
|
{ message = message
|
||||||
, action = Nothing
|
, action = Nothing
|
||||||
|
, payload = payload
|
||||||
, timeout = 2750
|
, timeout = 2750
|
||||||
, fade = 250
|
, fade = 250
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| Generate default snackbar with given message,
|
{-| Generate snackbar with given payload, message and label.
|
||||||
action-label, and action. Timeout is 2750ms, fade 250ms.
|
Timeout is 2750ms, fade 250ms.
|
||||||
-}
|
-}
|
||||||
snackbar : String -> String -> a -> Contents a
|
snackbar : a -> String -> String -> Contents a
|
||||||
snackbar message actionMessage action =
|
snackbar payload message label =
|
||||||
{ message = message
|
{ message = message
|
||||||
, action = Just (actionMessage, action)
|
, action = Just label
|
||||||
|
, payload = payload
|
||||||
, timeout = 2750
|
, timeout = 2750
|
||||||
, fade = 250
|
, fade = 250
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| TODO
|
|
||||||
(Bad name)
|
|
||||||
-}
|
|
||||||
isActive : Model a -> Maybe (Contents a)
|
|
||||||
isActive model =
|
|
||||||
case model.state of
|
|
||||||
Active c ->
|
|
||||||
Just c
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| TODO
|
|
||||||
-}
|
|
||||||
activeAction : Model a -> Maybe a
|
|
||||||
activeAction model =
|
|
||||||
isActive model
|
|
||||||
|> flip Maybe.andThen .action
|
|
||||||
|> Maybe.map snd
|
|
||||||
|
|
||||||
|
|
||||||
contentsOf : Model a -> Maybe (Contents a)
|
|
||||||
contentsOf model =
|
|
||||||
case model.state of
|
|
||||||
Inert -> Nothing
|
|
||||||
Active contents -> Just contents
|
|
||||||
Fading contents -> Just contents
|
|
||||||
|
|
||||||
|
|
||||||
-- SNACKBAR STATE MACHINE
|
-- SNACKBAR STATE MACHINE
|
||||||
|
|
||||||
|
|
||||||
type State' a
|
|
||||||
|
type State a
|
||||||
= Inert
|
= Inert
|
||||||
| Active (Contents a)
|
| Active (Contents a)
|
||||||
| Fading (Contents a)
|
| Fading (Contents a)
|
||||||
|
@ -131,40 +121,65 @@ type State' a
|
||||||
|
|
||||||
type Transition
|
type Transition
|
||||||
= Timeout
|
= Timeout
|
||||||
| Click
|
| Clicked
|
||||||
|
|
||||||
|
|
||||||
move : Transition -> Model a -> (Model a, Effects Transition)
|
forward : a -> Effects a
|
||||||
|
forward = Task.succeed >> Effects.task
|
||||||
|
|
||||||
|
|
||||||
|
next : Model a -> Effects Transition -> Effects (Action a)
|
||||||
|
next model =
|
||||||
|
Effects.map (Move model.seq)
|
||||||
|
|
||||||
|
|
||||||
|
move : Transition -> Model a -> (Model a, Effects (Action a))
|
||||||
move transition model =
|
move transition model =
|
||||||
case (model.state, transition) of
|
case (model.state, transition) of
|
||||||
(Inert, Timeout) ->
|
(Inert, Timeout) ->
|
||||||
tryDequeue model
|
tryDequeue model
|
||||||
|
|
||||||
(Active contents, _) ->
|
(Active contents, Clicked) ->
|
||||||
( { model | state = Fading contents }
|
( { model | state = Fading contents }
|
||||||
, delay contents.fade Timeout
|
, Effects.batch
|
||||||
|
[ delay contents.fade Timeout |> next model
|
||||||
|
, Click contents.payload |> forward
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
(Active contents, Timeout) ->
|
||||||
|
( { model | state = Fading contents }
|
||||||
|
, Effects.batch
|
||||||
|
[ delay contents.fade Timeout |> next model
|
||||||
|
, Begin contents.payload |> forward
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
(Fading contents, Timeout) ->
|
(Fading contents, Timeout) ->
|
||||||
( { model | state = Inert}
|
( { model | state = Inert}
|
||||||
, Effects.tick (\_ -> Timeout)
|
, Effects.batch
|
||||||
|
[ always Timeout |> Effects.tick |> next model
|
||||||
|
, End contents.payload |> forward
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
(model, none)
|
(model, none)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- NOTIFICATION QUEUE
|
-- NOTIFICATION QUEUE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enqueue : Contents a -> Model a -> Model a
|
enqueue : Contents a -> Model a -> Model a
|
||||||
enqueue contents model =
|
enqueue contents model =
|
||||||
{ model
|
{ model
|
||||||
| queue = List.append model.queue [contents]
|
| queue = List.append model.queue [contents]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
tryDequeue : Model a -> (Model a, Effects Transition)
|
tryDequeue : Model a -> (Model a, Effects (Action a))
|
||||||
tryDequeue model =
|
tryDequeue model =
|
||||||
case (model.state, model.queue) of
|
case (model.state, model.queue) of
|
||||||
(Inert, c :: cs) ->
|
(Inert, c :: cs) ->
|
||||||
|
@ -173,93 +188,86 @@ tryDequeue model =
|
||||||
, queue = cs
|
, queue = cs
|
||||||
, seq = model.seq + 1
|
, seq = model.seq + 1
|
||||||
}
|
}
|
||||||
, delay c.timeout Timeout
|
, Effects.batch
|
||||||
|
[ delay c.timeout Timeout |> Effects.map (Move (model.seq + 1))
|
||||||
|
, forward (Begin c.payload)
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
(model, none)
|
(model, none)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- ACTIONS, UPDATE
|
-- ACTIONS, UPDATE
|
||||||
|
|
||||||
|
|
||||||
{-| TODO
|
|
||||||
|
{-| Elm Architecture Action type.
|
||||||
|
The following actions are observable to you:
|
||||||
|
- `Begin a`. The snackbar is now displaying the message with payload `a`.
|
||||||
|
- `End a`. The snackbar is done displaying the message with payload `a`.
|
||||||
|
- `Click a`. The user clicked the action on the message with payload `a`.
|
||||||
|
You can consume these three actions without forwarding them to `Snackbar.update`.
|
||||||
|
(You still need to forward other Snackbar actions.)
|
||||||
-}
|
-}
|
||||||
type Action a
|
type Action a
|
||||||
= Add (Contents a)
|
= Begin a
|
||||||
| Action a
|
| End a
|
||||||
|
| Click a
|
||||||
|
-- Private
|
||||||
| Move Int Transition
|
| Move Int Transition
|
||||||
|
|
||||||
|
|
||||||
forwardClick : Transition -> Model a -> (Model a, Effects (Action a)) -> (Model a, Effects (Action a))
|
{-| Elm Architecture update function.
|
||||||
forwardClick transition model =
|
|
||||||
case (transition, model.state) of
|
|
||||||
(Click, Active contents) ->
|
|
||||||
contents.action
|
|
||||||
|> Maybe.map (snd >> Action >> Task.succeed >> Effects.task >> addFx)
|
|
||||||
|> Maybe.withDefault (\x -> x)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
\x -> x
|
|
||||||
|
|
||||||
|
|
||||||
liftTransition : (Model a, Effects Transition) -> (Model a, Effects (Action a))
|
|
||||||
liftTransition (model, effect) =
|
|
||||||
(model, Effects.map (Move model.seq) effect)
|
|
||||||
|
|
||||||
|
|
||||||
{-| TODO
|
|
||||||
-}
|
-}
|
||||||
update : Action a -> Model a -> (Model a, Effects (Action a))
|
update : Action a -> Model a -> (Model a, Effects (Action a))
|
||||||
update action model =
|
update action model =
|
||||||
case action of
|
case action of
|
||||||
Action _ ->
|
|
||||||
(model, none)
|
|
||||||
|
|
||||||
Add contents ->
|
|
||||||
enqueue contents model
|
|
||||||
|> tryDequeue
|
|
||||||
|> liftTransition
|
|
||||||
|
|
||||||
Move seq transition ->
|
Move seq transition ->
|
||||||
if seq == model.seq then
|
if seq == model.seq then
|
||||||
move transition model
|
move transition model
|
||||||
|> liftTransition
|
|
||||||
|> forwardClick transition model
|
|
||||||
else
|
else
|
||||||
(model, none)
|
(model, none)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
-- Begin, End, Click are for external consumption only.
|
||||||
|
(model, none)
|
||||||
|
|
||||||
|
|
||||||
|
{-| Add a message to the snackbar. If another message is currently displayed,
|
||||||
|
the provided message will be queued. You will be able to observe a `Begin` action
|
||||||
|
(see `Action` above) once the action begins displaying.
|
||||||
|
|
||||||
|
You must dispatch the returned effect for the Snackbar to begin displaying your
|
||||||
|
message.
|
||||||
|
-}
|
||||||
|
add : Contents a -> Model a -> (Model a, Effects (Action a))
|
||||||
|
add contents model =
|
||||||
|
enqueue contents model |> tryDequeue
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- VIEW
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
{-|
|
|
||||||
|
{-| Elm architecture update function.
|
||||||
-}
|
-}
|
||||||
view : Signal.Address (Action a) -> Model a -> Html
|
view : Signal.Address (Action a) -> Model a -> Html
|
||||||
view addr model =
|
view addr model =
|
||||||
let
|
let
|
||||||
active =
|
contents =
|
||||||
model.queue /= []
|
case model.state of
|
||||||
|
Inert -> Nothing
|
||||||
textBody =
|
Active c -> Just c
|
||||||
contentsOf model
|
Fading c -> Just c
|
||||||
|> Maybe.map (\c -> [ text c.message ])
|
|
||||||
|> Maybe.withDefault []
|
|
||||||
|
|
||||||
(buttonBody, buttonHandler) =
|
|
||||||
contentsOf model
|
|
||||||
|> (flip Maybe.andThen .action)
|
|
||||||
|> Maybe.map (\(msg, action') ->
|
|
||||||
([ text msg ],
|
|
||||||
[ onClick addr (Move model.seq Click) ])
|
|
||||||
)
|
|
||||||
|> Maybe.withDefault ([], [])
|
|
||||||
|
|
||||||
isActive =
|
isActive =
|
||||||
case model.state of
|
case model.state of
|
||||||
Inert -> False
|
Inert -> False
|
||||||
Active _ -> True
|
Active _ -> True
|
||||||
Fading _ -> False
|
Fading _ -> False
|
||||||
in
|
in
|
||||||
div
|
div
|
||||||
[ classList
|
[ classList
|
||||||
|
@ -272,70 +280,44 @@ view addr model =
|
||||||
[ div
|
[ div
|
||||||
[ class "mdl-snackbar__text"
|
[ class "mdl-snackbar__text"
|
||||||
]
|
]
|
||||||
textBody
|
(contents
|
||||||
|
|> Maybe.map (\c -> [ text c.message ])
|
||||||
|
|> Maybe.withDefault []
|
||||||
|
)
|
||||||
, button
|
, button
|
||||||
( class "mdl-snackbar__action"
|
( class "mdl-snackbar__action"
|
||||||
:: type' "button"
|
:: type' "button"
|
||||||
-- :: ariaHidden "true"
|
-- :: ariaHidden "true"
|
||||||
:: buttonHandler
|
:: ( contents
|
||||||
|
|> flip Maybe.andThen .action
|
||||||
|
|> Maybe.map (always [ onClick addr (Move model.seq Clicked) ])
|
||||||
|
|> Maybe.withDefault []
|
||||||
|
)
|
||||||
|
)
|
||||||
|
( contents
|
||||||
|
|> flip Maybe.andThen .action
|
||||||
|
|> Maybe.map (\action -> [ text action ])
|
||||||
|
|> Maybe.withDefault []
|
||||||
)
|
)
|
||||||
buttonBody
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- COMPONENT
|
-- COMPONENT
|
||||||
|
|
||||||
|
|
||||||
{-|
|
{- Component support for Snackbar is currently disabled. The type "Model a" of
|
||||||
|
the Snackbar Model escapes into the global Mdl model, polluting users model
|
||||||
|
with the type variable "a". This is unacceptable in itself; it makes
|
||||||
|
component support too hard to use for non-expert users.
|
||||||
|
|
||||||
|
This problem is compounded by the elm compiler bug
|
||||||
|
[#1192](https://github.com/elm-lang/elm-compiler/issues/1192), which causes
|
||||||
|
(apparently) unhelpful error messages on errors related to wrong use of type
|
||||||
|
constructors.
|
||||||
|
|
||||||
|
Component support for snackbar was implemented earlier. In case a solution
|
||||||
|
presents itself, the last commit to contain this support was:
|
||||||
|
|
||||||
|
f0a85912654713238694f48b1a4b7d5a7459965f
|
||||||
-}
|
-}
|
||||||
type alias Container s obs =
|
|
||||||
{ s | snackbar : Maybe (Model obs) }
|
|
||||||
|
|
||||||
|
|
||||||
{-|
|
|
||||||
-}
|
|
||||||
type alias Instance container obs =
|
|
||||||
Component.Instance (Model obs) container (Action obs) obs Html
|
|
||||||
|
|
||||||
|
|
||||||
{-|
|
|
||||||
-}
|
|
||||||
type alias Observer obs =
|
|
||||||
Component.Observer (Action obs) obs
|
|
||||||
|
|
||||||
|
|
||||||
actionObserver : Observer ons
|
|
||||||
actionObserver action =
|
|
||||||
case action of
|
|
||||||
Action action' ->
|
|
||||||
Just action'
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| Component instance.
|
|
||||||
-}
|
|
||||||
instance
|
|
||||||
: (Component.Action (Container c obs) obs -> obs)
|
|
||||||
-> (Model obs)
|
|
||||||
-> Instance (Container c obs) obs
|
|
||||||
|
|
||||||
instance lift model0 =
|
|
||||||
Component.instance1
|
|
||||||
view update .snackbar (\x y -> {y | snackbar = x}) lift model0 [ actionObserver ]
|
|
||||||
|
|
||||||
{-|
|
|
||||||
TODO
|
|
||||||
-}
|
|
||||||
add :
|
|
||||||
Contents obs
|
|
||||||
-> Instance (Container c obs) obs
|
|
||||||
-> (Container c obs)
|
|
||||||
-> (Container c obs, Effects obs)
|
|
||||||
add contents inst model =
|
|
||||||
let
|
|
||||||
(sb, fx) =
|
|
||||||
update (Add contents) (inst.get model)
|
|
||||||
in
|
|
||||||
(inst.set sb model, Effects.map inst.fwd fx)
|
|
||||||
|
|
Loading…
Reference in a new issue