From 999c9d640c2a661eb0752167d2c30180527a8b67 Mon Sep 17 00:00:00 2001 From: Anderson Cook Date: Mon, 10 Jun 2024 03:19:27 -0500 Subject: [PATCH] Adds a `v` shortcut to open/focus the version select (#1916) --- assets/js/keyboard-shortcuts.js | 20 +++++++++++++-- assets/js/sidebar/sidebar-drawer.js | 27 +++++++++++++-------- assets/js/sidebar/sidebar-version-select.js | 26 ++++++++++++++++++++ 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/assets/js/keyboard-shortcuts.js b/assets/js/keyboard-shortcuts.js index 797d94b88..0f9d85c0b 100644 --- a/assets/js/keyboard-shortcuts.js +++ b/assets/js/keyboard-shortcuts.js @@ -1,5 +1,6 @@ import { isAppleOS, qs } from './helpers' -import { toggleSidebar } from './sidebar/sidebar-drawer' +import { isSidebarOpened, openSidebar, toggleSidebar } from './sidebar/sidebar-drawer' +import { openVersionSelect } from './sidebar/sidebar-version-select' import { focusSearchInput } from './search-bar' import { cycleTheme } from './theme' import { openQuickSwitchModal } from './quick-switch' @@ -34,6 +35,11 @@ export const keyboardShortcuts = [ hasModifier: true, action: searchKeyAction }, + { + key: 'v', + description: 'Open/focus version select', + action: versionKeyAction + }, { key: 'g', description: 'Search HexDocs package', @@ -68,7 +74,7 @@ function addEventListeners () { function handleKeyDown (event) { if (state.shortcutBeingPressed) { return } - if (event.target.matches('input, textarea')) { return } + if (event.target.matches('input, select, textarea')) { return } const matchingShortcut = keyboardShortcuts.find(shortcut => { if (shortcut.hasModifier) { @@ -101,6 +107,16 @@ function searchKeyAction (event) { focusSearchInput() } +function versionKeyAction () { + closeModal() + + if (isSidebarOpened()) { + openVersionSelect() + } else { + openSidebar().then(openVersionSelect) + } +} + function toggleHelpModal () { if (isHelpModalOpen()) { closeModal() diff --git a/assets/js/sidebar/sidebar-drawer.js b/assets/js/sidebar/sidebar-drawer.js index 075d12946..54b5476e9 100644 --- a/assets/js/sidebar/sidebar-drawer.js +++ b/assets/js/sidebar/sidebar-drawer.js @@ -126,6 +126,13 @@ function isSidebarOpen () { ) } +/** + * Returns if sidebar is fully open. + */ +export function isSidebarOpened () { + return document.body.classList.contains(SIDEBAR_CLASS.opened) +} + /** * Opens the sidebar by applying an animation. * @@ -136,13 +143,13 @@ export function openSidebar () { sessionStorage.setItem('sidebar_state', 'opened') qs(SIDEBAR_TOGGLE_SELECTOR).setAttribute('aria-expanded', 'true') - requestAnimationFrame(() => { - setClass(SIDEBAR_CLASS.openingStart) - + return new Promise((resolve, reject) => { requestAnimationFrame(() => { - setClass(SIDEBAR_CLASS.opening) + setClass(SIDEBAR_CLASS.openingStart) + + requestAnimationFrame(() => { + setClass(SIDEBAR_CLASS.opening) - return new Promise((resolve, reject) => { state.togglingTimeout = setTimeout(() => { setClass(SIDEBAR_CLASS.opened) resolve() @@ -162,13 +169,13 @@ export function closeSidebar () { sessionStorage.setItem('sidebar_state', 'closed') qs(SIDEBAR_TOGGLE_SELECTOR).setAttribute('aria-expanded', 'false') - requestAnimationFrame(() => { - setClass(SIDEBAR_CLASS.closingStart) - + return new Promise((resolve, reject) => { requestAnimationFrame(() => { - setClass(SIDEBAR_CLASS.closing) + setClass(SIDEBAR_CLASS.closingStart) + + requestAnimationFrame(() => { + setClass(SIDEBAR_CLASS.closing) - return new Promise((resolve, reject) => { state.togglingTimeout = setTimeout(() => { setClass(SIDEBAR_CLASS.closed) resolve() diff --git a/assets/js/sidebar/sidebar-version-select.js b/assets/js/sidebar/sidebar-version-select.js index d79d4fa39..9b3d48e88 100644 --- a/assets/js/sidebar/sidebar-version-select.js +++ b/assets/js/sidebar/sidebar-version-select.js @@ -70,3 +70,29 @@ function handleVersionSelected (event) { } }) } + +/** + * Opens the version select if available. + * Only focuses the version select if + * - the browser's HTMLSelectElement lacks the showPicker method + * - there has been transient user interaction + */ +export function openVersionSelect () { + const select = qs(VERSIONS_DROPDOWN_SELECTOR) + + if (select) { + select.focus() + + // Prevent subsequent 'v' press from submitting form + select.addEventListener('keydown', event => { + if (event.key === 'Escape' || event.key === 'v') { + event.preventDefault() + select.blur() + } + }) + + if (navigator.userActivation.isActive && 'showPicker' in HTMLSelectElement.prototype) { + select.showPicker() + } + } +}