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

347 lines
10 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
]
<|
2024-12-16 01:57:02 -06:00
column
[ E.paddingEach
{ top = 20
, bottom = 20
, left = 20
, right = 20
}
]
[ image
[ width fill
]
{ src = "/blog/" ++ pic ++ ".png", description = "" }
2024-12-16 00:12:23 -06:00
]
renderDeviceMarkdown : String -> Element msg
renderDeviceMarkdown markdown =
case renderMarkdown markdown of
Ok renderedMarkdown ->
column
[ width fill
, centerX
2024-12-16 01:57:02 -06:00
, spacing 10
2024-12-16 00:12:23 -06:00
]
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 04:19:13 -06:00
el
[ width fill ]
<|
2024-12-16 01:29:31 -06:00
column
2024-12-16 04:19:13 -06:00
[ E.centerX
, E.centerY
, width fill
, E.paddingEach
{ top = 0
, bottom = 10
, left = 10
, right = 10
}
2024-12-16 01:29:31 -06:00
]
[ E.image
[ E.centerX
2024-12-16 04:19:13 -06:00
, width (fill |> maximum 600)
2024-12-16 01:29:31 -06:00
]
{ src = image.src
, description = image.alt
}
]
2024-12-16 00:12:23 -06:00
Nothing ->
2024-12-16 01:57:02 -06:00
el
[ width fill ]
<|
2024-12-16 01:29:31 -06:00
column
2024-12-16 01:57:02 -06:00
[ E.centerX
, E.centerY
, width fill
, E.paddingEach
2024-12-16 04:19:13 -06:00
{ top = 3
2024-12-16 01:57:02 -06:00
, bottom = 10
, left = 10
, right = 10
}
2024-12-16 01:29:31 -06:00
]
[ E.image
[ E.centerX
2024-12-16 01:57:02 -06:00
, width (fill |> maximum 600)
2024-12-16 01:29:31 -06:00
]
{ 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