mirror of
https://github.com/rowtype-yoga/purescript-docs-search.git
synced 2024-09-11 12:35:37 +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 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.
|
||||||
|
@ -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" ]
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user