-----------------------------------------------------------------------------
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ExistentialQuantification  #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE ScopedTypeVariables        #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE TypeApplications           #-}
{-# LANGUAGE RecordWildCards            #-}
{-# LANGUAGE DeriveFunctor              #-}
{-# LANGUAGE TypeFamilies               #-}
{-# LANGUAGE DataKinds                  #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Miso.Types
-- Copyright   :  (C) 2016-2025 David M. Johnson
-- License     :  BSD3-style (see the file LICENSE)
-- Maintainer  :  David M. Johnson <code@dmj.io>
-- Stability   :  experimental
-- Portability :  non-portable
----------------------------------------------------------------------------
module Miso.Types
  ( -- ** Types
    App
  , Component        (..)
  , Binding          (..)
  , ComponentId
  , SomeComponent    (..)
  , View             (..)
  , Key              (..)
  , Attribute        (..)
  , NS               (..)
  , CSS              (..)
  , JS               (..)
  , LogLevel         (..)
  , VTree            (..)
  , MountPoint
  , DOMRef
  , ROOT
  , Transition
  -- ** Classes
  , ToView           (..)
  , ToKey            (..)
  -- ** Smart Constructors
  , component
  -- ** Data binding
  , (-->)
  , (<--)
  , (<-->)
  , getDirection
  , Direction (..)
  -- ** Component
  , mount
  , (+>)
  -- ** Utils
  , getMountPoint
  -- *** Combinators
  , node
  , text
  , text_
  , textRaw
  , rawHtml
  -- *** MisoString
  , MisoString
  , toMisoString
  , fromMisoString
  , ms
  ) where
-----------------------------------------------------------------------------
import qualified Data.Map.Strict as M
import qualified Data.Text as T
import           Data.Aeson (Value, ToJSON)
import           Data.JSString (JSString)
import           Data.Kind (Type)
import           Data.Coerce (coerce)
import           Data.Maybe (fromMaybe)
import           Data.String (IsString, fromString)
import           Language.Javascript.JSaddle (ToJSVal(toJSVal), Object(..), JSM)
import           Prelude hiding              (null)
import           Servant.API (HasLink(MkLink, toLink))
-----------------------------------------------------------------------------
import           Miso.Concurrent (Mail)
import           Miso.Effect (Effect, Sub, Sink, DOMRef)
import           Miso.Lens (Lens)
import           Miso.String (MisoString, toMisoString, ms, fromMisoString)
import           Miso.Event.Types
import           Miso.Style.Types (StyleSheet)
import qualified Miso.String as MS
-----------------------------------------------------------------------------
-- | Application entry point
data Component parent model action
  = Component
  { forall parent model action. Component parent model action -> model
model :: model
  -- ^ initial model
  , forall parent model action.
Component parent model action
-> action -> Effect parent model action
update :: action -> Effect parent model action
  -- ^ Function to update model, optionally providing effects.
  , forall parent model action.
Component parent model action -> model -> View model action
view :: model -> View model action
  -- ^ Function to draw `View`
  , forall parent model action.
Component parent model action -> [Sub action]
subs :: [ Sub action ]
  -- ^ List of subscriptions to run during application lifetime
  , forall parent model action. Component parent model action -> Events
events :: Events
  -- ^ List of delegated events that the body element will listen for.
  --   You can start with 'Miso.Event.Types.defaultEvents' and modify as needed.
  , forall parent model action. Component parent model action -> [CSS]
styles :: [CSS]
  -- ^ List of CSS styles expressed as either a URL ('Href') or as 'Style' text.
  -- These styles are appended dynamically to the <head> section of your HTML page
  -- before the initial draw on <body> occurs.
  --
  -- @since 1.9.0.0
  , forall parent model action. Component parent model action -> [JS]
scripts :: [JS]
  -- ^ List of JavaScript <scripts> expressed as either a URL ('Src') or raw JS text.
  -- These scripts are appended dynamically to the <head> section of your HTML page
  -- before the initial draw on <body> occurs.
  --
  -- @since 1.9.0.0
  , forall parent model action.
Component parent model action -> Maybe action
initialAction :: Maybe action
  -- ^ Initial action that is run after the application has loaded, optional
  --
  -- @since 1.9.0.0
  , forall parent model action.
Component parent model action -> Maybe MisoString
mountPoint :: Maybe MountPoint
  -- ^ Id of the root element for DOM diff.
  -- If 'Nothing' is provided, the entire document body is used as a mount point.
  , forall parent model action.
Component parent model action -> LogLevel
logLevel :: LogLevel
  -- ^ Debugging for prerendering and event delegation
  , forall parent model action.
Component parent model action -> Mail -> Maybe action
mailbox :: Mail -> Maybe action
  -- ^ Used to receive mail from other 'Component'
  --
  -- @since 1.9.0.0
  , forall parent model action.
Component parent model action -> [Binding parent model]
bindings :: [ Binding parent model ]
  }
-----------------------------------------------------------------------------
-- | @mountPoint@ for @Component@, e.g "body"
type MountPoint = MisoString
-----------------------------------------------------------------------------
-- | ID for 'Component'
type ComponentId = Int
-----------------------------------------------------------------------------
-- | Allow users to express CSS and append it to <head> before the first draw
--
-- > Href "http://domain.com/style.css
--
data CSS
  = Href MisoString
  -- ^ 'Href' is a URL meant to link to hosted CSS
  | Style MisoString
  -- ^ 'Style' is meant to be raw CSS in a 'style_' tag
  | Sheet StyleSheet
  -- ^ 'Sheet' is meant to be CSS built with 'Miso.Style'
  deriving (Int -> CSS -> ShowS
[CSS] -> ShowS
CSS -> String
(Int -> CSS -> ShowS)
-> (CSS -> String) -> ([CSS] -> ShowS) -> Show CSS
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CSS -> ShowS
showsPrec :: Int -> CSS -> ShowS
$cshow :: CSS -> String
show :: CSS -> String
$cshowList :: [CSS] -> ShowS
showList :: [CSS] -> ShowS
Show, CSS -> CSS -> Bool
(CSS -> CSS -> Bool) -> (CSS -> CSS -> Bool) -> Eq CSS
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CSS -> CSS -> Bool
== :: CSS -> CSS -> Bool
$c/= :: CSS -> CSS -> Bool
/= :: CSS -> CSS -> Bool
Eq)
-----------------------------------------------------------------------------
-- | Allow users to express JS and append it to <head> before the first draw
--
-- This is meant to be useful in development only.
--
-- @
--   Src "http://example.com/script.js"
--   Script "http://example.com/script.js"
--   ImportMap [ "key" =: "value" ]
-- @
--
data JS
  = Src MisoString
  -- ^ 'src' is a URL meant to link to hosted JS
  | Script MisoString
  -- ^ 'script' is meant to be raw JS in a 'script' tag
  | ImportMap [(MisoString,MisoString)]
  -- ^ 'script' is meant to be an import map in a 'script' tag
  deriving (Int -> JS -> ShowS
[JS] -> ShowS
JS -> String
(Int -> JS -> ShowS)
-> (JS -> String) -> ([JS] -> ShowS) -> Show JS
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> JS -> ShowS
showsPrec :: Int -> JS -> ShowS
$cshow :: JS -> String
show :: JS -> String
$cshowList :: [JS] -> ShowS
showList :: [JS] -> ShowS
Show, JS -> JS -> Bool
(JS -> JS -> Bool) -> (JS -> JS -> Bool) -> Eq JS
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: JS -> JS -> Bool
== :: JS -> JS -> Bool
$c/= :: JS -> JS -> Bool
/= :: JS -> JS -> Bool
Eq)
-----------------------------------------------------------------------------
-- | Convenience for extracting mount point
getMountPoint :: Maybe MisoString -> MisoString
getMountPoint :: Maybe MisoString -> MisoString
getMountPoint = MisoString -> Maybe MisoString -> MisoString
forall a. a -> Maybe a -> a
fromMaybe MisoString
"body"
-----------------------------------------------------------------------------
-- | Smart constructor for @Component@ with sane defaults.
component
  :: model
  -> (action -> Effect parent model action)
  -> (model -> View model action)
  -> Component parent model action
component :: forall model action parent.
model
-> (action -> Effect parent model action)
-> (model -> View model action)
-> Component parent model action
component model
m action -> Effect parent model action
u model -> View model action
v = Component
  { model :: model
model = model
m
  , update :: action -> Effect parent model action
update = action -> Effect parent model action
u
  , view :: model -> View model action
view = model -> View model action
v
  , subs :: [Sub action]
subs = []
  , events :: Events
events = Events
defaultEvents
  , styles :: [CSS]
styles = []
  , scripts :: [JS]
scripts = []
  , mountPoint :: Maybe MisoString
mountPoint = Maybe MisoString
forall a. Maybe a
Nothing
  , logLevel :: LogLevel
logLevel = LogLevel
Off
  , initialAction :: Maybe action
initialAction = Maybe action
forall a. Maybe a
Nothing
  , mailbox :: Mail -> Maybe action
mailbox = Maybe action -> Mail -> Maybe action
forall a b. a -> b -> a
const Maybe action
forall a. Maybe a
Nothing
  , bindings :: [Binding parent model]
bindings = []
  }
-----------------------------------------------------------------------------
-- | A top-level 'Component' can have no 'parent'
--
-- The 'ROOT' type is for disallowing a top-level mounted 'Component' access
-- into its parent state. It has no inhabitants (spiritually Data.Void.Void)
--
data ROOT
-----------------------------------------------------------------------------
-- | For top-level `Component`, `ROOT` must always be specified for parent.
type App model action = Component ROOT model action
-----------------------------------------------------------------------------
-- | When `Component` are not in use, also for pre-1.9 `miso` applications.
type Transition model action = Effect ROOT model action
-----------------------------------------------------------------------------
-- | Optional logging for debugging miso internals (useful to see if prerendering is successful)
data LogLevel
  = Off
  -- ^ No debug logging, the default value used in @component@
  | DebugHydrate
  -- ^ Will warn if the structure or properties of the
  -- DOM vs. Virtual DOM differ during prerendering.
  | DebugEvents
  -- ^ Will warn if an event cannot be routed to the Haskell event
  -- handler that raised it. Also will warn if an event handler is
  -- being used, yet it's not being listened for by the event
  -- delegator mount point.
  | DebugAll
  -- ^ Logs on all of the above
  deriving (Int -> LogLevel -> ShowS
[LogLevel] -> ShowS
LogLevel -> String
(Int -> LogLevel -> ShowS)
-> (LogLevel -> String) -> ([LogLevel] -> ShowS) -> Show LogLevel
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> LogLevel -> ShowS
showsPrec :: Int -> LogLevel -> ShowS
$cshow :: LogLevel -> String
show :: LogLevel -> String
$cshowList :: [LogLevel] -> ShowS
showList :: [LogLevel] -> ShowS
Show, LogLevel -> LogLevel -> Bool
(LogLevel -> LogLevel -> Bool)
-> (LogLevel -> LogLevel -> Bool) -> Eq LogLevel
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: LogLevel -> LogLevel -> Bool
== :: LogLevel -> LogLevel -> Bool
$c/= :: LogLevel -> LogLevel -> Bool
/= :: LogLevel -> LogLevel -> Bool
Eq)
-----------------------------------------------------------------------------
-- | Core type for constructing a virtual DOM in Haskell
-----------------------------------------------------------------------------
data View model action
  = VNode NS MisoString [Attribute action] [View model action]
  | VText MisoString
  | VTextRaw MisoString
  | VComp NS MisoString [Attribute action] (SomeComponent model)
  deriving (forall a b. (a -> b) -> View model a -> View model b)
-> (forall a b. a -> View model b -> View model a)
-> Functor (View model)
forall a b. a -> View model b -> View model a
forall a b. (a -> b) -> View model a -> View model b
forall model a b. a -> View model b -> View model a
forall model a b. (a -> b) -> View model a -> View model b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall model a b. (a -> b) -> View model a -> View model b
fmap :: forall a b. (a -> b) -> View model a -> View model b
$c<$ :: forall model a b. a -> View model b -> View model a
<$ :: forall a b. a -> View model b -> View model a
Functor
-----------------------------------------------------------------------------
-- | Existential wrapper used to allow the nesting of @Component@ in @Component@
data SomeComponent parent
   = forall model action . Eq model
  => SomeComponent (Component parent model action)
-----------------------------------------------------------------------------
-- | Used in the @view@ function to mount a 'Component' on any 'VNode'
--
-- @
--   mount (p_ [ key_ "component-1" ]) $ component $ \\m ->
--     div_ [ id_ "foo" ] [ text (ms m) ]
-- @
--
-- Warning this *is* a partial function. Do not attempt to mount on a
-- Text node. This function will ignore the children given and mount the
-- new 'Component' on top of them. Attempts to mount a 'Component' ontop of an
-- existing 'Component' always prioritize the component specified in the lowest
-- level.
--
-- See usage above. In general, it's wise to only mount on `VNode`.
--
-- @since 1.9.0.0
mount
  :: forall parent model action a . Eq model
  => ([View parent a] -> View parent a)
  -> Component parent model action
  -> View parent a
mount :: forall parent model action a.
Eq model =>
([View parent a] -> View parent a)
-> Component parent model action -> View parent a
mount [View parent a] -> View parent a
mkNode Component parent model action
vcomp =
  case [View parent a] -> View parent a
mkNode [] of
    VNode NS
ns MisoString
tag [Attribute a]
attrs [View parent a]
_ ->
      NS
-> MisoString
-> [Attribute a]
-> SomeComponent parent
-> View parent a
forall model action.
NS
-> MisoString
-> [Attribute action]
-> SomeComponent model
-> View model action
VComp NS
ns MisoString
tag [Attribute a]
attrs
        (Component parent model action -> SomeComponent parent
forall parent model action.
Eq model =>
Component parent model action -> SomeComponent parent
SomeComponent Component parent model action
vcomp)
    VComp NS
ns MisoString
tag [Attribute a]
attrs SomeComponent parent
vcomp_ ->
      NS
-> MisoString
-> [Attribute a]
-> SomeComponent parent
-> View parent a
forall model action.
NS
-> MisoString
-> [Attribute action]
-> SomeComponent model
-> View model action
VComp NS
ns MisoString
tag [Attribute a]
attrs SomeComponent parent
vcomp_
    View parent a
_ ->
      String -> View parent a
forall a. HasCallStack => String -> a
error String
"Impossible: cannot mount on a Text node"
-----------------------------------------------------------------------------
(+>)
  :: forall parent model action a . Eq model
  => ([View parent a] -> View parent a)
  -> Component parent model action
  -> View parent a
infixr 0 +>
+> :: forall parent model action a.
Eq model =>
([View parent a] -> View parent a)
-> Component parent model action -> View parent a
(+>) = ([View parent a] -> View parent a)
-> Component parent model action -> View parent a
forall parent model action a.
Eq model =>
([View parent a] -> View parent a)
-> Component parent model action -> View parent a
mount
-----------------------------------------------------------------------------
-- | For constructing type-safe links
instance HasLink (View m a) where
  type MkLink (View m a) b = b
  toLink :: forall a.
(Link -> a) -> Proxy (View m a) -> Link -> MkLink (View m a) a
toLink Link -> a
x Proxy (View m a)
_ = Link -> a
Link -> MkLink (View m a) a
x
-----------------------------------------------------------------------------
-- | Convenience class for using View
class ToView m a where
  type ToViewAction m a :: Type
  toView :: a -> View m (ToViewAction m a)
-----------------------------------------------------------------------------
instance ToView model (View model action) where
  type ToViewAction model (View model action) = action
  toView :: View model action
-> View model (ToViewAction model (View model action))
toView = View model action -> View model action
View model action
-> View model (ToViewAction model (View model action))
forall a b. Coercible a b => a -> b
coerce
-----------------------------------------------------------------------------
instance ToView model (Component parent model action) where
  type ToViewAction model (Component parent model action) = action
  toView :: Component parent model action
-> View model (ToViewAction model (Component parent model action))
toView Component {model
[Binding parent model]
[JS]
[CSS]
[Sub action]
Maybe action
Maybe MisoString
Events
LogLevel
model -> View model action
action -> Effect parent model action
Mail -> Maybe action
model :: forall parent model action. Component parent model action -> model
update :: forall parent model action.
Component parent model action
-> action -> Effect parent model action
view :: forall parent model action.
Component parent model action -> model -> View model action
subs :: forall parent model action.
Component parent model action -> [Sub action]
events :: forall parent model action. Component parent model action -> Events
styles :: forall parent model action. Component parent model action -> [CSS]
scripts :: forall parent model action. Component parent model action -> [JS]
initialAction :: forall parent model action.
Component parent model action -> Maybe action
mountPoint :: forall parent model action.
Component parent model action -> Maybe MisoString
logLevel :: forall parent model action.
Component parent model action -> LogLevel
mailbox :: forall parent model action.
Component parent model action -> Mail -> Maybe action
bindings :: forall parent model action.
Component parent model action -> [Binding parent model]
model :: model
update :: action -> Effect parent model action
view :: model -> View model action
subs :: [Sub action]
events :: Events
styles :: [CSS]
scripts :: [JS]
initialAction :: Maybe action
mountPoint :: Maybe MisoString
logLevel :: LogLevel
mailbox :: Mail -> Maybe action
bindings :: [Binding parent model]
..} = View model action
-> View model (ToViewAction model (View model action))
forall m a. ToView m a => a -> View m (ToViewAction m a)
toView (model -> View model action
view model
model)
-----------------------------------------------------------------------------
-- | Namespace of DOM elements.
data NS
  = HTML   -- ^ HTML Namespace
  | SVG    -- ^ SVG Namespace
  | MATHML -- ^ MATHML Namespace
  deriving (Int -> NS -> ShowS
[NS] -> ShowS
NS -> String
(Int -> NS -> ShowS)
-> (NS -> String) -> ([NS] -> ShowS) -> Show NS
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> NS -> ShowS
showsPrec :: Int -> NS -> ShowS
$cshow :: NS -> String
show :: NS -> String
$cshowList :: [NS] -> ShowS
showList :: [NS] -> ShowS
Show, NS -> NS -> Bool
(NS -> NS -> Bool) -> (NS -> NS -> Bool) -> Eq NS
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: NS -> NS -> Bool
== :: NS -> NS -> Bool
$c/= :: NS -> NS -> Bool
/= :: NS -> NS -> Bool
Eq)
-----------------------------------------------------------------------------
instance ToJSVal NS where
  toJSVal :: NS -> JSM JSVal
toJSVal NS
SVG    = JSString -> JSM JSVal
forall a. ToJSVal a => a -> JSM JSVal
toJSVal (JSString
"svg" :: JSString)
  toJSVal NS
HTML   = JSString -> JSM JSVal
forall a. ToJSVal a => a -> JSM JSVal
toJSVal (JSString
"html" :: JSString)
  toJSVal NS
MATHML = JSString -> JSM JSVal
forall a. ToJSVal a => a -> JSM JSVal
toJSVal (JSString
"mathml" :: JSString)
-----------------------------------------------------------------------------
-- | A unique key for a DOM node.
--
-- This key is only used to speed up diffing the children of a DOM
-- node, the actual content is not important. The keys of the children
-- of a given DOM node must be unique. Failure to satisfy this
-- invariant gives undefined behavior at runtime.
newtype Key = Key MisoString
  deriving (Int -> Key -> ShowS
[Key] -> ShowS
Key -> String
(Int -> Key -> ShowS)
-> (Key -> String) -> ([Key] -> ShowS) -> Show Key
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Key -> ShowS
showsPrec :: Int -> Key -> ShowS
$cshow :: Key -> String
show :: Key -> String
$cshowList :: [Key] -> ShowS
showList :: [Key] -> ShowS
Show, Key -> Key -> Bool
(Key -> Key -> Bool) -> (Key -> Key -> Bool) -> Eq Key
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Key -> Key -> Bool
== :: Key -> Key -> Bool
$c/= :: Key -> Key -> Bool
/= :: Key -> Key -> Bool
Eq, String -> Key
(String -> Key) -> IsString Key
forall a. (String -> a) -> IsString a
$cfromString :: String -> Key
fromString :: String -> Key
IsString, [Key] -> Mail
[Key] -> Encoding
Key -> Bool
Key -> Mail
Key -> Encoding
(Key -> Mail)
-> (Key -> Encoding)
-> ([Key] -> Mail)
-> ([Key] -> Encoding)
-> (Key -> Bool)
-> ToJSON Key
forall a.
(a -> Mail)
-> (a -> Encoding)
-> ([a] -> Mail)
-> ([a] -> Encoding)
-> (a -> Bool)
-> ToJSON a
$ctoJSON :: Key -> Mail
toJSON :: Key -> Mail
$ctoEncoding :: Key -> Encoding
toEncoding :: Key -> Encoding
$ctoJSONList :: [Key] -> Mail
toJSONList :: [Key] -> Mail
$ctoEncodingList :: [Key] -> Encoding
toEncodingList :: [Key] -> Encoding
$comitField :: Key -> Bool
omitField :: Key -> Bool
ToJSON)
-----------------------------------------------------------------------------
-- | ToJSVal instance for Key
instance ToJSVal Key where
  toJSVal :: Key -> JSM JSVal
toJSVal (Key MisoString
x) = MisoString -> JSM JSVal
forall a. ToJSVal a => a -> JSM JSVal
toJSVal MisoString
x
-----------------------------------------------------------------------------
-- | Convert custom key types to @Key@.
--
-- Instances of this class do not have to guarantee uniqueness of the
-- generated keys, it is up to the user to do so. @toKey@ must be an
-- injective function.
class ToKey key where
  -- | Converts any key into @Key@
  toKey :: key -> Key
-----------------------------------------------------------------------------
-- | Identity instance
instance ToKey Key where toKey :: Key -> Key
toKey = Key -> Key
forall a. a -> a
id
-----------------------------------------------------------------------------
-- | Convert @MisoString@ to @Key@
instance ToKey JSString where toKey :: JSString -> Key
toKey = MisoString -> Key
Key (MisoString -> Key) -> (JSString -> MisoString) -> JSString -> Key
forall b c a. (b -> c) -> (a -> b) -> a -> c
. JSString -> MisoString
forall str. ToMisoString str => str -> MisoString
toMisoString
-----------------------------------------------------------------------------
-- | Convert @T.Text@ to @Key@
instance ToKey T.Text where toKey :: MisoString -> Key
toKey = MisoString -> Key
Key (MisoString -> Key)
-> (MisoString -> MisoString) -> MisoString -> Key
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MisoString -> MisoString
forall str. ToMisoString str => str -> MisoString
toMisoString
-----------------------------------------------------------------------------
-- | Convert @String@ to @Key@
instance ToKey String where toKey :: String -> Key
toKey = MisoString -> Key
Key (MisoString -> Key) -> (String -> MisoString) -> String -> Key
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> MisoString
forall str. ToMisoString str => str -> MisoString
toMisoString
-----------------------------------------------------------------------------
-- | Convert @Int@ to @Key@
instance ToKey Int where toKey :: Int -> Key
toKey = MisoString -> Key
Key (MisoString -> Key) -> (Int -> MisoString) -> Int -> Key
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> MisoString
forall str. ToMisoString str => str -> MisoString
toMisoString
-----------------------------------------------------------------------------
-- | Convert @Double@ to @Key@
instance ToKey Double where toKey :: Double -> Key
toKey = MisoString -> Key
Key (MisoString -> Key) -> (Double -> MisoString) -> Double -> Key
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> MisoString
forall str. ToMisoString str => str -> MisoString
toMisoString
-----------------------------------------------------------------------------
-- | Convert @Float@ to @Key@
instance ToKey Float where toKey :: Float -> Key
toKey = MisoString -> Key
Key (MisoString -> Key) -> (Float -> MisoString) -> Float -> Key
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Float -> MisoString
forall str. ToMisoString str => str -> MisoString
toMisoString
-----------------------------------------------------------------------------
-- | Convert @Word@ to @Key@
instance ToKey Word where toKey :: Word -> Key
toKey = MisoString -> Key
Key (MisoString -> Key) -> (Word -> MisoString) -> Word -> Key
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word -> MisoString
forall str. ToMisoString str => str -> MisoString
toMisoString
-----------------------------------------------------------------------------
-- | Attribute of a vnode in a @View@.
--
-- The @Sink@ callback can be used to dispatch actions which are fed back to
-- the @update@ function. This is especially useful for event handlers
-- like the @onclick@ attribute. The second argument represents the
-- vnode the attribute is attached to.
data Attribute action
  = Property MisoString Value
  | Event (Sink action -> VTree -> LogLevel -> Events -> JSM ())
  | Styles (M.Map MisoString MisoString)
  deriving (forall a b. (a -> b) -> Attribute a -> Attribute b)
-> (forall a b. a -> Attribute b -> Attribute a)
-> Functor Attribute
forall a b. a -> Attribute b -> Attribute a
forall a b. (a -> b) -> Attribute a -> Attribute b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall a b. (a -> b) -> Attribute a -> Attribute b
fmap :: forall a b. (a -> b) -> Attribute a -> Attribute b
$c<$ :: forall a b. a -> Attribute b -> Attribute a
<$ :: forall a b. a -> Attribute b -> Attribute a
Functor
-----------------------------------------------------------------------------
-- | @IsString@ instance
instance IsString (View model action) where
  fromString :: String -> View model action
fromString = MisoString -> View model action
forall model action. MisoString -> View model action
VText (MisoString -> View model action)
-> (String -> MisoString) -> String -> View model action
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> MisoString
forall a. IsString a => String -> a
fromString
-----------------------------------------------------------------------------
-- | Virtual DOM implemented as a JavaScript `Object`.
--   Used for diffing, patching and event delegation.
--   Not meant to be constructed directly, see `View` instead.
newtype VTree = VTree { VTree -> Object
getTree :: Object }
-----------------------------------------------------------------------------
instance ToJSVal VTree where
  toJSVal :: VTree -> JSM JSVal
toJSVal (VTree (Object JSVal
vtree)) = JSVal -> JSM JSVal
forall a. a -> JSM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure JSVal
vtree
-----------------------------------------------------------------------------
-- | Create a new @Miso.Types.TextRaw@.
--
-- @expandable@
-- a 'rawHtml' node takes raw HTML and attempts to convert it to a 'VTree'
-- at runtime. This is a way to dynamically populate the virtual DOM from
-- HTML received at runtime. If rawHtml cannot parse the HTML it will not render.
rawHtml
  :: MisoString
  -> View parent action
rawHtml :: forall model action. MisoString -> View model action
rawHtml = MisoString -> View parent action
forall model action. MisoString -> View model action
VTextRaw
-----------------------------------------------------------------------------
-- | Create a new @Miso.Types.VNode@.
--
-- @node ns tag key attrs children@ creates a new node with tag @tag@
-- and 'Key' @key@ in the namespace @ns@. All @attrs@ are called when
-- the node is created and its children are initialized to @children@.
node :: NS
     -> MisoString
     -> [Attribute action]
     -> [View parent action]
     -> View parent action
node :: forall action parent.
NS
-> MisoString
-> [Attribute action]
-> [View parent action]
-> View parent action
node = NS
-> MisoString
-> [Attribute action]
-> [View parent action]
-> View parent action
forall model action.
NS
-> MisoString
-> [Attribute action]
-> [View model action]
-> View model action
VNode
-----------------------------------------------------------------------------
-- | Create a new @Text@ with the given content.
text :: MisoString -> View parent action
text :: forall model action. MisoString -> View model action
text = MisoString -> View parent action
forall model action. MisoString -> View model action
VText
-----------------------------------------------------------------------------
-- | Create a new @Text@ with the given content.
text_ :: [MisoString] -> View parent action
text_ :: forall parent action. [MisoString] -> View parent action
text_ = MisoString -> View parent action
forall model action. MisoString -> View model action
VText (MisoString -> View parent action)
-> ([MisoString] -> MisoString)
-> [MisoString]
-> View parent action
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [MisoString] -> MisoString
MS.concat
-----------------------------------------------------------------------------
-- | `TextRaw` creation. Don't use directly
textRaw :: MisoString -> View parent action
textRaw :: forall model action. MisoString -> View model action
textRaw = MisoString -> View parent action
forall model action. MisoString -> View model action
VTextRaw
-----------------------------------------------------------------------------
-- | Type used for React-like "props" functionality. This is used to
-- to bind parent model changes to the child model, or vice versa.
--
-- The difference between miso and React here is that miso is
-- synchronizing model states of Components declaratively (outside of the
-- view). In React "props" are used in the view code.
--
-- <https://react.dev/learn/passing-props-to-a-component>
--
-- This can be thought of as establishing an "edge" in the 'Component' graph,
-- whereby events cause model change synchronization to "ripple" or "pulsate"
-- through the views. The "reactivity" of the graph is constructed manually
-- by the end-user, using the edge primitives `-->`, `<--`, `<-->` (reactive combinators).
--
-- This can also be thought of as a "Wire" (from `netwire`) for reactive
-- variable synchronization, except done at the granularity specified by the `Lens`.
--
-- @
--
-- main :: IO ()
-- main = run app { bindings = [ parentLens --> childLens ] }
--
-- @
--
-- @since 1.9.0.0
data Binding parent model = forall a . Binding Direction (Lens parent a) (Lens model a)
-----------------------------------------------------------------------------
-- | Smart constructor for 'Binding'
--
-- @since 1.9.0.0
bind
  :: Direction
  -> Lens parent a
  -> Lens model a
  -> Binding parent model
bind :: forall parent a model.
Direction -> Lens parent a -> Lens model a -> Binding parent model
bind = Direction -> Lens parent a -> Lens model a -> Binding parent model
forall parent model a.
Direction -> Lens parent a -> Lens model a -> Binding parent model
Binding
-----------------------------------------------------------------------------
data Direction
  = ParentToChild
  | ChildToParent
  | Bidirectional
  deriving (Int -> Direction -> ShowS
[Direction] -> ShowS
Direction -> String
(Int -> Direction -> ShowS)
-> (Direction -> String)
-> ([Direction] -> ShowS)
-> Show Direction
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Direction -> ShowS
showsPrec :: Int -> Direction -> ShowS
$cshow :: Direction -> String
show :: Direction -> String
$cshowList :: [Direction] -> ShowS
showList :: [Direction] -> ShowS
Show, Direction -> Direction -> Bool
(Direction -> Direction -> Bool)
-> (Direction -> Direction -> Bool) -> Eq Direction
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Direction -> Direction -> Bool
== :: Direction -> Direction -> Bool
$c/= :: Direction -> Direction -> Bool
/= :: Direction -> Direction -> Bool
Eq)
-----------------------------------------------------------------------------
-- | Extract 'Direction' from 'Binding'
getDirection :: Binding parent model -> Direction
getDirection :: forall parent model. Binding parent model -> Direction
getDirection (Binding Direction
dir Lens parent a
_ Lens model a
_) = Direction
dir
-----------------------------------------------------------------------------
-- | Smart constructor for a 'Binding', unidirectionally binds parent to child
--
-- @since 1.9.0.0
(-->) :: Lens parent a -> Lens model a -> Binding parent model
--> :: forall parent a model.
Lens parent a -> Lens model a -> Binding parent model
(-->) = Direction -> Lens parent a -> Lens model a -> Binding parent model
forall parent a model.
Direction -> Lens parent a -> Lens model a -> Binding parent model
bind Direction
ParentToChild
-----------------------------------------------------------------------------
-- | Smart constructor for a 'Binding', unidirectionally binds child to parent
--
-- @since 1.9.0.0
(<--) :: Lens parent a -> Lens model a -> Binding parent model
<-- :: forall parent a model.
Lens parent a -> Lens model a -> Binding parent model
(<--) = Direction -> Lens parent a -> Lens model a -> Binding parent model
forall parent a model.
Direction -> Lens parent a -> Lens model a -> Binding parent model
bind Direction
ChildToParent
-----------------------------------------------------------------------------
-- | Smart constructor for a 'Binding', bidirectionlly binds child to parent
--
-- @since 1.9.0.0
(<-->) :: Lens parent a -> Lens model a -> Binding parent model
<--> :: forall parent a model.
Lens parent a -> Lens model a -> Binding parent model
(<-->) = Direction -> Lens parent a -> Lens model a -> Binding parent model
forall parent a model.
Direction -> Lens parent a -> Lens model a -> Binding parent model
bind Direction
Bidirectional
-----------------------------------------------------------------------------