@@ -1,13 +1,14 @@
11-- | This is the main module of the client-side Halogen app.
22module Docs.Search.App where
33
4−import Prelude
5−
64import Docs.Search.App.SearchField as SearchField
75import Docs.Search.App.SearchResults as SearchResults
6+import Docs.Search.App.Sidebar as Sidebar
87import Docs.Search.Extra (whenJust)
8+import Docs.Search.ModuleIndex as ModuleIndex
99import Docs.Search.PackageIndex as PackageIndex
1010
11+import Prelude
1112import Control.Coroutine as Coroutine
1213import Data.Maybe (Maybe(..))
1314import Data.Newtype (wrap)
@@ -17,6 +18,7 @@ import Halogen as H
1718import Halogen.Aff as HA
1819import Halogen.VDom.Driver (runUI)
1920import MarkdownIt as MD
21+import Web.DOM.ChildNode as ChildNode
2022import Web.DOM.Document as Document
2123import Web.DOM.Element as Element
2224import Web.DOM.Node as Node
@@ -24,10 +26,12 @@ import Web.DOM.ParentNode as ParentNode
2426import Web.DOM.Text as Text
2527import Web.Event.EventTarget (addEventListener, eventListener)
2628import Web.HTML as HTML
29+import Web.HTML.Event.HashChangeEvent.EventTypes (hashchange)
2730import Web.HTML.HTMLDocument as HTMLDocument
2831import Web.HTML.HTMLElement (fromElement)
2932import Web.HTML.Window as Window
30−import Web.HTML.Event.HashChangeEvent.EventTypes (hashchange)
33+import Web.HTML.Event.EventTypes (focus)
34+
3135
3236main :: Effect Unit
3337main = do
@@ -40,11 +44,21 @@ main = do
4044 -- Initialize a `markdown-it` instance (we need it to render the docs as markdown)
4145 markdownIt <- MD.newMarkdownIt MD.Default mempty
4246
43− whenJust mbContainers \ { searchField, searchResults, pageContents } -> do
47+ whenJust mbContainers \ { searchField
48+ , searchResults
49+ , pageContents
50+ , sidebarContainer
51+ , realSidebar } -> do
52+
53+ -- Hide real sidebar completely - we are going to recreate it as Halogen component.
54+ ChildNode.remove $ Element.toChildNode realSidebar
55+
4456 HA.runHalogenAff do
4557 packageIndex <- PackageIndex.loadPackageIndex
58+ moduleIndex <- ModuleIndex.loadModuleIndex
4659
4760 let initialSearchEngineState = { packageIndex: packageIndex
61+ , moduleIndex: moduleIndex
4862 , index: mempty
4963 , typeIndex: mempty
5064 }
@@ -53,6 +67,9 @@ main = do
5367 SearchResults.mkComponent initialSearchEngineState pageContents markdownIt
5468
5569 sfio <- runUI SearchField.component unit searchField
70+ sbio <- do
71+ component <- Sidebar.mkComponent moduleIndex
72+ runUI component unit sidebarContainer
5673 srio <- runUI resultsComponent unit searchResults
5774
5875 sfio.subscribe $
@@ -72,6 +89,16 @@ main = do
7289
7390 addEventListener hashchange listener true (Window.toEventTarget win)
7491
92+ -- Subscribe to window focus events
93+ H.liftEffect do
94+
95+ listener <-
96+ eventListener \event ->
97+ launchAff_ do
98+ sbio.query $ H.tell Sidebar.UpdateModuleGrouping
99+
100+ addEventListener focus listener true (Window.toEventTarget win)
101+
75102
76103insertStyle :: Document.Document -> Effect Unit
77104insertStyle doc = do
@@ -102,24 +129,49 @@ insertStyle doc = do
102129 /* Add a margin between badge icons and package/module names. */
103130 margin-right: 0.25em;
104131 }
132+ .li-package {
133+ font-weight: bold;
134+ cursor: pointer;
135+ color: #c4953a;
136+ }
137+ /* Make spaces narrower in the sidebar */
138+ .li-package > details > ul {
139+ margin-top: auto;
140+ margin-bottom: auto;
141+ }
142+ #group-modules__label, #group-modules__input {
143+ display: inline-block;
144+ vertical-align: middle;
145+ color: #666666;
146+ font-size: 0.8rem;
147+ font-weight: 300;
148+ letter-spacing: 1px;
149+ margin-bottom: -0.8em;
150+ }
151+ summary:focus {
152+ outline: none;
153+ }
105154 """
106155
107156 mbHead <-
108157 ParentNode.querySelector (wrap "head") (Document.toParentNode doc)
109158
110159 whenJust mbHead \head -> do
111−
112160 contents <- Document.createTextNode styleContents doc
113161 style <- Document.createElement "style" doc
114162 void $ Node.appendChild (Text.toNode contents) (Element.toNode style)
115163 void $ Node.appendChild (Element.toNode style) (Element.toNode head)
116164
165+
117166-- | Query the DOM for specific elements that should always be present.
118167getContainers
119168 :: Document.Document
120169 -> Effect (Maybe { searchField :: HTML.HTMLElement
121170 , searchResults :: HTML.HTMLElement
122− , pageContents :: Element.Element })
171+ , pageContents :: Element.Element
172+ , sidebarContainer :: HTML.HTMLElement
173+ , realSidebar :: Element.Element
174+ })
123175getContainers doc = do
124176 let docPN = Document.toParentNode doc
125177 mbBanner <-
@@ -128,13 +180,21 @@ getContainers doc = do
128180 ParentNode.querySelector (wrap ".everything-except-footer") docPN
129181 mbContainer <-
130182 ParentNode.querySelector (wrap ".everything-except-footer > .container") docPN
183+ mbMainContainer <-
184+ ParentNode.querySelector (wrap ".everything-except-footer > main") docPN
185+ mbRealSidebar <-
186+ ParentNode.querySelector (wrap ".col--aside") docPN
131187 case unit of
132− _ | Just banner <- mbBanner
133− , Just everything <- mbEverything
134− , Just pageContents <- mbContainer -> do
188+ _ | Just banner <- mbBanner
189+ , Just everything <- mbEverything
190+ , Just pageContents <- mbContainer
191+ , Just mainContainer <- mbMainContainer
192+ , Just realSidebar <- mbRealSidebar -> do
135193 search <- Document.createElement "div" doc
136194 void $ Node.appendChild (Element.toNode search) (Element.toNode banner)
137− pure $ fromElement search >>= \searchField ->
138− fromElement everything >>= \searchResults ->
139− pure { searchField, searchResults, pageContents }
195+ pure do
196+ searchField <- fromElement search
197+ searchResults <- fromElement everything
198+ sidebarContainer <- fromElement mainContainer
199+ pure { searchField, searchResults, pageContents, realSidebar, sidebarContainer }
140200 | otherwise -> pure Nothing