packages feed

bulmex 2.1.0 → 3.0.0

raw patch · 10 files changed

+186/−47 lines, 10 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Reflex.Bulmex.Form.FormTypes: SpinRest :: SpinState
- Reflex.Bulmex.Form.FormTypes: Spinning :: SpinState
- Reflex.Bulmex.Form.FormTypes: data SpinState
- Reflex.Bulmex.Form.FormTypes: instance GHC.Base.Monoid Reflex.Bulmex.Form.FormTypes.SpinState
- Reflex.Bulmex.Form.FormTypes: instance GHC.Base.Semigroup Reflex.Bulmex.Form.FormTypes.SpinState
- Reflex.Bulmex.Form.FormTypes: instance GHC.Show.Show Reflex.Bulmex.Form.FormTypes.SpinState
+ Reflex.Bulmex.Form.FormTypes: FormStateRest :: FormState
+ Reflex.Bulmex.Form.FormTypes: FormStateSpinning :: FormState
+ Reflex.Bulmex.Form.FormTypes: data FormState
+ Reflex.Bulmex.Form.FormTypes: instance GHC.Base.Monoid Reflex.Bulmex.Form.FormTypes.FormState
+ Reflex.Bulmex.Form.FormTypes: instance GHC.Base.Semigroup Reflex.Bulmex.Form.FormTypes.FormState
+ Reflex.Bulmex.Form.FormTypes: instance GHC.Show.Show Reflex.Bulmex.Form.FormTypes.FormState
+ Reflex.Bulmex.Input.Debounce: InputAborted :: InputStates
+ Reflex.Bulmex.Input.Debounce: InputBuffered :: InputStates
+ Reflex.Bulmex.Input.Debounce: InputInitial :: InputStates
+ Reflex.Bulmex.Input.Debounce: InputProcessed :: InputStates
+ Reflex.Bulmex.Input.Debounce: InputStarted :: InputStates
+ Reflex.Bulmex.Input.Debounce: data InputStates
+ Reflex.Bulmex.Input.Debounce: defStateAttr :: InputStates -> Text
+ Reflex.Bulmex.Input.Debounce: withInput :: (PostBuild t m, MonadFix m, MonadHold t m) => (Event t inputEvt -> m (Event t inputEvt)) -> (result -> Bool) -> (Event t result -> Dynamic t InputStates -> m (actArgs, Event t inputEvt, finalRes)) -> (actArgs -> Event t inputEvt -> m (Event t result)) -> m (Event t result, finalRes)
+ Reflex.Bulmex.Input.Debounce: withInputDebounceEvt :: (PostBuild t m, MonadFix m, MonadHold t m, TriggerEvent t m, MonadIO (Performable m), PerformEvent t m) => NominalDiffTime -> (result -> Bool) -> (Dynamic t InputStates -> m (b, Event t inputEvt)) -> (b -> Event t inputEvt -> m (Event t result)) -> m (Event t result, b)
- Reflex.Bulmex.Form: aSpinButtonClass :: (DomBuilder t m, PostBuild t m) => Text -> Dynamic t SpinState -> m () -> m (Event t ())
+ Reflex.Bulmex.Form: aSpinButtonClass :: (DomBuilder t m, PostBuild t m) => Text -> Dynamic t FormState -> m () -> m (Event t ())
- Reflex.Bulmex.Form: actionForm :: (DomBuilder t m, MonadHold t m, MonadFix m) => (a -> Event t () -> m (Event t b)) -> (Event t b -> Dynamic t SpinState -> m (a, Event t FormAction, c)) -> m c
+ Reflex.Bulmex.Form: actionForm :: (DomBuilder t m, MonadHold t m, MonadFix m) => (a -> Event t () -> m (Event t b)) -> (Event t b -> Dynamic t FormState -> m (a, Event t FormAction, c)) -> m c
- Reflex.Bulmex.Form: loadAttr :: SpinState -> AttrMap
+ Reflex.Bulmex.Form: loadAttr :: FormState -> AttrMap
- Reflex.Bulmex.Form: spinWidget :: (DomBuilder t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadHold t m, MonadFix m) => (Dynamic t SpinState -> m (Event t ())) -> (Event t () -> m (Event t b)) -> m (Event t b)
+ Reflex.Bulmex.Form: spinWidget :: (DomBuilder t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadHold t m, MonadFix m) => (Dynamic t FormState -> m (Event t ())) -> (Event t () -> m (Event t b)) -> m (Event t b)

Files

