Skip to content

Commit e134ce7

Browse files
committed
rustdoc: use a button instead of a bar for search
This is a response to complaints that the header area takes up too much vertical space, forcing the user to scroll more than they ought to need. It also adds a reasonable way to pick the crate name before actually searching, which was also a feature that people ask for.
1 parent 9c707a8 commit e134ce7

File tree

5 files changed

+222
-120
lines changed

5 files changed

+222
-120
lines changed

src/librustdoc/html/static/css/rustdoc.css

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ h1, h2, h3, h4 {
177177
more aggressively when we want them to. */
178178
overflow-wrap: anywhere;
179179
}
180+
.search-results-main-heading nav.sub {
181+
grid-area: main-heading-h1;
182+
}
180183
.main-heading {
181184
position: relative;
182185
display: grid;
@@ -197,6 +200,16 @@ h1, h2, h3, h4 {
197200
align-items: end;
198201
padding-top: 5px;
199202
}
203+
.search-switcher {
204+
grid-area: main-heading-breadcrumbs;
205+
line-height: 1.25;
206+
display: flex;
207+
flex-wrap: wrap;
208+
color: var(--main-color);
209+
align-items: baseline;
210+
white-space: nowrap;
211+
margin-top: -1px;
212+
}
200213
.rustdoc-breadcrumbs a {
201214
padding: 4px 0;
202215
margin: -4px 0;
@@ -251,6 +264,7 @@ rustdoc-toolbar,
251264
summary.hideme,
252265
.scraped-example-list,
253266
.rustdoc-breadcrumbs,
267+
.search-switcher,
254268
/* This selector is for the items listed in the "all items" page. */
255269
ul.all-items {
256270
font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
@@ -1002,16 +1016,15 @@ div.where {
10021016
nav.sub {
10031017
flex-grow: 1;
10041018
flex-flow: row nowrap;
1005-
margin: 4px 0 0 0;
10061019
display: flex;
1007-
align-items: center;
1020+
align-items: start;
1021+
margin-top: 4px;
10081022
}
10091023
.search-form {
10101024
position: relative;
10111025
display: flex;
10121026
height: 34px;
10131027
flex-grow: 1;
1014-
margin-bottom: 4px;
10151028
}
10161029
.src nav.sub {
10171030
margin: 0 0 -10px 0;
@@ -1115,21 +1128,6 @@ table,
11151128
padding-right: 1.25rem;
11161129
}
11171130

1118-
.search-results-title {
1119-
margin-top: 0;
1120-
white-space: nowrap;
1121-
/* flex layout allows shrinking the <select> appropriately if it becomes too large */
1122-
display: flex;
1123-
/* make things look like in a line, despite the fact that we're using a layout
1124-
with boxes (i.e. from the flex layout) */
1125-
align-items: baseline;
1126-
}
1127-
.search-results-title + .sub-heading {
1128-
color: var(--main-color);
1129-
display: flex;
1130-
align-items: baseline;
1131-
white-space: nowrap;
1132-
}
11331131
#crate-search-div {
11341132
/* ensures that 100% in properties of #crate-search-div:after
11351133
are relative to the size of this div */
@@ -1854,7 +1852,7 @@ a.tooltip:hover::after {
18541852
border-bottom: 1px solid var(--border-color);
18551853
}
18561854

1857-
#settings-menu, #help-button, button#toggle-all-docs {
1855+
#search-button, #settings-menu, #help-button, button#toggle-all-docs {
18581856
margin-left: var(--button-left-margin);
18591857
display: flex;
18601858
line-height: 1.25;
@@ -1882,7 +1880,11 @@ a.tooltip:hover::after {
18821880
.hide-sidebar .src #sidebar-button {
18831881
position: static;
18841882
}
1885-
#settings-menu > a, #help-button > a, #sidebar-button > a, button#toggle-all-docs {
1883+
#search-button > a,
1884+
#settings-menu > a,
1885+
#help-button > a,
1886+
#sidebar-button > a,
1887+
button#toggle-all-docs {
18861888
display: flex;
18871889
align-items: center;
18881890
justify-content: center;
@@ -1891,11 +1893,11 @@ a.tooltip:hover::after {
18911893
border-radius: var(--button-border-radius);
18921894
color: var(--main-color);
18931895
}
1894-
#settings-menu > a, #help-button > a, button#toggle-all-docs {
1896+
#search-button > a, #settings-menu > a, #help-button > a, button#toggle-all-docs {
18951897
width: 80px;
18961898
border-radius: var(--toolbar-button-border-radius);
18971899
}
1898-
#settings-menu > a, #help-button > a {
1900+
#search-button > a, #settings-menu > a, #help-button > a {
18991901
min-width: 0;
19001902
}
19011903
#sidebar-button > a {
@@ -1904,6 +1906,7 @@ a.tooltip:hover::after {
19041906
width: 33px;
19051907
}
19061908

1909+
#search-button > a:hover, #search-button > a:focus-visible,
19071910
#settings-menu > a:hover, #settings-menu > a:focus-visible,
19081911
#help-button > a:hover, #help-button > a:focus-visible,
19091912
#sidebar-button > a:hover, #sidebar-button > a:focus-visible,
@@ -1912,6 +1915,19 @@ button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible {
19121915
text-decoration: none;
19131916
}
19141917

1918+
#search-button > a:before {
1919+
/* Magnifying glass */
1920+
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
1921+
width="18" height="18" viewBox="0 0 16 16">\
1922+
<circle r="5" cy="7" cx="6" style="fill:none;stroke:currentColor;stroke-width:3"></circle>\
1923+
<path d="M 16,15 10,10" style="fill:none;stroke:currentColor;stroke-width:4"></path>\
1924+
<desc>Search</desc>\
1925+
</svg>');
1926+
width: 18px;
1927+
height: 18px;
1928+
filter: var(--settings-menu-filter);
1929+
}
1930+
19151931
#settings-menu > a:before {
19161932
/* Wheel <https://www.svgrepo.com/svg/384069/settings-cog-gear> */
19171933
content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \
@@ -1954,6 +1970,7 @@ button#toggle-all-docs:before {
19541970
filter: var(--settings-menu-filter);
19551971
}
19561972

