Add prev and next navigation
This commit is contained in:
parent
ae7126bc19
commit
1b222217a1
|
@ -0,0 +1,62 @@
|
||||||
|
module Functions.Date
|
||||||
|
( Date
|
||||||
|
, extractDate
|
||||||
|
, formatDate
|
||||||
|
, formatDateShort
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Types (Token)
|
||||||
|
import DependencyGenerator (DepGenM, TokenableTo(..), onToken)
|
||||||
|
|
||||||
|
import Text.Printf (printf)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Data.List.NonEmpty (NonEmpty(..))
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
import Data.Text (Text)
|
||||||
|
|
||||||
|
data Date = Date Int Int Int
|
||||||
|
deriving (Eq, Ord, Show)
|
||||||
|
|
||||||
|
split :: Eq a => a -> [a] -> NonEmpty [a]
|
||||||
|
split sep = \case
|
||||||
|
[] ->
|
||||||
|
NE.singleton []
|
||||||
|
(c : cs) ->
|
||||||
|
(if sep == c
|
||||||
|
then NE.cons []
|
||||||
|
else \(h :| t) -> (c : h) :| t)
|
||||||
|
$ split sep cs
|
||||||
|
|
||||||
|
extractDate :: TokenableTo String a => a -> DepGenM (Token Date)
|
||||||
|
extractDate = onToken $ \dirName -> case split '-' dirName of
|
||||||
|
year :| (month : day : _) ->
|
||||||
|
Date (read year) (read month) (read day)
|
||||||
|
_ ->
|
||||||
|
error "unexpected"
|
||||||
|
|
||||||
|
formatDate :: Date -> Text
|
||||||
|
formatDate (Date year month day) =
|
||||||
|
T.concat [ "den "
|
||||||
|
, T.pack (show day)
|
||||||
|
, ". "
|
||||||
|
, months !! (month - 1)
|
||||||
|
, " "
|
||||||
|
, T.pack (show year)
|
||||||
|
]
|
||||||
|
where months = [ "januar"
|
||||||
|
, "februar"
|
||||||
|
, "marts"
|
||||||
|
, "april"
|
||||||
|
, "maj"
|
||||||
|
, "juni"
|
||||||
|
, "juli"
|
||||||
|
, "august"
|
||||||
|
, "september"
|
||||||
|
, "oktober"
|
||||||
|
, "november"
|
||||||
|
, "december"
|
||||||
|
]
|
||||||
|
|
||||||
|
formatDateShort :: Date -> Text
|
||||||
|
formatDateShort (Date year month day) =
|
||||||
|
T.concat [ T.pack (show year), "-", T.pack (printf "%02d" month), "-", T.pack (printf "%02d" day) ]
|
|
@ -38,5 +38,5 @@ extractTitle = onToken $ \(PD.Pandoc _ blocks) -> case blocks of
|
||||||
|
|
||||||
injectAfterTitle :: (TokenableTo Text a, TokenableTo Pandoc b) => a -> b -> DepGenM (Token Pandoc)
|
injectAfterTitle :: (TokenableTo Text a, TokenableTo Pandoc b) => a -> b -> DepGenM (Token Pandoc)
|
||||||
injectAfterTitle = onTupleToken $ \extra (PD.Pandoc meta blocks) -> case blocks of
|
injectAfterTitle = onTupleToken $ \extra (PD.Pandoc meta blocks) -> case blocks of
|
||||||
(header@(PD.Header _ _ _) : rest) -> PD.Pandoc meta (header : PD.Para [PD.Emph [PD.Str extra]] : rest)
|
(header@(PD.Header _ _ _) : rest) -> PD.Pandoc meta (header : PD.RawBlock "html" extra : rest)
|
||||||
_ -> error "unexpected"
|
_ -> error "unexpected"
|
||||||
|
|
|
@ -4,17 +4,22 @@ import Types (Token(..))
|
||||||
import DependencyGenerator
|
import DependencyGenerator
|
||||||
import Functions
|
import Functions
|
||||||
|
|
||||||
import Data.Ord (comparing)
|
import Data.List (sort, elemIndex)
|
||||||
import Data.List (sortBy)
|
|
||||||
import Data.Function ((&))
|
import Data.Function ((&))
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Control.Monad (forM_, (<=<))
|
import Control.Monad (forM_, (<=<))
|
||||||
|
|
||||||
|
elemIndex' :: (Show a, Eq a) => a -> [a] -> Int
|
||||||
|
elemIndex' x xs = case elemIndex x xs of
|
||||||
|
Just i -> i
|
||||||
|
Nothing -> error ("unexpected unknown directory index for " ++ show x ++ " in " ++ show xs)
|
||||||
|
|
||||||
handleRecipeDir :: Token FilePath -> Token FilePath
|
handleRecipeDir :: Token FilePath -> Token FilePath
|
||||||
-> Token Template -> Token FilePath
|
-> Token Template -> Token FilePath
|
||||||
-> Token FilePath -> DepGenM (Token ((Text, Date), FilePath))
|
-> Token [FilePath] -> Token FilePath
|
||||||
handleRecipeDir recipesDir outputDir htmlTemplate indexName name = do
|
-> DepGenM (Token ((Text, Date), FilePath))
|
||||||
|
handleRecipeDir recipesDir outputDir htmlTemplate indexName recipeSubDirs name = do
|
||||||
dir <- joinPaths recipesDir name
|
dir <- joinPaths recipesDir name
|
||||||
recipeDirOut <- joinPaths outputDir dir
|
recipeDirOut <- joinPaths outputDir dir
|
||||||
makeDir recipeDirOut
|
makeDir recipeDirOut
|
||||||
|
@ -49,13 +54,35 @@ handleRecipeDir recipesDir outputDir htmlTemplate indexName name = do
|
||||||
& readMarkdown
|
& readMarkdown
|
||||||
title <- extractTitle pandoc
|
title <- extractTitle pandoc
|
||||||
date <- extractDate name
|
date <- extractDate name
|
||||||
pandoc' <- injectAfterTitle (onTupleToken T.append
|
pandoc' <- injectAfterTitle (
|
||||||
(inject "Offentliggjort ")
|
(onToken T.concat [ inject "<p class=\"date\">Offentliggjort "
|
||||||
(onToken formatDate date))
|
, onToken formatDate date
|
||||||
|
, inject "</p>"
|
||||||
|
]))
|
||||||
pandoc
|
pandoc
|
||||||
|
dirIndex <- onTupleToken elemIndex' name recipeSubDirs
|
||||||
|
prev <- onTupleToken (
|
||||||
|
\ds i -> if i < length ds - 1
|
||||||
|
then T.concat [ "<a class=\"prev\" href=\"/retter/"
|
||||||
|
, T.pack (ds !! (i + 1))
|
||||||
|
, "\">Forrige</a>"
|
||||||
|
]
|
||||||
|
else "")
|
||||||
|
recipeSubDirs dirIndex
|
||||||
|
next <- onTupleToken (
|
||||||
|
\ds i -> if i > 0
|
||||||
|
then T.concat [ "<a class=\"next\" href=\"/retter/"
|
||||||
|
, T.pack (ds !! (i - 1))
|
||||||
|
, "\">Næste</a>"
|
||||||
|
]
|
||||||
|
else "")
|
||||||
|
recipeSubDirs dirIndex
|
||||||
|
prevnext <- onToken T.concat [ inject "<p class=\"prevnext\">", pure prev, pure next, inject "</p>" ]
|
||||||
html <-
|
html <-
|
||||||
pandoc'
|
pandoc'
|
||||||
& writeHtml
|
& writeHtml
|
||||||
|
& onTupleToken T.append prevnext
|
||||||
|
& onTupleToken (flip T.append) prevnext
|
||||||
& applyTemplate htmlTemplate
|
& applyTemplate htmlTemplate
|
||||||
saveTextFile html (joinPaths recipeDirOut indexName)
|
saveTextFile html (joinPaths recipeDirOut indexName)
|
||||||
pure $ TupleToken (TupleToken title date) dir
|
pure $ TupleToken (TupleToken title date) dir
|
||||||
|
@ -82,14 +109,14 @@ generateSite = do
|
||||||
makeDir $ outputRecipesDir
|
makeDir $ outputRecipesDir
|
||||||
recipeSubDirs <-
|
recipeSubDirs <-
|
||||||
listDirectory recipesDir
|
listDirectory recipesDir
|
||||||
|
& onToken (reverse . sort)
|
||||||
& filterDepGenM (isDirectory <=< joinPaths recipesDir)
|
& filterDepGenM (isDirectory <=< joinPaths recipesDir)
|
||||||
infos <- mapDepGenM (handleRecipeDir recipesDir outputDir htmlTemplate indexName) recipeSubDirs
|
infos <- mapDepGenM (handleRecipeDir recipesDir outputDir htmlTemplate indexName recipeSubDirs) recipeSubDirs
|
||||||
allRecipesHtml <-
|
allRecipesHtml <-
|
||||||
infos
|
infos
|
||||||
& onToken (T.append "# Alle retter\n\n"
|
& onToken (T.append "# Alle retter\n\n"
|
||||||
. T.intercalate "\n"
|
. T.intercalate "\n"
|
||||||
. map (\((t, d), u) -> T.concat ["- ", "[", t, " *(", formatDateShort d, ")*](/", T.pack u, ")"])
|
. map (\((t, d), u) -> T.concat ["- ", "[", t, " *(", formatDateShort d, ")*](/", T.pack u, ")"]))
|
||||||
. sortBy (flip (comparing (\((_, d), _) -> d))))
|
|
||||||
& markdownToHtml
|
& markdownToHtml
|
||||||
& applyTemplate htmlTemplate
|
& applyTemplate htmlTemplate
|
||||||
saveTextFile allRecipesHtml (joinPaths outputRecipesDir indexName)
|
saveTextFile allRecipesHtml (joinPaths outputRecipesDir indexName)
|
||||||
|
|
26
style.css
26
style.css
|
@ -66,7 +66,7 @@ section {
|
||||||
}
|
}
|
||||||
|
|
||||||
article {
|
article {
|
||||||
padding: 0 10px;
|
padding: 0 10px 50px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3 {
|
h1, h2, h3 {
|
||||||
|
@ -76,6 +76,7 @@ h1, h2, h3 {
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
|
@ -98,6 +99,29 @@ p.imagetext {
|
||||||
margin-bottom: 0.1em;
|
margin-bottom: 0.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.prevnext {
|
||||||
|
margin: 0;
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
a.prev {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
a.prev:before {
|
||||||
|
content: '↫ ';
|
||||||
|
}
|
||||||
|
a.next {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
a.next:after {
|
||||||
|
content: ' ↬';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.date {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
p.image {
|
p.image {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
img {
|
img {
|
||||||
|
|
Loading…
Reference in New Issue