website/src/TailwindMarkdownRenderer.elm

321 lines
10 KiB
Elm

module TailwindMarkdownRenderer exposing (renderer)
import Css
import Ellie
import Html.Styled as Html
import Html.Styled.Attributes as Attr exposing (css, class)
import Markdown.Block as Block
import Markdown.Html
import Markdown.Renderer
import Oembed
import SyntaxHighlight
import Tailwind.Theme as Theme
import Tailwind.Utilities as Tw
renderer : Markdown.Renderer.Renderer (Html.Html msg)
renderer =
{ heading = heading
, paragraph = Html.p []
, thematicBreak = Html.hr [] []
, text = Html.text
, strong = \content -> Html.strong [ css [ Tw.font_bold ] ] content
, emphasis = \content -> Html.em [ css [ Tw.italic ] ] content
, blockQuote = Html.blockquote []
, codeSpan =
\content ->
Html.code
[ css
[ Tw.font_semibold
, Tw.font_medium
, Css.color (Css.rgb 226 0 124) |> Css.important
]
]
[ Html.text content ]
--, codeSpan = code
, link =
\{ destination } body ->
Html.a
[ Attr.href destination
, css
[ Tw.underline
]
]
body
, hardLineBreak = Html.br [] []
, image =
\image ->
case image.title of
Just _ ->
Html.img [ Attr.src image.src, Attr.alt image.alt ] []
Nothing ->
Html.img [ Attr.src image.src, Attr.alt image.alt ] []
, unorderedList =
\items ->
Html.ul []
(items
|> List.map
(\item ->
case item of
Block.ListItem task children ->
let
checkbox =
case task of
Block.NoTask ->
Html.text ""
Block.IncompleteTask ->
Html.input
[ Attr.disabled True
, Attr.checked False
, Attr.type_ "checkbox"
]
[]
Block.CompletedTask ->
Html.input
[ Attr.disabled True
, Attr.checked True
, Attr.type_ "checkbox"
]
[]
in
Html.li [] (checkbox :: children)
)
)
, orderedList =
\startingIndex items ->
Html.ol
(case startingIndex of
1 ->
[ Attr.start startingIndex ]
_ ->
[]
)
(items
|> List.map
(\itemBlocks ->
Html.li []
itemBlocks
)
)
, html =
Markdown.Html.oneOf
[ Markdown.Html.tag "oembed"
(\url _ ->
Oembed.view [] Nothing url
|> Maybe.map Html.fromUnstyled
|> Maybe.withDefault (Html.div [] [])
)
|> Markdown.Html.withAttribute "url"
, Markdown.Html.tag "ellie-output"
(\ellieId _ ->
Ellie.outputTabElmCss ellieId
)
|> Markdown.Html.withAttribute "id"
, Markdown.Html.tag "cite"
(\ref _ ->
Html.span []
[ Html.label [ class "sidenote-number" ] []
, Html.span [ class "sidenote" ] [ Html.text ref ]
]
)
|> Markdown.Html.withAttribute "ref"
]
, codeBlock = codeBlock
--\{ body, language } ->
-- let
-- classes =
-- -- Only the first word is used in the class
-- case Maybe.map String.words language of
-- Just (actualLanguage :: _) ->
-- [ Attr.class <| "language-" ++ actualLanguage ]
--
-- _ ->
-- []
-- in
-- Html.pre []
-- [ Html.code classes
-- [ Html.text body
-- ]
-- ]
, table =
Html.table
[ {-
table-layout: auto;
text-align: left;
width: 100%;
margin-top: 2em;
margin-bottom: 2em;
-}
css
[--Tw.table_auto
--, Tw.w_full
--, Tw.mt_4
--, Tw.mb_4
]
]
, tableHeader = Html.thead []
, tableBody = Html.tbody []
, tableRow = Html.tr []
, strikethrough =
\children -> Html.del [] children
, tableHeaderCell =
\maybeAlignment ->
let
attrs =
maybeAlignment
|> Maybe.map
(\alignment ->
case alignment of
Block.AlignLeft ->
"left"
Block.AlignCenter ->
"center"
Block.AlignRight ->
"right"
)
|> Maybe.map Attr.align
|> Maybe.map List.singleton
|> Maybe.withDefault []
in
Html.th attrs
, tableCell =
\maybeAlignment ->
let
attrs =
maybeAlignment
|> Maybe.map
(\alignment ->
case alignment of
Block.AlignLeft ->
"left"
Block.AlignCenter ->
"center"
Block.AlignRight ->
"right"
)
|> Maybe.map Attr.align
|> Maybe.map List.singleton
|> Maybe.withDefault []
in
Html.td attrs
}
rawTextToId : String -> String
rawTextToId rawText =
rawText
|> String.split " "
|> String.join "-"
|> String.toLower
heading : { level : Block.HeadingLevel, rawText : String, children : List (Html.Html msg) } -> Html.Html msg
heading { level, rawText, children } =
case level of
Block.H1 ->
Html.h1
[ css
[ Tw.text_4xl
, Tw.font_bold
, Tw.tracking_tight
, Tw.mt_2
, Tw.mb_4
]
]
children
Block.H2 ->
Html.h2
[ Attr.id (rawTextToId rawText)
, Attr.attribute "name" (rawTextToId rawText)
, css
[ Tw.text_3xl
, Tw.font_semibold
, Tw.tracking_tight
, Tw.mt_10
, Tw.pb_1
, Tw.border_b
]
]
[ Html.a
[ Attr.href <| "#" ++ rawTextToId rawText
, css
[ Tw.no_underline |> Css.important
]
]
(children
++ [ Html.span
[ Attr.class "anchor-icon"
, css
[ Tw.ml_2
, Tw.text_color Theme.gray_500
, Tw.select_none
]
]
[ Html.text "#" ]
]
)
]
_ ->
(case level of
Block.H1 ->
Html.h1
Block.H2 ->
Html.h2
Block.H3 ->
Html.h3
Block.H4 ->
Html.h4
Block.H5 ->
Html.h5
Block.H6 ->
Html.h6
)
[ css
[ Tw.font_bold
, Tw.text_lg
, Tw.mt_8
, Tw.mb_4
]
]
children
--code : String -> Element msg
--code snippet =
-- Element.el
-- [ Element.Background.color
-- (Element.rgba255 50 50 50 0.07)
-- , Element.Border.rounded 2
-- , Element.paddingXY 5 3
-- , Font.family [ Font.typeface "Roboto Mono", Font.monospace ]
-- ]
-- (Element.text snippet)
--
--
codeBlock : { body : String, language : Maybe String } -> Html.Html msg
codeBlock details =
SyntaxHighlight.elm details.body
|> Result.map (SyntaxHighlight.toBlockHtml (Just 1))
|> Result.map Html.fromUnstyled
|> Result.withDefault (Html.pre [] [ Html.code [] [ Html.text details.body ] ])