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.
This commit is contained in:
Mara Bos 2020-11-05 10:29:45 +01:00 committed by GitHub
commit cbf74bc4ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 64 additions and 12 deletions

View File

@ -79,12 +79,12 @@ pub fn render<T: Print, S: Print>(
{sidebar}\
</nav>\
<div class=\"theme-picker\">\
<button id=\"theme-picker\" aria-label=\"Pick another theme!\">\
<button id=\"theme-picker\" aria-label=\"Pick another theme!\" aria-haspopup=\"menu\">\
<img src=\"{static_root_path}brush{suffix}.svg\" \
width=\"18\" \
alt=\"Pick another theme!\">\
</button>\
<div id=\"theme-choices\"></div>\
<div id=\"theme-choices\" role=\"menu\"></div>\
</div>\
<script src=\"{static_root_path}theme{suffix}.js\"></script>\
<nav class=\"sub\">\

View File

@ -798,10 +798,10 @@ function handleThemeButtonsBlur(e) {{
var active = document.activeElement;
var related = e.relatedTarget;
if (active.id !== "themePicker" &&
if (active.id !== "theme-picker" &&
(!active.parentNode || active.parentNode.id !== "theme-choices") &&
(!related ||
(related.id !== "themePicker" &&
(related.id !== "theme-picker" &&
(!related.parentNode || related.parentNode.id !== "theme-choices")))) {{
hideThemeButtonState();
}}

View File

@ -4,6 +4,7 @@
// Local js definitions:
/* global addClass, getCurrentValue, hasClass */
/* global onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */
/* global hideThemeButtonState, showThemeButtonState */
if (!String.prototype.startsWith) {
String.prototype.startsWith = function(searchString, position) {
@ -47,6 +48,14 @@ function getSearchElement() {
return document.getElementById("search");
}
function getThemesElement() {
return document.getElementById("theme-choices");
}
function getThemePickerElement() {
return document.getElementById("theme-picker");
}
// Sets the focus on the search bar at the top of the page
function focusSearchBar() {
getSearchInput().focus();
@ -137,10 +146,6 @@ function defocusSearchBar() {
sidebar.appendChild(div);
}
}
var themePickers = document.getElementsByClassName("theme-picker");
if (themePickers && themePickers.length > 0) {
themePickers[0].style.display = "none";
}
}
function hideSidebar() {
@ -155,10 +160,6 @@ function defocusSearchBar() {
filler.remove();
}
document.getElementsByTagName("body")[0].style.marginTop = "";
var themePickers = document.getElementsByClassName("theme-picker");
if (themePickers && themePickers.length > 0) {
themePickers[0].style.display = null;
}
}
function showSearchResults(search) {
@ -376,6 +377,7 @@ function defocusSearchBar() {
document.title = titleBeforeSearch;
}
defocusSearchBar();
hideThemeButtonState();
}
function handleShortcut(ev) {
@ -412,9 +414,59 @@ function defocusSearchBar() {
case "?":
displayHelp(true, ev);
break;
default:
var themePicker = getThemePickerElement();
if (themePicker.parentNode.contains(ev.target)) {
handleThemeKeyDown(ev);
}
}
}
}
function handleThemeKeyDown(ev) {
var active = document.activeElement;
var themes = getThemesElement();
switch (getVirtualKey(ev)) {
case "ArrowUp":
ev.preventDefault();
if (active.previousElementSibling && ev.target.id !== "theme-picker") {
active.previousElementSibling.focus();
} else {
showThemeButtonState();
themes.lastElementChild.focus();
}
break;
case "ArrowDown":
ev.preventDefault();
if (active.nextElementSibling && ev.target.id !== "theme-picker") {
active.nextElementSibling.focus();
} else {
showThemeButtonState();
themes.firstElementChild.focus();
}
break;
case "Enter":
case "Return":
case "Space":
if (ev.target.id === "theme-picker" && themes.style.display === "none") {
ev.preventDefault();
showThemeButtonState();
themes.firstElementChild.focus();
}
break;
case "Home":
ev.preventDefault();
themes.firstElementChild.focus();
break;
case "End":
ev.preventDefault();
themes.lastElementChild.focus();
break;
// The escape key is handled in handleEscape, not here,
// so that pressing escape will close the menu even if it isn't focused
}
}
function findParentElement(elem, tagName) {
do {