mirror of
https://github.com/rowtype-yoga/purescript-docs-search.git
synced 2024-08-16 16:10:40 +03:00
added: "S" hotkey to focus on search field (#4)
This commit is contained in:
parent
6ba28b3de8
commit
e69e81379a
@ -16,3 +16,9 @@ spago docs
|
||||
spago bundle-app -m Docs.Search.App --to generated-docs/docs-search-app.js
|
||||
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.
|
||||
|
@ -41,7 +41,7 @@ let additions =
|
||||
, "web-uievents"
|
||||
]
|
||||
"https://github.com/slamdata/purescript-halogen.git"
|
||||
"v5.0.0-rc.4"
|
||||
"v5.0.0-rc.5"
|
||||
, halogen-css =
|
||||
mkPackage
|
||||
[ "css", "halogen" ]
|
||||
|
@ -2,23 +2,38 @@ module Docs.Search.App.SearchField where
|
||||
|
||||
import Prelude
|
||||
|
||||
import CSS hiding (render, map)
|
||||
import Data.Maybe (Maybe(..))
|
||||
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(..), maybe)
|
||||
import Data.Newtype (wrap)
|
||||
import Effect (Effect)
|
||||
import Effect.Aff (Aff)
|
||||
import Halogen as H
|
||||
import Halogen.HTML as HH
|
||||
import Halogen.HTML.CSS as HS
|
||||
import Halogen.HTML.Events as HE
|
||||
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.EventTypes as KET
|
||||
|
||||
type State = { input :: String }
|
||||
type State = { input :: String, focused :: Boolean }
|
||||
|
||||
data Action
|
||||
= InputAction String
|
||||
| EnterPressed
|
||||
| EscapePressed
|
||||
| FocusChanged Boolean
|
||||
| InitKeyboardListener
|
||||
| HandleKey H.SubscriptionId KeyboardEvent
|
||||
|
||||
data SearchFieldMessage
|
||||
= InputUpdated String
|
||||
@ -31,28 +46,61 @@ component =
|
||||
H.mkComponent
|
||||
{ initialState
|
||||
, render
|
||||
, eval: H.mkEval $ H.defaultEval { handleAction = handleAction }
|
||||
, eval: H.mkEval $ H.defaultEval { handleAction = handleAction
|
||||
, initialize = Just InitKeyboardListener }
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
H.modify_ $ const { input }
|
||||
H.modify_ $ (_ { input = input })
|
||||
|
||||
EnterPressed -> do
|
||||
state <- H.get
|
||||
H.liftEffect $ withSearchField Web.blur
|
||||
H.raise $ InputUpdated state.input
|
||||
EscapePressed -> do
|
||||
H.modify_ (_ { input = "" })
|
||||
H.raise $ InputCleared
|
||||
|
||||
FocusChanged status -> do
|
||||
H.modify_ (_ { focused = status })
|
||||
H.raise
|
||||
if status
|
||||
then Focused
|
||||
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 state =
|
||||
HH.div
|
||||
@ -68,11 +116,11 @@ render state =
|
||||
[ HH.input
|
||||
[ HP.value state.input
|
||||
, HP.placeholder "Search for definitions"
|
||||
, HP.id_ "docs-search-query-field"
|
||||
, HP.type_ HP.InputText
|
||||
, HE.onKeyUp (\event ->
|
||||
case KeyboardEvent.code event of
|
||||
"Enter" -> Just EnterPressed
|
||||
"Escape" -> Just EscapePressed
|
||||
_ -> Nothing)
|
||||
, HE.onValueInput (Just <<< InputAction)
|
||||
, HE.onFocusIn $ const $ Just $ FocusChanged true
|
||||
|
Loading…
Reference in New Issue
Block a user