packages feed

web-view-0.2.0: src/Web/View/Element.hs

{-# LANGUAGE DataKinds #-}

module Web.View.Element where

import Control.Monad (forM_)
import Data.Function ((&))
import Data.Text (Text)
import Effectful
import Effectful.Writer.Static.Local
import Web.View.Style
import Web.View.Types
import Web.View.View


{- | A basic element

> el (bold . pad 10) "Hello"
-}
el :: Mod -> View c () -> View c ()
el = tag "div"


{- | A basic element, with no modifiers

> el_ "Hello"
-}
el_ :: View c () -> View c ()
el_ = tag "div" id


{- | Add text to a view. Not required for string literals

> el_ $ do
>   "Hello: "
>   text user.name
-}
text :: Text -> View c ()
text t = viewAddContent $ Text t


{- | Embed static, unescaped HTML or SVG. Take care not to use 'raw' with user-generated content.

> spinner = raw "<svg>...</svg>"
-}
raw :: Text -> View c ()
raw t = viewAddContent $ Raw t


{- | Do not show any content

> if isVisible
>  then content
>  else none
-}
none :: View c ()
none = pure ()


pre :: Mod -> Text -> View c ()
pre f t = tag "pre" f (text t)


-- * Inputs


form :: Mod -> View c () -> View c ()
form f = tag "form" (f . flexCol)


input :: Mod -> View c ()
input m = tag "input" (m . att "type" "text") none


name :: Text -> Mod
name = att "name"


value :: Text -> Mod
value = att "value"


label :: Mod -> View c () -> View c ()
label = tag "label"


button :: Mod -> View c () -> View c ()
button = tag "button"


-- * Document Metadata


script :: Text -> View c ()
script src = tag "script" (att "type" "text/javascript" . att "src" src) none


style :: Text -> View c ()
style cnt = tag "style" (att "type" "text/css") (text $ "\n" <> cnt <> "\n")


stylesheet :: Text -> View c ()
stylesheet href = tag "link" (att "rel" "stylesheet" . att "href" href) none


-- * Tables


{- | Create a type safe data table by specifying columns

> usersTable :: [User] -> View c ()
> usersTable us = do
>   table id us $ do
>     tcol (th hd "Name") $ \u -> td cell $ text u.name
>     tcol (th hd "Email") $ \u -> td cell $ text u.email
>  where
>   hd = cell . bold
>   cell = pad 4 . border 1
-}
table :: Mod -> [dt] -> Eff '[Writer [TableColumn c dt]] () -> View c ()
table f dts wcs = do
  c <- context
  let cols = runPureEff . execWriter $ wcs
  tag "table" borderCollapse $ do
    tag "thead" id $ do
      tag "tr" f $ do
        forM_ cols $ \tc -> do
          addContext (TableHead c) tc.headCell
    tag "tbody" id $ do
      forM_ dts $ \dt -> do
        tag "tr" f $ do
          forM_ cols $ \tc -> do
            addContext dt $ tc.dataCell dt
 where
  borderCollapse :: Mod
  borderCollapse = addClass $ cls "brd-cl" & prop @Text "border-collapse" "collapse"


tcol :: forall dt c. View (TableHead c) () -> (dt -> View dt ()) -> Eff '[Writer [TableColumn c dt]] ()
tcol hd view = do
  tell ([TableColumn hd view] :: [TableColumn c dt])


th :: Mod -> View c () -> View (TableHead c) ()
th f cnt = do
  TableHead c <- context
  addContext c $ tag "th" f cnt


td :: Mod -> View () () -> View dt ()
td f c = addContext () $ tag "td" f c


newtype TableHead a = TableHead a


data TableColumn c dt = TableColumn
  { headCell :: View (TableHead c) ()
  , dataCell :: dt -> View dt ()
  }