@@ -34,15 +34,17 @@ import Halogen as H
-- |
-- | - `s`: The state type defined for the primitive
-- | - `q`: The query type defined for the primitive
type State s q = Store s (H.ComponentHTML q)
type State s q m = Store s (H.ComponentHTML q () m)

-- | A helper to get and unpack the primitive state type from the Store type. When used with pattern matching,
-- | you can access state with:
-- |
-- | ```purescript
-- | (Tuple renderFunction state) <- getState
-- | ```
getState :: m s a. MonadState (Store s a) m => m (Tuple (s -> a) s)
getState :: m s a
. MonadState (Store s a) m
=> m (Tuple (s -> a) s)
getState = pure <<< runStore =<< H.get

-- | A helper for wholly updating the `State` (`Store`) of a primitive.
@@ -51,5 +53,9 @@ getState = pure <<< runStore =<< H.get
-- | query that handles `Input` updates.
-- |
-- | Note: Use `seeks` if only the primitive's internal state needs to be updated (not the entire Store).
updateStore :: state html. (state -> html) -> (state -> state) -> Store state html -> Store state html
updateStore :: state html
. (state -> html)
-> (state -> state)
-> Store state html
-> Store state html
updateStore r f = (\(Tuple _ s) -> store r s) <<< runStore <<< seeks f
@@ -22,39 +22,58 @@ import Data.Time.Duration (Milliseconds(..))
import Data.Traversable (traverse_)
import Data.Tuple (Tuple(..))
import Data.Newtype (class Newtype, unwrap)
import Data.Symbol (SProxy(..))
import DOM (DOM)
import DOM.Event.Event (preventDefault, currentTarget)
import DOM.Event.Types as ET
import DOM.Event.KeyboardEvent as KE
import DOM.Event.MouseEvent as ME
import DOM.HTML.HTMLElement (blur, focus)
import DOM.HTML.Types (HTMLElement, readHTMLElement)
import Halogen (Component, ComponentDSL, ComponentHTML, component, liftAff, liftEff, modify) as H
import Halogen as H
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
import Halogen.Query.HalogenM (fork, raise) as H
import Select.Internal.State (updateStore, getState)

----------
-- Component Types

-- | A useful shorthand for the Halogen component type
type Component o item eff m
= H.Component HH.HTML (Query o item eff) (Input o item eff) (Message o item) m
= H.Component HH.HTML (Query o item eff m) (Input o item eff m) (Message o item) m

-- | A useful shorthand for the Halogen component HTML type
type ComponentHTML o item eff
= H.ComponentHTML (Query o item eff)
type ComponentHTML o item eff m
= H.ComponentHTML (Query o item eff m) () m

-- | A useful shorthand for the Halogen component DSL type
type ComponentDSL o item eff m
= H.ComponentDSL (StateStore o item eff) (Query o item eff) (Message o item) m
= H.HalogenM (StateStore o item eff m) (Query o item eff m) () (Message o item) m

-- | The component slot type for convenience. Partially applied so you
-- | can still provide your own reference for slots.
type Slot o item eff m
= H.Slot (Query o item eff m) (Message o item)

-- | The symbol for this component in the slot row
_select :: SProxy "select"
_select = SProxy :: SProxy "select"

-- | A helper function to mount the Select component in a slot
select :: p o item eff m r
. Ord p
=> MonadAff (Effects eff) m
=> p
-> Input o item (Effects eff) m
-> (Message o item -> Maybe (o Unit))
-> H.ComponentHTML o ( select :: Slot o item (Effects eff) m p | r ) m
select slot i handler = HH.slot (SProxy :: SProxy "select") slot component i handler

-- | The component's state type, wrapped in `Store`. The state and result of the
-- | render function are stored so that `extract` from `Control.Comonad` can be
-- | used to pull out the render function.
type StateStore o item eff
= Store (State item eff) (ComponentHTML o item eff)
type StateStore o item eff m
= Store (State item eff) (ComponentHTML o item eff m)

-- | The effects necessary for this component to run. Your component will need to
-- | also support these effects.
@@ -92,7 +111,7 @@ type Effects eff = ( avar :: AVAR, dom :: DOM | eff )
-- | own queries. Triggers an `Emit` message containing the query when triggered.
-- | This can be used to easily extend `Select` with more behaviors.
-- | - `Receive`: Sets the component with new input
data Query o item eff a
data Query o item eff m a
= Search String a
| Highlight Target a
| Select Int a
@@ -103,9 +122,9 @@ data Query o item eff a
| SetVisibility Visibility a
| ToggleVisibility a
| ReplaceItems (Array item) a
| AndThen (DayPair (Query o item eff) a)
| AndThen (DayPair (Query o item eff m) a)
| Raise (o Unit) a
| Receive (Input o item eff) a
| Receive (Input o item eff m) a

