{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeApplications #-}

-- | Use Tailwind CSS with blaze-html? Try this module for rapid prototyping of
-- websites in Ema.
module Ema.Helper.Tailwind
  ( -- * Main functions
    layout,
    layoutWith,

    -- * Tailwind shims
    twindShim,
    twindShimCdn,
    twindShimOfficial,
    twindShimUnofficial,
  )
where

import qualified Ema.CLI
import NeatInterpolation (text)
import qualified Text.Blaze.Html.Renderer.Utf8 as RU
import Text.Blaze.Html5 ((!))
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A

-- | A simple and off-the-shelf layout using Tailwind CSS
layout :: Ema.CLI.Action -> H.Html -> H.Html -> LByteString
layout :: Action -> Html -> Html -> LByteString
layout Action
action =
  AttributeValue
-> AttributeValue -> Html -> Html -> Html -> LByteString
layoutWith AttributeValue
"en" AttributeValue
"UTF-8" (Html -> Html -> Html -> LByteString)
-> Html -> Html -> Html -> LByteString
forall a b. (a -> b) -> a -> b
$ Action -> Html
twindShim Action
action

twindShim :: Ema.CLI.Action -> H.Html
twindShim :: Action -> Html
twindShim Action
action =
  case Action
action of
    Ema.CLI.Generate FilePath
_ ->
      Html
twindShimUnofficial
    Action
_ ->
      -- Twind shim doesn't reliably work in dev server mode. Let's just use the
      -- tailwind CDN.
      Html
twindShimCdn

-- | Like @layout@, but pick your own language, encoding and tailwind shim.
layoutWith :: H.AttributeValue -> H.AttributeValue -> H.Html -> H.Html -> H.Html -> LByteString
layoutWith :: AttributeValue
-> AttributeValue -> Html -> Html -> Html -> LByteString
layoutWith AttributeValue
lang AttributeValue
encoding Html
tshim Html
appHead Html
appBody = Html -> LByteString
RU.renderHtml (Html -> LByteString) -> Html -> LByteString
forall a b. (a -> b) -> a -> b
$ do
  Html
H.docType
  Html -> Html
H.html (Html -> Html) -> Attribute -> Html -> Html
forall h. Attributable h => h -> Attribute -> h
! AttributeValue -> Attribute
A.lang AttributeValue
lang (Html -> Html) -> Html -> Html
forall a b. (a -> b) -> a -> b
$ do
    Html -> Html
H.head (Html -> Html) -> Html -> Html
forall a b. (a -> b) -> a -> b
$ do
      Html
H.meta Html -> Attribute -> Html
forall h. Attributable h => h -> Attribute -> h
! AttributeValue -> Attribute
A.charset AttributeValue
encoding
      -- This makes the site mobile friendly by default.
      Html
H.meta Html -> Attribute -> Html
forall h. Attributable h => h -> Attribute -> h
! AttributeValue -> Attribute
A.name AttributeValue
"viewport" Html -> Attribute -> Html
forall h. Attributable h => h -> Attribute -> h
! AttributeValue -> Attribute
A.content AttributeValue
"width=device-width, initial-scale=1"
      Html
tshim
      Html
appHead
    -- The "overflow-y-scroll" makes the scrollbar visible always, so as to
    -- avoid janky shifts when switching to routes with suddenly scrollable content.
    Html -> Html
H.body (Html -> Html) -> Attribute -> Html -> Html
forall h. Attributable h => h -> Attribute -> h
! AttributeValue -> Attribute
A.class_ AttributeValue
"overflow-y-scroll" (Html -> Html) -> Html -> Html
forall a b. (a -> b) -> a -> b
$ do
      Html
appBody

-- | Loads full tailwind CSS from CDN (not good for production)
twindShimCdn :: H.Html
twindShimCdn :: Html
twindShimCdn =
  Html
H.link
    Html -> Attribute -> Html
forall h. Attributable h => h -> Attribute -> h
! AttributeValue -> Attribute
A.href AttributeValue
"https://unpkg.com/tailwindcss@latest/dist/tailwind.min.css"
    Html -> Attribute -> Html
forall h. Attributable h => h -> Attribute -> h
! AttributeValue -> Attribute
A.rel AttributeValue
"stylesheet"
    Html -> Attribute -> Html
forall h. Attributable h => h -> Attribute -> h
! AttributeValue -> Attribute
A.type_ AttributeValue
"text/css"

-- | This shim may not work with hot reload.
twindShimOfficial :: H.Html
twindShimOfficial :: Html
twindShimOfficial =
  ByteString -> Html
H.unsafeByteString (ByteString -> Html) -> (Text -> ByteString) -> Text -> Html
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 (Text -> Html) -> Text -> Html
forall a b. (a -> b) -> a -> b
$
    [text|
    <script type="module" src="https://cdn.skypack.dev/twind/shim"></script>
    |]

-- | This shim does work with hot reload, but it spams console with warnings.
twindShimUnofficial :: H.Html
twindShimUnofficial :: Html
twindShimUnofficial = do
  Html -> Html
H.script
    (Html -> Html) -> Attribute -> Html -> Html
forall h. Attributable h => h -> Attribute -> h
! AttributeValue -> Attribute
A.type_ AttributeValue
"text/javascript"
    (Html -> Html) -> Attribute -> Html -> Html
forall h. Attributable h => h -> Attribute -> h
! AttributeValue -> Attribute
A.src AttributeValue
"https://cdn.jsdelivr.net/combine/npm/twind/twind.umd.min.js,npm/twind/observe/observe.umd.min.js"
    (Html -> Html) -> Html -> Html
forall a b. (a -> b) -> a -> b
$ Html
""
  Html -> Html
H.script (Html -> Html) -> Attribute -> Html -> Html
forall h. Attributable h => h -> Attribute -> h
! AttributeValue -> Attribute
A.type_ AttributeValue
"text/javascript" (Html -> Html) -> Html -> Html
forall a b. (a -> b) -> a -> b
$ Html
twindShimUnofficialEval
  where
    twindShimUnofficialEval :: H.Html
    twindShimUnofficialEval :: Html
twindShimUnofficialEval =
      ByteString -> Html
H.unsafeByteString (ByteString -> Html) -> (Text -> ByteString) -> Text -> Html
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 (Text -> Html) -> Text -> Html
forall a b. (a -> b) -> a -> b
$
        [text|
        // Be silent to avoid complaining about non-tailwind classes
        // https://github.com/tw-in-js/twind/discussions/180#discussioncomment-678272
        console.log("ema: Twind: setup & observe")
        twind.setup({mode: 'silent'})
        window.emaTwindObs = twindObserve.observe(document.documentElement);
        |]