mirror of
https://gitlab.com/upRootNutrition/website.git
synced 2025-06-16 12:25:12 -05:00
feat: added mackdown parser
This commit is contained in:
parent
ce159368e6
commit
8f7cd96f63
20 changed files with 1445 additions and 43 deletions
313
frontend/src/Config/Helpers/Markdown.elm
Normal file
313
frontend/src/Config/Helpers/Markdown.elm
Normal file
|
@ -0,0 +1,313 @@
|
|||
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 ->
|
||||
el [ centerX ] <|
|
||||
E.image
|
||||
[ E.width <| E.px 300 ]
|
||||
{ src = image.src
|
||||
, description = image.alt
|
||||
}
|
||||
|
||||
Nothing ->
|
||||
el [ centerX ] <|
|
||||
E.image
|
||||
[ E.width <| E.px 300
|
||||
]
|
||||
{ src = image.src
|
||||
, description = image.alt
|
||||
}
|
||||
, 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
|
Loading…
Add table
Add a link
Reference in a new issue