Reflex dropdown menu populated by sum type values

142 views Asked by At

I am following this tutorial, from which the below example comes: https://github.com/hansroland/reflex-dom-inbits/blob/master/tutorial.md

bodyElement :: MonadWidget t m => m ()
bodyElement = el "div" $ do
  el "h2" $ text "Dropdown"
  text "Select country "
  dd <- dropdown 2 (constDyn countries) def
  el "p" $ return ()
  let selItem = result <$> value dd
  dynText selItem

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

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

I want replace constDyn countries from the above with a function that takes the type (?) constructors of a sum type and uses them as the elements of the drop down.

For example, if I have the below sum type, I want the drop down to display "Workout" and "Run". And if I later add, say, Solo_WatchPracticeTape, the dropdown would automatically add "Watch Practice Tape".

data SoloPersonPracticeType =
        Solo_Workout 
    |   Solo_Run

I infer that I need to make a function that associates each sum type with a string, then another function that turns all of those things into something traversable that can be taken in by constDyn. But I do not see how to implement this.

EDIT:

I am trying this, but it is still unfinished:

displaySoloPersonPracticeType :: SoloPersonPracticeType -> [Char]
displaySoloPersonPracticeType Solo_Workout = "Workout"
displaySoloPersonPracticeType Solo_Run = "Run"

deriving instance Enum SoloPersonPracticeType 
deriving instance Bounded SoloPersonPracticeType 


instance Universe SoloPersonPracticeType where 
    universe = [minBound..]

But I still do not understand how to feed this into constDyn in the bodyElement function.

1

There are 1 answers

3
Elliot Cameron On BEST ANSWER

Classes like Universe and Enum are generally the tool you want for this. You effectively want a way to generate a list from these instances and that's pretty easy:

[(e, show e) | e <- [minBound..]]

Here show presumes your type has a Show instance. You could as easily replace that with your own custom "showy" thing:

showMyType :: MyType -> Text
showMyType = \case
  MyType_A -> "A"
  MyType_B -> "B"
  ...

Finally, all you need is to pass this entire list as a Dynamic t (Map MyType Text) to dropdown like this: constDyn (Map.fromList [(e, showMyType e) | e <- [minBound..]])