From 144ad63f472f9583cfb7dab48421ca64be7abc91 Mon Sep 17 00:00:00 2001 From: Abhishek Panda Date: Sat, 26 Sep 2026 23:14:54 +0530 Subject: [PATCH] fix: add keyboard shortcut for Jump To navigation --- src/components/Nav/JumpToLinks.tsx | 72 +++++- test/components/JumpToLinks.test.tsx | 314 +++++++++++++++++++++++++++ 2 files changed, 385 insertions(+), 1 deletion(-) create mode 100644 test/components/JumpToLinks.test.tsx diff --git a/src/components/Nav/JumpToLinks.tsx b/src/components/Nav/JumpToLinks.tsx index 431f977ed3..e1cd4340d6 100644 --- a/src/components/Nav/JumpToLinks.tsx +++ b/src/components/Nav/JumpToLinks.tsx @@ -1,6 +1,7 @@ import styles from "./styles.module.scss"; import { Icon } from "../Icon"; import type { JumpToLink } from "@/src/globals/state"; +import { useEffect, useRef, useState } from "preact/hooks"; type JumpToLinksProps = { links?: JumpToLink[]; @@ -9,23 +10,92 @@ type JumpToLinksProps = { isOpen: boolean; }; +const isJumpToShortcut = (event: KeyboardEvent): boolean => { + if (!event.altKey) { + return false; + } + + if (event.ctrlKey || event.metaKey || event.shiftKey) { + return false; + } + + const isJKey: boolean = + event.code === "KeyJ" || + event.key === "j" || + event.key === "J" || + event.key === "∆"; + return isJKey; +}; + +const isEditableContext = (target: EventTarget | null): boolean => { + if (!(target instanceof Element)) { + return false; + } + + if (target instanceof HTMLElement && target.isContentEditable) { + return true; + } + + if (target.closest("input, textarea, select")) { + return true; + } + + if (target.closest(".cm-editor")) { + return true; + } + return false; +}; + export const JumpToLinks = ({ links, heading, isOpen, handleToggle, }: JumpToLinksProps) => { + const toggleButtonRef = useRef(null); + const [shortcutLabel, setShortcutLabel] = useState("Alt + J"); + + useEffect(() => { + if (navigator.platform.startsWith("Mac")) { + setShortcutLabel("Option + J"); + } + + const handleKeyDown = (event: KeyboardEvent) => { + if (!isJumpToShortcut(event) || isEditableContext(event.target)) { + return; + } + + if (!toggleButtonRef.current) { + return; + } + + event.preventDefault(); + toggleButtonRef.current.focus(); + }; + + document.addEventListener("keydown", handleKeyDown); + + return () => { + document.removeEventListener("keydown", handleKeyDown); + }; + }, []); + if (!links || links?.length <= 0) return null; return (