on develop help sign in
Eliminate placeholder/deletable
commitec7373e716feb3b7a737b65a58b42e9b72a818fc authorRyan Trinkle <ryan.trinkle@gmail.com> dateSun 25 Sep 2016, 14:40 (10 years ago) parent9af728b
7 files changed+150−115
src/Foreign/JavaScript/TH.hs+5−5 src/Reflex/Dom/Builder/Class.hs+6−40 src/Reflex/Dom/Builder/Immediate.hs+69−20 src/Reflex/Dom/Builder/InputDisabled.hs+8−5 src/Reflex/Dom/Builder/Static.hs+30−17 src/Reflex/Dom/Old.hs−3 src/Reflex/Dom/Widget/Basic.hs+32−25
src/Foreign/JavaScript/TH.hs+5−5
@@ -21,7 +21,6 @@ module Foreign.JavaScript.TH ( module Foreign.JavaScript.TH
2121 ) where
2222
2323import Reflex.Class
24−import Reflex.Deletable.Class
2524import Reflex.DynamicWriter
2625import Reflex.PerformEvent.Base
2726import Reflex.PerformEvent.Class
@@ -81,6 +80,8 @@ import Data.Monoid
8180import Data.Text (Text)
8281import qualified Data.Text as T
8382
83+import Unsafe.Coerce
84+
8485class Monad m => HasWebView m where
8586 type WebViewPhantom m :: *
8687 askWebView :: m (WebViewSingleton (WebViewPhantom m))
@@ -111,6 +112,9 @@ instance HasWebView m => HasWebView (DynamicWriterT t w m) where
111112
112113newtype WithWebView x m a = WithWebView { unWithWebView :: ReaderT (WebViewSingleton x) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadTrans, MonadException, MonadAsyncException)
113114
115+instance MonadAdjust t m => MonadAdjust t (WithWebView x m) where
116+ sequenceDMapWithAdjust dm0 dm' = WithWebView $ sequenceDMapWithAdjust (coerce dm0) (unsafeCoerce dm')
117+
114118instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (WithWebView x m) where
115119 {-# INLINABLE newEventWithTrigger #-}
116120 newEventWithTrigger = lift . newEventWithTrigger
@@ -154,10 +158,6 @@ instance PerformEvent t m => PerformEvent t (WithWebView x m) where
154158 {-# INLINABLE performEvent #-}
155159 performEvent e = liftWith $ \run -> performEvent $ fmap run e
156160
157−instance Deletable t m => Deletable t (WithWebView x m) where
158− {-# INLINABLE deletable #-}
159− deletable = liftThrough . deletable
160−
161161runWithWebView :: WithWebView x m a -> WebViewSingleton x -> m a
162162runWithWebView = runReaderT . unWithWebView
163163
src/Reflex/Dom/Builder/Class.hs+6−40
@@ -18,12 +18,10 @@
1818module Reflex.Dom.Builder.Class
1919 ( module Reflex.Dom.Builder.Class
2020 , module Reflex.Dom.Builder.Class.Events
21− , module Reflex.Deletable.Class
2221 ) where
2322
2423import Reflex.Class as Reflex
2524import Reflex.Dom.Builder.Class.Events
26−import Reflex.Deletable.Class
2725import Reflex.DynamicWriter
2826import Reflex.PerformEvent.Class
2927import Reflex.PostBuild.Class
@@ -34,16 +32,13 @@ import Control.Monad.Reader
3432import Control.Monad.State.Strict
3533import Control.Monad.Trans.Control
3634import Data.Default
37−import Data.Foldable
3835import Data.Functor.Misc
39−import Data.List.NonEmpty (NonEmpty, nonEmpty)
4036import Data.Map (Map)
4137import qualified Data.Map as Map
4238import Data.Proxy
4339import Data.Semigroup
4440import Data.String
4541import Data.Text (Text)
46−import Data.Traversable
4742import Data.Type.Coercion
4843import GHC.Exts (Constraint)
4944
@@ -65,7 +60,7 @@ liftElementConfig cfg = cfg
6560
6661-- | @'DomBuilder' t m@ indicates that @m@ is a 'Monad' capable of building
6762-- dynamic DOM in the 'Reflex' timeline @t@
68−class (Monad m, Reflex t, Deletable t m, DomSpace (DomBuilderSpace m)) => DomBuilder t m | m -> t where
63+class (Monad m, Reflex t, DomSpace (DomBuilderSpace m), MonadAdjust t m) => DomBuilder t m | m -> t where
6964 type DomBuilderSpace m :: *
7065 textNode :: TextNodeConfig t -> m (TextNode (DomBuilderSpace m) t)
7166 default textNode :: ( MonadTrans f
@@ -86,9 +81,11 @@ class (Monad m, Reflex t, Deletable t m, DomSpace (DomBuilderSpace m)) => DomBui
8681 => Text -> ElementConfig er t m -> m a -> m (Element er (DomBuilderSpace m) t, a)
8782 element t cfg child = liftWith $ \run -> element t (liftElementConfig cfg) $ run child
8883 {-# INLINABLE element #-}
84+ {-
8985 -- | Create a placeholder in the DOM, with the ability to insert new DOM before it
9086 -- The provided DOM will be executed after the current frame, so it will not be affected by any occurrences that are concurrent with the occurrence that created it
9187 placeholder :: PlaceholderConfig above t m -> m (Placeholder above t)
88+-}
9289 inputElement :: InputElementConfig er t m -> m (InputElement er (DomBuilderSpace m) t)
9390 default inputElement :: ( MonadTransControl f
9491 , m ~ f m'
@@ -403,13 +400,6 @@ instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (ElementConfig
403400
404401instance (DomBuilder t m, PerformEvent t m, MonadFix m, MonadHold t m) => DomBuilder t (PostBuildT t m) where
405402 type DomBuilderSpace (PostBuildT t m) = DomBuilderSpace m
406− {-# INLINABLE placeholder #-}
407− placeholder cfg = lift $ do
408− rec childPostBuild <- deletable (_placeholder_deletedSelf p) $ performEvent $ return () <$ _placeholder_insertedAbove p
409− p <- placeholder $ cfg
410− { _placeholderConfig_insertAbove = ffor (_placeholderConfig_insertAbove cfg) $ \a -> runPostBuildT a =<< headE childPostBuild
411− }
412− return p
413403 wrapRawElement e cfg = liftWith $ \run -> wrapRawElement e $ fmap1 run cfg
414404
415405instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (DynamicWriterT t w m) where
@@ -421,32 +411,6 @@ instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (
421411 (el, (a, newS)) <- lift $ element elementTag cfg' $ runStateT child s
422412 put newS
423413 return (el, a)
424− placeholder cfg = do
425− let cfg' = cfg
426− { _placeholderConfig_insertAbove = runDynamicWriterTInternal <$> _placeholderConfig_insertAbove cfg
427− }
428− let manageChildren :: Event t (NonEmpty (Replaceable t (Dynamic t w))) -- ^ Add nodes on the right; these are in reverse order
429− -> Event t () -- ^ No more nodes will be added after this event fires
430− -> m (Replaceable t (Dynamic t w))
431− manageChildren newChildren additionsCeased = do
432− rec nextId <- hold (0 :: Int) newNextId -- We assume this will never wrap around
433− let numberedNewChildren :: Event t (Int, PatchMap (Map Int (Replaceable t (Dynamic t w))))
434− numberedNewChildren = flip pushAlways newChildren $ \rcs -> do
435− let cs = reverse $ toList rcs
436− myFirstId <- sample nextId
437− let (myNextId, numbered) = mapAccumL (\n v -> (succ n, (n, Just v))) myFirstId cs
438− return (myNextId, PatchMap $ Map.fromList numbered)
439− newNextId = fst <$> numberedNewChildren
440− mconcatIncrementalReplaceableDynMap Map.empty (snd <$> numberedNewChildren) additionsCeased
441− rec children <- lift $ manageChildren childOutputs $ cfg ^. deleteSelf
442− p <- DynamicWriterT $ do
443− modify (children:)
444− lift $ placeholder cfg'
445− let result = fst <$> _placeholder_insertedAbove p
446− childOutputs = fmapMaybe (nonEmpty . snd) $ _placeholder_insertedAbove p
447− return $ p
448− { _placeholder_insertedAbove = result
449− }
450414 inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftElementConfig
451415 textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftElementConfig
452416 selectElement cfg (DynamicWriterT child) = DynamicWriterT $ do
@@ -460,6 +424,7 @@ instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (
460424 { _rawElementConfig_eventSpec = _rawElementConfig_eventSpec cfg
461425 }
462426
427+
463428-- * Convenience functions
464429
465430--TODO: Move/replace
@@ -521,7 +486,6 @@ instance Reflex t => HasDomEvent t (TextAreaElement EventResult d t) en where
521486
522487instance DomBuilder t m => DomBuilder t (ReaderT r m) where
523488 type DomBuilderSpace (ReaderT r m) = DomBuilderSpace m
524− placeholder = liftPlaceholder
525489
526490type LiftDomBuilder t f m =
527491 ( Reflex t
@@ -555,8 +519,10 @@ liftTextNode = lift . textNode
555519liftElement :: LiftDomBuilder t f m => Text -> ElementConfig er t (f m) -> f m a -> f m (Element er (DomBuilderSpace m) t, a)
556520liftElement elementTag cfg child = liftWithStateless $ \run -> element elementTag (fmap1 run cfg) $ run child
557521
522+{-
558523liftPlaceholder :: LiftDomBuilder t f m => PlaceholderConfig above t (f m) -> f m (Placeholder above t)
559524liftPlaceholder cfg = liftWithStateless $ \run -> placeholder $ fmap1 run cfg
525+-}
560526
561527liftInputElement :: LiftDomBuilder t f m => InputElementConfig er t (f m) -> f m (InputElement er (DomBuilderSpace m) t)
562528liftInputElement cfg = liftWithStateless $ \run -> inputElement $ fmap1 run cfg
src/Reflex/Dom/Builder/Immediate.hs+69−20
@@ -9,6 +9,7 @@
99{-# LANGUAGE OverloadedStrings #-}
1010{-# LANGUAGE PolyKinds #-}
1111{-# LANGUAGE RankNTypes #-}
12+{-# LANGUAGE RecursiveDo #-}
1213{-# LANGUAGE ScopedTypeVariables #-}
1314{-# LANGUAGE TemplateHaskell #-}
1415{-# LANGUAGE TypeFamilies #-}
@@ -35,10 +36,15 @@ import Data.Default
3536import Data.Dependent.Map (DMap)
3637import qualified Data.Dependent.Map as DMap
3738import Data.Dependent.Sum
39+import Data.Functor.Compose
3840import Data.Functor.Misc
3941import Data.IORef
42+import Data.Map (Map)
43+import qualified Data.Map as Map
4044import Data.Maybe
4145import Data.Monoid
46+import Data.Some (Some)
47+import qualified Data.Some as Some
4248import Data.Text (Text)
4349import GHCJS.DOM.Document (Document, createDocumentFragment, createElement, createElementNS, createTextNode)
4450import GHCJS.DOM.Element (getScrollTop, removeAttribute, removeAttributeNS, setAttribute, setAttributeNS)
@@ -51,8 +57,9 @@ import qualified GHCJS.DOM.HTMLInputElement as Input
5157import qualified GHCJS.DOM.HTMLSelectElement as Select
5258import qualified GHCJS.DOM.HTMLTextAreaElement as TextArea
5359import GHCJS.DOM.MouseEvent
54−import GHCJS.DOM.Node (appendChild, getOwnerDocument, getParentNode, getPreviousSibling, insertBefore,
60+import GHCJS.DOM.Node (appendChild, getOwnerDocument, getParentNode, getPreviousSibling,
5561 removeChild, setNodeValue, toNode)
62+import qualified GHCJS.DOM.Node as DOM
5663import GHCJS.DOM.Types (FocusEvent, IsElement, IsEvent, IsNode, KeyboardEvent, Node, ToDOMString, TouchEvent,
5764 WheelEvent, castToHTMLInputElement, castToHTMLSelectElement,
5865 castToHTMLTextAreaElement)
@@ -121,15 +128,28 @@ deleteBetweenInclusive s e = do
121128 case mCurrentParent of
122129 Nothing -> return () --TODO: Is this the right behavior?
123130 Just currentParent -> do
124− let go = do
125− Just x <- getPreviousSibling e -- This can't be Nothing because we should hit 's' first
126− _ <- removeChild currentParent $ Just x
127− when (toNode s /= toNode x) go
128− go
131+ deleteUpToGivenParent currentParent s e
129132 _ <- removeChild currentParent $ Just e
130133 return ()
131134
132−type SupportsImmediateDomBuilder t m = (Reflex t, MonadIO m, MonadHold t m, MonadFix m, PerformEvent t m, Performable m ~ m, MonadReflexCreateTrigger t m, Deletable t m, MonadRef m, Ref m ~ Ref IO)
135+-- | s and e must both be children of the same node and s must precede e; s and all nodes between s and e will be removed, but e will not be removed
136+{-# INLINABLE deleteUpTo #-}
137+deleteUpTo :: (MonadIO m, IsNode start, IsNode end) => start -> end -> m ()
138+deleteUpTo s e = do
139+ mCurrentParent <- getParentNode e -- May be different than it was at initial construction, e.g., because the parent may have dumped us in from a DocumentFragment
140+ case mCurrentParent of
141+ Nothing -> return () --TODO: Is this the right behavior?
142+ Just currentParent -> deleteUpToGivenParent currentParent s e
143+
144+{-# INLINABLE deleteUpToGivenParent #-}
145+deleteUpToGivenParent :: (MonadIO m, IsNode parent, IsNode start, IsNode end) => parent -> start -> end -> m ()
146+deleteUpToGivenParent currentParent s e = do
147+ fix $ \loop -> do
148+ Just x <- getPreviousSibling e -- This can't be Nothing because we should hit 's' first
149+ _ <- removeChild currentParent $ Just x
150+ when (toNode s /= toNode x) loop
151+
152+type SupportsImmediateDomBuilder t m = (Reflex t, MonadIO m, MonadIO (Performable m), MonadHold t m, MonadFix m, PerformEvent t m, MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO, MonadAdjust t m)
133153
134154newtype EventFilterTriggerRef t er (en :: EventTag) = EventFilterTriggerRef (IORef (Maybe (EventTrigger t (er en))))
135155
@@ -248,6 +268,7 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
248268 return $ TextNode n
249269 {-# INLINABLE element #-}
250270 element elementTag cfg child = fst <$> makeElement elementTag cfg child
271+ {-
251272 {-# INLINABLE placeholder #-}
252273 placeholder (PlaceholderConfig toInsertAbove delete) = liftThrough (deletable delete) $ do
253274 n <- textNodeInternal ("" :: Text)
@@ -257,6 +278,7 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
257278 mp <- getParentNode n
258279 forM_ mp $ \p -> removeChild p $ Just n
259280 return $ Placeholder insertedAbove deleted
281+-}
260282 {-# INLINABLE inputElement #-}
261283 inputElement cfg = do
262284 ((e, _), domElement) <- makeElement "input" (cfg ^. inputElementConfig_elementConfig) $ return ()
@@ -354,6 +376,39 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
354376 placeRawElement = append
355377 wrapRawElement = wrap
356378
379+instance (Reflex t, MonadAdjust t m, MonadIO m, MonadHold t m, PerformEvent t m, MonadIO (Performable m)) => MonadAdjust t (ImmediateDomBuilderT t m) where
380+ sequenceDMapWithAdjust (dm0 :: DMap k (ImmediateDomBuilderT t m)) dm' = do
381+ initialEnv <- ImmediateDomBuilderT ask
382+ let drawChildInitial :: ImmediateDomBuilderT t m a -> m (DOM.DocumentFragment, DOM.Text, a)
383+ drawChildInitial child = runImmediateDomBuilderT ((,,) <$> pure (error "sequenceDMapWithAdjust{ImmediateDomBuilderT}: drawChildInitial: DocumentFragment evaluated") <*> textNodeInternal ("" :: Text) <*> child) initialEnv --TODO: Don't return a DocumentFragment at all here; this will require that sequenceDMapWithAdjust accept differently-typed values for the constant and Event arguments.
384+ drawChildUpdate :: ImmediateDomBuilderT t m a -> m (DOM.DocumentFragment, DOM.Text, a)
385+ drawChildUpdate child = do
386+ Just df <- createDocumentFragment $ _immediateDomBuilderEnv_document initialEnv
387+ runImmediateDomBuilderT ((,,) <$> pure df <*> textNodeInternal ("" :: Text) <*> child) $ initialEnv
388+ { _immediateDomBuilderEnv_parent = toNode df
389+ }
390+ updateChildren = ffor dm' $ \(PatchDMap p) -> PatchDMap $
391+ mapKeyValuePairsMonotonic (\(k :=> Compose mv) -> WrapArg k :=> Compose (fmap drawChildUpdate mv)) p
392+ (children0, children') <- lift $ sequenceDMapWithAdjust (mapKeyValuePairsMonotonic (\(k :=> v) -> WrapArg k :=> drawChildInitial v) dm0) (updateChildren :: Event t (PatchDMap (DMap (WrapArg ((,,) DOM.DocumentFragment DOM.Text) k) m))) --TODO: Update stuff
393+ let result0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (_, _, v)) -> k :=> Identity v) children0
394+ placeholders0 = dmapToMap $ mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (_, ph, _)) -> Const2 (Some.This k) :=> Identity ph) children0
395+ result' = ffor children' $ \(PatchDMap p) -> PatchDMap $
396+ mapKeyValuePairsMonotonic (\(WrapArg k :=> v) -> k :=> fmap (\(_, _, r) -> r) v) p
397+ placeholders' = ffor children' $ \(PatchDMap p) -> PatchMap $
398+ dmapToMap $ mapKeyValuePairsMonotonic (\(WrapArg k :=> v) -> Const2 (Some.This k) :=> Identity (fmap ((\(_, ph, _) -> ph) . runIdentity) (getCompose v))) p
399+ placeholders :: Behavior t (Map (Some k) DOM.Text) <- current . incrementalToDynamic <$> holdIncremental placeholders0 placeholders'
400+ lastPlaceholder <- textNodeInternal ("" :: Text)
401+ performEvent_ $ flip push children' $ \(PatchDMap p) -> do
402+ phs <- sample placeholders
403+ if DMap.null p then return Nothing else return $ Just $ do
404+ let f :: DSum (WrapArg ((,,) DOM.DocumentFragment DOM.Text) k) (Compose Maybe Identity) -> Performable m ()
405+ f (WrapArg k :=> Compose mv) = do
406+ let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phs
407+ forM_ (Map.lookup (Some.This k) phs) $ \thisPlaceholder -> thisPlaceholder `deleteUpTo` nextPlaceholder
408+ forM_ mv $ \(Identity (df, _, _)) -> df `insertBefore` nextPlaceholder
409+ mapM_ f $ DMap.toList p
410+ return (result0, result')
411+
357412mkHasFocus :: (MonadHold t m, Reflex t) => Element er d t -> m (Dynamic t Bool)
358413mkHasFocus e = do
359414 let initialFocus = False --TODO: Actually get the initial focus of the element
@@ -374,21 +429,15 @@ insertImmediateAbove n toInsertAbove = do
374429 , _immediateDomBuilderEnv_document = doc
375430 , _immediateDomBuilderEnv_events = events
376431 }
377− mp <- getParentNode n
378− case mp of
379− Nothing -> trace "ImmediateDomBuilderT: placeholder: Warning: (getParentNode n) returned Nothing" $ return ()
380− Just p -> void $ insertBefore p (Just df) (Just n) -- 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
432+ df `insertBefore` n
381433 return result
382434
383−instance SupportsImmediateDomBuilder t m => Deletable t (ImmediateDomBuilderT t m) where
384− {-# INLINABLE deletable #-}
385− deletable delete child = liftThrough (deletable delete) $ do
386− top <- textNodeInternal ("" :: Text)
387− result <- child
388− bottom <- textNodeInternal ("" :: Text)
389− lift $ performEvent_ $ ffor delete $ \_ -> do
390− deleteBetweenInclusive top bottom
391− return result
435+insertBefore :: (MonadIO m, IsNode new, IsNode existing) => new -> existing -> m ()
436+insertBefore new existing = do
437+ mp <- getParentNode existing
438+ case mp of
439+ Nothing -> trace "ImmediateDomBuilderT: placeholder: Warning: (getParentNode n) returned Nothing" $ return ()
440+ Just p -> void $ DOM.insertBefore p (Just 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
392441
393442instance PerformEvent t m => PerformEvent t (ImmediateDomBuilderT t m) where
394443 type Performable (ImmediateDomBuilderT t m) = Performable m
src/Reflex/Dom/Builder/InputDisabled.hs+8−5
@@ -7,18 +7,19 @@
77{-# LANGUAGE UndecidableInstances #-}
88module Reflex.Dom.Builder.InputDisabled where
99
10−import Control.Lens
1110import Control.Monad.Fix
1211import Control.Monad.Ref
1312import Control.Monad.Trans
1413import Control.Monad.Trans.Control
14+import Data.Coerce
1515import qualified Data.Map as Map
1616import Foreign.JavaScript.TH
1717import Reflex
18−import Reflex.Deletable.Class
1918import Reflex.Dom.Builder.Class
2019import Reflex.Host.Class
2120
21+import Unsafe.Coerce
22+
2223-- | A DomBuilder transformer that disables all 'inputElement's,
2324-- 'textAreaElement's, and 'selectElement's by adding the "disabled" HTML
2425-- attribute. Note that 'element's that happen to have "input", "textarea", or
@@ -53,9 +54,6 @@ disableElementConfig cfg = cfg
5354 , _elementConfig_modifyAttributes = Map.delete "disabled" <$> _elementConfig_modifyAttributes cfg
5455 }
5556
56−instance Deletable t m => Deletable t (InputDisabledT m) where
57− deletable d = liftThrough $ deletable d
58−
5957instance PostBuild t m => PostBuild t (InputDisabledT m) where
6058 getPostBuild = lift getPostBuild
6159
@@ -65,10 +63,15 @@ instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (InputDisabl
6563 newEventWithTrigger = lift . newEventWithTrigger
6664 newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
6765
66+instance MonadAdjust t m => MonadAdjust t (InputDisabledT m) where
67+ sequenceDMapWithAdjust dm0 dm' = InputDisabledT $ sequenceDMapWithAdjust (coerce dm0) (unsafeCoerce dm') --TODO: Eliminate unsafeCoerce
68+
6869instance DomBuilder t m => DomBuilder t (InputDisabledT m) where
6970 type DomBuilderSpace (InputDisabledT m) = DomBuilderSpace m
71+ {-
7072 placeholder cfg = lift $ placeholder $ cfg
7173 & placeholderConfig_insertAbove %~ fmap runInputDisabledT
74+ -}
7275 inputElement cfg = lift $ inputElement $ cfg
7376 { _inputElementConfig_elementConfig = liftElementConfig $ disableElementConfig $ _inputElementConfig_elementConfig cfg
7477 }
src/Reflex/Dom/Builder/Static.hs+30−17
@@ -24,14 +24,21 @@ import Control.Monad.Ref
2424import Control.Monad.State.Strict
2525import Control.Monad.Trans.Control
2626import Data.ByteString (ByteString)
27+import qualified Data.ByteString as BS
2728import Data.ByteString.Builder (toLazyByteString)
2829import qualified Data.ByteString.Lazy as BL
2930import Data.Default
31+import Data.Dependent.Map (DMap)
32+import qualified Data.Dependent.Map as DMap
3033import Data.Dependent.Sum (DSum (..))
34+import Data.Functor.Compose
35+import Data.Functor.Constant
36+import Data.Functor.Misc
3137import qualified Data.Map as Map
3238import Data.Monoid
3339import qualified Data.Set as Set
3440import Data.Text.Encoding
41+import Data.Tuple
3542import GHC.Generics
3643import Reflex.Class
3744import Reflex.Dom.Builder.Class
@@ -45,7 +52,7 @@ import Reflex.Spider
4552
4653
4754newtype StaticDomBuilderT t m a = StaticDomBuilderT
48− { unStaticDomBuilderT :: StateT [Behavior t ByteString] m a -- Accumulated Html will be in revesed order
55+ { unStaticDomBuilderT :: StateT [Behavior t ByteString] m a -- Accumulated Html will be in reversed order
4956 }
5057 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
5158
@@ -87,15 +94,6 @@ instance MonadHold t m => MonadHold t (StaticDomBuilderT t m) where
8794 {-# INLINABLE holdIncremental #-}
8895 holdIncremental v0 v' = lift $ holdIncremental v0 v'
8996
90−instance (Reflex t, Monad m, MonadHold t (StateT [Behavior t ByteString] m)) => Deletable t (StaticDomBuilderT t m) where
91− {-# INLINABLE deletable #-}
92− deletable delete (StaticDomBuilderT a) = StaticDomBuilderT $ do
93− (result, a') <- lift $ runStateT a []
94− let html = mconcat $ reverse a'
95− b <- hold html (mempty <$ delete)
96− modify (join b:)
97− return result
98−
9997instance (Monad m, Ref m ~ Ref IO, Reflex t) => TriggerEvent t (StaticDomBuilderT t m) where
10098 {-# INLINABLE newTriggerEvent #-}
10199 newTriggerEvent = return (never, const $ return ())
@@ -113,7 +111,7 @@ instance MonadRef m => MonadRef (StaticDomBuilderT t m) where
113111instance MonadAtomicRef m => MonadAtomicRef (StaticDomBuilderT t m) where
114112 atomicModifyRef r = lift . atomicModifyRef r
115113
116−type SupportsStaticDomBuilder t m = (Reflex t, MonadIO m, MonadHold t m, MonadFix m, PerformEvent t m, Performable m ~ m, MonadReflexCreateTrigger t m, Deletable t m, MonadRef m, Ref m ~ Ref IO)
114+type SupportsStaticDomBuilder t m = (Reflex t, MonadIO m, MonadHold t m, MonadFix m, PerformEvent t m, MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO, MonadAdjust t m)
117115
118116data StaticDomSpace
119117
@@ -137,6 +135,27 @@ instance DomSpace StaticDomSpace where
137135 type RawSelectElement StaticDomSpace = ()
138136 addEventSpecFlags _ _ _ _ = StaticEventSpec
139137
138+instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (StaticDomBuilderT t m) where
139+ sequenceDMapWithAdjust (dm0 :: DMap k (StaticDomBuilderT t m)) dm' = do
140+ let loweredDm0 = mapKeyValuePairsMonotonic (\(k :=> v) -> WrapArg k :=> fmap swap (runStaticDomBuilderT v)) dm0
141+ loweredDm' = ffor dm' $ \(PatchDMap p) -> PatchDMap $
142+ mapKeyValuePairsMonotonic (\(k :=> Compose mv) -> WrapArg k :=> Compose (fmap (fmap swap . runStaticDomBuilderT) mv)) p
143+ (children0, children') <- lift $ sequenceDMapWithAdjust loweredDm0 loweredDm'
144+ let result0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (_, v)) -> k :=> Identity v) children0
145+ result' = ffor children' $ \(PatchDMap p) -> PatchDMap $
146+ mapKeyValuePairsMonotonic (\(WrapArg k :=> mv) -> k :=> fmap snd mv) p
147+ outputs0 :: DMap k (Constant (Behavior t ByteString))
148+ outputs0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (o, _)) -> k :=> Constant o) children0
149+ outputs' :: Event t (PatchDMap (DMap k (Constant (Behavior t ByteString))))
150+ outputs' = ffor children' $ \(PatchDMap p) -> PatchDMap $
151+ mapKeyValuePairsMonotonic (\(WrapArg k :=> Compose mv) -> k :=> Compose (fmap (Constant . fst . runIdentity) mv)) p
152+ outputs <- holdIncremental outputs0 outputs'
153+ StaticDomBuilderT $ modify $ (:) $ pull $ do
154+ os <- sample $ currentIncremental outputs
155+ fmap BS.concat $ forM (DMap.toList os) $ \(_ :=> Constant o) -> do
156+ sample o
157+ return (result0, result')
158+
140159instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) where
141160 type DomBuilderSpace (StaticDomBuilderT t m) = StaticDomSpace
142161 {-# INLINABLE textNode #-}
@@ -163,12 +182,6 @@ instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) wh
163182 let close = constant $ "</" <> tagBS <> ">"
164183 modify $ (:) $ mconcat [open, innerHtml, close]
165184 return (Element es (), result)
166− {-# INLINABLE placeholder #-}
167− placeholder (PlaceholderConfig toInsertAbove _delete) = StaticDomBuilderT $ do
168− result <- lift $ performEvent (fmap runStaticDomBuilderT toInsertAbove)
169− acc <- foldDyn (:) [] (fmap snd result)
170− modify $ (:) $ join $ mconcat . reverse <$> current acc
171− return $ Placeholder (fmap fst result) never
172185 {-# INLINABLE inputElement #-}
173186 inputElement cfg = do
174187 (e, _result) <- element "input" (cfg ^. inputElementConfig_elementConfig) $ return ()
src/Reflex/Dom/Old.hs−3
@@ -43,7 +43,6 @@ module Reflex.Dom.Old
4343import Control.Arrow (first)
4444import Control.Lens (makeLenses, (%~), (&), (.~), (^.))
4545import Control.Monad
46−import Control.Monad.Exception
4746import Control.Monad.Fix
4847import Control.Monad.IO.Class
4948import Control.Monad.Reader
@@ -95,8 +94,6 @@ type MonadWidgetConstraints t m =
9594 , TriggerEvent t m
9695 , HasWebView m
9796 , HasWebView (Performable m)
98− , MonadAsyncException m
99− , MonadAsyncException (Performable m)
10097 , MonadRef m
10198 , Ref m ~ Ref IO
10299 , MonadRef (Performable m)
src/Reflex/Dom/Widget/Basic.hs+32−25
@@ -91,26 +91,34 @@ import Control.Lens hiding (children, element)
9191import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
9292import Data.Align
9393import Data.Default
94+import Data.Dependent.Map (DMap, GCompare)
95+import qualified Data.Dependent.Map as DMap
9496import Data.Either
9597import Data.Foldable
98+import Data.Functor.Compose
9699import Data.Functor.Misc
97100import Data.Map (Map)
98101import qualified Data.Map as Map
99102import Data.Maybe
100−import Data.Semigroup
101103import Data.Set (Set)
102104import qualified Data.Set as Set
105+import Data.Some (Some)
106+import qualified Data.Some as Some
103107import Data.Text (Text)
104108import qualified Data.Text as T
105109import Data.These
106110import Data.Traversable
107111import Prelude hiding (mapM, mapM_, sequence, sequence_)
108112
109−widgetHoldInternal :: DomBuilder t m => m a -> Event t (m b) -> m (a, Event t b)
113+--TODO: Implement this specially
114+widgetHoldInternal :: forall t m a b. DomBuilder t m => m a -> Event t (m b) -> m (a, Event t b)
110115widgetHoldInternal child0 child' = do
111− childResult0 <- deletable (void child') child0
112− childResult' <- placeholder $ def & placeholderConfig_insertAbove .~ fmap (deletable (void child')) child'
113− return (childResult0, _placeholder_insertedAbove childResult')
116+ (result0, result') <- sequenceDMapWithAdjust (DMap.singleton LeftTag child0) $ fmap (PatchDMap . DMap.insert LeftTag (Compose Nothing) . DMap.singleton RightTag . Compose . Just) child'
117+ let e :: forall x. x
118+ e = error "widgetHoldInternal: missing child (should be impossible)"
119+ return ( runIdentity $ DMap.findWithDefault e LeftTag result0
120+ , ffor result' $ \(PatchDMap p) -> runIdentity $ fromMaybe e $ getCompose $ DMap.findWithDefault e RightTag p
121+ )
114122
115123-- | Breaks the given Map into pieces based on the given Set. Each piece will contain only keys that are less than the key of the piece, and greater than or equal to the key of the piece with the next-smaller key. There will be one additional piece containing all keys from the original Map that are larger or equal to the largest key in the Set.
116124-- Either k () is used instead of Maybe k so that the resulting map of pieces is sorted so that the additional piece has the largest key.
@@ -129,28 +137,27 @@ partitionMapBySetLT s m0 = Map.fromDistinctAscList $ go (Set.toAscList s) m0
129137 then go t geq
130138 else (Left h, lt) : go t geq
131139
140+partitionDMapBySetLT :: forall k v. GCompare k => Set (Some k) -> DMap k v -> Map (Either (Some k) ()) (DMap k v)
141+partitionDMapBySetLT s m0 = Map.fromDistinctAscList $ go (Set.toAscList s) m0
142+ where go :: [Some k] -> DMap k v -> [(Either (Some k) (), DMap k v)]
143+ go [] m = if DMap.null m
144+ then []
145+ else [(Right (), m)]
146+ go (sh@(Some.This h) : t) m =
147+ let (lt, eq, gt) = DMap.splitLookup h m
148+ geq = maybe id (DMap.insert h) eq gt
149+ in if DMap.null lt
150+ then go t geq
151+ else (Left sh, lt) : go t geq
152+
132153newtype ChildResult t k a = ChildResult { unChildResult :: (a, Event t (Map k (Maybe (ChildResult t k a)))) }
133154
134−listHoldWithKey :: forall t m k v a. (Ord k, DomBuilder t m, MonadHold t m, MonadFix m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
135−listHoldWithKey initialChildren modifyChildren buildChild = do
136− let deleteChildSelector = fanMap $ fmap void modifyChildren
137− liveChildren :: Dynamic t (Set k) <- foldDyn applyMapKeysSet (Map.keysSet initialChildren) modifyChildren
138− let placeChildSelector = fanMap $ attachWith partitionMapBySetLT (current liveChildren) $ fmap (Map.mapMaybe id) modifyChildren
139− buildAugmentedChild :: k -> v -> m (ChildResult t k a)
140− buildAugmentedChild k v = do
141− let delete = select deleteChildSelector $ Const2 k
142− myCfg = def
143− & insertAbove .~ fmap (imapM buildAugmentedChild) (select placeChildSelector $ Const2 $ Left k)
144− & deleteSelf .~ delete
145− ph <- placeholder myCfg
146− result <- deletable delete $ buildChild k v
147− return $ ChildResult (result, (fmap Just <$> _placeholder_insertedAbove ph) <> (Map.singleton k Nothing <$ _placeholder_deletedSelf ph)) --Note: we could also use the "deleted" output on deletable, if it had one; we're using this so that everything changes all at once, instead of deletions being prompt and insertions being delayed
148− rec initialAugmentedResults <- iforM initialChildren buildAugmentedChild
149− augmentedResults <- foldDyn applyMap initialAugmentedResults $ newInsertedBelow <> newInsertedAbove
150− let newInsertedAbove = switch $ mconcat . reverse . fmap (snd . unChildResult) . Map.elems <$> current augmentedResults
151− belowAll <- placeholder $ def & placeholderConfig_insertAbove .~ fmap (imapM buildAugmentedChild) (select placeChildSelector $ Const2 $ Right ())
152− let newInsertedBelow = fmap Just <$> _placeholder_insertedAbove belowAll
153− return $ fmap (fmap (fst . unChildResult)) augmentedResults
155+listHoldWithKey :: forall t m k v a. (Ord k, DomBuilder t m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
156+listHoldWithKey m0 m' f = do
157+ let dm0 = mapWithFunctorToDMap $ Map.mapWithKey f m0
158+ dm' = fmap (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey (\k v -> Compose $ fmap (f k) v)) m'
159+ (a0, a') <- sequenceDMapWithAdjust dm0 dm'
160+ fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a' --TODO: Move the dmapToMap to the righthand side so it doesn't get fully redone every time
154161
155162text :: DomBuilder t m => Text -> m ()
156163text t = void $ textNode $ def & textNodeConfig_initialContents .~ t