Skip to content

Commit

Permalink
Handle varied search field configurations (#807)
Browse files Browse the repository at this point in the history
Co-authored-by: Chris Holdgraf <choldgraf@gmail.com>
  • Loading branch information
drammock and choldgraf authored Jul 13, 2022
1 parent 3d90a69 commit 6dab1f9
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 51 deletions.
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
"custom-template",
], # This ensures we test for custom sidebars
"demo/no-sidebar": [], # Test what page looks like with no sidebar items
"demo/persistent-search-field": ["search-field"],
}

myst_heading_anchors = 2
Expand Down
1 change: 1 addition & 0 deletions docs/demo/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ See the sections to the left and below to explore.
:caption: Reference and test section. Mostly meant for developers to check that things look OK.

no-sidebar
persistent-search-field
mult_headers
subpages/index
Link to an external site <https://jupyterbook.org/>
Expand Down
3 changes: 3 additions & 0 deletions docs/demo/persistent-search-field.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Test of persistent search field

There should also be a persistent (non-hidden) search field in the left sidebar of this page, and if you use the keyboard shortcut it should focus the persistent field, not overlay the hidden one.
30 changes: 15 additions & 15 deletions docs/user_guide/configuring.rst
Original file line number Diff line number Diff line change
Expand Up @@ -910,22 +910,22 @@ any other context values.
"some_other_arg": "?some-other-arg"
}
Search bar
==========
Search bar / search button
==========================

By default, the Search Bar is hidden, and will be displayed when a user either:
By default, the search input field is hidden, and there is a search button
(a magnifying glass icon :fas:`search`) in the top navbar.
The search input field will be displayed when a user either:

- Clicks the magnifying class icon in the header: :fas:`search`.
- Presses the keyboard shortcut :kbd:`Ctrl` + :kbd:`K` (Windows) or :kbd:`` + :kbd:`K` (Mac).
- Clicks the search button in the header.
- Presses the keyboard shortcut :kbd:`Ctrl` + :kbd:`K` (Linux, Windows) or :kbd:`` + :kbd:`K` (macOS).

You can also configure some aspects of the search bar, described below.
You can also configure some aspects of the search button and search field, described below.

Configure the search bar position
---------------------------------
Configure the search field position
-----------------------------------

To modify the position of the search bar, add the ``search-field.html``
template to your **sidebar**, or to one of the **navbar** positions, depending
on where you want it to be placed.
The position of the search *button* is controlled by ``search-button`` and by default is included in ``html_theme_options["navbar_end"]``; you may move it elsewhere as befits your site's layout, or remove it. You can also add an always-visible search field to some/all pages in your site by adding ``search-field.html`` to one of the configuration variables (e.g., ``html_sidebars``, ``html_theme_options["footer_items"]``, etc).

For example, if you'd like the search field to be in your side-bar, add it to
the sidebar templates like so:
Expand All @@ -936,7 +936,7 @@ the sidebar templates like so:
"**": ["search-field.html", "sidebar-nav-bs.html", "sidebar-ethical-ads.html"]
}
If instead you'd like to put the search bar in the top navbar, use the
If instead you'd like to put the search field in the top navbar, use the
following configuration:

.. code:: python
Expand All @@ -945,10 +945,10 @@ following configuration:
"navbar_end": ["navbar-icon-links.html", "search-field.html"]
}
.. note::
.. warning::

If a page includes *both* the search button and an always-visible search field, the keyboard shortcuts will focus the always-visible field and the hidden search field overlay will not display. *This may not be what you want:* on small screens (i.e. mobile devices) the sidebars may be hidden in a drawer, and if the persistent search field is there, it may receive focus without actually being made visible. It is **strongly recommended** that you use *either* search button and the hidden/overlaid field that comes with it, *or* use a persistent search field in a place that makes sense for your layout.

By default the search bar is placed in the sidebar. If you wish to move it to the navbar,
explicitly define a list of sidebar templates in `html_sidebars` and omit the `search-field.html` entry.

Configure the search bar text
-----------------------------
Expand Down
92 changes: 92 additions & 0 deletions src/pydata_sphinx_theme/assets/scripts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,98 @@ function scrollToActive() {
});
}

