packages feed

hyperbole-0.5.0: src/Web/Hyperbole/View/Types.hs

{-# LANGUAGE AllowAmbiguousTypes #-}

module Web.Hyperbole.View.Types where

import Data.Kind (Type)
import Data.String (IsString (..))
import Data.Text (Text, pack)
import Effectful
import Effectful.Reader.Static
import Web.Atomic.Html (Html (..))
import Web.Atomic.Html qualified as Atomic
import Web.Atomic.Types


-- View ------------------------------------------------------------

{- | 'View's are HTML fragments with a 'context'

@
#EMBED Example/Docs/BasicPage.hs helloWorld
@
-}
newtype View c a = View {html :: Eff '[Reader c] (Html a)}


instance IsString (View c ()) where
  fromString s = View $ pure $ Atomic.text (pack s)


runView :: forall c a. c -> View c a -> Html a
runView c (View eff) = do
  runPureEff $ runReader c eff


instance Functor (View c) where
  fmap f (View eff) = View $ do
    html <- eff
    pure $ fmap f html
instance Applicative (View ctx) where
  pure a = View $ pure $ pure a
  liftA2 :: (a -> b -> c) -> View ctx a -> View ctx b -> View ctx c
  liftA2 abc (View va) (View vb) = View $ do
    ha <- va
    hb <- vb
    pure $ liftA2 abc ha hb
  View va *> View vb = View $ do
    ha <- va
    hb <- vb
    pure $ ha *> hb
instance Monad (View ctx) where
  (>>) = (*>)
  (>>=) :: forall a b. View ctx a -> (a -> View ctx b) -> View ctx b
  -- TEST: appending Empty
  View ea >>= famb = View $ do
    a :: a <- (.value) <$> ea
    let View eb :: View ctx b = famb a
    hb <- eb
    pure $ hb


-- Context -----------------------------------------

type family ViewContext (v :: Type) where
  ViewContext (View c x) = c
  ViewContext (View c x -> View c x) = c


-- TEST: appending Empty
context :: forall c. View c c
context = View $ do
  c <- ask @c
  pure $ pure c


addContext :: ctx -> View ctx () -> View c ()
addContext c (View eff) = View $ do
  pure $ runPureEff $ runReader c eff


modifyContext
  :: forall ctx0 ctx1. (ctx0 -> ctx1) -> View ctx1 () -> View ctx0 ()
modifyContext f (View eff) = View $ do
  ctx0 <- ask @ctx0
  pure $ runPureEff $ runReader (f ctx0) eff


-- Html ---------------------------------------------

tag :: Text -> View c () -> View c ()
tag = tag' False


tag' :: Bool -> Text -> View c () -> View c ()
tag' inline n (View eff) = View $ do
  content <- eff
  pure $ Atomic.tag' inline n content


text :: Text -> View c ()
text t = View $ pure $ Atomic.text t


none :: View c ()
none = View $ pure Atomic.none


raw :: Text -> View c ()
raw t = View $ pure $ Atomic.raw t


-- Attributes -----------------------------------------

instance Attributable (View c a) where
  modAttributes f (View eff) = View $ do
    h <- eff
    pure $ modAttributes f h


instance Styleable (View c a) where
  modCSS f (View eff) = View $ do
    h <- eff
    pure $ modCSS f h