added: "S" hotkey to focus on search field (#4)

This commit is contained in:
Vladimir Kalnitsky 2019-07-15 23:08:58 +03:00 committed by GitHub
parent 6ba28b3de8
commit e69e81379a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 67 additions and 13 deletions

View File

@ -16,3 +16,9 @@ spago docs
spago bundle-app -m Docs.Search.App --to generated-docs/docs-search-app.js spago bundle-app -m Docs.Search.App --to generated-docs/docs-search-app.js
spago run -m Docs.Search.IndexBuilder spago run -m Docs.Search.IndexBuilder
``` ```
## UI
The user interface of the app is optimised for keyboard-only use.
**S** hotkey can be used to focus on the search field, **Escape** can be used to leave it. Pressing **Escape** twice will close the search results listing.

View File

@ -41,7 +41,7 @@ let additions =
, "web-uievents" , "web-uievents"
] ]
"https://github.com/slamdata/purescript-halogen.git" "https://github.com/slamdata/purescript-halogen.git"
"v5.0.0-rc.4" "v5.0.0-rc.5"
, halogen-css = , halogen-css =
mkPackage mkPackage
[ "css", "halogen" ] [ "css", "halogen" ]

View File

@ -2,23 +2,38 @@ module Docs.Search.App.SearchField where
import Prelude import Prelude
import CSS hiding (render, map) import CSS (border, borderRadius, color, em, float, floatLeft, fontWeight, lineHeight, marginBottom, marginLeft, paddingBottom, paddingLeft, paddingRight, paddingTop, pct, px, rgb, solid, weight, width)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..), maybe)
import Data.Newtype (wrap)
import Effect (Effect)
import Effect.Aff (Aff) import Effect.Aff (Aff)
import Halogen as H import Halogen as H
import Halogen.HTML as HH import Halogen.HTML as HH
import Halogen.HTML.CSS as HS import Halogen.HTML.CSS as HS
import Halogen.HTML.Events as HE import Halogen.HTML.Events as HE
import Halogen.HTML.Properties as HP import Halogen.HTML.Properties as HP
import Halogen.Query.EventSource as ES
import Web.DOM.Document as Document
import Web.DOM.ParentNode as ParentNode
import Web.HTML (window) as Web
import Web.HTML as HTML
import Web.HTML.HTMLDocument as HTMLDocument
import Web.HTML.HTMLElement (blur, focus, fromElement) as Web
import Web.HTML.Window (document) as Web
import Web.HTML.Window as Window
import Web.UIEvent.KeyboardEvent (KeyboardEvent)
import Web.UIEvent.KeyboardEvent as KE
import Web.UIEvent.KeyboardEvent as KeyboardEvent import Web.UIEvent.KeyboardEvent as KeyboardEvent
import Web.UIEvent.KeyboardEvent.EventTypes as KET
type State = { input :: String } type State = { input :: String, focused :: Boolean }
data Action data Action
= InputAction String = InputAction String
| EnterPressed | EnterPressed
| EscapePressed
| FocusChanged Boolean | FocusChanged Boolean
| InitKeyboardListener
| HandleKey H.SubscriptionId KeyboardEvent
data SearchFieldMessage data SearchFieldMessage
= InputUpdated String = InputUpdated String
@ -31,28 +46,61 @@ component =
H.mkComponent H.mkComponent
{ initialState { initialState
, render , render
, eval: H.mkEval $ H.defaultEval { handleAction = handleAction } , eval: H.mkEval $ H.defaultEval { handleAction = handleAction
, initialize = Just InitKeyboardListener }
} }
initialState :: forall i. i -> State initialState :: forall i. i -> State
initialState _ = { input: "" } initialState _ = { input: "", focused: false }
handleAction :: forall m. Action -> H.HalogenM State Action () SearchFieldMessage m Unit handleAction :: Action -> H.HalogenM State Action () SearchFieldMessage Aff Unit
handleAction = case _ of handleAction = case _ of
InitKeyboardListener -> do
document <- H.liftEffect $ Web.document =<< Web.window
H.subscribe' \sid ->
ES.eventListenerEventSource
KET.keyup
(HTMLDocument.toEventTarget document)
(map (HandleKey sid) <<< KE.fromEvent)
HandleKey sid ev -> do
when (KE.code ev == "KeyS") do
H.liftEffect $ withSearchField Web.focus
when (KE.code ev == "Escape") do
state <- H.get
if state.focused
then H.liftEffect $ withSearchField Web.blur
else do
H.modify_ (_ { input = "" })
H.raise $ InputCleared
InputAction input -> do InputAction input -> do
H.modify_ $ const { input } H.modify_ $ (_ { input = input })
EnterPressed -> do EnterPressed -> do
state <- H.get state <- H.get
H.liftEffect $ withSearchField Web.blur
H.raise $ InputUpdated state.input H.raise $ InputUpdated state.input
EscapePressed -> do
H.modify_ (_ { input = "" })
H.raise $ InputCleared
FocusChanged status -> do FocusChanged status -> do
H.modify_ (_ { focused = status })
H.raise H.raise
if status if status
then Focused then Focused
else LostFocus else LostFocus
withSearchField :: (HTML.HTMLElement -> Effect Unit) -> Effect Unit
withSearchField cont = do
doc <- Document.toParentNode <$>
HTMLDocument.toDocument <$>
(Window.document =<< HTML.window)
let selector = wrap "#docs-search-query-field"
mbEl <- ParentNode.querySelector selector doc
maybe mempty cont (mbEl >>= Web.fromElement)
render :: forall m. State -> H.ComponentHTML Action () m render :: forall m. State -> H.ComponentHTML Action () m
render state = render state =
HH.div HH.div
@ -68,11 +116,11 @@ render state =
[ HH.input [ HH.input
[ HP.value state.input [ HP.value state.input
, HP.placeholder "Search for definitions" , HP.placeholder "Search for definitions"
, HP.id_ "docs-search-query-field"
, HP.type_ HP.InputText , HP.type_ HP.InputText
, HE.onKeyUp (\event -> , HE.onKeyUp (\event ->
case KeyboardEvent.code event of case KeyboardEvent.code event of
"Enter" -> Just EnterPressed "Enter" -> Just EnterPressed
"Escape" -> Just EscapePressed
_ -> Nothing) _ -> Nothing)
, HE.onValueInput (Just <<< InputAction) , HE.onValueInput (Just <<< InputAction)
, HE.onFocusIn $ const $ Just $ FocusChanged true , HE.onFocusIn $ const $ Just $ FocusChanged true