bulmex.cabal view
@@ -4,12 +4,12 @@ -- -- see: https://github.com/sol/hpack ----- hash: d67294831c6d62f3268fe8a5d7abc24d2271686c6842d096537e2fdda8d7a170+-- hash: 437fddec128f807a107380e8703fad0e32c13b707a2252431b9ac45157b6de50  name:           bulmex-version:        2.1.0+version:        3.0.0 synopsis:       Reflex infused with bulma (css)-description:    Reflex infused with bulma, With a bunch of helper functions for making server side rendering easier. See related [blogpost](https://jappieklooster.nl/reflex-server-side-html-rendering.html).+description:    Reflex infused with bulma, [Implements](https://hackage.haskell.org/package/bulmex/docs/Reflex-Bulmex-Modal.html) the [modal](https://bulma.io/documentation/components/modal/) for example. Also has helper functions for common tasks. such as making server side rendering easier. See related [blogpost](https://jappieklooster.nl/reflex-server-side-html-rendering.html). category:       Web homepage:       https://github.com/jappeace/bulmex#readme bug-reports:    https://github.com/jappeace/bulmex/issues@@ -35,6 +35,7 @@       Reflex.Bulmex.Form.FormTypes       Reflex.Bulmex.Html       Reflex.Bulmex.Input.Basic+      Reflex.Bulmex.Input.Debounce       Reflex.Bulmex.Input.Polymorphic       Reflex.Bulmex.Load       Reflex.Bulmex.Modal@@ -81,6 +82,7 @@       Reflex.Bulmex.Form.FormTypes       Reflex.Bulmex.Html       Reflex.Bulmex.Input.Basic+      Reflex.Bulmex.Input.Debounce       Reflex.Bulmex.Input.Polymorphic       Reflex.Bulmex.Load       Reflex.Bulmex.Modal
src/Reflex/Bulmex/Event.hs view
@@ -3,7 +3,24 @@ {-# LANGUAGE RankNTypes        #-}  -- | Helper functions for dealing with events-module Reflex.Bulmex.Event where+module Reflex.Bulmex.Event+  ( eventJoin+  , switchTup+  -- * Hold+  , holdEvent+  , holdEvent_+  , holdAfter+  -- * Flash+  , flash+  , flash'+  -- * Display+  , evtText+  -- * Gate+  , noNothing+  , gatePrism+  , blockFalse+  )+  where  import           Control.Applicative      (empty) import           Control.Lens
src/Reflex/Bulmex/Form.hs view
@@ -10,8 +10,8 @@   ( actionForm   , form   -- * Spin-  , withSpinDyn   , spinWidget+  , withSpinDyn   , aSpinButtonClass   , loadAttr   -- * Types@@ -33,13 +33,27 @@ import qualified Reflex.Tags                  as T import qualified Web.KeyCode                  as Dom --- | This will make send on enter work for a form.---   Gives a spinstate to a form and hooks up onenter press to it---   use actReqButton for sane defaults+-- | This function mimics an arbitrary html form in control flow.+--   The first argument is the action function with arbitrary input a+--   and a trigger event.+--   This returns another event in a monadic context.+--+--   > action="(a -> Event t () -> m (Event t b))"+--+--   This perfectly aligns with servant-reflex.+--   The produced event is given to the form, the second argument.+--   which is the form body:+--+--   > <form>(Event t b -> Dynamic t FormState -> m (a, Event t FormAction, c))</form>+--+--   and also includes the 'FormState',+--   indicating if we're executing the action or not.+--   The form has to return 3 values, the information for the action function,+--   The 'FormAction' event that controls the form, and the third is a return value for the form. actionForm ::      (Dom.DomBuilder t m, MonadHold t m, MonadFix m)-  => (a -> Event t () -> m (Event t b))-  -> (Event t b -> Dynamic t SpinState -> m (a, Event t FormAction, c))+  => (a -> Event t () -> m (Event t b)) -- ^ Action function+  -> (Event t b -> Dynamic t FormState -> m (a, Event t FormAction, c)) -- ^ form body   -> m c actionForm actF monM =   form $ \onEnter -> do@@ -59,11 +73,14 @@      (Reflex t, MonadHold t m, MonadFix m)   => Event t ()   -> Event t ()-  -> m (Dynamic t SpinState)+  -> m (Dynamic t FormState) spinState start stop =-  accumDyn (const id) SpinRest $ leftmost [Spinning <$ start, SpinRest <$ stop]+  accumDyn (const id) FormStateRest $ leftmost [FormStateSpinning <$ start, FormStateRest <$ stop] --- | This looks a lot like `withDebounceEvtReq`, maybe a better abstraction is possible?+-- This looks a lot like `withDebounceEvtReq`, maybe a better abstraction is possible?+-- | A more general use of the spinstate.+--   The first argument is the widget that can indicate when to execute+--   the second function. It will be made aware of the 'FormState'. spinWidget ::      ( Dom.DomBuilder t m      , PerformEvent t m@@ -72,8 +89,8 @@      , MonadHold t m      , MonadFix m      )-  => (Dynamic t SpinState -> m (Event t ()))-  -> (Event t () -> m (Event t b))+  => (Dynamic t FormState -> m (Event t ())) -- ^ Widget body+  -> (Event t () -> m (Event t b)) -- ^ Trigger function   -> m (Event t b) spinWidget widgetF eventHandlr = do   rec onClick <- widgetF dynamicClass@@ -83,9 +100,9 @@       dynamicClass <- spinState onClick $ setClassOnReq <> setClassafterReq   pure onRequest -loadAttr :: SpinState -> AttrMap-loadAttr SpinRest = mempty-loadAttr Spinning = Map.fromList [("class", "is-loading"), ("disabled", "1")]+loadAttr :: FormState -> AttrMap+loadAttr FormStateRest = mempty+loadAttr FormStateSpinning = Map.fromList [("class", "is-loading"), ("disabled", "1")]  withSpinDyn -- XXX only used once? maybe remove  ::@@ -106,7 +123,7 @@ aSpinButtonClass ::      (Dom.DomBuilder t m, PostBuild t m)   => Text.Text-  -> Dynamic t SpinState+  -> Dynamic t FormState   -> m ()   -> m (Event t ()) aSpinButtonClass clazz spinstate =@@ -115,7 +132,7 @@  -- attrmap -- | A form captures enter presses of child componetns and---   sends it to them+--   sends it to them in an event. form :: (Dom.DomBuilder t m, MonadFix m) => (Event t () -> m a) -> m a form monF = do   rec val <-
src/Reflex/Bulmex/Form/FormTypes.hs view
@@ -5,7 +5,7 @@ -- | Types for Form. module Reflex.Bulmex.Form.FormTypes   ( FormAction(..)-  , SpinState(..)+  , FormState(..)   -- * Prisms   , _PostDefault   , _Loading@@ -31,14 +31,14 @@ _FormRest :: Prism' FormAction () _FormRest = _Ctor @"FormRest" -data SpinState -- TODO rename to load state?-  = SpinRest-  | Spinning+data FormState+  = FormStateRest+  | FormStateSpinning   deriving (Show) -instance Semigroup SpinState where-  (<>) SpinRest SpinRest = SpinRest-  (<>) _ _               = Spinning+instance Semigroup FormState where+  (<>) FormStateRest FormStateRest = FormStateRest+  (<>) _ _                         = FormStateSpinning -instance Monoid SpinState where-  mempty = SpinRest+instance Monoid FormState where+  mempty = FormStateRest
src/Reflex/Bulmex/Html.hs view
@@ -137,6 +137,7 @@     traversed .     to (Text.pack . show) +-- TODO Move to Load -- | Insert an encodable in the document body, --   in case of the server side rendering we encode it as script tag with jsonval, --   in case of ghcjsdom we read the value from that script tag
src/Reflex/Bulmex/Input/Basic.hs view
@@ -5,13 +5,14 @@  -- | Basic inputs with bulma styling module Reflex.Bulmex.Input.Basic-  ( abuttonClass+  ( abutton+  , abuttonClass   , abuttonLarge-  , txtInput-  , abutton-  , buttonClassAttr   , abuttonDynAttr+  , buttonClassAttr+  -- * Text inputs   , txtArea+  , txtInput   ) where  import           Control.Lens@@ -56,6 +57,11 @@   -> m (Event t (), a) abuttonClass = abutton . classAttr +-- | A button around an arbitrary dom element:+--+--   > <button> m a </button>+--+--   It's also styled nicely. abutton ::      (PostBuild t m, Dom.DomBuilder t m) => AttrMap -> m a -> m (Event t (), a) abutton = abuttonDynAttr . constDyn
+ src/Reflex/Bulmex/Input/Debounce.hs view
@@ -0,0 +1,88 @@+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecursiveDo       #-}+{-# LANGUAGE TypeFamilies      #-}++-- | This makes it easy to create auto-save operations+--   not limited to saves.+module Reflex.Bulmex.Input.Debounce+  ( InputStates(..)+  , defStateAttr+  , withInputDebounceEvt+  , withInput+  )+where++import           Control.Monad.Fix+import           Control.Monad.IO.Class (MonadIO)+import qualified Data.Text              as Text+import           Data.Time+import           Reflex+import           Reflex.Bulmex.Event++-- | Allows widgets to react according with what's happening+data InputStates+  = InputStarted+  | InputBuffered+  | InputProcessed+  | InputAborted+  | InputInitial++-- | do a debounced action, create a widget with the dynamic inputstates+--  that indicates the state of doing the action+--  This allows you to automatically request the server once user is+--  finished with typing for example,+--   while not flooding the server with requests trough debouncing+withInputDebounceEvt+  :: ( PostBuild t m+     , MonadFix m+     , MonadHold t m+     , TriggerEvent t m+     , MonadIO (Performable m)+     , PerformEvent t m+     )+  => NominalDiffTime -- ^ Delay before posting the request+  -> (result -> Bool) -- ^ Was the final requess successfull?+  -> (Dynamic t InputStates -> m (b, Event t inputEvt)) -- ^ Widget body reacting to states+  -> (b -> Event t inputEvt -> m (Event t result)) -- ^ Action function+  -> m (Event t result, b)+withInputDebounceEvt debtime succF stateF =+  withInput (debounce debtime) succF+    $ const+    $ fmap (\(one', two) -> (one', two, one'))+    . stateF++-- | Maps input state to bulmex classes: InputStarted = is-warning for example+defStateAttr :: InputStates -> Text.Text+defStateAttr InputStarted   = "is-warning"+defStateAttr InputBuffered  = "is-warning" -- is-loading+defStateAttr InputProcessed = "is-success"+defStateAttr InputAborted   = "is-danger"+defStateAttr InputInitial   = ""++-- | A general debounce widget+--  This looks a lot like 'actionForm', but it's not the same because+--  form allows user code to decide what+withInput+  :: (PostBuild t m, MonadFix m, MonadHold t m)+  => (Event t inputEvt -> m (Event t inputEvt)) -- ^ change input timeline, eg pure for no change+  -> (result -> Bool) -- ^ Was the final requess successfull?+  -> (  Event t result+     -> Dynamic t InputStates+     -> m (actArgs, Event t inputEvt, finalRes)+     ) -- ^ Widget body reacting to states+  -> (actArgs -> Event t inputEvt -> m (Event t result)) -- ^ Action function+  -> m (Event t result, finalRes)+withInput timeF isSuccessF createTypeEvt reqFunc = mdo+  (someData, typeEvtImmediate, result) <- createTypeEvt postFinished areaState+  typeEvtDeb                           <- timeF typeEvtImmediate+  postFinished                         <- reqFunc someData typeEvtDeb+  areaState                            <-+    holdDyn InputInitial+    $ leftmost+    $ [ InputStarted <$ typeEvtImmediate+      , InputBuffered <$ typeEvtDeb+      , InputProcessed <$ (blockFalse $ isSuccessF <$> postFinished)+      , InputAborted <$ (blockFalse $ not . isSuccessF <$> postFinished)+      ]+  pure (postFinished, result)
src/Reflex/Bulmex/Load.hs view
@@ -2,8 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecursiveDo       #-} --- | Bulma loading screen w/ help of extension:---   https://wikiki.github.io/elements/pageloader/+-- | Load time manipulation module Reflex.Bulmex.Load   ( pageLoader   , prerenderLoad@@ -24,10 +23,12 @@ import qualified Reflex.Dom.Widget.Basic    as Dom import qualified Reflex.Tags                as T +-- | Bulma loading screen with help of extension:+--   https://wikiki.github.io/elements/pageloader/ pageLoader :: DomBuilder t m => Text.Text -> m a -> m a pageLoader = partialDiv "pageloader" --- | show a spinning image while loading JS+-- | show input monad while JS is loading, for example a spinning image prerenderLoad :: (Prerender js t m, DomBuilder t m) => m () -> m () prerenderLoad spinner =   void $ prerender (T.divClass "prerender-load" $ spinner) Dom.blank
src/Reflex/Bulmex/Modal.hs view
@@ -5,9 +5,9 @@ --   Puts code behinds bulma's modal styling: https://bulma.io/documentation/components/modal/ module Reflex.Bulmex.Modal   ( modal-  , OnClose   , modal'   , modalClose+  , OnClose   ) where  import           Control.Lens@@ -18,34 +18,36 @@ import qualified Reflex.Dom.Widget.Basic  as Dom import qualified Reflex.Tags              as T -data OnClose =-  OnClose  -- | 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 ()-  -> m a+  => 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.---   Also has a cross to close it.+--   It also has a cross to close with. modal' ::      (PostBuild t m, MonadHold t m, MonadFix m, Dom.DomBuilder t m)-  => Event t ()-  -> Event t ()-  -> m a+  => 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 ()-  -> m (a, Event t ())+  => Event t () -- ^ open trigger+  -> m (a, Event t ()) -- ^ body + close trigger result   -> m (a, Event t OnClose) modalClose openEvt monad = do   rec stateDyn <-@@ -68,3 +70,7 @@   where     closed = classAttr "modal"     opened = classAttr "modal is-active"++-- | An incation that an event came from closing a modal+data OnClose =+  OnClose
src/Reflex/Bulmex/Tag/Hide.hs view
@@ -13,6 +13,7 @@ import qualified Reflex.Dom.Builder.Class as Dom import qualified Reflex.Tags              as T +-- | When the dynamic is true the first monad is shown, otherwise the second. switchDiv ::      (PostBuild t m, Dom.DomBuilder t m) => Dynamic t Bool -> m () -> m a -> m a switchDiv attrDyn true false = do