packages feed

web-rep-0.14.1.0: src/Web/Rep/Bootstrap.hs

{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-unused-top-binds #-}

-- | Some <https://getbootstrap.com/ bootstrap> assets and functionality.
module Web.Rep.Bootstrap
  ( bootstrapCss,
    bootstrapJs,
    bootstrapMeta,
    bootstrapPage,
    cardify,
    accordion,
    accordionChecked,
    accordionCard,
    accordionCardChecked,
    accordion_,
  )
where

import Control.Monad.State.Lazy
import Data.Bool
import Data.ByteString (ByteString)
import Data.Functor.Identity
import MarkupParse
import Web.Rep.Page
import Web.Rep.Shared

-- $setup
-- >>> :set -XOverloadedStrings
-- >>> import Web.Rep
-- >>> import MarkupParse

-- | bootstrap css link
bootstrapCss :: Markup
bootstrapCss =
  element_
    "link"
    [ Attr "rel" "stylesheet",
      Attr "href" "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css",
      Attr "integrity" "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC",
      Attr "crossorigin" "anonymous"
    ]

-- | bootstrap JS link
bootstrapJs :: Markup
bootstrapJs =
  element_
    "script"
    [ Attr "src" "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js",
      Attr "integrity" "sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM",
      Attr "crossorigin" "anonymous"
    ]
    <> element_
      "script"
      [ Attr "src" "https://code.jquery.com/jquery-3.3.1.slim.min.js",
        Attr "integrity" "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo",
        Attr "crossorigin" "anonymous"
      ]

-- | bootstrap meta element.
bootstrapMeta :: Markup
bootstrapMeta =
  element_ "meta" [Attr "charset" "utf-8"]
    <> element_
      "meta"
      [ Attr "name" "viewport",
        Attr "content" "width=device-width, initial-scale=1, shrink-to-fit=no"
      ]

-- | A page containing all the <https://getbootstrap.com/ bootstrap> needs for a web page.
bootstrapPage :: Page
bootstrapPage =
  Page
    bootstrapCss
    bootstrapJs
    mempty
    mempty
    mempty
    bootstrapMeta
    mempty

-- | wrap some Html with the bootstrap <https://getbootstrap.com/docs/4.3/components/card/ card> class
cardify :: (Markup, [Attr]) -> Maybe ByteString -> (Markup, [Attr]) -> Markup
cardify (h, hatts) t (b, batts) =
  element "div" ([Attr "class" "card"] <> hatts) $
    h
      <> element
        "div"
        ([Attr "class" "card-body"] <> batts)
        (maybe mempty (elementc "h5" [Attr "class" "card-title"]) t <> b)

-- | A Html object based on the bootstrap accordion card concept.
accordionCard :: Bool -> [Attr] -> ByteString -> ByteString -> ByteString -> ByteString -> Markup -> Markup
accordionCard collapse atts idp idh idb t0 b =
  element
    "div"
    ([Attr "class" "card"] <> atts)
    ( element
        "div"
        [Attr "class" "card-header p-0", Attr "id" idh]
        ( element
            "h2"
            [Attr "class" "m-0"]
            ( elementc
                "button"
                [ Attr "class" ("btn btn-link" <> bool "" " collapsed" collapse),
                  Attr "type" "button",
                  Attr "data-toggle" "collapse",
                  Attr "data-target" ("#" <> idb),
                  Attr "aria-expanded" (bool "true" "false" collapse),
                  Attr "aria-controls" idb
                ]
                t0
            )
            <> element
              "div"
              [ Attr "id" "idb",
                Attr "class" ("collapse" <> bool " show" "" collapse),
                Attr "aria-labelledby" idh,
                Attr "data-parent" ("#" <> idp)
              ]
              (element "div" [Attr "class" "card-body"] b)
        )
    )

-- | A bootstrap accordion card attached to a checkbox.
accordionCardChecked :: Bool -> ByteString -> ByteString -> ByteString -> ByteString -> Markup -> Markup -> Markup
accordionCardChecked collapse idp idh idb label bodyhtml checkhtml =
  element
    "div"
    [Attr "class" "card"]
    ( element
        "div"
        [ Attr "class" "card-header p-0",
          Attr "id" idh
        ]
        ( checkhtml
            <> element
              "h2"
              [Attr "class" "m-0"]
              ( elementc
                  "button"
                  [ Attr "class" ("btn btn-link" <> bool "" " collapsed" collapse),
                    Attr "type" "button",
                    Attr "data-toggle" "collapse",
                    Attr "data-target" ("#" <> idb),
                    Attr "aria-expanded" (bool "true" "false" collapse),
                    Attr "aria-controls" idb
                  ]
                  label
              )
        )
        <> element
          "div"
          [ Attr "id" "idb",
            Attr "class" ("collapse" <> bool " show" "" collapse),
            Attr "aria-labelledby" idh,
            Attr "data-parent" ("#" <> idp)
          ]
          (element "div" [Attr "class" "card-body"] bodyhtml)
    )

-- | create a bootstrapped accordian class
accordion ::
  (MonadState Int m) =>
  ByteString ->
  -- | name prefix.  This is needed because an Int doesn't seem to be a valid name.
  Maybe ByteString ->
  -- | card title
  [(ByteString, Markup)] ->
  -- | title, html tuple for each item in the accordion.
  m Markup
accordion pre x hs = do
  idp' <- genNamePre pre
  element "div" [Attr "class" "accordion m-1", Attr "id" idp'] . mconcat <$> aCards idp'
  where
    aCards par = mapM (aCard par) hs
    aCard par (t, b) = do
      idh <- genNamePre pre
      idb <- genNamePre pre
      pure $ accordionCard (x /= Just t) [] par idh idb t b

-- | create a bootstrapped accordian class
accordionChecked :: (MonadState Int m) => ByteString -> [(ByteString, Markup, Markup)] -> m Markup
accordionChecked pre hs = do
  idp' <- genNamePre pre
  element "div" [Attr "class" "accordion m-1", Attr "id" idp'] . mconcat <$> aCards idp'
  where
    aCards par = mapM (aCard par) hs
    aCard par (l, bodyhtml, checkhtml) = do
      idh <- genNamePre pre
      idb <- genNamePre pre
      pure $ accordionCardChecked True par idh idb l bodyhtml checkhtml

-- | This version of accordion runs a local state for naming, and will cause name clashes if the prefix is not unique.
accordion_ :: ByteString -> Maybe ByteString -> [(ByteString, Markup)] -> Markup
accordion_ pre x hs = runIdentity $ evalStateT (accordion pre x hs) 0