packages feed

reflex-dom-contrib-0.1: src/Reflex/Dom/Contrib/Widgets/DynTabs.hs

{-# LANGUAGE ConstraintKinds           #-}
{-# LANGUAGE FlexibleContexts          #-}
{-# LANGUAGE LambdaCase                #-}
{-# LANGUAGE MultiParamTypeClasses     #-}
{-# LANGUAGE MultiWayIf                #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE OverloadedStrings         #-}
{-# LANGUAGE RankNTypes                #-}
{-# LANGUAGE RecordWildCards           #-}
{-# LANGUAGE RecursiveDo               #-}
{-# LANGUAGE ScopedTypeVariables       #-}
{-# LANGUAGE TemplateHaskell           #-}
{-# LANGUAGE TypeFamilies              #-}

{-|

An API for constructing a tab bar where the list of tabs in the bar is
determined dynamically.

-}

module Reflex.Dom.Contrib.Widgets.DynTabs
  ( Tab(..)
  , tabBar
  , tabPane
  , activeHelper
  ) where

------------------------------------------------------------------------------
import qualified Data.Map as M
import           Data.Monoid
import           Reflex
import           Reflex.Dom
------------------------------------------------------------------------------
import           Reflex.Dom.Contrib.Utils
------------------------------------------------------------------------------


------------------------------------------------------------------------------
class Eq tab => Tab m tab where
    tabIndicator :: tab -> m ()


------------------------------------------------------------------------------
tabBar
    :: forall t m tab. (MonadWidget t m, Tab m tab)
    => String
    -> tab
    -- ^ Initial open tab
    -> [tab]
    -> Event t [tab]
    -- ^ Dynamic list of the displayed tabs
    -> Event t tab
    -- ^ Event updating the currently selected tab
    -> m (Dynamic t tab)
tabBar tabClass initialSelected initialTabs tabs curTab = do
    divClass "dyn-tab-bar" $ do
      elAttr "ul" ("class" =: tabClass) $ do
        rec let tabFunc = mapM (mkTab currentTab)
            foo <- widgetHoldHelper tabFunc initialTabs tabs
            let bar :: Event t tab = switch $ fmap leftmost $ current foo
            currentTab <- holdDyn initialSelected $ leftmost [bar, curTab]
        return currentTab


------------------------------------------------------------------------------
mkTab
  :: (MonadWidget t m, Tab m tab)
  => Dynamic t tab
  -> tab
  -> m (Event t tab)
mkTab currentTab t = do
    isSelected <- mapDyn (==t) currentTab
    e <- activeHelper "li" (el "a" $ tabIndicator t) isSelected
    return (t <$ e)


------------------------------------------------------------------------------
tabPane :: (MonadWidget t m, Eq tab) => Dynamic t tab -> tab -> m a -> m a
tabPane currentTab t child = do
    isShown <- mapDyn (\c ->
      if c == t then klass
                else klass <> "style" =: "display: none") currentTab
    elDynAttr "div" isShown child
  where
    klass = "class" =: "dyn-tab-pane"


------------------------------------------------------------------------------
-- | Sets the \"active\" class 
activeHelper
  :: MonadWidget t m
  => String
  -> m ()
  -> Dynamic t Bool
  -> m (Event t ())
activeHelper elName children isSelected = do
    attrs <- forDyn isSelected $ \selected ->
      if selected then "class" =: "active" else M.empty
    (li, _) <- elDynAttr' elName attrs children
    return $ _el_clicked li