Merge pull request #2363 from bobbymannino/keyboard-nav-on-more-pages

add keyboard nav for compose/database pages
This commit is contained in:
Mauricio Siu
2025-08-16 16:14:10 -06:00
committed by GitHub
8 changed files with 96 additions and 24 deletions

View File

@@ -3,7 +3,34 @@
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
const SHORTCUTS = {
const PAGES = [
"compose",
"application",
"postgres",
"redis",
"mysql",
"mariadb",
"mongodb",
] as const;
type Page = (typeof PAGES)[number];
type Shortcuts = Record<string, string>;
type ShortcutsDictionary = Record<Page, Shortcuts>;
const COMPOSE_SHORTCUTS: Shortcuts = {
g: "general",
e: "environment",
u: "domains",
d: "deployments",
b: "backups",
s: "schedules",
v: "volumeBackups",
l: "logs",
m: "monitoring",
a: "advanced",
};
const APPLICATION_SHORTCUTS: Shortcuts = {
g: "general",
e: "environment",
u: "domains",
@@ -16,22 +43,43 @@ const SHORTCUTS = {
a: "advanced",
};
const POSTGRES_SHORTCUTS: Shortcuts = {
g: "general",
e: "environment",
l: "logs",
m: "monitoring",
b: "backups",
a: "advanced",
};
const REDIS_SHORTCUTS: Shortcuts = {
g: "general",
e: "environment",
l: "logs",
m: "monitoring",
a: "advanced",
};
const SHORTCUTS: ShortcutsDictionary = {
application: APPLICATION_SHORTCUTS,
compose: COMPOSE_SHORTCUTS,
postgres: POSTGRES_SHORTCUTS,
redis: REDIS_SHORTCUTS,
mysql: POSTGRES_SHORTCUTS,
mariadb: POSTGRES_SHORTCUTS,
mongodb: POSTGRES_SHORTCUTS,
};
/**
* Use this to register keyboard shortcuts for the application page. Each
* shortcut must be prefixed with `g` (like GitHub).
* Use this to register keyboard shortcuts for different pages. Each shortcut
* must be prefixed with `g` (like GitHub).
*
* @example
* - `g g` "General",
* - `g e` "Environment",
* - `g u` "Domains",
* - `g p` "Preview Deployments",
* - `g s` "Schedules",
* - `g v` "Volume Backups",
* - `g d` "Deployments",
* - `g l` "Logs",
* - `g m` "Monitoring",
* - `g a` "Advanced"
*/
export function UseKeyboardNavForApplications() {
export function UseKeyboardNav({ forPage }: { forPage: Page }) {
const [isModPressed, setModPressed] = useState(false);
const [timer, setTimer] = useState<NodeJS.Timeout | null>(null);
@@ -39,6 +87,8 @@ export function UseKeyboardNavForApplications() {
const router = useRouter();
const pathname = usePathname();
const shortcuts = SHORTCUTS[forPage];
const updateSearchParam = useCallback(
(name: string, value: string) => {
const params = new URLSearchParams(sp.toString());
@@ -50,22 +100,32 @@ export function UseKeyboardNavForApplications() {
);
useEffect(() => {
const handleKeyDown = ({ key }: KeyboardEvent) => {
const handleKeyDown = ({ key, target }: KeyboardEvent) => {
const active = target as HTMLElement | null;
if (active) {
const tag = active.tagName;
if (
active.isContentEditable ||
tag === "INPUT" ||
tag === "TEXTAREA" ||
tag === "SELECT" ||
active.getAttribute("role") === "textbox"
)
return;
}
if (isModPressed) {
if (timer) clearTimeout(timer);
setModPressed(false);
if (key in SHORTCUTS) {
const tab = SHORTCUTS[key as keyof typeof SHORTCUTS];
router.push(
`${pathname}?${updateSearchParam("tab", tab.toLowerCase())}`,
);
}
} else {
if (key === "g") {
setModPressed(true);
setTimer(setTimeout(() => setModPressed(false), 5000));
if (key in shortcuts) {
const tab = shortcuts[key]!;
router.push(`${pathname}?${updateSearchParam("tab", tab)}`);
}
} else if (key === "g") {
setModPressed(true);
setTimer(setTimeout(() => setModPressed(false), 1500));
}
};

View File

@@ -51,7 +51,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { UseKeyboardNavForApplications } from "@/hooks/use-keyboard-nav";
import { UseKeyboardNav } from "@/hooks/use-keyboard-nav";
import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
@@ -92,7 +92,7 @@ const Service = (
return (
<div className="pb-10">
<UseKeyboardNavForApplications />
<UseKeyboardNav forPage="application" />
<BreadcrumbSidebar
list={[
{ name: "Projects", href: "/dashboard/projects" },

View File

@@ -48,6 +48,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { UseKeyboardNav } from "@/hooks/use-keyboard-nav";
import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
@@ -82,6 +83,7 @@ const Service = (
return (
<div className="pb-10">
<UseKeyboardNav forPage="compose" />
<BreadcrumbSidebar
list={[
{ name: "Projects", href: "/dashboard/projects" },

View File

@@ -29,6 +29,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { UseKeyboardNav } from "@/hooks/use-keyboard-nav";
import { cn } from "@/lib/utils";
import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
@@ -63,6 +64,7 @@ const Mariadb = (
return (
<div className="pb-10">
<UseKeyboardNav forPage="mariadb" />
<BreadcrumbSidebar
list={[
{ name: "Projects", href: "/dashboard/projects" },

View File

@@ -29,6 +29,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { UseKeyboardNav } from "@/hooks/use-keyboard-nav";
import { cn } from "@/lib/utils";
import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
@@ -63,6 +64,7 @@ const Mongo = (
return (
<div className="pb-10">
<UseKeyboardNav forPage="mongodb" />
<BreadcrumbSidebar
list={[
{ name: "Projects", href: "/dashboard/projects" },

View File

@@ -29,6 +29,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { UseKeyboardNav } from "@/hooks/use-keyboard-nav";
import { cn } from "@/lib/utils";
import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
@@ -62,6 +63,7 @@ const MySql = (
return (
<div className="pb-10">
<UseKeyboardNav forPage="mysql" />
<BreadcrumbSidebar
list={[
{ name: "Projects", href: "/dashboard/projects" },

View File

@@ -29,6 +29,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { UseKeyboardNav } from "@/hooks/use-keyboard-nav";
import { cn } from "@/lib/utils";
import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
@@ -62,6 +63,7 @@ const Postgresql = (
return (
<div className="pb-10">
<UseKeyboardNav forPage="postgres" />
<BreadcrumbSidebar
list={[
{ name: "Projects", href: "/dashboard/projects" },

View File

@@ -28,6 +28,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { UseKeyboardNav } from "@/hooks/use-keyboard-nav";
import { cn } from "@/lib/utils";
import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
@@ -62,6 +63,7 @@ const Redis = (
return (
<div className="pb-10">
<UseKeyboardNav forPage="redis" />
<BreadcrumbSidebar
list={[
{ name: "Projects", href: "/dashboard/projects" },