From 0619eabec2115b01d26fdf47b55f1f00a94fda07 Mon Sep 17 00:00:00 2001 From: henrygd Date: Wed, 5 Mar 2025 23:35:46 -0500 Subject: [PATCH] feat: add keyboard navigation for systems --- .../site/src/components/command-palette.tsx | 6 ++--- beszel/site/src/components/routes/system.tsx | 27 +++++++++++++++++++ beszel/site/src/lib/utils.ts | 11 +++++++- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/beszel/site/src/components/command-palette.tsx b/beszel/site/src/components/command-palette.tsx index cfb551a..0f76e6a 100644 --- a/beszel/site/src/components/command-palette.tsx +++ b/beszel/site/src/components/command-palette.tsx @@ -22,7 +22,7 @@ import { import { useEffect } from "react" import { useStore } from "@nanostores/react" import { $systems } from "@/lib/stores" -import { getHostDisplayValue, isAdmin } from "@/lib/utils" +import { getHostDisplayValue, isAdmin, listen } from "@/lib/utils" import { $router, basePath, navigate } from "./router" import { Trans, t } from "@lingui/macro" import { getPagePath } from "@nanostores/router" @@ -37,9 +37,7 @@ export default function CommandPalette({ open, setOpen }: { open: boolean; setOp setOpen(!open) } } - - document.addEventListener("keydown", down) - return () => document.removeEventListener("keydown", down) + return listen(document, "keydown", down) }, [open, setOpen]) return ( diff --git a/beszel/site/src/components/routes/system.tsx b/beszel/site/src/components/routes/system.tsx index 2735a78..da2ce76 100644 --- a/beszel/site/src/components/routes/system.tsx +++ b/beszel/site/src/components/routes/system.tsx @@ -12,6 +12,7 @@ import { getHostDisplayValue, getPbTimestamp, getSizeAndUnit, + listen, toFixedFloat, useLocalStorage, } from "@/lib/utils" @@ -25,6 +26,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from ". import { timeTicks } from "d3-time" import { Plural, Trans, t } from "@lingui/macro" import { useLingui } from "@lingui/react" +import { $router, navigate } from "../router" +import { getPagePath } from "@nanostores/router" const AreaChartDefault = lazy(() => import("../charts/area-chart")) const ContainerChart = lazy(() => import("../charts/container-chart")) @@ -288,6 +291,30 @@ export default function SystemDetail({ name }: { name: string }) { const distanceToBottom = wrapperRect.bottom - chartRect.bottom setBottomSpacing(tooltipHeight - distanceToBottom) }, [netCardRef, containerData]) + + // keyboard navigation between systems + useEffect(() => { + const handleKeyUp = (e: KeyboardEvent) => { + if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) { + return + } + const currentIndex = systems.findIndex(s => s.name === name) + if (currentIndex === -1 || systems.length <= 1) { + return + } + switch (e.key) { + case "ArrowLeft": + case "h": + const prevIndex = (currentIndex - 1 + systems.length) % systems.length + return navigate(getPagePath($router, "system", { name: systems[prevIndex].name })) + case "ArrowRight": + case "l": + const nextIndex = (currentIndex + 1) % systems.length + return navigate(getPagePath($router, "system", { name: systems[nextIndex].name })) + } + } + return listen(document, "keyup", handleKeyUp) + }, [name]) if (!system.id) { return null diff --git a/beszel/site/src/lib/utils.ts b/beszel/site/src/lib/utils.ts index 1d716a4..4472c51 100644 --- a/beszel/site/src/lib/utils.ts +++ b/beszel/site/src/lib/utils.ts @@ -15,7 +15,16 @@ import { prependBasePath } from "@/components/router" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } -// export const cn = clsx + +/** Adds event listener to node and returns function that removes the listener */ +export function listen( + node: Node, + event: string, + handler: (event: T) => void +) { + node.addEventListener(event, handler as EventListener) + return () => node.removeEventListener(event, handler as EventListener) +} export async function copyToClipboard(content: string) { const duration = 1500