{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -Wall -Werror #-} module Reflex.Dom.Colonnade ( -- * Types Cell(..) -- * Table Encoders , basic , static , capped , cappedTraversing , dynamic , dynamicCapped , expandable -- * Cell Functions , cell , charCell , stringCell , textCell , lazyTextCell , builderCell ) where import Data.String (IsString(..)) import qualified Data.Text as T import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Builder as LT import qualified Data.Map.Strict as M import qualified Data.Vector as V import Data.Foldable (Foldable(..),for_,forM_) import Data.Traversable (for) import Data.Semigroup (Semigroup(..)) import Control.Applicative (liftA2) import Reflex.Dom import Colonnade (Colonnade,Headed,Fascia,Cornice) import qualified Colonnade.Encode as E data Cell t m b = Cell { cellAttrs :: !(Dynamic t (M.Map T.Text T.Text)) , cellContents :: !(m b) } deriving (Functor) elFromCell :: (DomBuilder t m, PostBuild t m) => T.Text -> Cell t m b -> m b elFromCell e (Cell attr m) = elDynAttr e attr m -- | Convenience function for creating a 'Cell' representing -- a @td@ or @th@ with no attributes. cell :: Reflex t => m b -> Cell t m b cell = Cell (pure M.empty) charCell :: DomBuilder t m => Char -> Cell t m () charCell = textCell . T.singleton stringCell :: DomBuilder t m => String -> Cell t m () stringCell = cell . text . T.pack textCell :: DomBuilder t m => T.Text -> Cell t m () textCell = cell . text lazyTextCell :: DomBuilder t m => LT.Text -> Cell t m () lazyTextCell = textCell . LT.toStrict builderCell :: DomBuilder t m => LT.Builder -> Cell t m () builderCell = textCell . LT.toStrict . LT.toLazyText -- | This instance is requires @UndecidableInstances@ and is kind of -- bad, but @reflex@ already abusing type classes so much that it -- doesn\'t seem too terrible to add this to the mix. instance (DomBuilder t m, a ~ ()) => IsString (Cell t m a) where fromString = stringCell newtype WrappedApplicative m a = WrappedApplicative { unWrappedApplicative :: m a } deriving (Functor,Applicative,Monad) instance (Semigroup a, Applicative m) => Semigroup (WrappedApplicative m a) where (WrappedApplicative m1) <> (WrappedApplicative m2) = WrappedApplicative (liftA2 (<>) m1 m2) instance (Monoid a, Applicative m) => Monoid (WrappedApplicative m a) where mempty = WrappedApplicative (pure mempty) mappend (WrappedApplicative m1) (WrappedApplicative m2) = WrappedApplicative (liftA2 mappend m1 m2) basic :: (DomBuilder t m, PostBuild t m, Foldable f) => M.Map T.Text T.Text -- ^ @\
| @ -> f a -- ^ Values -> Colonnade Headed a (Cell t m (Event t (Maybe (m ())))) -- ^ Encoding into cells with events that can fire to create additional content under the row -> m () expandable tableAttrs tdExpandedAttrs as encoding@(E.Colonnade v) = do let vlen = V.length v elDynAttr "table" tableAttrs $ do -- Discarding this result is technically the wrong thing -- to do, but I cannot imagine why anyone would want to -- drop down content under the heading. _ <- el "thead" $ el "tr" $ E.headerMonadicGeneral_ encoding (elFromCell "th") el "tbody" $ forM_ as $ \a -> do e' <- el "tr" $ do elist <- E.rowMonadicWith [] (++) encoding (fmap (\k -> [k]) . elFromCell "td") a let e = leftmost elist e' = flip fmap e $ \mwidg -> case mwidg of Nothing -> return () Just widg -> el "tr" $ do elDynAttr "td" (M.insert "colspan" (T.pack (show vlen)) <$> tdExpandedAttrs) widg return e' widgetHold (return ()) e' |