@@ -8,6 +8,7 @@ let render
88?page
99~(package : Package.package)
1010~(documentation_status: Package.documentation_status)
11+ ~(search_index_digest: string option)
1112~left_sidebar_html
1213~right_sidebar_html
1314inner =
@@ -60,13 +61,21 @@ Layout.render
6061 </li>
6162 </ol>
6263
63- <div title="Sorry, in-package search is not yet implemented, but this is where it's going to appear." class="flex w-full items-center">
64- <input disabled type="search" name="q" class="focus:border-gray-800 text-gray-800 bg-gray-300 border-gray-300 h-10 rounded-l-md appearance-none px-4 flex-grow"
65- autocomplete="off"
66- placeholder="Sorry, in-package search is not yet implemented, but this is where it's going to appear :-)">
67- <button disabled aria-label="search" class="h-10 rounded-r-md bg-gray-300 text-gray-800 flex items-center justify-center px-4">
68- <%s! Icons.magnifying_glass "w-6 h-6" %>
69- </button>
64+ <div id="in-package-search" class="relative w-full">
65+ <div class="flex w-full items-center">
66+ <% if Option.is_some search_index_digest then (%>
67+ <input id="in-package-search-input" type="search" name="q" class="focus:border-gray-800 text-gray-800 border-primary-600 h-10 rounded-l-md appearance-none px-4 flex-grow"
68+ autocomplete="off"
69+ placeholder="Search names in this package..."
70+ >
71+ <div aria-hidden="true" class="h-10 rounded-r-md bg-primary-600 text-white flex items-center justify-center px-4">
72+ <%s! Icons.magnifying_glass "w-6 h-6" %>
73+ </div>
74+ <% ); %>
75+ </div>
76+
77+ <div id="in-package-search-results" class="absolute top-12 right-0 left-0 bg-white z-20 w-full max-h-[60vh] overflow-y-auto border rounded-lg">
78+ </div>
7079 </div>
7180 </div>
7281
@@ -78,6 +87,7 @@ Layout.render
7887 x-transition:leave-end="-translate-x-full">
7988 <%s! left_sidebar_html %>
8089 </div>
90+
8191 <div class="flex-1 z-0 z- min-w-0 pt-6 pb-12 md:pb-[70vh]">
8292 <%s! inner %>
8393 </div>
@@ -91,4 +101,166 @@ Layout.render
91101 </div>
92102 </div>
93103</div>
94- <%s! Toc.script %>
104+ <%s! Toc.script %>
105+ <% (match search_index_digest with | Some(digest) -> %>
106+ <script src="<%s Ocamlorg_static.Asset. url "vendors/minisearch.min.js" %>"></script>
107+ <script>
108+ function Fuse() {}
109+ </script>
110+ <script>
111+ {
112+ let miniSearch;
113+ let results = [];
114+
115+ function perform_search() {
116+ let q = document.getElementById("in-package-search-input").value;
117+ results = miniSearch.search(q, {
118+ fields: ['name', 'prefixname', 'comment'],
119+ prefix: true,
120+ boost: {
121+ name: 6,
122+ prefixname: 2.5,
123+ comment: 0.8,
124+ },
125+ fuzzy: 0.15,
126+ }).slice(0,50);
127+ let container = document.getElementById("in-package-search-results");
128+ container.innerHTML = "";
129+
130+ let search_results = document.createElement("ol");
131+
132+ results.map((entry) => {
133+ let kind = document.createElement("tt");
134+ kind.innerText = entry.kind
135+ .replace("module type", "mty").replace("root", "mod").replace("module", "mod")
136+ .replace("method", "mtd").replace("class typ", "cty").replace("class", "cls")
137+ .replace("core type", "typ").replace("type", "typ")
138+ .replace("exception", "exc").replace("core exception", "exc")
139+ .replace("parameter", "par")
140+ .replace("leaf page", "man").replace("page", "man");
141+ kind.title = entry.kind;
142+ kind.classList.add("entry-kind");
143+
144+ let list_item = document.createElement("li");
145+ let a = document.createElement("a");
146+ a.href = "/" + entry.url;
147+ a.id = "search-result-"+entry.id;
148+ a.classList.add("search-entry", kind.innerText.slice(0,3));
149+ let title = document.createElement("div");
150+ title.classList.add("entry-title");
151+
152+ let prefixname = document.createElement("tt");
153+ prefixname.innerText = entry.prefixname.split(".").reverse().join(".") + (entry.prefixname != "" ? ".": "");
154+ prefixname.classList.add("prefix-name");
155+ let name = document.createElement("tt");
156+ name.classList.add("entry-name");
157+ name.innerText = entry.name;
158+
159+ title.appendChild(kind);
160+ title.appendChild(prefixname);
161+ title.appendChild(name);
162+
163+ let comment = document.createElement("div");
164+ comment.innerText = entry.comment;
165+ comment.classList.add("entry-comment");
166+
167+ a.appendChild(title);
168+ a.appendChild(comment);
169+
170+ list_item.appendChild(a);
171+
172+ search_results.appendChild(list_item);
173+ });
174+ container.appendChild(search_results);
175+
176+ search_results_position = null;
177+ }
178+
179+ function init_search() {
180+ documents = documents.map((d, i) => {return {...d, id: i}});
181+
182+ miniSearch = new MiniSearch({
183+ fields: ['name', 'prefixname', 'comment'],
184+ storeFields: ['name', 'prefixname', 'kind', 'url', 'comment'],
185+ });
186+
187+ miniSearch.addAll(documents);
188+
189+ document.getElementById("in-package-search-input").addEventListener("input", perform_search);
190+ perform_search();
191+ }
192+
193+ function user_interacts() {
194+ let scriptTag = document.createElement("script");
195+ scriptTag.src = "<%s Url.Package.search_index ?version:(Package.url_version package) package.name ~digest %>";
196+ scriptTag.addEventListener("load", init_search);
197+ document.body.appendChild(scriptTag);
198+
199+ document.getElementById("in-package-search-input").removeEventListener("focus", user_interacts);
200+ }
201+
202+ document.getElementById("in-package-search-input").addEventListener("focus", user_interacts);
203+
204+
205+ let search_results_position = null;
206+ function adjust_position(event) {
207+ if (results.length == 0) return;
208+
209+ if (event.key == "ArrowDown") {
210+ if (search_results_position === null) {
211+ search_results_position = 0;
212+ return;
213+ }
214+ if (search_results_position < results.length - 1) {
215+ search_results_position++;
216+ return;
217+ }
218+ } else if (event.key == "ArrowUp") {
219+ if (search_results_position === null) return;
220+
221+ if (search_results_position == 0) {
222+ search_results_position = null;
223+ return;
224+ }
225+
226+ search_results_position--;
227+ return;
228+ }
229+ }
230+
231+ function keydown(event) {
232+ event.stopPropagation();
233+ if (event.key == "Enter") {
234+ if (results.length > 0) {
235+ let url = "/"+results[search_results_position || 0].url;
236+ console.log("going to", url);
237+ window.location = url;
238+
239+ if (url.indexOf("#") != -1) {
240+ let id = url.split("#")[1]
241+ let el = document.getElementById(id);
242+ console.log("id el", id, el);
243+ if (el) setTimeout(() => {
244+ console.log("scroll"); el.scrollIntoView();}, 10);
245+ }
246+
247+ document.getElementById("in-package-search-input").value = "";
248+ results = [];
249+ search_results_position = null;
250+ }
251+ return false;
252+ }
253+
254+ if (search_results_position !== null) document.getElementById("search-result-"+results[search_results_position].id).classList.remove("active");
255+ adjust_position(event);
256+ if (search_results_position !== null) {
257+ let el = document.getElementById("search-result-"+results[search_results_position].id);
258+ el.classList.add("active");
259+ el.scrollIntoView({block: "end"});
260+ }
261+ }
262+
263+ document.getElementById("in-package-search-input").addEventListener("keydown", keydown);
264+ }
265+ </script>
266+ <% | _ -> () ); %>
0 commit comments