Skip to content

Commit 8bd85d4

Browse files
authored
Use Algolia UI widgets for docs search (#847)
1 parent da4bb38 commit 8bd85d4

File tree

7 files changed

+223
-129
lines changed

7 files changed

+223
-129
lines changed

docs/extra/tweaks.css

+22
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,25 @@ li.md-nav__item>a[href^="#logfire.configure("] {
7979
.md-search__output em {
8080
color: var(--md-primary-fg-color);
8181
}
82+
83+
.md-search__input::-webkit-search-decoration,
84+
.md-search__input::-webkit-search-cancel-button,
85+
.md-search__input::-webkit-search-results-button,
86+
.md-search__input::-webkit-search-results-decoration {
87+
-webkit-appearance:none;
88+
}
89+
90+
.md-search-result__article {
91+
padding-bottom: .55em;
92+
}
93+
94+
.ais-SearchBox-form {
95+
display: flex;
96+
flex-direction: row;
97+
gap: 10px;
98+
}
99+
100+
.md-search-result mark.ais-Highlight-highlighted,
101+
.md-search-result mark.ais-Snippet-highlighted {
102+
color: var(--md-primary-fg-color);
103+
}

docs/javascripts/algolia-search.js

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
const ALGOLIA_APP_ID = 'KPPUDTIAVX';
2+
const ALGOLIA_API_KEY = '1fc841595212a2c3afe8c24dd4cb8790';
3+
const ALGOLIA_INDEX_NAME = 'alt-logfire-docs';
4+
5+
const { liteClient: algoliasearch } = window['algoliasearch/lite'];
6+
const searchClient = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_API_KEY);
7+
8+
const search = instantsearch({
9+
indexName: ALGOLIA_INDEX_NAME,
10+
searchClient,
11+
searchFunction(helper) {
12+
const query = helper.state.query
13+
14+
if (query && query.length > 1) {
15+
document.querySelector('#hits').hidden = false
16+
document.querySelector('#type-to-start-searching').hidden = true
17+
helper.search();
18+
} else {
19+
document.querySelector('#hits').hidden = true
20+
document.querySelector('#type-to-start-searching').hidden = false
21+
}
22+
},
23+
});
24+
25+
// create custom widget, to integrate with MkDocs built-in markup
26+
const customSearchBox = instantsearch.connectors.connectSearchBox((renderOptions, isFirstRender) => {
27+
const { query, refine, clear } = renderOptions;
28+
29+
if (isFirstRender) {
30+
document.querySelector('#searchbox').addEventListener('input', event => {
31+
refine(event.target.value);
32+
});
33+
34+
document.querySelector('#searchbox').addEventListener('focus', () => {
35+
document.querySelector('#__search').checked = true;
36+
});
37+
38+
document.querySelector('#searchbox-clear').addEventListener('click', () => {
39+
clear();
40+
});
41+
42+
document.querySelector('#searchbox').addEventListener('keydown', (event) => {
43+
// on down arrow, find the first search result and focus it
44+
if (event.key === 'ArrowDown') {
45+
document.querySelector('.md-search-result__link').focus();
46+
event.preventDefault();
47+
}
48+
});
49+
50+
// for Hits, add keyboard navigation
51+
document.querySelector('#hits').addEventListener('keydown', (event) => {
52+
if (event.key === 'ArrowDown') {
53+
const next = event.target.parentElement.nextElementSibling;
54+
if (next) {
55+
next.querySelector('.md-search-result__link').focus();
56+
event.preventDefault();
57+
}
58+
} else if (event.key === 'ArrowUp') {
59+
const prev = event.target.parentElement.previousElementSibling;
60+
if (prev) {
61+
prev.querySelector('.md-search-result__link').focus();
62+
} else {
63+
document.querySelector('#searchbox').focus();
64+
}
65+
event.preventDefault();
66+
}
67+
})
68+
69+
document.addEventListener('keydown', (event) => {
70+
// if forward slash is pressed, focus the search box
71+
if (event.key === '/' && event.target.tagName !== 'INPUT') {
72+
document.querySelector('#searchbox').focus();
73+
event.preventDefault();
74+
}
75+
})
76+
}
77+
78+
79+
document.querySelector('#type-to-start-searching').hidden = query.length > 1;
80+
document.querySelector('#searchbox').value = query;
81+
});
82+
83+
search.addWidgets([
84+
customSearchBox({}),
85+
86+
instantsearch.widgets.hits({
87+
container: '#hits',
88+
cssClasses: {
89+
'list': 'md-search-result__list',
90+
'item': 'md-search-result__item'
91+
},
92+
templates: {
93+
item: (hit, { html, components }) => {
94+
return html`
95+
<a href="${hit.abs_url}" class="md-search-result__link" tabindex="-1">
96+
<div class="md-search-result__article md-typeset">
97+
<div class="md-search-result__icon md-icon"></div>
98+
<h1>${components.Highlight({ attribute: 'title', hit })}</h1>
99+
<article>${components.Snippet({ attribute: 'content', hit })} </article>
100+
</div>
101+
</a>`
102+
},
103+
},
104+
})
105+
]);
106+
107+
search.start();

docs/javascripts/search-worker.js

-69
This file was deleted.

docs/overrides/main.html

-45
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,5 @@
11
{% extends "base.html" %}
22

3-
{% block config %}
4-
{%- set app = {
5-
"base": base_url,
6-
"features": features,
7-
"translations": {},
8-
"search": base_url + "/javascripts/search-worker.js" | url
9-
} -%}
10-
11-
<!-- Versioning -->
12-
{%- if config.extra.version -%}
13-
{%- set mike = config.plugins.get("mike") -%}
14-
{%- if not mike or mike.config.version_selector -%}
15-
{%- set _ = app.update({ "version": config.extra.version }) -%}
16-
{%- endif -%}
17-
{%- endif -%}
18-
19-
<!-- Tags -->
20-
{%- if config.extra.tags -%}
21-
{%- set _ = app.update({ "tags": config.extra.tags }) -%}
22-
{%- endif -%}
23-
24-
<!-- Translations -->
25-
{%- set translations = app.translations -%}
26-
{%- for key in [
27-
"clipboard.copy",
28-
"clipboard.copied",
29-
"search.result.placeholder",
30-
"search.result.none",
31-
"search.result.one",
32-
"search.result.other",
33-
"search.result.more.one",
34-
"search.result.more.other",
35-
"search.result.term.missing",
36-
"select.version"
37-
] -%}
38-
{%- set _ = translations.update({ key: lang.t(key) }) -%}
39-
{%- endfor -%}
40-
41-
<!-- Configuration -->
42-
<script id="__config" type="application/json">
43-
{{- app | tojson -}}
44-
</script>
45-
<!-- Add scripts that need to run afterwards here -->
46-
{% endblock %}
47-
483
{% block content %}
494
{{ super() }}
505
<script src="/flarelytics/client.js"></script>

docs/overrides/partials/search.html

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<div class="md-search" role="dialog">
2+
<label class="md-search__overlay" for="__search"></label>
3+
<div class="md-search__inner" role="search">
4+
<form class="md-search__form" name="search">
5+
<input type="text" id="searchbox" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" required="">
6+
<label class="md-search__icon md-icon" for="__search">
7+
8+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"></path></svg>
9+
10+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"></path></svg>
11+
</label>
12+
<nav class="md-search__options" aria-label="Search">
13+
14+
<button id="searchbox-clear" type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
15+
16+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"></path></svg>
17+
</button>
18+
</nav>
19+
20+
<div class="md-search__suggest"></div>
21+
22+
</form>
23+
<div class="md-search__output">
24+
<div class="md-search__scrollwrap" tabindex="0">
25+
<div class="md-search-result">
26+
<div class="md-search-result__meta" id="type-to-start-searching">Type to start searching</div>
27+
<ol class="md-search-result__list" id="hits" role="presentation"></ol>
28+
</div>
29+
</div>
30+
</div>
31+
</div>
32+
</div>

0 commit comments

Comments
 (0)