All you need to create, append and render toast stacks in the Elm architecture.
Create a new toast with a fixed expiry. Toast's lifespan is expressed in milliseconds.
Toast.expireIn 5000 "I'll disappear in five seconds"This kind of toast has an interaction-based expiration. It'll be removed automatically in given time if user doesn't interact with the toast, but it'll stay visible if receives focus or mouse over.
When the interaction has ended and the toast lost both focus and mouse over the given amount of milliseconds is awaited before it's removed.
Toast.expireOnBlur 5000 "I won't go away while I'm focused!"Add a delay between toast exit phase and actual removal.
Toast.persistent { message = "hello, world", color = "#7f7" }
|> Toast.withExitTransition 1000Toast.Toast is something you'll need if you have to reference
the output type of persistent, expireIn, expireOnBlur,
this is one of those things you'll know when you need it, so don't worry about this.
The toast id is needed to reference a single Toast instance, for example when you want to close it.
Toast.Tray represents the stack where toasts are stored.
You probably want to use this opaque type in your model:
type alias MyToast =
{ message : String
, color : String
}
type alias Model =
{ tray : Toast.Tray MyToast
-- model fields...
}An empty tray, it's a thing you can put in an init.
init : anything -> ( Model, Cmd msg )
init _ =
( { tray = Toast.tray }, Cmd.none )Add a toast to a tray, produces an updated tray and a Cmd Toast.Msg.
updateTuple :
( Toast.Tray { message : String, color : String }
, Cmd Toast.Msg
)
updateTuple =
Toast.persistent { message = "hello, world", color = "#7f7" }
|> Toast.withExitTransition 1000
|> Toast.add currentTrayAdd a toast only if its content is not already in the tray.
Toast contents are compared with structural equality.
-- if currentTray already contains a toast with the same
-- message and color it won't be added again
Toast.persistent { message = "hello, world", color = "#7f7" }
|> Toast.addUnique currentTrayThis is what you need if, for example, you want to have toast
with unique content.message.
-- no two "hello, world" in the same tray
Toast.persistent { message = "hello, world", color = "#7f7" }
|> Toast.addUniqueBy .message currentTrayMost powerful addUnique version: it takes a function that
compares two toast contents.
type alias MyToast =
{ message : String
, color : String
}
sameMessageLength : MyToast -> MyToast -> Bool
sameMessageLength t1 t2 =
String.length t1.message == String.length t2.message
-- we can't have two toast with same message length
-- for some reason...
Toast.persistent { message = "hello, world", color = "#7f7" }
|> Toast.addUniqueWith sameMessageLength currentTrayInternal message, you probably want to do something like
type Msg
= ToastMsg Toast.Msg
-- other stuff...
| AddToast MyToastContentin your app Msg.
Nothing fancy here: given a Toast.Msg and a Toast.Tray
updates tray's state and produces a Cmd.
Helps in conversion between
( Toast.Tray, Cmd Toast.Msg ) and ( Model, Cmd Msg ).
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
AddToast content ->
Toast.persistent content
|> Toast.add model.tray
|> Toast.tuple ToastMsg model
ToastMsg tmsg ->
Toast.update tmsg model.tray
|> Toast.tuple ToastMsg modelToast.Config is something that holds information about
rendering stuff.
To create an empty Toast.Config you have to provide
a Toast.Msg -> msg function, this probably should look like
type Msg
= ToastMsg Toast.Msg
-- other stuff...
| AddToast MyToastContent
toastConfig : Toast.Config Msg
toastConfig =
Toast.config ToastMsgThis function is where our money are: all our data shrunk down
to a beautiful Html msg, ready to be served.
The first thing needed to make this magic is a viewToast function
that I'll try to explain how it works:
elm-toast
which you need to remember to attach to some nodecontent and other stuffHtml msg that represent your toast,
or something that contains your toast like a wrapper
or whateverSecondly you have to provide a Toast.Tray and last but not least your Toast.Config.
viewToast : List (Html.Attribute Msg) -> Toast.Info Toast -> Html Msg
viewToast attrs toast =
Html.div
attrs -- do not forget this little friend!
[ Html.text toast.content.message ]
Toast.render viewToast model.tray toastConfigA toast go through three phases:
Toast.Enter when it's just been added to a trayToast.In during the next render, immediately after enter phaseToast.Exit when it's about to be removedYou can control how much time a toast is kept in
Toast.Exit phase through withExitTransition.
Both Toast.Enter and Toast.Exit are considered transition phases.
Can be Toast.Focus or Toast.Blur, just like Toast.Phase
you'll have this information while rendering a toast through Toast.Info.
viewToast :
List (Html.Attribute Msg)
-> Toast.Info Toast
-> Html Msg
viewToast attributes toast =
Html.div
(if toast.interaction == Toast.Focus then
class "toast-active" :: attributes
else
attributes
)
[ Html.text toast.content.message ]
view : Model -> Html Msg
view model =
Toast.config ToastMsg
|> Toast.render viewToast model.trayToast.Info represent data publicly exposed about a toast.
You already know Toast.Phase and Toast.Interaction,
of course you also know content since this is your own data.
Meet id, this little field contains a unique value for each toast
that you need to pass to Toast.remove and Toast.exit.
Add custom attributes to every toasts.
Toast.config ToastMsg
|> Toast.withAttributes [ style "background" "#4a90e2" ]
|> Toast.render viewToast model.trayAdd custom attributes to toasts when they're focused.
Toast.config ToastMsg
|> Toast.withFocusAttributes [ style "box-shadow" "2px 3px 7px 2px #c8cdd0" ]
|> Toast.render viewToast model.trayAdd custom attributes to toasts only during their Toast.Enter phase.
import Html.Attributes exposing (style)
Toast.config ToastMsg
|> Toast.withEnterAttributes [ style "opacity" "0" ]
|> Toast.render viewToast model.trayAdd custom attributes to toasts only during their Toast.Exit phase.
Toast.config ToastMsg
|> Toast.withExitAttributes [ style "transform" "translateX(20em)" ]
|> Toast.render viewToast model.trayShortcut for withEnterAttributes plus withExitAttributes.
Toast.config ToastMsg
|> Toast.withEnterAttributes [ class "toast-fade-out" ]
|> Toast.withExitAttributes [ class "toast-fade-out" ]
|> Toast.render viewToast model.tray
Toast.config ToastMsg
|> Toast.withTransitionAttributes [ class "toast-fade-out" ]
|> Toast.render viewToast model.trayAdd custom attributes to rendered Toast.Tray.
Toast.config ToastMsg
|> Toast.withTrayAttributes [ class "nice-tray" ]
|> Toast.render viewToast model.traySet nodeName of rendered Toast.Tray.
By default this is "div". I know, as a String,
but hey that's what you get from Html.Keyed.node
Inside your viewToast you may want to remove your toast programmatically. Remove means that the toast is deleted right away. If you want to go through the exit transition use exit.
closeButton : Toast.Info Toast -> Html Msg
closeButton toast =
Html.div
[ onClick <| ToastMsg (Toast.exit toast.id) ]
[ Html.text "✘" ]
viewToast : List (Html.Attribute Msg) -> Toast.Info Toast -> Html Msg
viewToast attrs toast =
Html.div
attrs
[ Html.text toast.content.message
, closeButton toast
]
Create a new toast that won't be automatically removed, it will stay visible until you explicitly remove it.