Skip to content

Commit

Permalink
Merge pull request #33 from citizennet/input-ref
Browse files Browse the repository at this point in the history
Input/Toggle Reference
  • Loading branch information
crcornwell committed Nov 9, 2018
2 parents 7e21954 + 5c82932 commit f5d1a7c
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 45 deletions.
6 changes: 3 additions & 3 deletions docs/tutorials/dropdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ Error found:
in type synonym ChildQuery
```

The compiler has noticed that `#!hs ChildQuery`, a type synonym, is partially applied. That's because `#!hs Select.Query`, itself a type synonym, takes several arguments as described in the [module documentation on Pursuit](https://pursuit.purescript.org/packages/purescript-halogen-select/2.0.0/docs/Select#t:Query). Let's walk through each one:
The compiler has noticed that `#!hs ChildQuery`, a type synonym, is partially applied. That's because `#!hs Select.Query`, itself a type synonym, takes several arguments as described in the [module documentation on Pursuit](https://pursuit.purescript.org/packages/purescript-halogen-select/3.0.0/docs/Select#t:Query). Let's walk through each one:

```hs
type ChildQuery o item = Select.Query o item
Expand Down Expand Up @@ -271,7 +271,7 @@ dropdown st =
]
```

From this, we can see that we need to use the state type from `Select` to drive our render function, not the state from our parent component. Will our function still work? Let's look at [`Select`'s state type in the module documentation](https://pursuit.purescript.org/packages/purescript-halogen-select/2.0.0/docs/Select#t:State) to see what we have available:
From this, we can see that we need to use the state type from `Select` to drive our render function, not the state from our parent component. Will our function still work? Let's look at [`Select`'s state type in the module documentation](https://pursuit.purescript.org/packages/purescript-halogen-select/3.0.0/docs/Select#t:State) to see what we have available:

```hs
type State item =
Expand Down Expand Up @@ -464,7 +464,7 @@ Error found in module Component:
in type constructor Query
```

This looks similar to the type error we got when we tried to just use `Select.Query` in a type synonym. We need to provide a `#!hs Type` to `#!hs HandleSelect`, but `#!hs Select.Message` is still awaiting 2 arguments, the first of which is *itself* awaiting an argument! Let's go look at the [module documentation for `Select.Message`](https://pursuit.purescript.org/packages/purescript-halogen-select/2.0.0/docs/Select#t:Message).
This looks similar to the type error we got when we tried to just use `Select.Query` in a type synonym. We need to provide a `#!hs Type` to `#!hs HandleSelect`, but `#!hs Select.Message` is still awaiting 2 arguments, the first of which is *itself* awaiting an argument! Let's go look at the [module documentation for `Select.Message`](https://pursuit.purescript.org/packages/purescript-halogen-select/3.0.0/docs/Select#t:Message).

```hs
data Message o item
Expand Down
5 changes: 2 additions & 3 deletions docs/tutorials/typeahead.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ data QueryF o item a
= Search String a
| Highlight Target a
| Select Int a
| CaptureRef ET.Event a
| Focus Boolean a
| Key KE.KeyboardEvent a
| PreventClick ME.MouseEvent a
Expand Down Expand Up @@ -183,7 +182,7 @@ render st =
[ HH.slot unit Select.component ?input (HE.input HandleSelect) ]
```

With that out of the way, we can turn to the component's input type. Here's what we're required to fill in, as per the [`Select` module documentation](https://pursuit.purescript.org/packages/purescript-halogen-select/2.0.0/docs/Select#t:Input):
With that out of the way, we can turn to the component's input type. Here's what we're required to fill in, as per the [`Select` module documentation](https://pursuit.purescript.org/packages/purescript-halogen-select/3.0.0/docs/Select#t:Input):

```hs
-- | Text-driven inputs will operate like a normal search-driven selection component.
Expand Down Expand Up @@ -333,7 +332,7 @@ initialState = const

Now that we've got a usable `#!hs State` type, let's turn to our queries. Queries are the computations available to the component, so they're the place where we ought to think about what the typeahead should *do*, rather than just how it should render.

Just like `#!hs State`, when we write our own `#!hs Query` type on top of `Select`, we should consider what is already available in the component. As usual, we'll turn to the [module documentation](https://pursuit.purescript.org/packages/purescript-halogen-select/2.0.0/docs/Select#t:QueryF) to look at our available queries. I'd recommend scrolling through the available functions to get a glimpse of what `Select` offers, but we'll skip to the main points here.
Just like `#!hs State`, when we write our own `#!hs Query` type on top of `Select`, we should consider what is already available in the component. As usual, we'll turn to the [module documentation](https://pursuit.purescript.org/packages/purescript-halogen-select/3.0.0/docs/Select#t:QueryF) to look at our available queries. I'd recommend scrolling through the available functions to get a glimpse of what `Select` offers, but we'll skip to the main points here.

`Select` is going to manage all the keyboard events, text input, debouncing, moving the highlighted index, and so on. On top of that, we'll need to add some extra functionality: the ability to remove items that have already been selected, and the ability to fetch new items when the user performs a search. We'll at least need two queries to handle these two features.

Expand Down
41 changes: 13 additions & 28 deletions src/Select.purs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,22 @@ import Prelude

import Control.Comonad (extract)
import Control.Comonad.Store (Store, store)
import Effect.Aff (Fiber, delay, error, forkAff, killFiber)
import Effect.Aff.Class (class MonadAff)
import Effect.Aff.AVar (AVar)
import Effect.Aff.AVar as AVar
import Control.Monad.Free (Free, foldFree, liftF)
import Web.Event.Event (preventDefault, currentTarget, Event)
import Web.UIEvent.KeyboardEvent as KE
import Web.UIEvent.MouseEvent as ME
import Web.HTML.HTMLElement (HTMLElement, blur, focus, fromEventTarget)
import Data.Array (length, (!!))
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Time.Duration (Milliseconds(..))
import Data.Traversable (for_, traverse_)
import Halogen (Component, ComponentDSL, ComponentHTML, component, liftAff, liftEffect) as H
import Effect.Aff (Fiber, delay, error, forkAff, killFiber)
import Effect.Aff.AVar (AVar)
import Effect.Aff.AVar as AVar
import Effect.Aff.Class (class MonadAff)
import Halogen as H
import Halogen.HTML as HH
import Halogen.Query.HalogenM (fork, raise) as H
import Renderless.State (getState, modifyState_, modifyStore)
import Web.Event.Event (preventDefault)
import Web.HTML.HTMLElement (blur, focus)
import Web.UIEvent.KeyboardEvent as KE
import Web.UIEvent.MouseEvent as ME

----------
-- Component Types
Expand Down Expand Up @@ -66,7 +65,6 @@ data QueryF o item a
= Search String a
| Highlight Target a
| Select Int a
| CaptureRef Event a
| Focus Boolean a
| Key KE.KeyboardEvent a
| PreventClick ME.MouseEvent a
Expand Down Expand Up @@ -95,12 +93,6 @@ highlight t = liftF (Highlight t unit)
select :: o item. Int -> Query o item Unit
select i = liftF (Select i unit)

-- | From an event, captures a reference to the element that triggered the
-- | event. Used to manage focus / blur for elements without requiring a
-- | particular identifier.
captureRef :: o item. Event -> Query o item Unit
captureRef r = liftF (CaptureRef r unit)

-- | Trigger the DOM focus event for the element we have a reference to.
triggerFocus :: o item . Query o item Unit
triggerFocus = liftF (Focus true unit)
Expand Down Expand Up @@ -205,7 +197,6 @@ type State item =
, search :: String
, debounceTime :: Milliseconds
, debouncer :: Maybe Debouncer
, inputElement :: Maybe HTMLElement
, items :: Array item
, visibility :: Visibility
, highlightedIndex :: Maybe Int
Expand Down Expand Up @@ -259,7 +250,6 @@ component =
, search: fromMaybe "" i.initialSearch
, debounceTime: fromMaybe (Milliseconds 0.0) i.debounceTime
, debouncer: Nothing
, inputElement: Nothing
, items: i.items
, highlightedIndex: Nothing
, visibility: Off
Expand Down Expand Up @@ -337,14 +327,9 @@ component =
for_ (st.items !! index)
\item -> H.raise (Selected item)

CaptureRef event a -> a <$ do
st <- getState
modifyState_ _ { inputElement = fromEventTarget =<< currentTarget event }
pure a

Focus focusOrBlur a -> a <$ do
st <- getState
traverse_ (H.liftEffect <<< if focusOrBlur then focus else blur) st.inputElement
inputElement <- H.getHTMLElementRef $ H.RefLabel "select-input"
traverse_ (H.liftEffect <<< if focusOrBlur then focus else blur) inputElement

Key ev a -> a <$ do
setVis On
Expand All @@ -353,9 +338,9 @@ component =
"ArrowUp" -> preventIt *> eval' (highlight Prev)
"ArrowDown" -> preventIt *> eval' (highlight Next)
"Escape" -> do
st <- getState
inputElement <- H.getHTMLElementRef $ H.RefLabel "select-input"
preventIt
for_ st.inputElement (H.liftEffect <<< blur)
for_ inputElement (H.liftEffect <<< blur)
"Enter" -> do
st <- getState
preventIt
Expand Down
20 changes: 9 additions & 11 deletions src/Select/Setters.purs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ module Select.Setters where

import Prelude

import Web.UIEvent.FocusEvent as FE
import Web.UIEvent.MouseEvent as ME
import Web.UIEvent.KeyboardEvent as KE
import Web.Event.Event (Event)
import Data.Maybe (Maybe(..))
import Halogen (RefLabel(..)) as H
import Halogen.HTML.Events as HE
import Halogen.HTML.Properties as HP
import Select (Query, Target(..), Visibility(..))
import Select as Select
import Web.Event.Event (Event)
import Web.UIEvent.FocusEvent as FE
import Web.UIEvent.KeyboardEvent as KE
import Web.UIEvent.MouseEvent as ME

-- | The properties that must be supported by the HTML element that serves
-- | as a menu toggle. This should be used with toggle-driven `Select` components.
Expand All @@ -41,11 +42,8 @@ setToggleProps
. Array (HP.IProp (ToggleProps p) (Query o item Unit))
-> Array (HP.IProp (ToggleProps p) (Query o item Unit))
setToggleProps = flip (<>)
[ HE.onFocus \ev -> Just do
Select.captureRef $ FE.toEvent ev
Select.setVisibility On
[ HE.onFocus $ Select.always $ Select.setVisibility On
, HE.onMouseDown \ev -> Just do
Select.captureRef $ ME.toEvent ev
Select.preventClick ev
Select.getVisibility >>= case _ of
Select.On -> do
Expand All @@ -57,6 +55,7 @@ setToggleProps = flip (<>)
, HE.onKeyDown $ Just <<< Select.key
, HE.onBlur $ Select.always $ Select.setVisibility Off
, HP.tabIndex 0
, HP.ref (H.RefLabel "select-input")
]

-- | The properties that must be supported by the HTML element that serves
Expand Down Expand Up @@ -85,14 +84,13 @@ setInputProps
. Array (HP.IProp (InputProps p) (Query o item Unit))
-> Array (HP.IProp (InputProps p) (Query o item Unit))
setInputProps = flip (<>)
[ HE.onFocus \ev -> Just do
Select.captureRef $ FE.toEvent ev
Select.setVisibility On
[ HE.onFocus $ Select.always $ Select.setVisibility On
, HE.onKeyDown $ Just <<< Select.key
, HE.onValueInput $ Just <<< Select.search
, HE.onMouseDown $ Select.always $ Select.setVisibility On
, HE.onBlur $ Select.always $ Select.setVisibility Off
, HP.tabIndex 0
, HP.ref (H.RefLabel "select-input")
]

-- | The properties that must be supported by the HTML element that acts as a
Expand Down

0 comments on commit f5d1a7c

Please sign in to comment.