{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecursiveDo #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} module Reflex.Dom.Widget.Basic ( -- * Displaying Values text , dynText , comment , dynComment , display , button , dyn , dyn_ , widgetHold , widgetHold_ -- * Creating DOM Elements , el , elAttr , elClass , elDynAttr , elDynClass , elDynAttrNS -- ** With Element Results , el' , elAttr' , elClass' , elDynAttr' , elDynClass' , elDynAttrNS' , dynamicAttributesToModifyAttributes , dynamicAttributesToModifyAttributesWithInitial -- * Specific DOM Elements , Link (..) , linkClass , link , divClass , dtdd , blank -- * Tables and Lists , tableDynAttr , tabDisplay , HasAttributes (..) , module Data.Map.Misc , module Reflex.Collection , module Reflex.Workflow , partitionMapBySetLT ) where import Reflex.Adjustable.Class import Reflex.Class import Reflex.Collection import Reflex.Dom.Builder.Class import Reflex.Dom.Class import Reflex.Dynamic import Reflex.Network import Reflex.PostBuild.Class import Reflex.Workflow import Control.Arrow import Control.Lens hiding (children, element) import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_) import Data.Align import Data.Default import Data.Either import Data.Foldable import Data.Functor (void) 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 import Data.Text (Text) import qualified Data.Text as T import Data.These import Data.Traversable import Prelude hiding (mapM, mapM_, sequence, sequence_) -- | 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. -- 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. -- No empty pieces will be included in the output. --TODO: This can probably be done more efficiently by dividing and conquering, re-using the structure of the Set instead of going through the Set linearally {-# DEPRECATED partitionMapBySetLT "This will be removed in future releases." #-} partitionMapBySetLT :: forall k v. Ord k => Set k -> Map k v -> Map (Either k ()) (Map k v) partitionMapBySetLT s m0 = Map.fromDistinctAscList $ go (Set.toAscList s) m0 where go :: [k] -> Map k v -> [(Either k (), Map k v)] go [] m = if Map.null m then [] else [(Right (), m)] go (h:t) m = let (lt, eq, gt) = Map.splitLookup h m geq = maybe id (Map.insert h) eq gt in if Map.null lt then go t geq else (Left h, lt) : go t geq {-# INLINABLE text #-} text :: DomBuilder t m => Text -> m () text t = void $ textNode $ def & textNodeConfig_initialContents .~ t {-# INLINABLE dynText #-} dynText :: forall t m. (PostBuild t m, DomBuilder t m) => Dynamic t Text -> m () dynText t = do postBuild <- getPostBuild void $ textNode $ (def :: TextNodeConfig t) & textNodeConfig_setContents .~ leftmost [ updated t , tag (current t) postBuild ] notReadyUntil postBuild comment :: DomBuilder t m => Text -> m () comment t = void $ commentNode $ def & commentNodeConfig_initialContents .~ t {-# INLINABLE dynComment #-} dynComment :: forall t m. (PostBuild t m, DomBuilder t m) => Dynamic t Text -> m () dynComment t = do postBuild <- getPostBuild void $ commentNode $ (def :: CommentNodeConfig t) & commentNodeConfig_setContents .~ leftmost [ updated t , tag (current t) postBuild ] notReadyUntil postBuild display :: (PostBuild t m, DomBuilder t m, Show a) => Dynamic t a -> m () display = dynText . fmap (T.pack . show) button :: DomBuilder t m => Text -> m (Event t ()) button t = do (e, _) <- element "button" def $ text t return $ domEvent Click e --TODO: Should this be renamed to 'widgetView' for consistency with 'widgetHold'? -- | Given a Dynamic of widget-creating actions, create a widget that is recreated whenever the Dynamic updates. -- The returned Event occurs whenever the child widget is updated, which is -- at post-build in addition to the times at which the input Dynamic is -- updated, and its value is the result of running the widget. -- Note: Often, the type @a@ is an 'Event', in which case the return value is an Event-of-Events that would typically be flattened (via 'switchHold'). dyn :: (Adjustable t m, NotReady t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a) dyn = networkView -- | Like 'dyn' but discards result. dyn_ :: (Adjustable t m, NotReady t m, PostBuild t m) => Dynamic t (m a) -> m () dyn_ = void . dyn -- | Given an initial widget and an Event of widget-creating actions, create a widget that is recreated whenever the Event fires. -- The returned Dynamic of widget results occurs when the Event does. -- Note: Often, the type 'a' is an Event, in which case the return value is a Dynamic-of-Events that would typically be flattened (via 'switchDyn'). widgetHold :: (Adjustable t m, MonadHold t m) => m a -> Event t (m a) -> m (Dynamic t a) widgetHold = networkHold -- | Like 'widgetHold' but discards result. widgetHold_ :: (Adjustable t m, MonadHold t m) => m a -> Event t (m a) -> m () widgetHold_ z = void . widgetHold z -- | Create a DOM element -- -- >>> el "div" (text "Hello World") --