/*******************************************************************************
* Search
*/
var changeShortcutText = () => {
// Change the search hint to `meta key` if we are a Mac
let forms = document.querySelectorAll("form.bd-search");
var isMac = window.navigator.platform.toUpperCase().indexOf("MAC") >= 0;
if (isMac) {
forms.forEach(
(f) => (f.querySelector("kbd.kbd-shortcut__modifier").innerText = "⌘")
);
}
};

var findSearchInput = () => {
// find the search form(s) on the page
let forms = document.querySelectorAll("form.bd-search");
if (!forms.length) {
// no search form found
return;
} else {
var form;
if (forms.length == 1) {
// there is exactly one search form (persistent or hidden)
form = forms[0];
} else {
// must be at least one persistent form, use the first persistent one
form = document.querySelector(
"div:not(.search-button__search-container) > form.bd-search"
);
}
return form.querySelector("input");
}
};

var toggleSearchField = () => {
// focus/unfocus the search field (and if it's an auto-hiding one,
// show/hide it too)
let input = findSearchInput();
let button = document.getElementById("bd-search-button");
// if the input field is the hidden one (the one associated with the
// search button) then toggle the button state (to show/hide the field)
let hidden_input = document.querySelector(
".search-button__search-container input"
);
if (input === hidden_input) {
button.classList.toggle("show");
}
// when toggling off the search field, remove its focus
if (document.activeElement === input) {
input.blur();
} else {
input.focus();
input.select();
input.scrollIntoView({ block: "center" });
}
};

// Add an event listener for toggleSearchField() for Ctrl/Cmd + K
window.addEventListener(
"keydown",
(event) => {
let input = findSearchInput();
// toggle on Ctrl+k or ⌘+k
if ((event.ctrlKey || event.metaKey) && event.code == "KeyK") {
event.preventDefault();
toggleSearchField();
}
// also allow Escape key to hide (but not show) the dynamic search field
else if (document.activeElement === input && event.code == "Escape") {
toggleSearchField();
}
},
true
);

window.onload = function () {
changeShortcutText();
let button = document.getElementById("bd-search-button");
let overlay = document.querySelector("div.search-button__overlay");
if (button) {
button.onclick = toggleSearchField;
}
if (overlay) {
overlay.onclick = toggleSearchField;
}
};

/*******************************************************************************
* Finalize
*/

// This is equivalent to the .ready() function as described in
// https://api.jquery.com/ready/
$(addModeListener);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,14 @@
{# Search page has its own UI / UX for search #}
<!-- A button that will trigger a search field to be displayed on click. -->
{% set containerClass="search-button__search-container" %}
<script>
var toggleSearchField = () => {
// Class to make the search field appear and expand the clickable div behind it
// Note that `.show` will only have an effect on pages that aren't `search.html`
let button = document.getElementById("bd-search-button");
button.classList.toggle('show');

// We'll grab the elements we need to modify for the search field
let form = document.querySelector("form.bd-search");
let input = form.querySelector("input");

// Change the symbol to `meta key` if we are a Mac
var isMac = window.navigator.platform.toUpperCase().indexOf('MAC')>=0;
if (isMac) {
let kbd = form.querySelector("kbd.kbd-shortcut__modifier");
kbd.innerText = "⌘";
};

// Select the search input field, and focus the page on it
input.focus();
input.select();
input.scrollIntoView({block: "center"});
};

// Add an event listener for this function for Ctrl/Cmd + K
window.addEventListener("keydown", (event) => {
if ((event.ctrlKey || event.metaKey) && event.code == "KeyK") {
event.preventDefault();
toggleSearchField();
}}, true);
</script>

<!-- An overlay that will expand in the background and cause the search to disappear when clicked -->
<button class="btn btn-sm navbar-btn search-button" id="bd-search-button" onclick="toggleSearchField()" title="{{ _('Toggle search field') }}">
<button class="btn btn-sm navbar-btn search-button" id="bd-search-button" title="{{ _('Search') }}">
<i class="fas fa-search"></i>
</button>

{#- Only on pages not `search.html`. This is because we only want the in-page search on this page -#}
{%- if pagename !="search" -%}
{#- This will be hidden by default until click -#}
<div class="search-button__overlay" onclick="toggleSearchField()"></div>
<div class="search-button__overlay"></div>
<div class="{{ containerClass }}">
{% include "../components/search-field.html" %}
</div>
{% endif %}

0 comments on commit 6dab1f9

Please sign in to comment.