module React.Interpret (reactNodeToJSAny, setProp') where
import Control.Applicative
import Control.Monad
import qualified Data.Aeson as Aeson
import qualified Data.HashMap.Strict as H
import Data.List
import Data.Maybe
import Data.Text (Text)
import React.GHCJS
import React.Imports
import React.Registry
import React.Types
data Attr = Attr Text JSON
reactNodeToJSAny :: (sig -> IO ()) -> Int -> ReactNode sig -> IO JSAny
reactNodeToJSAny sigHandler componentId (ComponentElement elem) =
componentToJSAny sigHandler elem
reactNodeToJSAny sigHandler componentId (DomElement elem) =
domToJSAny sigHandler componentId elem
reactNodeToJSAny sigHandler componentId (ForeignClass cls props children) = do
children' <- reactNodeToJSAny sigHandler componentId children
props' <- toJSRef props
js_foreignParent cls props' children'
reactNodeToJSAny sigHandler _ (NodeText str) =
castRef <$> toJSRef (toJSString str)
reactNodeToJSAny sigHandler componentId (NodeSequence seq) = do
jsNodes <- mapM (reactNodeToJSAny sigHandler componentId) seq
castRef <$> toArray jsNodes
reactNodeToJSAny sigHandler componentId (LocalNode f node) =
let sigHandler' = sigHandler . f
in reactNodeToJSAny sigHandler' componentId node
jsName :: EvtType -> JSString
jsName BlurEvt = "onBlur"
jsName FocusEvt = "onFocus"
jsName ChangeEvt = "onChange"
jsName KeyDownEvt = "onKeyDown"
jsName KeyPressEvt = "onKeyPress"
jsName KeyUpEvt = "onKeyUp"
jsName ClickEvt = "onClick"
jsName DoubleClickEvt = "onDoubleClick"
jsName MouseEnterEvt = "onMouseEnter"
jsName MouseLeaveEvt = "onMouseLeave"
unHandler :: (s -> IO ())
-> EventHandler s
-> (RawEvent -> Maybe (IO ()), EvtType)
unHandler act (EventHandler handle ty) = (\e -> act <$> handle e, ty)
makeHandler :: Int
-> JSAny
-> (RawEvent -> Maybe (IO ()), EvtType)
-> IO ()
makeHandler componentId obj (handle, evtTy) = do
handle' <- handlerToJs handle
js_set_handler componentId (jsName evtTy) handle' obj
handlerToJs :: (RawEvent -> Maybe (IO ()))
-> IO (JSFun (RawEvent -> IO ()))
handlerToJs handle = syncCallback1 AlwaysRetain True $
fromMaybe (return ()) . handle
attrsToJson :: [Attr] -> JSON
attrsToJson = Aeson.toJSON . H.fromList . map unAttr where
unAttr (Attr name json) = (name, json)
separateAttrs :: [AttrOrHandler sig] -> ([Attr], [EventHandler sig])
separateAttrs attrHandlers = (map makeA as, map makeH hs) where
(as, hs) = partition isAttr attrHandlers
isAttr :: AttrOrHandler sig -> Bool
isAttr (StaticAttr _ _) = True
isAttr _ = False
makeA :: AttrOrHandler sig -> Attr
makeA (StaticAttr t j) = Attr t j
makeH :: AttrOrHandler sig -> EventHandler sig
makeH (Handler h) = h
attrHandlerToJSAny :: (sig -> IO ()) -> Int -> [AttrOrHandler sig] -> IO JSAny
attrHandlerToJSAny sigHandler componentId attrHandlers = do
let (attrs, handlers) = separateAttrs attrHandlers
starter <- castRef <$> toJSRef (attrsToJson attrs)
forM_ handlers $ makeHandler componentId starter . unHandler sigHandler
return starter
setMaybeKey :: Maybe JSString -> JSAny -> IO ()
setMaybeKey maybeKey attrsObj = when (isJust maybeKey) $ do
let Just key = maybeKey
setProp' "key" key attrsObj
setProp' :: ToJSRef a => String -> a -> JSAny -> IO ()
setProp' key prop obj = do
propRef <- toJSRef prop
setProp key propRef obj
componentToJSAny :: (sig -> IO ()) -> ReactComponentElement sig -> IO JSAny
componentToJSAny
sigHandler
(ReactComponentElement ty children maybeKey ref props) = do
let registry = classStateRegistry ty
componentId <- allocProps registry props
let sigHandler' insig = do
RegistryStuff _ state _ <-
lookupRegistry registry componentId
let (state', maybeExSig) = classTransition ty (state, insig)
setState registry state' componentId
case maybeExSig of
Just exSig -> sigHandler exSig
Nothing -> return ()
setHandler registry sigHandler' componentId
attrsObj <- newObj
setMaybeKey maybeKey attrsObj
setProp' "ref" ref attrsObj
setProp' "componentId" componentId attrsObj
let ty' = classForeign ty
children' <- reactNodeToJSAny sigHandler' componentId children
castRef <$> js_react_createElement_Class ty' attrsObj children'
domToJSAny :: (sig -> IO ()) -> Int -> ReactDOMElement sig -> IO JSAny
domToJSAny sigHandler componentId (ReactDOMElement ty props children maybeKey ref) = do
attrsObj <- attrHandlerToJSAny sigHandler componentId props
setMaybeKey maybeKey attrsObj
setProp' "ref" ref attrsObj
children' <- reactNodeToJSAny sigHandler componentId children
castRef <$> js_react_createElement_DOM ty attrsObj children'