Search code examples
haskellreflex

How to get a DoubleClicked event from a reflex-dom listbox


The following code displays a reflex-dom dropdown element visually as a listbox and displays at the bottom always the last selected (clicked) line.

{-# LANGUAGE OverloadedStrings #-}
import           Reflex.Dom
import qualified Data.Text as T
import qualified Data.Map as Map
import           Data.Monoid((<>))
import           Data.Maybe (fromJust)

main :: IO ()
main = mainWidget $ el "div" $ do
  dd <- dropdown "2" (constDyn countries) $ def & attributes .~ constDyn ("size" =: "10")
  el "p" $ return ()
  let selItem = result <$> value dd 
  dynText selItem
  return ()

countries :: Map.Map T.Text T.Text
countries = Map.fromList [("1", "France"), ("2", "Switzerland"), ("3", "Germany"), ("4", "Italy"), ("5", "USA")]

result :: T.Text -> T.Text
result key = "You selected: " <> fromJust (Map.lookup key countries)

I want to change this code, so it displays at the bottom always the last double clicked line!

I tried several things

  • use the domEvent function: This does not work, because Dropdown is not an instance of the HasDomEvent class.
  • filter the event in the value _dropdown_change of the Dropdown record. But I didn't find any way to filter only DoubleClick events.
  • use the newtype EventSelector. Again I don't see hwo I can use it.

Question: How can I get at the double click event?


Solution

  • You can use domEvent to get at the double click.

    The following code uses elAttr to create a listbox like the one you created with dropdown. The domEvent function is used to create double click Event's for each of the listbox options which are then combined to get a Dynamic that represents the most recently double clicked option.

    I left the dropbox code in place for comparison purposes.

    {-# LANGUAGE OverloadedStrings #-}
    import           Reflex.Dom
    import qualified Data.Text as T
    import qualified Data.Map as Map
    import           Data.Monoid((<>))
    import           Data.Maybe (fromJust)
    import           Data.Traversable (forM)
    
    
    -- a listbox that responds to double clicks
    listbox :: MonadWidget t m =>    T.Text                -- default
                                  -> Map.Map T.Text T.Text -- entries
                                  -> Map.Map T.Text T.Text -- attributes
                                  -> m (Dynamic t T.Text)  
    listbox def entries attr = do
      optEv <- elAttr "select" attr $ 
                 forM (Map.toList entries) $ \(i,c) -> do
    
                   let sel = if i == def
                             then "selected" =: "selected"
                             else mempty 
    
                   (e, _) <- elAttr' "option"  sel $ text c
    
                   return (i <$ domEvent Dblclick e)
    
      holdDyn def $ leftmost optEv
    
    main :: IO ()
    main = mainWidget $ el "div" $ do
      -- original code (responds to single clicks)
      dd <- dropdown "2" (constDyn countries) $ def & attributes .~ constDyn ("size" =: "10")
      el "p" $ return ()
      let selItem = result <$> value dd
      dynText selItem
    
      el "p" $ return ()
    
      -- new code (responds to double clicks)
      lb <- listbox "3" countries ("size" =: "10")
      el "p" $ return ()
      let dblItem = result <$> lb
      dynText dblItem
    
      return ()
    
    countries :: Map.Map T.Text T.Text
    countries = Map.fromList [("1", "France"), ("2", "Switzerland"), ("3", "Germany"), ("4", "Italy"), ("5", "USA")]
    
    result :: T.Text -> T.Text
    result key = "You selected: " <> fromJust (Map.lookup key countries)