The following example that does something similar to what you describe.
modal
is presented with an address (to send a 'dismiss' event to), the current window dimensions, and an elm-html Html
component (which is the thing to be focussed, like a datepicker or a form).
We attach a click handler to the surrounding element; having given it an appropriate id we can work out if received clicks apply to it or the child, and forward them on appropriately. The only really clever bit is the deployment of customDecoder
to filter out clicks on the child element.
Elsewhere, on reception of the 'dismiss' event, our model state changes such that we no longer need to call modal
.
This is quite a large code sample that makes use of a fair few elm packages, so please ask if anything requires further explanation
import Styles exposing (..)
import Html exposing (Attribute, Html, button, div, text)
import Html.Attributes as Attr exposing (style)
import Html.Events exposing (on, onWithOptions, Options)
import Json.Decode as J exposing (Decoder, (:=))
import Result
import Signal exposing (Message)
modal : (Signal.Address ()) -> (Int, Int) -> Html -> Html
modal addr size content =
let modalId = "modal"
cancel = targetWithId (\_ -> Signal.message addr ()) "click" modalId
flexCss = [ ("display", "flex")
, ("align-items", "center")
, ("justify-content", "center")
, ("text-align", "center")
]
in div (
cancel :: (Attr.id modalId) :: [style (flexCss ++ absolute ++ dimensions size)]
) [content]
targetId : Decoder String
targetId = ("target" := ("id" := J.string))
isTargetId : String -> Decoder Bool
isTargetId id = J.customDecoder targetId (\eyed -> if eyed == id then Result.Ok True else Result.Err "nope!")
targetWithId : (Bool -> Message) -> String -> String -> Attribute
targetWithId msg event id = onWithOptions event stopEverything (isTargetId id) msg
stopEverything = (Options True True)