-----------------------------------------------------------------------------
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Miso.Event
-- Copyright   :  (C) 2016-2026 David M. Johnson
-- License     :  BSD3-style (see the file LICENSE)
-- Maintainer  :  David M. Johnson <code@dmj.io>
-- Stability   :  experimental
-- Portability :  non-portable
--
-- DOM event handlers and component lifecycle hooks for 'Miso.Types.View'.
--
-- There are two axes of event handling:
--
-- * __DOM events__ — 'on', 'onCapture', 'onWithOptions': attach JavaScript
--   event listeners to VDOM nodes. Decoded event payloads are dispatched as
--   @action@ values through the MVU loop.
--
-- * __Lifecycle hooks__ — 'onCreated', 'onDestroyed', etc.: fire Haskell
--   callbacks at specific points in a DOM element's mount\/unmount lifecycle.
--
-- See "Miso.Event.Decoder" for building custom t'Decoder' values and
-- "Miso.Event.Types" for structured payload types (@KeyboardEvent@,
-- t'PointerEvent', etc.).
--
----------------------------------------------------------------------------
module Miso.Event
   ( -- *** Smart constructors
     on
   , onMain
   , onCapture
   , onWithOptions
   , onMainWithOptions
   , Phase (..)
   -- *** Lifecycle events
   , onCreated
   , onCreatedWith
   , onBeforeCreated
   , onDestroyed
   , onBeforeDestroyed
   , onBeforeDestroyedWith
    -- *** Exports
   , module Miso.Event.Decoder
   , module Miso.Event.Types
   ) where
