@@ -46,46 +46,11 @@ module Reflex.Dom.Builder.Hydration
46 , askEvents
46 , askEvents
48 , textNodeInternal
48 , textNodeInternal
49− , deleteBetweenExclusive
50− , extractBetweenExclusive
51− , deleteBetweenInclusive
52− , extractBetweenInclusive
55 , SupportsHydrationDomBuilder
49 , SupportsHydrationDomBuilder
57− , collectUpToGivenParent
58 , EventFilterTriggerRef (..)
50 , EventFilterTriggerRef (..)
60 , makeElement
52 , makeElement
61− , GhcjsDomHandler (..)
62− , GhcjsDomHandler1 (..)
64− , GhcjsEventFilter (..)
67− , GhcjsEventSpec (..)
69− , ghcjsEventSpec_filters
70− , ghcjsEventSpec_handler
71− , GhcjsEventHandler (..)
72−#ifndef USE_TEMPLATE_HASKELL
75 , drawChildUpdate
53 , drawChildUpdate
76− , ChildReadyState (..)
77− , ChildReadyStateInt (..)
81− , defaultDomEventHandler
82− , defaultDomWindowEventHandler
89 -- * Internal
54 -- * Internal
90 , traverseDMapWithKeyWithAdjust'
55 , traverseDMapWithKeyWithAdjust'
91 , hoistTraverseWithKeyWithAdjust
56 , hoistTraverseWithKeyWithAdjust
@@ -98,7 +63,7 @@ import Reflex.Adjustable.Class
98import Reflex.Class as Reflex
63import Reflex.Class as Reflex
99import qualified Reflex.Spider.Internal as Spider
64import qualified Reflex.Spider.Internal as Spider
100import Reflex.Dom.Builder.Class
65import Reflex.Dom.Builder.Class
101−import Reflex.Dom.Builder.Immediate hiding (askParent, append, extractBetweenExclusive, extractUpTo, collectUpToGivenParent, askEvents, wrap, makeElement, textNodeInternal, commentNodeInternal, wrapDomEvent, mkHasFocus, insertBefore, deleteBetweenExclusive, traverseIntMapWithKeyWithAdjust', traverseDMapWithKeyWithAdjust', hoistTraverseWithKeyWithAdjust, deleteUpTo, collectUpTo, hoistTraverseIntMapWithKeyWithAdjust, drawChildUpdate, subscribeDomEvent)
66+import Reflex.Dom.Builder.Immediate hiding (askEvents, askParent, append, wrap, makeElement, textNodeInternal, commentNodeInternal, traverseIntMapWithKeyWithAdjust', traverseDMapWithKeyWithAdjust', hoistTraverseWithKeyWithAdjust, hoistTraverseIntMapWithKeyWithAdjust, drawChildUpdate)
102import Reflex.Dynamic
67import Reflex.Dynamic
103import Reflex.Host.Class
68import Reflex.Host.Class
104import qualified Reflex.Patch.DMap as PatchDMap
69import qualified Reflex.Patch.DMap as PatchDMap
@@ -232,8 +197,6 @@ data HydrationDomBuilderEnv = HydrationDomBuilderEnv
232 -- ^ Action to take when all children are ready --TODO: we should probably get rid of this once we invoke it
197 -- ^ Action to take when all children are ready --TODO: we should probably get rid of this once we invoke it
233 , _hydrationDomBuilderEnv_hydrationMode :: {-# UNPACK #-} !(IORef HydrationMode)
198 , _hydrationDomBuilderEnv_hydrationMode :: {-# UNPACK #-} !(IORef HydrationMode)
234 -- ^ In hydration mode? Should be switched to `HydrationMode_Immediate` after hydration is finished
199 -- ^ In hydration mode? Should be switched to `HydrationMode_Immediate` after hydration is finished
235− , _hydrationDomBuilderEnv_prerenderDepth :: {-# UNPACK #-} !(IORef Word)
239data DOM t m
202data DOM t m
@@ -264,7 +227,6 @@ runDOMTree = \case
265-- | The monad which performs the delayed actions to reuse prerendered nodes and set up events
228-- | The monad which performs the delayed actions to reuse prerendered nodes and set up events
266-- State contains reference to the previous node sibling, if any.
229-- State contains reference to the previous node sibling, if any.
267−-- TODO: This probably doesn't need to be a full transformer of `m`, seems like we only need (Reflex t, MonadJSM m, MonadReflexCreateTrigger t m, DomRenderHook t m)
268newtype HydrationRunnerT t m a = HydrationRunnerT { unHydrationRunnerT :: StateT (Maybe Node) (ReaderT Node (DomRenderHookT t m)) a }
230newtype HydrationRunnerT t m a = HydrationRunnerT { unHydrationRunnerT :: StateT (Maybe Node) (ReaderT Node (DomRenderHookT t m)) a }
269 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
231 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
270#if MIN_VERSION_base(4,9,1)
232#if MIN_VERSION_base(4,9,1)
@@ -273,13 +235,19 @@ newtype HydrationRunnerT t m a = HydrationRunnerT { unHydrationRunnerT :: StateT
275localRunner :: Monad m => HydrationRunnerT t m a -> Maybe Node -> Node -> HydrationRunnerT t m a
237localRunner :: Monad m => HydrationRunnerT t m a -> Maybe Node -> Node -> HydrationRunnerT t m a
276−localRunner (HydrationRunnerT m) prev = HydrationRunnerT . lift . lift . runReaderT (evalStateT m prev)
238+localRunner (HydrationRunnerT m) prev parent = HydrationRunnerT . lift $ local (\_ -> parent) (evalStateT m prev)
278runHydrationRunnerT
240runHydrationRunnerT
279 :: (MonadRef m, Ref m ~ IORef, Monad m, PerformEvent t m, MonadFix m, MonadReflexCreateTrigger t m, MonadJSM m, MonadJSM (Performable m))
241 :: (MonadRef m, Ref m ~ IORef, Monad m, PerformEvent t m, MonadFix m, MonadReflexCreateTrigger t m, MonadJSM m, MonadJSM (Performable m))
280 => HydrationRunnerT t m a -> Maybe Node -> Node -> Chan [DSum (EventTriggerRef t) TriggerInvocation] -> m a
242 => HydrationRunnerT t m a -> Maybe Node -> Node -> Chan [DSum (EventTriggerRef t) TriggerInvocation] -> m a
281runHydrationRunnerT (HydrationRunnerT m) prev parent = runDomRenderHookT (runReaderT (evalStateT m prev) parent)
243runHydrationRunnerT (HydrationRunnerT m) prev parent = runDomRenderHookT (runReaderT (evalStateT m prev) parent)
245+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (HydrationRunnerT t m) where
246+ {-# INLINABLE newEventWithTrigger #-}
247+ newEventWithTrigger = lift . newEventWithTrigger
248+ {-# INLINABLE newFanEventWithTrigger #-}
249+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
283instance MonadTrans (HydrationRunnerT t) where
251instance MonadTrans (HydrationRunnerT t) where
284 lift = HydrationRunnerT . lift . lift . lift
252 lift = HydrationRunnerT . lift . lift . lift
@@ -288,9 +256,6 @@ hydrateDOM dom = getHydrationMode >>= \case
288 HydrationMode_Hydrating -> HydrationDomBuilderT $ modify (dom :)
256 HydrationMode_Hydrating -> HydrationDomBuilderT $ modify (dom :)
289 HydrationMode_Immediate -> pure ()
257 HydrationMode_Immediate -> pure ()
291−-- TODO probably can separate these two monads entirely, they have grown apart
292−-- over the course of refactoring
294-- | A monad for DomBuilder which just gets the results of children and pushes
259-- | A monad for DomBuilder which just gets the results of children and pushes
295-- work into an action that is delayed until after postBuild (to match the
260-- work into an action that is delayed until after postBuild (to match the
296-- static builder). The action runs in 'HydrationRunnerT', which performs the
261-- static builder). The action runs in 'HydrationRunnerT', which performs the
@@ -404,10 +369,6 @@ getPreviousNode = HydrationRunnerT get
404setPreviousNode :: Monad m => Maybe DOM.Node -> HydrationRunnerT t m ()
369setPreviousNode :: Monad m => Maybe DOM.Node -> HydrationRunnerT t m ()
405setPreviousNode = HydrationRunnerT . put
370setPreviousNode = HydrationRunnerT . put
407−{-# INLINABLE getPrerenderDepth #-}
408−getPrerenderDepth :: MonadIO m => HydrationDomBuilderT t m Word
409−getPrerenderDepth = liftIO . readIORef =<< HydrationDomBuilderT (asks _hydrationDomBuilderEnv_prerenderDepth)
411{-# INLINABLE getParent #-}
372{-# INLINABLE getParent #-}
412getParent :: MonadIO m => HydrationDomBuilderT t m DOM.Node
373getParent :: MonadIO m => HydrationDomBuilderT t m DOM.Node
413getParent = liftIO . readIORef =<< HydrationDomBuilderT (asks _hydrationDomBuilderEnv_parent)
374getParent = liftIO . readIORef =<< HydrationDomBuilderT (asks _hydrationDomBuilderEnv_parent)
@@ -428,7 +389,6 @@ getHydrationMode = liftIO . readIORef =<< HydrationDomBuilderT (asks _hydrationD
428askEvents :: Monad m => HydrationDomBuilderT t m (Chan [DSum (EventTriggerRef t) TriggerInvocation])
389askEvents :: Monad m => HydrationDomBuilderT t m (Chan [DSum (EventTriggerRef t) TriggerInvocation])
429askEvents = HydrationDomBuilderT . lift . lift . DomRenderHookT . lift $ TriggerEventT.askEvents
390askEvents = HydrationDomBuilderT . lift . lift . DomRenderHookT . lift $ TriggerEventT.askEvents
432{-# INLINABLE makeNodeInternal #-}
392{-# INLINABLE makeNodeInternal #-}
433makeNodeInternal
393makeNodeInternal
434 :: forall node t m. (MonadJSM m, IsNode node, Typeable node)
394 :: forall node t m. (MonadJSM m, IsNode node, Typeable node)
@@ -469,74 +429,8 @@ hydrateNode check constructor = do
469 setPreviousNode $ Just $ toNode n
429 setPreviousNode $ Just $ toNode n
473−-- | s and e must both be children of the same node and s must precede e;
474−-- all nodes between s and e will be removed, but s and e will not be removed
475−deleteBetweenExclusive :: (MonadJSM m, IsNode start, IsNode end) => start -> end -> m ()
476−deleteBetweenExclusive s e = do
477− df <- createDocumentFragment =<< getOwnerDocumentUnchecked s
478− extractBetweenExclusive df s e -- In many places in HydrationDomBuilderT, we assume that things always have a parent; by adding them to this DocumentFragment, we maintain that invariant
480−-- | s and e must both be children of the same node and s must precede e; all
481−-- nodes between s and e will be moved into the given DocumentFragment, but s
482−-- and e will not be moved
483−extractBetweenExclusive :: (MonadJSM m, IsNode start, IsNode end) => DOM.DocumentFragment -> start -> end -> m ()
484−extractBetweenExclusive df s e = liftJSM $ do
485− f <- eval ("(function(df,s,e) { var x; for(;;) { x = s['nextSibling']; if(e===x) { break; }; df['appendChild'](x); } })" :: Text)
486− void $ call f f (df, s, e)
488−-- | s and e must both be children of the same node and s must precede e;
489−-- all nodes between s and e will be removed, including s and e
490−deleteBetweenInclusive :: (MonadJSM m, IsNode start, IsNode end) => start -> end -> m ()
491−deleteBetweenInclusive s e = do
492− df <- createDocumentFragment =<< getOwnerDocumentUnchecked s
493− extractBetweenInclusive df s e -- In many places in HydrationDomBuilderT, we assume that things always have a parent; by adding them to this DocumentFragment, we maintain that invariant
495−-- | s and e must both be children of the same node and s must precede e; all
496−-- nodes between s and e will be moved into the given DocumentFragment,
498−extractBetweenInclusive :: (MonadJSM m, IsNode start, IsNode end) => DOM.DocumentFragment -> start -> end -> m ()
499−extractBetweenInclusive df s e = liftJSM $ do
500− f <- eval ("(function(df,s,e) { var x = s; var next; for(;;) { next = x['nextSibling']; df['appendChild'](x); if(e===x) { break; }; x = next; } })" :: Text)
501− void $ call f f (df, s, e)
503−-- | s and e must both be children of the same node and s must precede e;
504−-- s and all nodes between s and e will be removed, but e will not be removed
505−{-# INLINABLE deleteUpTo #-}
506−deleteUpTo :: (MonadJSM m, IsNode start, IsNode end) => start -> end -> m ()
508− df <- createDocumentFragment =<< getOwnerDocumentUnchecked s
509− extractUpTo df s e -- In many places in HydrationDomBuilderT, we assume that things always have a parent; by adding them to this DocumentFragment, we maintain that invariant
511−extractUpTo :: (MonadJSM m, IsNode start, IsNode end) => DOM.DocumentFragment -> start -> end -> m ()
513−--NOTE: Although wrapping this javascript in a function seems unnecessary, GHCJS's optimizer will break it if it is entered without that wrapping (as of 2017-09-04)
514−foreign import javascript unsafe
515− "(function() { var x = $2; while(x !== $3) { var y = x['nextSibling']; $1['appendChild'](x); x = y; } })()"
516− extractUpTo_ :: DOM.DocumentFragment -> DOM.Node -> DOM.Node -> IO ()
517−extractUpTo df s e = liftJSM $ extractUpTo_ df (toNode s) (toNode e)
519−extractUpTo df s e = liftJSM $ do
520− f <- eval ("(function(df,s,e){ var x = s; var y; for(;;) { y = x['nextSibling']; df['appendChild'](x); if(e===y) { break; } x = y; } })" :: Text)
521− void $ call f f (df, s, e)
524type SupportsHydrationDomBuilder t m = (Reflex t, MonadJSM m, MonadHold t m, MonadFix m, MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref JSM, Adjustable t m, PrimMonad m, PerformEvent t m, MonadJSM (Performable m))
432type SupportsHydrationDomBuilder t m = (Reflex t, MonadJSM m, MonadHold t m, MonadFix m, MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref JSM, Adjustable t m, PrimMonad m, PerformEvent t m, MonadJSM (Performable m))
526−{-# INLINABLE collectUpTo #-}
527−collectUpTo :: (MonadJSM m, IsNode start, IsNode end) => start -> end -> m DOM.DocumentFragment
529− currentParent <- getParentNodeUnchecked e -- May be different than it was at initial construction, e.g., because the parent may have dumped us in from a DocumentFragment
530− collectUpToGivenParent currentParent s e
532−{-# INLINABLE collectUpToGivenParent #-}
533−collectUpToGivenParent :: (MonadJSM m, IsNode parent, IsNode start, IsNode end) => parent -> start -> end -> m DOM.DocumentFragment
534−collectUpToGivenParent currentParent s e = do
535− doc <- getOwnerDocumentUnchecked currentParent
536− df <- createDocumentFragment doc
540-- | This 'wrap' is only partial: it doesn't create the 'EventSelector' itself
434-- | This 'wrap' is only partial: it doesn't create the 'EventSelector' itself
541{-# INLINABLE wrap #-}
435{-# INLINABLE wrap #-}
@@ -626,15 +520,8 @@ makeElement elementTag cfg child = do
626 let env' = env { _hydrationDomBuilderEnv_parent = parent}
520 let env' = env { _hydrationDomBuilderEnv_parent = parent}
627 (result, childDom) <- HydrationDomBuilderT $ lift $ lift $ runStateT (runReaderT (unHydrationDomBuilderT child) env') []
521 (result, childDom) <- HydrationDomBuilderT $ lift $ lift $ runStateT (runReaderT (unHydrationDomBuilderT child) env') []
628 wrapResult <- liftIO newEmptyMVar
522 wrapResult <- liftIO newEmptyMVar
629− prerenderDepth <- getPrerenderDepth
630 let activateElement = do
523 let activateElement = do
631− e <- case prerenderDepth of
632− 0 -> hydrateNode hasSSRAttribute DOM.Element
524+ e <- hydrateNode hasSSRAttribute DOM.Element
635− maybe (askParent >>= \p -> Node.appendChild_ p e) (insertAfter e) =<< getPreviousNode
636− setPreviousNode $ Just $ toNode e
638 -- Update the parent node used by the children
525 -- Update the parent node used by the children
639 liftIO $ writeIORef parent $ toNode e
526 liftIO $ writeIORef parent $ toNode e
640 -- Setup events, store the result so we can wait on it later
527 -- Setup events, store the result so we can wait on it later
@@ -674,16 +561,9 @@ textNodeInternal !t mSetContents = getHydrationMode >>= \case
675 HydrationMode_Hydrating -> do
562 HydrationMode_Hydrating -> do
676 doc <- askDocument
563 doc <- askDocument
677− prerenderDepth <- getPrerenderDepth
678 let activateText = do
564 let activateText = do
679 liftIO $ putStrLn $ "Action: Text: " <> show (DOM.toJSString t)
565 liftIO $ putStrLn $ "Action: Text: " <> show (DOM.toJSString t)
680− n <- case prerenderDepth of
681− 0 -> hydrateNode (const $ pure True) DOM.Text
566+ n <- hydrateNode (const $ pure True) DOM.Text
683− tn <- createTextNode doc t
684− maybe (askParent >>= \p -> Node.appendChild_ p tn) (insertAfter tn) =<< getPreviousNode
685− setPreviousNode $ Just $ toNode tn
687 mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
567 mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
688 hydrateDOM $ DOM_Node activateText
568 hydrateDOM $ DOM_Node activateText
689 pure $ error "textNodeInternal: DOM.Text"
569 pure $ error "textNodeInternal: DOM.Text"
@@ -697,16 +577,9 @@ commentNodeInternal !t mSetContents = getHydrationMode >>= \case
698 HydrationMode_Hydrating -> do
578 HydrationMode_Hydrating -> do
699 doc <- askDocument
579 doc <- askDocument
700− prerenderDepth <- getPrerenderDepth
701 let activateComment = do
580 let activateComment = do
702 liftIO $ putStrLn $ "Action: Comment: " <> show (DOM.toJSString t)
581 liftIO $ putStrLn $ "Action: Comment: " <> show (DOM.toJSString t)
703− n <- case prerenderDepth of
704− 0 -> hydrateNode (const $ pure True) DOM.Comment
582+ n <- hydrateNode (const $ pure True) DOM.Comment
706− tn <- createComment doc t
707− maybe (askParent >>= \p -> Node.appendChild_ p tn) (insertAfter tn) =<< getPreviousNode
708− setPreviousNode $ Just $ toNode tn
710 mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
583 mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
711 hydrateDOM $ DOM_Node activateComment
584 hydrateDOM $ DOM_Node activateComment
712 pure $ error "commentNodeInternal: DOM.Comment"
585 pure $ error "commentNodeInternal: DOM.Comment"
@@ -1446,25 +1319,12 @@ drawChildUpdateInt initialEnv markReady child = do
1446 , placeholder, childReadyState, result
1319 , placeholder, childReadyState, result
1449−mkHasFocus :: (MonadHold t m, Reflex t) => Element er d t -> m (Dynamic t Bool)
1451− let initialFocus = False --TODO: Actually get the initial focus of the element
1452− holdDyn initialFocus $ leftmost
1453− [ False <$ Reflex.select (_element_events e) (WrapArg Blur)
1454− , True <$ Reflex.select (_element_events e) (WrapArg Focus)
1457insertAfter :: (MonadJSM m, IsNode new, IsNode existing) => new -> existing -> m ()
1322insertAfter :: (MonadJSM m, IsNode new, IsNode existing) => new -> existing -> m ()
1458insertAfter new existing = do
1323insertAfter new existing = do
1459 liftIO $ putStrLn "insertAfter"
1324 liftIO $ putStrLn "insertAfter"
1460 p <- getParentNodeUnchecked existing
1325 p <- getParentNodeUnchecked existing
1461 Node.getNextSibling existing >>= DOM.insertBefore_ p new
1326 Node.getNextSibling existing >>= DOM.insertBefore_ p new
1463−insertBefore :: (MonadJSM m, IsNode new, IsNode existing) => new -> existing -> m ()
1464−insertBefore new existing = do
1465− p <- getParentNodeUnchecked existing
1466− DOM.insertBefore_ p new (Just existing) -- If there's no parent, that means we've been removed from the DOM; this should not happen if the we're removing ourselves from the performEvent properly
1468instance PerformEvent t m => PerformEvent t (HydrationDomBuilderT t m) where
1328instance PerformEvent t m => PerformEvent t (HydrationDomBuilderT t m) where
1469 type Performable (HydrationDomBuilderT t m) = Performable m
1329 type Performable (HydrationDomBuilderT t m) = Performable m
1470 {-# INLINABLE performEvent_ #-}
1330 {-# INLINABLE performEvent_ #-}
@@ -1482,12 +1342,6 @@ instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (HydrationDo
1482 {-# INLINABLE newFanEventWithTrigger #-}
1342 {-# INLINABLE newFanEventWithTrigger #-}
1483 newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
1343 newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
1485−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (HydrationRunnerT t m) where
1486− {-# INLINABLE newEventWithTrigger #-}
1487− newEventWithTrigger = lift . newEventWithTrigger
1488− {-# INLINABLE newFanEventWithTrigger #-}
1489− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
1491instance (Monad m, MonadRef m, Ref m ~ Ref IO, MonadReflexCreateTrigger t m) => TriggerEvent t (HydrationDomBuilderT t m) where
1345instance (Monad m, MonadRef m, Ref m ~ Ref IO, MonadReflexCreateTrigger t m) => TriggerEvent t (HydrationDomBuilderT t m) where
1492 {-# INLINABLE newTriggerEvent #-}
1346 {-# INLINABLE newTriggerEvent #-}
1493 newTriggerEvent = HydrationDomBuilderT . lift . lift $ newTriggerEvent
1347 newTriggerEvent = HydrationDomBuilderT . lift . lift $ newTriggerEvent
@@ -1525,23 +1379,6 @@ instance (HasJS x m, ReflexHost t) => HasJS x (HydrationDomBuilderT t m) where
1525 type JSX (HydrationDomBuilderT t m) = JSX m
1379 type JSX (HydrationDomBuilderT t m) = JSX m
1526 liftJS = lift . liftJS
1380 liftJS = lift . liftJS
1528−{-# INLINABLE wrapDomEvent #-}
1529−wrapDomEvent :: (TriggerEvent t m, MonadJSM m) => e -> (e -> EventM e event () -> JSM (JSM ())) -> EventM e event a -> m (Event t a)
1530−wrapDomEvent el elementOnevent getValue = wrapDomEventMaybe el elementOnevent $ fmap Just getValue
1532−{-# INLINABLE subscribeDomEvent #-}
1533−subscribeDomEvent :: (EventM e event () -> JSM (JSM ()))
1534− -> EventM e event (Maybe a)
1535− -> Chan [DSum (EventTriggerRef t) TriggerInvocation]
1536− -> EventTrigger t a
1538−subscribeDomEvent elementOnevent getValue eventChan et = elementOnevent $ do
1540− forM_ mv $ \v -> liftIO $ do
1541− --TODO: I don't think this is quite right: if a new trigger is created between when this is enqueued and when it fires, this may not work quite right
1542− etr <- newIORef $ Just et
1543− writeChan eventChan [EventTriggerRef etr :=> TriggerInvocation v (return ())]
1545instance MonadSample t m => MonadSample t (HydrationDomBuilderT t m) where
1382instance MonadSample t m => MonadSample t (HydrationDomBuilderT t m) where
1546 {-# INLINABLE sample #-}
1383 {-# INLINABLE sample #-}
1547 sample = lift . sample
1384 sample = lift . sample