-- | The modal component sits atop everything else. It darkens the -- | rest of the page and centers a box in which we can put -- | content. Clicking outside of the box will close the modal module Gargantext.Components.Modal where import Prelude (Unit, bind, const, discard, pure, unit, ($)) import Data.Maybe ( maybe ) import Data.Nullable ( Nullable, null ) import DOM.Simple as DOM import DOM.Simple.EventListener ( callback ) import DOM.Simple.Element as Element import DOM.Simple.Event (MouseEvent, target) import DOM.Simple.Document ( document ) import Effect (Effect) import Reactix as R import Reactix.DOM.HTML as H import Gargantext.Utils.Reactix as R2 type Props = ( setVisible :: R2.Setter Boolean ) modal :: Record Props -> Array R.Element -> R.Element modal = R.createElement modalCpt modalCpt :: R.Component Props modalCpt = R.hooksComponent "Modal" cpt where cpt {setVisible} children = do host <- R2.getPortalHost root <- R.useRef null -- used to close when user clicks outside R2.useLayoutEffectOnce $ modalEffect root setVisible pure $ R.createPortal [ H.div { ref: root, className: "modal", data: {toggle: "popover", placement: "right"}} [ H.div { className: "popover-content" } [ H.div { className: "panel panel-default" } [ H.ul { className: "list-group" } children ]]]] host modalEffect :: R.Ref (Nullable DOM.Element) -> R2.Setter Boolean -> Effect (Effect Unit) modalEffect rootRef setVisible = maybe (pure R.nothing) withRoot (R.readNullableRef rootRef) where onScroll = R2.named "hideModalOnScroll" $ callback handler where -- removing this type declaration will unleash the hounds, so don't handler :: MouseEvent -> Effect Unit handler _ = setVisible (const false) withRoot root = do let onClick = clickHandler root DOM.addEventListener document "click" onClick DOM.addEventListener document "scroll" onScroll pure $ do DOM.removeEventListener document "click" onClick DOM.removeEventListener document "scroll" onScroll clickHandler root = R2.named "hideModalOnClickOutside" $ callback handler where -- removing this type declaration will unleash the hounds, so don't handler :: MouseEvent -> Effect Unit handler e = if Element.contains root (target e) then pure unit else setVisible (const false)