{-----------------------------------------------------------------------------
    reactive-banana-threepenny
------------------------------------------------------------------------------}
{-# LANGUAGE ExistentialQuantification #-}

module Reactive.Banana.Threepenny (
    -- * Synopsis
    -- | Utility functions for interfacing with "Graphics.UI.Threepenny".
    -- Note: Useful, but I haven't done any serious design work on these.
    
    -- * General
    event, behavior, sink,
    module Reactive.Banana.Frameworks,
    
    -- * Specific widgets
    eventValue, behaviorValue, eventSelection,
    ) where

import Control.Monad (void)

import Reactive.Banana
import Reactive.Banana.Frameworks

import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core hiding (Event)

{-----------------------------------------------------------------------------
    General
------------------------------------------------------------------------------}
-- | Obtain an event for an element.
event :: Frameworks t => (x -> UI.Event a) -> x -> Moment t (Event t a)
event e widget = fromAddHandler . register $ e widget

-- | Behavior from an attribute.
-- Uses 'fromPoll', so may behave as you expect.
behavior :: Frameworks t => UI.ReadWriteAttr x i o -> x -> Moment t (Behavior t o)
behavior widget attr = fromPoll $ get widget attr

-- | "Animate" an attribute with a behavior.
sink :: Frameworks t
    => UI.ReadWriteAttr x i o -> Behavior t i -> Moment t x -> Moment t ()
sink attr b mx = do
    x <- mx
    i <- initial b
    liftIOLater $ void $ return x # set attr i
    e <- changes b
    reactimate $ (\i -> void $ return x # set attr i) <$> e 

{-----------------------------------------------------------------------------
    Specific widgets
------------------------------------------------------------------------------}
-- | Event that occurs when the /user/ changes the value of the input element.
eventValue :: Frameworks t => UI.Element -> Moment t (Event t String)
eventValue = event $ \widget ->
    UI.mapIO (const $ get value widget) (domEvent "keydown" widget)

-- | Behavior corresponding to user input in the element.
behaviorValue :: Frameworks t => UI.Element -> String -> Moment t (Behavior t String)
behaviorValue w s = stepper s <$> eventValue w

-- | Event that occurs when the /user/ changes the selection of a @<select>@ element.
eventSelection :: Frameworks t => UI.Element -> Moment t (Event t (Maybe Int))
eventSelection = event $ \widget ->
    UI.mapIO (const $ get UI.selection widget) (UI.click widget)