{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE CPP #-}
#ifdef SSR
{-# LANGUAGE RecordWildCards #-}
#endif
module Miso.Html.Render
(
ToHtml (..)
, toHtmlWith
) where
import qualified Data.Set as S
import Data.Set (Set)
import Data.ByteString.Builder
import qualified Data.ByteString.Lazy as L
import qualified Data.Map.Strict as M
#ifdef SSR
import Control.Exception (SomeException, catch)
import System.IO.Unsafe (unsafePerformIO)
#endif
import GHC.StaticPtr
import Miso.JSON
import Miso.String hiding (intercalate)
import qualified Miso.String as MS
import Miso.Types
class ToHtml a where
toHtml :: a -> L.ByteString
instance (context ~ (), props ~ (), model ~ ()) => ToHtml (View context props model action) where
toHtml :: View context props model action -> ByteString
toHtml = View context props model action -> ByteString
View () () () action -> ByteString
forall action. View () () () action -> ByteString
renderView
instance (context ~ (), props ~ (), model ~ ()) => ToHtml [View context props model action] where
toHtml :: [View context props model action] -> ByteString
toHtml = Builder -> ByteString
toLazyByteString (Builder -> ByteString)
-> ([View context props model action] -> Builder)
-> [View context props model action]
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (View () () () action -> Builder)
-> [View () () () action] -> Builder
forall m a. Monoid m => (a -> m) -> [a] -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (() -> () -> () -> View () () () action -> Builder
forall context props model action.
context
-> props -> model -> View context props model action -> Builder
renderBuilder () () ()) ([View () () () action] -> Builder)
-> ([View context props model action] -> [View () () () action])
-> [View context props model action]
-> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. context
-> props
-> model
-> [View context props model action]
-> [View context props model action]
forall context props model action.
context
-> props
-> model
-> [View context props model action]
-> [View context props model action]
collapseSiblingTextNodes () () ()
renderView :: View () () () action -> L.ByteString
renderView :: forall action. View () () () action -> ByteString
renderView = () -> () -> () -> View () () () action -> ByteString
forall context props model action.
context
-> props -> model -> View context props model action -> ByteString
toHtmlWith () () ()
toHtmlWith :: context -> props -> model -> View context props model action -> L.ByteString
toHtmlWith :: forall context props model action.
context
-> props -> model -> View context props model action -> ByteString
toHtmlWith context
ctx props
props model
model_ = Builder -> ByteString
toLazyByteString (Builder -> ByteString)
-> (View context props model action -> Builder)
-> View context props model action
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. context
-> props -> model -> View context props model action -> Builder
forall context props model action.
context
-> props -> model -> View context props model action -> Builder
renderBuilder context
ctx props
props model
model_
intercalate :: Builder -> [Builder] -> Builder
intercalate :: Builder -> [Builder] -> Builder
intercalate Builder
_ [] = Builder
""
intercalate Builder
_ [Builder
x] = Builder
x
intercalate Builder
sep (Builder
x:[Builder]
xs) =
[Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ Builder
x
, Builder
sep
, Builder -> [Builder] -> Builder
intercalate Builder
sep [Builder]
xs
]
booleanProperties :: Set MisoString
booleanProperties :: Set Text
booleanProperties = [Text] -> Set Text
forall a. Ord a => [a] -> Set a
S.fromList
[ Text
"allowfullscreen"
, Text
"allowpaymentrequest"
, Text
"allowusermedia"
, Text
"async"
, Text
"autofocus"
, Text
"autoplay"
, Text
"checked"
, Text
"controls"
, Text
"default"
, Text
"defer"
, Text
"disabled"
, Text
"download"
, Text
"formnovalidate"
, Text
"hidden"
, Text
"inert"
, Text
"ismap"
, Text
"itemscope"
, Text
"loop"
, Text
"multiple"
, Text
"muted"
, Text
"nomodule"
, Text
"novalidate"
, Text
"open"
, Text
"playsinline"
, Text
"readonly"
, Text
"required"
, Text
"reversed"
, Text
"selected"
, Text
"truespeed"
]
renderBuilder :: context -> props -> model -> View context props model action -> Builder
renderBuilder :: forall context props model action.
context
-> props -> model -> View context props model action -> Builder
renderBuilder context
_ props
_ model
_ (VText Maybe Key
_ Text
"") = Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString Text
" "
renderBuilder context
_ props
_ model
_ (VText Maybe Key
_ Text
s) = Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString Text
s
renderBuilder context
_ props
_ model
_ (VNode Namespace
_ Text
"doctype" [] [] Set Text
_) = Builder
"<!doctype html>"
renderBuilder context
ctx_ props
props_ model
model_ (VNode Namespace
ns Text
tag [Attribute model action]
attrs [View context props model action]
children Set Text
_) = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ Builder
"<"
, Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString Text
tag
, [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat [ Builder
" " Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder -> [Builder] -> Builder
intercalate Builder
" " (Attribute model action -> Builder
forall model action. Attribute model action -> Builder
renderAttrs (Attribute model action -> Builder)
-> [Attribute model action] -> [Builder]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Attribute model action]
attrs)
| Bool -> Bool
not ([Attribute model action] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
Prelude.null [Attribute model action]
attrs)
]
, if Text
tag Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
selfClosing then Builder
"/>" else Builder
">"
, [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ (View context props model action -> Builder)
-> [View context props model action] -> Builder
forall m a. Monoid m => (a -> m) -> [a] -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (context
-> props -> model -> View context props model action -> Builder
forall context props model action.
context
-> props -> model -> View context props model action -> Builder
renderBuilder context
ctx_ props
props_ model
model_) (context
-> props
-> model
-> [View context props model action]
-> [View context props model action]
forall context props model action.
context
-> props
-> model
-> [View context props model action]
-> [View context props model action]
collapseSiblingTextNodes context
ctx_ props
props_ model
model_ [View context props model action]
children)
, Builder
"</" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString Text
tag Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
">"
]
| Text
tag Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [Text]
selfClosing
]
] where
selfClosing :: [Text]
selfClosing = [Text]
htmls [Text] -> [Text] -> [Text]
forall a. Semigroup a => a -> a -> a
<> [Text]
svgs [Text] -> [Text] -> [Text]
forall a. Semigroup a => a -> a -> a
<> [Text]
mathmls
htmls :: [Text]
htmls = [ Text
x
| Namespace
ns Namespace -> Namespace -> Bool
forall a. Eq a => a -> a -> Bool
== Namespace
HTML
, Text
x <- [ Text
"area", Text
"base", Text
"col", Text
"embed", Text
"img", Text
"input", Text
"br", Text
"hr", Text
"meta", Text
"link", Text
"param", Text
"source", Text
"track", Text
"wbr" ]
]
svgs :: [Text]
svgs = [ Text
x
| Namespace
ns Namespace -> Namespace -> Bool
forall a. Eq a => a -> a -> Bool
== Namespace
SVG
, Text
x <- [ Text
"circle", Text
"line", Text
"rect", Text
"path", Text
"ellipse", Text
"polygon", Text
"polyline", Text
"use", Text
"image"]
]
mathmls :: [Text]
mathmls =
[ Text
x
| Namespace
ns Namespace -> Namespace -> Bool
forall a. Eq a => a -> a -> Bool
== Namespace
MATHML
, Text
x <- [Text
"mglyph", Text
"mprescripts", Text
"none", Text
"maligngroup", Text
"malignmark" ]
]
renderBuilder context
ctx_ props
_ model
_ (VComp SomeComponent context
someComp) = context -> SomeComponent context -> Builder
forall context. context -> SomeComponent context -> Builder
renderComp context
ctx_ SomeComponent context
someComp
renderBuilder context
ctx_ props
_ model
_ (VCompStatic StaticPtr (SomeStaticComponent childProps context)
ptr childProps
props0) =
case StaticPtr (SomeStaticComponent childProps context)
-> SomeStaticComponent childProps context
forall a. StaticPtr a -> a
deRefStaticPtr StaticPtr (SomeStaticComponent childProps context)
ptr of
SomeStaticComponent Component context childProps model action
comp_ -> context -> SomeComponent context -> Builder
forall context. context -> SomeComponent context -> Builder
renderComp context
ctx_ (Maybe Key
-> childProps
-> Component context childProps model action
-> SomeComponent context
forall context model action props.
MountConstraints context props model action =>
Maybe Key
-> props
-> Component context props model action
-> SomeComponent context
SomeComponent Maybe Key
forall a. Maybe a
Nothing childProps
props0 Component context childProps model action
comp_)
renderBuilder context
ctx_ props
props_ model
model_ (VFrag Maybe Key
_ [View context props model action]
kids) =
(View context props model action -> Builder)
-> [View context props model action] -> Builder
forall m a. Monoid m => (a -> m) -> [a] -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (context
-> props -> model -> View context props model action -> Builder
forall context props model action.
context
-> props -> model -> View context props model action -> Builder
renderBuilder context
ctx_ props
props_ model
model_) (context
-> props
-> model
-> [View context props model action]
-> [View context props model action]
forall context props model action.
context
-> props
-> model
-> [View context props model action]
-> [View context props model action]
collapseSiblingTextNodes context
ctx_ props
props_ model
model_ [View context props model action]
kids)
renderBuilder context
ctx_ props
props_ model
model_ (VContext context -> View context props model action
f) = context
-> props -> model -> View context props model action -> Builder
forall context props model action.
context
-> props -> model -> View context props model action -> Builder
renderBuilder context
ctx_ props
props_ model
model_ (context -> View context props model action
f context
ctx_)
renderBuilder context
ctx_ props
props_ model
model_ (VProps props -> View context props model action
f) = context
-> props -> model -> View context props model action -> Builder
forall context props model action.
context
-> props -> model -> View context props model action -> Builder
renderBuilder context
ctx_ props
props_ model
model_ (props -> View context props model action
f props
props_)
renderBuilder context
ctx_ props
props_ model
model_ (VModel model -> View context props model action
f) = context
-> props -> model -> View context props model action -> Builder
forall context props model action.
context
-> props -> model -> View context props model action -> Builder
renderBuilder context
ctx_ props
props_ model
model_ (model -> View context props model action
f model
model_)
renderComp :: context -> SomeComponent context -> Builder
renderComp :: forall context. context -> SomeComponent context -> Builder
renderComp context
ctx (SomeComponent Maybe Key
_key props
props Component context props model action
comp_) =
#ifdef SSR
let m = getInitialComponentModel comp_ in renderBuilder ctx props m (view comp_ m)
#else
context
-> props -> model -> View context props model action -> Builder
forall context props model action.
context
-> props -> model -> View context props model action -> Builder
renderBuilder context
ctx props
props (Component context props model action -> model
forall context props model action.
Component context props model action -> model
model Component context props model action
comp_) (Component context props model action
-> model -> View context props model action
forall context props model action.
Component context props model action
-> model -> View context props model action
view Component context props model action
comp_ (Component context props model action -> model
forall context props model action.
Component context props model action -> model
model Component context props model action
comp_))
#endif
renderAttrs :: Attribute model action -> Builder
renderAttrs :: forall model action. Attribute model action -> Builder
renderAttrs (ClassList [Text]
classes) =
[Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ Builder
"class"
, [Char] -> Builder
stringUtf8 [Char]
"=\""
, Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString ([Text] -> Text
MS.unwords [Text]
classes)
, [Char] -> Builder
stringUtf8 [Char]
"\""
]
renderAttrs (Property Text
key (Bool Bool
enabled))
| Text -> Set Text -> Bool
forall a. Ord a => a -> Set a -> Bool
S.member Text
key Set Text
booleanProperties, Bool
enabled = Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString Text
key
| Text -> Set Text -> Bool
forall a. Ord a => a -> Set a -> Bool
S.member Text
key Set Text
booleanProperties, Bool -> Bool
not Bool
enabled = Builder
forall a. Monoid a => a
mempty
| Bool
otherwise = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString Text
key
, [Char] -> Builder
stringUtf8 [Char]
"=\""
, Value -> Builder
toHtmlFromJSON (Bool -> Value
Bool Bool
enabled)
, [Char] -> Builder
stringUtf8 [Char]
"\""
]
renderAttrs (Property Text
"key" Value
_) = Builder
forall a. Monoid a => a
mempty
renderAttrs (Property Text
key Value
value) =
[Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString Text
key
, [Char] -> Builder
stringUtf8 [Char]
"=\""
, Value -> Builder
toHtmlFromJSON Value
value
, [Char] -> Builder
stringUtf8 [Char]
"\""
]
renderAttrs (On model -> Sink action -> VTree -> LogLevel -> Events -> IO ()
_) = Builder
forall a. Monoid a => a
mempty
renderAttrs (OnStatic StaticPtr (EventHandler model action)
_) = Builder
forall a. Monoid a => a
mempty
renderAttrs (Styles Map Text Text
styles_) =
[Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ Builder
"style"
, [Char] -> Builder
stringUtf8 [Char]
"=\""
, [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
[ Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString Text
k
, Char -> Builder
charUtf8 Char
':'
, Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString Text
v
, Char -> Builder
charUtf8 Char
';'
]
| (Text
k,Text
v) <- Map Text Text -> [(Text, Text)]
forall k a. Map k a -> [(k, a)]
M.toList Map Text Text
styles_
]
, [Char] -> Builder
stringUtf8 [Char]
"\""
]
collapseSiblingTextNodes
:: context
-> props
-> model
-> [View context props model action]
-> [View context props model action]
collapseSiblingTextNodes :: forall context props model action.
context
-> props
-> model
-> [View context props model action]
-> [View context props model action]
collapseSiblingTextNodes context
ctx_ props
props_ model
model_ = [View context props model action]
-> [View context props model action]
go
where
resolve :: View context props model action -> View context props model action
resolve (VProps props -> View context props model action
f) = View context props model action -> View context props model action
resolve (props -> View context props model action
f props
props_)
resolve (VContext context -> View context props model action
f) = View context props model action -> View context props model action
resolve (context -> View context props model action
f context
ctx_)
resolve (VModel model -> View context props model action
f) = View context props model action -> View context props model action
resolve (model -> View context props model action
f model
model_)
resolve View context props model action
v = View context props model action
v
go :: [View context props model action]
-> [View context props model action]
go (View context props model action
x : View context props model action
y : [View context props model action]
xs)
| VText Maybe Key
_ Text
a <- View context props model action -> View context props model action
resolve View context props model action
x, VText Maybe Key
k Text
b <- View context props model action -> View context props model action
resolve View context props model action
y = [View context props model action]
-> [View context props model action]
go (Maybe Key -> Text -> View context props model action
forall context props model action.
Maybe Key -> Text -> View context props model action
VText Maybe Key
k (Text
a Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
b) View context props model action
-> [View context props model action]
-> [View context props model action]
forall a. a -> [a] -> [a]
: [View context props model action]
xs)
| Bool
otherwise = View context props model action
x View context props model action
-> [View context props model action]
-> [View context props model action]
forall a. a -> [a] -> [a]
: [View context props model action]
-> [View context props model action]
go (View context props model action
y View context props model action
-> [View context props model action]
-> [View context props model action]
forall a. a -> [a] -> [a]
: [View context props model action]
xs)
go [View context props model action]
xs = [View context props model action]
xs
toHtmlFromJSON :: Value -> Builder
toHtmlFromJSON :: Value -> Builder
toHtmlFromJSON (String Text
t) = Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString (Text -> Text
forall str. ToMisoString str => str -> Text
ms Text
t)
toHtmlFromJSON (Number Double
t) = Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString (Text -> Builder) -> Text -> Builder
forall a b. (a -> b) -> a -> b
$ [Char] -> Text
forall str. ToMisoString str => str -> Text
ms (Double -> [Char]
forall a. Show a => a -> [Char]
show Double
t)
toHtmlFromJSON (Bool Bool
True) = Builder
"true"
toHtmlFromJSON (Bool Bool
False) = Builder
"false"
toHtmlFromJSON Value
Null = Builder
"null"
toHtmlFromJSON (Object Map Text Value
o) = Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString (Text -> Builder) -> Text -> Builder
forall a b. (a -> b) -> a -> b
$ [Char] -> Text
forall str. ToMisoString str => str -> Text
ms (Map Text Value -> [Char]
forall a. Show a => a -> [Char]
show Map Text Value
o)
toHtmlFromJSON (Array [Value]
a) = Text -> Builder
forall a. FromMisoString a => Text -> a
fromMisoString (Text -> Builder) -> Text -> Builder
forall a b. (a -> b) -> a -> b
$ [Char] -> Text
forall str. ToMisoString str => str -> Text
ms ([Value] -> [Char]
forall a. Show a => a -> [Char]
show [Value]
a)
#ifdef SSR
getInitialComponentModel :: Component context props model action -> model
getInitialComponentModel Component {..} =
case hydrateModel of
Nothing -> model
Just action -> unsafePerformIO $
action `catch` (\(e :: SomeException) -> do
putStrLn "Encountered exception during model hydration, falling back to default model"
print e
pure model)
#endif