1973+
#search-button > a:before,
19571974
button#toggle-all-docs:before,
19581975
#help-button > a:before,
19591976
#settings-menu > a:before {
@@ -1962,6 +1979,7 @@ button#toggle-all-docs:before,
19621979
}
19631980

19641981
@media not (pointer: coarse) {
1982+
#search-button > a:hover:before,
19651983
button#toggle-all-docs:hover:before,
19661984
#help-button > a:hover:before,
19671985
#settings-menu > a:hover:before {
@@ -2295,6 +2313,20 @@ However, it's not needed with smaller screen width because the doc/code block is
22952313
.side-by-side > div {
22962314
width: auto;
22972315
}
2316+
2317+
/* Text label takes up too much space at this size. */
2318+
rustdoc-toolbar span.label {
2319+
display: none;
2320+
}
2321+
#search-button > a, #settings-menu > a, #help-button > a, button#toggle-all-docs {
2322+
width: 33px;
2323+
}
2324+
#settings.popover {
2325+
--popover-arrow-offset: 86px;
2326+
}
2327+
#help.popover {
2328+
--popover-arrow-offset: 48px;
2329+
}
22982330
}
22992331

23002332
/*
@@ -2325,20 +2357,6 @@ in src-script.js and main.js
23252357
visibility: hidden;
23262358
}
23272359

2328-
/* Text label takes up too much space at this size. */
2329-
rustdoc-toolbar span.label {
2330-
display: none;
2331-
}
2332-
#settings-menu > a, #help-button > a, button#toggle-all-docs {
2333-
width: 33px;
2334-
}
2335-
#settings.popover {
2336-
--popover-arrow-offset: 86px;
2337-
}
2338-
#help.popover {
2339-
--popover-arrow-offset: 48px;
2340-
}
2341-
23422360
.rustdoc {
23432361
/* Sidebar should overlay main content, rather than pushing main content to the right.
23442362
Turn off `display: flex` on the body element. */

src/librustdoc/html/static/js/main.js

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,36 @@ function preLoadCss(cssUrl) {
241241
window.searchState = {
242242
rustdocToolbar: document.querySelector("rustdoc-toolbar"),
243243
loadingText: "Loading search results...",
244-
input: document.getElementsByClassName("search-input")[0],
245-
outputElement: () => {
244+
inputElement: () => {
245+
let el = document.getElementsByClassName("search-input")[0];
246+
if (!el) {
247+
const out = searchState.outputElement().parentElement;
248+
const hdr = document.createElement("div");
249+
hdr.className = "main-heading search-results-main-heading";
250+
const rootPath = getVar("root-path");
251+
const currentCrate = getVar("current-crate");
252+
hdr.innerHTML = `<nav class="sub">
253+
<form class="search-form">
254+
<span></span> <!-- This empty span is a hacky fix for Safari: see #93184 -->
255+
<div id="sidebar-button" tabindex="-1">
256+
<a href="${rootPath}${currentCrate}/all.html" title="show sidebar"></a>
257+
</div>
258+
<input
259+
class="search-input"
260+
name="search"
261+
aria-label="Run search in the documentation"
262+
autocomplete="off"
263+
spellcheck="false"
264+
placeholder="Type ‘S’ or ‘/’ to search, ‘?’ for more options…"
265+
type="search">
266+
</form>
267+
</nav><div class="search-switcher"></div>`;
268+
out.insertBefore(hdr, searchState.outputElement());
269+
el = document.getElementsByClassName("search-input")[0];
270+
}
271+
return el;
272+
},
273+
containerElement: () => {
246274
let el = document.getElementById("search");
247275
if (!el) {
248276
el = document.createElement("section");
@@ -251,6 +279,16 @@ function preLoadCss(cssUrl) {
251279
}
252280
return el;
253281
},
282+
outputElement: () => {
283+
const container = searchState.containerElement();
284+
let el = container.querySelector(".search-out");
285+
if (!el) {
286+
el = document.createElement("div");
287+
el.className = "search-out";
288+
container.appendChild(el);
289+
}
290+
return el;
291+
},
254292
title: document.title,
255293
titleBeforeSearch: document.title,
256294
timeout: null,
@@ -268,22 +306,38 @@ function preLoadCss(cssUrl) {
268306
searchState.timeout = null;
269307
}
270308
},
271-
isDisplayed: () => searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID,
309+
isDisplayed: () => searchState.containerElement().parentElement.id ===
310+
ALTERNATIVE_DISPLAY_ID,
272311
// Sets the focus on the search bar at the top of the page
273312
focus: () => {
274-
searchState.input.focus();
313+
searchState.showResults();
314+
searchState.inputElement().focus();
275315
},
276316
// Removes the focus from the search bar.
277317
defocus: () => {
278-
searchState.input.blur();
318+
searchState.inputElement().blur();
319+
},
320+
toggle: () => {
321+
if (searchState.isDisplayed()) {
322+
searchState.defocus();
323+
searchState.hideResults();
324+
} else {
325+
searchState.focus();
326+
}
279327
},
280-
showResults: search => {
281-
if (search === null || typeof search === "undefined") {
282-
search = searchState.outputElement();
328+
showResults: () => {
329+
document.title = searchState.title;
330+
if (searchState.isDisplayed()) {
331+
return;
283332
}
333+
const search = searchState.containerElement();
284334
switchDisplayedElement(search);
285335
searchState.mouseMovedAfterSearch = false;
286-
document.title = searchState.title;
336+
const btn = document.querySelector("#search-button a");
337+
if (browserSupportsHistoryApi() && btn &&
338+
searchState.getQueryStringParams().search === undefined) {
339+
history.pushState(null, "", btn.href);
340+
}
287341
},
288342
removeQueryParameters: () => {
289343
// We change the document title.
@@ -309,11 +363,8 @@ function preLoadCss(cssUrl) {
309363
return params;
310364
},
311365
setup: () => {
312-
const search_input = searchState.input;
313-
if (!searchState.input) {
314-
return;
315-
}
316366
let searchLoaded = false;
367+
const search_input = searchState.inputElement();
317368
// If you're browsing the nightly docs, the page might need to be refreshed for the
318369
// search to work because the hash of the JS scripts might have changed.
319370
function sendSearchForm() {
@@ -333,8 +384,16 @@ function preLoadCss(cssUrl) {
333384
loadSearch();
334385
});
335386

336-
if (search_input.value !== "") {
337-
loadSearch();
387+
const btn = document.getElementById("search-button");
388+
if (btn) {
389+
btn.onclick = event => {
390+
if (event.ctrlKey || event.altKey || event.metaKey) {
391+
return;
392+
}
393+
event.preventDefault();
394+
searchState.toggle();
395+
loadSearch();
396+
};
338397
}
339398

340399
const params = searchState.getQueryStringParams();
@@ -346,7 +405,7 @@ function preLoadCss(cssUrl) {
346405
setLoadingSearch: () => {
347406
const search = searchState.outputElement();
348407
search.innerHTML = "<h3 class=\"search-loading\">" + searchState.loadingText + "</h3>";
349-
searchState.showResults(search);
408+
searchState.showResults();
350409
},
351410
descShards: new Map(),
352411
loadDesc: async function({descShard, descIndex}) {

0 commit comments

Comments
 (0)