-- | A type representing a pair of queries that can be run in order.
newtype DayPair f a = DayPair (Day f f a)
@@ -120,7 +139,7 @@ derive instance newtypeDayPair :: Newtype (DayPair f a) _
-- | TriggerFocus a
-- | ]
-- | ```
andThen :: o item eff a. Query o item eff Unit -> Query o item eff a -> Query o item eff a
andThen :: o item eff m a. Query o item eff m Unit -> Query o item eff m a -> Query o item eff m a
andThen q1 q2 = AndThen $ DayPair $ day (const id) q1 q2

-- | Represents a way to navigate on `Highlight` events: to the previous
@@ -181,12 +200,12 @@ type Debouncer eff =
-- | The component's input type, which includes the component`s render function. This
-- | render function can also be used to share data with the parent component, as every
-- | time the parent re-renders, the render function will refresh in `Select`.
type Input o item eff =
type Input o item eff m =
{ inputType :: InputType
, items :: Array item
, initialSearch :: Maybe String
, debounceTime :: Maybe Milliseconds
, render :: State item eff -> ComponentHTML o item eff
, render :: State item eff -> ComponentHTML o item eff m
}

-- | The parent is only notified for a few important events, but `Emit` makes it
@@ -211,6 +230,8 @@ component =
{ initialState
, render: extract
, eval
, initializer: Nothing
, finalizer: Nothing
, receiver: HE.input Receive
}
where
@@ -226,7 +247,7 @@ component =
, lastIndex: length i.items - 1
}

eval :: (Query o item (Effects eff)) ~> ComponentDSL o item (Effects eff) m
eval :: (Query o item (Effects eff) m) ~> ComponentDSL o item (Effects eff) m
eval = case _ of
Search str a -> a <$ do
(Tuple _ st) <- getState
@@ -35,9 +35,9 @@ type ToggleProps p =
-- | renderToggle = div (setToggleProps [ class "btn-class" ]) [ ...html ]
-- | ```
setToggleProps
:: o item eff p
. Array (HP.IProp (ToggleProps p) (Query o item eff Unit))
-> Array (HP.IProp (ToggleProps p) (Query o item eff Unit))
:: o item eff p m
. Array (HP.IProp (ToggleProps p) (Query o item eff m Unit))
-> Array (HP.IProp (ToggleProps p) (Query o item eff m Unit))
setToggleProps = flip (<>)
[ HE.onFocus $ HE.input $ \ev a ->
(H.action $ CaptureRef $ FE.focusEventToEvent ev)
@@ -78,9 +78,9 @@ type InputProps p =
-- | renderInput = input_ (setInputProps [ class "my-class" ])
-- | ```
setInputProps
:: o item eff p
. Array (HP.IProp (InputProps p) (Query o item eff Unit))
-> Array (HP.IProp (InputProps p) (Query o item eff Unit))
:: o item eff p m
. Array (HP.IProp (InputProps p) (Query o item eff m Unit))
-> Array (HP.IProp (InputProps p) (Query o item eff m Unit))
setInputProps = flip (<>)
[ HE.onFocus $ HE.input $ \ev a ->
(H.action $ CaptureRef $ FE.focusEventToEvent ev)
@@ -114,10 +114,10 @@ type ItemProps p =
-- | render = renderItem `mapWithIndex` itemsArray
-- | ```
setItemProps
:: o item eff p
:: o item eff p m
. Int
-> Array (HP.IProp (ItemProps p) (Query o item eff Unit))
-> Array (HP.IProp (ItemProps p) (Query o item eff Unit))
-> Array (HP.IProp (ItemProps p) (Query o item eff m Unit))
-> Array (HP.IProp (ItemProps p) (Query o item eff m Unit))
setItemProps index = flip (<>)
[ HE.onMouseDown $ HE.input $ \ev a ->
(H.action $ PreventClick ev)
@@ -132,9 +132,9 @@ setItemProps index = flip (<>)
-- | will not bubble up a blur event to the DOM. This should be used on the parent
-- | element that contains your items.
setContainerProps
:: o item eff p
. Array (HP.IProp (onMouseDown :: ET.MouseEvent | p) (Query o item eff Unit))
-> Array (HP.IProp (onMouseDown :: ET.MouseEvent | p) (Query o item eff Unit))
:: o item eff p m
. Array (HP.IProp (onMouseDown :: ET.MouseEvent | p) (Query o item eff m Unit))
-> Array (HP.IProp (onMouseDown :: ET.MouseEvent | p) (Query o item eff m Unit))
setContainerProps = flip (<>)
[ HE.onMouseDown $ HE.input PreventClick ]