Skip to content

Commit

Permalink
Make it less ugly
Browse files Browse the repository at this point in the history
  • Loading branch information
stsewd committed Mar 10, 2023
1 parent f36ee3d commit 58408d1
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 26 deletions.
8 changes: 8 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,11 @@ You can customize these configuration options in your ``conf.py`` file:
Default: ``'minified'``

Type: ``string``

.. confval:: rtd_sphinx_search_filters

Description: List of filters to show in the search bar.

Default: ``{"default": "project:@this"}``

Type: ``dict``
13 changes: 13 additions & 0 deletions sphinx_search/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,16 @@ def _get_static_files(config):


def get_context(config):
"""
Get context for templates.
This mainly returns the settings from the extension
that are needed in our JS code.
"""
filters = config.rtd_sphinx_search_filters.copy()
default_filter = filters.pop("default", "")
# When converting to JSON, the order of the keys is not guaranteed.
# So we pass a list of tuples to preserve the order.
filters = [(name, filter) for name, filter in filters.items()]
return {
"rtd_search_config": {
Expand All @@ -39,6 +46,11 @@ def get_context(config):


def copy_asset_files(app, exception):
"""
Copy assets files to the output directory.
If the name of the file ends with ``_t``, it will be interpreted as a template.
"""
if exception is None: # build succeeded
root = Path(__file__).parent
for file in _get_static_files(app.config):
Expand All @@ -56,6 +68,7 @@ def inject_static_files(app):
"""Inject correct CSS and JS files based on the value of ``rtd_sphinx_search_file_type``."""
for file in _get_static_files(app.config):
file = str(file)
# Templates end with `_t`, Sphinx removes the _t when copying the file.
if file.endswith('_t'):
file = file[:-2]
if file.endswith('.js'):
Expand Down
30 changes: 27 additions & 3 deletions sphinx_search/static/css/rtd_sphinx_search.css
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,12 @@

/* Search result */

.search__result__box {
padding: 0px 10px;
}

.search__result__single {
margin-top: 10px;
padding: 0px 10px;
border-bottom: 1px solid #e6e6e6;
}

Expand Down Expand Up @@ -282,17 +285,38 @@
letter-spacing: 1px;
}

.search__filters {
padding: 0px 10px;
}

.search__filters ul {
list-style: none;
padding: 0;
margin: 0;
}

.search__filters li {
display: inline;
margin-right: 0.5em;
display: inline;
margin-right: 5px;
}

.search__filters .search__filters__title {
color: black;
font-size: 15px;
}

.search__filters button {
background: #f3f4f5;
border-color: #d0d7de;
color: black;
border-style: solid;
border-width: 1px;
border-radius: 999px;
padding: 2px 7px;
font-size: 15px;
}


@media (max-width: 670px) {
.rtd__search__credits {
height: 50px;
Expand Down
1 change: 1 addition & 0 deletions sphinx_search/static/js/rtd_search_config.js_t
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
{# Set the extension options as a JSON object, so it can be used from our JS code. #}
var RTD_SEARCH_CONFIG = {{ rtd_search_config | tojson }};
85 changes: 62 additions & 23 deletions sphinx_search/static/js/rtd_sphinx_search.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ const FETCH_RESULTS_DELAY = 250;
const CLEAR_RESULTS_DELAY = 300;
const RTD_SEARCH_PARAMETER = "rtd_search";

/**
* Parse the search query and return an array with the query and the options.
*
* This is a simplified version of our parser
* https://github.com/readthedocs/readthedocs.org/blob/main/readthedocs/search/api/v3/queryparser.py.
* All `foo:bar` terms are considered options, and the rest are considered part of the query.
*
* @param {String} search_query
* @return {Array} [query, options]
*/
function parseQuery(search_query) {
let query = [];
let options = {};
Expand Down Expand Up @@ -131,6 +141,11 @@ const updateSearchBar = () => {
};


/**
* Set the search query in the modal.
*
* @param {String} query
*/
function setSearchQuery(query) {
let search_outer_input = document.querySelector(".search__outer__input");
search_outer_input.value = query;
Expand Down Expand Up @@ -297,13 +312,16 @@ const generateSingleResult = (resultData, projectName, id) => {
let h2_element = createDomNode("h2", {class: "search__result__title"});
h2_element.innerHTML = page_title;

// If the result is not from the same project,
// then it's from a subproject.
// Results can belong to different projects.
// If the result isn't from the current project, add a note about it.
const project_slug = resultData.project.slug
if (projectName !== project_slug) {
let subtitle = createDomNode("small", {class: "rtd_ui_search_subtitle"});
subtitle.innerText = ` (from project ${project_slug})`;
h2_element.appendChild(subtitle);
// If the result isn't from the current project,
// then we create an absolute link to the page.
page_link = `${resultData.domain}${page_link}`;
}
h2_element.appendChild(createDomNode("br"))

Expand Down Expand Up @@ -577,7 +595,8 @@ const fetchAndGenerateResults = (api_endpoint, parameters) => {
* This html structure will serve as the boilerplate
* to show our search results.
*
* @param {Array} filters: filters to be applied to the search
* @param {Array} filters: filters to be applied to the search.
* {["Filter name", "Filter value"]}
* @return {String} initial html structure
*/
const generateAndReturnInitialHtml = (filters) => {
Expand All @@ -597,35 +616,53 @@ const generateAndReturnInitialHtml = (filters) => {
</div>
</div>
<div class="rtd__search__credits">
Search by <a href="https://readthedocs.org/">Read the Docs</a> & <a href="https://readthedocs-sphinx-search.readthedocs.io/en/latest/">readthedocs-sphinx-search</a>
Search by <a href="https://readthedocs.org/">Read the Docs</a> & <a href="https://readthedocs-sphinx-search.readthedocs.io/en/latest/">readthedocs-sphinx-search</a>.
<a href="https://docs.readthedocs.io/page/server-side-search/syntax.html">Search syntax</a>.
</div>
`;

let div = createDomNode("div", {
class: "search__outer__wrapper search__backdrop",
});
div.innerHTML = innerHTML;

let filters_list = div.querySelector(".search__filters ul");
const config = getConfig();
for (const [filter, value] of filters) {
let li = createDomNode("li");
let button = createDomNode("button");
button.innerText = filter;
button.value = value;
button.addEventListener("click", event => {
event.preventDefault();
let search_query = getSearchTerm();
let [query, _] = parseQuery(search_query);
setSearchQuery(event.target.value + " " + query);
const search_params = {
q: search_query,
project: config.project,
version: config.version,
};
fetchAndGenerateResults(config.api_endpoint, search_params)();
});
li.appendChild(button);
filters_list.appendChild(li);
// Add filters below the search box if present.
if (filters.length > 0) {
let li = createDomNode("li", {"class": "search__filters__title"});
li.innerText = "Filters:";
filters_list.appendChild(li);
}
// Each filter is a button in the filters list.
// Each button contains the index of the filter,
// so we can get the proper filter when clicked.
for (let i = 0, len = filters.length; i < len; i++) {
const [name, filter] = filters[i];
let li = createDomNode("li");
let button = createDomNode("button");
button.innerText = name;
button.value = i;
button.title = filter;
button.addEventListener("click", event => {
event.preventDefault();
// To apply a filter, we extract the current query and
// replace the current options with the selected filter.
let filter = filters[parseInt(event.target.value)][1];
let search_query = getSearchTerm();
let [query, _] = parseQuery(search_query);
search_query = filter + " " + query;
setSearchQuery(search_query);
// Perform the search with the new query.
const search_params = {
q: search_query,
project: config.project,
version: config.version,
};
fetchAndGenerateResults(config.api_endpoint, search_params)();
});
li.appendChild(button);
filters_list.appendChild(li);
}
return div;
};
Expand Down Expand Up @@ -767,6 +804,8 @@ window.addEventListener("DOMContentLoaded", () => {
// cancel previous ajax request.
current_request.cancel();
}
// If the query doesn't have options,
// use the default filter.
let [query, options] = parseQuery(search_query);
if (Object.keys(options).length == 0) {
search_query = config.default_filter + " " + query;
Expand Down

0 comments on commit 58408d1

Please sign in to comment.