ghcjs-vdom-0.2.0.0: src/GHCJS/VDOM.hs
{-# LANGUAGE QuasiQuotes #-}
{-|
Bindings for the virtual-dom library.
The virtual-dom diff function has been changed slightly to allow it to work
with full functionality in asynchronous threads.
It's possible to implement the bindings without the modifications at the
cost of tail-call optimization and preemptive threading in the diff, by
recursively forcing the thunks in synchronous threads.
-}
module GHCJS.VDOM ( Attributes, Children
, Attributes', Children'
, VMount, VNode, VComp, DComp, Patch, DOMNode
, mount, unmount
, diff, patch
, memo, memoKey
) where
import GHCJS.Types
import GHCJS.Foreign.QQ
import GHCJS.Prim
import GHCJS.Marshal.Pure
import Control.Monad
import Data.Coerce
import System.IO.Unsafe
import GHCJS.VDOM.Internal.Types
import GHCJS.VDOM.Internal.Thunk
import GHCJS.VDOM.Internal (j,J)
import qualified GHCJS.VDOM.Internal as I
class MemoNode a where memoNode :: (J, [JSIdent], a) -> a
instance MemoNode VNode
where
memoNode (_,[],a) = a
memoNode (k,xs,v) =
let vd = I.unsafeExportValue v
xs1 = unsafePerformIO (toJSArray $ coerce xs)
in VNode [j| h$vdom.th(`vd, `xs1, `k, true) |]
{-# INLINE memoNode #-}
instance MemoNode b => MemoNode (a -> b)
where
memoNode (k,xs,f) = \a -> memoNode (k, I.objectIdent a:xs, f a)
{-# INLINE memoNode #-}
memoKey :: MemoNode a => JSString -> a -> a
memoKey k = memo' (pToJSVal k)
{-# NOINLINE memoKey #-}
memo :: MemoNode a => a -> a
memo = memo' [j| $r = null; |]
{-# NOINLINE memo #-}
memo' :: MemoNode a => J -> a -> a
memo' k f = memoNode (k,[I.objectIdent f],f)
{-# INLINE memo' #-}
{-|
Mount a virtual-dom tree in the real DOM. The mount point can be updated
with patch.
-}
mount :: DOMNode -> VNode -> IO VMount
mount n v = do
m <- VMount <$> [js| h$vdom.mount(`n) |]
void $ patch m =<< diff m v
return m
{-# INLINE mount #-}
{-|
Remove a virtual-dom tree from the document. It's important to use
unmount rather than removing the mount point any other way since this
releases all associated Haskell data structures.
-}
unmount :: VMount -> IO ()
unmount (VMount m) = [jsu_| h$vdom.unmount(`m); |]
{-# INLINE unmount #-}
{-|
Compute a patch to update the mounted tree to match the virtual-dom tree
-}
diff :: VMount -> VNode -> IO Patch
diff (VMount m) (VNode v) = do
thunks <- [jsu| [] |]
patch <- [jsu| `m.diff(`v, `thunks) |]
forceThunks thunks
forcePatch [j| `patch.patch |]
return (Patch patch)
{-# INLINE diff #-}
{-|
Apply a patch to a mounted virtual-dom tree. Fails if the tree has already
been patched after the diff was computed.
-}
patch :: VMount -> Patch -> IO Bool
patch (VMount m) (Patch p) = [jsu| `m.patch(`p); |]
{-# INLINE patch #-}