Skip to content

Commit 11426cd

Browse files
authored
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)
1 parent 8561721 commit 11426cd

File tree

4 files changed

+95
-6
lines changed

4 files changed

+95
-6
lines changed

src/Docs/Search/App.purs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
5162
insertStyle :: Document.Document -> Effect Unit
5263
insertStyle doc = do
5364
let styleContents = """

src/Docs/Search/App/SearchField.purs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Prelude
77
import CSS (border, borderRadius, color, em, float, floatLeft, fontWeight, lineHeight, marginBottom, marginLeft, paddingBottom, paddingLeft, paddingRight, paddingTop, pct, px, rgb, solid, weight, width)
88
import Data.Maybe (Maybe(..), maybe)
99
import Data.Newtype (wrap)
10+
import Docs.Search.URIHash as URIHash
1011
import Effect (Effect)
1112
import Effect.Aff (Aff)
1213
import Halogen as H
@@ -38,28 +39,45 @@ data Action
3839
| InitKeyboardListener
3940
| HandleKey H.SubscriptionId KeyboardEvent
4041

42+
data Query a
43+
= ReadURIHash a
44+
4145
data 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
4852
component =
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+
5670
initialState :: forall i. i -> State
5771
initialState _ = { input: "", focused: false }
5872

5973
handleAction :: Action -> H.HalogenM State Action () SearchFieldMessage Aff Unit
6074
handleAction = 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+
105128
withSearchField :: (HTML.HTMLInputElement -> Effect Unit) -> Effect Unit
106129
withSearchField cont = do
107130
doc <- Document.toParentNode <$>

src/Docs/Search/URIHash.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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.purs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 ""

0 commit comments

Comments
 (0)