-----------------------------------------------------------------------------
import           Control.Monad (when)
import qualified Data.Map.Strict as M
import qualified Data.IntMap.Strict as IM
import           Data.IORef
import           Miso.JSON (parseEither)
-----------------------------------------------------------------------------
import           Miso.DSL
import           Miso.Event.Decoder
import           Miso.Event.Types
import qualified Miso.FFI.Internal as FFI
import           Miso.Types (LogLevel(..), DOMRef, VTree(..), EventHandler(..), Attribute(..))
import           Miso.Runtime
import           Miso.String (MisoString, ms)
-----------------------------------------------------------------------------
-- | Like 'on' but meant to used with the "Miso.Native" namespace.
--
-- @
-- view_ [ event $ static ('onMain' "tap" emptyDecoder ) ] [ text_ \"+\" ]
-- @
--
-- @since 1.13.0.0
onMain :: MisoString
   -- ^ DOM event name (e.g. @\"click\"@, @\"input\"@)
   -> Decoder result
   -- ^ How to extract a Haskell value from the browser event object
   -> (result -> model -> DOMRef -> action)
   -- ^ Converts the decoded payload and the element's DOM reference to an @action@
   -> EventHandler model action
onMain :: forall result model action.
MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> EventHandler model action
onMain = Phase
-> Options
-> MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> EventHandler model action
forall result model action.
Phase
-> Options
-> MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> EventHandler model action
onMainWithOptions Phase
BUBBLE Options
defaultOptions
-----------------------------------------------------------------------------
-- | Attach a bubble-phase event handler to a VDOM node.
-- Convenience wrapper for @'onWithOptions' 'BUBBLE' 'defaultOptions'@.
--
-- The decoded event payload is converted to an @action@ by @toAction@ and
-- dispatched into the component's @update@ function.
--
-- @
-- let clickHandler = on \"click\" emptyDecoder $ \\() _ -> MyAction
-- in button_ [ clickHandler, class_ \"add\" ] [ text_ \"+\" ]
-- @
--
on :: MisoString
   -- ^ DOM event name (e.g. @\"click\"@, @\"input\"@)
   -> Decoder result
   -- ^ How to extract a Haskell value from the browser event object
   -> (result -> model -> DOMRef -> action)
   -- ^ Converts the decoded payload and the element's DOM reference to an @action@
   -> Attribute model action
on :: forall result model action.
MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> Attribute model action
on = Phase
-> Options
-> MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> Attribute model action
forall result model action.
Phase
-> Options
-> MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> Attribute model action
onWithOptions Phase
BUBBLE Options
defaultOptions
-----------------------------------------------------------------------------
-- | Attach a capture-phase event handler to a VDOM node.
-- Convenience wrapper for @'onWithOptions' 'CAPTURE' 'defaultOptions'@.
--
-- Events in the capture phase propagate from the document root down to the
-- target element, before any bubble-phase handlers run.
--
-- @
-- let captureClick = onCapture \"click\" emptyDecoder $ \\() _ -> MyAction
-- in button_ [ captureClick ] [ text_ \"capture me\" ]
-- @
--
onCapture
   :: MisoString
   -- ^ DOM event name (e.g. @\"click\"@)
   -> Decoder result
   -- ^ How to extract a Haskell value from the browser event object
   -> (result -> model -> DOMRef -> action)
   -- ^ Converts the decoded payload and the element's DOM reference to an @action@
   -> Attribute model action
onCapture :: forall result model action.
MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> Attribute model action
onCapture = Phase
-> Options
-> MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> Attribute model action
forall result model action.
Phase
-> Options
-> MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> Attribute model action
onWithOptions Phase
CAPTURE Options
defaultOptions
-----------------------------------------------------------------------------
-- | Mark an event handler to be dispatched on the Lynx __main thread__ (@MTS@)
-- rather than the background thread. This is the analog of Lynx's
-- @main-thread:bind@ prefix, and is decided __per handler__ — so @tap@ can be a
-- main-thread handler on one element and a background handler on another.
--
-- A main-thread handler runs imperatively on the MTS (no VDOM diff, no repaint);
-- pair it with a @*With@ combinator to receive the target 'DOMRef' and mutate it
-- via "Miso.Native.MainThread". No-op on the browser\/WASM runtime.
--
-- @
-- view_ [ event (static (mainThread (onTapWith Grow))) ] children
-- @
--
-- @since 1.13.0.0
onMainWithOptions
  :: Phase
  -- ^ Event propagation phase: 'BUBBLE' (default) or 'CAPTURE'
  -> Options
  -- ^ Propagation options (@preventDefault@, @stopPropagation@)
  -> MisoString
  -- ^ DOM event name (e.g. @\"click\"@, @\"keydown\"@)
  -> Decoder result
  -- ^ How to extract a Haskell value from the browser event object
  -> (result -> model -> DOMRef -> action)
  -- ^ Converts the decoded payload and the element's DOM reference to an @action@
  -> EventHandler model action
onMainWithOptions :: forall result model action.
Phase
-> Options
-> MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> EventHandler model action
onMainWithOptions Phase
phase Options
opts MisoString
name Decoder result
decoder result -> model -> DOMRef -> action
conversion =
  EventHandler
    { eventHandlerInstall :: model -> Sink action -> VTree -> LogLevel -> Events -> IO ()
eventHandlerInstall = \model
m Sink action
snk VTree
tree LogLevel
ll Events
events ->
        case Phase
-> Options
-> MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> Attribute model action
forall result model action.
Phase
-> Options
-> MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> Attribute model action
onWithOptions Phase
phase Options
opts MisoString
name Decoder result
decoder result -> model -> DOMRef -> action
conversion of
          On model -> Sink action -> VTree -> LogLevel -> Events -> IO ()
cb -> do
            MisoString -> Bool -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
"pendingMainThread" Bool
True (Object -> IO ()) -> IO Object -> IO ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< VTree -> IO Object
forall a. ToObject a => a -> IO Object
toObject VTree
tree
            model -> Sink action -> VTree -> LogLevel -> Events -> IO ()
cb model
m Sink action
snk VTree
tree LogLevel
ll Events
events
          Attribute model action
_ ->
            [Char] -> IO ()
forall a. HasCallStack => [Char] -> a
error [Char]
"onMainWithOptions: impossible"
    , eventHandlerDecoder :: Decoder result
eventHandlerDecoder = Decoder result
decoder
    , eventHandlerConvert :: result -> model -> DOMRef -> action
eventHandlerConvert = result -> model -> DOMRef -> action
conversion
    }
-----------------------------------------------------------------------------
-- | Attach an event handler with explicit phase and propagation options.
--
-- * @phase@    — 'BUBBLE' (default) or 'CAPTURE': which DOM propagation phase
--   the listener is registered on.
-- * @options@  — 'defaultOptions' or a custom t'Options' value: controls
--   @preventDefault@ and @stopPropagation@ behaviour.
-- * @eventName@ — the DOM event name, e.g. @\"click\"@, @\"keydown\"@.
-- * @decoder@  — a t'Decoder' that extracts relevant fields from the JS event object.
-- * @toAction@ — maps the decoded payload and the element's 'DOMRef' to an @action@.
--
-- @
-- let clickHandler = onWithOptions BUBBLE defaultOptions \"click\" emptyDecoder $ \\() _ -> Action
-- in button_ [ clickHandler, class_ \"add\" ] [ text_ \"+\" ]
-- @
--
onWithOptions
  :: Phase
  -- ^ Event propagation phase: 'BUBBLE' (default) or 'CAPTURE'
  -> Options
  -- ^ Propagation options (@preventDefault@, @stopPropagation@)
  -> MisoString
  -- ^ DOM event name (e.g. @\"click\"@, @\"keydown\"@)
  -> Decoder result
  -- ^ How to extract a Haskell value from the browser event object
  -> (result -> model -> DOMRef -> action)
  -- ^ Converts the decoded payload and the element's DOM reference to an @action@
  -> Attribute model action
onWithOptions :: forall result model action.
Phase
-> Options
-> MisoString
-> Decoder result
-> (result -> model -> DOMRef -> action)
-> Attribute model action
onWithOptions Phase
phase Options
options MisoString
eventName Decoder{DecodeTarget
Value -> Parser result
decoder :: Value -> Parser result
decodeAt :: DecodeTarget
decodeAt :: forall a. Decoder a -> DecodeTarget
decoder :: forall a. Decoder a -> Value -> Parser a
..} result -> model -> DOMRef -> action
toAction =
  (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall model action.
(model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
On ((model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
 -> Attribute model action)
-> (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall a b. (a -> b) -> a -> b
$ \model
_model Sink action
sink (VTree Object
n) LogLevel
logLevel Events
events -> do
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (LogLevel
logLevel LogLevel -> LogLevel -> Bool
forall a. Eq a => a -> a -> Bool
== LogLevel
DebugAll Bool -> Bool -> Bool
|| LogLevel
logLevel LogLevel -> LogLevel -> Bool
forall a. Eq a => a -> a -> Bool
== LogLevel
DebugEvents) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
      case MisoString -> Events -> Maybe Phase
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup MisoString
eventName Events
events of
        Maybe Phase
Nothing ->
            MisoString -> IO ()
FFI.consoleError (MisoString -> IO ()) -> MisoString -> IO ()
forall a b. (a -> b) -> a -> b
$ [MisoString] -> MisoString
forall a. Monoid a => [a] -> a
mconcat
              [ MisoString
"Event \""
              , MisoString
eventName
              , MisoString
"\" is not being listened on. To use this event, "
              , MisoString
"add to the 'events' Map in Component"
              ]
        Maybe Phase
_ -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    DOMRef
eventsVal <-
      MisoString -> Object -> IO DOMRef
forall o. ToObject o => MisoString -> o -> IO DOMRef
getProp MisoString
"events" Object
n
    DOMRef
eventObj <-
      case Phase
phase of
        Phase
CAPTURE -> MisoString -> Object -> IO DOMRef
forall o. ToObject o => MisoString -> o -> IO DOMRef
getProp MisoString
"captures" (DOMRef -> Object
Object DOMRef
eventsVal)
        Phase
BUBBLE -> MisoString -> Object -> IO DOMRef
forall o. ToObject o => MisoString -> o -> IO DOMRef
getProp MisoString
"bubbles" (DOMRef -> Object
Object DOMRef
eventsVal)
    eventHandlerObject :: Object
eventHandlerObject@(Object DOMRef
eo) <- IO Object
create
    DOMRef
jsOptions <- Options -> IO DOMRef
forall a. ToJSVal a => a -> IO DOMRef
toJSVal Options
options
    DOMRef
decodeAtVal <- DecodeTarget -> IO DOMRef
forall a. ToJSVal a => a -> IO DOMRef
toJSVal DecodeTarget
decodeAt
    DOMRef
cb <- (DOMRef -> DOMRef -> IO ()) -> IO DOMRef
FFI.asyncCallback2 ((DOMRef -> DOMRef -> IO ()) -> IO DOMRef)
-> (DOMRef -> DOMRef -> IO ()) -> IO DOMRef
forall a b. (a -> b) -> a -> b
$ \DOMRef
e DOMRef
domRef -> do
        Just Value
v <- DOMRef -> IO (Maybe Value)
forall a. FromJSVal a => DOMRef -> IO (Maybe a)
fromJSVal (DOMRef -> IO (Maybe Value)) -> IO DOMRef -> IO (Maybe Value)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< DOMRef -> DOMRef -> IO DOMRef
FFI.eventJSON DOMRef
decodeAtVal DOMRef
e
        case (Value -> Parser result) -> Value -> Either MisoString result
forall a b. (a -> Parser b) -> a -> Either MisoString b
parseEither Value -> Parser result
decoder Value
v of
          Left MisoString
msg -> MisoString -> IO ()
FFI.consoleError (MisoString
"[EVENT DECODE ERROR]: " MisoString -> MisoString -> MisoString
forall a. Semigroup a => a -> a -> a
<> MisoString -> MisoString
forall str. ToMisoString str => str -> MisoString
ms MisoString
msg)
          Right result
event -> do
            Key
vcompId <- DOMRef -> IO Key
forall a. FromJSVal a => DOMRef -> IO a
fromJSValUnchecked (DOMRef -> IO Key) -> IO DOMRef -> IO Key
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< MisoString -> Object -> IO DOMRef
forall o. ToObject o => MisoString -> o -> IO DOMRef
getProp MisoString
"pendingComponentId" Object
n
            Key
-> IntMap (ComponentState Any Any model Any)
-> Maybe (ComponentState Any Any model Any)
forall a. Key -> IntMap a -> Maybe a
IM.lookup Key
vcompId (IntMap (ComponentState Any Any model Any)
 -> Maybe (ComponentState Any Any model Any))
-> IO (IntMap (ComponentState Any Any model Any))
-> IO (Maybe (ComponentState Any Any model Any))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef (IntMap (ComponentState Any Any model Any))
-> IO (IntMap (ComponentState Any Any model Any))
forall a. IORef a -> IO a
readIORef IORef (IntMap (ComponentState Any Any model Any))
forall context props model action.
IORef (IntMap (ComponentState context props model action))
components IO (Maybe (ComponentState Any Any model Any))
-> (Maybe (ComponentState Any Any model Any) -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
              Maybe (ComponentState Any Any model Any)
Nothing ->
                MisoString -> IO ()
FFI.consoleError (MisoString
"[COMPONENT]: No component found at ID: " MisoString -> MisoString -> MisoString
forall a. Semigroup a => a -> a -> a
<> Key -> MisoString
forall str. ToMisoString str => str -> MisoString
ms Key
vcompId)
              Just ComponentState {model
Bool
Key
[DOMRef]
Maybe StaticKey
Maybe Key
Any
IORef (Map MisoString ThreadId)
IORef VTree
Events
Map MisoString (Value -> IO ())
ComponentIds
DOMRef
model -> IO ()
model -> model -> Bool
Sink Any
Any -> Sink Any
Seq Any -> model -> Any -> Any -> (model, [Schedule Any Any])
Value -> Maybe Any
_componentId :: Key
_componentKey :: Maybe Key
_componentStaticKey :: Maybe StaticKey
_componentParentId :: Key
_componentProps :: Any
_prevComponentProps :: Any
_componentSubThreads :: IORef (Map MisoString ThreadId)
_componentDOMRef :: DOMRef
_componentVTree :: IORef VTree
_componentSink :: Sink Any
_componentPostEffect :: Sink Any
_componentModel :: model
_componentScripts :: [DOMRef]
_componentEvents :: Events
_componentUseContext :: Bool
_componentMailbox :: Value -> Maybe Any
_componentDraw :: model -> IO ()
_componentHydrate :: model -> IO ()
_componentPropsPhase :: Any -> Sink Any
_componentModelDirty :: model -> model -> Bool
_componentApplyActions :: Seq Any -> model -> Any -> Any -> (model, [Schedule Any Any])
_componentTopics :: Map MisoString (Value -> IO ())
_componentChildren :: ComponentIds
_componentChildren :: forall context props model action.
ComponentState context props model action -> ComponentIds
_componentTopics :: forall context props model action.
ComponentState context props model action
-> Map MisoString (Value -> IO ())
_componentApplyActions :: forall context props model action.
ComponentState context props model action
-> Seq action
-> model
-> props
-> context
-> (model, [Schedule context action])
_componentModelDirty :: forall context props model action.
ComponentState context props model action -> model -> model -> Bool
_componentPropsPhase :: forall context props model action.
ComponentState context props model action
-> props -> props -> IO ()
_componentHydrate :: forall context props model action.
ComponentState context props model action -> model -> IO ()
_componentDraw :: forall context props model action.
ComponentState context props model action -> model -> IO ()
_componentMailbox :: forall context props model action.
ComponentState context props model action -> Value -> Maybe action
_componentUseContext :: forall context props model action.
ComponentState context props model action -> Bool
_componentEvents :: forall context props model action.
ComponentState context props model action -> Events
_componentScripts :: forall context props model action.
ComponentState context props model action -> [DOMRef]
_componentModel :: forall context props model action.
ComponentState context props model action -> model
_componentPostEffect :: forall context props model action.
ComponentState context props model action -> Sink action
_componentSink :: forall context props model action.
ComponentState context props model action -> Sink action
_componentVTree :: forall context props model action.
ComponentState context props model action -> IORef VTree
_componentDOMRef :: forall context props model action.
ComponentState context props model action -> DOMRef
_componentSubThreads :: forall context props model action.
ComponentState context props model action
-> IORef (Map MisoString ThreadId)
_prevComponentProps :: forall context props model action.
ComponentState context props model action -> props
_componentProps :: forall context props model action.
ComponentState context props model action -> props
_componentParentId :: forall context props model action.
ComponentState context props model action -> Key
_componentStaticKey :: forall context props model action.
ComponentState context props model action -> Maybe StaticKey
_componentKey :: forall context props model action.
ComponentState context props model action -> Maybe Key
_componentId :: forall context props model action.
ComponentState context props model action -> Key
..} ->
                Sink action
sink (result -> model -> DOMRef -> action
toAction result
event model
_componentModel DOMRef
domRef)
    -- The runtime frees this callback when the vtree that owns it is
    -- replaced; see Note [Freeing event handler callbacks] in "Miso.Runtime".
    DOMRef -> IO ()
registerEventHandler DOMRef
cb
    MisoString -> DOMRef -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
"runEvent" DOMRef
cb Object
eventHandlerObject
    MisoString -> DOMRef -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
"options" DOMRef
jsOptions Object
eventHandlerObject
    -- Only 'mainThread'-marked handlers carry their 'StaticKey' \/ @ComponentId@
    -- (stashed on the node by 'setAttrs') onto the per-event object. That is what
    -- puts them in the node's @eventKeys@, telling the native delegator to
    -- dispatch this handler on the main thread. Unmarked handlers (and the
    -- browser\/WASM runtime) leave these off and delegate to the background.
    DOMRef
pendingMT <- MisoString -> Object -> IO DOMRef
forall o. ToObject o => MisoString -> o -> IO DOMRef
getProp MisoString
"pendingMainThread" Object
n
    Maybe Bool
isMainThread <- DOMRef -> IO (Maybe Bool)
forall a. FromJSVal a => DOMRef -> IO (Maybe a)
fromJSVal DOMRef
pendingMT :: IO (Maybe Bool)
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Maybe Bool
isMainThread Maybe Bool -> Maybe Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
      DOMRef
pendingKey <- MisoString -> Object -> IO DOMRef
forall o. ToObject o => MisoString -> o -> IO DOMRef
getProp MisoString
"pendingStaticKey" Object
n
      Maybe MisoString
mKey <- DOMRef -> IO (Maybe MisoString)
forall a. FromJSVal a => DOMRef -> IO (Maybe a)
fromJSVal DOMRef
pendingKey :: IO (Maybe MisoString)
      IO () -> (MisoString -> IO ()) -> Maybe MisoString -> IO ()
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (() -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()) (\MisoString
k -> MisoString -> MisoString -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
"staticKey" (MisoString
k :: MisoString) Object
eventHandlerObject) Maybe MisoString
mKey
      DOMRef
pendingCid <- MisoString -> Object -> IO DOMRef
forall o. ToObject o => MisoString -> o -> IO DOMRef
getProp MisoString
"pendingComponentId" Object
n
      Maybe Key
mCid <- DOMRef -> IO (Maybe Key)
forall a. FromJSVal a => DOMRef -> IO (Maybe a)
fromJSVal DOMRef
pendingCid :: IO (Maybe Int)
      IO () -> (Key -> IO ()) -> Maybe Key -> IO ()
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (() -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()) (\Key
c -> MisoString -> Key -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
"componentId" (Key
c :: Int) Object
eventHandlerObject) Maybe Key
mCid
    MisoString -> DOMRef -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
eventName DOMRef
eo (DOMRef -> Object
Object DOMRef
eventObj)
    -- The handler object is now reachable from the node; release the scratch
    -- handles. @decodeAtVal@, @cb@ and @n@ are captured by the callback and
    -- must stay alive. See Note [Freeing VTree handles] in "Miso.Runtime".
    (DOMRef -> IO ()) -> [DOMRef] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ DOMRef -> IO ()
freeJSVal [DOMRef
eventsVal, DOMRef
eventObj, DOMRef
eo, DOMRef
jsOptions, DOMRef
pendingMT]
-----------------------------------------------------------------------------
-- | Fire an action immediately after the DOM element is inserted into the document.
--
-- Use this to trigger imperative setup (focus, measurements, third-party widget
-- initialisation) that requires the element to be live in the page.
--
-- @since 1.9.0.0
--
onCreated
  :: action
  -- ^ Action to dispatch after the element is inserted into the DOM
  -> Attribute model action
onCreated :: forall action model. action -> Attribute model action
onCreated action
action =
  (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall model action.
(model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
On ((model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
 -> Attribute model action)
-> (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall a b. (a -> b) -> a -> b
$ \model
_model Sink action
sink (VTree Object
object) LogLevel
_ Events
_ -> do
    DOMRef
callback <- IO () -> IO DOMRef
FFI.syncCallback (Sink action
sink action
action)
    MisoString -> DOMRef -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
"onCreated" DOMRef
callback Object
object
-----------------------------------------------------------------------------
-- | Like 'onCreated' but also receives the element's 'DOMRef'.
--
-- Useful when you need to store or forward the raw DOM node to a JS library.
--
-- @since 1.9.0.0
--
onCreatedWith
  :: (DOMRef -> action)
  -- ^ Callback receiving the element's 'DOMRef' after it is inserted into the DOM
  -> Attribute model action
onCreatedWith :: forall action model. (DOMRef -> action) -> Attribute model action
onCreatedWith DOMRef -> action
action =
  (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall model action.
(model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
On ((model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
 -> Attribute model action)
-> (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall a b. (a -> b) -> a -> b
$ \model
_model Sink action
sink (VTree Object
object) LogLevel
_ Events
_ -> do
    DOMRef
callback <- (DOMRef -> IO ()) -> IO DOMRef
FFI.syncCallback1 (Sink action
sink Sink action -> (DOMRef -> action) -> DOMRef -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DOMRef -> action
action)
    MisoString -> DOMRef -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
"onCreated" DOMRef
callback Object
object
-----------------------------------------------------------------------------
-- | Fire an action immediately after the DOM element is removed from the document.
--
-- The element has already been detached from the DOM when this fires.
--
-- @since 1.9.0.0
--
onDestroyed
  :: action
  -- ^ Action to dispatch after the element is removed from the DOM
  -> Attribute model action
onDestroyed :: forall action model. action -> Attribute model action
onDestroyed action
action =
  (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall model action.
(model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
On ((model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
 -> Attribute model action)
-> (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall a b. (a -> b) -> a -> b
$ \model
_model Sink action
sink (VTree Object
object) LogLevel
_ Events
_ -> do
    DOMRef
callback <- IO () -> IO DOMRef
FFI.syncCallback (Sink action
sink action
action)
    MisoString -> DOMRef -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
"onDestroyed" DOMRef
callback Object
object
-----------------------------------------------------------------------------
-- | Fire an action just before the DOM element is removed from the document.
--
-- The element is still present in the DOM when this fires, making it suitable
-- for teardown logic (cancel animations, disconnect observers, etc.).
--
-- @since 1.9.0.0
--
onBeforeDestroyed
  :: action
  -- ^ Action to dispatch just before the element is removed from the DOM
  -> Attribute model action
onBeforeDestroyed :: forall action model. action -> Attribute model action
onBeforeDestroyed action
action =
  (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall model action.
(model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
On ((model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
 -> Attribute model action)
-> (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall a b. (a -> b) -> a -> b
$ \model
_model Sink action
sink (VTree Object
object) LogLevel
_ Events
_ -> do
    DOMRef
callback <- IO () -> IO DOMRef
FFI.syncCallback (Sink action
sink action
action)
    MisoString -> DOMRef -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
"onBeforeDestroyed" DOMRef
callback Object
object
-----------------------------------------------------------------------------
-- | Like 'onBeforeDestroyed' but also receives the element's 'DOMRef'.
--
-- @since 1.9.0.0
--
onBeforeDestroyedWith
  :: (DOMRef -> action)
  -- ^ Callback receiving the element's 'DOMRef' just before it is removed from the DOM
  -> Attribute model action
onBeforeDestroyedWith :: forall action model. (DOMRef -> action) -> Attribute model action
onBeforeDestroyedWith DOMRef -> action
action =
  (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall model action.
(model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
On ((model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
 -> Attribute model action)
-> (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall a b. (a -> b) -> a -> b
$ \model
_model Sink action
sink (VTree Object
object) LogLevel
_ Events
_ -> do
    DOMRef
callback <- (DOMRef -> IO ()) -> IO DOMRef
FFI.syncCallback1 (Sink action
sink Sink action -> (DOMRef -> action) -> DOMRef -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DOMRef -> action
action)
    MisoString -> DOMRef -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
"onBeforeDestroyed" DOMRef
callback Object
object
-----------------------------------------------------------------------------
-- | Fire an action just before the DOM element is inserted into the document.
--
-- The element has been constructed but is not yet attached to the live DOM when
-- this fires.
--
-- @since 1.9.0.0
--
onBeforeCreated
  :: action
  -- ^ Action to dispatch just before the element is inserted into the DOM
  -> Attribute model action
onBeforeCreated :: forall action model. action -> Attribute model action
onBeforeCreated action
action =
  (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall model action.
(model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
On ((model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
 -> Attribute model action)
-> (model -> Sink action -> VTree -> LogLevel -> Events -> IO ())
-> Attribute model action
forall a b. (a -> b) -> a -> b
$ \model
_model Sink action
sink (VTree Object
object) LogLevel
_ Events
_ -> do
    DOMRef
callback <- IO () -> IO DOMRef
FFI.syncCallback (Sink action
sink action
action)
    MisoString -> DOMRef -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
FFI.set MisoString
"onBeforeCreated" DOMRef
callback Object
object
-----------------------------------------------------------------------------