Skip to content

Instantly share code, notes, and snippets.

@dariooddenino
Created October 29, 2018 16:46
Show Gist options
  • Save dariooddenino/eb0fc7fd863ae37d8ca9962b7ea824dc to your computer and use it in GitHub Desktop.
Save dariooddenino/eb0fc7fd863ae37d8ca9962b7ea824dc to your computer and use it in GitHub Desktop.
module Components.Form.Search where
import Prelude
import Bulma as B
import Component.Utils.Debounced (DebounceTrigger, cancelDebounceTrigger, debouncedEventSource, runDebounceTrigger)
import Data.Foldable (for_)
import Data.Maybe (Maybe(..))
import Data.String (length, null)
import Effect.Aff (Aff, Milliseconds)
import Halogen as H
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
import Halogen.HTML.Properties as HP
import Record as Record
data Query a
= Initialize a
| Finalize a
| Search String a
| SendSearch a
| Receive Input a
type Input = { minLength :: Int
, debounceTime :: Milliseconds
, loading :: Boolean
}
type State = { minLength :: Int
, debounceTime :: Milliseconds
, search :: Maybe String
, loading :: Boolean
, searchTrigger :: Maybe (DebounceTrigger Query Aff)
}
data Message = New (Maybe String)
component :: H.Component HH.HTML Query Input Message Aff
component =
H.lifecycleComponent
{ initialState: Record.merge { searchTrigger: Nothing
, search: Nothing
}
, render
, eval
, receiver: const Nothing
, initializer: Just (H.action Initialize)
, finalizer: Just (H.action Finalize)
}
where
render :: State -> H.ComponentHTML Query
render s =
HH.div
[ HP.class_ B.field ]
[ HH.div
[ HP.classes [ B.control, B.hasIconsRight ] ]
[ HH.input [ HP.class_ B.input
, HP.type_ HP.InputText
, HP.placeholder "Search..."
, HE.onValueInput (HE.input $ Search)
, HP.disabled s.loading
]
, HH.span
[ HP.classes [ B.icon, B.isSmall, B.isRight ] ]
[ HH.i [ HP.classes [ B.fa, B.faSearch ] ] [] ]
]
]
eval :: Query ~> H.ComponentDSL State Query Message Aff
eval (Initialize next) = next <$ do
s <- H.get
trigger <- debouncedEventSource s.debounceTime
H.modify_ (_ { searchTrigger = Just trigger })
eval (Finalize next) = next <$ do
s <- H.get
H.liftAff $ for_ s.searchTrigger cancelDebounceTrigger
eval (Search str next) = next <$ do
s <- H.get
let val = if null str || length str < s.minLength then Nothing else Just str
when (val /= s.search) $ do
H.modify_ (_ { search = val })
case s.searchTrigger of
Nothing -> pure unit
Just tr -> H.liftAff $ runDebounceTrigger tr SendSearch
eval (SendSearch next) = next <$ do
s <- H.get
H.raise $ New s.search
eval (Receive i next) = next <$ do
s <- H.get
-- update only loading.
when (s.loading /= i.loading) $
H.modify_ (_ { loading = i.loading })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment