{-----------------------------------------------------------------------------
reactive-banana-threepenny
Example: Bar tab with a variable number of widgets
------------------------------------------------------------------------------}
{-# LANGUAGE ScopedTypeVariables #-} -- allows "forall t. Moment t"
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
import Control.Monad (void)
import Data.Maybe (listToMaybe)
import Data.Traversable (sequenceA)
import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core hiding (Event)
import Reactive.Banana
import Reactive.Banana.Threepenny
{-----------------------------------------------------------------------------
Main
------------------------------------------------------------------------------}
main :: IO ()
main = do
startGUI Config
{ tpPort = 10000
, tpCustomHTML = Nothing
, tpStatic = ""
} setup
setup :: Window -> IO ()
setup window = do
return window # set title "Bar Tab"
msg <- UI.span # set text "Sum:"
total <- UI.span
add <- UI.button #+ [string "Add"]
remove <- UI.button #+ [string "Remove"]
body <- UI.getBody window
let networkDescription :: forall t. Frameworks t => Moment t ()
networkDescription = do
eAdd <- event UI.click add
eRemove <- event UI.click remove
let
newEntry :: Frameworks s
=> Moment s (Element, AnyMoment Behavior String)
newEntry = do
wentry <- liftIO $ UI.input # set value "0"
bentry <- trimB =<< behaviorValue wentry "0"
return (wentry, bentry)
eNewEntry <- execute $ (FrameworksMoment newEntry <$ eAdd)
let
eDoRemove = whenE (not . null <$> bEntries) eRemove
eEntries :: Event t [(Element, AnyMoment Behavior String)]
eEntries = accumE [] $
((\x -> (++ [x])) <$> eNewEntry) `union` (init <$ eDoRemove)
bEntries = stepper [] eEntries
let hideElement e = void $ element e # set style [("hidden","true")]
reactimate $ (hideElement . fst . last)
<$> bEntries <@ eDoRemove
let
ePrices :: Event t [AnyMoment Behavior Number]
ePrices = map (fmap readNumber . snd) <$> eEntries
bLayout :: Behavior t [IO Element]
bLayout = mkLayout . map fst <$> bEntries
mkLayout entries = [column $
[row [element add, element remove]] ++ map element entries
++ [row [element msg, element total]]]
bTotal :: Behavior t Number
bTotal = switchB (pure Nothing) $
(fmap sum . sequenceA) <$> ePrices
return total # sink text (showNumber <$> bTotal)
return body # sink layout bLayout
network <- compile networkDescription
actuate network
layout :: WriteAttr Element [IO Element]
layout = mkWriteAttr $ \i x -> void $ element x # set children [] #+ i
{-----------------------------------------------------------------------------
Utilities
------------------------------------------------------------------------------}
type Number = Maybe Double
instance Num Number where
(+) = liftA2 (+)
(-) = liftA2 (-)
(*) = liftA2 (*)
abs = fmap abs
signum = fmap signum
fromInteger = pure . fromInteger
readNumber s = listToMaybe [x | (x,"") <- reads s]
showNumber = maybe "--" show