Enable keyboard nav of slash commands with arrow keys on mount (#4543)
This commit is contained in:
parent
797920a25f
commit
be82f91fc3
@ -186,6 +186,8 @@ function PresetItem({ preset, onUse, onEdit, onPublish }) {
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
data-slash-command={preset.command}
|
||||
onClick={onUse}
|
||||
className="border-none w-full hover:cursor-pointer hover:bg-theme-action-menu-item-hover px-2 py-2 rounded-xl flex flex-row justify-start items-center relative"
|
||||
>
|
||||
|
||||
@ -6,6 +6,8 @@ export default function EndAgentSession({ setShowing, sendCommand }) {
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
data-slash-command="/exit"
|
||||
onClick={() => {
|
||||
setShowing(false);
|
||||
sendCommand({ text: "/exit", autoSubmit: true });
|
||||
|
||||
@ -5,6 +5,7 @@ import ResetCommand from "./reset";
|
||||
import EndAgentSession from "./endAgentSession";
|
||||
import SlashPresets from "./SlashPresets";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSlashCommandKeyboardNavigation } from "@/hooks/useSlashCommandKeyboardNavigation";
|
||||
|
||||
export default function SlashCommandsButton({ showing, setShowSlashCommand }) {
|
||||
const { t } = useTranslation();
|
||||
@ -34,6 +35,8 @@ export default function SlashCommandsButton({ showing, setShowSlashCommand }) {
|
||||
|
||||
export function SlashCommands({ showing, setShowing, sendCommand, promptRef }) {
|
||||
const cmdRef = useRef(null);
|
||||
useSlashCommandKeyboardNavigation({ showing });
|
||||
|
||||
useEffect(() => {
|
||||
function listenForOutsideClick() {
|
||||
if (!showing || !cmdRef.current) return false;
|
||||
|
||||
@ -8,6 +8,8 @@ export default function ResetCommand({ setShowing, sendCommand }) {
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
data-slash-command="/reset"
|
||||
onClick={() => {
|
||||
setShowing(false);
|
||||
sendCommand({ text: "/reset", autoSubmit: true });
|
||||
|
||||
62
frontend/src/hooks/useSlashCommandKeyboardNavigation.js
Normal file
62
frontend/src/hooks/useSlashCommandKeyboardNavigation.js
Normal file
@ -0,0 +1,62 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
/**
|
||||
* Handles keyboard navigation for the slash commands menu is presented in the UI.
|
||||
* @param {boolean} showing - Whether the slash commands menu is showing
|
||||
* @returns {void}
|
||||
*/
|
||||
export function useSlashCommandKeyboardNavigation({ showing }) {
|
||||
const focusedCommandRef = useRef(null);
|
||||
const availableCommands = useRef([]);
|
||||
|
||||
useEffect(() => {
|
||||
const commands = document.querySelectorAll("[data-slash-command]");
|
||||
availableCommands.current = Array.from(commands).map(
|
||||
(cmd) => cmd.dataset.slashCommand
|
||||
);
|
||||
}, [showing]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showing) return;
|
||||
document.addEventListener("keydown", handleKeyboardNavigation);
|
||||
return () =>
|
||||
document.removeEventListener("keydown", handleKeyboardNavigation);
|
||||
}, [showing]);
|
||||
|
||||
useEffect(() => {
|
||||
// Reset the focused command when the slash commands menu is closed or opened
|
||||
focusedCommandRef.current = null;
|
||||
}, [showing]);
|
||||
|
||||
function handleKeyboardNavigation(event) {
|
||||
event.preventDefault();
|
||||
if (!availableCommands.current.length) return;
|
||||
let currentIndex = availableCommands.current.indexOf(
|
||||
focusedCommandRef.current
|
||||
);
|
||||
|
||||
// If the enter key is pressed, click the focused command if it exists
|
||||
// This will also trigger the onClick event of the focused command
|
||||
// to cleanup everything on hide
|
||||
if (event.key === "Enter" && !!focusedCommandRef.current) {
|
||||
document
|
||||
.querySelector(`[data-slash-command="${focusedCommandRef.current}"]`)
|
||||
?.click();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the current index is -1, set it to the last command, otherwise inc/dec by 1
|
||||
if (currentIndex === -1)
|
||||
currentIndex = availableCommands.current.length - 1;
|
||||
else currentIndex += event.key === "ArrowUp" ? -1 : 1;
|
||||
|
||||
// Wrap around the array both ways if index is out of bounds
|
||||
if (currentIndex < 0) currentIndex = availableCommands.current.length - 1;
|
||||
else if (currentIndex >= availableCommands.current.length) currentIndex = 0;
|
||||
|
||||
focusedCommandRef.current = availableCommands.current[currentIndex];
|
||||
document
|
||||
.querySelector(`[data-slash-command="${focusedCommandRef.current}"]`)
|
||||
?.focus();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user