diff --git a/Quickref.md b/Quickref.md index 3299fbcb..5f4b5f99 100644 --- a/Quickref.md +++ b/Quickref.md @@ -76,30 +76,7 @@ Note the "list" functions do not imply particular HTML tags (ul, li, etc), thoug -- Same as dyn, but takes initial value and an update Event instead of a Dynamic. [W] widgetHold :: m a -> Event (m a) -> m (Dynamic a) --- Turn a Dynamic key/value map into a set of dynamically-changing widgets. -[W] listWithKey :: Ord k => - Dynamic (Map k v) -> (k -> Dynamic v -> m a ) -> m (Dynamic (Map k a)) - --- Same as above where the widget constructor doesn't care about the key. -[W] list :: Ord k => - Dynamic (Map k v) -> ( Dynamic v -> m a ) -> m (Dynamic (Map k a)) - --- Even simpler version where there are no keys and we just use a list. -[W] simpleList :: - Dynamic [v] -> ( Dynamic v -> m a ) -> m (Dynamic [a]) - --- Like listWithKey specialized for widgets returning (Event a). -[W] listViewWithKey :: Ord k => - Dynamic (Map k v) -> (k -> Dynamic v -> m (Event a)) -> m (Event (Map k a)) - --- Create a dynamically-changing set of widgets, one of which is selected at any time. -[W] selectViewListWithKey_ :: Ord k => Dynamic k -> - Dynamic (Map k v) -> (k -> Dynamic v -> Dynamic Bool -> m (Event a)) -> m (Event k) - --- Same as listWithKey, but takes initial values and an updates Event instead of a Dynamic. -[W] listWithKey' :: Ord k => - Map k v -> Event (Map k (Maybe v)) -> (k -> v -> Event v -> m a) -> m (Dynamic (Map k a)) -``` +Also see the "Collection management functions" section in the `reflex` Quick Reference. ### Utility widgets @@ -135,27 +112,12 @@ Some of these widget builders take a configuration record and return a record co ## Connecting to the real world (I/O) -### Connecting to DOM events - ```haskell -- Extract the specified Event from an 'El'. See Reflex.Dom.Widget.Basic [ ] domEvent :: EventName en -> El -> Event (EventResultType en) ``` -### Performing arbitrary I/O in response to Events - -```haskell --- Run side-effecting actions in Event when it occurs; returned Event contains --- results. Side effects run in (WidgetHost m) monad, which includes [S] and [H] --- and can also do I/O via liftIO -[W] performEvent :: Event ( WidgetHost m a) -> m (Event a) - --- Just run side-effects; no return Event -[W] performEvent_ :: Event ( WidgetHost m ()) -> m () - --- Actions run asynchronously; actions are given a callback to send return values -[W] performEventAsync :: Event ((a -> IO ()) -> WidgetHost m ()) -> m (Event a) -``` +Also see the "Connection to the real world" and "Time" sections in the `reflex` Quick Reference. ### XMLHttpRequest @@ -178,16 +140,6 @@ Convenience functions for XMLHttpRequest. see Reflex.Dom.Xhr [W] getAndDecode :: FromJSON a => Event Text -> m (Event (Maybe a)) ``` -### Time - -```haskell --- Create Event at given interval with given basis time. -[W] tickLossy :: NominalDiffTime -> UTCTime -> m (Event t TickInfo) - --- Delay an Event's occurrences by a given amount in seconds. -[W] delay :: NominalDiffTime -> Event t a -> m (Event t a) -``` - ## Startup ```haskell @@ -201,7 +153,6 @@ Convenience functions for XMLHttpRequest. see Reflex.Dom.Xhr [I] mainWidgetWithCss :: ByteString -> Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO () - --- One-shot Event that is triggered once all initial widgets are built -[W] getPostBuild :: m (Event ()) ``` + +Also see the corresponding section in the `reflex` Quick Reference. diff --git a/reflex-dom-core/default.nix b/reflex-dom-core/default.nix index 248375b1..3f8463b4 100644 --- a/reflex-dom-core/default.nix +++ b/reflex-dom-core/default.nix @@ -2,10 +2,10 @@ , bytestring, constraints, containers, contravariant, data-default , dependent-map, dependent-sum, dependent-sum-template, directory , exception-transformers, ghcjs-dom, hlint, jsaddle, keycode, lens -, monad-control, mtl, primitive, random, ref-tf, reflex, semigroups -, stdenv, stm, template-haskell, temporary, text, these, time -, transformers, unbounded-delays, unix, zenc, hashable -, chromium, process, jsaddle-warp, linux-namespaces, iproute +, monad-control, mtl, primitive, ref-tf, reflex, semigroups, stdenv +, stm, template-haskell, temporary, text, these, transformers +, unix, zenc, hashable, chromium, process, jsaddle-warp +, linux-namespaces, iproute }: let addGcTestDepends = drv: if stdenv.system != "x86_64-linux" then drv else drv // { testHaskellDepends = (drv.testHaskellDepends or []) ++ [ temporary jsaddle-warp process linux-namespaces ]; @@ -19,9 +19,9 @@ in mkDerivation (addGcTestDepends { aeson base bifunctors bimap blaze-builder bytestring constraints containers contravariant data-default dependent-map dependent-sum dependent-sum-template directory exception-transformers ghcjs-dom - jsaddle keycode lens monad-control mtl primitive random ref-tf - reflex semigroups stm template-haskell text these time transformers - unbounded-delays unix zenc + jsaddle keycode lens monad-control mtl primitive ref-tf + reflex semigroups stm template-haskell text these transformers + unix zenc ] ++ (if ghc.isGhcjs or false then [ hashable ] else []); diff --git a/reflex-dom-core/reflex-dom-core.cabal b/reflex-dom-core/reflex-dom-core.cabal index 6faf6193..70de2fb8 100644 --- a/reflex-dom-core/reflex-dom-core.cabal +++ b/reflex-dom-core/reflex-dom-core.cabal @@ -64,16 +64,13 @@ library monad-control >= 1.0.1 && < 1.1, mtl >= 2.1 && < 2.3, primitive >= 0.5 && < 0.7, - random == 1.1.*, ref-tf == 0.4.*, reflex == 0.5.*, semigroups >= 0.16 && < 0.19, stm == 2.4.*, text == 1.2.*, these >= 0.4 && < 0.8, - time >= 1.4 && < 1.9, transformers >= 0.3 && < 0.6, - unbounded-delays >= 0.1.0.9 && < 0.2, zenc == 0.1.* if impl(ghcjs) @@ -152,6 +149,7 @@ test-suite hlint test-suite gc build-depends: base + , reflex , reflex-dom-core , jsaddle , jsaddle-warp diff --git a/reflex-dom-core/src/Foreign/JavaScript/TH.hs b/reflex-dom-core/src/Foreign/JavaScript/TH.hs index 8f01ce74..a7edf8b2 100644 --- a/reflex-dom-core/src/Foreign/JavaScript/TH.hs +++ b/reflex-dom-core/src/Foreign/JavaScript/TH.hs @@ -136,7 +136,7 @@ instance PrimMonad m => PrimMonad (WithJSContextSingleton x m) where type PrimState (WithJSContextSingleton x m) = PrimState m primitive = lift . primitive -instance MonadAdjust t m => MonadAdjust t (WithJSContextSingleton x m) where +instance Adjustable t m => Adjustable t (WithJSContextSingleton x m) where runWithReplace a0 a' = WithJSContextSingleton $ runWithReplace (coerce a0) (coerceEvent a') traverseIntMapWithKeyWithAdjust f dm0 dm' = WithJSContextSingleton $ traverseIntMapWithKeyWithAdjust (\k v -> unWithJSContextSingleton $ f k v) (coerce dm0) (coerceEvent dm') traverseDMapWithKeyWithAdjust f dm0 dm' = WithJSContextSingleton $ traverseDMapWithKeyWithAdjust (\k v -> unWithJSContextSingleton $ f k v) (coerce dm0) (coerceEvent dm') diff --git a/reflex-dom-core/src/Reflex/Dom/Builder/Class.hs b/reflex-dom-core/src/Reflex/Dom/Builder/Class.hs index 1d802041..95cb2d2e 100644 --- a/reflex-dom-core/src/Reflex/Dom/Builder/Class.hs +++ b/reflex-dom-core/src/Reflex/Dom/Builder/Class.hs @@ -70,7 +70,7 @@ class Default (EventSpec d EventResult) => DomSpace d where -- | @'DomBuilder' t m@ indicates that @m@ is a 'Monad' capable of building -- dynamic DOM in the 'Reflex' timeline @t@ -class (Monad m, Reflex t, DomSpace (DomBuilderSpace m), MonadAdjust t m) => DomBuilder t m | m -> t where +class (Monad m, Reflex t, DomSpace (DomBuilderSpace m), Adjustable t m) => DomBuilder t m | m -> t where type DomBuilderSpace m :: * textNode :: TextNodeConfig t -> m (TextNode (DomBuilderSpace m) t) default textNode :: ( MonadTrans f diff --git a/reflex-dom-core/src/Reflex/Dom/Builder/Immediate.hs b/reflex-dom-core/src/Reflex/Dom/Builder/Immediate.hs index 2822fa8c..fa000c56 100644 --- a/reflex-dom-core/src/Reflex/Dom/Builder/Immediate.hs +++ b/reflex-dom-core/src/Reflex/Dom/Builder/Immediate.hs @@ -337,7 +337,7 @@ extractUpTo df s e = liftJSM $ do void $ call f f (df, s, e) #endif -type SupportsImmediateDomBuilder t m = (Reflex t, MonadJSM m, MonadHold t m, MonadFix m, MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref JSM, MonadAdjust t m, PrimMonad m) +type SupportsImmediateDomBuilder 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) {-# INLINABLE collectUpTo #-} collectUpTo :: (MonadJSM m, IsNode start, IsNode end) => start -> end -> m DOM.DocumentFragment @@ -400,7 +400,7 @@ wrap e cfg = do } {-# INLINABLE makeElement #-} -makeElement :: forall er t m a. (MonadJSM m, MonadFix m, MonadReflexCreateTrigger t m, MonadAdjust t m) => Text -> ElementConfig er t GhcjsDomSpace -> ImmediateDomBuilderT t m a -> ImmediateDomBuilderT t m ((Element er GhcjsDomSpace t, a), DOM.Element) +makeElement :: forall er t m a. (MonadJSM m, MonadFix m, MonadReflexCreateTrigger t m, Adjustable t m) => Text -> ElementConfig er t GhcjsDomSpace -> ImmediateDomBuilderT t m a -> ImmediateDomBuilderT t m ((Element er GhcjsDomSpace t, a), DOM.Element) makeElement elementTag cfg child = do doc <- askDocument e <- liftJSM $ uncheckedCastTo DOM.Element <$> case cfg ^. namespace of @@ -710,7 +710,7 @@ data ChildReadyStateInt -- ^The child is not yet installed in the DOM and if it has unready children then the key into the unready count tracking 'DMap' is given. deriving (Show, Read, Eq, Ord) -instance (Reflex t, MonadAdjust t m, MonadJSM m, MonadHold t m, MonadFix m, MonadRef m, Ref m ~ IORef, MonadReflexCreateTrigger t m, PrimMonad m) => MonadAdjust t (ImmediateDomBuilderT t m) where +instance (Reflex t, Adjustable t m, MonadJSM m, MonadHold t m, MonadFix m, MonadRef m, Ref m ~ IORef, MonadReflexCreateTrigger t m, PrimMonad m) => Adjustable t (ImmediateDomBuilderT t m) where {-# INLINABLE runWithReplace #-} runWithReplace a0 a' = do initialEnv <- ImmediateDomBuilderT ask @@ -845,7 +845,7 @@ instance (Reflex t, MonadAdjust t m, MonadJSM m, MonadHold t m, MonadFix m, Mona {-# INLINABLE traverseDMapWithKeyWithAdjust' #-} -traverseDMapWithKeyWithAdjust' :: forall t m (k :: * -> *) v v'. (MonadAdjust t m, MonadHold t m, MonadFix m, MonadIO m, MonadJSM m, PrimMonad m, MonadRef m, Ref m ~ IORef, MonadReflexCreateTrigger t m, DMap.GCompare k) => (forall a. k a -> v a -> ImmediateDomBuilderT t m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> ImmediateDomBuilderT t m (DMap k v', Event t (PatchDMap k v')) +traverseDMapWithKeyWithAdjust' :: forall t m (k :: * -> *) v v'. (Adjustable t m, MonadHold t m, MonadFix m, MonadIO m, MonadJSM m, PrimMonad m, MonadRef m, Ref m ~ IORef, MonadReflexCreateTrigger t m, DMap.GCompare k) => (forall a. k a -> v a -> ImmediateDomBuilderT t m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> ImmediateDomBuilderT t m (DMap k v', Event t (PatchDMap k v')) traverseDMapWithKeyWithAdjust' = hoistTraverseWithKeyWithAdjust traverseDMapWithKeyWithAdjust mapPatchDMap updateChildUnreadiness applyDomUpdate_ where @@ -879,7 +879,7 @@ traverseDMapWithKeyWithAdjust' = liftIO $ writeIORef currentChildInstallations $! applyAlways (weakenPatchDMapWith (_child_installation . getCompose) $ PatchDMap p) childInstallations {-# INLINABLE traverseIntMapWithKeyWithAdjust' #-} -traverseIntMapWithKeyWithAdjust' :: forall t m v v'. (MonadAdjust t m, MonadHold t m, MonadFix m, MonadIO m, MonadJSM m, PrimMonad m, MonadRef m, Ref m ~ IORef, MonadReflexCreateTrigger t m) => (IntMap.Key -> v -> ImmediateDomBuilderT t m v') -> IntMap v -> Event t (PatchIntMap v) -> ImmediateDomBuilderT t m (IntMap v', Event t (PatchIntMap v')) +traverseIntMapWithKeyWithAdjust' :: forall t m v v'. (Adjustable t m, MonadHold t m, MonadFix m, MonadIO m, MonadJSM m, PrimMonad m, MonadRef m, Ref m ~ IORef, MonadReflexCreateTrigger t m) => (IntMap.Key -> v -> ImmediateDomBuilderT t m v') -> IntMap v -> Event t (PatchIntMap v) -> ImmediateDomBuilderT t m (IntMap v', Event t (PatchIntMap v')) traverseIntMapWithKeyWithAdjust' = hoistTraverseIntMapWithKeyWithAdjust traverseIntMapWithKeyWithAdjust updateChildUnreadiness applyDomUpdate_ where @@ -920,7 +920,7 @@ traverseIntMapWithKeyWithAdjust' = -- adjust only versus adjust and move to the caller by way of its @applyDomUpdate_@ parameter. {-# INLINABLE hoistTraverseIntMapWithKeyWithAdjust #-} hoistTraverseIntMapWithKeyWithAdjust :: forall v v' t m p. - ( MonadAdjust t m + ( Adjustable t m , MonadHold t m , MonadIO m , MonadJSM m @@ -936,7 +936,7 @@ hoistTraverseIntMapWithKeyWithAdjust :: forall v v' t m p. -> Event t (p v) -> RequesterT t JSM Identity (TriggerEventT t m) (IntMap (Child ChildReadyStateInt v'), Event t (p (Child ChildReadyStateInt v'))) ) - -- ^@base@: The base monad's 'MonadAdjust' traversal function to augment with DOM handling. + -- ^@base@: The base monad's 'Adjustable' traversal function to augment with DOM handling. -> (p (Child ChildReadyStateInt v') -> IntMap (IORef ChildReadyStateInt) -> IO (IntMap (IORef ChildReadyStateInt))) -- ^@updateChildUnreadiness@: Given a patch for the children DOM elements, produce a patch for the childrens' unreadiness state -> (IORef (IntMap ChildInstallation) -> IORef DOM.Text -> p (Child ChildReadyStateInt v') -> JSM ()) @@ -1023,7 +1023,7 @@ hoistTraverseIntMapWithKeyWithAdjust base updateChildUnreadiness applyDomUpdate_ -- adjust only versus adjust and move to the caller by way of its @applyDomUpdate_@ parameter. {-# INLINABLE hoistTraverseWithKeyWithAdjust #-} hoistTraverseWithKeyWithAdjust :: forall (k :: * -> *) v v' t m p. - ( MonadAdjust t m + ( Adjustable t m , MonadHold t m , DMap.GCompare k , MonadIO m @@ -1043,7 +1043,7 @@ hoistTraverseWithKeyWithAdjust :: forall (k :: * -> *) v v' t m p. -> Event t (p k vv) -> RequesterT t JSM Identity (TriggerEventT t m) (DMap k vv', Event t (p k vv')) ) - -- ^@base@: The base monad's 'MonadAdjust' traversal function to augment with DOM handling. + -- ^@base@: The base monad's 'Adjustable' traversal function to augment with DOM handling. -> (forall vv vv'. (forall a. vv a -> vv' a) -> p k vv -> p k vv') -- ^@mapPatch@: A way of mapping over the patch type, e.g. 'mapPatchDMap' or 'mapPatchDMapWithMove' -> (p k (Compose (Child (ChildReadyState k)) v') -> DMap k (Constant (IORef (ChildReadyState k))) -> IO (DMap k (Constant (IORef (ChildReadyState k))))) diff --git a/reflex-dom-core/src/Reflex/Dom/Builder/InputDisabled.hs b/reflex-dom-core/src/Reflex/Dom/Builder/InputDisabled.hs index 06bd0a18..d5f9aa36 100644 --- a/reflex-dom-core/src/Reflex/Dom/Builder/InputDisabled.hs +++ b/reflex-dom-core/src/Reflex/Dom/Builder/InputDisabled.hs @@ -76,7 +76,7 @@ instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (InputDisabl newEventWithTrigger = lift . newEventWithTrigger newFanEventWithTrigger f = lift $ newFanEventWithTrigger f -instance MonadAdjust t m => MonadAdjust t (InputDisabledT m) where +instance Adjustable t m => Adjustable t (InputDisabledT m) where runWithReplace a0 a' = InputDisabledT $ runWithReplace (coerce a0) (coerceEvent a') traverseDMapWithKeyWithAdjust f dm0 dm' = InputDisabledT $ traverseDMapWithKeyWithAdjust (\k v -> runInputDisabledT $ f k v) (coerce dm0) (coerceEvent dm') traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = InputDisabledT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runInputDisabledT $ f k v) (coerce dm0) (coerceEvent dm') diff --git a/reflex-dom-core/src/Reflex/Dom/Builder/Static.hs b/reflex-dom-core/src/Reflex/Dom/Builder/Static.hs index 6c283eea..6d352bf2 100644 --- a/reflex-dom-core/src/Reflex/Dom/Builder/Static.hs +++ b/reflex-dom-core/src/Reflex/Dom/Builder/Static.hs @@ -33,6 +33,7 @@ import Data.Dependent.Sum (DSum (..)) import Data.Functor.Compose import Data.Functor.Constant import qualified Data.Map as Map +import Data.Map.Misc (applyMap) import Data.Monoid import qualified Data.Set as Set import Data.Text (Text) @@ -41,7 +42,6 @@ import Data.Tuple import GHC.Generics import Reflex.Class import Reflex.Dom.Builder.Class -import Reflex.Dom.Widget.Basic (applyMap) import Reflex.Dynamic import Reflex.Host.Class import Reflex.PerformEvent.Base @@ -122,7 +122,7 @@ instance MonadRef m => MonadRef (StaticDomBuilderT t m) where instance MonadAtomicRef m => MonadAtomicRef (StaticDomBuilderT t m) where atomicModifyRef r = lift . atomicModifyRef r -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) +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, Adjustable t m) data StaticDomSpace @@ -146,7 +146,7 @@ instance DomSpace StaticDomSpace where type RawSelectElement StaticDomSpace = () addEventSpecFlags _ _ _ _ = StaticEventSpec -instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (StaticDomBuilderT t m) where +instance (Reflex t, Adjustable t m, MonadHold t m) => Adjustable t (StaticDomBuilderT t m) where runWithReplace a0 a' = do e <- StaticDomBuilderT ask (result0, result') <- lift $ runWithReplace (runStaticDomBuilderT a0 e) (flip runStaticDomBuilderT e <$> a') @@ -157,7 +157,7 @@ instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (StaticDomB traverseDMapWithKeyWithAdjustWithMove = hoistDMapWithKeyWithAdjust traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove hoistDMapWithKeyWithAdjust :: forall (k :: * -> *) v v' t m p. - ( MonadAdjust t m + ( Adjustable t m , MonadHold t m , PatchTarget (p k (Constant (Behavior t Builder))) ~ DMap k (Constant (Behavior t Builder)) , Patch (p k (Constant (Behavior t Builder))) diff --git a/reflex-dom-core/src/Reflex/Dom/Core.hs b/reflex-dom-core/src/Reflex/Dom/Core.hs index fe4a371f..6d3976f1 100644 --- a/reflex-dom-core/src/Reflex/Dom/Core.hs +++ b/reflex-dom-core/src/Reflex/Dom/Core.hs @@ -12,7 +12,6 @@ import Reflex.Dom.Modals.Base as X import Reflex.Dom.Modals.Class as X import Reflex.Dom.Old as X import Reflex.Dom.Prerender as X -import Reflex.Dom.Time as X import Reflex.Dom.WebSocket as X import Reflex.Dom.Widget as X import Reflex.Dom.Xhr as X diff --git a/reflex-dom-core/src/Reflex/Dom/Location.hs b/reflex-dom-core/src/Reflex/Dom/Location.hs index b3f1ef7b..c91a1082 100644 --- a/reflex-dom-core/src/Reflex/Dom/Location.hs +++ b/reflex-dom-core/src/Reflex/Dom/Location.hs @@ -1,13 +1,86 @@ -module Reflex.Dom.Location (getLocationHost, getLocationProtocol) where +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +module Reflex.Dom.Location + ( browserHistoryWith + , getLocationAfterHost + , getLocationFragment + , getLocationHost + , getLocationPath + , getLocationProtocol + , getLocationUrl + ) where + +import Reflex +import Reflex.Dom.Builder.Immediate (wrapDomEvent) + +import Control.Lens ((^.)) +import Control.Monad ((>=>)) import Data.Text (Text) -import GHCJS.DOM (currentWindowUnchecked) -import GHCJS.DOM.Location (getHost, getProtocol) -import GHCJS.DOM.Types (MonadJSM) -import GHCJS.DOM.Window (getLocation) +import qualified GHCJS.DOM as DOM +import qualified GHCJS.DOM.EventM as DOM +import qualified GHCJS.DOM.Location as Location +import GHCJS.DOM.Types (Location) +import qualified GHCJS.DOM.Types as DOM +import qualified GHCJS.DOM.Window as Window +import qualified GHCJS.DOM.WindowEventHandlers as DOM +import Language.Javascript.JSaddle (FromJSString, MonadJSM, ToJSString, fromJSValUnchecked, js1, toJSVal) + +withLocation :: (MonadJSM m) => (Location -> m a) -> m a +withLocation f = DOM.currentWindowUnchecked >>= Window.getLocation >>= f + +-- | Returns the full URI-decoded URL of the current window location. +getLocationUrl :: (MonadJSM m) => m Text +getLocationUrl = withLocation (Location.getHref >=> decodeURIText) + +-- | Returns the host of the current window location getLocationHost :: (MonadJSM m) => m Text -getLocationHost = currentWindowUnchecked >>= getLocation >>= getHost +getLocationHost = withLocation Location.getHost +-- | Returns the protocol/scheme (e.g. @http:@ or @https:@) of the current window location getLocationProtocol :: (MonadJSM m) => m Text -getLocationProtocol = currentWindowUnchecked >>= getLocation >>= getProtocol +getLocationProtocol = withLocation Location.getProtocol + +-- | Returns the URI-decoded location after the host and port; i.e. returns the path, query, and fragment of the location. +getLocationAfterHost :: (MonadJSM m) => m Text +getLocationAfterHost = withLocation $ \loc -> do + pathname <- Location.getPathname loc + search <- Location.getSearch loc + hash <- Location.getHash loc + decodeURI (mconcat [pathname, search, hash] :: Text) + +-- | Returns the URI-decoded path of the current window location. +getLocationPath :: (MonadJSM m) => m Text +getLocationPath = withLocation (Location.getPathname >=> decodeURIText) + +-- | Returns the URI-decoded fragment/hash of the current window location. +getLocationFragment :: (MonadJSM m) => m Text +getLocationFragment = withLocation (Location.getHash >=> decodeURIText) + + +-- | Decodes a URI with JavaScript's @decodeURI@ function. +-- +-- FIXME: @decodeURI@ will throw when URI is malformed +decodeURI :: (MonadJSM m, ToJSString a, FromJSString b) => a -> m b +decodeURI input = do + window <- DOM.currentWindowUnchecked + window' <- DOM.liftJSM $ toJSVal window + DOM.liftJSM $ window' ^. js1 ("decodeURI"::Text) input >>= fromJSValUnchecked + +decodeURIText :: (MonadJSM m) => Text -> m Text +decodeURIText = decodeURI + +-- | Builds a Dynamic carrying the current window location. +browserHistoryWith :: (MonadJSM m, TriggerEvent t m, MonadHold t m) + => (forall jsm. MonadJSM jsm => Location -> jsm a) + -- ^ A function to encode the window location in a more useful form (e.g. @getLocationAfterHost@). + -> m (Dynamic t a) +browserHistoryWith f = do + window <- DOM.currentWindowUnchecked + location <- Window.getLocation window + loc0 <- f location + locEv <- wrapDomEvent window (`DOM.on` DOM.popState) $ f location + holdDyn loc0 locEv diff --git a/reflex-dom-core/src/Reflex/Dom/Modals/Base.hs b/reflex-dom-core/src/Reflex/Dom/Modals/Base.hs index 6814b747..2ac0f086 100644 --- a/reflex-dom-core/src/Reflex/Dom/Modals/Base.hs +++ b/reflex-dom-core/src/Reflex/Dom/Modals/Base.hs @@ -106,7 +106,7 @@ instance MonadAtomicRef m => MonadAtomicRef (ModalsT t m) where {-# INLINABLE atomicModifyRef #-} atomicModifyRef r = lift . atomicModifyRef r -instance (MonadAdjust t m, MonadHold t m, MonadFix m) => MonadAdjust t (ModalsT t m) where +instance (Adjustable t m, MonadHold t m, MonadFix m) => Adjustable t (ModalsT t m) where runWithReplace a0 a' = ModalsT $ runWithReplace (unModalsT a0) (fmapCheap unModalsT a') traverseDMapWithKeyWithAdjust f dm0 dm' = ModalsT $ traverseDMapWithKeyWithAdjust (coerce f) dm0 dm' traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = ModalsT $ traverseDMapWithKeyWithAdjustWithMove (coerce f) dm0 dm' diff --git a/reflex-dom-core/src/Reflex/Dom/Time.hs b/reflex-dom-core/src/Reflex/Dom/Time.hs index e085fc6d..c05249d9 100644 --- a/reflex-dom-core/src/Reflex/Dom/Time.hs +++ b/reflex-dom-core/src/Reflex/Dom/Time.hs @@ -1,218 +1,3 @@ -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE RecursiveDo #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeFamilies #-} -module Reflex.Dom.Time where +module Reflex.Dom.Time {-# DEPRECATED "Use Reflex.Time instead" #-} (module Reflex.Time) where -import Reflex.Class -import Reflex.Dynamic -import Reflex.PerformEvent.Class -import Reflex.PostBuild.Class -import Reflex.TriggerEvent.Class - -import Control.Concurrent -import qualified Control.Concurrent.Thread.Delay as Concurrent -import Control.Monad -import Control.Monad.Fix -import Control.Monad.IO.Class -import Data.Align -import Data.Fixed -import Data.Sequence (Seq, (|>)) -import qualified Data.Sequence as Seq -import Data.These -import Data.Time.Clock -import Data.Typeable -import System.Random - -data TickInfo - = TickInfo { _tickInfo_lastUTC :: UTCTime - -- ^ UTC time immediately after the last tick. - , _tickInfo_n :: Integer - -- ^ Number of time periods since t0 - , _tickInfo_alreadyElapsed :: NominalDiffTime - -- ^ Amount of time already elapsed in the current tick period. - } - deriving (Eq, Ord, Show, Typeable) - --- | Special case of tickLossyFrom that uses the post-build event to start the --- tick thread. -tickLossy :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> UTCTime -> m (Event t TickInfo) -tickLossy dt t0 = tickLossyFrom dt t0 =<< getPostBuild - --- | Send events over time with the given basis time and interval --- If the system starts running behind, occurrences will be dropped rather than buffered --- Each occurrence of the resulting event will contain the index of the current interval, with 0 representing the basis time -tickLossyFrom - :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) - => NominalDiffTime - -> UTCTime - -> Event t a - -- ^ Event that starts a tick generation thread. Usually you want this to - -- be something like the result of getPostBuild that only fires once. But - -- there could be uses for starting multiple timer threads. - -> m (Event t TickInfo) -tickLossyFrom dt t0 e = tickLossyFrom' $ (dt, t0) <$ e - --- | Generalization of tickLossyFrom that takes dt and t0 in the event. -tickLossyFrom' - :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) - => Event t (NominalDiffTime, UTCTime) - -- ^ Event that starts a tick generation thread. Usually you want this to - -- be something like the result of getPostBuild that only fires once. But - -- there could be uses for starting multiple timer threads. - -> m (Event t TickInfo) -tickLossyFrom' e = do - rec result <- performEventAsync $ callAtNextInterval <$> leftmost [e, snd <$> result] - return $ fst <$> result - where callAtNextInterval pair cb = void $ liftIO $ forkIO $ do - tick <- uncurry getCurrentTick pair - Concurrent.delay $ ceiling $ (fst pair - _tickInfo_alreadyElapsed tick) * 1000000 - cb (tick, pair) - -clockLossy :: (MonadIO m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), PostBuild t m, MonadHold t m, MonadFix m) => NominalDiffTime -> UTCTime -> m (Dynamic t TickInfo) -clockLossy dt t0 = do - initial <- liftIO $ getCurrentTick dt t0 - e <- tickLossy dt t0 - holdDyn initial e - -getCurrentTick :: NominalDiffTime -> UTCTime -> IO TickInfo -getCurrentTick dt t0 = do - t <- getCurrentTime - let offset = t `diffUTCTime` t0 - (n, alreadyElapsed) = offset `divMod'` dt - return $ TickInfo t n alreadyElapsed - --- | Delay an Event's occurrences by a given amount in seconds. -delay :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> Event t a -> m (Event t a) -delay dt e = performEventAsync $ ffor e $ \a cb -> liftIO $ void $ forkIO $ do - Concurrent.delay $ ceiling $ dt * 1000000 - cb a - --- | Send events with Poisson timing with the given basis and rate --- Each occurence of the resulting event will contain the index of --- the current interval, with 0 representing the basis time -poissonLossyFrom - :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m) - => g - -> Double - -- ^ Poisson event rate (Hz) - -> UTCTime - -- ^ Baseline time for events - -> Event t a - -- ^ Event that starts a tick generation thread. Usually you want this to - -- be something like the result of getPostBuild that only fires once. But - -- there could be uses for starting multiple timer threads. - -- Start sending events in response to the event parameter. - -> m (Event t TickInfo) -poissonLossyFrom rnd rate = inhomogeneousPoissonFrom rnd (constant rate) rate - - --- | Send events with Poisson timing with the given basis and rate --- Each occurence of the resulting event will contain the index of --- the current interval, with 0 representing the basis time. --- Automatically begin sending events when the DOM is built -poissonLossy - :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m, PostBuild t m) - => g - -> Double - -- ^ Poisson event rate (Hz) - -> UTCTime - -- ^ Baseline time for events - -> m (Event t TickInfo) -poissonLossy rnd rate t0 = poissonLossyFrom rnd rate t0 =<< getPostBuild - --- | Send events with inhomogeneous Poisson timing with the given basis --- and variable rate. Provide a maxRate that you expect to support. -inhomogeneousPoissonFrom - :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m) - => g - -> Behavior t Double - -> Double - -> UTCTime - -> Event t a - -> m (Event t TickInfo) -inhomogeneousPoissonFrom rnd rate maxRate t0 e = do - - -- Create a thread for producing homogeneous poisson events - -- along with random Doubles (usage of Double's explained below) - ticksWithRateRand <- performEventAsync $ - fmap callAtNextInterval e - - -- Filter homogeneous events according to associated - -- random values and the current rate parameter - return $ attachWithMaybe filterFun rate ticksWithRateRand - - where - - -- Inhomogeneous poisson processes are built from faster - -- homogeneous ones by randomly dropping events from the - -- fast process. For each fast homogeneous event, choose - -- a uniform random sample from (0, rMax). If the - -- inhomogeneous rate at this moment is greater than the - -- random sample, then keep this event, otherwise drop it - filterFun :: Double -> (TickInfo, Double) -> Maybe TickInfo - filterFun r (tInfo, p) - | r >= p = Just tInfo - | otherwise = Nothing - - callAtNextInterval _ cb = void $ liftIO $ forkIO $ go t0 rnd cb 0 - - go tTargetLast lastGen cb lastN = do - t <- getCurrentTime - - -- Generate random numbers for this poisson interval (u) - -- and sample-retention likelihood (p) - let (u, nextGen) = randomR (0,1) lastGen - (p :: Double, nextGen') = randomR (0,maxRate) nextGen - - -- Inter-event interval is drawn from exponential - -- distribution accourding to u - let dt = realToFrac $ (-1) * log u / maxRate :: NominalDiffTime - nEvents = lastN + 1 - alreadyElapsed = diffUTCTime t tTargetLast - tTarget = addUTCTime dt tTargetLast - thisDelay = realToFrac $ diffUTCTime tTarget t :: Double - Concurrent.delay $ ceiling $ thisDelay * 1000000 - _ <- cb (TickInfo t nEvents alreadyElapsed, p) - go tTarget nextGen' cb nEvents - --- | Send events with inhomogeneous Poisson timing with the given basis --- and variable rate. Provide a maxRate that you expect to support -inhomogeneousPoisson - :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m, PostBuild t m) - => g - -> Behavior t Double - -> Double - -> UTCTime - -> m (Event t TickInfo) -inhomogeneousPoisson rnd rate maxRate t0 = - inhomogeneousPoissonFrom rnd rate maxRate t0 =<< getPostBuild - --- | Block occurrences of an Event until the given number of seconds elapses without --- the Event firing, at which point the last occurrence of the Event will fire. -debounce :: (MonadFix m, MonadHold t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> Event t a -> m (Event t a) -debounce dt e = do - n :: Dynamic t Integer <- count e - let tagged = attachPromptlyDynWith (,) n e - delayed <- delay dt tagged - return $ attachWithMaybe (\n' (t, v) -> if n' == t then Just v else Nothing) (current n) delayed - --- | When the given 'Event' occurs, wait the given amount of time and collect --- all occurrences during that time. Then, fire the output 'Event' with the --- collected output. -batchOccurrences :: (MonadFix m, MonadHold t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> Event t a -> m (Event t (Seq a)) -batchOccurrences t newValues = do - let f s x = (Just newState, out) - where newState = case x of - This a -> s |> a - That _ -> mempty - These a _ -> Seq.singleton a - out = case x of - This _ -> if Seq.null s then Just () else Nothing - That _ -> Nothing - These _ _ -> Just () - rec (buffer, toDelay) <- mapAccumMaybe f mempty $ align newValues delayed - delayed <- delay t toDelay - return $ tag buffer delayed +import Reflex.Time diff --git a/reflex-dom-core/src/Reflex/Dom/Widget/Basic.hs b/reflex-dom-core/src/Reflex/Dom/Widget/Basic.hs index 42260c40..65112c18 100644 --- a/reflex-dom-core/src/Reflex/Dom/Widget/Basic.hs +++ b/reflex-dom-core/src/Reflex/Dom/Widget/Basic.hs @@ -3,7 +3,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} @@ -12,7 +11,6 @@ {-# LANGUAGE TypeFamilies #-} module Reflex.Dom.Widget.Basic ( partitionMapBySetLT - , listHoldWithKey , ChildResult (..) -- * Displaying Values @@ -24,21 +22,6 @@ module Reflex.Dom.Widget.Basic , widgetHold , untilReady - -- * Working with Maps - , diffMapNoEq - , diffMap - , applyMap - , mapPartitionEithers - , applyMapKeysSet - - -- * Widgets on Collections - , listWithKey - , listWithKey' - , listWithKeyShallowDiff - , listViewWithKey - , selectViewListWithKey - , selectViewListWithKey_ - -- * Creating DOM Elements , el , elAttr @@ -56,10 +39,6 @@ module Reflex.Dom.Widget.Basic , dynamicAttributesToModifyAttributes , dynamicAttributesToModifyAttributesWithInitial - -- * List Utils - , list - , simpleList - -- * Specific DOM Elements , Link (..) , linkClass @@ -80,9 +59,12 @@ module Reflex.Dom.Widget.Basic , tabDisplay , HasAttributes (..) + , module Reflex.Collection + , module Data.Map.Misc ) where import Reflex.Class +import Reflex.Collection import Reflex.Dom.Builder.Class import Reflex.Dom.Class import Reflex.Dynamic @@ -95,9 +77,9 @@ import Data.Align import Data.Default import Data.Either import Data.Foldable -import Data.Functor.Misc import Data.Map (Map) import qualified Data.Map as Map +import Data.Map.Misc import Data.Maybe import Data.Set (Set) import qualified Data.Set as Set @@ -129,13 +111,6 @@ partitionMapBySetLT s m0 = Map.fromDistinctAscList $ go (Set.toAscList s) m0 newtype ChildResult t k a = ChildResult { unChildResult :: (a, Event t (Map k (Maybe (ChildResult t k a)))) } -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)) -listHoldWithKey m0 m' f = do - let dm0 = mapWithFunctorToDMap $ Map.mapWithKey f m0 - dm' = fmap (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey (\k v -> ComposeMaybe $ fmap (f k) v)) m' - (a0, a') <- sequenceDMapWithAdjust dm0 dm' - fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a' --TODO: Move the dmapToMap to the righthand side so it doesn't get fully redone every time - text :: DomBuilder t m => Text -> m () text t = void $ textNode $ def & textNodeConfig_initialContents .~ t @@ -182,107 +157,6 @@ untilReady a b = do postBuild <- getPostBuild runWithReplace a $ b <$ postBuild -diffMapNoEq :: (Ord k) => Map k v -> Map k v -> Map k (Maybe v) -diffMapNoEq olds news = flip Map.mapMaybe (align olds news) $ \case - This _ -> Just Nothing - These _ new -> Just $ Just new - That new -> Just $ Just new - -diffMap :: (Ord k, Eq v) => Map k v -> Map k v -> Map k (Maybe v) -diffMap olds news = flip Map.mapMaybe (align olds news) $ \case - This _ -> Just Nothing - These old new - | old == new -> Nothing - | otherwise -> Just $ Just new - That new -> Just $ Just new - -applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v -applyMap patch old = insertions `Map.union` (old `Map.difference` deletions) - where (deletions, insertions) = mapPartitionEithers $ maybeToEither <$> patch - maybeToEither = \case - Nothing -> Left () - Just r -> Right r - -mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b) -mapPartitionEithers m = (fromLeft <$> ls, fromRight <$> rs) - where (ls, rs) = Map.partition isLeft m - fromLeft (Left l) = l - fromLeft _ = error "mapPartitionEithers: fromLeft received a Right value; this should be impossible" - fromRight (Right r) = r - fromRight _ = error "mapPartitionEithers: fromRight received a Left value; this should be impossible" - --- | Apply a map patch to a set --- > applyMapKeysSet patch (Map.keysSet m) == Map.keysSet (applyMap patch m) -applyMapKeysSet :: Ord k => Map k (Maybe v) -> Set k -> Set k -applyMapKeysSet patch old = Map.keysSet insertions `Set.union` (old `Set.difference` Map.keysSet deletions) - where (insertions, deletions) = Map.partition isJust patch - ---TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value -listWithKey :: forall t k v m a. (Ord k, DomBuilder t m, PostBuild t m, MonadFix m, MonadHold t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a)) -listWithKey vals mkChild = do - postBuild <- getPostBuild - let childValChangedSelector = fanMap $ updated vals - -- We keep track of changes to children values in the mkChild function we pass to listHoldWithKey - -- The other changes we need to keep track of are child insertions and deletions. diffOnlyKeyChanges - -- keeps track of insertions and deletions but ignores value changes, since they're already accounted for. - diffOnlyKeyChanges olds news = flip Map.mapMaybe (align olds news) $ \case - This _ -> Just Nothing - These _ _ -> Nothing - That new -> Just $ Just new - rec sentVals :: Dynamic t (Map k v) <- foldDyn applyMap Map.empty changeVals - let changeVals :: Event t (Map k (Maybe v)) - changeVals = attachWith diffOnlyKeyChanges (current sentVals) $ leftmost - [ updated vals - , tag (current vals) postBuild --TODO: This should probably be added to the attachWith, not to the updated; if we were using diffMap instead of diffMapNoEq, I think it might not work - ] - listHoldWithKey Map.empty changeVals $ \k v -> - mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k) - -{-# DEPRECATED listWithKey' "listWithKey' has been renamed to listWithKeyShallowDiff; also, its behavior has changed to fix a bug where children were always rebuilt (never updated)" #-} -listWithKey' :: (Ord k, DomBuilder t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a)) -listWithKey' = listWithKeyShallowDiff - --- | Display the given map of items (in key order) using the builder function provided, and update it with the given event. 'Nothing' update entries will delete the corresponding children, and 'Just' entries will create them if they do not exist or send an update event to them if they do. -listWithKeyShallowDiff :: (Ord k, DomBuilder t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a)) -listWithKeyShallowDiff initialVals valsChanged mkChild = do - let childValChangedSelector = fanMap $ fmap (Map.mapMaybe id) valsChanged - sentVals <- foldDyn applyMap Map.empty $ fmap (fmap void) valsChanged - let relevantPatch patch _ = case patch of - Nothing -> Just Nothing -- Even if we let a Nothing through when the element doesn't already exist, this doesn't cause a problem because it is ignored - Just _ -> Nothing -- We don't want to let spurious re-creations of items through - listHoldWithKey initialVals (attachWith (flip (Map.differenceWith relevantPatch)) (current sentVals) valsChanged) $ \k v -> - mkChild k v $ select childValChangedSelector $ Const2 k - ---TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value --- | Create a dynamically-changing set of Event-valued widgets. --- This is like listWithKey, specialized for widgets returning (Event t a). listWithKey would return 'Dynamic t (Map k (Event t a))' in this scenario, but listViewWithKey flattens this to 'Event t (Map k a)' via 'switch'. -listViewWithKey :: (Ord k, DomBuilder t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a)) -listViewWithKey vals mkChild = switch . fmap mergeMap <$> listViewWithKey' vals mkChild - -listViewWithKey' :: (Ord k, DomBuilder t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a)) -listViewWithKey' vals mkChild = current <$> listWithKey vals mkChild - --- | Create a dynamically-changing set of widgets, one of which is selected at any time. -selectViewListWithKey :: forall t m k v a. (DomBuilder t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m) - => Dynamic t k -- ^ Current selection key - -> Dynamic t (Map k v) -- ^ Dynamic key/value map - -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected - -> m (Event t (k, a)) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget. -selectViewListWithKey selection vals mkChild = do - let selectionDemux = demux selection -- For good performance, this value must be shared across all children - selectChild <- listWithKey vals $ \k v -> do - let selected = demuxed selectionDemux k - selectSelf <- mkChild k v selected - return $ fmap ((,) k) selectSelf - return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild - -selectViewListWithKey_ :: forall t m k v a. (DomBuilder t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m) - => Dynamic t k -- ^ Current selection key - -> Dynamic t (Map k v) -- ^ Dynamic key/value map - -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected - -> m (Event t k) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget. -selectViewListWithKey_ selection vals mkChild = fmap fst <$> selectViewListWithKey selection vals mkChild - -- | Create a DOM element -- > el "div" (text "Hello World") --
Hello World
@@ -380,15 +254,6 @@ dynamicAttributesToModifyAttributesWithInitial attrs0 d = do -- Copied and pasted from Reflex.Widget.Class -------------------------------------------------------------------------------- --- | Create a dynamically-changing set of widgets from a Dynamic key/value map. --- Unlike the 'withKey' variants, the child widgets are insensitive to which key they're associated with. -list :: (Ord k, DomBuilder t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a)) -list dm mkChild = listWithKey dm (\_ dv -> mkChild dv) - --- | Create a dynamically-changing set of widgets from a Dynamic list. -simpleList :: (DomBuilder t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a]) -simpleList xs mkChild = fmap (fmap (map snd . Map.toList)) $ flip list mkChild $ fmap (Map.fromList . zip [(1::Int)..]) xs - {- schedulePostBuild x = performEvent_ . (x <$) =<< getPostBuild diff --git a/reflex-dom-core/src/Reflex/Dom/Widget/Input.hs b/reflex-dom-core/src/Reflex/Dom/Widget/Input.hs index 51682fab..b7ccb575 100644 --- a/reflex-dom-core/src/Reflex/Dom/Widget/Input.hs +++ b/reflex-dom-core/src/Reflex/Dom/Widget/Input.hs @@ -41,6 +41,7 @@ import GHCJS.DOM.HTMLTextAreaElement (HTMLTextAreaElement) import GHCJS.DOM.Types (MonadJSM, File, uncheckedCastTo) import qualified GHCJS.DOM.Types as DOM (HTMLElement(..), EventTarget(..)) import Reflex.Class +import Reflex.Collection import Reflex.Dom.Builder.Class import Reflex.Dom.Builder.Immediate import Reflex.Dom.Class diff --git a/reflex-dom-core/src/Reflex/Dom/Widget/Lazy.hs b/reflex-dom-core/src/Reflex/Dom/Widget/Lazy.hs index 86243bfc..6c01e2e2 100644 --- a/reflex-dom-core/src/Reflex/Dom/Widget/Lazy.hs +++ b/reflex-dom-core/src/Reflex/Dom/Widget/Lazy.hs @@ -7,6 +7,7 @@ module Reflex.Dom.Widget.Lazy where import Reflex.Class +import Reflex.Collection import Reflex.Dom.Builder.Class import Reflex.Dom.Builder.Immediate import Reflex.Dom.Class diff --git a/reflex-dom-core/src/Reflex/Dom/Widget/Resize.hs b/reflex-dom-core/src/Reflex/Dom/Widget/Resize.hs index 6fa5095c..744579af 100644 --- a/reflex-dom-core/src/Reflex/Dom/Widget/Resize.hs +++ b/reflex-dom-core/src/Reflex/Dom/Widget/Resize.hs @@ -7,10 +7,10 @@ module Reflex.Dom.Widget.Resize where import Reflex.Class +import Reflex.Time import Reflex.Dom.Builder.Class import Reflex.Dom.Builder.Immediate import Reflex.Dom.Class -import Reflex.Dom.Time import Reflex.Dom.Widget.Basic import Reflex.PerformEvent.Class import Reflex.PostBuild.Class diff --git a/reflex-dom-core/test/gc.hs b/reflex-dom-core/test/gc.hs index 3b3d68d2..ca4b35c8 100644 --- a/reflex-dom-core/test/gc.hs +++ b/reflex-dom-core/test/gc.hs @@ -11,6 +11,7 @@ import Data.Int import GHC.Stats import Language.Javascript.JSaddle.Warp import Reflex.Dom.Core +import Reflex.Time import System.Exit import System.IO.Temp import System.Linux.Namespaces diff --git a/reflex-dom/cbits/MainWidget.c b/reflex-dom/cbits/MainWidget.c index 69b10ef2..9b9ff424 100644 --- a/reflex-dom/cbits/MainWidget.c +++ b/reflex-dom/cbits/MainWidget.c @@ -20,7 +20,7 @@ jobject Reflex_Dom_Android_MainWidget_start(jobject activity, const char *url, c jstring jurl = (*env)->NewStringUTF(env, url); assert(jurl); jstring initialJS = (*env)->NewStringUTF(env, jsaddleCallbacks->jsaddleJsData); - jobject result = (*env)->CallStaticObjectMethod(env, cls, startMainWidget, activity, jurl, (long)jsaddleCallbacks, initialJS); + jobject result = (*env)->CallStaticObjectMethod(env, cls, startMainWidget, activity, jurl, (jlong)jsaddleCallbacks, initialJS); (*env)->DeleteLocalRef(env, initialJS); if((*env)->ExceptionOccurred(env)) { __android_log_write(ANDROID_LOG_DEBUG, "MainWidget", "startMainWidget exception"); @@ -51,13 +51,13 @@ void Reflex_Dom_Android_MainWidget_runJS(jobject jsExecutor, const char* js) { (*env)->PopLocalFrame(env, 0); } -JNIEXPORT void JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallbacks_startProcessing (JNIEnv *env, jobject thisObj, long callbacksLong) { +JNIEXPORT void JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallbacks_startProcessing (JNIEnv *env, jobject thisObj, jlong callbacksLong) { const JSaddleCallbacks *callbacks = (const JSaddleCallbacks *)callbacksLong; (*(callbacks->jsaddleStart))(); return; } -JNIEXPORT void JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallbacks_processMessage (JNIEnv *env, jobject thisObj, long callbacksLong, jstring msg) { +JNIEXPORT void JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallbacks_processMessage (JNIEnv *env, jobject thisObj, jlong callbacksLong, jstring msg) { const JSaddleCallbacks *callbacks = (const JSaddleCallbacks *)callbacksLong; const char *msg_str = (*env)->GetStringUTFChars(env, msg, NULL); (*(callbacks->jsaddleResult))(msg_str); @@ -65,7 +65,7 @@ JNIEXPORT void JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallb return; } -JNIEXPORT jstring JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallbacks_processSyncMessage (JNIEnv *env, jobject thisObj, long callbacksLong, jstring msg) { +JNIEXPORT jstring JNICALL Java_org_reflexfrp_reflexdom_MainWidget_00024JSaddleCallbacks_processSyncMessage (JNIEnv *env, jobject thisObj, jlong callbacksLong, jstring msg) { const JSaddleCallbacks *callbacks = (const JSaddleCallbacks *)callbacksLong; const char *msg_str = (*env)->GetStringUTFChars(env, msg, NULL); char *next_str = (*(callbacks->jsaddleSyncResult))(msg_str); diff --git a/reflex-dom/examples/sortableList.hs b/reflex-dom/examples/sortableList.hs index 92404150..55e1ee70 100644 --- a/reflex-dom/examples/sortableList.hs +++ b/reflex-dom/examples/sortableList.hs @@ -67,7 +67,7 @@ displayRedrawTime e = do text "Time: " dynText =<< holdDyn "not yet run" (T.pack . show <$> diff) -simpleSortableList :: forall t m k v. (MonadHold t m, MonadFix m, MonadAdjust t m, Ord k) => (k -> v -> m ()) -> Map k v -> Event t (v -> v -> Ordering) -> Event t (v -> v -> Ordering) -> m () +simpleSortableList :: forall t m k v. (MonadHold t m, MonadFix m, Adjustable t m, Ord k) => (k -> v -> m ()) -> Map k v -> Event t (v -> v -> Ordering) -> Event t (v -> v -> Ordering) -> m () simpleSortableList f m0 resortFunc resortSlowFunc = do rec let resortPatchFast = attachWith (flip patchThatSortsMapWith) (currentIncremental m) resortFunc redrawPatch :: Map k v -> (v -> v -> Ordering) -> PatchMapWithMove k v diff --git a/reflex-dom/java/org/reflexfrp/reflexdom/MainWidget.java b/reflex-dom/java/org/reflexfrp/reflexdom/MainWidget.java index 2a793207..8fa01c3b 100644 --- a/reflex-dom/java/org/reflexfrp/reflexdom/MainWidget.java +++ b/reflex-dom/java/org/reflexfrp/reflexdom/MainWidget.java @@ -26,6 +26,7 @@ private static Object startMainWidget(Activity a, String url, long jsaddleCallba ws.setJavaScriptEnabled(true); ws.setAllowFileAccessFromFileURLs(true); ws.setAllowUniversalAccessFromFileURLs(true); + ws.setDomStorageEnabled(true); wv.setWebContentsDebuggingEnabled(true); // allow video to play without user interaction wv.getSettings().setMediaPlaybackRequiresUserGesture(false); diff --git a/reflex-dom/reflex-dom.cabal b/reflex-dom/reflex-dom.cabal index 77d6a38e..9c42a17c 100644 --- a/reflex-dom/reflex-dom.cabal +++ b/reflex-dom/reflex-dom.cabal @@ -90,7 +90,6 @@ library , Reflex.Dom.Location , Reflex.Dom.Main , Reflex.Dom.Old - , Reflex.Dom.Time , Reflex.Dom.WebSocket , Reflex.Dom.Widget , Reflex.Dom.Widget.Basic @@ -135,13 +134,16 @@ executable benchmark if !impl(ghcjs) buildable: False -benchmark krausest +executable krausest build-depends: base, reflex, reflex-dom, text, prim-uniq, dependent-map, containers, transformers, mtl, ghcjs-dom, ghc-prim, random, dependent-sum, vector hs-source-dirs: benchmarks main-is: krausest.hs - ghc-options: -O2 -fspecialise-aggressively -DGHCJS_GC_INTERVAL=60000 + ghc-options: -O2 -fspecialise-aggressively if impl(ghcjs) ghc-options: -dedupe + cpp-options: -DGHCJS_BROWSER -DGHCJS_GC_INTERVAL=60000 + else + buildable: False type: exitcode-stdio-1.0 source-repository head