Skip to content

Commit cbf74bc

Browse files
authored
Rollup merge of #78584 - notriddle:master, r=GuillaumeGomez
Add keyboard handling to the theme picker menu This PR is mostly designed to bring the theme picker closer to feature parity with the menu bar from docs.rs. Though the rustdoc theme picker is technically already usable from the keyboard, it's really weird that arrow keys work on some of the menus, but not all of them, in the exact same page.
2 parents 6d7098f + 17b8ca9 commit cbf74bc

File tree

3 files changed

+64
-12
lines changed

3 files changed

+64
-12
lines changed

src/librustdoc/html/layout.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,12 @@ pub fn render<T: Print, S: Print>(
7979
{sidebar}\
8080
</nav>\
8181
<div class=\"theme-picker\">\
82-
<button id=\"theme-picker\" aria-label=\"Pick another theme!\">\
82+
<button id=\"theme-picker\" aria-label=\"Pick another theme!\" aria-haspopup=\"menu\">\
8383
<img src=\"{static_root_path}brush{suffix}.svg\" \
8484
width=\"18\" \
8585
alt=\"Pick another theme!\">\
8686
</button>\
87-
<div id=\"theme-choices\"></div>\
87+
<div id=\"theme-choices\" role=\"menu\"></div>\
8888
</div>\
8989
<script src=\"{static_root_path}theme{suffix}.js\"></script>\
9090
<nav class=\"sub\">\

src/librustdoc/html/render/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -798,10 +798,10 @@ function handleThemeButtonsBlur(e) {{
798798
var active = document.activeElement;
799799
var related = e.relatedTarget;
800800
801-
if (active.id !== "themePicker" &&
801+
if (active.id !== "theme-picker" &&
802802
(!active.parentNode || active.parentNode.id !== "theme-choices") &&
803803
(!related ||
804-
(related.id !== "themePicker" &&
804+
(related.id !== "theme-picker" &&
805805
(!related.parentNode || related.parentNode.id !== "theme-choices")))) {{
806806
hideThemeButtonState();
807807
}}

src/librustdoc/html/static/main.js

+60-8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// Local js definitions:
55
/* global addClass, getCurrentValue, hasClass */
66
/* global onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */
7+
/* global hideThemeButtonState, showThemeButtonState */
78

89
if (!String.prototype.startsWith) {
910
String.prototype.startsWith = function(searchString, position) {
@@ -47,6 +48,14 @@ function getSearchElement() {
4748
return document.getElementById("search");
4849
}
4950

51+
function getThemesElement() {
52+
return document.getElementById("theme-choices");
53+
}
54+
55+
function getThemePickerElement() {
56+
return document.getElementById("theme-picker");
57+
}
58+
5059
// Sets the focus on the search bar at the top of the page
5160
function focusSearchBar() {
5261
getSearchInput().focus();
@@ -137,10 +146,6 @@ function defocusSearchBar() {
137146
sidebar.appendChild(div);
138147
}
139148
}
140-
var themePickers = document.getElementsByClassName("theme-picker");
141-
if (themePickers && themePickers.length > 0) {
142-
themePickers[0].style.display = "none";
143-
}
144149
}
145150

146151
function hideSidebar() {
@@ -155,10 +160,6 @@ function defocusSearchBar() {
155160
filler.remove();
156161
}
157162
document.getElementsByTagName("body")[0].style.marginTop = "";
158-
var themePickers = document.getElementsByClassName("theme-picker");
159-
if (themePickers && themePickers.length > 0) {
160-
themePickers[0].style.display = null;
161-
}
162163
}
163164

164165
function showSearchResults(search) {
@@ -376,6 +377,7 @@ function defocusSearchBar() {
376377
document.title = titleBeforeSearch;
377378
}
378379
defocusSearchBar();
380+
hideThemeButtonState();
379381
}
380382

381383
function handleShortcut(ev) {
@@ -412,7 +414,57 @@ function defocusSearchBar() {
412414
case "?":
413415
displayHelp(true, ev);
414416
break;
417+
418+
default:
419+
var themePicker = getThemePickerElement();
420+
if (themePicker.parentNode.contains(ev.target)) {
421+
handleThemeKeyDown(ev);
422+
}
423+
}
424+
}
425+
}
426+
427+
function handleThemeKeyDown(ev) {
428+
var active = document.activeElement;
429+
var themes = getThemesElement();
430+
switch (getVirtualKey(ev)) {
431+
case "ArrowUp":
432+
ev.preventDefault();
433+
if (active.previousElementSibling && ev.target.id !== "theme-picker") {
434+
active.previousElementSibling.focus();
435+
} else {
436+
showThemeButtonState();
437+
themes.lastElementChild.focus();
438+
}
439+
break;
440+
case "ArrowDown":
441+
ev.preventDefault();
442+
if (active.nextElementSibling && ev.target.id !== "theme-picker") {
443+
active.nextElementSibling.focus();
444+
} else {
445+
showThemeButtonState();
446+
themes.firstElementChild.focus();
447+
}
448+
break;
449+
case "Enter":
450+
case "Return":
451+
case "Space":
452+
if (ev.target.id === "theme-picker" && themes.style.display === "none") {
453+
ev.preventDefault();
454+
showThemeButtonState();
455+
themes.firstElementChild.focus();
415456
}
457+
break;
458+
case "Home":
459+
ev.preventDefault();
460+
themes.firstElementChild.focus();
461+
break;
462+
case "End":
463+
ev.preventDefault();
464+
themes.lastElementChild.focus();
465+
break;
466+
// The escape key is handled in handleEscape, not here,
467+
// so that pressing escape will close the menu even if it isn't focused
416468
}
417469
}
418470

0 commit comments

Comments
 (0)