{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE UndecidableInstances #-}
module Miso.Html.Internal (
VTree (..)
, View (..)
, ToView (..)
, Attribute (..)
, node
, text
, Key (..)
, ToKey (..)
, NS (..)
, prop
, style_
, on
, onWithOptions
, onCreated
, onDestroyed
, onBeforeDestroyed
) where
import Data.Aeson (Value(..), ToJSON(..))
import qualified Data.Map as M
import Data.Monoid
import Data.Proxy
import Data.String (IsString(..))
import qualified Data.Text as T
import qualified Data.Vector as V
import qualified Lucid as L
import qualified Lucid.Base as L
import Servant.API
import Miso.Event
import Miso.String hiding (map)
data VTree action where
VNode :: { vType :: Text
, vNs :: NS
, vProps :: Props
, vKey :: Maybe Key
, vChildren :: V.Vector (VTree action)
} -> VTree action
VText :: { vText :: Text
} -> VTree action
deriving Functor
instance Show (VTree action) where
show = show . L.toHtml
instance L.ToHtml (VTree action) where
toHtmlRaw = L.toHtml
toHtml (VText x) | T.null x = L.toHtml (" " :: MisoString)
| otherwise = L.toHtml x
toHtml VNode{..} =
let ele = L.makeElement (toTag vType) kids
in L.with ele as
where
Props xs = vProps
as = [ L.makeAttribute k (if k `elem` exceptions && v == Bool True then k else v')
| (k,v) <- M.toList xs
, let v' = toHtmlFromJSON v
, not (k `elem` exceptions && v == Bool False)
]
exceptions = [ "checked"
, "disabled"
, "selected"
, "hidden"
, "readOnly"
, "autoplay"
, "required"
, "default"
, "autofocus"
, "multiple"
, "noValidate"
, "autocomplete"
]
toTag = T.toLower
kids = foldMap L.toHtml vChildren
toHtmlFromJSON :: Value -> Text
toHtmlFromJSON (String t) = t
toHtmlFromJSON (Number t) = pack (show t)
toHtmlFromJSON (Bool b) = if b then "true" else "false"
toHtmlFromJSON Null = "null"
toHtmlFromJSON (Object o) = pack (show o)
toHtmlFromJSON (Array a) = pack (show a)
newtype View action = View { runView :: VTree action }
deriving Functor
instance HasLink (View a) where
#if MIN_VERSION_servant(0,14,0)
type MkLink (View a) b = MkLink (Get '[] ()) b
toLink toA Proxy = toLink toA (Proxy :: Proxy (Get '[] ()))
#else
type MkLink (View a) = MkLink (Get '[] ())
toLink Proxy = toLink (Proxy :: Proxy (Get '[] ()))
#endif
class ToView v where toView :: v -> View action
instance Show (View action) where
show (View xs) = show xs
instance L.ToHtml (View action) where
toHtmlRaw = L.toHtml
toHtml (View xs) = L.toHtml xs
data NS
= HTML
| SVG
| MATHML
deriving (Show, Eq)
node :: NS -> MisoString -> Maybe Key -> [Attribute action] -> [View action] -> View action
node vNs vType vKey as xs =
let vProps = Props $ M.fromList [ (k,v) | P k v <- as ]
vChildren = V.fromList $ map runView xs
in View VNode {..}
text :: MisoString -> View action
text = View . VText
instance IsString (View a) where
fromString = text . fromString
newtype Key = Key MisoString
deriving (Show, Eq, Ord)
class ToKey key where toKey :: key -> Key
instance ToKey Key where toKey = id
instance ToKey MisoString where toKey = Key
instance ToKey String where toKey = Key . T.pack
instance ToKey Int where toKey = Key . T.pack . show
instance ToKey Double where toKey = Key . T.pack . show
instance ToKey Float where toKey = Key . T.pack . show
instance ToKey Word where toKey = Key . T.pack . show
newtype Props = Props (M.Map MisoString Value)
deriving (Show, Eq)
data Attribute action
= P MisoString Value
| E ()
deriving (Show, Eq)
newtype AllowDrop = AllowDrop Bool
deriving (Show, Eq)
prop :: ToJSON a => MisoString -> a -> Attribute action
prop k v = P k (toJSON v)
on :: MisoString
-> Decoder r
-> (r -> action)
-> Attribute action
on _ _ _ = E ()
onWithOptions
:: Options
-> MisoString
-> Decoder r
-> (r -> action)
-> Attribute action
onWithOptions _ _ _ _ = E ()
onCreated :: action -> Attribute action
onCreated _ = E ()
onDestroyed :: action -> Attribute action
onDestroyed _ = E ()
onBeforeDestroyed :: action -> Attribute action
onBeforeDestroyed _ = E ()
style_ :: M.Map MisoString MisoString -> Attribute action
style_ map' = P "style" $ String (M.foldrWithKey go mempty map')
where
go :: MisoString -> MisoString -> MisoString -> MisoString
go k v xs = mconcat [ k, ":", v, ";" ] <> xs