{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} -- | 'Lucid.HtmlT' inspired monad for creating 'ReactElement's module Glazier.React.Element ( ReactElement -- constructor is not exported , unsafeCoerceElement , mkBranchElement , mkLeafElement , textElement , mkCombinedElements ) where import Control.DeepSeq import Control.Newtype.Generics import Data.String import qualified GHC.Generics as G import qualified GHCJS.Marshal.Pure as J import qualified GHCJS.Types as J import qualified JavaScript.Array as JA import qualified JavaScript.Extras as JE import qualified JavaScript.Object as JO -- | NB. No FromJS provided. See 'unsafeCoerceElement' below. newtype ReactElement = ReactElement JE.JSRep deriving (G.Generic, Show, J.IsJSVal, J.PToJSVal, JE.ToJS, IsString, NFData) instance Newtype ReactElement -- | Unfortunately, ReactJS did not export an easy way to check if something is a ReactElement, -- although they do so in the internal code with REACT_ELEMENT_TYPE. -- This function allow coercing a ReactElement from a JSVal -- and is named unsafe as a reminder that the coersion is unchecked. -- This function is required when receiving ReactElement from javascript (eg in a callback) -- or to interface with foreign React Elements. unsafeCoerceElement :: J.JSVal -> ReactElement unsafeCoerceElement = ReactElement . JE.JSRep -- | Create a react element (with children) from a HashMap of properties mkBranchElement :: JE.JSRep -> [JE.Property] -> [ReactElement] -> IO ReactElement mkBranchElement n props xs = js_mkBranchElement n (JE.fromProperties props) (JA.fromList $ JE.toJS <$> xs) -- | Create a react element (with no children) from a HashMap of properties mkLeafElement :: JE.JSRep -> [JE.Property] -> IO ReactElement mkLeafElement n props = js_mkLeafElement n (JE.fromProperties props) -- | Not an IO action because JSString is immutable textElement :: J.JSString -> ReactElement textElement = js_textElement -- | React only allows a single top most element. -- Provide a handly function to wrap a list of ReactElements inside a 'div' if required. -- If there is only one element in the list, then nothing is changed. mkCombinedElements :: [ReactElement] -> IO ReactElement mkCombinedElements xs = js_mkCombinedElements (JA.fromList $ JE.toJS <$> xs) #ifdef __GHCJS__ -- | This is an IO action because even if the same args was used -- a different ReactElement may be created, because JSVal -- and JSArray are mutable. foreign import javascript unsafe "$r = hgr$React().createElement($1, $2, $3);" js_mkBranchElement :: JE.JSRep -> JO.Object -> JA.JSArray -> IO ReactElement foreign import javascript unsafe "$r = hgr$React().createElement($1, $2);" js_mkLeafElement :: JE.JSRep -> JO.Object -> IO ReactElement foreign import javascript unsafe "$r = $1;" js_textElement :: J.JSString -> ReactElement -- | Wrap a list of ReactElements inside a 'div' foreign import javascript unsafe "$r = hgr$mkCombinedElements($1);" js_mkCombinedElements :: JA.JSArray -> IO ReactElement #else -- | This is an IO action because even if the same args was used -- a different ReactElement may be created, because JSVal -- and JSArray are mutable. js_mkBranchElement :: JE.JSRep -> JO.Object -> JA.JSArray -> IO ReactElement js_mkBranchElement _ _ _ = pure (ReactElement $ JE.JSRep J.nullRef) js_mkLeafElement :: JE.JSRep -> JO.Object -> IO ReactElement js_mkLeafElement _ _ = pure (ReactElement $ JE.JSRep J.nullRef) js_textElement :: J.JSString -> ReactElement js_textElement _ = ReactElement $ JE.JSRep J.nullRef js_mkCombinedElements :: JA.JSArray -> IO ReactElement js_mkCombinedElements _ = pure (ReactElement $ JE.JSRep J.nullRef) #endif