[WV2] Add search keyboard shortcuts
This commit is contained in:
parent
10e2140045
commit
b462272dc3
2 changed files with 62 additions and 0 deletions
61
app/assets/javascripts/wardrobe/item-search-keys.js
Normal file
61
app/assets/javascripts/wardrobe/item-search-keys.js
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
* Keyboard shortcuts for item search.
|
||||||
|
*
|
||||||
|
* - Up/Down arrows move focus between the search field and the item list,
|
||||||
|
* so keyboard users can quickly browse results without tabbing.
|
||||||
|
* - Escape exits search mode (clicks the back button).
|
||||||
|
*/
|
||||||
|
document.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
const backButton = document.querySelector(
|
||||||
|
".item-search-form .back-button",
|
||||||
|
);
|
||||||
|
if (!backButton) return;
|
||||||
|
|
||||||
|
// Only act when focus is on the search input or a search result.
|
||||||
|
const section = document.querySelector(".outfit-controls-section");
|
||||||
|
const searchInput = section?.querySelector(
|
||||||
|
'.search-form input[type="text"]',
|
||||||
|
);
|
||||||
|
const isSearchFocused =
|
||||||
|
document.activeElement === searchInput ||
|
||||||
|
document.activeElement?.closest(".search-results-list") != null;
|
||||||
|
if (!isSearchFocused) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
backButton.click();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
|
||||||
|
|
||||||
|
const section = document.querySelector(".outfit-controls-section");
|
||||||
|
if (!section) return;
|
||||||
|
|
||||||
|
const searchInput = section.querySelector('.search-form input[type="text"]');
|
||||||
|
if (!searchInput) return;
|
||||||
|
|
||||||
|
// Collect all focusable item inputs in the results list.
|
||||||
|
const itemInputs = [
|
||||||
|
...section.querySelectorAll(
|
||||||
|
'.search-results-list item-card input[type="checkbox"]',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
if (itemInputs.length === 0) return;
|
||||||
|
|
||||||
|
const allTargets = [searchInput, ...itemInputs];
|
||||||
|
const currentIndex = allTargets.indexOf(document.activeElement);
|
||||||
|
if (currentIndex === -1) return;
|
||||||
|
|
||||||
|
let nextIndex;
|
||||||
|
if (e.key === "ArrowDown") {
|
||||||
|
nextIndex = Math.min(currentIndex + 1, allTargets.length - 1);
|
||||||
|
} else {
|
||||||
|
nextIndex = Math.max(currentIndex - 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextIndex !== currentIndex) {
|
||||||
|
e.preventDefault();
|
||||||
|
allTargets[nextIndex].focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
= javascript_include_tag "tab-panel", async: true
|
= javascript_include_tag "tab-panel", async: true
|
||||||
= javascript_include_tag "outfit-rename-field", async: true
|
= javascript_include_tag "outfit-rename-field", async: true
|
||||||
= javascript_include_tag "wardrobe/item-card", async: true
|
= javascript_include_tag "wardrobe/item-card", async: true
|
||||||
|
= javascript_include_tag "wardrobe/item-search-keys", async: true
|
||||||
= javascript_include_tag "wardrobe/show", async: true
|
= javascript_include_tag "wardrobe/show", async: true
|
||||||
= csrf_meta_tags
|
= csrf_meta_tags
|
||||||
%meta{name: 'outfit-viewer-morph-mode', value: 'full-page'}
|
%meta{name: 'outfit-viewer-morph-mode', value: 'full-page'}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue