{-# 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
(
text
, dynText
, display
, button
, dyn
, dyn_
, widgetHold
, widgetHold_
, el
, elAttr
, elClass
, elDynAttr
, elDynClass
, elDynAttrNS
, el'
, elAttr'
, elClass'
, elDynAttr'
, elDynClass'
, elDynAttrNS'
, dynamicAttributesToModifyAttributes
, dynamicAttributesToModifyAttributesWithInitial
, Link (..)
, linkClass
, link
, divClass
, dtdd
, blank
, tableDynAttr
, tabDisplay
, HasAttributes (..)
, module Data.Map.Misc
, module Reflex.Collection
, module Reflex.Workflow
, partitionMapBySetLT
) where
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_)
{-# 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
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
dyn :: (DomBuilder t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
dyn = networkView
dyn_ :: (DomBuilder t m, PostBuild t m) => Dynamic t (m a) -> m ()
dyn_ = void . dyn
widgetHold :: (DomBuilder t m, MonadHold t m) => m a -> Event t (m a) -> m (Dynamic t a)
widgetHold = networkHold
widgetHold_ :: (DomBuilder t m, MonadHold t m) => m a -> Event t (m a) -> m ()
widgetHold_ z = void . widgetHold z
{-# INLINABLE el #-}
el :: forall t m a. DomBuilder t m => Text -> m a -> m a
el elementTag child = snd <$> el' elementTag child
{-# INLINABLE elAttr #-}
elAttr :: forall t m a. DomBuilder t m => Text -> Map Text Text -> m a -> m a
elAttr elementTag attrs child = snd <$> elAttr' elementTag attrs child
{-# INLINABLE elClass #-}
elClass :: forall t m a. DomBuilder t m => Text -> Text -> m a -> m a
elClass elementTag c child = snd <$> elClass' elementTag c child
{-# INLINABLE elDynAttr #-}
elDynAttr :: forall t m a. (DomBuilder t m, PostBuild t m) => Text -> Dynamic t (Map Text Text) -> m a -> m a
elDynAttr elementTag attrs child = snd <$> elDynAttr' elementTag attrs child
{-# INLINABLE elDynClass #-}
elDynClass :: forall t m a. (DomBuilder t m, PostBuild t m) => Text -> Dynamic t Text -> m a -> m a
elDynClass elementTag c child = snd <$> elDynClass' elementTag c child
{-# INLINABLE el' #-}
el' :: forall t m a. DomBuilder t m => Text -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
el' elementTag = element elementTag def
{-# INLINABLE elAttr' #-}
elAttr' :: forall t m a. DomBuilder t m => Text -> Map Text Text -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
elAttr' elementTag attrs = element elementTag $ def
& initialAttributes .~ Map.mapKeys (AttributeName Nothing) attrs
{-# INLINABLE elClass' #-}
elClass' :: forall t m a. DomBuilder t m => Text -> Text -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
elClass' elementTag c = elAttr' elementTag ("class" =: c)
{-# INLINABLE elDynAttr' #-}
elDynAttr' :: forall t m a. (DomBuilder t m, PostBuild t m) => Text -> Dynamic t (Map Text Text) -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
elDynAttr' = elDynAttrNS' Nothing
{-# INLINABLE elDynClass' #-}
elDynClass' :: forall t m a. (DomBuilder t m, PostBuild t m) => Text -> Dynamic t Text -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
elDynClass' elementTag c = elDynAttr' elementTag (fmap ("class" =:) c)
{-# INLINABLE elDynAttrNS' #-}
elDynAttrNS' :: forall t m a. (DomBuilder t m, PostBuild t m) => Maybe Text -> Text -> Dynamic t (Map Text Text) -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
elDynAttrNS' mns elementTag attrs child = do
modifyAttrs <- dynamicAttributesToModifyAttributes attrs
let cfg = def
& elementConfig_namespace .~ mns
& modifyAttributes .~ fmapCheap mapKeysToAttributeName modifyAttrs
result <- element elementTag cfg child
postBuild <- getPostBuild
notReadyUntil postBuild
return result
{-# INLINABLE elDynAttrNS #-}
elDynAttrNS :: forall t m a. (DomBuilder t m, PostBuild t m) => Maybe Text -> Text -> Dynamic t (Map Text Text) -> m a -> m a
elDynAttrNS mns elementTag attrs child = fmap snd $ elDynAttrNS' mns elementTag attrs child
dynamicAttributesToModifyAttributes :: (Ord k, PostBuild t m) => Dynamic t (Map k Text) -> m (Event t (Map k (Maybe Text)))
dynamicAttributesToModifyAttributes = dynamicAttributesToModifyAttributesWithInitial mempty
dynamicAttributesToModifyAttributesWithInitial :: (Ord k, PostBuild t m) => Map k Text -> Dynamic t (Map k Text) -> m (Event t (Map k (Maybe Text)))
dynamicAttributesToModifyAttributesWithInitial attrs0 d = do
postBuild <- getPostBuild
let modificationsNeeded = flip push (align postBuild $ updated d) $ \x -> do
p <- case x of
This () -> do
new <- sample $ current d
return $ diffMap attrs0 new
These () new -> return $ diffMap attrs0 new
That new -> do
old <- sample $ current d
return $ diffMap old new
return $ if Map.null p then Nothing else Just p
return modificationsNeeded
newtype Link t
= Link { _link_clicked :: Event t ()
}
linkClass :: DomBuilder t m => Text -> Text -> m (Link t)
linkClass s c = do
(l,_) <- elAttr' "a" ("class" =: c) $ text s
return $ Link $ domEvent Click l
link :: DomBuilder t m => Text -> m (Link t)
link s = linkClass s ""
divClass :: forall t m a. DomBuilder t m => Text -> m a -> m a
divClass = elClass "div"
dtdd :: forall t m a. DomBuilder t m => Text -> m a -> m a
dtdd h w = do
el "dt" $ text h
el "dd" w
blank :: forall m. Monad m => m ()
blank = return ()
tableDynAttr :: forall t m r k v. (Ord k, DomBuilder t m, MonadHold t m, PostBuild t m, MonadFix m)
=> Text
-> [(Text, k -> Dynamic t r -> m v)]
-> Dynamic t (Map k r)
-> (k -> m (Dynamic t (Map Text Text)))
-> m (Dynamic t (Map k (Element EventResult (DomBuilderSpace m) t, [v])))
tableDynAttr klass cols dRows rowAttrs = elAttr "div" (Map.singleton "style" "zoom: 1; overflow: auto; background: white;") $
elAttr "table" (Map.singleton "class" klass) $ do
el "thead" $ el "tr" $
mapM_ (\(h, _) -> el "th" $ text h) cols
el "tbody" $
listWithKey dRows (\k r -> do
dAttrs <- rowAttrs k
elDynAttr' "tr" dAttrs $ mapM (\x -> el "td" $ snd x k r) cols)
tabDisplay :: forall t m k. (MonadFix m, DomBuilder t m, MonadHold t m, PostBuild t m, Ord k)
=> Text
-> Text
-> Map k (Text, m ())
-> m ()
tabDisplay ulClass activeClass tabItems = do
let t0 = listToMaybe $ Map.keys tabItems
rec currentTab :: Demux t (Maybe k) <- elAttr "ul" ("class" =: ulClass) $ do
tabClicksList :: [Event t k] <- Map.elems <$> imapM (\k (s,_) -> headerBarLink s k $ demuxed currentTab (Just k)) tabItems
let eTabClicks :: Event t k = leftmost tabClicksList
fmap demux $ holdDyn t0 $ fmap Just eTabClicks
el "div" $ do
iforM_ tabItems $ \k (_, w) -> do
let isSelected = demuxed currentTab $ Just k
attrs = ffor isSelected $ \s -> if s then Map.empty else Map.singleton "style" "display:none;"
elDynAttr "div" attrs w
return ()
where
headerBarLink :: Text -> k -> Dynamic t Bool -> m (Event t k)
headerBarLink x k isSelected = do
let attrs = fmap (\b -> if b then Map.singleton "class" activeClass else Map.empty) isSelected
elDynAttr "li" attrs $ do
a <- link x
return $ fmap (const k) (_link_clicked a)
class HasAttributes a where
type Attrs a :: *
attributes :: Lens' a (Attrs a)