1 {-# LANGUAGE OverloadedLists #-}
2 {-# LANGUAGE UndecidableInstances #-}
3 {-# OPTIONS_GHC -Wno-orphans #-}
5 module Literate.Document.HTML (
6 module Literate.Document.HTML,
7 module Text.Blaze.Html5,
8 module Text.Blaze.Renderer.Utf8,
12 import Data.Char qualified as Char
13 import Data.List qualified as List
14 import Data.Map.Strict qualified as Map
15 import Data.Text qualified as Text
16 import Data.Text.Short qualified as ShortText
17 import Literate.Document qualified as Doc
18 import Literate.Document.Table
19 import Literate.Document.Type
20 import Literate.Prelude
22 import Text.Blaze.Html5
23 import Text.Blaze.Html5.Attributes qualified as HA
24 import Text.Blaze.Renderer.Utf8
26 instance IsString AttributeValue
27 instance IsString Html
29 classes :: [Class] -> Attribute
33 & List.filter (not . Text.null . Doc.unClass)
35 & List.intersperse " "
38 className :: Show a => a -> String
40 x & show & List.map \c ->
45 type CSSBlock = Map String String
47 styles :: CSSBlock -> Attribute
50 [ toValue k <> ":" <> toValue v <> ";"
51 | (k, v) <- kvs & Map.toList
56 type CSS = Map [String] CSSBlock
57 styleCSS :: CSS -> Markup
61 $ [ mconcat [n <> " {" | n <- ns]
64 [ k <> ":" <> v <> ";"
65 | (k, v) <- kvs & Map.toList
68 <> mconcat [" }" | _n <- ns]
70 | (ns, kvs) <- m & Map.toList
79 instance ToMarkup ShortText where
80 toMarkup = ShortText.toText >>> toMarkup
81 preEscapedToMarkup = ShortText.toText >>> preEscapedToMarkup
83 instance ToCSS Text where
86 instance ToCSS Length where
88 LengthAbsolute x -> x & toCSS
89 LengthRelative x -> x & toCSS
90 instance ToCSS LengthAbsolute where
92 LengthAbsoluteMillimeters x -> show x <> "mm"
93 instance ToCSS LengthRelative where
95 LengthRelativeFractionalRatio x -> show x <> "fr"
96 LengthRelativeMaxContent -> "max-content"
97 LengthRelativeMinContent -> "min-content"
100 PageOrientationLandscape -> 29.7 & cm
101 PageOrientationPortrait -> 21.0 & cm
102 cssPageHeight = \case
103 PageOrientationLandscape -> 21.0 & cm
104 PageOrientationPortrait -> 29.7 & cm
108 PageSizeA4Plus -> "A4plus"
110 cssPageOrientation = \case
111 PageOrientationPortrait -> "portrait"
112 PageOrientationLandscape -> "landscape"
114 cssPrintPage :: PageOrientation -> PageSize -> CSS
115 cssPrintPage pageOrient pageSize =
120 [ cssPageSize pageSize
121 , cssPageOrientation pageOrient
128 cssBlockObjectFitCover :: CSSBlock
129 cssBlockObjectFitCover = ["object-fit" := "cover"]
131 instance ToMarkup Block where
134 { blockDivAnchor = maybeId
135 , blockDivClasses = cls
138 (maybe div (\anchor -> div ! HA.id (anchor & toValue)) maybeId) ! classes cls $
140 BlockFlex x -> x & toMarkup
141 BlockList x -> x & toMarkup
142 BlockPara x -> p $ x & toMarkup
143 BlockTable x -> x & toMarkup
144 BlockDict x -> x & toMarkup
145 Blocks xs -> xs & foldMap toMarkup
146 instance ToMarkup Inline where
148 InlineText x -> x & toMarkup
149 InlineCode x -> code $ x & toMarkup
150 InlineLink{inlineLinkText, inlineLinkTarget} ->
151 a ! HA.href (inlineLinkTarget & unTarget & toValue) $ inlineLinkText & toMarkup
152 Inlines xs -> xs & foldMap toMarkup
153 instance ToMarkup FlexItem where
155 forM_ (itm & flexItemContent) toMarkup
156 instance ToMarkup Flex where
160 [ "display" := "flex"
161 , "flex-direction" := flx & flexDirection & toCSS
162 , "gap" := flx & flexGap & toCSS
165 forM_ (flx & flexItems) toMarkup
167 instance ToMarkup List where
168 toMarkup List{listItems} =
172 [ "grid-template-columns" :=
173 [ LengthRelative LengthRelativeMaxContent
174 , LengthRelative $ 1 & fr
180 forM_ (listItems & ol1) \(itemCount, (itemKey, itemValue)) -> do
181 div ! classes ["list-body", if even itemCount then "even" else "odd"] $ do
183 ! classes ["list-key"]
184 ! styles ["justify-items" := JustificationRight & toCSS]
188 ! classes ["list-value"]
189 ! styles ["justify-items" := JustificationLeft & toCSS]
192 instance ToMarkup Table where
196 ! styles ["grid-template-columns" := tbl & tableTemplateFinal <&> toCSS & List.unwords]
198 forM_ (tbl & tableHeads) \hd -> do
199 div ! classes ["table-head"] $ do
200 forM_ (hd & tableHeadColumns) \cel -> do
201 div ! classes ["table-cell"] $ do
203 cel & tableCellContent & toMarkup
204 forM_ (tbl & tableRows & ol1) \(rowCount, row) -> do
205 div ! classes ["table-body", if even rowCount then "even" else "odd"] $ do
206 forM_ (row & tableRowColumns) \cel -> do
208 ! classes ["table-cell"]
209 ! styles ["justify-items" := cel & tableCellJustify & toCSS]
211 cel & tableCellContent & toMarkup
212 instance ToMarkup Dict where
217 forM_ (dic & dictEntries) \(key, val) ->
218 div ! classes ["dict-entry"] $ do
219 div ! classes ["dict-key"] $ do
222 div ! classes ["dict-value"] $ do
224 instance ToCSS Justification where
226 JustificationLeft -> "left"
227 JustificationBegin -> "begin"
228 JustificationCenter -> "center"
229 JustificationEnd -> "end"
230 JustificationRight -> "right"
231 instance ToCSS FlexDirection where
233 FlexDirectionColumn -> "column"
234 FlexDirectionRow -> "row"