{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecursiveDo       #-}

-- | A modal dialogue, eg pop up, for example to confirm an action.
--   Puts code behinds bulma's modal styling: https://bulma.io/documentation/components/modal/
module Reflex.Bulmex.Modal
  ( modal
  , modal'
  , modalClose
  , OnClose
  ) where

import           Control.Lens
import           Control.Monad.Fix
import           Reflex
import           Reflex.Bulmex.Attr
import qualified Reflex.Dom.Builder.Class as Dom
import qualified Reflex.Dom.Widget.Basic  as Dom
import qualified Reflex.Tags              as T


-- | A modal that opens on event and has a cross to close it.
--   m a dictates what's inside the modal.
--   You probably want to use a 'Reflex.Bulmex.Tag.Bulma.box'.
modal ::
     (PostBuild t m, MonadHold t m, MonadFix m, Dom.DomBuilder t m)
  => Event t () -- ^ open trigger
  -> m a -- ^ modal body
  -> m (a, Event t OnClose)
modal = modal' never

-- | A modal that can be opened and closed with events.
--   It also has a cross to close with.
modal' ::
     (PostBuild t m, MonadHold t m, MonadFix m, Dom.DomBuilder t m)
  => Event t () -- ^ close trigger
  -> Event t () -- ^ open trigger
  -> m a -- ^ modal body
  -> m (a, Event t OnClose)
modal' closeEvt openEvt monad =
  modalClose openEvt $ do
    res <- monad
    pure (res, closeEvt)

-- | The most generic modal, It receives an open event.
--   And the inner monad can indicate when to close with a closeEvent.
modalClose ::
     (PostBuild t m, MonadHold t m, MonadFix m, Dom.DomBuilder t m)
  => Event t () -- ^ open trigger
  -> m (a, Event t ()) -- ^ body + close trigger result
  -> m (a, Event t OnClose)
modalClose openEvt monad = do
  rec stateDyn <-
        holdDyn closed (leftmost [opened <$ openEvt, closed <$ result ^. _2])
      result <-
        T.divDynAttr stateDyn $ do
          (backgroundEl, _) <-
            T.divAttr' (classAttr "modal-background") $ Dom.blank
          monadResult <- T.divClass "modal-content" $ monad
          (buttonEl, _) <-
            T.aAttr' (classAttr "modal-close is-large") $ Dom.blank
          let closeClick =
                leftmost
                  [ Dom.domEvent Dom.Click backgroundEl
                  , Dom.domEvent Dom.Click buttonEl
                  , monadResult ^. _2
                  ]
          pure $ (monadResult ^. _1, difference closeClick openEvt)
  pure $ over _2 ((<$) OnClose) result
  where
    closed = classAttr "modal"
    opened = classAttr "modal is-active"

-- | An incation that an event came from closing a modal
data OnClose =
  OnClose