website/frontend/src/Config/Helpers/Markdown.elm

326 lines
9.6 KiB
Elm
Raw Normal View History

2024-12-16 00:12:23 -06:00
module Config.Helpers.Markdown exposing (..)
import Browser
import Config.Helpers.Format
exposing
( headerFontSizeBig
, headerFontSizeMedium
, headerFontSizeSmall
, paragraphFontSize
)
import Config.Helpers.Response exposing (pageList)
import Config.Style.Colour exposing (colourTheme)
import Config.Style.Transitions
exposing
( hoverFontDarkOrange
, hoverFontLightOrange
, transitionStyleFast
, transitionStyleMedium
)
import Element as E exposing (..)
import Element.Background as B
import Element.Border as D
import Element.Font as F
import Element.Input as Input
import Element.Region as Region
import Html exposing (Attribute, Html)
import Html.Attributes
import Markdown.Block as Block exposing (Block, Inline, ListItem(..), Task(..))
import Markdown.Html
import Markdown.Parser
import Markdown.Renderer
articleImage : String -> Element msg
articleImage pic =
el
[ centerX
, width fill
]
<|
image
[ width fill
]
{ src = "/blog/" ++ pic ++ ".png", description = "" }
renderDeviceMarkdown : String -> Element msg
renderDeviceMarkdown markdown =
case renderMarkdown markdown of
Ok renderedMarkdown ->
column
[ width fill
, centerX
, spacing 8
, paddingEach
{ top = 10
, bottom = 0
, left = 0
, right = 0
}
]
renderedMarkdown
Err error ->
E.text error
renderMarkdown : String -> Result String (List (Element msg))
renderMarkdown markdown =
markdown
|> Markdown.Parser.parse
|> Result.mapError (\error -> error |> List.map Markdown.Parser.deadEndToString |> String.join "\n")
|> Result.andThen (Markdown.Renderer.render elmUiRenderer)
elmUiRenderer : Markdown.Renderer.Renderer (Element msg)
elmUiRenderer =
{ heading = heading
, paragraph =
E.paragraph
[ E.spacing 3
, paragraphFontSize
, F.alignLeft
, width fill
]
, thematicBreak = E.none
, text = E.text
, strong =
\content ->
E.row
[ F.bold
]
content
, emphasis =
\content ->
E.row
[ F.italic
]
content
, strikethrough =
\content ->
E.row
[ F.strike
]
content
, codeSpan = code
, link =
\{ title, destination } body ->
E.newTabLink
[ width fill ]
{ url = destination
, label =
E.paragraph
[ F.color colourTheme.textLightOrange
, paragraphFontSize
, transitionStyleFast
, hoverFontDarkOrange
]
body
}
, hardLineBreak = Html.br [] [] |> E.html
, image =
\image ->
case image.title of
Just title ->
2024-12-16 01:29:31 -06:00
el [] <|
column
[ width fill
, E.centerX
]
[ E.image
[ E.centerX
, width <| px 600
]
{ src = image.src
, description = image.alt
}
]
2024-12-16 00:12:23 -06:00
Nothing ->
2024-12-16 01:29:31 -06:00
el [] <|
column
[ width fill
, E.centerX
]
[ E.image
[ E.centerX
, width <| px 600
]
{ src = image.src
, description = image.alt
}
2024-12-16 00:12:23 -06:00
]
, blockQuote =
\children ->
E.column
[ D.widthEach
{ top = 0
, right = 0
, bottom = 0
, left = 5
}
, D.rounded 10
, E.paddingEach
{ top = 13
, bottom = 10
, left = 10
, right = 10
}
, D.color colourTheme.textLightOrange
, B.color colourTheme.backgroundLightGrey
, paragraphFontSize
, width fill
]
children
, unorderedList =
\items ->
E.column
[ E.spacing 3
, paragraphFontSize
]
(items
|> List.map
(\(ListItem task children) ->
E.row
[ E.width fill
]
[ E.el
[ E.width <| E.px 20
, E.alignTop
, alignRight
]
(case task of
IncompleteTask ->
Input.defaultCheckbox False
CompletedTask ->
Input.defaultCheckbox True
NoTask ->
E.text ""
)
, E.column
[ E.width fill, F.alignLeft ]
children
]
)
)
, orderedList =
\startingIndex items ->
E.column
[ E.spacing 3
, paragraphFontSize
, E.width fill
]
(items
|> List.indexedMap
(\index itemBlocks ->
E.row
[ E.width fill
]
[ E.el
[ E.alignTop
, width <| px 25
, F.bold
, alignRight
]
(E.text (String.fromInt (index + startingIndex) ++ "."))
, E.column
[ alignLeft
, E.width fill
]
itemBlocks
]
)
)
, codeBlock = codeBlock
, html = Markdown.Html.oneOf []
, table = E.column [ width fill ]
, tableHeader = E.column [ width fill ]
, tableBody = E.column [ width fill ]
, tableRow = E.row [ width fill ]
, tableHeaderCell =
\maybeAlignment children ->
E.paragraph [ width fill ] children
, tableCell =
\maybeAlignment children ->
E.paragraph [ width fill ] children
}
code : String -> Element msg
code snippet =
E.el
[ B.color colourTheme.backgroundLightGrey
, D.rounded 2
, E.paddingXY 5 3
, width fill
, F.family
[ F.external
{ url = "https://fonts.googleapis.com/css?family=Source+Code+Pro"
, name = "Source Code Pro"
}
]
]
(E.text snippet)
codeBlock : { body : String, language : Maybe String } -> Element msg
codeBlock details =
E.el
[ B.color colourTheme.backgroundLightGrey
, E.htmlAttribute (Html.Attributes.style "white-space" "pre")
, width fill
, E.paddingEach
{ top = 23
, bottom = 20
, left = 20
, right = 20
}
, paragraphFontSize
, F.family
[ F.external
{ url = "https://fonts.googleapis.com/css?family=Source+Code+Pro"
, name = "Source Code Pro"
}
]
]
(E.text details.body)
heading : { level : Block.HeadingLevel, rawText : String, children : List (Element msg) } -> Element msg
heading { level, rawText, children } =
E.paragraph
[ F.size
(case level of
Block.H1 ->
headerFontSizeBig
Block.H2 ->
headerFontSizeMedium
_ ->
headerFontSizeSmall
)
, F.bold
, F.center
, width fill
, F.color colourTheme.textLightOrange
, Region.heading (Block.headingLevelToInt level)
, E.htmlAttribute
(Html.Attributes.attribute "name" (rawTextToId rawText))
, E.htmlAttribute
(Html.Attributes.id (rawTextToId rawText))
]
children
rawTextToId rawText =
rawText
|> String.split " "
|> Debug.log "split"
|> String.join "-"
|> Debug.log "joined"
|> String.toLower