git.iko.soy/rowtype-yoga/purescript-docs-search
on master help sign in
Allow using the app as a search engine (show search term in URI h… (#26)

Allow using the app as a search engine (show search term in URI hash)

commit11426cdb7350b5e5f5c3f6b8144759991ea8cdbd authorVladimir Kalnitsky <klntsky@gmail.com> dateSat 23 Nov 2019, 02:06 (7 years ago) parent8561721
4 files changed+95−6
src/Docs/Search/App.purs+13−2 src/Docs/Search/App/SearchField.purs+27−4 src/Docs/Search/URIHash.js+17 src/Docs/Search/URIHash.purs+38
src/Docs/Search/App.purs+13−2
@@ -41,13 +41,24 @@ main = do
4141 HA.runHalogenAff do
4242 packageIndex <- PackageIndex.loadPackageIndex
4343
44− let initialSearchEngineState = { packageIndex: packageIndex, index: mempty, typeIndex: mempty }
44+ let initialSearchEngineState = { packageIndex: packageIndex
45+ , index: mempty
46+ , typeIndex: mempty
47+ }
48+
49+ resultsComponent =
50+ SearchResults.mkComponent initialSearchEngineState pageContents markdownIt
4551
4652 sfio <- runUI SearchField.component unit searchField
47− srio <- runUI (SearchResults.mkComponent initialSearchEngineState pageContents markdownIt) unit searchResults
53+ srio <- runUI resultsComponent unit searchResults
54+
4855 sfio.subscribe $
4956 Coroutine.consumer (srio.query <<< H.tell <<< SearchResults.MessageFromSearchField)
5057
58+ -- We need to read the URI hash only when both components are initialized and
59+ -- the search field is subscribed to the main component.
60+ sfio.query (SearchField.ReadURIHash unit)
61+
5162insertStyle :: Document.Document -> Effect Unit
5263insertStyle doc = do
5364 let styleContents = """
src/Docs/Search/App/SearchField.purs+27−4
@@ -7,6 +7,7 @@ import Prelude
77import CSS (border, borderRadius, color, em, float, floatLeft, fontWeight, lineHeight, marginBottom, marginLeft, paddingBottom, paddingLeft, paddingRight, paddingTop, pct, px, rgb, solid, weight, width)
88import Data.Maybe (Maybe(..), maybe)
99import Data.Newtype (wrap)
10+import Docs.Search.URIHash as URIHash
1011import Effect (Effect)
1112import Effect.Aff (Aff)
1213import Halogen as H
@@ -38,21 +39,34 @@ data Action
3839 | InitKeyboardListener
3940 | HandleKey H.SubscriptionId KeyboardEvent
4041
42+data Query a
43+ = ReadURIHash a
44+
4145data SearchFieldMessage
4246 = InputUpdated String
4347 | InputCleared
4448 | Focused
4549 | LostFocus
4650
47−component :: forall q i. H.Component HH.HTML q i SearchFieldMessage Aff
51+component :: forall i. H.Component HH.HTML Query i SearchFieldMessage Aff
4852component =
4953 H.mkComponent
5054 { initialState
5155 , render
5256 , eval: H.mkEval $ H.defaultEval { handleAction = handleAction
57+ , handleQuery = handleQuery
5358 , initialize = Just InitKeyboardListener }
5459 }
5560
61+handleQuery
62+ :: forall a
63+ . Query a
64+ -> H.HalogenM State Action () SearchFieldMessage Aff (Maybe a)
65+handleQuery (ReadURIHash next) = do
66+ state <- H.get
67+ H.raise (InputUpdated state.input)
68+ pure Nothing
69+
5670initialState :: forall i. i -> State
5771initialState _ = { input: "", focused: false }
5872
@@ -60,6 +74,10 @@ handleAction :: Action -> H.HalogenM State Action () SearchFieldMessage Aff Unit
6074handleAction = case _ of
6175
6276 InitKeyboardListener -> do
77+
78+ input <- H.liftEffect URIHash.getInput
79+ H.modify_ (_ { input = input })
80+
6381 document <- H.liftEffect $ Web.document =<< Web.window
6482 H.subscribe' \sid ->
6583 ES.eventListenerEventSource
@@ -82,9 +100,7 @@ handleAction = case _ of
82100 then do
83101 H.liftEffect do
84102 withSearchField (HTMLInputElement.toHTMLElement >>> Web.blur)
85− else do
86− H.modify_ (_ { input = "" })
87− H.raise $ InputCleared
103+ else clearInput
88104
89105 InputAction input -> do
90106 H.modify_ $ (_ { input = input })
@@ -93,6 +109,7 @@ handleAction = case _ of
93109 state <- H.get
94110 H.liftEffect do
95111 withSearchField (HTMLInputElement.toHTMLElement >>> Web.blur)
112+ H.liftEffect (URIHash.setInput state.input)
96113 H.raise $ InputUpdated state.input
97114
98115 FocusChanged status -> do
@@ -102,6 +119,12 @@ handleAction = case _ of
102119 then Focused
103120 else LostFocus
104121
122+clearInput :: H.HalogenM State Action () SearchFieldMessage Aff Unit
123+clearInput = do
124+ H.modify_ (_ { input = "" })
125+ H.liftEffect URIHash.removeHash
126+ H.raise InputCleared
127+
105128withSearchField :: (HTML.HTMLInputElement -> Effect Unit) -> Effect Unit
106129withSearchField cont = do
107130 doc <- Document.toParentNode <$>
src/Docs/Search/URIHash.jsnew file+17
@@ -0,0 +1,17 @@
1+/* global exports history */
2+
3+// https://stackoverflow.com/questions/1397329
4+exports.removeHash = function () {
5+ var scrollV, scrollH, loc = window.location;
6+ if ("pushState" in history)
7+ history.pushState("", document.title, loc.pathname + loc.search);
8+ else {
9+ scrollV = document.body.scrollTop;
10+ scrollH = document.body.scrollLeft;
11+
12+ loc.hash = "";
13+
14+ document.body.scrollTop = scrollV;
15+ document.body.scrollLeft = scrollH;
16+ }
17+}
src/Docs/Search/URIHash.pursnew file+38
@@ -0,0 +1,38 @@
1+module Docs.Search.URIHash
2+ ( getInput
3+ , setInput
4+ , removeHash
5+ )
6+where
7+
8+import Prelude
9+
10+import Data.Maybe (Maybe(Just), fromMaybe)
11+import Data.String.CodeUnits as String
12+import Effect (Effect)
13+import Global (decodeURIComponent, encodeURIComponent)
14+import Web.HTML as HTML
15+import Web.HTML.Location as Location
16+import Web.HTML.Window as Window
17+
18+foreign import removeHash :: Effect Unit
19+
20+setInput :: String -> Effect Unit
21+setInput "" = removeHash
22+setInput input = do
23+ window <- HTML.window
24+ location <- Window.location window
25+ let hash = "search:" <> fromMaybe "" (encodeURIComponent input)
26+ Location.setHash hash location
27+
28+getInput :: Effect String
29+getInput = do
30+ window <- HTML.window
31+ location <- Window.location window
32+ hash <- Location.hash location
33+ pure $
34+ if String.slice 0 8 hash == Just "#search:"
35+ then fromMaybe "" $
36+ decodeURIComponent $
37+ String.drop 8 hash
38+ else ""