etienne-moqueur/src/Components.hs
etienne 1068b16a67 Changed Prop's cssMap from Map String Css to IntMap String.
The cssMap now contains the rendered css together with its hash. That
way there is no need to give a component a name, and css is rendered
along the way and carried as a string.
2023-07-03 16:21:46 +02:00

62 lines
1.7 KiB
Haskell

module Components
( Prop(..)
, Component(..)
, new
, (<.>)
, addCss
, addAsset
, addAsset'
, getBody
, getCss
, getAssets
) where
import Clay ( Css )
import Core.Render ( )
import Core.Writer
import Data.Hashable ( hash )
import qualified Data.IntMap as Map
import Routes
import Utils.FileTree
-- |Props are data on top of a component's html. They are aggregated when components are combined. The choice of structures for Prop ensures that there is no duplicate at compile time (e.g. the css of a button used 5 times is only rendered once.)
data Prop = Prop
{ cssMap :: Map.IntMap String
, assetsTree :: FileTree
}
instance Semigroup Prop where
p1 <> p2 = Prop { cssMap = Map.union (cssMap p1) (cssMap p2)
, assetsTree = assetsTree p1 <> assetsTree p2
}
instance Monoid Prop where
mempty = Prop Map.empty mempty
type Component = Writer Prop
-- |Add a name and css to a Prop.
addCss :: Css -> Component ()
addCss css = tell $ Prop (Map.singleton cssHash cssString) mempty
where
cssString = show css
cssHash = hash cssString
-- |Add an asset to the Prop's tree.
addAsset :: FilePath -> Component ()
addAsset fp = tell $ Prop Map.empty (build fp)
addAsset' :: Route -> Component ()
addAsset' r = tell $ Prop Map.empty (build' r)
-- |Get a Component's body
getBody :: Component a -> a
getBody = fst . runWriter
-- |Get a Component's Css
getCss :: Component a -> String
getCss = mconcat . map snd . Map.toList . cssMap . snd . runWriter
-- |Get a Component's assets tree
getAssets :: Component a -> FileTree
getAssets = assetsTree . snd . runWriter