diff --git a/src/pydata_sphinx_theme/assets/scripts/pydata-sphinx-theme.js b/src/pydata_sphinx_theme/assets/scripts/pydata-sphinx-theme.js index f2b677077..48a1e4257 100644 --- a/src/pydata_sphinx_theme/assets/scripts/pydata-sphinx-theme.js +++ b/src/pydata_sphinx_theme/assets/scripts/pydata-sphinx-theme.js @@ -194,7 +194,7 @@ var findSearchInput = () => { } 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", + ":not(#pst-search-dialog) > form.bd-search", ); } return form.querySelector("input"); @@ -208,22 +208,30 @@ var findSearchInput = () => { */ var toggleSearchField = () => { // Find the search input to highlight - let input = findSearchInput(); + const input = findSearchInput(); // 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 searchPopupWrapper = document.querySelector(".search-button__wrapper"); - let hiddenInput = searchPopupWrapper.querySelector("input"); + const searchDialog = document.getElementById("pst-search-dialog"); + const hiddenInput = searchDialog.querySelector("input"); if (input === hiddenInput) { - searchPopupWrapper.classList.toggle("show"); - } - // when toggling off the search field, remove its focus - if (document.activeElement === input) { - input.blur(); + if (searchDialog.open) { + searchDialog.close(); + } else { + // Note: browsers should focus the input field inside the modal dialog + // automatically when it is opened. + searchDialog.showModal(); + } } else { - input.focus(); - input.select(); - input.scrollIntoView({ block: "center" }); + // if the input field is not the hidden one, then toggle its focus state + + if (document.activeElement === input) { + input.blur(); + } else { + input.focus(); + input.select(); + input.scrollIntoView({ block: "center" }); + } } }; @@ -295,11 +303,30 @@ var setupSearchButtons = () => { btn.onclick = toggleSearchField; }); - // Add the search button overlay event callback - let overlay = document.querySelector(".search-button__overlay"); - if (overlay) { - overlay.onclick = toggleSearchField; - } + // If user clicks outside the search modal dialog, then close it. + const searchDialog = document.getElementById("pst-search-dialog"); + // Dialog click handler includes clicks on dialog ::backdrop. + searchDialog.addEventListener("click", (event) => { + if (!searchDialog.open) { + return; + } + + // Dialog.getBoundingClientRect() does not include ::backdrop. (This is the + // trick that allows us to determine if click was inside or outside of the + // dialog: click handler includes backdrop, getBoundingClientRect does not.) + const { left, right, top, bottom } = searchDialog.getBoundingClientRect(); + + // 0, 0 means top left + const clickWasOutsideDialog = + event.clientX < left || + right < event.clientX || + event.clientY < top || + bottom < event.clientY; + + if (clickWasOutsideDialog) { + searchDialog.close(); + } + }); }; /******************************************************************************* diff --git a/src/pydata_sphinx_theme/assets/styles/components/_search.scss b/src/pydata_sphinx_theme/assets/styles/components/_search.scss index ce8256d4c..e75b08d72 100644 --- a/src/pydata_sphinx_theme/assets/styles/components/_search.scss +++ b/src/pydata_sphinx_theme/assets/styles/components/_search.scss @@ -16,6 +16,15 @@ color: var(--pst-color-text-muted); } + // Hoist the focus ring from the input field to its parent + &:focus-within { + box-shadow: $focus-ring-box-shadow; + + input:focus { + box-shadow: none; + } + } + .icon { position: absolute; color: var(--pst-color-border); @@ -28,7 +37,11 @@ color: var(--pst-color-text-muted); } - input { + input.form-control { + background-color: var(--pst-color-background); + color: var(--pst-color-text-base); + border: none; + // Inner-text of the search bar &::placeholder { color: var(--pst-color-text-muted); @@ -39,46 +52,36 @@ &::-webkit-search-decoration { appearance: none; } + + &:focus, + &:focus-visible { + color: var(--pst-color-text-muted); + } } // Shows off the keyboard shortcuts for the button .search-button__kbd-shortcut { display: flex; - position: absolute; - right: 0.5rem; + margin-inline-end: 0.5rem; color: var(--pst-color-border); } } -.form-control { - background-color: var(--pst-color-background); - color: var(--pst-color-text-base); - - &:focus, - &:focus-visible { - border: none; - background-color: var(--pst-color-background); - color: var(--pst-color-text-muted); - } -} - /** * Search button - located in the navbar */ - -// Search link icon should be a bit bigger since it is separate from icon links .search-button i { + // Search link icon should be a bit bigger since it is separate from icon links font-size: 1.3rem; } -// __search-container will only show up when we use the search pop-up bar -.search-button__search-container, -.search-button__overlay { +/** + * The search modal + */ +#pst-search-dialog { display: none; -} -.search-button__wrapper.show { - .search-button__search-container { + &[open] { display: flex; // Center in middle of screen just underneath header @@ -91,30 +94,24 @@ margin-top: 0.5rem; width: 90%; max-width: 800px; - } + background-color: transparent; + padding: $focus-ring-width; + border: none; - .search-button__overlay { - display: flex; - position: fixed; - z-index: $zindex-modal-backdrop; - background-color: black; - opacity: 0.5; - width: 100%; - height: 100%; - top: 0; - left: 0; - } + &::backdrop { + background-color: black; + opacity: 0.5; + } - form.bd-search { - flex-grow: 1; - padding-top: 0; - padding-bottom: 0; - } + form.bd-search { + flex-grow: 1; - // Font and input text a bit bigger - svg, - input { - font-size: var(--pst-font-size-icon); + // Font and input text a bit bigger + svg, + input { + font-size: var(--pst-font-size-icon); + } + } } } @@ -141,7 +138,7 @@ border-radius: $search-button-border-radius; } - // The keyboard shotcut text + // The keyboard shortcut text .search-button__default-text { font-size: var(--bs-nav-link-font-size); font-weight: var(--bs-nav-link-font-weight); diff --git a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/search-field.html b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/search-field.html index c1d605292..cd6891028 100644 --- a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/search-field.html +++ b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/search-field.html @@ -6,7 +6,6 @@ {# A search field pop-up that will only show when the search button is clicked #} -
-
-
{% include "../components/search-field.html" %}
-
+ + {% include "../components/search-field.html" %} + {% include "sections/announcement.html" %}