How can I obtain a Dynamic Bool that corresponds to the mouse being over a certain element?

200 views Asked by At

Suppose I have a button and a square, like this:

main = mainWidget $ do
    x <- button "Change color."
    y <- toggle False x
    z <- mapDyn style y
    elDynAttr "div" z blank

style :: Bool -> Map String String
style b | b         = "style" =: "height: 10ex; width: 10ex; background-color: #f00;"
        | otherwise = "style" =: "height: 10ex; width: 10ex; background-color: #900;"

The colour of the square will alternate between bright and dark red when the button is pressed.

I want to replace the button with a green rectangle in such a way that, when mouse pointer is over it, the red square is bright, otherwise dark. For bonus points, I would like the green rectangle to be a div tabindex=0 and to make sure the red square is bright when it has focus, but, once I understand the general approach, I will likely be able to figure that out by myself.

My guess is that there is some API that allows adding event handlers to el, but I did not find one so far. And maybe there is a different approch altogether, that I could not know?

1

There are 1 answers

0
Ignat Insarov On

The stuff you need is all found in Reflex.Dom.Widget.Basic. If you create a widget with a function that has a prime in its name, such as el', you can get a hold of an object of type El, which is a member of HasDomEvent. domEvent, the only method of this class, lets you extract an event stream corresponding to one of a selection of event names. Mixing the events together and converting them to an appropriate Dynamic is then done with some usual Reflex tooling found in Reflex.Class and Reflex.Dynamic. You do need to familiarize yourself with these interfaces, but there is a helpful quick reference.

(Make sure to use the documentation matching the version of Reflex you build against, as there are significant changes between versions.)

One way to encode your case is as follows:

main = mainWidget $ do
    y <- greenBox
    z <- mapDyn style y
    elDynAttr "div" z blank

style :: Bool -> Map String String
style ...  -- Abridged for brevity.

greenBox :: MonadWidget t m => m (Dynamic t Bool)
greenBox = do
    (e, _) <- elAttr' "div" ("style" =: "height: 10ex; width: 10ex; background-color: #0c0;") blank
    let
        eEnter = domEvent Mouseenter e
        eLeave = domEvent Mouseleave e
    holdDyn False . leftmost $ [True <$ eEnter, False <$ eLeave]

What we do here is create a widget for the green box that returns a Dynamic indicating whether it is "activated". Everything else is just as it were in your initial example.

Thanks to /u/dalaing for advising me on this.